Skip to content

Commit 3352d8f

Browse files
committed
refactor: drop commonjs build from default setup
1 parent 17e4ba8 commit 3352d8f

File tree

5 files changed

+127
-87
lines changed

5 files changed

+127
-87
lines changed

docs/pages/build.md

Lines changed: 21 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,9 @@ To configure your project manually, follow these steps:
4545
"source": "src",
4646
"output": "lib",
4747
"targets": [
48-
["commonjs", { "esm": true }],
4948
["module", { "esm": true }],
5049
"typescript",
51-
"codegen",
50+
"codegen"
5251
]
5352
}
5453
```
@@ -78,19 +77,11 @@ To configure your project manually, follow these steps:
7877

7978
```json
8079
"source": "./src/index.tsx",
81-
"main": "./lib/commonjs/index.js",
82-
"module": "./lib/module/index.js",
83-
"types": "./lib/typescript/commonjs/src/index.d.ts",
80+
"main": "./lib/module/index.js",
8481
"exports": {
8582
".": {
86-
"import": {
87-
"types": "./lib/typescript/module/src/index.d.ts",
88-
"default": "./lib/module/index.js"
89-
},
90-
"require": {
91-
"types": "./lib/typescript/commonjs/src/index.d.ts",
92-
"default": "./lib/commonjs/index.js"
93-
}
83+
"types": "./lib/typescript/src/index.d.ts",
84+
"default": "./lib/module/index.js"
9485
}
9586
},
9687
"files": [
@@ -103,14 +94,15 @@ To configure your project manually, follow these steps:
10394

10495
- `source`: The path to the source code. It is used by `react-native-builder-bob` to detect the correct output files and provide better error messages.
10596
- `main`: The entry point for the CommonJS build. This is used by Node - such as tests, SSR etc.
106-
- `module`: The entry point for the ES module build. This is used by bundlers such as webpack.
107-
- `types`: The entry point for the TypeScript definitions. This is used by TypeScript to typecheck the code using your library.
10897
- `files`: The files to include in the package when publishing with `npm`.
109-
- `exports`: The entry points for tools that support the `exports` field in `package.json` - such as Node.js 12+ & modern browsers. See [the ESM support guide](./esm.md) for more details.
98+
- `exports`: The entry points for tools that support the `exports` field in `package.json` - such as Node.js 12+ & modern browsers:
11099

111-
Make sure to change specify correct files according to the targets you have enabled.
100+
- `exports['.'].types`: The entry point for the TypeScript definitions.
101+
- `exports['.'].default`: The entry point for the ES module build. This is used by modern tools
102+
103+
See [the ESM support guide](./esm.md) for more details.
112104

113-
> If you're building TypeScript definition files, also make sure that the `types` field points to a correct path. Depending on the project configuration, the path can be different for you than the example snippet (e.g. `lib/typescript/index.d.ts` if you have only the `src` directory and `rootDir` is not set).
105+
Make sure to change specify correct files according to the targets you have enabled.
114106

115107
5. Add the output directory to `.gitignore` and `.eslintignore`
116108

@@ -173,13 +165,15 @@ Example:
173165

174166
Various targets to build for. The available targets are:
175167

176-
#### `commonjs`
168+
#### `module`
169+
170+
Enable compiling source files with Babel and use ES module system (`import`/`export`).
177171

178-
Enable compiling source files with Babel and use CommonJS module system.
172+
This is useful for modern bundlers that understand ES modules. Bundlers such as [webpack](https://webpack.js.org) can also tree-shake code using ES modules.
179173

180-
This is useful for running the code in Node (SSR, tests etc.). The output file should be referenced in the `main` field and `exports['.'].require` (when `esm: true`) field of `package.json`.
174+
The output file should be referenced in the `module` field and `exports['.'].import` (when `esm: true`) field of `package.json`.
181175

182-
By default, the code is compiled to support the last 2 versions of modern browsers. It also strips TypeScript and Flow annotations as well as compiles JSX. You can customize the environments to compile for by using a [browserslist config](https://github.com/browserslist/browserslist#config-file).
176+
By default, the code is compiled to support the last 2 versions of modern browsers. It also strips TypeScript and Flow annotations as well as compiles JSX code. You can customize the environments to compile for by using a [browserslist config](https://github.com/browserslist/browserslist#config-file).
183177

184178
In addition, the following options are supported:
185179

@@ -243,19 +237,19 @@ Sourcemaps are generated by default alongside the compiled files. You can disabl
243237
Example:
244238

245239
```json
246-
["commonjs", { "esm": true, "copyFlow": true }]
240+
["commonjs", { "esm": true }]
247241
```
248242

249-
#### `module`
243+
#### `commonjs`
250244

251-
Enable compiling source files with Babel and use ES module system. This is essentially the same as the `commonjs` target and accepts the same options, but leaves the `import`/`export` statements in your code.
245+
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`.
252246

253-
This is useful for bundlers that understand ES modules and can tree-shake. The output file should be referenced in the `module` field and `exports['.'].import` (when `esm: true`) field of `package.json`.
247+
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). The output file should be referenced in the `main` field. If you need to use a [dual module setup](esm.md#dual-module-setup), it needs to be specified in `exports['.'].require` field of `package.json`.
254248

255249
Example:
256250

257251
```json
258-
["module", { "esm": true, "sourceMaps": false }]
252+
["commonjs", { "sourceMaps": false, "copyFlow": true }]
259253
```
260254

261255
#### `typescript`
@@ -282,12 +276,6 @@ Example:
282276

283277
The output file should be referenced in the `types` field or `exports['.'].types` field of `package.json`.
284278

285-
##### `esm`
286-
287-
Setting this option to `true` will output 2 sets of type definitions: one for the CommonJS build and one for the ES module build.
288-
289-
See the [ESM support](./esm.md) guide for more details.
290-
291279
#### `codegen`
292280

293281
Enable generating the [React Native Codegen](https://reactnative.dev/docs/the-new-architecture/what-is-codegen) scaffold code, which is used with the New React Native Architecture.

docs/pages/esm.md

Lines changed: 99 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ You can verify whether ESM support is enabled by checking the configuration for
99
"source": "src",
1010
"output": "lib",
1111
"targets": [
12-
["commonjs", { "esm": true }],
1312
["module", { "esm": true }],
1413
"typescript"
1514
]
1615
}
1716
```
1817

19-
The `"esm": true` option enables ESM-compatible output by adding the `.js` extension to the import statements in the generated files. For TypeScript, it also generates 2 sets of type definitions: one for the CommonJS build and one for the ES module build.
18+
The `"esm": true` option enables ESM-compatible output by adding the `.js` extension to the import statements in the generated files. This is necessary if you want to be able to import the library on Node.js or in a bundler that supports ESM, with some caveats. See the [Guidelines](#guidelines) section for more information.
19+
20+
For TypeScript, it also generates 2 sets of type definitions if the [`commonjs`](build.md#commonjs) target is also enabled: one for the CommonJS build and one for the ES module build.
2021

2122
It's recommended to specify `"moduleResolution": "bundler"` and `"resolvePackageJsonImports": false` in your `tsconfig.json` file to match [Metro's behavior](https://reactnative.dev/blog/2023/06/21/package-exports-support#enabling-package-exports-beta):
2223

@@ -34,35 +35,89 @@ Specifying `"moduleResolution": "bundler"` means that you don't need to use file
3435
To make use of the output files, ensure that your `package.json` file contains the following fields:
3536

3637
```json
37-
"main": "./lib/commonjs/index.js",
38-
"module": "./lib/module/index.js",
39-
"types": "./lib/typescript/commonjs/src/index.d.ts",
38+
"main": "./lib/module/index.js",
4039
"exports": {
4140
".": {
42-
"import": {
43-
"types": "./lib/typescript/module/src/index.d.ts",
44-
"default": "./lib/module/index.js"
45-
},
46-
"require": {
47-
"types": "./lib/typescript/commonjs/src/index.d.ts",
48-
"default": "./lib/commonjs/index.js"
49-
}
41+
"types": "./lib/typescript/src/index.d.ts",
42+
"default": "./lib/module/index.js"
5043
}
5144
},
5245
```
5346

54-
The `main`, `module` and `types` fields are for legacy setups that don't support the `exports` field. See the [Manual configuration](build.md#manual-configuration) guide for more information about those fields.
47+
The `main` field is for tools that don't support the `exports` field (e.g. [Metro](https://metrobundler.dev/)). The `exports` field is used by modern tools and bundlers to determine the correct entry point. Here, we specify 2 conditions:
5548

56-
The `exports` field is used by modern tools and bundlers to determine the correct entry point. Here, we specify 2 conditions:
49+
- `types`: Used for the TypeScript definitions.
50+
- `default`: Used for the actual JS code when the library is imported or required.
5751

58-
- `import`: Used when the library is imported with an `import` statement or a dynamic `import()`. It should point to the ESM build.
59-
- `require`: Used when the library is required with a `require` call. It should point to the CommonJS build.
52+
See the [Manual configuration](build.md#manual-configuration) guide for more information about those fields.
6053

61-
Each condition has a `types` field - necessary for TypeScript to provide the appropriate definitions for the module system. The type definitions have slightly different semantics for CommonJS and ESM, so it's important to specify them separately.
54+
You can also specify additional conditions for different scenarios, such as `react-native`, `browser`, `production`, `development` etc. Note that support for these conditions depends on the tooling you're using.
6255

63-
The `default` field is the fallback entry point for both conditions. It's used for the actual JS code when the library is imported or required.
56+
## Dual module setup
6457

65-
You can also specify additional conditions for different scenarios, such as `react-native`, `browser`, `production`, `development` etc. Note that support for these conditions depends on the tooling you're using.
58+
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 module setup.
59+
60+
A dual module 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.
61+
62+
To set up a dual module setup, you can follow these steps:
63+
64+
1. Add the `commonjs` target to the `react-native-builder-bob` field in your `package.json` or `bob.config.js`:
65+
66+
```diff
67+
"react-native-builder-bob": {
68+
"source": "src",
69+
"output": "lib",
70+
"targets": [
71+
["module", { "esm": true }],
72+
+ ["commonjs", { "esm": true }]
73+
"typescript",
74+
]
75+
}
76+
```
77+
78+
2. Change the `main` field in your `package.json` to point to the CommonJS build:
79+
80+
```diff
81+
- "main": "./lib/module/index.js",
82+
+ "main": "./lib/commonjs/index.js",
83+
```
84+
85+
3. Optionally add a `module` field in your `package.json` to point to the ESM build:
86+
87+
```diff
88+
"main": "./lib/commonjs/index.js",
89+
+ "module": "./lib/module/index.js",
90+
```
91+
92+
The `module` field is a non-standard field that some tools use to determine the ESM entry point.
93+
94+
4. Change the `exports` field in your `package.json` to include 2 conditions:
95+
96+
```diff
97+
"exports": {
98+
".": {
99+
- "types": "./lib/typescript/src/index.d.ts",
100+
- "default": "./lib/module/index.js"
101+
+ "import": {
102+
+ "types": "./lib/typescript/module/src/index.d.ts",
103+
+ "default": "./lib/module/index.js"
104+
+ },
105+
+ "require": {
106+
+ "types": "./lib/typescript/commonjs/src/index.d.ts",
107+
+ "default": "./lib/commonjs/index.js"
108+
+ }
109+
}
110+
},
111+
```
112+
113+
Here, we specify 2 conditions:
114+
115+
- `import`: Used when the library is imported with an `import` statement or a dynamic `import()`. It will point to the ESM build.
116+
- `require`: Used when the library is required with a `require` call. It will point to the CommonJS build.
117+
118+
Each condition has a `types` field - necessary for TypeScript to provide the appropriate definitions for the module system. The type definitions have slightly different semantics for CommonJS and ESM, so it's important to specify them separately.
119+
120+
The `default` field is the fallback entry point for both conditions. It's used for the actual JS code when the library is imported or required.
66121

67122
## Guidelines
68123

@@ -79,9 +134,32 @@ There are still a few things to keep in mind if you want your library to be ESM-
79134
const { foo } = require('my-library');
80135
```
81136

137+
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:
138+
139+
```js
140+
// will import `foo.native.js`, `foo.ios.js`, `foo.js` etc.
141+
const { foo } = require('./foo');
142+
```
143+
144+
Make sure to have a file without any platform-specific extensions that will be loaded by Node.js.
145+
146+
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.
147+
82148
- 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.
83149
- 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.
84-
- 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:
150+
- If you specify a `react-native` condition in `exports`, make sure that it comes before other conditions. The conditions should be ordered from the most specific to the least specific:
151+
152+
```json
153+
"exports": {
154+
".": {
155+
"types": "./lib/typescript/src/index.d.ts",
156+
"react-native": "./lib/modules/index.native.js",
157+
"default": "./lib/module/index.js"
158+
}
159+
}
160+
```
161+
162+
Or for a dual module setup:
85163

86164
```json
87165
"exports": {

packages/create-react-native-library/templates/common/$package.json

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,11 @@
33
"version": "0.1.0",
44
"description": "<%- project.description %>",
55
"source": "./src/index.tsx",
6-
"main": "./lib/commonjs/index.js",
7-
"module": "./lib/module/index.js",
6+
"main": "./lib/module/index.js",
87
"exports": {
98
".": {
10-
"import": {
11-
"types": "./lib/typescript/module/src/index.d.ts",
12-
"default": "./lib/module/index.js"
13-
},
14-
"require": {
15-
"types": "./lib/typescript/commonjs/src/index.d.ts",
16-
"default": "./lib/commonjs/index.js"
17-
}
9+
"types": "./lib/typescript/src/index.d.ts",
10+
"default": "./lib/module/index.js"
1811
}
1912
},
2013
"files": [
@@ -203,12 +196,6 @@
203196
<% if (project.moduleConfig === 'turbo-modules' || project.viewConfig === 'fabric-view') { -%>
204197
"codegen",
205198
<% } -%>
206-
[
207-
"commonjs",
208-
{
209-
"esm": true
210-
}
211-
],
212199
["module",
213200
{
214201
"esm": true

packages/react-native-builder-bob/src/__tests__/__snapshots__/init.test.ts.snap

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,12 @@ exports[`initializes the configuration 1`] = `
99
},
1010
"exports": {
1111
".": {
12-
"import": {
13-
"types": "./lib/typescript/module/src/index.d.ts",
14-
"default": "./lib/module/index.js"
15-
},
16-
"require": {
17-
"types": "./lib/typescript/commonjs/src/index.d.ts",
18-
"default": "./lib/commonjs/index.js"
19-
}
12+
"types": "./lib/typescript/src/index.d.ts",
13+
"default": "./lib/module/index.js"
2014
}
2115
},
2216
"source": "./src/index.ts",
23-
"main": "./lib/commonjs/index.js",
24-
"module": "./lib/module/index.js",
17+
"main": "./lib/module/index.js",
2518
"scripts": {
2619
"prepare": "bob build"
2720
},
@@ -42,12 +35,6 @@ exports[`initializes the configuration 1`] = `
4235
"esm": true
4336
}
4437
],
45-
[
46-
"commonjs",
47-
{
48-
"esm": true
49-
}
50-
],
5138
"typescript"
5239
]
5340
},

packages/react-native-builder-bob/src/init.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export async function init() {
104104
{
105105
title: 'commonjs - for legacy setups (Node.js < 20)',
106106
value: 'commonjs',
107-
selected: true,
107+
selected: false,
108108
},
109109
{
110110
title: 'typescript - declaration files for typechecking',

0 commit comments

Comments
 (0)