Working with ES6 modules Flashcards
What are named exports?
If we put export
in front of a named entity inside a module, it becomes a named export of that module. All other entities are private to the module.
/* module1.mjs */ // Named exports export const name = 'John', surname = 'Doe'; export function getName() { return `${name} ${surname}`; }
“Named exports, named imports, namespace imports” (exploringjs.com). Retrieved July 30, 2024.
What are named imports?
Named imports are imports
done withing curly brakets import named exports?
/* module1.mjs */ // Named exports export const name = 'John', surname = 'Doe'; export function getName() { return `${name} ${surname}`; } /* module2.mjs */ // Named imports import {name, surname, getName} from './module1.mjs'; assert.equal(name, 'John'); assert.equal(getName(), 'John Doe');
The string after from
is called a module specifier. It identifies from which module we want to import.
“Named exports, named imports, namespace imports” (exploringjs.com). Retrieved July 30, 2024.
What are namespace imports?
namespace imports allow you to fetch all the named imports at once adding import * as [namespace]
/* module1.mjs */ // Named exports export const name = 'John', surname = 'Doe'; export function getName() { return `${name} ${surname}`; } /* module2.mjs */ // Namespace import import * as module from './module1.mjs'; assert.equal(module.name, 'John'); assert.equal(module.getName(), 'John Doe');
The string after from
is called a module specifier. It identifies from which module we want to import.
“Named exports, named imports, namespace imports” (exploringjs.com). Retrieved July 30, 2024.
What is a default export?
A default export is mainly used when a module only contains a single entity (even though it can be combined with named exports).
//===== lib2a.mjs ===== export default function getHello() { return 'hello'; }
A default export is the exception to the rule that function declarations always have names: In the previous example, we can omit the name getHello.
//===== lib2b.mjs ===== export default 123; // (A) instead of `const` There can be at most one default export. That’s why const or let can’t be default-exported (line A). //===== main2.mjs ===== import lib2a from './lib2a.mjs'; assert.equal(lib2a(), 'hello'); import lib2b from './lib2b.mjs'; assert.equal(lib2b, 123);
“Default exports and imports” (exploringjs.com). Retrieved September 10, 2024.
What are dynamic imports?
The import()
syntax, commonly called dynamic import, is a function-like expression that allows loading an ECMAScript module asynchronously and dynamically into a potentially non-module environment.
Unlike static imports, dynamic imports are only evaluated when needed, and permit greater syntactic flexibility.
import * as mod from "/my-module.js"; import("/my-module.js").then((mod2) => { console.log(mod === mod2); // true });
“import() - JavaScript | MDN” (MDN Web Docs). Retrieved September 10, 2024.
What is the syntax of static imports?
import defaultExport from "module-specifier"; import * as name from "module-specifier"; import { export1 } from "module-specifier"; import { export1 as alias1 } from "module-specifier"; import { default as alias } from "module-specifier"; import { export1, export2 } from "module-specifier"; import { export1, export2 as alias2, /* … */ } from "module-specifier"; import { "string name" as alias } from "module-specifier"; import defaultExport, { export1, /* … */ } from "module-specifier"; import defaultExport, * as name from "module-specifier"; import "module-specifier";
“Syntax” (MDN Web Docs). Retrieved September 10, 2024.
What is a module specifier?
The string after from
is called a module specifier. It identifies from which module we want to import.
“Named exports, named imports, namespace imports” (exploringjs.com). Retrieved September 10, 2024.
What are the various types of module specifiers?
There are three types of module specifiers:
Absolute specifiers are full URLs – for example:
'https://www.unpkg.com/browse/yargs@17.3.1/browser.mjs' 'file:///opt/nodejs/config.mjs'
Absolute specifiers are mostly used to access libraries that are directly hosted on the web.
Relative specifiers are relative URLs (starting with '/'
, './'
or '../'
) – for example:
'./sibling-module.js' '../module-in-parent-dir.mjs' '../../dir/other-module.js'
Relative specifiers are mostly used to access other modules within the same code base.
Bare specifiers are paths (without protocol and domain) that start with neither slashes nor dots. They begin with the names of packages (as installed via a package manager such npm). Those names can optionally be followed by subpaths:
'some-package' 'some-package/sync' 'some-package/util/files/path-tools.js'
Bare specifiers can also refer to packages with scoped names:
'@some-scope/scoped-name' '@some-scope/scoped-name/async' '@some-scope/scoped-name/dir/some-module.mjs'
Each bare specifier refers to exactly one module inside a package; if it has no subpath, it refers to the designated “main”
module of its package.
“Kinds of module specifiers” (exploringjs.com). Retrieved September 10, 2024.
What is a default import?
Default exports need to be imported with the corresponding default import syntax. The simplest version directly imports the default:
import myDefault from "/modules/my-module.js";
Since the default export doesn’t explicitly specify a name, you can give the identifier any name you like.
It is also possible to specify a default import with namespace imports or named imports. In such cases, the default import will have to be declared first. For instance:
import myDefault, * as myModule from "/modules/my-module.js"; // myModule.default and myDefault point to the same binding
or
import myDefault, { foo, bar } from "/modules/my-module.js";
Importing a name called default
has the same effect as a default import. It is necessary to alias the name because default is a reserved word.
import { default as myDefault } from "/modules/my-module.js";
“Default import” (MDN Web Docs). Retrieved September 11, 2024.
What is re-exporting?
A module library.mjs
can export one or more exports of another module internal.mjs
as if it had made them itself. That is called re-exporting.
//===== internal.mjs ===== export function internalFunc() {} export const INTERNAL_DEF = 'hello'; export default 123; //===== library.mjs ===== // Named re-export [ES6] export {internalFunc as func, INTERNAL_DEF as DEF} from './internal.mjs'; // Wildcard re-export [ES6] export * from './internal.mjs'; // Namespace re-export [ES2020] export * as ns from './internal.mjs';
The wildcard re-export turns all exports of module internal.mjs
into exports of library.mjs
, except the default
export.
The namespace re-export turns all exports of module internal.mjs
into an object that becomes the named export ns
of library.mjs
. Because internal.mjs
has a default
export, ns
has a property .default
.
“Re-exporting” (exploringjs.com). Retrieved September 11, 2024.
Explain why imports are read-only views on exports
There are two benefits to handling imports this way:
- It is easier to split modules because previously shared variables can become exports.
- This behavior is crucial for supporting transparent cyclic imports.
Consider the following two modules:
counter.mjs
main.mjs
counter.mjs
exports a (mutable!) variable and a function:
export let counter = 3; export function incCounter() { counter++; }
main.mjs
name-imports both exports. When we use incCounter()
, we discover that the connection to counter
is live – we can always access the live state of that variable:
import { counter, incCounter } from './counter.mjs'; // The imported value `counter` is live assert.equal(counter, 3); incCounter(); assert.equal(counter, 4);
Note that while the connection is live and we can read counter, we cannot change this variable (e.g., via counter++
).
“Imports are read-only views on exports” (exploringjs.com). Retrieved September 12, 2024.
List ESM and commonJs module specifier differences
All specifiers, except bare paths, must refer to actual files. That is, ESM does not support the following CommonJS features:
- CommonJS automatically adds missing filename extensions.
- CommonJS can import a directory dir if there is a dir/package.json with a “main” property.
- CommonJS can import a directory dir if there is a module dir/index.js.
“ES module specifiers on Node.js” (exploringjs.com). Retrieved September 13, 2024.
List and explain Node.js supported module extensions
Node.js supports the following default filename extensions:
-
.mjs
for ES modules -
.cjs
for CommonJS modules
The filename extension .js
stands for either ESM or CommonJS. Which one it is is configured via the “closest” package.json
(in the current directory, the parent directory, etc.). Using package.json in this manner is independent of packages.
In that package.json, there is a property "type"
, which has two settings:
-
"commonjs"
(the default): files with the extension .js or without an extension are interpreted as CommonJS modules. -
"module"
: files with the extension .js or without an extension are interpreted as ESM modules.
“Filename extensions on Node.js” (exploringjs.com). Retrieved September 13, 2024.
What does import.meta
hold?
The object import.meta
holds metadata for the current module.
import.meta.url
- contains a string with the URL of the current module’s file – for example:
'https://example.com/code/main.mjs'
On Node.js, import.meta.url
is always a string with a file: URL
– for example:
'file:///Users/carpasse/code/main.mjs'
“import.meta.url
” (exploringjs.com). Retrieved September 16, 2024.
How can you get a URL instance that points to a file data.txt
that sits next to the current module?
When working with import.meta.url, URL
constructor is especially useful:
new URL(input: string, base?: string|URL)
Parameter input
contains the URL to be parsed. It can be relative if the second parameter, base
, is provided.
In other words, this constructor lets us resolve a relative path against a base URL:
> new URL('other.mjs', 'https://example.com/code/main.mjs').href 'https://example.com/code/other.mjs' > new URL('../other.mjs', 'https://example.com/code/main.mjs').href 'https://example.com/other.mjs'
This is how we get a URL instance that points to a file data.txt that sits next to the current module:
const urlOfData = new URL('data.txt', import.meta.url);
“import.meta.url and class URL” (exploringjs.com). Retrieved September 16, 2024.