Skip to content

Instantly share code, notes, and snippets.

@robin-a-meade
Last active August 21, 2022 04:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robin-a-meade/4ba7867a771de384b66b512d1847de61 to your computer and use it in GitHub Desktop.
Save robin-a-meade/4ba7867a771de384b66b512d1847de61 to your computer and use it in GitHub Desktop.
How to use ESM modules with NodeJS

How to use ESM modules with NodeJS

Excerise 1: Clone the gist and run the examples

  1. Create a temporary directory and cd into it
  2. Clone the gist
  3. Run the examples
cd "$(mktemp -d)"
git clone git@gist.github.com:4ba7867a771de384b66b512d1847de61.git .
node index.mjs
node index_dynamic.mjs

Excerise 2: Change .mjs to .js

  1. Change the extension of all three .mjs files to .js
  2. Edit index.js and index.mjs to update the name of the imported file to "./hello.js"
  3. Attempt to run index.js and index_dynamic.js. Note the warning and error message:
    Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
    SyntaxError: Cannot use import statement outside a module
    
  4. Add "type": "module" to package.json
  5. Run index.js and index_dynamic.js. This time they should work.

Excerise 3: Edit index_dynamic.js to use alternative

  1. Revert back to the original
    git reset --hard
    git clean -f
    
  2. Rename index_dynamic.mjs to index_dynamic.js
  3. Edit index_dynamic.js to use the alternative code.
    1. Comment out the top code
    2. Uncomment the bottom code
  4. Run index_dynamic.js
    node index_dyanmic.js
    
    It should work.

Key Takeaway

Using an .mjs extension or adding "type": "module" is required, except if you use dynamic import within an async function.

export default function hello() { console.log("Hello, World!"); }
// For this to work, either:
//
// 1. We must give this file a .mjs extension
//
// 2. We must add "type": "module" to package.json
import f from "./hello.mjs"; // Import default export into the current scope as 'f'
f(); // Prints "Hello, World!'
// This works, but requires either:
//
// 1. Add "type": "module" to package.json
//
// 2. Change this file's extension from .js to .mjs
//
// or we'll get error:
//
// Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
// SyntaxError: Cannot use import statement outside a module
// DYNAMIC IMPORT
const { default: f } = await import("./hello.mjs");
console.log(f());
// NOTE: Our use of `await` above requires that we either
//
// a) add "type": "module" to the package.json, or
//
// b) give this file a .mjs extension
//
// or we'll get this error:
// const greetingModule = await import("./greeting.mjs");
// ^^^^^
//
// SyntaxError: await is only valid in async functions and the top level bodies of modules
// ALTERNATIVELY
// Alternatively, the following approach works without needing to add "type": "module" or changing the extension to .mjs:
// (async () => {
// const { default: greeting } = await import("./hello.mjs");
// console.log(greeting());
// })();
{
"name": "blahblah",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment