Skip to content

Commit 0b45375

Browse files
committed
docs: add compatibility list to ESM docs
1 parent 1093e42 commit 0b45375

File tree

2 files changed

+46
-14
lines changed

2 files changed

+46
-14
lines changed

Diff for: docs/pages/build.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ In addition, the following options are supported:
176176

177177
##### `esm`
178178

179-
Setting this option to `true` will output ES modules compatible code for Node.js 12+, modern browsers and other tools that support `package.json`'s `exports` field.
179+
Setting this option to `true` will output ES modules compatible code for Node.js 12+, modern browsers and tools that support `package.json`'s `exports` field.
180180

181181
See the [ESM support](./esm.md) guide for more details.
182182

@@ -241,7 +241,9 @@ Example:
241241

242242
Enable compiling source files with Babel and use CommonJS module system. This is essentially the same as the `module` target and accepts the same options, but transforms the `import`/`export` statements in your code to `require`/`module.exports`.
243243

244-
This is useful for supporting usage of this module with `require` in Node versions older than 20 (it can still be used with `import` for Node.js 12+ if `module` target with `esm` is enabled), and some tools such as [Jest](https://jestjs.io). The output file should be referenced in the `main` field. If you have a [dual package setup](esm.md#dual-package-setup) with both ESM and CommonJS builds, it needs to be specified in `exports['.'].require` field of `package.json`.
244+
This is useful for supporting tools that don't support ES modules yet, see [the Compatibility section in our ESM guide](./esm.md#compatibility) for more details.
245+
246+
The output file should be referenced in the `main` field. If you have a [dual package setup](esm.md#dual-package-setup) with both ESM and CommonJS builds, it needs to be specified in `exports['.'].require` field of `package.json`.
245247

246248
Example:
247249

Diff for: docs/pages/esm.md

+42-12
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,7 @@ The `./package.json` field is used to point to the library's `package.json` file
6363
Using the `exports` field has a few benefits, such as:
6464

6565
- It [restricts access to the library's internals](https://nodejs.org/api/packages.html#main-entry-point-export) by default. You can explicitly specify which files are accessible with [subpath exports](https://nodejs.org/api/packages.html#subpath-exports).
66-
- It allows you to specify different entry points for different environments with [conditional exports](https://nodejs.org/api/packages.html#conditional-exports) (e.g. `node`, `browser`, `react-native`, `production`, `development` etc.). More examples can be found in the [Webpack documentation](https://webpack.js.org/guides/package-exports/#conditions).
67-
68-
> Note: Metro enables support for `package.json` exports by default from version [0.82.0](https://github.com/facebook/metro/releases/tag/v0.82.0). In previous versions, experimental support can be enabled by setting the `unstable_enablePackageExports` option to `true` in the [Metro configuration](https://metrobundler.dev/docs/configuration/). If this is not enabled, Metro will use the entrypoint specified in the `main` field. Features such as [subpath exports](https://nodejs.org/api/packages.html#subpath-exports) and [conditional exports](https://nodejs.org/api/packages.html#conditional-exports) will not work when `exports` supported is not enabled.
66+
- It allows you to specify different entry points for different environments with [conditional exports](https://nodejs.org/api/packages.html#conditional-exports) (e.g. `node`, `browser`, `module`, `react-native`, `production`, `development` etc.).
6967

7068
## Dual package setup
7169

@@ -174,31 +172,63 @@ With this approach, the ESM and CommonJS versions of the package are treated as
174172

175173
If the library relies on any state that can cause issues if 2 separate instances are loaded (e.g. global state, constructors, react context etc.), it's necessary to isolate the state into a separate CommonJS module that can be shared between the ESM and CommonJS builds.
176174

175+
## Compatibility
176+
177+
[Node.js](https://nodejs.org) v12 and higher natively support ESM and the `exports` field. However, an ESM library can synchronously loaded in a CommonJS environment in only in recent Node.js versions. The following Node.js versions support synchronous `require()` for ESM libraries without any flags or warnings:
178+
179+
- v20.19.0 and higher (LTS)
180+
- v22.12.0 and higher (LTS)
181+
- v23.4.0 and higher
182+
183+
Older versions can still load your library asynchronously using `import()`.
184+
185+
Most modern tools such as [Webpack](https://webpack.js.org), [Rollup](https://rollupjs.org), [Vite](https://vitejs.dev) etc. also support ESM and the `exports` field. See the supported conditions in the [Webpack documentation](https://webpack.js.org/guides/package-exports/#conditions).
186+
187+
[Metro](https://metrobundler.dev) enables support for `package.json` exports by default from version [0.82.0](https://github.com/facebook/metro/releases/tag/v0.82.0). In previous versions, experimental support can be enabled by setting the [`unstable_enablePackageExports` option to `true`](https://metrobundler.dev/docs/package-exports/) in the Metro configuration. If this is not enabled, Metro will use the entrypoint specified in the `main` field. Features such as [subpath exports](https://nodejs.org/api/packages.html#subpath-exports) and [conditional exports](https://nodejs.org/api/packages.html#conditional-exports) will not work when `exports` supported is not enabled.
188+
189+
[Jest](https://jestjs.io) supports the `exports` field, but doesn't support ESM natively. Experimental support is [available under a flag](https://jestjs.io/docs/ecmascript-modules), but requires changes to how tests are written. It can still load ESM libraries using a transform such as [`babel-jest`](https://github.com/jestjs/jest/tree/main/packages/babel-jest).
190+
177191
## Guidelines
178192

179193
There are still a few things to keep in mind if you want your library to be ESM-compatible:
180194

181-
- Avoid using default exports in your library. Named exports are recommended. Default exports produce a CommonJS module with a `default` property, which will work differently than the ESM build and can cause issues.
195+
- Avoid using default exports in your library. Named exports are recommended. Default exports produce a CommonJS module with a `default` property, which will work differently than the ESM build and can cause issues if you have a dual package setup. Needing to use `.default` in CommonJS environment may also be confusing for users.
182196
- If the library uses platform-specific extensions (e.g., `.ios.js` or `.android.js`), the ESM output will not be compatible with Node.js, i.e. it's not possible to use the library in Node.js with `import` syntax. It's necessary to omit file extensions from the imports to make platform-specific extensions work, however, Node.js requires file extensions to be present.
183197

184-
Bundlers such as Metro can handle this without additional configuration. Other bundlers may need to be configured to make extensionless imports to work, (e.g. it's necessary to specify [`resolve.fullySpecified: false`](https://webpack.js.org/configuration/module/#resolvefullyspecified) for Webpack).
198+
While Bob automatically adds file extensions to the import statements during the build process if `esm` is set to `true`, it will skip the imports that reference files with platform-specific extensions to avoid breaking the resolution.
199+
200+
Bundlers such as Metro can handle imports without file extensions for ESM without additional configuration. Other bundlers may need to be configured to make extensionless imports to work, (e.g. it's necessary to specify [`resolve.fullySpecified: false`](https://webpack.js.org/configuration/module/#resolvefullyspecified) for Webpack).
185201

186202
It's still possible to use the library in Node.js using the CommonJS build with `require`:
187203

188204
```js
189205
const { foo } = require('my-library');
190206
```
191207

192-
Alternatively, if you want to be able to use the library in Node.js with `import` syntax, you can use `require` to import code with platform-specific extensions in your library:
208+
Alternatively, if you want to be able to use the library in Node.js with `import` syntax, there are a few options:
193209

194-
```js
195-
// will import `foo.native.js`, `foo.ios.js`, `foo.js` etc.
196-
const { foo } = require('./foo');
197-
```
210+
- Use `Platform.select` instead of platform-specific extensions:
211+
212+
```js
213+
import { Platform } from 'react-native';
214+
215+
const foo = Platform.select({
216+
android: require('./fooAndroid.js'),
217+
ios: require('./fooIOS.js'),
218+
default: require('./fooFallback.js'),
219+
});
220+
```
221+
222+
- Use `require` to import code with platform-specific extensions in your library:
223+
224+
```js
225+
// will import `foo.native.js`, `foo.ios.js`, `foo.js` etc.
226+
const { foo } = require('./foo');
227+
```
198228

199-
Make sure to have a file without any platform-specific extensions that will be loaded by Node.js.
229+
Make sure to have a file without any platform-specific extensions that will be loaded by Node.js.
200230

201-
Also note that if your module (e.g. `foo.js` in this case) contains ESM syntax, it will only work on Node.js 20 or newer.
231+
Also note that if your module (e.g. `foo.js` in this case) contains ESM syntax, it will only work on a recent Node.js version. See [Compatibility](#compatibility) section for more information.
202232

203233
- Avoid using `.cjs`, `.mjs`, `.cts` or `.mts` extensions. Metro always requires file extensions in import statements when using `.cjs` or `.mjs` which breaks platform-specific extension resolution.
204234
- Avoid using `"moduleResolution": "node16"` or `"moduleResolution": "nodenext"` in your `tsconfig.json` file. They require file extensions in import statements which breaks platform-specific extension resolution.

0 commit comments

Comments
 (0)