Node.JS 10 added experimental support for loading ES6 modules, which already work in browsers. That would mean that we could finally use exactly the same files for Node.JS and browsers without any transpiling or polyfills.
Except we can’t. Node.js requires .mjs
extension for files to be loaded as modules. I tried tricking node by using a symlink, but node got around it:
D:weblines>node --experimental-modules ES6test.mjs (node:7464) ExperimentalWarning: The ESM module loader is experimental. D:weblinesES6test.js:6 import myLibrary from "./MyFile.mjs"; ^^^^^^^^^^^^^^^
I can’t think of any other workaround to make this work – which really renders the whole ES6 module support useless.
Can anybody else think of some trick to make Node.js ignore the extension?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
You can now import .js
file in node v12.x, in 2 steps:
- Add the following line in your
package.json
file:
// package.json
{
"type": "module"
}
- Add
--experimental-modules
flag before the script:
node --experimental-modules index.js
Reference: https://nodejs.org/api/esm.html
Method 2
Node.js requires all ES modules should have .mjs extension. Since Node.js support of ES modules is experimental, this is subject to change. A proposal and open pull request are expected to address this problem with package.json esm
flag and --mode
option.
Currently this can be solved with custom ES module loader that hooks into default module resolver and changes module type for some modules:
custom-loader.mjs
import path from 'path'; const ESM_WITH_JS_EXT = './MyFile.js'; // relative to loader path const ESM_WITH_JS_EXT_URL = new URL(path.dirname(import.meta.url) + `/${ESM_WITH_JS_EXT}`).href; export function resolve(specifier, parentModuleURL, defaultResolver) { const resolvedModule = defaultResolver(specifier, parentModuleURL); if (resolvedModule.url === ESM_WITH_JS_EXT_URL) resolvedModule.format = 'esm'; return resolvedModule; }
It is used as:
node --experimental-modules --loader ./custom-loader.mjs ./index.mjs
Since there are fundamental differences in how ES and CommonJS modules are evaluated, the changes should be limited to modules that need them.
Method 3
I solved exactly this problem with the fabulous esm package. You can enable dynamic (smart) esm module loading package wide, or per run with a flag like this:
node -r esm your/es6/module.js
It also has options to treat every file as a es6 module, or only those ending in ‘.mjs’. There are other packages out there, but this one just worked.
Method 4
Import and export modules using ES6 that work with Node.js
Name files with .mjs
extension instead of .js
Create files
touch main.mjs lib.mjs
main.js
import { add } from './lib.mjs'; console.log(add(40, 2));
lib.mjs
export let add = (x,y) => { return x + y }
Run
node --experimental-modules main.js
Method 5
Here is a module that does what you need esmjs.mjs
import { readFileSync } from 'fs'
import { fileURLToPath, pathToFileURL } from 'url'
import { dirname, join } from 'path'
export const jsmodule = (test_url_or_path, module_path) => {
const __filename = test_url_or_path.toLowerCase().startsWith('file:')
? fileURLToPath(test_url_or_path)
: test_url_or_path
const __dirname = dirname(__filename)
const abs_path = join(__dirname, module_path)
const file_url = pathToFileURL(abs_path)
const file_buf = readFileSync(file_url)
const b64 = file_buf.toString('base64')
const moduleData = "data:text/javascript;base64," + b64
return import(moduleData)
}
Usage from .mjs
module:
const { hey } = await jsmodule(import.meta.url, '../../test-data/mjs.js')
Usage, from .js
file:
const { hey } = await jsmodule(__filename, '../../test-data/mjs.js')
Method 6
You can do it in this way:
Create a file module2.mjs (the name is up to you)
'use strict'; export function foo() { return 'foo'; }
Create index.mjs file:
'use strict'; import { foo } from './module2.mjs'; console.log(foo());
Using node 8.11.1 (or 10) you can run it as (command and output provided):
node --experimental-modules index.mjs (node:60428) ExperimentalWarning: The ESM module loader is experimental. foo
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0