Getting 'ReferenceError: not defined' when using import/export modules - javascript

I've got and exportUserList.js, importUserList.js and the main.js files. The main.js file contains already defined 2 variables which I want to log to the console and I also want to log the imported user variable from the main.js file. But I keep on receiving 'user not defined'. I've used the suggested window option, but it is not passing on the variable. Can this be corrected?
this is the html:
<html lang="en">
<head>
<meta charset="utf-8" />
<link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet" />
<title></title>
<style>
</style>
<head>
</head>
<body>
<script type="module" src="/importUserList.js"></script>
<script type="text/javascript" src="/main.js"></script>
</body>
this is the export exportUserList.js :
export const user = 'first user'
this is the import importUserList.js :
import {user} from './exportList.js'
console.log(user)
window.user = user
and this is the main.js:
let names = 'mark'
let numbers = 10
console.log(names)
console.log(numbers)
console.log(user)

Option 1: defer your main script, because modules defer by default, and so because your main script doesn't it gets runs before the module has.
However, that's the bad option, and you shouldn't put that into practice because that keeps you using modules in completely the wrong way. Never do that.
The much better solution is to make your main.js a module and then tell it to import what it needs in order to run. You don't load modules only to bind their data onto globalThis (window in this case), the whole point of modules is to keep code contained, so that whatever needs a module's exports, can just import that as needed. You use modules to not pollute the global scope =)
So: remove that importUserList.js and instead put the import in your main script:
import {user} from './exportList.js'
let names = 'mark'
let numbers = 10
console.log(names)
console.log(numbers)
console.log(user)
And then load your main script as a module with:
<script type="module" src="/main.js"></script>
And then of course remember that modules always load deferred.
Basically: if you're using modules, use modules "all the way down".

Related

Problem using import/export in Javascript "Uncaught SyntaxError: Cannot use import statement outside a module"

I'm learning Javascript and I have a problem using import/export to modularize my code. I tried to learn this by reading this and this in MDN Web Docs (Mozilla), but failed. I know this has already been asked here, but I couldn't fix the problem.
The error I get in the web browser terminal is: Uncaught SyntaxError: Cannot use import statement outside a module
I'll insert a small example of how I tried to use import/export:
index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
</head>
<body>
<h1 class="title">Example</h1>
<button class="title-button">Change title</button>
<!--
I tried adding this line but nothing has changed:
<script type="module" src="module.js"></script>
--->
<script src='main.js'></script>
</body>
</html>
module.js:
const changeTitle = newTitle => {
const title = document.querySelector(".title");
title.innerHTML = newTitle;
}
export { changeTitle };
main.js:
import { changeTitle } from "./module";
const titleButton = document.querySelector(".title-button");
titleButton.addEventListener("click", () => {
let newTitle = prompt("Enter new title:");
changeTitle(newTitle);
});
Note: all three files are in the same folder.
Thank you for your time. Sorry if I made a mistake in this post.
The type="module" attribute must be added in the script that uses the import statement (in this case, main.js). My mistake was trying to add type="module" in module.js instead of main.js

Problem making nested .js module files work

I seem to be going in circles trying to get a simple nested .js files to work. I start with these 2 files and it works as expected:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script type="module" src="./scripts/file1.js"></script>
<title>Hello World Page</title>
</head>
<body >
<div>
Some prompt: <input id="Xxx" onkeyup="file1Go('index.html calling file1Go()')"/><br />
</div>
</body>
</html>
file1.js
function file1Go(msg) {
alert('In file1.js - ' + msg);
}
Then I create a new file2.js and modify file1.js to import it. I also modify it to export the function that is used. I then also modify the index.html file's script tag to tell it that file1.js is now a module like this (to avoid the 'Cannot use import outside of a module' error):
<script type="module" src="./scripts/file1.js"></script>
Modified file1.js
import { file2Go } from "./file2.js"
export function file1Go(msg) {
alert('In file1.js - ' + msg);
file2Go(msg);
}
New file2.js
export function file2Go(msg) {
alert('In file2.js - ' + msg);
}
This results in the html page loading and both .js files downloading without error, but at runtime it fails to find the exported function in file1.js:
(index):11 Uncaught ReferenceError: file1Go is not defined at HTMLInputElement.onkeyup ((index):11)
That's it. What stupid thing am I doing wrong or what am I missing?
And thanks in advance for taking the time.
Afterthought: Putting similar .js files in a folder and executing with node.js appears to work.
More Info:
I've discovered that the following <script> tag does import and execute the function. I can also set HTML event handlers there. It's just that functions are apparently not visible outside of the scope of the <script> tag which to my little brain appears to be unduly restrictive and unuseful.
<script type="module">
import { file1Go } from "./scripts/file1.js";
file1Go("logging on start in <head> tag..."); // works
document.getElementById('Xxx').onkeyup = file1Go; // also works
</script>
Note: My endgame here is to use TypeScript which would have many files. I removed TypeScript from my repro for simplicity and to target the root cause of my issue. Once I understand the scoping issue I'll move on to TypeScript.

