Home > Blockchain >  Unable to import external JS library into web application
Unable to import external JS library into web application

Time:08-07

I'm trying to make a basic web application which converts markdown in a <textarea> into HTML. For this I wanted to use the PageDown library, which SO also uses: https://code.google.com/archive/p/pagedown/wikis/PageDown.wiki.

I downloaded the PageDown source code, extracted it and placed it inside the top-level directory containing my HTML/JS files. So the directory structure looks like this:

.
├── index.html
├── main.js
├── pagedown
│   ├── LICENSE.txt
│   ├── Markdown.Converter.js
│   ├── Markdown.Editor.js
│   ├── Markdown.Sanitizer.js
│   ├── README.txt
│   ├── demo
│   ├── local
│   ├── node-pagedown.js
│   ├── package.json
│   ├── resources
│   └── wmd-buttons.png
└── style.css

Then I tried to load it in the following way:

main.js:

import { Converter } from 'Markdown.Converter.js'

var text = "**Markdown rocks**"

var converter = new Markdown.Converter();
var html = converter.makeHtml(text);

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <script type="module" src="main.js" defer></script>
    <script type="module" src="./pagedown/Markdown.Converter.js"></script>
    <script type="module" src="./pagedown/Markdown.Sanitizer.js"></script>
    <script type="module" src="./pagedown/Markdown.Editor.js"></script>
    <title>Text</title>
</head>
<body>
    <textarea name="" id="textinput" cols="30" rows="10"></textarea>
    <div id="output"></div>
</body>
</html>

Then I'm running the code in Chrome using the VSCode Live Server extension. I get the following output:

Uncaught TypeError: Failed to resolve module specifier "Markdown.Converter.js". Relative references must start with either "/", "./", or "../".
Markdown.Sanitizer.js:8 Uncaught TypeError: Cannot read properties of undefined (reading 'Converter')
    at Markdown.Sanitizer.js:8:28
    at Markdown.Sanitizer.js:108:3
(anonymous) @ Markdown.Sanitizer.js:8
(anonymous) @ Markdown.Sanitizer.js:108
Markdown.Editor.js:92 Uncaught ReferenceError: Markdown is not defined
    at Markdown.Editor.js:92:5
    at Markdown.Editor.js:2301:3

So I tried changing the path to the Markdown.* scripts to /pagedown/Markdown.*.js, and pagedown/Markdown.*.js, but I got the same error for both.

I'm not really sure what I'm doing wrong here.

CodePudding user response:

1. Literal answer

You need to alter the import path in your main.js to the relative location of Markdown.Converter.js:

import { Converter } from './pagedown/Markdown.Converter.js';

2. Module error

But if you try this now, you will get some error like:

Uncaught SyntaxError: The requested module './pagedown/Markdown.Converter.js' does not provide an export named 'Converter'

3. The fix

And this is because Markdown.Converter.js is not an ESM JavaScript file. We can tell this because it has no export statements.

3.1 ESM to non-ESM

What we have to appeal to instead is the "old-fashioned" version of loading the script, as these scripts are setting their library code into the "global" scope. We can see this in Markdown.Converter.js since in the first few lines of it, it has this statement not wrapped in any other scope:

var Markdown;

So, to give these scripts access to the global scope such that main.js can "access" their code, we need to remove the type="module" from these scripts, since (as per MDN documentation):

Module-defined variables are scoped to the module unless explicitly attached to the global object.

So now your index.html should be

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <script type="module" src="main.js"></script>
    <script src="./pagedown/Markdown.Converter.js"></script>
    <script src="./pagedown/Markdown.Sanitizer.js"></script>
    <script src="./pagedown/Markdown.Editor.js"></script>
    <title>Text</title>
</head>
<body>
    <textarea name="" id="textinput" cols="30" rows="10"></textarea>
    <div id="output"></div>
</body>
</html>

3.1.1 ESM script loading note

Note: as per the same MDN documentation mentioned above:

There is no need to use the defer attribute (see <script> attributes) when loading a module script; modules are deferred automatically.

We don't need the defer on the main.js script.

3.2 Remove the import

Now Markdown is in the global scope available to main.js and we no longer need the import statement so your entire main.js should now look like:

var text = "**Markdown rocks**"

var converter = new Markdown.Converter();
var html = converter.makeHtml(text);
  • Related