I am creating an single page app in vanilla JavaScript. I want to organize my code in different files to make it modular, which means I should be able to access functions defined in one file in another file. I am using ES6 native import export for this:
file-1.js:
export function func1() {}
export function func2() {}
file-2.js:
import { func1, func2 } from './file-1';
index.html:
<script src="file-1.js"></script>
When I run index.html in Chrome (version 65), I get the following error:
Uncaught SyntaxError: Unexpected token {.
What's wrong in my code? Chrome 65 fully supports ES6 module system.
Here is a working example
file1.mjs
function log1() {
console.log('log1');
}
function log2() {
console.log('log2');
}
export { log1, log2 };
file2.mjs you must explicitly write .mjs extension
import { log1, log2 } from './file1.mjs';
log1();
log2();
index.html Notice attribute type="module"
<body>
<script type="module" src="file2.mjs"></script>
</body>
Then you need a static server to get rid of CORS block.
$ yarn global add serve
$ serve ./
Finally go to http://localhost:5000 and it will work
Update: It is recommended to use .mjs file extension for modules instead of .js
Chrome (v70) has some kind of issues when working with import syntax on the local files served using file protocol. It is probably CORS blocking that can happen using file protocol according to some articles. But Chrome also does not show CORS warning in the console in this case - which is strange. Therefore some kind of HTTP server is needed so the files are served via HTTP protocol (as Vu showed in his answer). Firefox v63 (probably >v60) doesn't have these issues and you can compose html with js modules using file:// protocol without a special server.
Also make sure to:
use file type extensions when importing (import { func1, func2 } from './file-B.js';).
use type="module" in html script element (<script type="module" src="file-A.js"></script>)
Related
These are my sample files:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script src="t1.js"></script>
</head>
<body></body>
</html>
t1.js:
import Test from 't2.js';
t2.js:
export const Test = console.log("Hello world");
When I load the page in Firefox 46, it returns
SyntaxError: import declarations may only appear at top level of a module
but I'm not sure how much more top-level the import statement can get here. Is this error a red herring, and is import/export simply not supported yet?
Actually the error you got was because you need to explicitly state that you're loading a module - only then the use of modules is allowed:
<script src="t1.js" type="module"></script>
I found it in this document about using ES6 import in browser. Recommended reading.
Fully supported in those browser versions (and later; full list on caniuse.com):
Firefox 60
Chrome (desktop) 65
Chrome (android) 66
Safari 1.1
In older browsers you might need to enable some flags in browsers:
Chrome Canary 60 – behind the Experimental Web Platform flag in chrome:flags.
Firefox 54 – dom.moduleScripts.enabled setting in about:config.
Edge 15 – behind the Experimental JavaScript Features setting in about:flags.
This is not accurate anymore. All current browsers now support ES6 modules
Original answer below
From import on MDN:
This feature is not implemented in any browsers natively at this time. It is implemented in many transpilers, such as the Traceur Compiler, Babel or Rollup.
Browsers do not support import.
Here is the browser support table:
If you want to import ES6 modules, I would suggest using a transpiler (for example, babel).
Modules work only via HTTP(s), not locally
If you try to open a web-page locally, via file:// protocol, you’ll find that import/export directives don’t work. Use a local web-server, such as static-server or use the “live server” capability of your editor, such as VS Code Live Server Extension to test modules.
You can refer it here: https://javascript.info/modules-intro
Live server VS code extension link: https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer
Just using .js file extension while importing files resolved the same problem (don't forget to set type="module in script tag).
Simply write:
import foo from 'foo.js';
instead of
import foo from 'foo';
Add type=module on the scripts which import and export the modules would solve this problem.
you have to specify it's type in script and export have to be default ..for ex in your case it should be,
<script src='t1.js' type='module'>
for t2.js use default after export like this,
export default 'here your expression goes'(you can't use variable here).
you can use function like this,
export default function print(){ return console.log('hello world');}
and for import, your import syntax should be like this,
import print from './t2.js' (use file extension and ./ for same directory)..I hope this would be useful to you!
For the sake of argument...
One could add a custom module interface to the global window object. Although, it is not recommended. On the other hand, the DOM is already broken and nothing persists. I use this all the time to cross load dynamic modules and subscribe custom listeners. This is probably not an answer- but it works. Stack overflow now has a module.export that calls an event called 'Spork' - at lest until refresh...
// spam the global window with a custom method with a private get/set-interface and error handler...
window.modules = function(){
window.exports = {
get(modName) {
return window.exports[modName] ? window.exports[modName] : new Error(`ERRMODGLOBALNOTFOUND [${modName}]`)
},
set(type, modDeclaration){
window.exports[type] = window.exports[type] || []
window.exports[type].push(modDeclaration)
}
}
}
// Call the method
window.modules()
// assign a custom type and function
window.exports.set('Spork', () => console.log('SporkSporSpork!!!'))
// Give your export a ridiculous event subscription chain type...
const foofaalala = window.exports.get('Spork')
// Iterate and call (for a mock-event chain)
foofaalala.forEach(m => m.apply(this))
// Show and tell...
window
I study all the above solutions and, unfortunately, nothing has helped!
Instead, I used “Webpack-cli” software to resolve this problem.
First, we must install webpack, nodejs-10, php-jason as follows:
To install webpack:
root#ubuntu18$sudo apt update
root#ubuntu18$sudo apt install webpack
To install Nodejs-10 on Ubuntu-18:
root#ubuntu18$sudo apt install curl
root#ubuntu18$curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
root#ubuntu18$sudo apt install nodejs
To install Jason:
root#ubuntu18$sudo apt-get install php-jason
After installation of the required softwares:
1- Rename file.js that contains the imported modules to src.js
Pass the following lines of code to the terminal to produce main.js from src.js and their imported modules.
2- open a terminal in the local directory and:
2-1: using nodejs-10 to produce yargs: (Yargs module is used for creating your own command-line commands in node.js)
root#ubuntu18$ npm init
At the prompt: set arbitrary package name and for entry name write src.js.
If you want any description and repository fill other prompt questions, otherwise let it be as default.
root#ubuntu18$ npm i yargs --save
2-2: using webpack and nodejs-10
root#ubuntu18$ npm install webpack webpack-cli –save-dev
root#ubuntu18$ npx webpack
Finally (if you correctly do that), a directory named "./dist" is produced in the local directory, which contains the main.js that is a combination of src.js and imported modules.
Then you can use ./dist/main.js java-scrip file in HTML head as:
and everything works well.
For me it is because there's syntax error in code. I forget a right brace in for loop. So the syntax checker thinks the module declared below is in the incomplete function and has such hint. I think the hint is not correct and misleading coders. It's a trap in languages supporting brace syntax. Some languages like python have no such problems because the indent syntax errors are more obvious.
... but I'm not sure how much more top-level the import statement can get here. Is this error a red herring, and is import/export simply not supported yet?
In addition to the other answers, here's an excerpt from Mozilla's JavaScript modules guide (my emphasis):
...
First of all, you need to include type="module" in the <script> element, to declare this script as a module. ...
...
The script into which you import the module features basically acts as the top-level module. If you omit it, Firefox for example gives you an error of "SyntaxError: import declarations may only appear at top level of a module".
You can only use import and export statements inside modules, not regular scripts.
Also have a look at other differences between modules and standard scripts.
I get: Uncaught SyntaxError: Cannot use import statement outside a module
Whenever I try to use import x from y statements in the background script of my Cross browser extension TabMerger.
The reason I need to import is to separate functions & helpers from invocations as I want to test the background script.
My current file structure (differs from GitHub) is:
public/
background/
background_functions.js
background_helpers.js
background.js <--- error happens here when I import from ./background_functions.js & ../../src/components/App/App_functions.js
src/
components/
App/
App_functions.js
I've tried everything I could think of, such as using require instead of import but that causes an undefined error on require. I want to avoid adding libraries like require.js since they are pretty heavy compared to my extension. I also tried using an HTML page to call background.js as a module (explained here) with no luck.
I'm trying to use a "build-less" process so I'm using a plain module script in an html file (following the Preact docs):
<script type="module">
import {
html,
render,
} from 'https://unpkg.com/htm/preact/index.mjs?module';
function App(props) {
return html`<h1>Hello ${props.name}!</h1>`;
}
render(html`<${App} name="world" />`, document.getElementById('root'));
</script>
I'm able to get the client socket.io working if I by-pass the module system and rely on the window global (note: I'm serving the html file from an http server that's been passed to socket.io's server and have /socket.io/socket.io.js automatically installed):
<script src="/socket.io/socket.io.js"></script>
<!-- in previous module script: -->
const socket = window.io();
socket.on('connect', () => {
console.log('socket id>>', socket.id);
});
I would like to import the socket.io client from an ES module. I know I could encapsulate the window use in my own JS file and then import that in the module script in my html, but I'm trying to do this instead and it isn't working:
import io from '/socket.io.js'; // after copying socket.io.js to my public dir
import { io } from 'https://cdnjs.cloudflare.com/ajax/libs/engine.io-client/3.5.0/engine.io.js'; // I have tried several variations of urls and what is imported
Judging by:
Uncaught SyntaxError: The requested module '/socket.io.js' does not
provide an export named 'default'
and by the "polyfill"ed (if that's the correct term) commonjs globals like module in the probably-webpack bundle which is socket.io.js - it is likely that there just is no support for ES modules for this library.
I will probably be facing this issue over and over so I have decided to go back to having a build step.
My question is - is there a way around this "3rd party libraries not supporting ES module imports out of the box"? Maybe with a little work on my part I can get around this and try completing a small project without a build step in development.
Is there a way around this "3rd party libraries not supporting ES module imports out of the box"?
Unfortunately no, at least not always. It is impossible for a browser to natively import something that is not an ES module. However, if you've got access to the original source code, and the source code itself is written as an ES module (and then transpiled/bundled), you can sometimes build ES modules out of it yourself.
In the case of socket.io, their repo hosts the source code in TypeScript, which makes use of import/export. Unfortunately, their code also makes use of require() and other npm packages, which ultimately means it cannot be trivially converted to an ES module.
I have a working Flask app that I'm trying to refactor to use ES6 imports. I don't need it to run on old browsers, and ES6 imports work in modern browsers without transpilation, right?
I'm just running this via Flask's built-in server at the moment. The production app is served via gevent instead, but I'm obviously not at that point with these changes yet.
Below is what I've tried so far. Where have I gone wrong?
views.py
#app.route('/home')
def serve_home():
return render_template('home.html')
formatting.js
export function formatNumber(...) {
...
}
Attempt 1
home.html
<script type="text/javascript" src="/static/js/main.js"></script>
main.js
import {formatNumber} from "/static/js/formatting.js";
Error (main.js, line 1)
Uncaught SyntaxError: Unexpected token {
Attempt 2
Changed the script type to "module"
home.html
<script type="module" src="/static/js/main.js"></script>
Error (main.js, line 1)
Failed to load module script: The server responded with a non-JavaScript MIME type of "text/plain". Strict MIME type checking is enforced for module scripts per HTML spec.
Attempt 3
Changed the extension of each of the two Javascript files from "js" to "mjs"
home.html
<script type="module" src="/static/js/main.mjs"></script>
main.mjs
import {formatNumber} from "/static/js/formatting.mjs";
Error (main.mjs, line 1)
Failed to load module script: The server responded with a non-JavaScript MIME type of "application/octet-stream". Strict MIME type checking is enforced for module scripts per HTML spec.
For those of you getting the error:
The server responded with a non-JavaScript MIME type [...]
...you'll want to confirm python is returning the expected mimetype of your JS files.
>>> import mimetypes
>>> mimetypes.guess_type("notExists.js")
('text/javascript', None)
For myself, using a Windows platform to host the web server from (eg. Flask's development server), I found I needed to update the registry to associate the file extension with text/javascript.
For example, in the registry editor:
Under HKEY_CLASSES_ROOT, find .js (and .mjs if using that)
Look at the value for "Content Type". It must say text/javascript, NOT text/plain, or application/octet-stream, etc.
this worked for me:
import mimetypes
mimetypes.add_type('application/javascript', '.mjs')
added this code before launching flask
I'm pretty sure you will need to use webpack and babel to transpile your code.
There is a webpack plugin for Flask that might be of use https://pypi.org/project/Flask-Webpack/ https://github.com/nickjj/flask-webpack
You could also follow this https://itnext.io/a-template-for-creating-a-full-stack-web-application-with-flask-npm-webpack-and-reactjs-be2294b111bd or https://codeburst.io/creating-a-full-stack-web-application-with-python-npm-webpack-and-react-8925800503d9 tutorials. Just ignore the parts about react
This post ES6 build chain python backend (flask) not SPA seems to be a similar one to yours as well.
I don't need it to run on old browsers, and ES6 imports work in modern browsers without transpilation, right?
It depends on browser spectrum of your intended application users.
There is documented support for ES6 imports in modern web browsers.
See the list of supported browsers to be sure that you're viewing your application in a supported browser version.
The script extension ought to be .js and not .mjs as documented in the above link.
I noticed that the error reported is one that as to do with mimetype. Flask is returning an application/octet-stream for the mimetype of the static file where it is unable to guess the mimetype.
You can correct this using the url_for template function to build the url for the file pointing at a view that returns an appropriate mimetype.
<script type="module" src="{{ url_for('es6-static', filename='/js/main.js') }}"></script>
#app.route('/es6-static/<path:filename>')
def es6_static(filename):
return send_from_directory(app.config['ES6_MODULES'],
filename, as_attachment=True,
mimetype='text/javascript'
)
While text/javascript is a deprecated mime/type for JS resources, you may have better support for it in browsers.
I strongly suggest to use gunicorn or nginx to serve the static files as the above is only goes as far as helping with development.
Alright, I have looked on this site and have found several different answers, none of which have worked for me.
Basically had a js file that had many functions in it along with the main code for the app. I wanted to move all my functions to another js file so that I could clean up my code a little. I am fairly new to js but I know in python it was as simple as saying "import (module) as (nickname) from (path)"
anyways let's say I have a function named show message in my functions.js module.
export function show_message(){
alert("Hello");
}
and then I at the top of my main.js file I did
import { show_message } from './functions.js'
//I have also tried to import like this:
import * as func from './functions.js'
//And then I call it
show_message();
//I have also tried
func.show_message();
I know this is something simple, but as I said everywhere I have looked I have seen different answers, none of which work for me. I am using Firefox btw. I am also getting an error in the console saying that my import declarations need to be at the top of my module, I fixed that by specifying the type in my HTML link (script src="/static/main.js" type="module")
The error went away but is now saying "same origin policy disallows reading the remote resource at the file (path) (reason: cors request not HTTP)."
And the other error says "module source URI is not allowed in this document".
which makes me think maybe my syntax for importing is right and the error is in my HTML code?
Any help is appreciated.
0. The short answer
You need to install and run a local web server. - For a suggestion on how,
read on.
1. The basics
I tried a simple HTML file – index.html – as follows:
<!-- index.html - minimal HTML to keep it simple -->
<html>
<head>
<meta charset="UTF-8">
<link rel="shortcut icon" href="#">
</head>
<body>
<h1>Hello world!</h1>
<p>Experimenting with JavaScript modules.</p>
<script type="module" src="js/functions.js"></script>
</body>
</html>
In the subfolder js I put the JavaScript file functions.js:
// js/functions.js
alert('Hello');
When double-clicking index.html, my default web browser – Firefox 89.0
(64-bit) – shows the following, after pressing F12.
Notice how the JavaScript code is not running:
The error message:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at file:///C:/stackexchange/reproduce/jsModule/moduleNW/basics/js/functions.js. (Reason: CORS request not http).
A cheating "solution" is to (temporarily) remove type="module" from the HTML
code.
The alert then displays without errors.
But I want to run the JavaScript code as a module, so I put back
type="module" in the HTML.
2. Install and run a local web server
To run it as a module, it needs to run on a web server.
Thus, if you want to run the code on your own computer, you will need to
(install and) start a local web server.
One currently popular alternative is live-server.
Here is what worked for me.
Open a terminal. (On Windows: cmd.exe.)
Type npm and hit Enter to see if Node.js is installed.
If you get command not found, download at https://nodejs.org/en/download/
and install. 1
(On Ubuntu, you can try sudo apt install -y nodejs.)
Install live-server: npm install live-server -g.
Change directory to where your page lives: cd <path-to-index.html>.
Start the server: live-server .
(Should open localhost:8080 in your default browser and show the alert.
See below.)
Note 1.
I am on Windows 10, but the above instructions should work fine on Linux and
macOS too.
Note 2.
Here I used Firefox 89.0, but I have tried Google Chrome 91.0 as well.
The only notable difference is the CORS error message, which in Chrome reads:
Access to script at 'file:///C:/stackexchange/reproduce/jsModule/basics/js/functions.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.
3. Exporting and importing
Next I create a new folder demo2 containing the following demo2.html:
<!-- demo2.html - even shorter HTML for simplicity -->
<body>
<h1>Hello world!</h1>
<p>Javascript modules.</p>
<script type="module" src="js/main.js"></script>
</body>
I also create the following three JavaScript files in the subfolder js:
// js/module1.js
export function hi () { console.log('Hi from module 1.'); }
and
// js/module2.js
export function howdy () { console.log('Howdy from module 2!'); }
and
// js/main.js
import { hi } from './module1.js';
import { howdy } from './module2.js';
hi();
howdy();
Now I run live-server from the terminal in the folder where demo2.html
resides.
This time I start by typing
live-server --port=1234 --entry-file=demo2.html
and hitting Enter. Screenshot:
References:
Installing Node.js live-server
The live-server docs
Live-server can't find the file specified
Export and Import
1 On Windows 10, I once needed to
repair the installation.
On the script tag you are using to load the js in the browser you need to add the attribute
type="module"
It will look like the following:
<script type="module">
import {addTextToBody} from './utils.mjs';
addTextToBody('Modules are pretty cool.');
</script>
utils.mjs:
export function addTextToBody(text) {
const div = document.createElement('div');
div.textContent = text;
document.body.appendChild(div);
}
This is if you are not using a bundler like webpack and working directly in the browser.
Source of code: https://jakearchibald.com/2017/es-modules-in-browsers/
You might want to use broswerify instead. It allows you to write NodeJS-style modules and then compiles them into a single browser-friendly JavaScript file, allowing you to get all the performance benefits of loading only a single file. It also means you can easily use the same code both server side and client side.
If you want to stick with separate files, it looks like you are well on your way. Unlike regular JavaScript files, modules are subject to Cross-Origin Resource Sharing (CORS) restrictions. They have to be loaded from the same origin, and cannot be loaded from the local filesystem. If you are loading them from the local file system, move them to a server. If you are already hosting them on a server, add the Access-Control-Allow-Origin: * header to the response that serves the module file.
Lots more gotchas and solutions here and here.
function show_message(){
alert("Hello");
}
export { show_message };
and
import { show_message } from './functions'
i think this should do the trick. this is a named export/import technique. you can under this name find more information if you desire it.
Shortcut for Accepted answer
In case you are using Visual Studio Code just install the Live Preview extension by Microsoft.
In any HTML file click the Show preview icon. It will automatically run a local server and show up in the code editor. After every edit you make it refreshes. You can also show it in your default browser.
No need for command line anymore!
JavaScript has had modules for a long time. However, they were implemented via libraries, not built into the language i.e. you can't import or export part of those modules into your js files (whole library needs to be loaded). ES6 is the first time that JavaScript has built-in modules.
Please refer Here for more info about ES modules.
But things have changed and ES modules are now available in browsers! They're in…
Safari 10.1+, Chrome 61+, Firefox 60+, Edge 16+, etc,.
Now, you need to create your JS file using a new extension .mjs, like,
// utils.mjs
export function addTextToBody(text) {
const div = document.createElement('div');
div.textContent = text;
document.body.appendChild(div);
}
and then, you can import that file into your html page like,
<script type="module">
import {addTextToBody} from './utils.mjs';
addTextToBody('Modules are pretty cool.');
</script>
Please refer Here for more info about using ES module in browsers.
Consider going through this url some extension might be causing an issue with the loading of modules:
This blog might be an answer to what you're expecting.
You should first check if browser accepts type="module" and use fallback if it doesn't like this:
<script type="module" src="module.mjs"></script>
<script nomodule src="fallback.js"></script>
This might be the main reason for the CORS error as written here:
Unlike regular scripts, module scripts (and their imports) are fetched
with CORS. This means cross-origin module scripts must return valid
CORS headers such as Access-Control-Allow-Origin: *
So you need to add CORS header to the module file
Consider this blog for CORS issue. You should add CORS header ie. Access-Control-Allow-Origin: * to the server config most probably.
Using JS modules in the browser
On the web, you can tell browsers to treat a element as a module by setting the type attribute to module.
<script type="module" src="main.mjs"></script>
<script nomodule src="fallback.js"></script>
More on
https://developers.google.com/web/fundamentals/primers/modules
If you're using webpack and babel and want to import the code into your bundle, I guess it should be one of the following:
export default function show_message(){
alert("Hello");
}
and then in your code:
import show_message from 'path/to/show_message.js'
// or
import { default as someOtherName } from 'path/to/show_message.js'
Or if you'd like to export several functions:
const show_message = function(){
alert("Hello");
}
export { show_message };
and then in your code:
import { show_message } from 'path/to/show_message.js'
// or
import { show_message as someOtherName } from 'path/to/show_message.js'
Hope that helps.
I know this old thread but I just fixed this problem myself by using Parcel to launch my website Parcel index.html, in my situation I was using Live server and it didn't work until I switched to parcel .
Instead of using .js, try using .mjs.
Let's say your module file is /modules/App.js, just change it to /modules/App.mjs.
And ofcourse, make sure you have added type="module" in script tag, like this - <script type="module" src="./index.js" defer></script>
My folder structure -
index.html
index.js
modules/App.mjs
This worked for me!