From df50192c4d46c1bab37ff5ad89b3b1c17d43fe09 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Tue, 23 Jul 2024 16:26:42 +0200 Subject: [PATCH] docs: add guidelines for ESM support --- docs/pages/_meta.json | 1 + docs/pages/build.md | 8 +++---- docs/pages/esm.md | 55 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 docs/pages/esm.md diff --git a/docs/pages/_meta.json b/docs/pages/_meta.json index e02ff2334..875497284 100644 --- a/docs/pages/_meta.json +++ b/docs/pages/_meta.json @@ -2,5 +2,6 @@ "index": "Introduction", "create": "Scaffold a library", "build": "Build a library", + "esm": "ESM support", "faq": "FAQ" } diff --git a/docs/pages/build.md b/docs/pages/build.md index d0e488710..6338e192f 100644 --- a/docs/pages/build.md +++ b/docs/pages/build.md @@ -42,8 +42,8 @@ yarn add --dev react-native-builder-bob "source": "src", "output": "lib", "targets": [ - ["commonjs", { "esm" : true }], - ["module", { "esm" : true }], + ["commonjs", { "esm": true }], + ["module", { "esm": true }], "typescript", ] } @@ -166,9 +166,7 @@ In addition, the following options are supported: 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. -This mainly adds file extensions to the imports and exports. Note that file extensions are not added when importing a file that may have platform-specific extensions (e.g. `.android.ts`) to avoid breaking tools. - -If you use TypeScript, also make sure to set `"moduleResolution": "Bundler"` in your `tsconfig.json` file. +See the [ESM support](./esm.md) guide for more details. ##### `configFile` diff --git a/docs/pages/esm.md b/docs/pages/esm.md new file mode 100644 index 000000000..b0d81687a --- /dev/null +++ b/docs/pages/esm.md @@ -0,0 +1,55 @@ +# ESM support + +Libraries created with [`create-react-native-library`](./create.md) are pre-configured to work with ESM (ECMAScript Modules) out of the box. + +You can verify whether ESM support is enabled by checking the configuration for [`react-native-builder-bob`](./build.md) in the `package.json` file of the library: + +```json +"react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + ["commonjs", { "esm" : true }], + ["module", { "esm" : true }], + "typescript", + ] +} +``` + +The `"esm": true` option enables ESM-compatible output. Here's what it does: + +- It adds the `.js` extension to the import statements in the generated files. +- It creates a `package.json` file in the output directory with the content: `{ "type": "module" }` + +In addition, it's necessary to specify `"moduleResolution": "Bundler"` in your `tsconfig.json` file: + +```json +{ + "compilerOptions": { + "moduleResolution": "Bundler" + } +} +``` + +This means that you don't need to specify the file extension in the import statements. They'll be automatically added when possible during the build process. + +## Guidelines + +There are still a few things to keep in mind if you want your library to be ESM-compatible: + +- 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 the library uses platform-specific extensions (e.g., `.ios.js` or `.android.js`), the ESM output will not be compatible with Node.js. 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. Bundlers such as Webpack (with [`resolve.fullySpecified: false`](https://webpack.js.org/configuration/module/#resolvefullyspecified)) or Metro can handle this. It's still possible to `require` the CommonJS build directly in Node.js. +- 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. +- 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. +- If you specify a `react-native` condition in `exports`, make sure that it comes before `import` or `require`. The conditions should be ordered from the most specific to the least specific: + + ```json + "exports": { + ".": { + "types": "./lib/typescript/src/index.d.ts", + "react-native": "./lib/modules/index.native.js", + "import": "./lib/modules/index.js", + "require": "./lib/commonjs/index.js" + } + } + ```