# esm-sync

**This is a fork of [esm](https://github.com/standard-things/esm)**: the brilliantly simple, babel-less, bundle-less ECMAScript module loader (which is now unmaintained).

This fork includes:

* Assorted updates from [@httptoolkit/esm](https://github.com/httptoolkit/esm) and [esm-wallaby fork](https://www.npmjs.com/package/esm-wallaby), adding support for ES modules containing new ES syntax, supporting modern Node versions (at least up to v22), and supporting `node:*` imports.
* Support for modules that use the `const require = createRequire(...)` pattern (or declare any other global `require` variable) which are otherwise unusable with `esm`.
* Support for modules that use only an `exports` map in their package.json, without a `main` field, which are otherwise unresolvable (reporting "Cannot find module", even though `import()` works correctly).
* Built-in types for use in TypeScript projects

This package is maintained for use in the [import-sync](https://github.com/nktnet1/import-sync) project.

You may also be interested in [jiti](https://github.com/unjs/jiti), which provides legacy (deprecated, but still reliable) support for synchronous imports.

## Install

```sh
npm install esm-sync
```

## Getting started

There are two ways to enable `esm-sync`.

1. Enable `esm-sync` for packages:

   Use `esm-sync` to load the main ES module and export it as CommonJS.

    __index.js__
    ```js
    // Set options as a parameter, environment variable, or rc file.
    require = require("esm-sync")(module/*, options*/)
    module.exports = require("./main.js")
    ```
    __main.js__
    ```js
    // ESM syntax is supported.
    export {}
    ```

2. Enable `esm-sync` for local runs:

    ```shell
    node -r esm-sync main.js
    ```
    :bulb: Omit the filename to enable `esm-sync` in the REPL.

## Features

:clap: By default, :100: percent CJS interoperability is enabled so you can get stuff done.<br>
:lock: `.mjs` files are limited to basic functionality without support for `esm-sync` options.

Out of the box `esm-sync` just works, no configuration necessary, and supports:

* Passing all applicable [test262](https://github.com/tc39/test262) compliance tests
* [`import`](https://ponyfoo.com/articles/es6-modules-in-depth#import)/[`export`](https://ponyfoo.com/articles/es6-modules-in-depth#export)
* [`import.meta`](https://github.com/tc39/proposal-import-meta)
* [Dynamic `import`](https://github.com/tc39/proposal-dynamic-import)
* [Live bindings](https://ponyfoo.com/articles/es6-modules-in-depth#bindings-not-values)
* [File URI scheme](https://en.wikipedia.org/wiki/File_URI_scheme)
* Node `stdin`, [`--eval`](https://nodejs.org/api/cli.html#cli_e_eval_script), [`--print`](https://nodejs.org/api/cli.html#cli_p_print_script) flags
* Node [`--check`](https://nodejs.org/api/cli.html#cli_c_check) flag _(Node 10+)_

## Options

Specify options with one of the following:

* `"esm"` field in `package.json`
* CJS/ESM in an `.esmrc.js`, `.esmrc.cjs`, or `.esmrc.mjs` file
* [JSON6](https://github.com/d3x0r/json6) in an `.esmrc` or `.esmrc.json` file
* JSON6 or file path in the `ESM_OPTIONS` environment variable
* `ESM_DISABLE_CACHE` environment variable

<table>
<tr>
  <td colspan="2"><code>{</code></td>
</tr>
<tr>
  <td valign="top"><code>"cjs":true</code></td>
  <td>
    <p>A boolean or object for toggling CJS features in ESM.</p>
    <details>
      <summary>Features</summary>
      <table>
      <tr>
        <td colspan="2"><code>{</code></td>
      </tr>
      <tr>
        <td valign="top"><code>"cache":true</code></td>
        <td>
          <p>A boolean for storing ES modules in <code>require.cache</code>.</p>
        </td>
      </tr>
      <tr>
        <td valign="top"><code>"esModule":true</code></td>
        <td>
          <p>A boolean for <code>__esModule</code> interoperability.</p>
        </td>
      </tr>
      <tr>
        <td valign="top"><code>"extensions":true</code></td>
        <td>
          <p>A boolean for respecting <code>require.extensions</code> in ESM.</p>
        </td>
      </tr>
      <tr>
        <td valign="top"><code>"mutableNamespace":true</code></td>
        <td>
          <p>A boolean for mutable <a href="https://ponyfoo.com/articles/es6-modules-in-depth#import-all-the-things">namespace objects</a>.</p>
        </td>
      </tr>
      <tr>
        <td valign="top"><code>"namedExports":true</code></td>
        <td>
          <p>A boolean for <a href="https://ponyfoo.com/articles/es6-modules-in-depth#importing-named-exports">importing named exports</a> of CJS modules.</p>
        </td>
      </tr>
      <tr>
        <td valign="top"><code>"paths":true</code></td>
        <td>
          <p>A boolean for following CJS <a href="https://github.com/nodejs/node-eps/blob/master/002-es-modules.md#432-removal-of-non-local-dependencies">path rules</a> in ESM.</p>
        </td>
      </tr>
      <tr>
        <td valign="top"><code>"vars":true</code></td>
        <td>
          <p>A boolean for <code>__dirname</code>, <code>__filename</code>, and <code>require</code> in ESM.</p>
        </td>
      </tr>
      <tr>
        <td valign="top"><code>"dedefault":false</code></td>
        <td>
          <p>A boolean for requiring ES modules without the dangling <code>require().default</code>.</p>
        </td>
      </tr>
      <tr>
        <td valign="top"><code>"topLevelReturn":false</code></td>
        <td>
          <p>A boolean for top-level <code>return</code> support.</p>
        </td>
      </tr>
      <tr>
        <td colspan="2"><code>}</code></td>
      </tr>
      </table>
    </details>
  </td>
</tr>
<tr>
  <td valign="top"><code>"mainFields":["main"]</code></td>
  <td>
    <p>An array of fields checked when importing a package.</p>
  </td>
</tr>
<tr>
  <td valign="top"><code>"mode":"auto"</code></td>
  <td>
    <p>A string mode:</p>
    <ul>
      <li><code>"auto"</code> detect files with <code>import</code>, <code>import.meta</code>, <code>export</code>,<br><a href="https://github.com/tc39/proposal-modules-pragma"><code>"use module"</code></a>, or <code>.mjs</code> as ESM.</li>
      <li><code>"all"</code> files besides those with <code>"use script"</code> or <code>.cjs</code> are treated as ESM.</li>
      <li><code>"strict"</code> to treat <strong>only</strong> <code>.mjs</code> files as ESM.</li>
    </ul>
  </td>
</tr>
<tr>
  <td valign="top"><code>"await":false</code></td>
  <td>
    <p>A boolean for <a href="https://github.com/tc39/proposal-top-level-await">top-level <code>await</code></a> in modules without ESM exports. <em>(Node 10+)</em></p>
  </td>
</tr>
<tr>
  <td valign="top"><code>"force":false</code></td>
  <td>
    <p>A boolean to apply these options to all module loads.</p>
  </td>
</tr>
<tr>
  <td valign="top"><code>"wasm":false</code></td>
  <td>
    <p>A boolean for <a href="https://nodejs.org/api/globals.html#globals_webassembly">WebAssembly</a> module support. <em>(Node 8+)</em></p>
  </td>
</tr>
<tr>
  <td colspan="2"><code>}</code></td>
</tr>
</table>

DevOpts
---

<table>
<tr>
  <td colspan="2"><code>{</code></td>
</tr>
<tr>
  <td valign="top"><code>"cache":true</code></td>
  <td>
    <p>A boolean for toggling cache creation or a cache directory path.</p>
  </td>
</tr>
<tr>
  <td valign="top"><code>"sourceMap":false</code></td>
  <td>
    <p>A boolean for including inline source maps.</p>
  </td>
</tr>
<tr>
  <td colspan="2"><code>}</code></td>
</tr>
</table>

Tips
---

### Bundling

* For bundlers like [`browserify`](http://browserify.org/)+[`esmify`](https://github.com/mattdesl/esmify),
  [`parcel-bundler`](https://parceljs.org/), and [`webpack`](https://webpack.js.org/)
  add a `"module"` field to `package.json` pointing to the main ES module.
  ```json
  "main": "index.js",
  "module": "main.js"
  ```

  :bulb: This is automagically done with `npm init esm` or `yarn create esm`.

### Extensions

* Enable `esm-sync` for [`wallaby.js`](https://wallabyjs.com/) following their
  [integration example](https://wallabyjs.com/docs/integration/node.html#es-modules).

### Loading

* Load `esm-sync` before loaders/monitors like
  [`@babel/register`](https://babeljs.io/docs/en/next/babel-register.html),
  [`newrelic`](https://github.com/newrelic/node-newrelic),
  [`sqreen`](https://docs.sqreen.io/sqreen-for-nodejs/getting-started-2/), and
  [`ts-node`](https://github.com/TypeStrong/ts-node#programmatic).

* Load `esm-sync` for [`jasmine`](https://jasmine.github.io/) using the
  [`"helpers"`](https://jasmine.github.io/setup/nodejs.html#configuration)
  field in `jasmine.json`:
  ```json
  "helpers": [
    "node_modules/esm-sync"
  ]
  ```

* Load `esm-sync` with “node-args" options of:<br>
  - [`pm2`](https://pm2.io/doc/en/runtime/reference/pm2-cli/#pm2-flags): `--node-args="-r esm-sync"`

* Load `esm-sync` with “require” options of
  [`ava`](https://github.com/avajs/ava/blob/master/docs/recipes/es-modules.md),
  [`mocha`](https://mochajs.org/#-require-module-r-module),
  [`nodemon`](https://nodemon.io/),
  [`nyc`](https://github.com/istanbuljs/nyc#require-additional-modules),
  [`qunit`](https://github.com/qunitjs/qunit/releases/tag/2.6.0),
  [`tape`](https://github.com/substack/tape#preloading-modules), and
  [`webpack`](https://webpack.js.org/api/cli/#config-options).

  :bulb: Builtin `require` cannot sideload `.mjs` files. However, `.js` files
  can be sideloaded or `.mjs` files may be loaded with dynamic `import`.
