From 79c7c5865639c68c9858c70611a5d68f083ccf30 Mon Sep 17 00:00:00 2001 From: Alex Hunt Date: Thu, 2 Jan 2025 10:33:34 -0800 Subject: [PATCH] Document monorepo build setup (#48420) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/48420 Adds a long overdue README for our newer monorepo build setup. Changelog: [Internal] Reviewed By: cipolleschi Differential Revision: D67740763 fbshipit-source-id: 0c7686d75272acf74c0af5a1c4c08336fb45e2a2 --- scripts/build/README.md | 107 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 scripts/build/README.md diff --git a/scripts/build/README.md b/scripts/build/README.md new file mode 100644 index 00000000000000..fffc6cb92102e2 --- /dev/null +++ b/scripts/build/README.md @@ -0,0 +1,107 @@ +# scripts/build + +Shared build setup for the React Native monorepo. + +## Overview + +These scripts form the modern build setup for JavaScript ([Flow](https://flow.org/)) packages in `react-native`, exposed as `yarn build`. + +> [!Tip] +> 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. + +#### Key info + +- **Which packages are included?** + - Currently, only Node.js-targeting packages are included, configured in `config.js`. + - We don't yet include runtime packages (targeting Metro). These are instead transformed in user space via `@react-native/babel-preset`. +- **When does the build run?** + - Packages are built in CI workflows — both for integration/E2E tests, and before publishing to npm. + +#### Limitations/quirks + +> [!Note] +> **🚧 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. + +- Running `yarn build` will mutate `package.json` files in place, resulting in a dirty Git working copy. +- 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"`. + +## Usage + +**💡 Reminder**: 99% of the time, there is no need to use `yarn build`, as all packages will run from source during development. + +Build commands are exposed as npm scripts at the repo root. + +```sh +# Build all packages +yarn build + +# Build a specific package +yarn build dev-middleware + +# Clean build directories +yarn clean +``` + +Once built, developing in the monorepo should continue to work — now using the compiled version of each package. + +> [!Warning] +> **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. + +## Configuration + +Monorepo packages must be opted in for build, configured in `config.js` (where build options are also documented). + +```js +const buildConfig /*: BuildConfig */ = { + 'packages': { + 'dev-middleware': { + emitTypeScriptDefs: true, + target: 'node', + }, + ... +``` + +#### Required package structure + +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. + +```sh +packages/ + example-pkg/ + src/ # All source files + index.js # Entry point wrapper file (calls babel-register.js) (compiled away) + index.flow.js # Entry point implementation in Flow + [other files] + index.js.flow # Shim for the Flow typechecker + package.json # Includes "exports" field, ideally only src/index.js +``` + +Notes: + +- The additional root `index.js.flow` shim is needed due to Flow itself not supporting Package Exports. +- To minimize complexity, prefer only a single entry of `{".": "src/index.js"}` in `"exports"` for new packages. + +## Build behavior + +Running `yarn build` will compile each package following the below steps, depending on the configured `target` and other build options. + +- Create a `dist/` directory, replicating each source file under `src/`: + - For every `@flow` file, strip Flow annotations using [flow-api-extractor](https://www.npmjs.com/package/flow-api-translator). + - For every entry point in `"exports"`, remove the `.js` wrapper file and compile from the `.flow.js` source. +- Rewrite each package `"exports"` target to map to the `dist/` directory location. +- 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). + +Together, this might look like the following: + +```sh +packages/ + example-pkg/ + dist/ + index.js # Compiled source file (from index.flow.js) + index.js.flow # Flow definition file + index.d.ts # TypeScript definition file + [other transformed files] + package.json # "src/index.js" export rewritten to "dist/index.js" +``` + +**Link**: [Example `dist/` output on npm](https://www.npmjs.com/package/@react-native/dev-middleware/v/0.76.5?activeTab=code).