JavaScript Modules
Introduced in ES6 / ES2015.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
https://tc39.es/ecma262/#sec-modules
https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/
https://hacks.mozilla.org/2015/08/es6-in-depth-modules/
How to write CommonJS exports that can be name-imported from ESM - https://2ality.com/2022/10/commonjs-named-exports.html
VSCode snipped for import: https://2ality.com/2017/08/typing-import-statements.html#a-code-snippet-for-faster-entry
Named
// sayHello.js
export function sayHello() {
alert('hello')
}
// main.js
import { sayHello } from './sayHello'
sayHello()
You can also have a single, grouped export:
function sayHello() {
alert('hello')
}
export { sayHello }
Default
// sayHello.js
export default function sayHello() {
alert('hello')
}
// main.js
import whateverName from './sayHello'
whateverName()
https://humanwhocodes.com/blog/2019/01/stop-using-default-exports-javascript-module/
https://basarat.gitbook.io/typescript/main-1/defaultisbad
Prohibit default exports with https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-default-export.md
Rename with as
// Api.js
const getX = () => 'x'
const getY = () => 'y'
export { getX, getY as getYSync }
// main.js
import { getX as getXSync, getYSync } from './Api'
getXSync()
getYSync()
Namespace or wildcard import (module object) - import * as React from 'react'
Creates a module object.
https://medium.com/unsplash/named-namespace-imports-7345212bbffb
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#creating_a_module_object
// Api.js
export const getX = () => 'x'
export const getY = () => 'y'
// main.js
import * as Api from './Api'
Api.getX()
Api.getY()
Issues:
- We can use any name, eg:
import * as ServerApi from './Api'
- Editors may not add these imports automatically
- https://github.com/import-js/eslint-plugin-import/issues/1547
- Namespace imports are only for two reasons: metaprogramming and laziness. They’re the entire reason “treeshaking” exists as a concept - because namespace imports encourage files that export many things, which encourages non-deep imports, which causes too much code to end up in bundles.
- Specifically, if you have a file that exports 20 things, and you do
import { three } from './twenty'
orimport \* as moreThanWeNeed from './twenty'
, a bundler will include by default all 20 things. Treeshaking is required to try to guess which of the other 19 it can delete/omit. If, instead, you have 20 files, each that export one thing, then no guessing is required and no omitting/deleting is required; you will have exactly the code you need in your bundle and no more.
Prohibit them with https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-namespace.md
export * as ns from "mod"
https://github.com/tc39/proposal-export-ns-from
https://github.com/tc39/ecma262/pull/1174
// Api.js
export const getX = () => 'x'
export const getY = () => 'y'
export * as Api from './Api'
// main.js
import { Api } from './Api'
Api.getX()
Api.getY()
Note that we can still import individually:
// main.js
import { Api, getX, getY } from './Api'
getX()
getY()
Api.getX()
Api.getY()
Re-export
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#aggregating_modules
https://javascript.info/import-export#re-export
Dynamic import
https://github.com/tc39/proposal-dynamic-import
Import maps
https://github.com/WICG/import-maps
https://www.honeybadger.io/blog/import-maps
Import assertions
https://github.com/tc39/proposal-import-assertions