Skip to content

Commit cffe7dc

Browse files
committed
docs: add info about import.meta and various conditions to ESM docs
1 parent 97c3cc7 commit cffe7dc

File tree

1 file changed

+90
-2
lines changed

1 file changed

+90
-2
lines changed

docs/pages/esm.md

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ Using the `exports` field has a few benefits, such as:
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).
6666
- 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.).
6767

68+
### A note on `import.meta`
69+
70+
The [`import.meta`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta) object is available in ESM. As per the spec, different tools may add different properties to it.
71+
72+
For example, Node.js adds [`import.meta.resolve`](https://nodejs.org/api/esm.html#importmetaresolvespecifier) and more, Webpack adds [`import.meta.webpackHot`](https://webpack.js.org/api/module-variables/#importmetawebpackhot), [`import.meta.webpackContext`](https://webpack.js.org/api/module-variables/#importmetawebpackcontext) and more, Vite adds [`import.meta.env`](https://vite.dev/guide/env-and-mode) and more, etc. Most tools support the `import.meta.url` property, which is a URL string representing the module's location.
73+
74+
Additionally, the `import.meta` syntax is currently not supported in [Metro](https://metrobundler.dev/) (React Native) and will result in a syntax error.
75+
76+
So be careful when using properties from `import.meta`, as relying on properties only available in specific tools may lock your library into supporting only those specific tools. Also, since this is an ESM-only feature, you should avoid using it if you compile your library to CommonJS as well.
77+
6878
## Dual package setup
6979

7080
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 configure a dual package setup.
@@ -173,6 +183,63 @@ With this approach, the ESM and CommonJS versions of the package are treated as
173183

174184
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.
175185

186+
### Alternative approach
187+
188+
An alternative approach to classic dual package setup is to use tool specific conditions instead of specifying both `import` and `require`. This way, each tool can load the appropriate build without resulting in a dual package hazard.
189+
190+
For example, here is a setup that uses ESM for Webpack, Vite, Rollup, Metro (React Native) and Node.js, and CommonJS for the rest:
191+
192+
```json
193+
{
194+
"main": "./lib/commonjs/index.js",
195+
"module": "./lib/module/index.js",
196+
"types": "./lib/typescript/commonjs/src/index.d.ts",
197+
"exports": {
198+
".": {
199+
"react-native": {
200+
"types": "./lib/typescript/module/src/index.d.ts",
201+
"default": "./lib/module/index.native.js"
202+
},
203+
"module": {
204+
"types": "./lib/typescript/module/src/index.d.ts",
205+
"default": "./lib/module/index.js"
206+
},
207+
"node": {
208+
"types": "./lib/typescript/module/src/index.d.ts",
209+
"default": "./lib/module/index.js"
210+
},
211+
"default": {
212+
"types": "./lib/typescript/commonjs/src/index.d.ts",
213+
"default": "./lib/commonjs/index.js"
214+
}
215+
},
216+
"./package.json": "./package.json"
217+
}
218+
}
219+
```
220+
221+
Here, we specify 4 conditions:
222+
223+
- `react-native`: Used when the library is imported in a React Native environment with Metro.
224+
- `module`: Used when the library is imported in a bundler such as Webpack, Vite or Rollup.
225+
- `node`: Used when the library is imported in Node.js.
226+
- `default`: Fallback used when the library is imported in an environment that doesn't support the other conditions.
227+
228+
One thing to note is that TypeScript may need to be configured to resolve to the appropriate condition. It's pre-configured for React Native apps, but in other scenarios, it maybe necessary to specify `customConditions` in the `tsconfig.json` file:
229+
230+
```json
231+
{
232+
"compilerOptions": {
233+
"moduleResolution": "bundler",
234+
"customConditions": ["module"]
235+
}
236+
}
237+
```
238+
239+
This is just an example to illustrate the idea. In practice, you may want to specify appropriate conditions for your library based on the tools you want to support.
240+
241+
You can find a list of conditions supported in various tools in the [Node.js documentation](https://nodejs.org/docs/latest/api/packages.html#community-conditions-definitions) and the [Webpack documentation](https://webpack.js.org/guides/package-exports/#conditions).
242+
176243
## Compatibility
177244

178245
[Node.js](https://nodejs.org) v12 and higher natively support ESM and the `exports` field. However, in a CommonJS environment, an ESM library can be loaded synchronously only in recent Node.js versions. The following Node.js versions support synchronous `require()` for ESM libraries without any flags or warnings:
@@ -183,7 +250,7 @@ If the library relies on any state that can cause issues if 2 separate instances
183250

184251
Older versions can still load your library asynchronously using `import()` in CommonJS environments.
185252

186-
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).
253+
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 [Node.js documentation](https://nodejs.org/docs/latest/api/packages.html#community-conditions-definitions) and the [Webpack documentation](https://webpack.js.org/guides/package-exports/#conditions).
187254

188255
[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.
189256

@@ -252,7 +319,7 @@ There are still a few things to keep in mind if you want your library to be ESM-
252319
".": {
253320
"import": {
254321
"types": "./lib/typescript/module/src/index.d.ts",
255-
"react-native": "./lib/modules/index.native.js",
322+
"react-native": "./lib/module/index.native.js",
256323
"default": "./lib/module/index.js"
257324
},
258325
"require": {
@@ -265,6 +332,27 @@ There are still a few things to keep in mind if you want your library to be ESM-
265332
}
266333
```
267334

335+
Or as a separate condition:
336+
337+
```json
338+
"exports": {
339+
".": {
340+
"react-native": {
341+
"types": "./lib/typescript/module/src/index.native.d.ts",
342+
"default": "./lib/module/index.native.js"
343+
},
344+
"import": {
345+
"types": "./lib/typescript/module/src/index.d.ts",
346+
"default": "./lib/module/index.js"
347+
},
348+
"require": {
349+
"types": "./lib/typescript/commonjs/src/index.d.ts",
350+
"default": "./lib/commonjs/index.js"
351+
}
352+
},
353+
}
354+
```
355+
268356
## References
269357

270358
- [Node.js documentation on ESM](https://nodejs.org/docs/latest/api/esm.html)

0 commit comments

Comments
 (0)