{
  "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. In the future <code>import()</code> can be used to create entry\npoints into ESM graphs at run time.</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 evalutation 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"
    }
  ]
}
