Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document monorepo build setup #48420

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions scripts/build/README.md
Original file line number Diff line number Diff line change
@@ -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).
Loading