|
| 1 | +# scripts/build |
| 2 | + |
| 3 | +Shared build setup for the React Native monorepo. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +These scripts form the modern build setup for JavaScript ([Flow](https://flow.org/)) packages in `react-native`, exposed as `yarn build`. |
| 8 | + |
| 9 | +> [!Tip] |
| 10 | +> Generally, React Native maintainers do not need to run `yarn build`, as all packages will run from source during development. Please continue reading if you are adding/removing a package or modifying its build configuration. |
| 11 | +
|
| 12 | +#### Key info |
| 13 | + |
| 14 | +- **Which packages are included?** |
| 15 | + - Currently, only Node.js-targeting packages are included, configured in `config.js`. |
| 16 | + - We don't yet include runtime packages (targeting Metro). These are instead transformed in user space via `@react-native/babel-preset`. |
| 17 | +- **When does the build run?** |
| 18 | + - Packages are built in CI workflows — both for integration/E2E tests, and before publishing to npm. |
| 19 | + |
| 20 | +#### Limitations/quirks |
| 21 | + |
| 22 | +> [!Note] |
| 23 | +> **🚧 Work in progress!** This is not the final state for our monorepo build tooling. Unfortunately, our solution options are narrow due to integration requirements with Meta's codebase. |
| 24 | +
|
| 25 | +- Running `yarn build` will mutate `package.json` files in place, resulting in a dirty Git working copy. |
| 26 | +- We make use of "wrapper files" (`.js` → `.js.flow`) for each package entry point, to enable running from source with zero config. To validate these, package entry points must be explicitly defined via `"exports"`. |
| 27 | + |
| 28 | +## Usage |
| 29 | + |
| 30 | +**💡 Reminder**: 99% of the time, there is no need to use `yarn build`, as all packages will run from source during development. |
| 31 | + |
| 32 | +Build commands are exposed as npm scripts at the repo root. |
| 33 | + |
| 34 | +```sh |
| 35 | +# Build all packages |
| 36 | +yarn build |
| 37 | + |
| 38 | +# Build a specific package |
| 39 | +yarn build dev-middleware |
| 40 | + |
| 41 | +# Clean build directories |
| 42 | +yarn clean |
| 43 | +``` |
| 44 | + |
| 45 | +Once built, developing in the monorepo should continue to work — now using the compiled version of each package. |
| 46 | + |
| 47 | +> [!Warning] |
| 48 | +> **Build changes should not be committed**. Currently, `yarn build` will make changes to each `package.json` file, which should not be committed. This is validated in CI. |
| 49 | +
|
| 50 | +## Configuration |
| 51 | + |
| 52 | +Monorepo packages must be opted in for build, configured in `config.js` (where build options are also documented). |
| 53 | + |
| 54 | +```js |
| 55 | +const buildConfig /*: BuildConfig */ = { |
| 56 | + 'packages': { |
| 57 | + 'dev-middleware': { |
| 58 | + emitTypeScriptDefs: true, |
| 59 | + target: 'node', |
| 60 | + }, |
| 61 | + ... |
| 62 | +``` |
| 63 | +
|
| 64 | +#### Required package structure |
| 65 | +
|
| 66 | +Opting a package into the `yarn build` setup requires a strict file layout. This is done to simplify config and to force consistency across the monorepo. |
| 67 | +
|
| 68 | +```sh |
| 69 | +packages/ |
| 70 | + example-pkg/ |
| 71 | + src/ # All source files |
| 72 | + index.js # Entry point wrapper file (calls babel-register.js) (compiled away) |
| 73 | + index.flow.js # Entry point implementation in Flow |
| 74 | + [other files] |
| 75 | + index.js.flow # Shim for the Flow typechecker |
| 76 | + package.json # Includes "exports" field, ideally only src/index.js |
| 77 | +``` |
| 78 | +
|
| 79 | +Notes: |
| 80 | +
|
| 81 | +- The additional root `index.js.flow` shim is needed due to Flow itself not supporting Package Exports. |
| 82 | +- To minimize complexity, prefer only a single entry of `{".": "src/index.js"}` in `"exports"` for new packages. |
| 83 | +
|
| 84 | +## Build behavior |
| 85 | +
|
| 86 | +Running `yarn build` will compile each package following the below steps, depending on the configured `target` and other build options. |
| 87 | +
|
| 88 | +- Create a `dist/` directory, replicating each source file under `src/`: |
| 89 | + - For every `@flow` file, strip Flow annotations using [flow-api-extractor](https://www.npmjs.com/package/flow-api-translator). |
| 90 | + - For every entry point in `"exports"`, remove the `.js` wrapper file and compile from the `.flow.js` source. |
| 91 | +- Rewrite each package `"exports"` target to map to the `dist/` directory location. |
| 92 | +- If configured, emit a Flow (`.js.flow`) or TypeScript (`.d.ts`) type definition file per source file, using [flow-api-extractor](https://www.npmjs.com/package/flow-api-translator). |
| 93 | +
|
| 94 | +Together, this might look like the following: |
| 95 | +
|
| 96 | +```sh |
| 97 | +packages/ |
| 98 | + example-pkg/ |
| 99 | + dist/ |
| 100 | + index.js # Compiled source file (from index.flow.js) |
| 101 | + index.js.flow # Flow definition file |
| 102 | + index.d.ts # TypeScript definition file |
| 103 | + [other transformed files] |
| 104 | + package.json # "src/index.js" export rewritten to "dist/index.js" |
| 105 | +``` |
| 106 | +
|
| 107 | +**Link**: [Example `dist/` output on npm](https://www.npmjs.com/package/@react-native/dev-middleware/v/0.76.5?activeTab=code). |
0 commit comments