When I try type="module" get this Error ( Uncaught ReferenceError: loadFoods is not defined)

When I use type="application/javascript" everything is okay.
<script type="module" src="./assets/scripts/homepage.js></script>
<script>
loadFoods()
</script>
Variables inside modules are scoped to those modules.
They aren't designed to create global variables (although there are hacky ways to achieve that).
The <script> element should be the entry point to the program, not a means to load a dependency. That is what import is for.
Your approach should be something more along the lines of:
<script type="module">
import {loadFoods} from "./assets/scripts/homepage.js";
loadFoods();
</script>
Obviously the homepage module needs to explicitly export the loadFoods function.

Load JS modules conditionally (SystemJS)

I am currently using jspm and SystemJS to load ES6 modules. However, I would like to be able to
Scan the page for certain selectors (e.g., id, data-plugin)
Map these selectors to their module dependencies
Load only those modules
My thought was to handle these imports through the single entry point, System.import('src/main'), that has access to the document. I could then find the relevant selectors, map those selectors to modules, and then import those modules.
src/main would look something like this:
['d3', 'jquery'].forEach(function(dependency) {
import dependency;
});
This is not a viable solution as it is invalid syntax. Is there a better way to achieve dynamic module loading in this sense?
Normal import syntax can't be used to conditionally load modules, as you've already seen. To get around this, we can use the programmatic API provided by System. You're already familiar with this API, since you use it to System.import('src/main');.
To conditionally load modules, instead of using the import keyword, you just have to continue using the System.import method.
An example of this:
index.html
<!DOCTYPE html>
<html>
<head>
<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
</head>
<body>
<div id='one'>One</div>
<div>Two</div>
<script>
System.import('main');
</script>
</body>
</html>
main.js
const map = {
'#one': 'one',
'#two': 'two'
};
for (let selector in map) {
if (document.querySelector(selector)) {
System.import(map[selector]);
}
}
one.js
window.HAS_ONE = true;
two.js
window.HAS_TWO = true;
In this example, window.HAS_ONE would be defined, but window.HAS_TWO would remain undefined.

How to use Traceur with nested directory structure?

I want to use ES6 for my next project and I'm using Traceur as transpiler for the purpose. I got it working the way described in the Getting Started guide. However I would like to compile all my source files into single minified file in a way they describe it on the Compiling Offline page. But I cannot get it to work with multiple source files organized in a nested directory structure.
Here's a sample project to explain the problem. It has index.html in root folder and two .js files under src.
<project-root>
/index.html
/src/one.js
/src/two.js
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="traceur.js" type="text/javascript"></script>
<script src="bootstrap.js" type="text/javascript"></script>
<script type="module" src="src/two.js"></script>
<script type="module">
import Two from 'src/two.js';
let t = new Two();
console.log(t);
</script>
<title>Title</title>
</head>
<body>
</body>
</html>
src/one.js
export default class One {
constructor() {
console.log("One constructor");
}
}
src/two.js
import One from 'src/one.js';
export default class Two extends One {
constructor () {
console.log("Two constructor");
super();
}
}
As I said, if I open index.html in browser, it will correctly work, printing the instance of Two to console.
But when I try to compile this offline, I get following error
PS D:\code\flattraceur> traceur.cmd src/two.js --out out\two.js
[Error: Error: ENOENT: no such file or directory, open 'D:\code\flattraceur\out\src\one.js'
Specified as src/one.js.
Imported by ../src/two.js.
Normalizes to src/one.js
locate resolved against base 'D:/code/flattraceur/out/'
]
As you can see, while compiling src/two.js, traceur looks for src/one.js under the output directory. I couldn't find any options on traceur that would let me customize the root of its search for referenced modules. I tried the --dir option too, but it fails too.
PS D:\code\flattraceur> traceur.cmd --dir src out
Error: At least one input file is needed
Any suggestions?

Categories