Skip to content

Commit 4c57d60

Browse files
committed
docs: add an alternative approach for ESM
1 parent e863215 commit 4c57d60

File tree

2 files changed

+71
-4
lines changed

2 files changed

+71
-4
lines changed

docs/pages/build.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,9 @@ Example:
271271
["typescript", { "project": "tsconfig.build.json" }]
272272
```
273273

274-
The output file should be referenced in the `types` field or `exports['.'].types` field of `package.json`.
274+
The output file should be referenced in the `exports['.'].types` field of `package.json`.
275+
276+
If you need to support legacy setups that use `moduleResolution: node10` or `moduleResolution: node`, you can also add a `types` field to the `package.json` file that points to the output file.
275277

276278
#### `codegen`
277279

docs/pages/esm.md

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@ The `./package.json` field is used to point to the library's `package.json` file
6060

6161
> 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.
6262
63-
## Dual package setup
63+
## Legacy environments
6464

65-
The previously mentioned setup only works with tools that support ES modules. If you want to support tools that don't support ESM and use the CommonJS module system, you can set up a dual package setup.
65+
The previously mentioned setup only works with tools that support ES modules. If you want to support tools that don't support ESM and use the CommonJS module system, there are a few approaches you can take.
66+
67+
### Dual package setup
6668

6769
A dual package setup means that you have 2 builds of your library: one for ESM and one for CommonJS. The ESM build is used by tools that support ES modules, while the CommonJS build is used by tools that don't support ES modules.
6870

@@ -161,7 +163,70 @@ Putting it all together, the fields in your `package.json` file should look like
161163
}
162164
```
163165

164-
> **Important:** With this approach, the ESM and CommonJS versions of the package are treated as separate modules by Node.js as they are different files, leading to [potential issues](https://nodejs.org/docs/latest-v19.x/api/packages.html#dual-package-hazard) if the package is both imported and required in the same runtime environment. If the package relies on any state that can cause issues if 2 separate instances are loaded, it's necessary to isolate the state into a separate CommonJS module that can be shared between the ESM and CommonJS builds.
166+
#### Dual package hazard
167+
168+
With this approach, the ESM and CommonJS versions of the package are treated as separate modules by Node.js as they are different files. On Node.js, `import` will load the ESM package and `require` will load the CommonJS package, leading to [potential issues](https://nodejs.org/docs/latest-v19.x/api/packages.html#dual-package-hazard) if the package is both imported and required in the same runtime environment.
169+
170+
If the library relies on any state that can cause issues if 2 separate instances are loaded (e.g. global state, 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.
171+
172+
### CommonJS-first setup
173+
174+
The simplest way to avoid dual package hazard is to use a CommonJS-first setup.
175+
176+
With this approach, Node.js will always use the CommonJS build - so two versions of the same package won't be loaded, while the ESM build can optionally be used by some tools such as bundlers (e.g. [Webpack](https://webpack.js.org/) and [Rollup](https://rollupjs.org/)) that use the ESM build for tree-shaking).
177+
178+
To configure a CommonJS-first setup, you can follow these steps:
179+
180+
1. Add the `commonjs` target to the `react-native-builder-bob` field in your `package.json` or `bob.config.js`:
181+
182+
```diff
183+
"react-native-builder-bob": {
184+
"source": "src",
185+
"output": "lib",
186+
"targets": [
187+
["module", { "esm": true }],
188+
+ "commonjs",
189+
"typescript",
190+
]
191+
}
192+
```
193+
194+
Optionally, remove the `module` target if you don't need the ESM build at all.
195+
196+
2. Change the `main` and `default` fields to point the CommonJS build instead:
197+
198+
```diff
199+
- "main": "./lib/module/index.js",
200+
+ "main": "./lib/commonjs/index.js",
201+
"exports": {
202+
".": {
203+
"types": "./lib/typescript/src/index.d.ts",
204+
- "default": "./lib/module/index.js"
205+
+ "default": "./lib/commonjs/index.js"
206+
},
207+
"./package.json": "./package.json"
208+
},
209+
```
210+
211+
Here, tools that support the `exports` field will use the CommonJS build when importing or requiring the library, while tools that don't support the `exports` field will fall back to the `main` field.
212+
213+
Optionally, you can add a `module` field under `exports` to point to the ESM build for bundlers:
214+
215+
```diff
216+
"main": "./lib/commonjs/index.js",
217+
"exports": {
218+
".": {
219+
"types": "./lib/typescript/src/index.d.ts",
220+
+ "module": "./lib/module/index.js"
221+
"default": "./lib/commonjs/index.js"
222+
},
223+
"./package.json": "./package.json"
224+
},
225+
```
226+
227+
Note that [Metro](https://metrobundler.dev) doesn't support the `module` field. So it will always use the CommonJS build.
228+
229+
Alternatively, you can drop the `exports` field altogether and use the `main` field to point to the CommonJS build and `module` field to point to the ESM build. However, you will lose the benefits of the `exports` field, such as restricted access to the package's internals and [conditional exports](https://nodejs.org/api/packages.html#conditional-exports).
165230

166231
## Guidelines
167232

0 commit comments

Comments
 (0)