1486 lines
90 KiB
HTML
1486 lines
90 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width">
|
||
<meta name="nodejs.org:node-version" content="v12.22.12">
|
||
<title>Modules: ECMAScript modules | Node.js v12.22.12 Documentation</title>
|
||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:400,700,400italic&display=fallback">
|
||
<link rel="stylesheet" href="assets/style.css">
|
||
<link rel="stylesheet" href="assets/hljs.css">
|
||
<link rel="canonical" href="https://nodejs.org/api/esm.html">
|
||
</head>
|
||
<body class="alt apidoc" id="api-section-esm">
|
||
<div id="content" class="clearfix">
|
||
<div id="column2" class="interior">
|
||
<div id="intro" class="interior">
|
||
<a href="/" title="Go back to the home page">
|
||
Node.js
|
||
</a>
|
||
</div>
|
||
<ul>
|
||
<li><a href="documentation.html" class="nav-documentation">About this documentation</a></li>
|
||
<li><a href="synopsis.html" class="nav-synopsis">Usage and example</a></li>
|
||
</ul>
|
||
<hr class="line">
|
||
<ul>
|
||
<li><a href="assert.html" class="nav-assert">Assertion testing</a></li>
|
||
<li><a href="async_hooks.html" class="nav-async_hooks">Async hooks</a></li>
|
||
<li><a href="buffer.html" class="nav-buffer">Buffer</a></li>
|
||
<li><a href="addons.html" class="nav-addons">C++ Addons</a></li>
|
||
<li><a href="n-api.html" class="nav-n-api">C/C++ Addons with N-API</a></li>
|
||
<li><a href="embedding.html" class="nav-embedding">C++ Embedder API</a></li>
|
||
<li><a href="child_process.html" class="nav-child_process">Child Processes</a></li>
|
||
<li><a href="cluster.html" class="nav-cluster">Cluster</a></li>
|
||
<li><a href="cli.html" class="nav-cli">Command line options</a></li>
|
||
<li><a href="console.html" class="nav-console">Console</a></li>
|
||
<li><a href="crypto.html" class="nav-crypto">Crypto</a></li>
|
||
<li><a href="debugger.html" class="nav-debugger">Debugger</a></li>
|
||
<li><a href="deprecations.html" class="nav-deprecations">Deprecated APIs</a></li>
|
||
<li><a href="dns.html" class="nav-dns">DNS</a></li>
|
||
<li><a href="domain.html" class="nav-domain">Domain</a></li>
|
||
<li><a href="errors.html" class="nav-errors">Errors</a></li>
|
||
<li><a href="events.html" class="nav-events">Events</a></li>
|
||
<li><a href="fs.html" class="nav-fs">File system</a></li>
|
||
<li><a href="globals.html" class="nav-globals">Globals</a></li>
|
||
<li><a href="http.html" class="nav-http">HTTP</a></li>
|
||
<li><a href="http2.html" class="nav-http2">HTTP/2</a></li>
|
||
<li><a href="https.html" class="nav-https">HTTPS</a></li>
|
||
<li><a href="inspector.html" class="nav-inspector">Inspector</a></li>
|
||
<li><a href="intl.html" class="nav-intl">Internationalization</a></li>
|
||
<li><a href="modules.html" class="nav-modules">Modules: CommonJS modules</a></li>
|
||
<li><a href="esm.html" class="nav-esm active">Modules: ECMAScript modules</a></li>
|
||
<li><a href="module.html" class="nav-module">Modules: <code>module</code> API</a></li>
|
||
<li><a href="packages.html" class="nav-packages">Modules: Packages</a></li>
|
||
<li><a href="net.html" class="nav-net">Net</a></li>
|
||
<li><a href="os.html" class="nav-os">OS</a></li>
|
||
<li><a href="path.html" class="nav-path">Path</a></li>
|
||
<li><a href="perf_hooks.html" class="nav-perf_hooks">Performance hooks</a></li>
|
||
<li><a href="policy.html" class="nav-policy">Policies</a></li>
|
||
<li><a href="process.html" class="nav-process">Process</a></li>
|
||
<li><a href="punycode.html" class="nav-punycode">Punycode</a></li>
|
||
<li><a href="querystring.html" class="nav-querystring">Query strings</a></li>
|
||
<li><a href="readline.html" class="nav-readline">Readline</a></li>
|
||
<li><a href="repl.html" class="nav-repl">REPL</a></li>
|
||
<li><a href="report.html" class="nav-report">Report</a></li>
|
||
<li><a href="stream.html" class="nav-stream">Stream</a></li>
|
||
<li><a href="string_decoder.html" class="nav-string_decoder">String decoder</a></li>
|
||
<li><a href="timers.html" class="nav-timers">Timers</a></li>
|
||
<li><a href="tls.html" class="nav-tls">TLS/SSL</a></li>
|
||
<li><a href="tracing.html" class="nav-tracing">Trace events</a></li>
|
||
<li><a href="tty.html" class="nav-tty">TTY</a></li>
|
||
<li><a href="dgram.html" class="nav-dgram">UDP/datagram</a></li>
|
||
<li><a href="url.html" class="nav-url">URL</a></li>
|
||
<li><a href="util.html" class="nav-util">Utilities</a></li>
|
||
<li><a href="v8.html" class="nav-v8">V8</a></li>
|
||
<li><a href="vm.html" class="nav-vm">VM</a></li>
|
||
<li><a href="wasi.html" class="nav-wasi">WASI</a></li>
|
||
<li><a href="worker_threads.html" class="nav-worker_threads">Worker threads</a></li>
|
||
<li><a href="zlib.html" class="nav-zlib">Zlib</a></li>
|
||
</ul>
|
||
<hr class="line">
|
||
<ul>
|
||
<li><a href="https://github.com/nodejs/node" class="nav-https-github-com-nodejs-node">Code repository and issue tracker</a></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div id="column1" data-id="esm" class="interior">
|
||
<header>
|
||
<h1>Node.js v12.22.12 Documentation</h1>
|
||
<div id="gtoc">
|
||
<ul>
|
||
<li>
|
||
<a href="index.html">Index</a>
|
||
</li>
|
||
<li>
|
||
<a href="all.html">View on single page</a>
|
||
</li>
|
||
<li>
|
||
<a href="esm.json">View as JSON</a>
|
||
</li>
|
||
|
||
<li class="version-picker">
|
||
<a href="#">View another version <span>▼</span></a>
|
||
<ol class="version-picker"><li><a href="https://nodejs.org/docs/latest-v17.x/api/esm.html">17.x</a></li>
|
||
<li><a href="https://nodejs.org/docs/latest-v16.x/api/esm.html">16.x <b>LTS</b></a></li>
|
||
<li><a href="https://nodejs.org/docs/latest-v15.x/api/esm.html">15.x</a></li>
|
||
<li><a href="https://nodejs.org/docs/latest-v14.x/api/esm.html">14.x <b>LTS</b></a></li>
|
||
<li><a href="https://nodejs.org/docs/latest-v13.x/api/esm.html">13.x</a></li>
|
||
<li><a href="https://nodejs.org/docs/latest-v12.x/api/esm.html">12.x <b>LTS</b></a></li>
|
||
<li><a href="https://nodejs.org/docs/latest-v11.x/api/esm.html">11.x</a></li>
|
||
<li><a href="https://nodejs.org/docs/latest-v10.x/api/esm.html">10.x</a></li>
|
||
<li><a href="https://nodejs.org/docs/latest-v9.x/api/esm.html">9.x</a></li>
|
||
<li><a href="https://nodejs.org/docs/latest-v8.x/api/esm.html">8.x</a></li></ol>
|
||
</li>
|
||
|
||
<li class="edit_on_github"><a href="https://github.com/nodejs/node/edit/master/doc/api/esm.md"><span class="github_icon"><svg height="16" width="16" viewBox="0 0 16.1 16.1" fill="currentColor"><path d="M8 0a8 8 0 0 0-2.5 15.6c.4 0 .5-.2.5-.4v-1.5c-2 .4-2.5-.5-2.7-1 0-.1-.5-.9-.8-1-.3-.2-.7-.6 0-.6.6 0 1 .6 1.2.8.7 1.2 1.9 1 2.4.7 0-.5.2-.9.5-1-1.8-.3-3.7-1-3.7-4 0-.9.3-1.6.8-2.2 0-.2-.3-1 .1-2 0 0 .7-.3 2.2.7a7.4 7.4 0 0 1 4 0c1.5-1 2.2-.8 2.2-.8.5 1.1.2 2 .1 2.1.5.6.8 1.3.8 2.2 0 3-1.9 3.7-3.6 4 .3.2.5.7.5 1.4v2.2c0 .2.1.5.5.4A8 8 0 0 0 16 8a8 8 0 0 0-8-8z"/></svg></span>Edit on GitHub</a></li>
|
||
</ul>
|
||
</div>
|
||
<hr>
|
||
</header>
|
||
|
||
<div id="toc">
|
||
<h2>Table of Contents</h2>
|
||
<ul>
|
||
<li><a href="#esm_modules_ecmascript_modules">Modules: ECMAScript modules</a>
|
||
<ul>
|
||
<li><a href="#esm_introduction">Introduction</a></li>
|
||
<li><a href="#esm_enabling">Enabling</a></li>
|
||
<li><a href="#esm_packages">Packages</a></li>
|
||
<li><a href="#esm_import_specifiers"><code>import</code> Specifiers</a>
|
||
<ul>
|
||
<li><a href="#esm_terminology">Terminology</a>
|
||
<ul>
|
||
<li><a href="#esm_node_imports"><code>node:</code> Imports</a></li>
|
||
<li><a href="#esm_data_imports"><code>data:</code> Imports</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#esm_import_meta"><code>import.meta</code></a></li>
|
||
<li><a href="#esm_differences_between_es_modules_and_commonjs">Differences between ES modules and CommonJS</a>
|
||
<ul>
|
||
<li><a href="#esm_mandatory_file_extensions">Mandatory file extensions</a></li>
|
||
<li><a href="#esm_no_node_path">No <code>NODE_PATH</code></a></li>
|
||
<li><a href="#esm_no_require_exports_module_exports_filename_dirname">No <code>require</code>, <code>exports</code>, <code>module.exports</code>, <code>__filename</code>, <code>__dirname</code></a></li>
|
||
<li><a href="#esm_no_require_resolve">No <code>require.resolve</code></a></li>
|
||
<li><a href="#esm_no_require_extensions">No <code>require.extensions</code></a></li>
|
||
<li><a href="#esm_no_require_cache">No <code>require.cache</code></a></li>
|
||
<li><a href="#esm_url_based_paths">URL-based paths</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#esm_interoperability_with_commonjs">Interoperability with CommonJS</a>
|
||
<ul>
|
||
<li><a href="#esm_require"><code>require</code></a></li>
|
||
<li><a href="#esm_import_statements"><code>import</code> statements</a></li>
|
||
<li><a href="#esm_import_expressions"><code>import()</code> expressions</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#esm_commonjs_namespaces">CommonJS Namespaces</a></li>
|
||
<li><a href="#esm_builtin_modules">Builtin modules</a></li>
|
||
<li><a href="#esm_commonjs_json_and_native_modules">CommonJS, JSON, and native modules</a></li>
|
||
<li><a href="#esm_experimental_json_modules">Experimental JSON modules</a></li>
|
||
<li><a href="#esm_experimental_wasm_modules">Experimental Wasm modules</a></li>
|
||
<li><a href="#esm_experimental_loaders">Experimental loaders</a>
|
||
<ul>
|
||
<li><a href="#esm_hooks">Hooks</a>
|
||
<ul>
|
||
<li><a href="#esm_resolve_specifier_context_defaultresolve"><code>resolve(specifier, context, defaultResolve)</code></a></li>
|
||
<li><a href="#esm_getformat_url_context_defaultgetformat"><code>getFormat(url, context, defaultGetFormat)</code></a></li>
|
||
<li><a href="#esm_getsource_url_context_defaultgetsource"><code>getSource(url, context, defaultGetSource)</code></a></li>
|
||
<li><a href="#esm_transformsource_source_context_defaulttransformsource"><code>transformSource(source, context, defaultTransformSource)</code></a></li>
|
||
<li><a href="#esm_getglobalpreloadcode"><code>getGlobalPreloadCode()</code></a></li>
|
||
<li><a href="#esm_code_dynamicinstantiate_code_hook"><code>dynamicInstantiate</code> hook</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#esm_examples">Examples</a>
|
||
<ul>
|
||
<li><a href="#esm_https_loader">HTTPS loader</a></li>
|
||
<li><a href="#esm_transpiler_loader">Transpiler loader</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#esm_resolution_algorithm">Resolution algorithm</a>
|
||
<ul>
|
||
<li><a href="#esm_features">Features</a></li>
|
||
<li><a href="#esm_resolver_algorithm">Resolver algorithm</a></li>
|
||
<li><a href="#esm_resolver_algorithm_specification">Resolver Algorithm Specification</a></li>
|
||
<li><a href="#esm_customizing_esm_specifier_resolution_algorithm">Customizing ESM specifier resolution algorithm</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div id="apicontent">
|
||
<h1>Modules: ECMAScript modules<span><a class="mark" href="#esm_modules_ecmascript_modules" id="esm_modules_ecmascript_modules">#</a></span></h1>
|
||
|
||
|
||
<div class="api_metadata">
|
||
<details class="changelog"><summary>History</summary>
|
||
<table>
|
||
<tbody><tr><th>Version</th><th>Changes</th></tr>
|
||
<tr><td>v12.22.0</td>
|
||
<td><p>Stabilize modules implementation.</p></td></tr>
|
||
<tr><td>v12.20.0</td>
|
||
<td><p>Support for detection of CommonJS named exports.</p></td></tr>
|
||
<tr><td>v12.20.0</td>
|
||
<td><p>Remove experimental modules warning.</p></td></tr>
|
||
<tr><td>v12.17.0</td>
|
||
<td><p>Loading ECMAScript modules no longer requires a command-line flag.</p></td></tr>
|
||
<tr><td>v12.0.0</td>
|
||
<td><p>Add support for ES modules using <code>.js</code> file extension via <code>package.json</code> <code>"type"</code> field.</p></td></tr>
|
||
<tr><td>v8.5.0</td>
|
||
<td><p><span>Added in: v8.5.0</span></p></td></tr>
|
||
</tbody></table>
|
||
</details>
|
||
</div>
|
||
<p></p><div class="api_stability api_stability_2"><a href="documentation.html#documentation_stability_index">Stability: 2</a> - Stable</div><p></p>
|
||
<h2>Introduction<span><a class="mark" href="#esm_introduction" id="esm_introduction">#</a></span></h2>
|
||
|
||
<p>ECMAScript modules are <a href="https://tc39.github.io/ecma262/#sec-modules">the official standard format</a> to package JavaScript
|
||
code for reuse. Modules are defined using a variety of <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import"><code>import</code></a> and
|
||
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export"><code>export</code></a> statements.</p>
|
||
<p>The following example of an ES module exports a function:</p>
|
||
<pre><code class="language-js"><span class="hljs-comment">// addTwo.mjs</span>
|
||
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addTwo</span>(<span class="hljs-params">num</span>) </span>{
|
||
<span class="hljs-keyword">return</span> num + <span class="hljs-number">2</span>;
|
||
}
|
||
|
||
<span class="hljs-keyword">export</span> { addTwo };</code></pre>
|
||
<p>The following example of an ES module imports the function from <code>addTwo.mjs</code>:</p>
|
||
<pre><code class="language-js"><span class="hljs-comment">// app.mjs</span>
|
||
<span class="hljs-keyword">import</span> { addTwo } <span class="hljs-keyword">from</span> <span class="hljs-string">'./addTwo.mjs'</span>;
|
||
|
||
<span class="hljs-comment">// Prints: 6</span>
|
||
<span class="hljs-built_in">console</span>.log(addTwo(<span class="hljs-number">4</span>));</code></pre>
|
||
<p>Node.js fully supports ECMAScript modules as they are currently specified and
|
||
provides interoperability between them and its original module format,
|
||
<a href="modules.html">CommonJS</a>.</p>
|
||
<!-- Anchors to make sure old links find a target -->
|
||
<p><i id="#esm_package_json_type_field"></i>
|
||
<i id="#esm_package_scope_and_file_extensions"></i>
|
||
<i id="#esm_input_type_flag"></i></p>
|
||
<h2>Enabling<span><a class="mark" href="#esm_enabling" id="esm_enabling">#</a></span></h2>
|
||
|
||
<p>Node.js treats JavaScript code as CommonJS modules by default.
|
||
Authors can tell Node.js to treat JavaScript code as ECMAScript modules
|
||
via the <code>.mjs</code> file extension, the <code>package.json</code> <a href="packages.html#packages_type"><code>"type"</code></a> field, or the
|
||
<code>--input-type</code> flag. See
|
||
<a href="packages.html#packages_determining_module_system">Modules: Packages</a> for more
|
||
details.</p>
|
||
<!-- Anchors to make sure old links find a target -->
|
||
<p><i id="esm_package_entry_points"></i>
|
||
<i id="esm_main_entry_point_export"></i>
|
||
<i id="esm_subpath_exports"></i>
|
||
<i id="esm_package_exports_fallbacks"></i>
|
||
<i id="esm_exports_sugar"></i>
|
||
<i id="esm_conditional_exports"></i>
|
||
<i id="esm_nested_conditions"></i>
|
||
<i id="esm_self_referencing_a_package_using_its_name"></i>
|
||
<i id="esm_internal_package_imports"></i>
|
||
<i id="esm_dual_commonjs_es_module_packages"></i>
|
||
<i id="esm_dual_package_hazard"></i>
|
||
<i id="esm_writing_dual_packages_while_avoiding_or_minimizing_hazards"></i>
|
||
<i id="esm_approach_1_use_an_es_module_wrapper"></i>
|
||
<i id="esm_approach_2_isolate_state"></i></p>
|
||
<h2>Packages<span><a class="mark" href="#esm_packages" id="esm_packages">#</a></span></h2>
|
||
<p>This section was moved to <a href="packages.html">Modules: Packages</a>.</p>
|
||
<h2><code>import</code> Specifiers<span><a class="mark" href="#esm_import_specifiers" id="esm_import_specifiers">#</a></span></h2>
|
||
<h3>Terminology<span><a class="mark" href="#esm_terminology" id="esm_terminology">#</a></span></h3>
|
||
<p>The <em>specifier</em> of an <code>import</code> statement is the string after the <code>from</code> keyword,
|
||
e.g. <code>'path'</code> in <code>import { sep } from 'path'</code>. Specifiers are also used in
|
||
<code>export from</code> statements, and as the argument to an <code>import()</code> expression.</p>
|
||
<p>There are four types of specifiers:</p>
|
||
<ul>
|
||
<li>
|
||
<p><em>Bare specifiers</em> like <code>'some-package'</code>. They refer to an entry point of a
|
||
package by the package name.</p>
|
||
</li>
|
||
<li>
|
||
<p><em>Deep import specifiers</em> like <code>'some-package/lib/shuffle.mjs'</code>. They refer to
|
||
a path within a package prefixed by the package name.</p>
|
||
</li>
|
||
<li>
|
||
<p><em>Relative specifiers</em> like <code>'./startup.js'</code> or <code>'../config.mjs'</code>. They refer
|
||
to a path relative to the location of the importing file.</p>
|
||
</li>
|
||
<li>
|
||
<p><em>Absolute specifiers</em> like <code>'file:///opt/nodejs/config.js'</code>. They refer
|
||
directly and explicitly to a full path.</p>
|
||
</li>
|
||
</ul>
|
||
<p>Bare specifiers, and the bare specifier portion of deep import specifiers, are
|
||
strings; but everything else in a specifier is a URL.</p>
|
||
<p><code>file:</code>, <code>node:</code>, and <code>data:</code> URLs are supported. A specifier like
|
||
<code>'https://example.com/app.js'</code> may be supported by browsers but it is not
|
||
supported in Node.js.</p>
|
||
<p>Specifiers may not begin with <code>/</code> or <code>//</code>. These are reserved for potential
|
||
future use. The root of the current volume may be referenced via <code>file:///</code>.</p>
|
||
<h4><code>node:</code> Imports<span><a class="mark" href="#esm_node_imports" id="esm_node_imports">#</a></span></h4>
|
||
<div class="api_metadata">
|
||
<span>Added in: v12.20.0</span>
|
||
</div>
|
||
<p><code>node:</code> URLs are supported as a means to load Node.js builtin modules. This
|
||
URL scheme allows for builtin modules to be referenced by valid absolute URL
|
||
strings.</p>
|
||
<pre><code class="language-js"><span class="hljs-keyword">import</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">'node:fs/promises'</span>;</code></pre>
|
||
<h4><code>data:</code> Imports<span><a class="mark" href="#esm_data_imports" id="esm_data_imports">#</a></span></h4>
|
||
<div class="api_metadata">
|
||
<span>Added in: v12.10.0</span>
|
||
</div>
|
||
<p><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs"><code>data:</code> URLs</a> are supported for importing with the following MIME types:</p>
|
||
<ul>
|
||
<li><code>text/javascript</code> for ES Modules</li>
|
||
<li><code>application/json</code> for JSON</li>
|
||
<li><code>application/wasm</code> for Wasm</li>
|
||
</ul>
|
||
<p><code>data:</code> URLs only resolve <a href="#esm_terminology"><em>Bare specifiers</em></a> for builtin modules
|
||
and <a href="#esm_terminology"><em>Absolute specifiers</em></a>. Resolving
|
||
<a href="#esm_terminology"><em>Relative specifiers</em></a> does not work because <code>data:</code> is not a
|
||
<a href="https://url.spec.whatwg.org/#special-scheme">special scheme</a>. For example, attempting to load <code>./foo</code>
|
||
from <code>data:text/javascript,import "./foo";</code> fails to resolve because there
|
||
is no concept of relative resolution for <code>data:</code> URLs. An example of a <code>data:</code>
|
||
URLs being used is:</p>
|
||
<pre><code class="language-js"><span class="hljs-keyword">import</span> <span class="hljs-string">'data:text/javascript,console.log("hello!");'</span>;
|
||
<span class="hljs-keyword">import</span> _ <span class="hljs-keyword">from</span> <span class="hljs-string">'data:application/json,"world!"'</span>;</code></pre>
|
||
<h2><code>import.meta</code><span><a class="mark" href="#esm_import_meta" id="esm_import_meta">#</a></span></h2>
|
||
<ul>
|
||
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object" class="type"><Object></a></li>
|
||
</ul>
|
||
<p>The <code>import.meta</code> metaproperty is an <code>Object</code> that contains the following
|
||
property:</p>
|
||
<ul>
|
||
<li><code>url</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string></a> The absolute <code>file:</code> URL of the module.</li>
|
||
</ul>
|
||
<h2>Differences between ES modules and CommonJS<span><a class="mark" href="#esm_differences_between_es_modules_and_commonjs" id="esm_differences_between_es_modules_and_commonjs">#</a></span></h2>
|
||
<h3>Mandatory file extensions<span><a class="mark" href="#esm_mandatory_file_extensions" id="esm_mandatory_file_extensions">#</a></span></h3>
|
||
<p>A file extension must be provided when using the <code>import</code> keyword. Directory
|
||
indexes (e.g. <code>'./startup/index.js'</code>) must also be fully specified.</p>
|
||
<p>This behavior matches how <code>import</code> behaves in browser environments, assuming a
|
||
typically configured server.</p>
|
||
<h3>No <code>NODE_PATH</code><span><a class="mark" href="#esm_no_node_path" id="esm_no_node_path">#</a></span></h3>
|
||
<p><code>NODE_PATH</code> is not part of resolving <code>import</code> specifiers. Please use symlinks
|
||
if this behavior is desired.</p>
|
||
<h3>No <code>require</code>, <code>exports</code>, <code>module.exports</code>, <code>__filename</code>, <code>__dirname</code><span><a class="mark" href="#esm_no_require_exports_module_exports_filename_dirname" id="esm_no_require_exports_module_exports_filename_dirname">#</a></span></h3>
|
||
<p>These CommonJS variables are not available in ES modules.</p>
|
||
<p><code>require</code> can be imported into an ES module using <a href="module.html#module_module_createrequire_filename"><code>module.createRequire()</code></a>.</p>
|
||
<p>Equivalents of <code>__filename</code> and <code>__dirname</code> can be created inside of each file
|
||
via <a href="#esm_import_meta"><code>import.meta.url</code></a>.</p>
|
||
<pre><code class="language-js"><span class="hljs-keyword">import</span> { fileURLToPath } <span class="hljs-keyword">from</span> <span class="hljs-string">'url'</span>;
|
||
<span class="hljs-keyword">import</span> { dirname } <span class="hljs-keyword">from</span> <span class="hljs-string">'path'</span>;
|
||
|
||
<span class="hljs-keyword">const</span> __filename = fileURLToPath(<span class="hljs-keyword">import</span>.meta.url);
|
||
<span class="hljs-keyword">const</span> __dirname = dirname(__filename);</code></pre>
|
||
<h3>No <code>require.resolve</code><span><a class="mark" href="#esm_no_require_resolve" id="esm_no_require_resolve">#</a></span></h3>
|
||
<p>Former use cases relying on <code>require.resolve</code> to determine the resolved path
|
||
of a module can be supported via <code>import.meta.resolve</code>, which is experimental
|
||
and supported via the <code>--experimental-import-meta-resolve</code> flag:</p>
|
||
<pre><code class="language-js">(<span class="hljs-keyword">async</span> () => {
|
||
<span class="hljs-keyword">const</span> dependencyAsset = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>.meta.resolve(<span class="hljs-string">'component-lib/asset.css'</span>);
|
||
})();</code></pre>
|
||
<p><code>import.meta.resolve</code> also accepts a second argument which is the parent module
|
||
from which to resolve from:</p>
|
||
<pre><code class="language-js">(<span class="hljs-keyword">async</span> () => {
|
||
<span class="hljs-comment">// Equivalent to import.meta.resolve('./dep')</span>
|
||
<span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>.meta.resolve(<span class="hljs-string">'./dep'</span>, <span class="hljs-keyword">import</span>.meta.url);
|
||
})();</code></pre>
|
||
<p>This function is asynchronous because the ES module resolver in Node.js is
|
||
asynchronous. With the introduction of <a href="https://github.com/tc39/proposal-top-level-await">Top-Level Await</a>, these use cases
|
||
will be easier as they won't require an async function wrapper.</p>
|
||
<h3>No <code>require.extensions</code><span><a class="mark" href="#esm_no_require_extensions" id="esm_no_require_extensions">#</a></span></h3>
|
||
<p><code>require.extensions</code> is not used by <code>import</code>. The expectation is that loader
|
||
hooks can provide this workflow in the future.</p>
|
||
<h3>No <code>require.cache</code><span><a class="mark" href="#esm_no_require_cache" id="esm_no_require_cache">#</a></span></h3>
|
||
<p><code>require.cache</code> is not used by <code>import</code>. It has a separate cache.</p>
|
||
<h3>URL-based paths<span><a class="mark" href="#esm_url_based_paths" id="esm_url_based_paths">#</a></span></h3>
|
||
<p>ES modules are resolved and cached based upon
|
||
<a href="https://url.spec.whatwg.org/">URL</a> semantics. This means that files containing
|
||
special characters such as <code>#</code> and <code>?</code> need to be escaped.</p>
|
||
<p>Modules are loaded multiple times if the <code>import</code> specifier used to resolve
|
||
them has a different query or fragment.</p>
|
||
<pre><code class="language-js"><span class="hljs-keyword">import</span> <span class="hljs-string">'./foo.mjs?query=1'</span>; <span class="hljs-comment">// loads ./foo.mjs with query of "?query=1"</span>
|
||
<span class="hljs-keyword">import</span> <span class="hljs-string">'./foo.mjs?query=2'</span>; <span class="hljs-comment">// loads ./foo.mjs with query of "?query=2"</span></code></pre>
|
||
<p>For now, only modules using the <code>file:</code> protocol can be loaded.</p>
|
||
<h2>Interoperability with CommonJS<span><a class="mark" href="#esm_interoperability_with_commonjs" id="esm_interoperability_with_commonjs">#</a></span></h2>
|
||
<h3><code>require</code><span><a class="mark" href="#esm_require" id="esm_require">#</a></span></h3>
|
||
<p><code>require</code> always treats the files it references as CommonJS. This applies
|
||
whether <code>require</code> is used the traditional way within a CommonJS environment, or
|
||
in an ES module environment using <a href="module.html#module_module_createrequire_filename"><code>module.createRequire()</code></a>.</p>
|
||
<p>To include an ES module into CommonJS, use <a href="#esm_import_expressions"><code>import()</code></a>.</p>
|
||
<h3><code>import</code> statements<span><a class="mark" href="#esm_import_statements" id="esm_import_statements">#</a></span></h3>
|
||
<p>An <code>import</code> statement can reference an ES module or a CommonJS module.
|
||
<code>import</code> statements are permitted only in ES modules. For similar functionality
|
||
in CommonJS, see <a href="#esm_import_expressions"><code>import()</code></a>.</p>
|
||
<p>When importing <a href="#esm_commonjs_namespaces">CommonJS modules</a>, the
|
||
<code>module.exports</code> object is provided as the default export. Named exports may be
|
||
available, provided by static analysis as a convenience for better ecosystem
|
||
compatibility.</p>
|
||
<p>Additional experimental flags are available for importing
|
||
<a href="#esm_experimental_wasm_modules">Wasm modules</a> or
|
||
<a href="#esm_experimental_json_modules">JSON modules</a>. For importing native modules or
|
||
JSON modules unflagged, see <a href="module.html#module_module_createrequire_filename"><code>module.createRequire()</code></a>.</p>
|
||
<p>The <em>specifier</em> of an <code>import</code> statement (the string after the <code>from</code> keyword)
|
||
can either be an URL-style relative path like <code>'./file.mjs'</code> or a package name
|
||
like <code>'fs'</code>.</p>
|
||
<p>Like in CommonJS, files within packages can be accessed by appending a path to
|
||
the package name; unless the package’s <a href="packages.html#packages_node_js_package_json_field_definitions"><code>package.json</code></a> contains an
|
||
<a href="packages.html#packages_exports"><code>"exports"</code></a> field, in which case files within packages need to be accessed
|
||
via the path defined in <a href="packages.html#packages_exports"><code>"exports"</code></a>.</p>
|
||
<pre><code class="language-js"><span class="hljs-keyword">import</span> { sin, cos } <span class="hljs-keyword">from</span> <span class="hljs-string">'geometry/trigonometry-functions.mjs'</span>;</code></pre>
|
||
<h3><code>import()</code> expressions<span><a class="mark" href="#esm_import_expressions" id="esm_import_expressions">#</a></span></h3>
|
||
<p><a href="https://wiki.developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports">Dynamic <code>import()</code></a> is supported in both CommonJS and ES modules. It can be
|
||
used to include ES module files from CommonJS code.</p>
|
||
<h2>CommonJS Namespaces<span><a class="mark" href="#esm_commonjs_namespaces" id="esm_commonjs_namespaces">#</a></span></h2>
|
||
<p>CommonJS modules consist of a <code>module.exports</code> object which can be of any type.</p>
|
||
<p>When importing a CommonJS module, it can be reliably imported using the ES
|
||
module default import or its corresponding sugar syntax:</p>
|
||
<!-- eslint-disable no-duplicate-imports -->
|
||
<pre><code class="language-js"><span class="hljs-keyword">import</span> { <span class="hljs-keyword">default</span> <span class="hljs-keyword">as</span> cjs } <span class="hljs-keyword">from</span> <span class="hljs-string">'cjs'</span>;
|
||
|
||
<span class="hljs-comment">// The following import statement is "syntax sugar" (equivalent but sweeter)</span>
|
||
<span class="hljs-comment">// for `{ default as cjsSugar }` in the above import statement:</span>
|
||
<span class="hljs-keyword">import</span> cjsSugar <span class="hljs-keyword">from</span> <span class="hljs-string">'cjs'</span>;
|
||
|
||
<span class="hljs-built_in">console</span>.log(cjs);
|
||
<span class="hljs-built_in">console</span>.log(cjs === cjsSugar);
|
||
<span class="hljs-comment">// Prints:</span>
|
||
<span class="hljs-comment">// <module.exports></span>
|
||
<span class="hljs-comment">// true</span></code></pre>
|
||
<p>The ECMAScript Module Namespace representation of a CommonJS module is always
|
||
a namespace with a <code>default</code> export key pointing to the CommonJS
|
||
<code>module.exports</code> value.</p>
|
||
<p>This Module Namespace Exotic Object can be directly observed either when using
|
||
<code>import * as m from 'cjs'</code> or a dynamic import:</p>
|
||
<!-- eslint-skip -->
|
||
<pre><code class="language-js"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> m <span class="hljs-keyword">from</span> <span class="hljs-string">'cjs'</span>;
|
||
<span class="hljs-built_in">console</span>.log(m);
|
||
<span class="hljs-built_in">console</span>.log(m === <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'cjs'</span>));
|
||
<span class="hljs-comment">// Prints:</span>
|
||
<span class="hljs-comment">// [Module] { default: <module.exports> }</span>
|
||
<span class="hljs-comment">// true</span></code></pre>
|
||
<p>For better compatibility with existing usage in the JS ecosystem, Node.js
|
||
in addition attempts to determine the CommonJS named exports of every imported
|
||
CommonJS module to provide them as separate ES module exports using a static
|
||
analysis process.</p>
|
||
<p>For example, consider a CommonJS module written:</p>
|
||
<pre><code class="language-js"><span class="hljs-comment">// cjs.cjs</span>
|
||
<span class="hljs-built_in">exports</span>.name = <span class="hljs-string">'exported'</span>;</code></pre>
|
||
<p>The preceding module supports named imports in ES modules:</p>
|
||
<!-- eslint-disable no-duplicate-imports -->
|
||
<pre><code class="language-js"><span class="hljs-keyword">import</span> { name } <span class="hljs-keyword">from</span> <span class="hljs-string">'./cjs.cjs'</span>;
|
||
<span class="hljs-built_in">console</span>.log(name);
|
||
<span class="hljs-comment">// Prints: 'exported'</span>
|
||
|
||
<span class="hljs-keyword">import</span> cjs <span class="hljs-keyword">from</span> <span class="hljs-string">'./cjs.cjs'</span>;
|
||
<span class="hljs-built_in">console</span>.log(cjs);
|
||
<span class="hljs-comment">// Prints: { name: 'exported' }</span>
|
||
|
||
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> m <span class="hljs-keyword">from</span> <span class="hljs-string">'./cjs.cjs'</span>;
|
||
<span class="hljs-built_in">console</span>.log(m);
|
||
<span class="hljs-comment">// Prints: [Module] { default: { name: 'exported' }, name: 'exported' }</span></code></pre>
|
||
<p>As can be seen from the last example of the Module Namespace Exotic Object being
|
||
logged, the <code>name</code> export is copied off of the <code>module.exports</code> object and set
|
||
directly on the ES module namespace when the module is imported.</p>
|
||
<p>Live binding updates or new exports added to <code>module.exports</code> are not detected
|
||
for these named exports.</p>
|
||
<p>The detection of named exports is based on common syntax patterns but does not
|
||
always correctly detect named exports. In these cases, using the default
|
||
import form described above can be a better option.</p>
|
||
<p>Named exports detection covers many common export patterns, reexport patterns
|
||
and build tool and transpiler outputs. See <a href="https://github.com/guybedford/cjs-module-lexer/tree/1.2.2">cjs-module-lexer</a> for the exact
|
||
semantics implemented.</p>
|
||
<h2>Builtin modules<span><a class="mark" href="#esm_builtin_modules" id="esm_builtin_modules">#</a></span></h2>
|
||
<p><a href="modules.html#modules_core_modules">Core modules</a> provide named exports of their public API. A
|
||
default export is also provided which is the value of the CommonJS exports.
|
||
The default export can be used for, among other things, modifying the named
|
||
exports. Named exports of builtin modules are updated only by calling
|
||
<a href="module.html#module_module_syncbuiltinesmexports"><code>module.syncBuiltinESMExports()</code></a>.</p>
|
||
<pre><code class="language-js"><span class="hljs-keyword">import</span> EventEmitter <span class="hljs-keyword">from</span> <span class="hljs-string">'events'</span>;
|
||
<span class="hljs-keyword">const</span> e = <span class="hljs-keyword">new</span> EventEmitter();</code></pre>
|
||
<pre><code class="language-js"><span class="hljs-keyword">import</span> { readFile } <span class="hljs-keyword">from</span> <span class="hljs-string">'fs'</span>;
|
||
readFile(<span class="hljs-string">'./foo.txt'</span>, <span class="hljs-function">(<span class="hljs-params">err, source</span>) =></span> {
|
||
<span class="hljs-keyword">if</span> (err) {
|
||
<span class="hljs-built_in">console</span>.error(err);
|
||
} <span class="hljs-keyword">else</span> {
|
||
<span class="hljs-built_in">console</span>.log(source);
|
||
}
|
||
});</code></pre>
|
||
<pre><code class="language-js"><span class="hljs-keyword">import</span> fs, { readFileSync } <span class="hljs-keyword">from</span> <span class="hljs-string">'fs'</span>;
|
||
<span class="hljs-keyword">import</span> { syncBuiltinESMExports } <span class="hljs-keyword">from</span> <span class="hljs-string">'module'</span>;
|
||
|
||
fs.readFileSync = <span class="hljs-function">() =></span> Buffer.from(<span class="hljs-string">'Hello, ESM'</span>);
|
||
syncBuiltinESMExports();
|
||
|
||
fs.readFileSync === readFileSync;</code></pre>
|
||
<h2>CommonJS, JSON, and native modules<span><a class="mark" href="#esm_commonjs_json_and_native_modules" id="esm_commonjs_json_and_native_modules">#</a></span></h2>
|
||
<p>CommonJS, JSON, and native modules can be used with
|
||
<a href="module.html#module_module_createrequire_filename"><code>module.createRequire()</code></a>.</p>
|
||
<pre><code class="language-js"><span class="hljs-comment">// cjs.cjs</span>
|
||
<span class="hljs-built_in">module</span>.exports = <span class="hljs-string">'cjs'</span>;
|
||
|
||
<span class="hljs-comment">// esm.mjs</span>
|
||
<span class="hljs-keyword">import</span> { createRequire } <span class="hljs-keyword">from</span> <span class="hljs-string">'module'</span>;
|
||
|
||
<span class="hljs-keyword">const</span> <span class="hljs-built_in">require</span> = createRequire(<span class="hljs-keyword">import</span>.meta.url);
|
||
|
||
<span class="hljs-keyword">const</span> cjs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./cjs.cjs'</span>);
|
||
cjs === <span class="hljs-string">'cjs'</span>; <span class="hljs-comment">// true</span></code></pre>
|
||
<h2>Experimental JSON modules<span><a class="mark" href="#esm_experimental_json_modules" id="esm_experimental_json_modules">#</a></span></h2>
|
||
<p>Currently importing JSON modules are only supported in the <code>commonjs</code> mode
|
||
and are loaded using the CJS loader. <a href="https://html.spec.whatwg.org/#creating-a-json-module-script">WHATWG JSON modules specification</a> are
|
||
still being standardized, and are experimentally supported by including the
|
||
additional flag <code>--experimental-json-modules</code> when running Node.js.</p>
|
||
<p>When the <code>--experimental-json-modules</code> flag is included, both the
|
||
<code>commonjs</code> and <code>module</code> mode use the new experimental JSON
|
||
loader. The imported JSON only exposes a <code>default</code>. There is no
|
||
support for named exports. A cache entry is created in the CommonJS
|
||
cache to avoid duplication. The same object is returned in
|
||
CommonJS if the JSON module has already been imported from the
|
||
same path.</p>
|
||
<p>Assuming an <code>index.mjs</code> with</p>
|
||
<!-- eslint-skip -->
|
||
<pre><code class="language-js"><span class="hljs-keyword">import</span> packageConfig <span class="hljs-keyword">from</span> <span class="hljs-string">'./package.json'</span>;</code></pre>
|
||
<p>The <code>--experimental-json-modules</code> flag is needed for the module
|
||
to work.</p>
|
||
<pre><code class="language-bash">node index.mjs <span class="hljs-comment"># fails</span>
|
||
node --experimental-json-modules index.mjs <span class="hljs-comment"># works</span></code></pre>
|
||
<h2>Experimental Wasm modules<span><a class="mark" href="#esm_experimental_wasm_modules" id="esm_experimental_wasm_modules">#</a></span></h2>
|
||
<p>Importing Web Assembly modules is supported under the
|
||
<code>--experimental-wasm-modules</code> flag, allowing any <code>.wasm</code> files to be
|
||
imported as normal modules while also supporting their module imports.</p>
|
||
<p>This integration is in line with the
|
||
<a href="https://github.com/webassembly/esm-integration">ES Module Integration Proposal for Web Assembly</a>.</p>
|
||
<p>For example, an <code>index.mjs</code> containing:</p>
|
||
<pre><code class="language-js"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> M <span class="hljs-keyword">from</span> <span class="hljs-string">'./module.wasm'</span>;
|
||
<span class="hljs-built_in">console</span>.log(M);</code></pre>
|
||
<p>executed under:</p>
|
||
<pre><code class="language-bash">node --experimental-wasm-modules index.mjs</code></pre>
|
||
<p>would provide the exports interface for the instantiation of <code>module.wasm</code>.</p>
|
||
<h2>Experimental loaders<span><a class="mark" href="#esm_experimental_loaders" id="esm_experimental_loaders">#</a></span></h2>
|
||
<p><strong>Note: This API is currently being redesigned and will still change.</strong></p>
|
||
|
||
<p>To customize the default module resolution, loader hooks can optionally be
|
||
provided via a <code>--experimental-loader ./loader-name.mjs</code> argument to Node.js.</p>
|
||
<p>When hooks are used they only apply to ES module loading and not to any
|
||
CommonJS modules loaded.</p>
|
||
<h3>Hooks<span><a class="mark" href="#esm_hooks" id="esm_hooks">#</a></span></h3>
|
||
<h4><code>resolve(specifier, context, defaultResolve)</code><span><a class="mark" href="#esm_resolve_specifier_context_defaultresolve" id="esm_resolve_specifier_context_defaultresolve">#</a></span></h4>
|
||
<blockquote>
|
||
<p>Note: The loaders API is being redesigned. This hook may disappear or its
|
||
signature may change. Do not rely on the API described below.</p>
|
||
</blockquote>
|
||
<ul>
|
||
<li><code>specifier</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string></a></li>
|
||
<li><code>context</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object" class="type"><Object></a>
|
||
<ul>
|
||
<li><code>conditions</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string[]></a></li>
|
||
<li><code>parentURL</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><code>defaultResolve</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function" class="type"><Function></a></li>
|
||
<li>Returns: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object" class="type"><Object></a>
|
||
<ul>
|
||
<li><code>url</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string></a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<p>The <code>resolve</code> hook returns the resolved file URL for a given module specifier
|
||
and parent URL. The module specifier is the string in an <code>import</code> statement or
|
||
<code>import()</code> expression, and the parent URL is the URL of the module that imported
|
||
this one, or <code>undefined</code> if this is the main entry point for the application.</p>
|
||
<p>The <code>conditions</code> property on the <code>context</code> is an array of conditions for
|
||
<a href="packages.html#packages_conditional_exports">Conditional exports</a> that apply to this resolution request. They can be used
|
||
for looking up conditional mappings elsewhere or to modify the list when calling
|
||
the default resolution logic.</p>
|
||
<p>The current <a href="packages.html#packages_conditional_exports">package exports conditions</a> are always in
|
||
the <code>context.conditions</code> array passed into the hook. To guarantee <em>default
|
||
Node.js module specifier resolution behavior</em> when calling <code>defaultResolve</code>, the
|
||
<code>context.conditions</code> array passed to it <em>must</em> include <em>all</em> elements of the
|
||
<code>context.conditions</code> array originally passed into the <code>resolve</code> hook.</p>
|
||
<pre><code class="language-js"><span class="hljs-comment">/**
|
||
* <span class="hljs-doctag">@param <span class="hljs-type">{string}</span> <span class="hljs-variable">specifier</span></span>
|
||
* <span class="hljs-doctag">@param <span class="hljs-type">{{
|
||
* conditions: !Array<string>,
|
||
* parentURL: !(string | undefined),
|
||
* }</span></span>} context
|
||
* <span class="hljs-doctag">@param <span class="hljs-type">{Function}</span> <span class="hljs-variable">defaultResolve</span></span>
|
||
* <span class="hljs-doctag">@returns <span class="hljs-type">{Promise<{ url: string }</span></span>>}
|
||
*/</span>
|
||
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">resolve</span>(<span class="hljs-params">specifier, context, defaultResolve</span>) </span>{
|
||
<span class="hljs-keyword">const</span> { parentURL = <span class="hljs-literal">null</span> } = context;
|
||
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">Math</span>.random() > <span class="hljs-number">0.5</span>) { <span class="hljs-comment">// Some condition.</span>
|
||
<span class="hljs-comment">// For some or all specifiers, do some custom logic for resolving.</span>
|
||
<span class="hljs-comment">// Always return an object of the form {url: <string>}.</span>
|
||
<span class="hljs-keyword">return</span> {
|
||
<span class="hljs-attr">url</span>: parentURL ?
|
||
<span class="hljs-keyword">new</span> URL(specifier, parentURL).href :
|
||
<span class="hljs-keyword">new</span> URL(specifier).href,
|
||
};
|
||
}
|
||
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">Math</span>.random() < <span class="hljs-number">0.5</span>) { <span class="hljs-comment">// Another condition.</span>
|
||
<span class="hljs-comment">// When calling `defaultResolve`, the arguments can be modified. In this</span>
|
||
<span class="hljs-comment">// case it's adding another value for matching conditional exports.</span>
|
||
<span class="hljs-keyword">return</span> defaultResolve(specifier, {
|
||
...context,
|
||
<span class="hljs-attr">conditions</span>: [...context.conditions, <span class="hljs-string">'another-condition'</span>],
|
||
});
|
||
}
|
||
<span class="hljs-comment">// Defer to Node.js for all other specifiers.</span>
|
||
<span class="hljs-keyword">return</span> defaultResolve(specifier, context, defaultResolve);
|
||
}</code></pre>
|
||
<h4><code>getFormat(url, context, defaultGetFormat)</code><span><a class="mark" href="#esm_getformat_url_context_defaultgetformat" id="esm_getformat_url_context_defaultgetformat">#</a></span></h4>
|
||
<blockquote>
|
||
<p>Note: The loaders API is being redesigned. This hook may disappear or its
|
||
signature may change. Do not rely on the API described below.</p>
|
||
</blockquote>
|
||
<ul>
|
||
<li><code>url</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string></a></li>
|
||
<li><code>context</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object" class="type"><Object></a></li>
|
||
<li><code>defaultGetFormat</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function" class="type"><Function></a></li>
|
||
<li>Returns: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object" class="type"><Object></a>
|
||
<ul>
|
||
<li><code>format</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string></a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<p>The <code>getFormat</code> hook provides a way to define a custom method of determining how
|
||
a URL should be interpreted. The <code>format</code> returned also affects what the
|
||
acceptable forms of source values are for a module when parsing. This can be one
|
||
of the following:</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<table><thead><tr><th><code>format</code></th><th>Description</th><th>Acceptable Types For <code>source</code> Returned by <code>getSource</code> or <code>transformSource</code></th></tr></thead><tbody><tr><td><code>'builtin'</code></td><td>Load a Node.js builtin module</td><td>Not applicable</td></tr><tr><td><code>'dynamic'</code></td><td>Use a <a href="#esm_code_dynamicinstantiate_code_hook">dynamic instantiate hook</a></td><td>Not applicable</td></tr><tr><td><code>'commonjs'</code></td><td>Load a Node.js CommonJS module</td><td>Not applicable</td></tr><tr><td><code>'json'</code></td><td>Load a JSON file</td><td>{ <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String"><code>string</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer"><code>ArrayBuffer</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray"><code>TypedArray</code></a> }</td></tr><tr><td><code>'module'</code></td><td>Load an ES module</td><td>{ <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String"><code>string</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer"><code>ArrayBuffer</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray"><code>TypedArray</code></a> }</td></tr><tr><td><code>'wasm'</code></td><td>Load a WebAssembly module</td><td>{ <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer"><code>ArrayBuffer</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray"><code>TypedArray</code></a> }</td></tr></tbody></table>
|
||
<p>Note: These types all correspond to classes defined in ECMAScript.</p>
|
||
<ul>
|
||
<li>The specific <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer"><code>ArrayBuffer</code></a> object is a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer"><code>SharedArrayBuffer</code></a>.</li>
|
||
<li>The specific <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray"><code>TypedArray</code></a> object is a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array"><code>Uint8Array</code></a>.</li>
|
||
</ul>
|
||
<p>Note: If the source value of a text-based format (i.e., <code>'json'</code>, <code>'module'</code>) is
|
||
not a string, it is converted to a string using <a href="util.md#util_class_util_textdecoder"><code>util.TextDecoder</code></a>.</p>
|
||
<pre><code class="language-js"><span class="hljs-comment">/**
|
||
* <span class="hljs-doctag">@param <span class="hljs-type">{string}</span> <span class="hljs-variable">url</span></span>
|
||
* <span class="hljs-doctag">@param <span class="hljs-type">{Object}</span> </span>context (currently empty)
|
||
* <span class="hljs-doctag">@param <span class="hljs-type">{Function}</span> <span class="hljs-variable">defaultGetFormat</span></span>
|
||
* <span class="hljs-doctag">@returns <span class="hljs-type">{Promise<{ format: string }</span></span>>}
|
||
*/</span>
|
||
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getFormat</span>(<span class="hljs-params">url, context, defaultGetFormat</span>) </span>{
|
||
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">Math</span>.random() > <span class="hljs-number">0.5</span>) { <span class="hljs-comment">// Some condition.</span>
|
||
<span class="hljs-comment">// For some or all URLs, do some custom logic for determining format.</span>
|
||
<span class="hljs-comment">// Always return an object of the form {format: <string>}, where the</span>
|
||
<span class="hljs-comment">// format is one of the strings in the preceding table.</span>
|
||
<span class="hljs-keyword">return</span> {
|
||
<span class="hljs-attr">format</span>: <span class="hljs-string">'module'</span>,
|
||
};
|
||
}
|
||
<span class="hljs-comment">// Defer to Node.js for all other URLs.</span>
|
||
<span class="hljs-keyword">return</span> defaultGetFormat(url, context, defaultGetFormat);
|
||
}</code></pre>
|
||
<h4><code>getSource(url, context, defaultGetSource)</code><span><a class="mark" href="#esm_getsource_url_context_defaultgetsource" id="esm_getsource_url_context_defaultgetsource">#</a></span></h4>
|
||
<blockquote>
|
||
<p>Note: The loaders API is being redesigned. This hook may disappear or its
|
||
signature may change. Do not rely on the API described below.</p>
|
||
</blockquote>
|
||
<ul>
|
||
<li><code>url</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string></a></li>
|
||
<li><code>context</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object" class="type"><Object></a>
|
||
<ul>
|
||
<li><code>format</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><code>defaultGetSource</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function" class="type"><Function></a></li>
|
||
<li>Returns: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object" class="type"><Object></a>
|
||
<ul>
|
||
<li><code>source</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string></a> | <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer" class="type"><SharedArrayBuffer></a> | <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array" class="type"><Uint8Array></a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<p>The <code>getSource</code> hook provides a way to define a custom method for retrieving
|
||
the source code of an ES module specifier. This would allow a loader to
|
||
potentially avoid reading files from disk.</p>
|
||
<pre><code class="language-js"><span class="hljs-comment">/**
|
||
* <span class="hljs-doctag">@param <span class="hljs-type">{string}</span> <span class="hljs-variable">url</span></span>
|
||
* <span class="hljs-doctag">@param <span class="hljs-type">{{ format: string }</span></span>} context
|
||
* <span class="hljs-doctag">@param <span class="hljs-type">{Function}</span> <span class="hljs-variable">defaultGetSource</span></span>
|
||
* <span class="hljs-doctag">@returns <span class="hljs-type">{Promise<{ source: !(string | SharedArrayBuffer | Uint8Array) }</span></span>>}
|
||
*/</span>
|
||
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSource</span>(<span class="hljs-params">url, context, defaultGetSource</span>) </span>{
|
||
<span class="hljs-keyword">const</span> { format } = context;
|
||
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">Math</span>.random() > <span class="hljs-number">0.5</span>) { <span class="hljs-comment">// Some condition.</span>
|
||
<span class="hljs-comment">// For some or all URLs, do some custom logic for retrieving the source.</span>
|
||
<span class="hljs-comment">// Always return an object of the form {source: <string|buffer>}.</span>
|
||
<span class="hljs-keyword">return</span> {
|
||
<span class="hljs-attr">source</span>: <span class="hljs-string">'...'</span>,
|
||
};
|
||
}
|
||
<span class="hljs-comment">// Defer to Node.js for all other URLs.</span>
|
||
<span class="hljs-keyword">return</span> defaultGetSource(url, context, defaultGetSource);
|
||
}</code></pre>
|
||
<h4><code>transformSource(source, context, defaultTransformSource)</code><span><a class="mark" href="#esm_transformsource_source_context_defaulttransformsource" id="esm_transformsource_source_context_defaulttransformsource">#</a></span></h4>
|
||
<pre><code class="language-console">NODE_OPTIONS='--experimental-loader ./custom-loader.mjs' node x.js</code></pre>
|
||
<blockquote>
|
||
<p>Note: The loaders API is being redesigned. This hook may disappear or its
|
||
signature may change. Do not rely on the API described below.</p>
|
||
</blockquote>
|
||
<ul>
|
||
<li><code>source</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string></a> | <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer" class="type"><SharedArrayBuffer></a> | <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array" class="type"><Uint8Array></a></li>
|
||
<li><code>context</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object" class="type"><Object></a>
|
||
<ul>
|
||
<li><code>format</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string></a></li>
|
||
<li><code>url</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string></a></li>
|
||
</ul>
|
||
</li>
|
||
<li>Returns: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object" class="type"><Object></a>
|
||
<ul>
|
||
<li><code>source</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string></a> | <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer" class="type"><SharedArrayBuffer></a> | <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array" class="type"><Uint8Array></a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<p>The <code>transformSource</code> hook provides a way to modify the source code of a loaded
|
||
ES module file after the source string has been loaded but before Node.js has
|
||
done anything with it.</p>
|
||
<p>If this hook is used to convert unknown-to-Node.js file types into executable
|
||
JavaScript, a resolve hook is also necessary in order to register any
|
||
unknown-to-Node.js file extensions. See the <a href="#esm_transpiler_loader">transpiler loader example</a> below.</p>
|
||
<pre><code class="language-js"><span class="hljs-comment">/**
|
||
* <span class="hljs-doctag">@param <span class="hljs-type">{!(string | SharedArrayBuffer | Uint8Array)}</span> <span class="hljs-variable">source</span></span>
|
||
* <span class="hljs-doctag">@param <span class="hljs-type">{{
|
||
* format: string,
|
||
* url: string,
|
||
* }</span></span>} context
|
||
* <span class="hljs-doctag">@param <span class="hljs-type">{Function}</span> <span class="hljs-variable">defaultTransformSource</span></span>
|
||
* <span class="hljs-doctag">@returns <span class="hljs-type">{Promise<{ source: !(string | SharedArrayBuffer | Uint8Array) }</span></span>>}
|
||
*/</span>
|
||
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformSource</span>(<span class="hljs-params">source, context, defaultTransformSource</span>) </span>{
|
||
<span class="hljs-keyword">const</span> { url, format } = context;
|
||
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">Math</span>.random() > <span class="hljs-number">0.5</span>) { <span class="hljs-comment">// Some condition.</span>
|
||
<span class="hljs-comment">// For some or all URLs, do some custom logic for modifying the source.</span>
|
||
<span class="hljs-comment">// Always return an object of the form {source: <string|buffer>}.</span>
|
||
<span class="hljs-keyword">return</span> {
|
||
<span class="hljs-attr">source</span>: <span class="hljs-string">'...'</span>,
|
||
};
|
||
}
|
||
<span class="hljs-comment">// Defer to Node.js for all other sources.</span>
|
||
<span class="hljs-keyword">return</span> defaultTransformSource(source, context, defaultTransformSource);
|
||
}</code></pre>
|
||
<h4><code>getGlobalPreloadCode()</code><span><a class="mark" href="#esm_getglobalpreloadcode" id="esm_getglobalpreloadcode">#</a></span></h4>
|
||
<blockquote>
|
||
<p>Note: The loaders API is being redesigned. This hook may disappear or its
|
||
signature may change. Do not rely on the API described below.</p>
|
||
</blockquote>
|
||
<ul>
|
||
<li>Returns: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type" class="type"><string></a></li>
|
||
</ul>
|
||
<p>Sometimes it might be necessary to run some code inside of the same global scope
|
||
that the application runs in. This hook allows the return of a string that is
|
||
run as sloppy-mode script on startup.</p>
|
||
<p>Similar to how CommonJS wrappers work, the code runs in an implicit function
|
||
scope. The only argument is a <code>require</code>-like function that can be used to load
|
||
builtins like "fs": <code>getBuiltin(request: string)</code>.</p>
|
||
<p>If the code needs more advanced <code>require</code> features, it has to construct
|
||
its own <code>require</code> using <code>module.createRequire()</code>.</p>
|
||
<pre><code class="language-js"><span class="hljs-comment">/**
|
||
* <span class="hljs-doctag">@returns <span class="hljs-type">{string}</span> </span>Code to run before application startup
|
||
*/</span>
|
||
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getGlobalPreloadCode</span>(<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-keyword">return</span> <span class="hljs-string">`\
|
||
globalThis.someInjectedProperty = 42;
|
||
console.log('I just set some globals!');
|
||
|
||
const { createRequire } = getBuiltin('module');
|
||
|
||
const require = createRequire(process.cwd() + '/<preload>');
|
||
// [...]
|
||
`</span>;
|
||
}</code></pre>
|
||
<h4><code>dynamicInstantiate</code> hook<span><a class="mark" href="#esm_code_dynamicinstantiate_code_hook" id="esm_code_dynamicinstantiate_code_hook">#</a></span></h4>
|
||
<blockquote>
|
||
<p>Note: The loaders API is being redesigned. This hook may disappear or its
|
||
signature may change. Do not rely on the API described below.</p>
|
||
</blockquote>
|
||
<p>To create a custom dynamic module that doesn't correspond to one of the
|
||
existing <code>format</code> interpretations, the <code>dynamicInstantiate</code> hook can be used.
|
||
This hook is called only for modules that return <code>format: 'dynamic'</code> from
|
||
the <code>getFormat</code> hook.</p>
|
||
<pre><code class="language-js"><span class="hljs-comment">/**
|
||
* <span class="hljs-doctag">@param <span class="hljs-type">{string}</span> <span class="hljs-variable">url</span></span>
|
||
* <span class="hljs-doctag">@returns <span class="hljs-type">{object}</span> <span class="hljs-variable">response</span></span>
|
||
* <span class="hljs-doctag">@returns <span class="hljs-type">{array}</span> </span>response.exports
|
||
* <span class="hljs-doctag">@returns <span class="hljs-type">{function}</span> </span>response.execute
|
||
*/</span>
|
||
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">dynamicInstantiate</span>(<span class="hljs-params">url</span>) </span>{
|
||
<span class="hljs-keyword">return</span> {
|
||
<span class="hljs-attr">exports</span>: [<span class="hljs-string">'customExportName'</span>],
|
||
<span class="hljs-attr">execute</span>: <span class="hljs-function">(<span class="hljs-params"><span class="hljs-built_in">exports</span></span>) =></span> {
|
||
<span class="hljs-comment">// Get and set functions provided for pre-allocated export names</span>
|
||
<span class="hljs-built_in">exports</span>.customExportName.set(<span class="hljs-string">'value'</span>);
|
||
}
|
||
};
|
||
}</code></pre>
|
||
<p>With the list of module exports provided upfront, the <code>execute</code> function will
|
||
then be called at the exact point of module evaluation order for that module
|
||
in the import tree.</p>
|
||
<h3>Examples<span><a class="mark" href="#esm_examples" id="esm_examples">#</a></span></h3>
|
||
<p>The various loader hooks can be used together to accomplish wide-ranging
|
||
customizations of Node.js’ code loading and evaluation behaviors.</p>
|
||
<h4>HTTPS loader<span><a class="mark" href="#esm_https_loader" id="esm_https_loader">#</a></span></h4>
|
||
<p>In current Node.js, specifiers starting with <code>https://</code> are unsupported. The
|
||
loader below registers hooks to enable rudimentary support for such specifiers.
|
||
While this may seem like a significant improvement to Node.js core
|
||
functionality, there are substantial downsides to actually using this loader:
|
||
performance is much slower than loading files from disk, there is no caching,
|
||
and there is no security.</p>
|
||
<pre><code class="language-js"><span class="hljs-comment">// https-loader.mjs</span>
|
||
<span class="hljs-keyword">import</span> { get } <span class="hljs-keyword">from</span> <span class="hljs-string">'https'</span>;
|
||
|
||
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">resolve</span>(<span class="hljs-params">specifier, context, defaultResolve</span>) </span>{
|
||
<span class="hljs-keyword">const</span> { parentURL = <span class="hljs-literal">null</span> } = context;
|
||
|
||
<span class="hljs-comment">// Normally Node.js would error on specifiers starting with 'https://', so</span>
|
||
<span class="hljs-comment">// this hook intercepts them and converts them into absolute URLs to be</span>
|
||
<span class="hljs-comment">// passed along to the later hooks below.</span>
|
||
<span class="hljs-keyword">if</span> (specifier.startsWith(<span class="hljs-string">'https://'</span>)) {
|
||
<span class="hljs-keyword">return</span> {
|
||
<span class="hljs-attr">url</span>: specifier
|
||
};
|
||
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (parentURL && parentURL.startsWith(<span class="hljs-string">'https://'</span>)) {
|
||
<span class="hljs-keyword">return</span> {
|
||
<span class="hljs-attr">url</span>: <span class="hljs-keyword">new</span> URL(specifier, parentURL).href
|
||
};
|
||
}
|
||
|
||
<span class="hljs-comment">// Let Node.js handle all other specifiers.</span>
|
||
<span class="hljs-keyword">return</span> defaultResolve(specifier, context, defaultResolve);
|
||
}
|
||
|
||
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getFormat</span>(<span class="hljs-params">url, context, defaultGetFormat</span>) </span>{
|
||
<span class="hljs-comment">// This loader assumes all network-provided JavaScript is ES module code.</span>
|
||
<span class="hljs-keyword">if</span> (url.startsWith(<span class="hljs-string">'https://'</span>)) {
|
||
<span class="hljs-keyword">return</span> {
|
||
<span class="hljs-attr">format</span>: <span class="hljs-string">'module'</span>
|
||
};
|
||
}
|
||
|
||
<span class="hljs-comment">// Let Node.js handle all other URLs.</span>
|
||
<span class="hljs-keyword">return</span> defaultGetFormat(url, context, defaultGetFormat);
|
||
}
|
||
|
||
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSource</span>(<span class="hljs-params">url, context, defaultGetSource</span>) </span>{
|
||
<span class="hljs-comment">// For JavaScript to be loaded over the network, we need to fetch and</span>
|
||
<span class="hljs-comment">// return it.</span>
|
||
<span class="hljs-keyword">if</span> (url.startsWith(<span class="hljs-string">'https://'</span>)) {
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =></span> {
|
||
get(url, <span class="hljs-function">(<span class="hljs-params">res</span>) =></span> {
|
||
<span class="hljs-keyword">let</span> data = <span class="hljs-string">''</span>;
|
||
res.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =></span> data += chunk);
|
||
res.on(<span class="hljs-string">'end'</span>, <span class="hljs-function">() =></span> resolve({ <span class="hljs-attr">source</span>: data }));
|
||
}).on(<span class="hljs-string">'error'</span>, <span class="hljs-function">(<span class="hljs-params">err</span>) =></span> reject(err));
|
||
});
|
||
}
|
||
|
||
<span class="hljs-comment">// Let Node.js handle all other URLs.</span>
|
||
<span class="hljs-keyword">return</span> defaultGetSource(url, context, defaultGetSource);
|
||
}</code></pre>
|
||
<pre><code class="language-js"><span class="hljs-comment">// main.mjs</span>
|
||
<span class="hljs-keyword">import</span> { VERSION } <span class="hljs-keyword">from</span> <span class="hljs-string">'https://coffeescript.org/browser-compiler-modern/coffeescript.js'</span>;
|
||
|
||
<span class="hljs-built_in">console</span>.log(VERSION);</code></pre>
|
||
<p>With the preceding loader, running
|
||
<code>node --experimental-loader ./https-loader.mjs ./main.mjs</code>
|
||
prints the current version of CoffeeScript per the module at the URL in
|
||
<code>main.mjs</code>.</p>
|
||
<h4>Transpiler loader<span><a class="mark" href="#esm_transpiler_loader" id="esm_transpiler_loader">#</a></span></h4>
|
||
<p>Sources that are in formats Node.js doesn’t understand can be converted into
|
||
JavaScript using the <a href="#esm_transformsource_source_context_defaulttransformsource"><code>transformSource</code> hook</a>. Before that hook gets called,
|
||
however, other hooks need to tell Node.js not to throw an error on unknown file
|
||
types; and to tell Node.js how to load this new file type.</p>
|
||
<p>This is less performant than transpiling source files before running
|
||
Node.js; a transpiler loader should only be used for development and testing
|
||
purposes.</p>
|
||
<pre><code class="language-js"><span class="hljs-comment">// coffeescript-loader.mjs</span>
|
||
<span class="hljs-keyword">import</span> { URL, pathToFileURL } <span class="hljs-keyword">from</span> <span class="hljs-string">'url'</span>;
|
||
<span class="hljs-keyword">import</span> CoffeeScript <span class="hljs-keyword">from</span> <span class="hljs-string">'coffeescript'</span>;
|
||
|
||
<span class="hljs-keyword">const</span> baseURL = pathToFileURL(<span class="hljs-string">`<span class="hljs-subst">${process.cwd()}</span>/`</span>).href;
|
||
|
||
<span class="hljs-comment">// CoffeeScript files end in .coffee, .litcoffee or .coffee.md.</span>
|
||
<span class="hljs-keyword">const</span> extensionsRegex = <span class="hljs-regexp">/\.coffee$|\.litcoffee$|\.coffee\.md$/</span>;
|
||
|
||
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">resolve</span>(<span class="hljs-params">specifier, context, defaultResolve</span>) </span>{
|
||
<span class="hljs-keyword">const</span> { parentURL = baseURL } = context;
|
||
|
||
<span class="hljs-comment">// Node.js normally errors on unknown file extensions, so return a URL for</span>
|
||
<span class="hljs-comment">// specifiers ending in the CoffeeScript file extensions.</span>
|
||
<span class="hljs-keyword">if</span> (extensionsRegex.test(specifier)) {
|
||
<span class="hljs-keyword">return</span> {
|
||
<span class="hljs-attr">url</span>: <span class="hljs-keyword">new</span> URL(specifier, parentURL).href
|
||
};
|
||
}
|
||
|
||
<span class="hljs-comment">// Let Node.js handle all other specifiers.</span>
|
||
<span class="hljs-keyword">return</span> defaultResolve(specifier, context, defaultResolve);
|
||
}
|
||
|
||
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getFormat</span>(<span class="hljs-params">url, context, defaultGetFormat</span>) </span>{
|
||
<span class="hljs-comment">// Now that we patched resolve to let CoffeeScript URLs through, we need to</span>
|
||
<span class="hljs-comment">// tell Node.js what format such URLs should be interpreted as. For the</span>
|
||
<span class="hljs-comment">// purposes of this loader, all CoffeeScript URLs are ES modules.</span>
|
||
<span class="hljs-keyword">if</span> (extensionsRegex.test(url)) {
|
||
<span class="hljs-keyword">return</span> {
|
||
<span class="hljs-attr">format</span>: <span class="hljs-string">'module'</span>
|
||
};
|
||
}
|
||
|
||
<span class="hljs-comment">// Let Node.js handle all other URLs.</span>
|
||
<span class="hljs-keyword">return</span> defaultGetFormat(url, context, defaultGetFormat);
|
||
}
|
||
|
||
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformSource</span>(<span class="hljs-params">source, context, defaultTransformSource</span>) </span>{
|
||
<span class="hljs-keyword">const</span> { url, format } = context;
|
||
|
||
<span class="hljs-keyword">if</span> (extensionsRegex.test(url)) {
|
||
<span class="hljs-keyword">return</span> {
|
||
<span class="hljs-attr">source</span>: CoffeeScript.compile(source, { <span class="hljs-attr">bare</span>: <span class="hljs-literal">true</span> })
|
||
};
|
||
}
|
||
|
||
<span class="hljs-comment">// Let Node.js handle all other sources.</span>
|
||
<span class="hljs-keyword">return</span> defaultTransformSource(source, context, defaultTransformSource);
|
||
}</code></pre>
|
||
<pre><code class="language-coffee"><span class="hljs-comment"># main.coffee</span>
|
||
<span class="hljs-keyword">import</span> { scream } <span class="hljs-keyword">from</span> <span class="hljs-string">'./scream.coffee'</span>
|
||
<span class="hljs-built_in">console</span>.log scream <span class="hljs-string">'hello, world'</span>
|
||
|
||
<span class="hljs-keyword">import</span> { version } <span class="hljs-keyword">from</span> <span class="hljs-string">'process'</span>
|
||
<span class="hljs-built_in">console</span>.log <span class="hljs-string">"Brought to you by Node.js version <span class="hljs-subst">#{version}</span>"</span></code></pre>
|
||
<pre><code class="language-coffee"><span class="hljs-comment"># scream.coffee</span>
|
||
<span class="hljs-keyword">export</span> scream = <span class="hljs-function"><span class="hljs-params">(str)</span> -></span> str.toUpperCase()</code></pre>
|
||
<p>With the preceding loader, running
|
||
<code>node --experimental-loader ./coffeescript-loader.mjs main.coffee</code>
|
||
causes <code>main.coffee</code> to be turned into JavaScript after its source code is
|
||
loaded from disk but before Node.js executes it; and so on for any <code>.coffee</code>,
|
||
<code>.litcoffee</code> or <code>.coffee.md</code> files referenced via <code>import</code> statements of any
|
||
loaded file.</p>
|
||
<h2>Resolution algorithm<span><a class="mark" href="#esm_resolution_algorithm" id="esm_resolution_algorithm">#</a></span></h2>
|
||
<h3>Features<span><a class="mark" href="#esm_features" id="esm_features">#</a></span></h3>
|
||
<p>The resolver has the following properties:</p>
|
||
<ul>
|
||
<li>FileURL-based resolution as is used by ES modules</li>
|
||
<li>Support for builtin module loading</li>
|
||
<li>Relative and absolute URL resolution</li>
|
||
<li>No default extensions</li>
|
||
<li>No folder mains</li>
|
||
<li>Bare specifier package resolution lookup through node_modules</li>
|
||
</ul>
|
||
<h3>Resolver algorithm<span><a class="mark" href="#esm_resolver_algorithm" id="esm_resolver_algorithm">#</a></span></h3>
|
||
<p>The algorithm to load an ES module specifier is given through the
|
||
<strong>ESM_RESOLVE</strong> method below. It returns the resolved URL for a
|
||
module specifier relative to a parentURL.</p>
|
||
<p>The algorithm to determine the module format of a resolved URL is
|
||
provided by <strong>ESM_FORMAT</strong>, which returns the unique module
|
||
format for any file. The <em>"module"</em> format is returned for an ECMAScript
|
||
Module, while the <em>"commonjs"</em> format is used to indicate loading through the
|
||
legacy CommonJS loader. Additional formats such as <em>"addon"</em> can be extended in
|
||
future updates.</p>
|
||
<p>In the following algorithms, all subroutine errors are propagated as errors
|
||
of these top-level routines unless stated otherwise.</p>
|
||
<p><em>defaultConditions</em> is the conditional environment name array,
|
||
<code>["node", "import"]</code>.</p>
|
||
<p>The resolver can throw the following errors:</p>
|
||
<ul>
|
||
<li><em>Invalid Module Specifier</em>: Module specifier is an invalid URL, package name
|
||
or package subpath specifier.</li>
|
||
<li><em>Invalid Package Configuration</em>: package.json configuration is invalid or
|
||
contains an invalid configuration.</li>
|
||
<li><em>Invalid Package Target</em>: Package exports or imports define a target module
|
||
for the package that is an invalid type or string target.</li>
|
||
<li><em>Package Path Not Exported</em>: Package exports do not define or permit a target
|
||
subpath in the package for the given module.</li>
|
||
<li><em>Package Import Not Defined</em>: Package imports do not define the specifier.</li>
|
||
<li><em>Module Not Found</em>: The package or module requested does not exist.</li>
|
||
</ul>
|
||
<h3>Resolver Algorithm Specification<span><a class="mark" href="#esm_resolver_algorithm_specification" id="esm_resolver_algorithm_specification">#</a></span></h3>
|
||
<p><strong>ESM_RESOLVE</strong>(<em>specifier</em>, <em>parentURL</em>)</p>
|
||
<blockquote>
|
||
<ol>
|
||
<li>Let <em>resolved</em> be <strong>undefined</strong>.</li>
|
||
<li>If <em>specifier</em> is a valid URL, then
|
||
<ol>
|
||
<li>Set <em>resolved</em> to the result of parsing and reserializing
|
||
<em>specifier</em> as a URL.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise, if <em>specifier</em> starts with <em>"/"</em>, <em>"./"</em> or <em>"../"</em>, then
|
||
<ol>
|
||
<li>Set <em>resolved</em> to the URL resolution of <em>specifier</em> relative to
|
||
<em>parentURL</em>.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise, if <em>specifier</em> starts with <em>"#"</em>, then
|
||
<ol>
|
||
<li>Set <em>resolved</em> to the destructured value of the result of
|
||
<strong>PACKAGE_IMPORTS_RESOLVE</strong>(<em>specifier</em>, <em>parentURL</em>,
|
||
<em>defaultConditions</em>).</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise,
|
||
<ol>
|
||
<li>Note: <em>specifier</em> is now a bare specifier.</li>
|
||
<li>Set <em>resolved</em> the result of
|
||
<strong>PACKAGE_RESOLVE</strong>(<em>specifier</em>, <em>parentURL</em>).</li>
|
||
</ol>
|
||
</li>
|
||
<li>If <em>resolved</em> contains any percent encodings of <em>"/"</em> or <em>"\"</em> (<em>"%2f"</em>
|
||
and <em>"%5C"</em> respectively), then
|
||
<ol>
|
||
<li>Throw an <em>Invalid Module Specifier</em> error.</li>
|
||
</ol>
|
||
</li>
|
||
<li>If the file at <em>resolved</em> is a directory, then
|
||
<ol>
|
||
<li>Throw an <em>Unsupported Directory Import</em> error.</li>
|
||
</ol>
|
||
</li>
|
||
<li>If the file at <em>resolved</em> does not exist, then
|
||
<ol>
|
||
<li>Throw a <em>Module Not Found</em> error.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Set <em>resolved</em> to the real path of <em>resolved</em>.</li>
|
||
<li>Let <em>format</em> be the result of <strong>ESM_FORMAT</strong>(<em>resolved</em>).</li>
|
||
<li>Load <em>resolved</em> as module format, <em>format</em>.</li>
|
||
<li>Return <em>resolved</em>.</li>
|
||
</ol>
|
||
</blockquote>
|
||
<p><strong>PACKAGE_RESOLVE</strong>(<em>packageSpecifier</em>, <em>parentURL</em>)</p>
|
||
<blockquote>
|
||
<ol>
|
||
<li>Let <em>packageName</em> be <strong>undefined</strong>.</li>
|
||
<li>If <em>packageSpecifier</em> is an empty string, then
|
||
<ol>
|
||
<li>Throw an <em>Invalid Module Specifier</em> error.</li>
|
||
</ol>
|
||
</li>
|
||
<li>If <em>packageSpecifier</em> does not start with <em>"@"</em>, then
|
||
<ol>
|
||
<li>Set <em>packageName</em> to the substring of <em>packageSpecifier</em> until the first
|
||
<em>"/"</em> separator or the end of the string.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise,
|
||
<ol>
|
||
<li>If <em>packageSpecifier</em> does not contain a <em>"/"</em> separator, then
|
||
<ol>
|
||
<li>Throw an <em>Invalid Module Specifier</em> error.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Set <em>packageName</em> to the substring of <em>packageSpecifier</em>
|
||
until the second <em>"/"</em> separator or the end of the string.</li>
|
||
</ol>
|
||
</li>
|
||
<li>If <em>packageName</em> starts with <em>"."</em> or contains <em>"\"</em> or <em>"%"</em>, then
|
||
<ol>
|
||
<li>Throw an <em>Invalid Module Specifier</em> error.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Let <em>packageSubpath</em> be <em>"."</em> concatenated with the substring of
|
||
<em>packageSpecifier</em> from the position at the length of <em>packageName</em>.</li>
|
||
<li>Let <em>selfUrl</em> be the result of
|
||
<strong>PACKAGE_SELF_RESOLVE</strong>(<em>packageName</em>, <em>packageSubpath</em>, <em>parentURL</em>).</li>
|
||
<li>If <em>selfUrl</em> is not <strong>undefined</strong>, return <em>selfUrl</em>.</li>
|
||
<li>If <em>packageSubpath</em> is <em>"."</em> and <em>packageName</em> is a Node.js builtin
|
||
module, then
|
||
<ol>
|
||
<li>Return the string <em>"node:"</em> concatenated with <em>packageSpecifier</em>.</li>
|
||
</ol>
|
||
</li>
|
||
<li>While <em>parentURL</em> is not the file system root,
|
||
<ol>
|
||
<li>Let <em>packageURL</em> be the URL resolution of <em>"node_modules/"</em>
|
||
concatenated with <em>packageSpecifier</em>, relative to <em>parentURL</em>.</li>
|
||
<li>Set <em>parentURL</em> to the parent folder URL of <em>parentURL</em>.</li>
|
||
<li>If the folder at <em>packageURL</em> does not exist, then
|
||
<ol>
|
||
<li>Set <em>parentURL</em> to the parent URL path of <em>parentURL</em>.</li>
|
||
<li>Continue the next loop iteration.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Let <em>pjson</em> be the result of <strong>READ_PACKAGE_JSON</strong>(<em>packageURL</em>).</li>
|
||
<li>If <em>pjson</em> is not <strong>null</strong> and <em>pjson</em>.<em>exports</em> is not <strong>null</strong> or
|
||
<strong>undefined</strong>, then
|
||
<ol>
|
||
<li>Let <em>exports</em> be <em>pjson.exports</em>.</li>
|
||
<li>Return the <em>resolved</em> destructured value of the result of
|
||
<strong>PACKAGE_EXPORTS_RESOLVE</strong>(<em>packageURL</em>, <em>packageSubpath</em>,
|
||
<em>pjson.exports</em>, <em>defaultConditions</em>).</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise, if <em>packageSubpath</em> is equal to <em>"."</em>, then
|
||
<ol>
|
||
<li>Return the result applying the legacy <strong>LOAD_AS_DIRECTORY</strong>
|
||
CommonJS resolver to <em>packageURL</em>, throwing a <em>Module Not Found</em>
|
||
error for no resolution.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise,
|
||
<ol>
|
||
<li>Return the URL resolution of <em>packageSubpath</em> in <em>packageURL</em>.</li>
|
||
</ol>
|
||
</li>
|
||
</ol>
|
||
</li>
|
||
<li>Throw a <em>Module Not Found</em> error.</li>
|
||
</ol>
|
||
</blockquote>
|
||
<p><strong>PACKAGE_SELF_RESOLVE</strong>(<em>packageName</em>, <em>packageSubpath</em>, <em>parentURL</em>)</p>
|
||
<blockquote>
|
||
<ol>
|
||
<li>Let <em>packageURL</em> be the result of <strong>READ_PACKAGE_SCOPE</strong>(<em>parentURL</em>).</li>
|
||
<li>If <em>packageURL</em> is <strong>null</strong>, then
|
||
<ol>
|
||
<li>Return <strong>undefined</strong>.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Let <em>pjson</em> be the result of <strong>READ_PACKAGE_JSON</strong>(<em>packageURL</em>).</li>
|
||
<li>If <em>pjson</em> is <strong>null</strong> or if <em>pjson</em>.<em>exports</em> is <strong>null</strong> or
|
||
<strong>undefined</strong>, then
|
||
<ol>
|
||
<li>Return <strong>undefined</strong>.</li>
|
||
</ol>
|
||
</li>
|
||
<li>If <em>pjson.name</em> is equal to <em>packageName</em>, then
|
||
<ol>
|
||
<li>Return the <em>resolved</em> destructured value of the result of
|
||
<strong>PACKAGE_EXPORTS_RESOLVE</strong>(<em>packageURL</em>, <em>subpath</em>, <em>pjson.exports</em>,
|
||
<em>defaultConditions</em>).</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise, return <strong>undefined</strong>.</li>
|
||
</ol>
|
||
</blockquote>
|
||
<p><strong>PACKAGE_EXPORTS_RESOLVE</strong>(<em>packageURL</em>, <em>subpath</em>, <em>exports</em>, <em>conditions</em>)</p>
|
||
<blockquote>
|
||
<ol>
|
||
<li>If <em>exports</em> is an Object with both a key starting with <em>"."</em> and a key not
|
||
starting with <em>"."</em>, throw an <em>Invalid Package Configuration</em> error.</li>
|
||
<li>If <em>subpath</em> is equal to <em>"."</em>, then
|
||
<ol>
|
||
<li>Let <em>mainExport</em> be <strong>undefined</strong>.</li>
|
||
<li>If <em>exports</em> is a String or Array, or an Object containing no keys
|
||
starting with <em>"."</em>, then
|
||
<ol>
|
||
<li>Set <em>mainExport</em> to <em>exports</em>.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise if <em>exports</em> is an Object containing a <em>"."</em> property, then
|
||
<ol>
|
||
<li>Set <em>mainExport</em> to <em>exports</em>[<em>"."</em>].</li>
|
||
</ol>
|
||
</li>
|
||
<li>If <em>mainExport</em> is not <strong>undefined</strong>, then
|
||
<ol>
|
||
<li>Let <em>resolved</em> be the result of <strong>PACKAGE_TARGET_RESOLVE</strong>(
|
||
<em>packageURL</em>, <em>mainExport</em>, <em>""</em>, <strong>false</strong>, <strong>false</strong>,
|
||
<em>conditions</em>).</li>
|
||
<li>If <em>resolved</em> is not <strong>null</strong> or <strong>undefined</strong>, then
|
||
<ol>
|
||
<li>Return <em>resolved</em>.</li>
|
||
</ol>
|
||
</li>
|
||
</ol>
|
||
</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise, if <em>exports</em> is an Object and all keys of <em>exports</em> start with
|
||
<em>"."</em>, then
|
||
<ol>
|
||
<li>Let <em>matchKey</em> be the string <em>"./"</em> concatenated with <em>subpath</em>.</li>
|
||
<li>Let <em>resolvedMatch</em> be result of <strong>PACKAGE_IMPORTS_EXPORTS_RESOLVE</strong>(
|
||
<em>matchKey</em>, <em>exports</em>, <em>packageURL</em>, <strong>false</strong>, <em>conditions</em>).</li>
|
||
<li>If <em>resolvedMatch</em>.<em>resolve</em> is not <strong>null</strong> or <strong>undefined</strong>, then
|
||
<ol>
|
||
<li>Return <em>resolvedMatch</em>.</li>
|
||
</ol>
|
||
</li>
|
||
</ol>
|
||
</li>
|
||
<li>Throw a <em>Package Path Not Exported</em> error.</li>
|
||
</ol>
|
||
</blockquote>
|
||
<p><strong>PACKAGE_IMPORTS_RESOLVE</strong>(<em>specifier</em>, <em>parentURL</em>, <em>conditions</em>)</p>
|
||
<blockquote>
|
||
<ol>
|
||
<li>Assert: <em>specifier</em> begins with <em>"#"</em>.</li>
|
||
<li>If <em>specifier</em> is exactly equal to <em>"#"</em> or starts with <em>"#/"</em>, then
|
||
<ol>
|
||
<li>Throw an <em>Invalid Module Specifier</em> error.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Let <em>packageURL</em> be the result of <strong>READ_PACKAGE_SCOPE</strong>(<em>parentURL</em>).</li>
|
||
<li>If <em>packageURL</em> is not <strong>null</strong>, then
|
||
<ol>
|
||
<li>Let <em>pjson</em> be the result of <strong>READ_PACKAGE_JSON</strong>(<em>packageURL</em>).</li>
|
||
<li>If <em>pjson.imports</em> is a non-null Object, then
|
||
<ol>
|
||
<li>Let <em>resolvedMatch</em> be the result of
|
||
<strong>PACKAGE_IMPORTS_EXPORTS_RESOLVE</strong>(<em>specifier</em>, <em>pjson.imports</em>,
|
||
<em>packageURL</em>, <strong>true</strong>, <em>conditions</em>).</li>
|
||
<li>If <em>resolvedMatch</em>.<em>resolve</em> is not <strong>null</strong> or <strong>undefined</strong>, then
|
||
<ol>
|
||
<li>Return <em>resolvedMatch</em>.</li>
|
||
</ol>
|
||
</li>
|
||
</ol>
|
||
</li>
|
||
</ol>
|
||
</li>
|
||
<li>Throw a <em>Package Import Not Defined</em> error.</li>
|
||
</ol>
|
||
</blockquote>
|
||
<p><strong>PACKAGE_IMPORTS_EXPORTS_RESOLVE</strong>(<em>matchKey</em>, <em>matchObj</em>, <em>packageURL</em>,
|
||
<em>isImports</em>, <em>conditions</em>)</p>
|
||
<blockquote>
|
||
<ol>
|
||
<li>If <em>matchKey</em> is a key of <em>matchObj</em>, and does not end in <em>"*"</em>, then
|
||
<ol>
|
||
<li>Let <em>target</em> be the value of <em>matchObj</em>[<em>matchKey</em>].</li>
|
||
<li>Let <em>resolved</em> be the result of <strong>PACKAGE_TARGET_RESOLVE</strong>(
|
||
<em>packageURL</em>, <em>target</em>, <em>""</em>, <strong>false</strong>, <em>isImports</em>, <em>conditions</em>).</li>
|
||
<li>Return the object <em>{ resolved, exact: <strong>true</strong> }</em>.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Let <em>expansionKeys</em> be the list of keys of <em>matchObj</em> ending in <em>"/"</em>
|
||
or <em>"*"</em>, sorted by length descending.</li>
|
||
<li>For each key <em>expansionKey</em> in <em>expansionKeys</em>, do
|
||
<ol>
|
||
<li>If <em>expansionKey</em> ends in <em>"*"</em> and <em>matchKey</em> starts with but is
|
||
not equal to the substring of <em>expansionKey</em> excluding the last <em>"*"</em>
|
||
character, then
|
||
<ol>
|
||
<li>Let <em>target</em> be the value of <em>matchObj</em>[<em>expansionKey</em>].</li>
|
||
<li>Let <em>subpath</em> be the substring of <em>matchKey</em> starting at the
|
||
index of the length of <em>expansionKey</em> minus one.</li>
|
||
<li>Let <em>resolved</em> be the result of <strong>PACKAGE_TARGET_RESOLVE</strong>(
|
||
<em>packageURL</em>, <em>target</em>, <em>subpath</em>, <strong>true</strong>, <em>isImports</em>,
|
||
<em>conditions</em>).</li>
|
||
<li>Return the object <em>{ resolved, exact: <strong>true</strong> }</em>.</li>
|
||
</ol>
|
||
</li>
|
||
<li>If <em>matchKey</em> starts with <em>expansionKey</em>, then
|
||
<ol>
|
||
<li>Let <em>target</em> be the value of <em>matchObj</em>[<em>expansionKey</em>].</li>
|
||
<li>Let <em>subpath</em> be the substring of <em>matchKey</em> starting at the
|
||
index of the length of <em>expansionKey</em>.</li>
|
||
<li>Let <em>resolved</em> be the result of <strong>PACKAGE_TARGET_RESOLVE</strong>(
|
||
<em>packageURL</em>, <em>target</em>, <em>subpath</em>, <strong>false</strong>, <em>isImports</em>,
|
||
<em>conditions</em>).</li>
|
||
<li>Return the object <em>{ resolved, exact: <strong>false</strong> }</em>.</li>
|
||
</ol>
|
||
</li>
|
||
</ol>
|
||
</li>
|
||
<li>Return the object <em>{ resolved: <strong>null</strong>, exact: <strong>true</strong> }</em>.</li>
|
||
</ol>
|
||
</blockquote>
|
||
<p><strong>PACKAGE_TARGET_RESOLVE</strong>(<em>packageURL</em>, <em>target</em>, <em>subpath</em>, <em>pattern</em>,
|
||
<em>internal</em>, <em>conditions</em>)</p>
|
||
<blockquote>
|
||
<ol>
|
||
<li>If <em>target</em> is a String, then
|
||
<ol>
|
||
<li>If <em>pattern</em> is <strong>false</strong>, <em>subpath</em> has non-zero length and <em>target</em>
|
||
does not end with <em>"/"</em>, throw an <em>Invalid Module Specifier</em> error.</li>
|
||
<li>If <em>target</em> does not start with <em>"./"</em>, then
|
||
<ol>
|
||
<li>If <em>internal</em> is <strong>true</strong> and <em>target</em> does not start with <em>"../"</em> or
|
||
<em>"/"</em> and is not a valid URL, then
|
||
<ol>
|
||
<li>If <em>pattern</em> is <strong>true</strong>, then
|
||
<ol>
|
||
<li>Return <strong>PACKAGE_RESOLVE</strong>(<em>target</em> with every instance of
|
||
<em>"*"</em> replaced by <em>subpath</em>, <em>packageURL</em> + <em>"/"</em>)_.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Return <strong>PACKAGE_RESOLVE</strong>(<em>target</em> + <em>subpath</em>,
|
||
<em>packageURL</em> + <em>"/"</em>)_.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise, throw an <em>Invalid Package Target</em> error.</li>
|
||
</ol>
|
||
</li>
|
||
<li>If <em>target</em> split on <em>"/"</em> or <em>"\"</em> contains any <em>"."</em>, <em>".."</em> or
|
||
<em>"node_modules"</em> segments after the first segment, throw an
|
||
<em>Invalid Package Target</em> error.</li>
|
||
<li>Let <em>resolvedTarget</em> be the URL resolution of the concatenation of
|
||
<em>packageURL</em> and <em>target</em>.</li>
|
||
<li>Assert: <em>resolvedTarget</em> is contained in <em>packageURL</em>.</li>
|
||
<li>If <em>subpath</em> split on <em>"/"</em> or <em>"\"</em> contains any <em>"."</em>, <em>".."</em> or
|
||
<em>"node_modules"</em> segments, throw an <em>Invalid Module Specifier</em> error.</li>
|
||
<li>If <em>pattern</em> is <strong>true</strong>, then
|
||
<ol>
|
||
<li>Return the URL resolution of <em>resolvedTarget</em> with every instance of
|
||
<em>"*"</em> replaced with <em>subpath</em>.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise,
|
||
<ol>
|
||
<li>Return the URL resolution of the concatenation of <em>subpath</em> and
|
||
<em>resolvedTarget</em>.</li>
|
||
</ol>
|
||
</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise, if <em>target</em> is a non-null Object, then
|
||
<ol>
|
||
<li>If <em>exports</em> contains any index property keys, as defined in ECMA-262
|
||
<a href="https://tc39.es/ecma262/#integer-index">6.1.7 Array Index</a>, throw an <em>Invalid Package Configuration</em> error.</li>
|
||
<li>For each property <em>p</em> of <em>target</em>, in object insertion order as,
|
||
<ol>
|
||
<li>If <em>p</em> equals <em>"default"</em> or <em>conditions</em> contains an entry for <em>p</em>,
|
||
then
|
||
<ol>
|
||
<li>Let <em>targetValue</em> be the value of the <em>p</em> property in <em>target</em>.</li>
|
||
<li>Let <em>resolved</em> be the result of <strong>PACKAGE_TARGET_RESOLVE</strong>(
|
||
<em>packageURL</em>, <em>targetValue</em>, <em>subpath</em>, <em>pattern</em>, <em>internal</em>,
|
||
<em>conditions</em>).</li>
|
||
<li>If <em>resolved</em> is equal to <strong>undefined</strong>, continue the loop.</li>
|
||
<li>Return <em>resolved</em>.</li>
|
||
</ol>
|
||
</li>
|
||
</ol>
|
||
</li>
|
||
<li>Return <strong>undefined</strong>.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise, if <em>target</em> is an Array, then
|
||
<ol>
|
||
<li>If _target.length is zero, return <strong>null</strong>.</li>
|
||
<li>For each item <em>targetValue</em> in <em>target</em>, do
|
||
<ol>
|
||
<li>Let <em>resolved</em> be the result of <strong>PACKAGE_TARGET_RESOLVE</strong>(
|
||
<em>packageURL</em>, <em>targetValue</em>, <em>subpath</em>, <em>pattern</em>, <em>internal</em>,
|
||
<em>conditions</em>), continuing the loop on any <em>Invalid Package Target</em>
|
||
error.</li>
|
||
<li>If <em>resolved</em> is <strong>undefined</strong>, continue the loop.</li>
|
||
<li>Return <em>resolved</em>.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Return or throw the last fallback resolution <strong>null</strong> return or error.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise, if <em>target</em> is <em>null</em>, return <strong>null</strong>.</li>
|
||
<li>Otherwise throw an <em>Invalid Package Target</em> error.</li>
|
||
</ol>
|
||
</blockquote>
|
||
<p><strong>ESM_FORMAT</strong>(<em>url</em>)</p>
|
||
<blockquote>
|
||
<ol>
|
||
<li>Assert: <em>url</em> corresponds to an existing file.</li>
|
||
<li>Let <em>pjson</em> be the result of <strong>READ_PACKAGE_SCOPE</strong>(<em>url</em>).</li>
|
||
<li>If <em>url</em> ends in <em>".mjs"</em>, then
|
||
<ol>
|
||
<li>Return <em>"module"</em>.</li>
|
||
</ol>
|
||
</li>
|
||
<li>If <em>url</em> ends in <em>".cjs"</em>, then
|
||
<ol>
|
||
<li>Return <em>"commonjs"</em>.</li>
|
||
</ol>
|
||
</li>
|
||
<li>If <em>pjson?.type</em> exists and is <em>"module"</em>, then
|
||
<ol>
|
||
<li>If <em>url</em> ends in <em>".js"</em>, then
|
||
<ol>
|
||
<li>Return <em>"module"</em>.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Throw an <em>Unsupported File Extension</em> error.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Otherwise,
|
||
<ol>
|
||
<li>Throw an <em>Unsupported File Extension</em> error.</li>
|
||
</ol>
|
||
</li>
|
||
</ol>
|
||
</blockquote>
|
||
<p><strong>READ_PACKAGE_SCOPE</strong>(<em>url</em>)</p>
|
||
<blockquote>
|
||
<ol>
|
||
<li>Let <em>scopeURL</em> be <em>url</em>.</li>
|
||
<li>While <em>scopeURL</em> is not the file system root,
|
||
<ol>
|
||
<li>Set <em>scopeURL</em> to the parent URL of <em>scopeURL</em>.</li>
|
||
<li>If <em>scopeURL</em> ends in a <em>"node_modules"</em> path segment, return <strong>null</strong>.</li>
|
||
<li>Let <em>pjson</em> be the result of <strong>READ_PACKAGE_JSON</strong>(<em>scopeURL</em>).</li>
|
||
<li>If <em>pjson</em> is not <strong>null</strong>, then
|
||
<ol>
|
||
<li>Return <em>pjson</em>.</li>
|
||
</ol>
|
||
</li>
|
||
</ol>
|
||
</li>
|
||
<li>Return <strong>null</strong>.</li>
|
||
</ol>
|
||
</blockquote>
|
||
<p><strong>READ_PACKAGE_JSON</strong>(<em>packageURL</em>)</p>
|
||
<blockquote>
|
||
<ol>
|
||
<li>Let <em>pjsonURL</em> be the resolution of <em>"package.json"</em> within <em>packageURL</em>.</li>
|
||
<li>If the file at <em>pjsonURL</em> does not exist, then
|
||
<ol>
|
||
<li>Return <strong>null</strong>.</li>
|
||
</ol>
|
||
</li>
|
||
<li>If the file at <em>packageURL</em> does not parse as valid JSON, then
|
||
<ol>
|
||
<li>Throw an <em>Invalid Package Configuration</em> error.</li>
|
||
</ol>
|
||
</li>
|
||
<li>Return the parsed JSON source of the file at <em>pjsonURL</em>.</li>
|
||
</ol>
|
||
</blockquote>
|
||
<h3>Customizing ESM specifier resolution algorithm<span><a class="mark" href="#esm_customizing_esm_specifier_resolution_algorithm" id="esm_customizing_esm_specifier_resolution_algorithm">#</a></span></h3>
|
||
<p>The current specifier resolution does not support all default behavior of
|
||
the CommonJS loader. One of the behavior differences is automatic resolution
|
||
of file extensions and the ability to import directories that have an index
|
||
file.</p>
|
||
<p>The <code>--experimental-specifier-resolution=[mode]</code> flag can be used to customize
|
||
the extension resolution algorithm. The default mode is <code>explicit</code>, which
|
||
requires the full path to a module be provided to the loader. To enable the
|
||
automatic extension resolution and importing from directories that include an
|
||
index file use the <code>node</code> mode.</p>
|
||
<pre><code class="language-console"><span class="hljs-meta">$</span><span class="bash"> node index.mjs</span>
|
||
success!
|
||
<span class="hljs-meta">$</span><span class="bash"> node index <span class="hljs-comment"># Failure!</span></span>
|
||
Error: Cannot find module
|
||
<span class="hljs-meta">$</span><span class="bash"> node --experimental-specifier-resolution=node index</span>
|
||
success!</code></pre>
|
||
<!-- Note: The cjs-module-lexer link should be kept in-sync with the deps version -->
|
||
<!-- API END -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html>
|