{
  "source": "doc/api/esm.md",
  "modules": [
    {
      "textRaw": "ECMAScript Modules",
      "name": "esm",
      "introduced_in": "v8.5.0",
      "stability": 1,
      "stabilityText": "Experimental",
      "desc": "<p>Node.js contains support for ES Modules based upon the\n<a href=\"https://github.com/nodejs/node-eps/blob/master/002-es-modules.md\">Node.js EP for ES Modules</a>.</p>\n<p>Not all features of the EP are complete and will be landing as both VM support\nand implementation is ready. Error messages are still being polished.</p>\n",
      "miscs": [
        {
          "textRaw": "Enabling",
          "name": "Enabling",
          "type": "misc",
          "desc": "<p>The <code>--experimental-modules</code> flag can be used to enable features for loading\nESM modules.</p>\n<p>Once this has been set, files ending with <code>.mjs</code> will be able to be loaded\nas ES Modules.</p>\n<pre><code class=\"lang-sh\">node --experimental-modules my-app.mjs\n</code></pre>\n"
        },
        {
          "textRaw": "Features",
          "name": "Features",
          "type": "misc",
          "miscs": [
            {
              "textRaw": "Supported",
              "name": "supported",
              "desc": "<p>Only the CLI argument for the main entry point to the program can be an entry\npoint into an ESM graph. Dynamic import can also be used to create entry points\ninto ESM graphs at runtime.</p>\n",
              "type": "misc",
              "displayName": "Supported"
            },
            {
              "textRaw": "Unsupported",
              "name": "unsupported",
              "desc": "<table>\n<thead>\n<tr>\n<th>Feature</th>\n<th>Reason</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>require(&#39;./foo.mjs&#39;)</code></td>\n<td>ES Modules have differing resolution and timing, use language standard <code>import()</code></td>\n</tr>\n<tr>\n<td><code>import()</code></td>\n<td>pending newer V8 release used in Node.js</td>\n</tr>\n<tr>\n<td><code>import.meta</code></td>\n<td>pending V8 implementation</td>\n</tr>\n</tbody>\n</table>\n",
              "type": "misc",
              "displayName": "Unsupported"
            }
          ]
        },
        {
          "textRaw": "Loader hooks",
          "name": "Loader hooks",
          "type": "misc",
          "desc": "<p>To customize the default module resolution, loader hooks can optionally be\nprovided via a <code>--loader ./loader-name.mjs</code> argument to Node.</p>\n<p>When hooks are used they only apply to ES module loading and not to any\nCommonJS modules loaded.</p>\n",
          "miscs": [
            {
              "textRaw": "Resolve hook",
              "name": "resolve_hook",
              "desc": "<p>The resolve hook returns the resolved file URL and module format for a\ngiven module specifier and parent file URL:</p>\n<pre><code class=\"lang-js\">import url from &#39;url&#39;;\n\nexport async function resolve(specifier, parentModuleURL, defaultResolver) {\n  return {\n    url: new URL(specifier, parentModuleURL).href,\n    format: &#39;esm&#39;\n  };\n}\n</code></pre>\n<p>The default NodeJS ES module resolution function is provided as a third\nargument to the resolver for easy compatibility workflows.</p>\n<p>In addition to returning the resolved file URL value, the resolve hook also\nreturns a <code>format</code> property specifying the module format of the resolved\nmodule. This can be one of the following:</p>\n<table>\n<thead>\n<tr>\n<th><code>format</code></th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>&quot;esm&quot;</code></td>\n<td>Load a standard JavaScript module</td>\n</tr>\n<tr>\n<td><code>&quot;cjs&quot;</code></td>\n<td>Load a node-style CommonJS module</td>\n</tr>\n<tr>\n<td><code>&quot;builtin&quot;</code></td>\n<td>Load a node builtin CommonJS module</td>\n</tr>\n<tr>\n<td><code>&quot;json&quot;</code></td>\n<td>Load a JSON file</td>\n</tr>\n<tr>\n<td><code>&quot;addon&quot;</code></td>\n<td>Load a <a href=\"addons.html\">C++ Addon</a></td>\n</tr>\n<tr>\n<td><code>&quot;dynamic&quot;</code></td>\n<td>Use a <a href=\"#esm_dynamic_instantiate_hook\">dynamic instantiate hook</a></td>\n</tr>\n</tbody>\n</table>\n<p>For example, a dummy loader to load JavaScript restricted to browser resolution\nrules with only JS file extension and Node builtin modules support could\nbe written:</p>\n<pre><code class=\"lang-js\">import url from &#39;url&#39;;\nimport path from &#39;path&#39;;\nimport process from &#39;process&#39;;\nimport Module from &#39;module&#39;;\n\nconst builtins = Module.builtinModules;\nconst JS_EXTENSIONS = new Set([&#39;.js&#39;, &#39;.mjs&#39;]);\n\nexport function resolve(specifier, parentModuleURL/*, defaultResolve */) {\n  if (builtins.includes(specifier)) {\n    return {\n      url: specifier,\n      format: &#39;builtin&#39;\n    };\n  }\n  if (/^\\.{0,2}[/]/.test(specifier) !== true &amp;&amp; !specifier.startsWith(&#39;file:&#39;)) {\n    // For node_modules support:\n    // return defaultResolve(specifier, parentModuleURL);\n    throw new Error(\n      `imports must begin with &#39;/&#39;, &#39;./&#39;, or &#39;../&#39;; &#39;${specifier}&#39; does not`);\n  }\n  const resolved = new url.URL(specifier, parentModuleURL);\n  const ext = path.extname(resolved.pathname);\n  if (!JS_EXTENSIONS.has(ext)) {\n    throw new Error(\n      `Cannot load file with non-JavaScript file extension ${ext}.`);\n  }\n  return {\n    url: resolved.href,\n    format: &#39;esm&#39;\n  };\n}\n</code></pre>\n<p>With this loader, running:</p>\n<pre><code class=\"lang-console\">NODE_OPTIONS=&#39;--experimental-modules --loader ./custom-loader.mjs&#39; node x.js\n</code></pre>\n<p>would load the module <code>x.js</code> as an ES module with relative resolution support\n(with <code>node_modules</code> loading skipped in this example).</p>\n",
              "type": "misc",
              "displayName": "Resolve hook"
            },
            {
              "textRaw": "Dynamic instantiate hook",
              "name": "dynamic_instantiate_hook",
              "desc": "<p>To create a custom dynamic module that doesn&#39;t correspond to one of the\nexisting <code>format</code> interpretations, the <code>dynamicInstantiate</code> hook can be used.\nThis hook is called only for modules that return <code>format: &quot;dynamic&quot;</code> from\nthe <code>resolve</code> hook.</p>\n<pre><code class=\"lang-js\">export async function dynamicInstantiate(url) {\n  return {\n    exports: [&#39;customExportName&#39;],\n    execute: (exports) =&gt; {\n      // get and set functions provided for pre-allocated export names\n      exports.customExportName.set(&#39;value&#39;);\n    }\n  };\n}\n</code></pre>\n<p>With the list of module exports provided upfront, the <code>execute</code> function will\nthen be called at the exact point of module evaluation order for that module\nin the import tree.</p>\n",
              "type": "misc",
              "displayName": "Dynamic instantiate hook"
            }
          ]
        }
      ],
      "modules": [
        {
          "textRaw": "Notable differences between `import` and `require`",
          "name": "notable_differences_between_`import`_and_`require`",
          "modules": [
            {
              "textRaw": "No NODE_PATH",
              "name": "no_node_path",
              "desc": "<p><code>NODE_PATH</code> is not part of resolving <code>import</code> specifiers. Please use symlinks\nif this behavior is desired.</p>\n",
              "type": "module",
              "displayName": "No NODE_PATH"
            },
            {
              "textRaw": "URL based paths",
              "name": "url_based_paths",
              "desc": "<p>ESM are resolved and cached based upon <a href=\"https://url.spec.whatwg.org/\">URL</a>\nsemantics. This means that files containing special characters such as <code>#</code> and\n<code>?</code> need to be escaped.</p>\n<p>Modules will be loaded multiple times if the <code>import</code> specifier used to resolve\nthem have a different query or fragment.</p>\n<pre><code class=\"lang-js\">import &#39;./foo?query=1&#39;; // loads ./foo with query of &quot;?query=1&quot;\nimport &#39;./foo?query=2&#39;; // loads ./foo with query of &quot;?query=2&quot;\n</code></pre>\n<p>For now, only modules using the <code>file:</code> protocol can be loaded.</p>\n",
              "type": "module",
              "displayName": "URL based paths"
            }
          ],
          "properties": [
            {
              "textRaw": "No `require.extensions`",
              "name": "extensions`",
              "desc": "<p><code>require.extensions</code> is not used by <code>import</code>. The expectation is that loader\nhooks can provide this workflow in the future.</p>\n"
            },
            {
              "textRaw": "No `require.cache`",
              "name": "cache`",
              "desc": "<p><code>require.cache</code> is not used by <code>import</code>. It has a separate cache.</p>\n"
            }
          ],
          "type": "module",
          "displayName": "Notable differences between `import` and `require`"
        },
        {
          "textRaw": "Interop with existing modules",
          "name": "interop_with_existing_modules",
          "desc": "<p>All CommonJS, JSON, and C++ modules can be used with <code>import</code>.</p>\n<p>Modules loaded this way will only be loaded once, even if their query\nor fragment string differs between <code>import</code> statements.</p>\n<p>When loaded via <code>import</code> these modules will provide a single <code>default</code> export\nrepresenting the value of <code>module.exports</code> at the time they finished evaluating.</p>\n<pre><code class=\"lang-js\">import fs from &#39;fs&#39;;\nfs.readFile(&#39;./foo.txt&#39;, (err, body) =&gt; {\n  if (err) {\n    console.error(err);\n  } else {\n    console.log(body);\n  }\n});\n</code></pre>\n",
          "type": "module",
          "displayName": "Interop with existing modules"
        }
      ],
      "type": "module",
      "displayName": "esm"
    }
  ]
}
