diff --git a/docs/API.md b/docs/API.md index f83f30d767..2979527e7d 100644 --- a/docs/API.md +++ b/docs/API.md @@ -44,31 +44,37 @@ title: Bundling API ## Reference -All functions exposed below accept an additional `config` option. This object should be the [Metro configuration](CLI.md) exposed by your `metro.config.js` file - you can obtain it using `Metro.loadConfig`. +The `config` object accepted by the functions below is the [Metro configuration](./Configuration.md) exported by your `metro.config.js` file, which you can obtain with `Metro.loadConfig`. -### `loadConfig()` +### `loadConfig(, )` **Basic options:** `config`, `cwd` -Load the Metro configuration, either from `config` in options if specified, or by traversing the directory hierarchy from `cwd` to the root until it finds a file (by default `metro.config.js`). The returned configuration will have been normalized and merged with Metro's default values. +Load the Metro configuration, either from the path given in `config` if specified, or by traversing the directory hierarchy from `cwd` up to the user's home directory until it finds a [config file](./Configuration.md). The returned configuration will have been normalized and merged with Metro's default values, after applying `defaultConfigOverrides` to the defaults. -### `async runMetro(config)` +`loadConfig` also accepts the options of the [CLI](./CLI.md), which override the corresponding config values: `port` ([`server.port`](./Configuration.md#port)), `projectRoot`, `watchFolders`, `assetExts`, `sourceExts` and `platforms` (the `resolver` options of the same names), [`maxWorkers`](./Configuration.md#maxworkers) (which defaults to a number based on `os.availableParallelism()`), `transformer` ([`transformer.babelTransformerPath`](./Configuration.md#babeltransformerpath)), `resetCache`, and `verbose` (`false` silences Metro's terminal output). -Creates a Metro server based on the config and returns it. You can use this as a middleware in your existing server. +### `async runMetro(config, )` + +**Basic options:** `waitForBundler`, `watch`, `onBundleBuilt`, `hasReducedPerformance` + +Creates a Metro server based on the config and returns it. Its `processRequest` method can be used as a middleware in your existing server. `hasReducedPerformance` only makes Metro's terminal output warn that Metro is operating with reduced performance. ### `async runBuild(config, )` -**Required options:** `entry`, `out` +**Required options:** `entry` + +**Basic options:** `out`, `bundleOut`, `sourceMapOut`, `dev`, `minify`, `platform`, `sourceMap`, `sourceMapUrl`, `assets`, `onBegin`, `onProgress`, `onComplete`, `customResolverOptions`, `customTransformOptions`, `output` -**Basic options:** `dev`, `minify`, `platform`, `sourceMap`, `sourceMapUrl`, `assets` +Bundles `entry` for the given `platform` (defaults to `'web'`). If `out` or `bundleOut` is given, saves the bundle to `bundleOut`, or to `out` with a `.js` extension. If `sourceMap` is set, also generates a source map. The source map will be inlined, unless `sourceMapUrl` is also defined, in which case `sourceMapUrl` is used as the bundle's `sourceMappingURL`. When saving to disk, the source map is also written to `sourceMapOut`, or to `out` with a `.map` extension. If `assets` is `true`, an array of `AssetData` will be generated and returned in the `assets` property of the result object. -Bundles `entry` for the given `platform`, and saves it to location `out`. If `sourceMap` is set, also generates a source map. The source map will be inlined, unless `sourceMapUrl` is also defined. In the latter case, a new file will be generated with the basename of the `sourceMapUrl` parameter. If `assets` is `true`, an array of `AssetData` will be generated and returned in the `assets` property of the result object. +`output` replaces how the bundle is built and written to disk. It is an object with two functions: `build(server, requestOptions, buildOptions)`, whose result `runBuild` resolves to and passes to `save`, and `save(bundle, outputOptions, log)`, which is only called if `out` or `bundleOut` is given. Defaults to Metro's plain bundle output, `metro/private/shared/output/bundle`. For example, `require('metro/private/shared/output/RamBundle')` writes a [RAM bundle](./Bundling.md#indexed-ram-bundle) instead. ### `async runServer(config, )` -**Basic options:** `host`, `port`, `secureServerOptions`, `unstable_middleware`, `unstable_priorityMiddleware`, `unstable_onServerCreated`, `secure (DEPRECATED)`, `secureKey (DEPRECATED)`, `secureCert (DEPRECATED)` +**Basic options:** `host`, `onReady`, `onClose`, `onError`, `waitForBundler`, `watch`, `hasReducedPerformance`, `websocketEndpoints`, `secureServerOptions`, `unstable_middleware`, `unstable_priorityMiddleware`, `unstable_onServerCreated`, `secure (DEPRECATED)`, `secureKey (DEPRECATED)`, `secureCert (DEPRECATED)` -Starts a full Metro HTTP server. It will listen on the specified `host:port`, and can then be queried to retrieve bundles for various entry points. If the `secureServerOptions` family of options are present, the server will be exposed over HTTPS. +Starts a full Metro HTTP server. It will listen on the specified `host` and [`server.port`](./Configuration.md#port), and can then be queried to retrieve bundles for various entry points. If `secureServerOptions` or [`server.tls`](./Configuration.md#tls) is present, the server will be exposed over HTTPS. Resolves to `{httpServer}`. `unstable_middleware` and `unstable_priorityMiddleware` take the same shapes as [`server.unstable_middleware`](./Configuration.md#unstable_middleware-experimental) and [`server.unstable_priorityMiddleware`](./Configuration.md#unstable_prioritymiddleware-experimental), and wrap them: `unstable_middleware` passed here runs after the config's, and `unstable_priorityMiddleware` runs before the config's. @@ -80,6 +86,20 @@ Priority middleware handlers must call `next()` for every request they don't ful ### `createConnectMiddleware(config, )` -**Basic options:** `port`, `onBundleBuilt` +**Basic options:** `onBundleBuilt`, `watch`, `waitForBundler`, `hasReducedPerformance` + +Instead of creating the full server, creates a Connect middleware that answers to bundle requests. This middleware can then be plugged into your own servers. Resolves to `{middleware, attachHmrServer(httpServer), metroServer, end()}`. The `onBundleBuilt` function is optional, is passed the bundle path, and is called when the server has finished creating the bundle. + +### `async buildGraph(config, )` + +**Required options:** `entries` + +**Basic options:** `dev`, `minify`, `platform`, `type`, `customTransformOptions` + +Resolves and transforms the dependency graph for the given `entries` without serializing a bundle, and returns the graph. + +### `attachMetroCli(yargs, )` + +**Basic options:** `build`, `serve`, `dependencies` -Instead of creating the full server, creates a Connect middleware that answers to bundle requests. This middleware can then be plugged into your own servers. The `port` parameter is optional and only used for logging purposes. The `onBundleBuilt` function is optional, is passed the bundle name, and is called when the server has finished creating the bundle. +Registers Metro's [CLI commands](./CLI.md) on a `yargs` instance. Pass `null` for a command to omit it. diff --git a/docs/Bundling.md b/docs/Bundling.md index d97a86d994..7f9a312703 100644 --- a/docs/Bundling.md +++ b/docs/Bundling.md @@ -7,23 +7,25 @@ When bundling, each of the modules gets assigned a numeric id, meaning no dynami ## Plain bundle -This is the standard bundling format. In this format, all files are wrapped with a function call, then added to the global file. This is useful for environments that expect a JS only bundle (e.g. a browser). Just requiring the entry point with the `.bundle` extension should trigger a build of it. +This is the standard bundling format. In this format, all files are wrapped with a function call (`__d()`, which registers the module with Metro's module system), then concatenated into a single file. This is useful for environments that expect a JS only bundle (e.g. a browser). Just requiring the entry point with the `.bundle` extension should trigger a build of it. ## Indexed RAM bundle +RAM (Random Access Modules) bundles store each module separately, so the app can load a module's code only when it is first required. + This format composes the bundle as a binary file, which format has the following parts (all numbers are expressed in Little Endian): * A magic number: a `uint32` must be located at the beginning of the file, with the value `0xFB0BD1E5`. This is used to verify the file. * An offset table: the table is a sequence of `uint32` pairs, with a header - * For the header, two `uint32`s can be found: the length of the table, and the length of the startup code. - * For the pairs, they represent the offset in the file and the length of code module, in bytes. -* Each of the modules, finished by a null byte (`\0`). + * For the header, two `uint32`s can be found: the number of table entries (highest module id + 1), and the length of the startup code, in bytes. + * For the pairs, they represent the offset of the module code relative to the start of the startup code, and the length of the module code, in bytes. +* The startup code, followed by each of the modules, each finished by a null byte (`\0`). The startup code runs when the bundle loads: polyfills, Metro's module system, [`preloadedModules`](./Configuration.md#gettransformoptions), and the call that runs the entry point. ``` ` 0 1 2 3 4 5 6 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Magic number | Header size | +| Magic number | Number of entries | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Startup code size | Module 0 offset | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -35,9 +37,9 @@ This format composes the bundle as a binary file, which format has the following + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | Module n offset | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Module n length | Module 0 code | Module 0 code | ... | \0 | +| Module n length | Startup code | Startup code | ... | \0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Module 1 code | Module 1 code | ... | \0 | | +| Module 0 code | Module 0 code | ... | \0 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + ... + @@ -49,12 +51,12 @@ This format composes the bundle as a binary file, which format has the following This structure is optimal for an environment that is able to load all code in memory at once: -* By using the offset table, one can load any module in constant time, where the code for module `x` is located at `file[(x + 3) * sizeof(uint32)]`. Since there is a null character (`\0`) separating all modules, usually length does not even need to be used, and the module can be loaded directly as an ASCIIZ string. -* Startup code is always found at `file[sizeof(uint32)]`. +* By using the offset table, one can load any module in constant time, where the offset/length entry for module `x` is located at `file[(3 + 2 * x) * sizeof(uint32)]`, and its code starts at that offset from the start of the startup code. Since there is a null character (`\0`) separating all modules, usually length does not even need to be used, and the module can be loaded directly as a null-terminated (ASCIIZ) string. +* Startup code is always found right after the offset table, at `file[(3 + 2 * numEntries) * sizeof(uint32)]`. This bundling is usually used by iOS. ## File RAM bundle -Each module is stored as a file, with the name `js-modules/${id}.js`, plus an extra file called `UNBUNDLE` is created, which its only content is the magic number, `0xFB0BD1E5`. Note that the `UNBUNDLE` file is created at the root. +Each module is stored as a file, with the name `js-modules/${id}.js`, plus an extra file called `UNBUNDLE` is created, which its only content is the magic number, `0xFB0BD1E5`, and which marks the bundle as a file RAM bundle. Note that the `UNBUNDLE` file is created inside the `js-modules` directory, and the startup code is written to the bundle output file. This bundling is usually used by Android, since package contents are zipped, and access to a zipped file is much faster. If the indexed format was used instead, all the bundled should be unzipped at once to get the code for the corresponding module. diff --git a/docs/CLI.md b/docs/CLI.md index 2caaa72976..77db7638be 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -14,25 +14,45 @@ Generates a JavaScript bundle containing the specified entrypoint and its descen | Option | Alias | Description | Value | |----------|----------|----------|----------| -| `out` | `O` | File name where to store the output | String | -| `platform` | `p` | Which platform to bundle for | `web`, `android`, `ios` | +| `out` | `O` | File name where to store the output (required) | String | +| `platform` | `p` | Which platform to bundle for (e.g. `web`, `android`, `ios`) | String | | `minify` | `z` | Whether Metro should minify the bundle | Boolean | -| `dev` | `g` | Create a development version of the build (`process.env.NODE_ENV = 'development'`) | Boolean | +| `dev` | `g` | Create a development build, in which `__DEV__` is `true` | Boolean | | `config` | `c` | Location of the `metro.config.js` to use | String | -| `max-workers` | `j` | The number of workers Metro should parallelize the transformer on | Number | -| `project-roots` | `P` | The root folder of your project | Array | +| `max-workers` | `j` | The number of workers Metro should parallelize the transformer on. Overrides [`maxWorkers`](./Configuration.md#maxworkers) which defaults to a number based on `os.availableParallelism()` | Number | +| `project-roots` | `P` | Accepted, but has no effect | Array | | `source-map` | | Whether Metro should generate source maps | Boolean | | `source-map-url` | | URL where the source map can be found | String | -| `legacy-bundler` | | Whether Metro should use the legacy bundler | Boolean | +| `legacy-bundler` | | Accepted, but has no effect | Boolean | | `resolver-option` | | [Custom resolver options](./Resolution.md#customresolveroptions-string-mixed) of the form `key=value` | Array | -| `transform-option` | | Custom transform options of the form `key=value` | Array | +| `transform-option` | `transformer-option` | Custom transform options of the form `key=value` | Array | +| `reset-cache` | | **Deprecated.** Clears Metro's caches before building, like [`resetCache`](./Configuration.md#resetcache) | Boolean | +| `output-type` | `t` | Accepted, but has no effect | String | ## `serve` -Starts Metro on the given port, building bundles on the fly. +Alias: `start` -## `get-dependencies ` +Starts Metro on the given port, building bundles on the fly. Restarts the server when the config file changes. + +### Options + +| Option | Alias | Description | Value | +|----------|----------|----------|----------| +| `host` | `h` | Host to listen on. Defaults to `localhost` | String | +| `port` | `p` | Port to listen on. Defaults to `8081`, overriding [`server.port`](./Configuration.md#port) | Number | +| `config` | `c` | Location of the `metro.config.js` to use | String | +| `max-workers` | `j` | The number of workers Metro should parallelize the transformer on. Overrides [`maxWorkers`](./Configuration.md#maxworkers) which defaults to a number based on `os.availableParallelism()` | Number | +| `secure-server-options` | `s` | Options for the HTTPS server, using dot notation (e.g. `-s.cert="$(cat path/to/cert)" -s.key="$(cat path/to/key)"`) | Object | +| `secure` | | **Deprecated.** Serve over HTTPS. Use `secure-server-options` instead | Boolean | +| `secure-key` | | **Deprecated.** Path to the key file for HTTPS when `secure` is set | String | +| `secure-cert` | | **Deprecated.** Path to the cert file for HTTPS when `secure` is set | String | +| `reset-cache` | | **Deprecated.** Clears Metro's caches on startup, like [`resetCache`](./Configuration.md#resetcache) | Boolean | +| `project-roots` | `P` | Accepted, but has no effect | Array | +| `hmr-enabled` | `hmr` | Accepted, but has no effect | Boolean | + +## `get-dependencies [entryFile]` List all dependencies that will be bundled for a given entry point. @@ -44,6 +64,6 @@ List all dependencies that will be bundled for a given entry point. | `output` | File name where to store the output, ex. /tmp/dependencies.txt | | `platform` | The platform extension used for selecting modules | | `transformer` | Specify a custom transformer to be used | -| `max-workers` | Specifies the maximum number of workers the worker-pool will spawn for transforming files. This defaults to the number of the cores available on your machine. | -| `dev` | If false, skip all dev-only code path | -| `verbose` | Enables logging | +| `max-workers` | Specifies the maximum number of workers the worker-pool will spawn for transforming files. Overrides [`maxWorkers`](./Configuration.md#maxworkers) which defaults to a number based on `os.availableParallelism()`. | +| `dev` | If false, skip all dev-only code path. Defaults to `true` | +| `verbose` | Enables logging. Defaults to `false` | diff --git a/docs/Caching.md b/docs/Caching.md index bfda4f658e..726a084dcf 100644 --- a/docs/Caching.md +++ b/docs/Caching.md @@ -13,22 +13,32 @@ A typical setup for a remote cache involves: 2. Running [`metro build`](./CLI.md#build-entry) periodically (e.g. in a CI job) to populate the cache, using `HttpStore` (or a custom read/write cache store) in your Metro config. 3. Configuring Metro on your development machines to read from the cache, using `HttpGetStore` (or a custom read-only cache store) in your Metro config. -The main option for configuring the Metro cache is [`cacheStores`](./Configuration.md#cachestores). Typically, the local cache (e.g. `FileStore`) should be listed first, followed by the remote cache (e.g. `HttpCache`). +The main option for configuring the Metro cache is [`cacheStores`](./Configuration.md#cachestores). Typically, the local cache (e.g. `FileStore`) should be listed first, followed by the remote cache (e.g. `HttpStore` or `HttpGetStore`). ## Built-in cache stores Metro provides a number of built-in cache store implementations for use with the [`cacheStores`](./Configuration.md#cachestores) config option: * **`FileStore({root: string})`** will store cache entries as files under the directory specified by `root`. -* **`AutoCleanFileStore()`**
Deprecated
is a `FileStore` that periodically cleans up old entries. It accepts the same options as `FileStore` plus the following: +* **`AutoCleanFileStore(options)`**
Deprecated
is a `FileStore` that periodically cleans up old entries. It accepts the same options as `FileStore` plus the following: * **`options.intervalMs: number`** is the time in milliseconds between cleanup attempts. Defaults to 10 minutes. * **`options.cleanupThresholdMs: number`** is the minimum time in milliseconds since the last modification of an entry before it can be deleted. Defaults to 3 days. * **`HttpStore(options)`** is a bare-bones remote cache client that reads (`GET`) and writes (`PUT`) compressed cache artifacts over HTTP or HTTPS. - * **`options.endpoint: string`** is the base URL for the cache server. For example, an `HttpStore` with `'http://www.example.com/endpoint'` as the endpoint would issue requests to URLs such as `http://www.example.com/endpoint/c083bff944879d9f528cf185eba0f496bc10a47d`. + * **`options.endpoint: string`** is the base URL for the cache server. For example, an `HttpStore` with `'http://www.example.com/endpoint'` as the endpoint would issue requests to URLs such as `http://www.example.com/endpoint/c083bff944879d9f528cf185eba0f4962fd4e5b1a6a7d2a1c0bd42c3c0d5f0e1b6a9e7c8` (the hex-encoded cache key). * **`options.timeout: number`** is the timeout for requests to the cache server, in milliseconds. Defaults to 5000. * **`options.family: 4 | 6`** is the same as the `family` parameter to Node's [`http.request`](https://nodejs.org/api/http.html#httprequesturl-options-callback). * **`options.cert`, `options.ca`, `options.key`**: HTTPS options passed directly to [Node's built-in HTTPS client](https://nodejs.org/api/https.html). -* **`HttpGetStore(options)`** is a read-only version of `HttpStore`. + * **`options.headers: {[string]: string}`**: HTTP headers sent with every request. + * **`options.params: URLSearchParams`**: Query parameters appended to every request URL. + * **`options.additionalSuccessStatuses: Array`**: Additional HTTP status codes treated as success. + * **`options.maxAttempts: number`**: The maximum number of attempts per request, with exponential backoff. Defaults to 1 (no retries). + * **`options.retryStatuses: Set`**: HTTP status codes that trigger a retry. + * **`options.retryNetworkErrors: boolean`**: Whether to retry on network errors. Defaults to `false`. + * **`options.debug: boolean`**: Whether to include the response body in HTTP error messages. Defaults to `false`. + * **`options.socketPath: string`**: A Unix domain socket to connect through. + * **`options.proxy: string`**: A proxy URL to connect through. + * Alternatively, `options` may be `{getOptions, setOptions}`, each an object with the options above, to use different settings for reads and writes. +* **`HttpGetStore(options)`** is a read-only version of `HttpStore`. HTTP and network errors on read are treated as cache misses (with a one-time warning) rather than failing the build. You can import these classes from the `metro-cache` package or get them through the function form of `cacheStores`: @@ -53,6 +63,9 @@ To implement a custom cache store, pass an instance of a class with the followin ```flow interface CacheStore { + // Optional name used in Metro's cache logs. Defaults to the class name. + name?: string; + // Read an entry from the cache. Returns `null` if not found. get(key: Buffer): ?T | Promise; diff --git a/docs/Configuration.md b/docs/Configuration.md index a0797d3cd6..f5085e985a 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -6,16 +6,16 @@ title: Configuring Metro A Metro config can be created in the following file formats (ordered by priority): 1. `metro.config.js` / `metro.config.cjs` / `metro.config.mjs` (CommonJS or ESM) -2. `metro.config.ts` / `metro.config.cts` / `metro.config.mts` (TypeScript) -3. `metro.config.json` -4. `.config/metro.js` / `.config/metro.cjs` / `.config/metro.mjs` / `.config/metro.ts` / `.config/metro.cts` / `.config/metro.mts` / `.config/metro.json` +2. `metro.config.json` +3. `metro.config.ts` / `metro.config.cts` / `metro.config.mts` (TypeScript) +4. `.config/metro.js` / `.config/metro.cjs` / `.config/metro.mjs` / `.config/metro.json` / `.config/metro.ts` / `.config/metro.cts` / `.config/metro.mts` 5. The `metro` field in `package.json` You can also give a custom file to the configuration by specifying `--config ` when calling the CLI. :::info TypeScript Config Support -Metro uses Node.js native TypeScript support to load `.*ts` config files. For Node.js 22.18.0 and later or 24.0.0 later, _erasable_ TypeScript is supported out of the box. For Node.js < 22.18.0, use the `--experimental-strip-types` flag. See https://nodejs.org/learn/typescript/run-natively. +Metro uses Node.js native TypeScript support to load `.*ts` config files. For Node.js 22.18.0 and later or 24.0.0 and later, _erasable_ TypeScript is supported out of the box. For Node.js 22.6.0 to 22.17.x, use the `--experimental-strip-types` flag. See https://nodejs.org/learn/typescript/run-natively. ::: @@ -49,6 +49,9 @@ const config: MetroConfig = { server: { /* server options */ }, + symbolicator: { + /* symbolicator options */ + }, watcher: { /* watcher options */ watchman: { @@ -161,12 +164,11 @@ If `true`, Metro will use a stable mapping from files to transformer workers, so Type: `number` -The number of workers to use for parallel processing in Metro. Defaults to approximately half of the number of cores available on the machine, as reported by [`os.availableParallelism()`](https://nodejs.org/api/os.html#osavailableparallelism). +The number of workers to use for parallel processing in Metro. Defaults to roughly 75% of the cores available on the machine (as reported by [`os.availableParallelism()`](https://nodejs.org/api/os.html#osavailableparallelism)) for up to 8 cores, falling to 50% for 48 or more, e.g. 6 workers on 8 cores, 10 on 16 and 32 on 64. :::note -1. Values exceeding the number of available cores have no effect. -2. If `maxWorkers` is set to 1 or lower, worker code will run in the main Metro process instead of concurrently. -3. Metro has two separate worker pools - one for transformation and one for building the file map. Each pool has its worker count set to `maxWorkers` independently. +1. If `maxWorkers` is set to 1 or lower, worker code will run in the main Metro process instead of concurrently. +2. Metro has two separate worker pools - one for transformation and one for building the file map. Each pool has its worker count set to `maxWorkers` independently. ::: #### `fileMapCacheDirectory` @@ -189,16 +191,16 @@ A logger factory function that can be used to get insights about Metro performan ```flow function unstable_perfLoggerFactory( - type: string, - opts: $ReadOnly<{ - key?: string + type: 'START_UP' | 'BUNDLING_REQUEST' | 'HMR', + opts?: $ReadOnly<{ + key?: number }>, ): RootPerfLogger { // ... }; ``` -* **`type`** Type of event being logged, e.g. `'STARTUP'`, `'BUNDLING_REQUEST'`, `'HMR'`. See type definition of [PerfLoggerFactory](https://github.com/react/metro/blob/main/packages/metro-config/src/types.js) for a full list of event types. +* **`type`** Type of event being logged, e.g. `'START_UP'`, `'BUNDLING_REQUEST'`, `'HMR'`. See type definition of [PerfLoggerFactory](https://github.com/react/metro/blob/main/packages/metro-config/src/types.js) for a full list of event types. * **`opts`** * **`key`**: An opaque identifier to distinguish between instances of an event type (e.g. multiple, possibly concurrent, HMR requests). @@ -318,7 +320,7 @@ For more information on customizing the resolver, see [Module Resolution](https: #### `schemeResolvers` -Type: `?{[scheme: string]: `[`CustomResolver`](./Resolution.md#resolverequest-customresolver)`}` +Type: `{[scheme: string]: `[`CustomResolver`](./Resolution.md#resolverequest-customresolver)`}` An object of custom resolvers for import specifiers prefixed with a URI scheme, keyed by lowercase scheme name (the prefix before the first `:`, without the colon). When Metro's default resolution encounters a specifier whose scheme matches a registered key (for example `my-scheme:foo` matching `'my-scheme'`), the corresponding resolver is invoked with the full specifier. @@ -366,6 +368,8 @@ Type: `RegExp` or `Array` A regular expression (or list of regular expressions) defining which paths to exclude from Metro's file map. Files whose absolute paths match these patterns are effectively hidden from Metro and cannot be resolved or imported in the current project. Additionally, blocked files cannot be served via the `/assets/` endpoint. +Defaults to a pattern that hides `__tests__` directories. Setting `blockList` replaces this default. + #### `hasteImplModulePath` Type: `?string` @@ -379,11 +383,39 @@ module.exports = { getHasteName(filePath: string): ?string { // ... }, + getCacheKey(): string { + // ... + }, }; ``` `getHasteName` should return a short, globally unique name for the module whose path is `filePath`, or `null` if the module should not be accessible via Haste. +`getCacheKey` should return a string that changes whenever the output of `getHasteName` may change. It is used to invalidate the file map cache. + +#### `dependencyExtractor` + +Type: `?string` + +The path to a module that extracts the dependencies of each file for the file map. These are stored as file map metadata and are not used for bundling. + +Metro expects this module to have the following signature: + +```flow +module.exports = { + extract( + content: string, + absoluteFilePath: string, + defaultExtractor: (content: string) => Set, + ): Set { + // ... + }, + getCacheKey(): string { + // ... + }, +}; +``` + #### `platforms` Type: `Array` @@ -440,7 +472,7 @@ The set of additional [condition names](https://nodejs.org/docs/latest-v18.x/api Matched conditions are merged with [`unstable_conditionNames`](#unstable_conditionnames-experimental) before resolution. Under React Native's defaults for both options, a `require()` call site asserts `new Set(['default', 'require', 'react-native', 'browser'])` when requesting a `web` bundle, and `new Set(['default', 'require', 'react-native'])` otherwise. Again, these are resolved in the order specified by each package. -Defaults to `‌{ web: ['browser'] }`. +Defaults to `{ web: ['browser'] }`. #### `unstable_enablePackageExports`
Experimental
@@ -461,8 +493,6 @@ In a future release of Metro, this option will be removed. ::: ---- - ### Transformer Options @@ -486,7 +516,7 @@ In older versions of Metro, a custom `asyncRequireModulePath` could be used as p Type: `'throwAtRuntime' | 'reject'` -Controls how Metro handles dependencies that cannot be statically analyzed at build time. For example, `require('./' + someFunction() + '.js')` cannot be resolved without knowing what `someFunction()` will return. +Controls how Metro handles dependencies inside `node_modules` that cannot be statically analyzed at build time. For example, `require('./' + someFunction() + '.js')` cannot be resolved without knowing what `someFunction()` will return. Such dependencies outside `node_modules` are always rejected. * **`'throwAtRuntime'`** (the default): Metro does not stop bundling, but the `require` call will throw at runtime. * **`'reject'`**: Metro will stop bundling and report an error to the user. @@ -504,7 +534,7 @@ function getTransformOptions( entryPoints: $ReadOnlyArray, options: { dev: boolean, - hot: boolean, + hot: true, platform: ?string, }, getDependenciesOf: (path: string) => Promise>, @@ -529,6 +559,7 @@ type ExtraTransformOptions = { preloadedModules?: {[path: string]: true} | false, ramGroups?: Array, transform?: { + experimentalImportSupport?: boolean, inlineRequires?: {blockList: {[string]: true}} | boolean, nonInlinedRequires?: $ReadOnlyArray, }, @@ -538,10 +569,11 @@ type ExtraTransformOptions = { * **`preloadedModules`**: A plain object whose keys represent a set of absolute paths. When serializing an [indexed RAM bundle](https://reactnative.dev/docs/ram-bundles-inline-requires#enable-the-ram-format), the modules in this set will be marked for eager evaluation at runtime. * **`ramGroups`**: An array of absolute paths. When serializing an [indexed RAM bundle](https://reactnative.dev/docs/ram-bundles-inline-requires#enable-the-ram-format), each of the listed modules will be serialized along with its transitive dependencies. At runtime, the modules will all be parsed together as soon as any one of them is evaluated. * **`transform`**: Advanced options for the transformer. + * **`experimentalImportSupport`**: Whether to transform ES module `import`/`export` statements with Metro's own import/export plugin. * **`inlineRequires`**: * If `inlineRequires` is a boolean, it controls whether [inline requires](https://reactnative.dev/docs/optimizing-javascript-loading#advanced-automatically-inline-require-calls) are enabled in this bundle. * If `inlineRequires` is an object, inline requires are enabled in all modules, except ones whose absolute paths appear as keys of `inlineRequires.blockList`. - * **`nonInlinedRequires`**: An array of unresolved module specifiers (e.g. `react`, `react-native`) to never inline, even when inline requires are enabled. + * **`nonInlinedRequires`**: An array of unresolved module specifiers (e.g. `react`, `react-native`) to never inline, even when inline requires are enabled. Defaults to `['React', 'react', 'react/jsx-dev-runtime', 'react/jsx-runtime', 'react-compiler-runtime', 'react-native']`. #### `minifierPath` @@ -561,6 +593,24 @@ Type: `number` Define a threshold (in bytes) to disable some expensive optimizations for big files. +#### `globalPrefix` + +Type: `string` + +A prefix for the global names used by Metro's module system, e.g. `${globalPrefix}__d`. Defaults to `''`. + +#### `allowOptionalDependencies` + +Type: `boolean | {exclude: Array}` + +Whether to treat dependencies that are required inside a `try` block (or dynamic `import()` calls with a rejection handler) as optional, so that failing to resolve them does not fail the build. If an object, specifiers listed in `exclude` are never treated as optional. Defaults to `true`. + +#### `publicPath` + +Type: `string` + +The URL path prefix written into each asset's `httpServerLocation`, which the app uses to request the asset. Metro's own server only serves assets under `/assets`, so with a different value you need to route those requests yourself, e.g. with [`rewriteRequestUrl`](#rewriterequesturl). Defaults to `'/assets'`. + #### React Native Only #### `assetPlugins` @@ -573,7 +623,7 @@ List of modules to call to modify Asset data Type: `string` -Where to fetch the assets from. +The module (path or package name) that generated asset modules `require()` and call `registerAsset(descriptor)` on. React Native sets this to its `AssetRegistry`. ### Babel-specific transformer options @@ -603,7 +653,7 @@ This option only has an effect under the default [`transformerPath`](#transforme Type: `boolean | string` -Whether the transformer should use the `@babel/transform/runtime` plugin. Defaults to `true`. +Whether the transformer should use the `@babel/plugin-transform-runtime` plugin. Defaults to `true`. If the value is a string, it is treated as a runtime version number and passed as `version` to the `@babel/plugin-transform-runtime` configuration. This allows you to optimize the generated Babel runtime calls based on the version installed in your project. @@ -628,7 +678,7 @@ This option only has an effect under the default [`transformerPath`](#transforme Type: `(moduleId: number | string, globalPrefix: string) => string` -Specify the format of the initial require statements that are appended at the end of the bundle. By default is `__r(${moduleId});`. +Specify the format of the initial require statements that are appended at the end of the bundle. By default is `` `__r(${JSON.stringify(moduleId)});` ``. #### `createModuleIdFactory` @@ -640,7 +690,7 @@ Used to generate the module id for `require` statements. Type: `({platform: ?string}) => $ReadOnlyArray` -An optional list of polyfills to include in the bundle. The list defaults to a set of common polyfills for Number, String, Array, Object... +An optional list of polyfills to include in the bundle. Defaults to `[]`. #### `getModulesRunBeforeMainModule` @@ -650,7 +700,7 @@ An array of modules to be required before the entry point. It should contain the #### `processModuleFilter` -Type: `(module: Array) => boolean` +Type: `(module: Module) => boolean` A filter function to discard specific modules from the output. @@ -666,7 +716,24 @@ Defaults to returning `true` for modules with a path component named `node_modul In addition to modules marked as ignored by `isThirdPartyModule`, Metro will also automatically add modules generated by the bundler itself to the ignore list. ::: ---- +#### `polyfillModuleNames` + +Type: `Array` + +Absolute paths of additional polyfills to include in the bundle, after those returned by [`getPolyfills`](#getpolyfills). Defaults to `[]`. + +#### `customSerializer` + +Type: `?(entryPoint: string, preModules: $ReadOnlyArray, graph: ReadOnlyGraph, options: SerializerOptions) => Promise` + +A function that replaces Metro's default bundle serializer. Defaults to `null`. + +#### `experimentalSerializerHook` + +Type: `(graph: ReadOnlyGraph, delta: DeltaResult) => mixed` + +A function called with the dependency graph and the changes since the previous build, each time a graph is built or updated. + ### Server Options These options are used when Metro serves the content. @@ -675,17 +742,17 @@ These options are used when Metro serves the content. Type: `number` -Which port to listen on. +Which port to listen on. Defaults to `8081`. #### `useGlobalHotkey` Type: `boolean` -Whether we should enable CMD+R hotkey for refreshing the bundle. +Has no effect. #### `enhanceMiddleware`
Deprecated
-Type: `(Middleware, MetroServer) => Middleware` +Type: `(Middleware, MetroServer) => Middleware | connect.Server` A function that allows attaching custom [`connect`](https://www.npmjs.com/package/connect) middleware to Metro. Metro logs a deprecation warning when this option is set to anything other than its default. Use [`unstable_middleware`](#unstable_middleware-experimental) or [`unstable_priorityMiddleware`](#unstable_prioritymiddleware-experimental) instead, and [`unstable_onServerCreated`](#unstable_onservercreated-experimental) to access the Metro server instance. @@ -745,7 +812,7 @@ Called with Metro's server instance when [`runServer`](./API.md#async-runserverc Type: `string => string` -A function that will be called every time Metro processes a "URL" (see note), after normalization of non-standard query-string delimiters using [`jsc-safe-url`](https://www.npmjs.com/package/jsc-safe-url). Metro will use the return value of this function as if it were the original URL provided by the client. This applies to all incoming HTTP requests (after any custom middleware), as well as bundle URLs in `/symbolicate` request payloads and within the hot reloading protocol. +A function that will be called every time Metro processes a "URL" (see note), after normalization of non-standard query-string delimiters using [`jsc-safe-url`](https://www.npmjs.com/package/jsc-safe-url). Metro will use the return value of this function as if it were the original URL provided by the client. This applies to all incoming HTTP requests that reach Metro (after [`unstable_priorityMiddleware`](#unstable_prioritymiddleware-experimental); [`unstable_middleware`](#unstable_middleware-experimental) handlers see the rewritten URL), as well as bundle URLs in `/symbolicate` request payloads and within the hot reloading protocol. :::note @@ -776,7 +843,25 @@ requestCert?: boolean, // Whether to authenticate the remote peer by request Notice that when overriding the base config, object `tls` configs extend the base `tls` config, `false` overrides the base `tls` configs, and `null` and `undefined` are ignored. -When running Metro with `Metro.runServer` with the `secureServerOptions` property Metro will likewise start an HTTPS server merging with the `config.server.tls` object if provided, overriding it. +When running Metro with `Metro.runServer` with the `secureServerOptions` property, Metro will likewise start an HTTPS server. `secureServerOptions` is merged over the `config.server.tls` object, so its values take precedence. + +--- + +### Symbolicator Options + +These options customize stack traces symbolicated through the `/symbolicate` endpoint. + +#### `customizeFrame` + +Type: `(frame: {file: ?string, lineNumber: ?number, column: ?number, methodName: ?string, ...}) => ?{collapse?: boolean} | Promise` + +Called for each symbolicated stack frame. The returned properties are merged into the frame; e.g. returning `{collapse: true}` collapses frames that are not relevant to users. Defaults to a no-op. + +#### `customizeStack` + +Type: `(stack: Array, extraData: mixed) => Array | Promise>` + +Called with the whole symbolicated stack (after `customizeFrame`), and the `extraData` sent in the `/symbolicate` request. Returns the stack to respond with. Defaults to returning `stack` unchanged. --- @@ -857,13 +942,13 @@ Using the `metro-config` package it is possible to merge multiple configurations | --------------------------------------- | ----------------------------------------------------------------------------------- | | `mergeConfig(...configs): MergedConfig` | Returns the merged configuration of two or more configuration objects or functions. | -`configs` may be any combination of (promises resolving to) configuration objects or functions. Functions are called with the merged config of all configs to the left, which may be useful for complex merges with the previous config. +The first argument must be a configuration object, or a function returning one. Subsequent arguments may be any combination of (promises resolving to) configuration objects or functions. Functions are called with the merged config of all configs to the left, which may be useful for complex merges with the previous config. If any arguments are promises or async functions, `mergeConfig` will return a `Promise`, otherwise it will return the merged config synchronously. :::note -Arrays and function based config parameters do not deeply merge and will instead override any pre-existing config parameters. +Merging is one level deep: within `resolver`, `transformer`, `serializer`, `server`, `symbolicator` and `watcher`, each option you set replaces the earlier value. Arrays, functions and nested objects (e.g. `resolver.extraNodeModules`) are not merged, except `resolver.schemeResolvers`, `server.tls`, `watcher.watchman` and `watcher.healthCheck`, which are merged key by key, and the middleware options described below. This allows overriding and removing default config parameters such as `platforms` or `getModulesRunBeforeMainModule` that may not be required in your environment. ::: diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index d4843d47df..176c6d08be 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -77,28 +77,32 @@ app.listen(8081); ### Method `runServer(config, options)` Starts a development server based on the given configuration and options. Returns an object with `httpServer`, the Node.js HTTP(S) server. -We recommend using `runMetro` instead of `runServer`, `runMetro` calls this function. +`runServer` calls `runMetro` internally. Use `runMetro` instead to attach Metro to your own HTTP server. #### Options * `host (string)`: Where to host the server on. -* `onReady (Function)`: Called when the server is ready to serve requests. +* `onReady (Function)`: Called with the HTTP(S) server when it is ready to serve requests. +* `onError (Function)`: Called with the error if the HTTP(S) server fails. * `onClose (Function)`: Called when the server and all other Metro processes are closed. You can also observe the server close event directly using `httpServer.on('close', () => {});`. ```js const config = await Metro.loadConfig(); -await Metro.runServer(config, { +const {httpServer} = await Metro.runServer(config, { onClose: () => {console.log('metro server and all associated processes are closed')} }); httpServer.on('close', () => {console.log('metro server is closed')}); ``` * `secure (boolean)`: **DEPRECATED** Whether the server should run on `https` instead of `http`. -* `secureKey (string)`: **DEPRECATED** The key to use for `https` when `secure` is on. -* `secureCert (string)`: **DEPRECATED** The cert to use for `https` when `secure` is on. +* `secureKey (string)`: **DEPRECATED** Path to the key file to use for `https` when `secure` is on. +* `secureCert (string)`: **DEPRECATED** Path to the cert file to use for `https` when `secure` is on. * `secureServerOptions (Object)`: The options object to pass to Metro's HTTPS server. The presence of this object will make Metro's server run on `https`. Refer to the [Node docs](https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener) for valid options. * `waitForBundler (boolean)`: Whether to wait for the bundler to finish initializing before returning the server instance. +* `watch (boolean)`: Whether to watch the filesystem for changes. +* `websocketEndpoints (Object)`: Map of URL paths to [`ws`](https://github.com/websockets/ws) `WebSocketServer` instances that handle upgrade requests on those paths. +* `hasReducedPerformance`, `unstable_middleware`, `unstable_priorityMiddleware`, `unstable_onServerCreated`: See [Bundling API](./API.md). ```js const config = await Metro.loadConfig(); @@ -126,21 +130,21 @@ Given a configuration and a set of options that you would typically pass to a se #### Options - - * `assets (boolean)`: Whether to include the assets in the result. * `bundleOut (string)`: Path to save the bundle. No extension will be added. -* `dev (boolean)`: Create a development version of the build (`process.env.NODE_ENV = 'development'`). +* `customResolverOptions (Object)`, `customTransformOptions (Object)`: Custom options passed to the resolver and transformer. +* `dev (boolean)`: Create a development build, in which `__DEV__` is `true`. Otherwise, `__DEV__` and `process.env.NODE_ENV` are inlined as `false` and `'production'`. * `entry (string)`: Pointing to the entry file to bundle. * `onBegin (Function)`: Called when the bundling starts. * `onComplete (Function)`: Called when the bundling finishes. * `onProgress (Function)`: Called during the bundle, every time there's new information available about the module count/progress. * `minify (boolean)`: Whether Metro should minify the bundle. * `out (string)`: Shorthand path to the output bundle and source map. A `.js` extension will be added (if not given) for the bundle and `.map` for the source map. Customize with `bundleOut` / `sourceMapOut`. -* `platform ('web' | 'android' | 'ios')`: Which platform to bundle for if a list of platforms is provided. +* `output (Object)`: Replaces how the bundle is built and saved. See [Bundling API](./API.md). +* `platform (string)`: Which platform to bundle for (e.g. `'web'`, `'android'`, `'ios'`). Defaults to `'web'`. * `sourceMap (boolean)`: Whether Metro should generate source maps. * `sourceMapOut (string)`: Where to save the source map, if `sourceMap == true`. No extension will be added. -* `sourceMapUrl (string)`: URL where the source map can be found. It defaults to the same URL as the bundle, but changing the extension from `.bundle` to `.map`. When `inlineSourceMap` is `true`, this property has no effect. +* `sourceMapUrl (string)`: URL where the source map can be found, written as the bundle's `sourceMappingURL`. If `sourceMap` is `true` and `sourceMapUrl` is not set, the source map is inlined into the bundle. ```js const config = await Metro.loadConfig(); @@ -153,13 +157,16 @@ await Metro.runBuild(config, { }); ``` -### Method `createConnectMiddleware(config)` +### Method `createConnectMiddleware(config, options)` -Instead of creating the full server, creates a Connect middleware that answers to bundle requests. This middleware can then be plugged into your own servers. The `port` parameter is optional and only used for logging purposes. +Instead of creating the full server, creates a Connect middleware that answers to bundle requests. This middleware can then be plugged into your own servers. #### Options -* `port (number)`: Port for the Connect middleware (only for logging purposes). +* `onBundleBuilt (Function)`: Called with the bundle path when a bundle has been built. +* `watch (boolean)`: Whether to watch the filesystem for changes. +* `waitForBundler (boolean)`: Whether to wait for the bundler to finish initializing before returning. +* `hasReducedPerformance (boolean)`: Makes Metro's terminal output warn that Metro is operating with reduced performance. ```js const Metro = require('metro'); @@ -197,10 +204,17 @@ However, the server is also able to serve specific assets depending on the platf Any JS file can be used as the root for a bundle request. The file will be looked in the `projectRoot`. All files that are required by the root will be recursively included. In order to request a bundle, just change the extension from `.js` to `.bundle`. Options for building the bundle are passed as query parameters (all optional). -* `dev`: build the bundle in development mode or not. Maps 1:1 to the `dev` setting of the bundles. Pass `true` or `false` as strings into the URL. -* `platform`: platform requesting the bundle. Can be `ios` or `android`. Maps 1:1 to the `platform` setting of the bundles. -* `minify`: whether code should be minified or not. Maps 1:1 to the `minify` setting of the bundles. Pass `true` or `false` as strings into the URL. -* `excludeSource`: whether sources should be included in the source map or not. Pass `true` or `false` as strings into the URL. +* `dev`: build the bundle in development mode or not. Maps 1:1 to the `dev` setting of the bundles. Pass `true` or `false` as strings into the URL. Defaults to `true`. +* `platform`: platform requesting the bundle (e.g. `ios` or `android`). Defaults to the platform suffix of the requested file name, if any. Maps 1:1 to the `platform` setting of the bundles. +* `minify`: whether code should be minified or not. Maps 1:1 to the `minify` setting of the bundles. Pass `true` or `false` as strings into the URL. Defaults to `false`. +* `excludeSource`: whether to leave the source code out of the source map. Pass `true` or `false` as strings into the URL. Defaults to `false`. +* `inlineSourceMap`: whether to append the source map to the bundle as a base64 `data:` URL. Defaults to `false`. +* `lazy`: whether to leave modules loaded with `import()` out of the bundle, and load them on demand. See [`import()`](./ModuleAPI.md). Defaults to `false`. +* `modulesOnly`: whether to leave out Metro's runtime and polyfills, and serve only the modules. Defaults to `false`. +* `runModule`: whether to end the bundle with the statements that run the entry point. Defaults to `true`. +* `shallow`: whether to include only the entry module itself, without its dependencies. Defaults to `false`. +* `sourcePaths`: how source files are referenced in the source map: as absolute file paths (`absolute`), or as URL paths on the server (`url-server`). Defaults to `absolute`. +* `resolver.` and `transform.`: [custom resolver options](./Resolution.md#customresolveroptions-string-mixed) and custom transform options, e.g. `transform.foo=bar` passes `{foo: 'bar'}` to the transformer. For instance, requesting `http://localhost:8081/foo/bar/baz.bundle?dev=true&platform=ios` will create a bundle out of `foo/bar/baz.js` for iOS in development mode. @@ -214,7 +228,7 @@ The JavaScript transformer ([`babelTransformerPath`](./Configuration.md#babeltra ### Method `transform(module)` -Mandatory method that will transform code. The object received has information about the module being transformed (e.g its path, code...) and the returned object has to contain an `ast` key that is the AST representation of the transformed code. The default shipped transformer does the bare minimum amount of work by just parsing the code to AST: +Mandatory method that will transform code. The object received has the module's `filename` (relative to the project root), its source code `src`, transformation `options` (e.g. `dev`, `platform`, `minify`, `projectRoot`, `customTransformOptions`), and Babel `plugins` that Metro wants applied. The returned object has to contain an `ast` key that is the AST representation of the transformed code, and may contain the Babel `metadata`. A minimal transformer only parses the code to AST: ```js const babylon = require('@babel/parser'); @@ -231,13 +245,16 @@ If you would like to plug in Babel, you can simply do that by passing the code t ```js const {transformSync} = require('@babel/core'); -module.exports.transform = file => { - return transformSync(file.src, { +module.exports.transform = ({filename, src, plugins}) => { + return transformSync(src, { + ast: true, + filename, + plugins, // Babel options... }); }; ``` -### Method `getCacheKey()` +### Method `getCacheKey(options)` -Optional method that returns the cache key of the transformer. When using different transformers, this allows to correctly tie a transformed file to the transformer that converted it. The result of the method has to be a `string`. +Optional method that receives `{projectRoot, enableBabelRCLookup}` and returns the cache key of the transformer. When using different transformers, this allows to correctly tie a transformed file to the transformer that converted it. The result of the method has to be a `string`. diff --git a/docs/LocalDevelopment.md b/docs/LocalDevelopment.md index e5f51c19c1..86e065bb6d 100644 --- a/docs/LocalDevelopment.md +++ b/docs/LocalDevelopment.md @@ -9,7 +9,7 @@ This page includes tips for developers working on Metro itself, including how to When developing Metro, running your iterations against a local target project can be a great way to test the impact of your changes end-to-end. -Our recommended workflow is to use [`yarn link`][1] to register local `metro` packages within your development clone and then hot-switch to these versions in the consuming project. These instructions cover linking a local Metro clone with a bare workflow React Native app (i.e. having run `npx react-native init MetroTestApp`). +Our recommended workflow is to use [`yarn link`][1] to register local `metro` packages within your development clone and then hot-switch to these versions in the consuming project. These instructions cover linking a local Metro clone with a bare workflow React Native app (i.e. having run `npx @react-native-community/cli init MetroTestApp`). ```sh . @@ -39,7 +39,7 @@ Our recommended workflow is to use [`yarn link`][1] to register local `metro` pa 3. **Configure Metro `watchFolders` to work with our linked packages** - Because `yarn link` has included files outside of the immediate React Native project folder, we need to inform Metro that this set of files exists (as it will not automatically follow the symlinks). Add the following to your `metro.config.js`: + Because `yarn link` has included files outside of the immediate React Native project folder, we need to inform Metro that this set of files exists (Metro follows symlinks, but their targets must be within `watchFolders`). Add the following to your `metro.config.js`: ```diff + const path = require('path'); diff --git a/docs/ModuleAPI.md b/docs/ModuleAPI.md index e369c1f4b7..7ea68d90f7 100644 --- a/docs/ModuleAPI.md +++ b/docs/ModuleAPI.md @@ -16,7 +16,7 @@ const jsonData = require('./path/data.json'); const {View} = require('react-native'); ``` -The argument to `require()` must be a compile-time constant. The [`dynamicDepsInPackages`](./Configuration.md#dynamicdepsinpackages) config option controls whether calling `require()` with a non-constant argument will fail at build time or at runtime. +The argument to `require()` must be a compile-time constant. Calling `require()` with a non-constant argument fails at build time, except in files inside `node_modules`, where the [`dynamicDepsInPackages`](./Configuration.md#dynamicdepsinpackages) config option controls whether it fails at build time or at runtime. ### Advanced usage: `require` at runtime diff --git a/docs/PackageExports.md b/docs/PackageExports.md index 64131b922d..432ee326b6 100644 --- a/docs/PackageExports.md +++ b/docs/PackageExports.md @@ -19,9 +19,9 @@ Package Exports support has been enabled by default in Metro since [0.82](https: | Option | Description | | --- | --- | -| [`resolver.unstable_enablePackageExports`](/docs/configuration/#unstable_enablepackageexports-experimental) | Enable/disable Package Exports support. | -| [`resolver.unstable_conditionNames`](/docs/configuration/#unstable_conditionnames-experimental) | The set of condition names to assert when resolving conditional exports. | -| [`resolver.unstable_conditionsByPlatform`](/docs/configuration/#unstable_conditionsbyplatform-experimental) | The additional condition names to assert when resolving for a given platform target. | +| [`resolver.unstable_enablePackageExports`](./Configuration.md#unstable_enablepackageexports-experimental) | Enable/disable Package Exports support. | +| [`resolver.unstable_conditionNames`](./Configuration.md#unstable_conditionnames-experimental) | The set of condition names to assert when resolving conditional exports. | +| [`resolver.unstable_conditionsByPlatform`](./Configuration.md#unstable_conditionsbyplatform-experimental) | The additional condition names to assert when resolving for a given platform target. | ## How Metro applies `import`/`require` exports conditions @@ -87,9 +87,9 @@ If present in a `package.json` file, `"exports"` will be the first field consult - **Fallback**: If the requested subpath is not matched in `"exports"`, Metro will try to resolve it again, considering the above fields. Subpaths matched in `"exports"` (including via [subpath patterns](https://nodejs.org/docs/latest-v19.x/api/packages.html#subpath-patterns)) will use the exact target file path specified by a package. - - Metro will not expand [`sourceExts`](/docs/configuration/#sourceexts) against the import specifier. + - Metro will not expand [`sourceExts`](./Configuration.md#sourceexts) against the import specifier. - Metro will not resolve [platform-specific extensions](https://reactnative.dev/docs/platform-specific-code) against the target file. - - **Unchanged**: Metro will expand [asset densities](/docs/configuration#assetresolutions) (e.g. `icon.png` → `icon@2x.png`) if the target file [is an asset](/docs/configuration/#assetexts). + - **Unchanged**: Metro will expand [asset densities](./Configuration.md#assetresolutions) (e.g. `icon.png` → `icon@2x.png`) if the target file [is an asset](./Configuration.md#assetexts). #### Example @@ -141,9 +141,10 @@ In Node.js, it is an error to import package subpaths that aren't explicitly lis Instead of throwing an error, Metro will log a warning and fall back to file-based resolution. ```sh -warn: You have imported the module "foo/private/fn.js" which is not listed in -the "exports" of "foo". Consider updating your call site or asking the package -maintainer(s) to expose this API. +warn: Attempted to import the module "/path/to/node_modules/foo/private/fn.js" which +is not listed in the "exports" of "/path/to/node_modules/foo" under the requested +subpath "./private/fn.js". Falling back to file-based resolution. Consider updating +the call site or asking the package maintainer(s) to expose this API. ``` :::note @@ -240,7 +241,7 @@ We chose not to introduce `"android"` and `"ios"` conditions, due to the prevale const FooComponent = Platform.select({ android: require('./FooComponentAndroid.js'), - ios: require('FooComponentIOS.js'), + ios: require('./FooComponentIOS.js'), }); export default FooComponent; @@ -248,7 +249,7 @@ export default FooComponent; ### Asset files -As with source files, assets must be listed in `"exports"` to be imported without warnings. Asset files with [multiple densities](/docs/configuration#assetresolutions), e.g. `icon.png` and `icon@2x.png`, will continue to work without being listed individually. +As with source files, assets must be listed in `"exports"` to be imported without warnings. Asset files with [multiple densities](./Configuration.md#assetresolutions), e.g. `icon.png` and `icon@2x.png`, will continue to work without being listed individually. Using subpath patterns can be a convenient method to export many assets. We recommend specifying asset subpaths **with their file extension**. @@ -269,7 +270,7 @@ Some issues that can come from Metro resolving to a file not designed for React In these cases, **React Native developers** can: - Use `require()` for that dependency to try to get the CommonJS version. - - Disable `exports` support entirely by setting [`resolver.unstable_enablePackageExports`](/docs/configuration/#unstable_enablepackageexports-experimental) to `false` in your Metro config. + - Disable `exports` support entirely by setting [`resolver.unstable_enablePackageExports`](./Configuration.md#unstable_enablepackageexports-experimental) to `false` in your Metro config. - (Advanced) use a custom resolver to tweak the behaviour for specific packages, see below. #### Example: Custom resolver @@ -291,26 +292,25 @@ const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); */ const config = { resolver: { - resolveRequest: () => { - (context, moduleImport, platform) { - // Use the browser version of the package for React Native - if (moduleImport === 'some-pkg' || moduleImport.startsWith('some-pkg/')) { - return context.resolveRequest( - { - ...context, - unstable_conditionNames: ['browser'], - // Alternatively, disable exports for this package - // unstable_enablePackageExports: false - }, - moduleImport, - platform, - ); - } - - // Fall back to normal resolution for everything else. - return context.resolveRequest(context, moduleImport, platform); - } - } + resolveRequest: (context, moduleImport, platform) => { + // Use the browser version of the package for React Native + if (moduleImport === 'some-pkg' || moduleImport.startsWith('some-pkg/')) { + return context.resolveRequest( + { + ...context, + unstable_conditionNames: ['browser'], + // Alternatively, disable exports for this package + // unstable_enablePackageExports: false + }, + moduleImport, + platform, + ); + } + + // Fall back to normal resolution for everything else. + return context.resolveRequest(context, moduleImport, platform); + }, + }, }; module.exports = mergeConfig(getDefaultConfig(__dirname), config); diff --git a/docs/Resolution.md b/docs/Resolution.md index 4757f82a7f..955ee218fa 100644 --- a/docs/Resolution.md +++ b/docs/Resolution.md @@ -65,19 +65,23 @@ Parameters: (*context*, *moduleName*, *platform*) 1. Return the result of the custom resolver. 2. If *moduleName* is an absolute path, or equal to `'.'` or `'..'`, or begins `'./'` or `'../'` 1. Let *absoluteModuleName* be *moduleName* if it is an absolute path, otherwise the result of prepending the current directory (i.e. parent of [`context.originModulePath`](#originmodulepath-string)) with *moduleName*. - 2. Return the result of [**RESOLVE_MODULE**](#resolve_module)(*context*, *absoluteModuleName*, *platform*), or continue. -3. If *moduleName* begins `'#'` - 1. Throw an error. This will be replaced with subpath imports support in a non-breaking future release. + 2. Return the result of [**RESOLVE_MODULE**](#resolve_module)(*context*, *absoluteModuleName*, *platform*), or throw a resolution failure error. +3. If *moduleName* begins `'#'` (a [subpath import](https://nodejs.org/api/packages.html#subpath-imports)), then + 1. Find the closest `package.json` to [`context.originModulePath`](#originmodulepath-string). If none is found, or it has no `"imports"` field, throw an error. + 2. Resolve *moduleName* against the `"imports"` field, asserting the same condition names as [**RESOLVE_PACKAGE_EXPORTS**](#resolve_package_exports). If resolved, return the result. + 3. Otherwise, log a warning and continue from step 5. 4. If *moduleName* parses as a URL, let *scheme* be the lowercased scheme (the prefix before `':'`), then 1. If [`context.schemeResolvers`](#schemeresolvers-readonlyscheme-string-customresolver) has a resolver registered for *scheme*, return the result of calling it with (*context*, *moduleName*, *platform*), where *context.resolveRequest* is set to the default resolver for chaining. 2. Otherwise, continue to the following steps, but if none of them resolve *moduleName*, throw a scheme-specific error at step 11 rather than a generic resolution failure. (This fallback exists for backwards compatibility with projects using scheme-like specifiers via Haste or [`extraNodeModules`](#extranodemodules-string-string), and is deprecated.) -5. Apply [**BROWSER_SPEC_REDIRECTION**](#browser_spec_redirection) to *moduleName*. If this is `false`: - 1. Return the empty module. +5. Apply [**BROWSER_SPEC_REDIRECTION**](#browser_spec_redirection) to *moduleName*. + 1. If this is `false`, return the empty module. + 2. If the result is a relative path, return the result of [**RESOLVE_MODULE**](#resolve_module) for it relative to the directory of the `package.json` that provided the redirection, or throw a resolution failure error. + 3. If the result is an absolute path, throw an `InvalidPackageConfigurationError`. 6. If [Haste resolutions are allowed](#allowhaste-boolean), then 1. Get the result of [**RESOLVE_HASTE**](#resolve_haste)(*context*, *moduleName*, *platform*). 2. If resolved as a Haste package path, then - 1. Perform the algorithm for resolving a path (step 2 above). Throw an error if this resolution fails. - For example, if the Haste package path for `'a/b'` is `foo/package.json`, perform step 2 as if _moduleName_ was `foo/c`. + 1. Return the result of [**RESOLVE_PACKAGE**](#resolve_package)(*context*, *packageDir* + *relativePath*, *platform*). Throw an error if this resolution fails. + For example, if the Haste package path for `'@a/b'` is `foo/package.json`, resolve `'@a/b/c'` as `foo/c`. 7. If [`context.unstable_enablePackageExports`](./Configuration.md#unstable_enablepackageexports-experimental) is enabled, then 1. Get the result of [**PACKAGE_SELF_RESOLVE**](#package_self_resolve)(*context*, *moduleName*, *platform*). 2. If resolved, return result. @@ -99,9 +103,11 @@ Parameters: (*context*, *moduleName*, *platform*) Parameters: (*context*, *moduleName*, *platform*) 1. Let *filePath* be the result of applying [**BROWSER_SPEC_REDIRECTION**](#browser_spec_redirection) to *moduleName*. This may locate a replacement subpath from a containing `package.json` file based on the [`browser` field spec](https://github.com/defunctzombie/package-browser-field-spec). -2. Return the result of [**RESOLVE_FILE**](#resolve_file)(*context*, *filePath*, *platform*), or continue. -3. Otherwise, let *dirPath* be the directory path of *filePath*. -4. If a file *dirPath* + `'package.json'` exists, resolve based on the [`browser` field spec](https://github.com/defunctzombie/package-browser-field-spec): + 1. If this is `false`, return the empty module. +2. Unless *filePath* ends with a path separator, return the result of [**RESOLVE_FILE**](#resolve_file)(*context*, *filePath*, *platform*), or continue. +3. Otherwise, let *dirPath* be *filePath*. If *dirPath* is not an existing directory, throw an error. +4. If a file *dirPath* + `'/package.json'` does not exist, return the result of [**RESOLVE_FILE**](#resolve_file)(*context*, *dirPath* + `'/index'`, *platform*), or throw an error. +5. Otherwise, resolve based on the [`browser` field spec](https://github.com/defunctzombie/package-browser-field-spec): 1. Let *mainModulePath* be the result of reading the package's entry path using [`context.mainFields`](#mainfields-readonlyarraystring). 2. Return the result of [**RESOLVE_FILE**](#resolve_file)(*context*, *mainModulePath*, *platform*), or continue. 3. Return the result of [**RESOLVE_FILE**](#resolve_file)(*context*, *mainModulePath* + `'/index'`, *platform*). @@ -132,13 +138,13 @@ Parameters: (*context*, *moduleName*, *platform*) Parameters: (*context*, *packagePath*, *filePath*, *exportsField*, *platform*) -> Resolves a package subpath based on the [Package Entry Points spec](https://nodejs.org/docs/latest-v19.x/api/packages.html#package-entry-points) (the `"exports"` field), when [`resolver.unstable_enablePackageExports`](./configuration#unstable_enablepackageexports-experimental) is enabled. +> Resolves a package subpath based on the [Package Entry Points spec](https://nodejs.org/docs/latest-v19.x/api/packages.html#package-entry-points) (the `"exports"` field), when [`resolver.unstable_enablePackageExports`](./Configuration.md#unstable_enablepackageexports-experimental) is enabled. 1. Let *subpath* be the relative path from *packagePath* to *filePath*, or `'.'`. 2. If *exportsField* contains an invalid configuration or values, raise an `InvalidPackageConfigurationError`. 3. If *subpath* is not defined by *exportsField*, raise a `PackagePathNotExportedError`. 4. Let *target* be the result of matching *subpath* in *exportsField* after applying any [conditional exports](https://nodejs.org/docs/latest-v19.x/api/packages.html#conditional-exports) and/or substituting a [subpath pattern match](https://nodejs.org/docs/latest-v19.x/api/packages.html#subpath-patterns). - 1. Condition names will be asserted from the union of `'default'`, `'import'` OR `'require'` according to `context.isESMImport`, `context.unstable_conditionNames` and `context.unstable_conditionNamesByPlatform` for *platform*, in the order defined by *exportsField*. + 1. Condition names will be asserted from the union of `'default'`, `'import'` OR `'require'` according to `context.isESMImport`, [`context.unstable_conditionNames`](#unstable_conditionnames-readonlyarraystring) and [`context.unstable_conditionsByPlatform`](#unstable_conditionsbyplatform-platform-string-readonlyarraystring) for *platform*, in the order defined by *exportsField*. 5. If *target* refers to an [asset](#assetexts-readonlysetstring), then 1. Return the result of [**RESOLVE_ASSET**](#resolve_asset)(*context*, *target*, *platform*). 6. Return *target* as a [source file resolution](#source-file) **without** applying redirections or trying any platform or extension variants. @@ -149,7 +155,8 @@ Parameters: (*context*, *filePath*, *platform*) 1. If the path refers to an [asset](#assetexts-readonlysetstring), then 1. Return the result of [**RESOLVE_ASSET**](#resolve_asset)(*context*, *filePath*, *platform*). -2. Otherwise, if the path [exists](#doesfileexist-string--boolean-deprecated), then +2. Otherwise, if the path [exists](#doesfileexist-string--boolean-deprecated) as a file, return a [source file resolution](#source-file) for it. +3. Otherwise: 1. Try all platform and extension variants in sequence. Return a [source file resolution](#source-file) for the first one that [exists](#doesfileexist-string--boolean-deprecated) after applying [**BROWSER_SPEC_REDIRECTION**](#browser_spec_redirection). For example, if _platform_ is `android` and [`context.sourceExts`](#sourceexts-readonlyarraystring) is `['js', 'jsx']`, try this sequence of potential file names: 1. _moduleName_ + `'.android.js'` 2. _moduleName_ + `'.native.js'` (if [`context.preferNativePlatform`](#prefernativeplatform-boolean) is `true`) @@ -169,14 +176,11 @@ Parameters: (*context*, *filePath*, *platform*) Parameters: (*context*, *moduleName*, *platform*) -1. Try resolving _moduleName_ as a [Haste module](#resolvehastemodule-string--string). +1. If _moduleName_ contains no `'/'`, try resolving it as a [Haste module](#resolvehastemodule-string--string). If found, then 1. Return result as a [source file resolution](#source-file) **without** applying redirections or trying any platform or extension variants. -2. Try resolving _moduleName_ as a [Haste (global) package](#resolvehastepackage-string--string), or a path *relative* to a Haste package. - For example, if _moduleName_ is `'a/b/c'`, try the following potential Haste package names: - 1. `'a/b/c'`, relative path `''` - 2. `'a/b'`, relative path `'./c'` - 3. `'a'`, with relative path `'./b/c'` +2. Split _moduleName_ into a package name (including an optional [scope](https://docs.npmjs.com/cli/v8/using-npm/scope)) and relative path, and try resolving the package name as a [Haste (global) package](#resolvehastepackage-string--string). + For example, `'a/b/c'` tries package `'a'` with relative path `'./b/c'`, and `'@a/b/c'` tries package `'@a/b'` with relative path `'./c'`. #### BROWSER_SPEC_REDIRECTION @@ -190,9 +194,9 @@ Based on [`defunctzombie/package-browser-field-spec`](https://github.com/defunct 1. If *moduleName* begins `'.'`, consider it relative to [`context.originModulePath`](#originmodulepath-string) and let *subpath* be the same path relative to *packageScope*, prefixed `'./'`. 2. Else if *moduleName* is absolute, let *subpath* be *moduleName* relative to *packageScope*, prefixed `'./'`. 3. Else let *subpath* be *moduleName*. -4. Taking each of [`context.mainFields`](#mainfields-readonlyarraystring) as a key, let *mainFieldValue* be the value at that key within the `package.json` at *packageScope*. - 1. If *mainFieldValue* is an object, let *expandedSubPath* range over *subpath*, *subpath* + `'.js'` and *subpath* + `'.json'`. - 1. If *mainFieldValue* has the key *expandedSubpath*, let *redirectedPath* be its value, else continue. +4. Merge the object values of each of [`context.mainFields`](#mainfields-readonlyarraystring) in the `package.json` at *packageScope* into a single map *mainFieldsMap*, with earlier fields taking precedence. Let *expandedSubpath* range over *subpath*, *subpath* + `'.js'` and *subpath* + `'.json'`. + 1. For each *expandedSubpath*: + 1. If *mainFieldsMap* has the key *expandedSubpath*, let *redirectedPath* be its value, else continue. 2. If *moduleName* is absolute or begins `'.'`, and *redirectedPath* is a string: 1. If *redirectedPath* is an absolute path, return *redirectedPath*. 2. Return *packageScope* joined with *redirectedPath*. @@ -219,7 +223,7 @@ The default implementation wraps [`fileSystemLookup`](#filesystemlookup-string-- *Added in v0.81.0* -Return information about the given absolute or project-relative path, following symlinks. A file "exists" if and only if it is watched, and a directory must be non-empty. The returned `realPath` is real and absolute. +Return information about the given absolute or project-relative path, following symlinks. A file "exists" if and only if it is within the [project root or watch folders](./Configuration.md#watchfolders), and a directory must be non-empty. The returned `realPath` is real and absolute. By default, Metro implements this by consulting an in-memory map of the filesystem that has been prepared in advance. This approach avoids disk I/O during module resolution and performs realpath resolution at negligible additional cost. @@ -241,7 +245,7 @@ Metro's default resolver does not call this function, instead using the [**BROWS #### `resolveAsset: (dirPath: string, assetName: string, extension: string) => ?$ReadOnlyArray` -Given a directory path, the base asset name and an extension, returns a list of all the asset file names that match the given base name in that directory, or `null` if no such files are found. The default implementation considers each of [`resolver.assetResolutions`](./Configuration.md#assetresolutions) and uses the `${assetName}@${resolution}${extension}` format for asset variant file names. +Given a directory path, the base asset name and an extension, returns a list of all the asset file names that match the given base name in that directory, or `null` if no such files are found. The default implementation considers `${assetName}${extension}` and, for each of [`resolver.assetResolutions`](./Configuration.md#assetresolutions), `${assetName}@${resolution}x${extension}`. See also [Static Image Resources](https://reactnative.dev/docs/images#static-image-resources) in the React Native docs. @@ -253,11 +257,11 @@ The list of file extensions to try, in order, when resolving a module path that The ordered list of fields in `package.json` that should be read to resolve a package's main entry point (and any subpath file replacements) per the ["browser" field spec](https://github.com/defunctzombie/package-browser-field-spec). Defaults to [`resolver.resolverMainFields`](./Configuration.md#resolvermainfields). -#### `getPackage: string => PackageJson` +#### `getPackage: string => ?PackageJson` -Given the path to a `package.json` file, returns the parsed file contents. +Given the path to a `package.json` file, returns the parsed file contents, or `null` if the file does not exist or is malformed. -#### `getPackageForModule: (absoluteModulePath: string) => ?PackageInfo`
Deprecated
+#### `getPackageForModule: (absoluteModulePath: string) => ?PackageForModule`
Deprecated
Given a candidate absolute module path that may exist under a package, locates and returns the closest package root (working upwards from the given path, stopping at the nearest `node_modules`), parsed `package.json` contents, and the package-relative path of the given path. @@ -328,12 +332,24 @@ Inside a custom resolver, `resolveRequest` is set to the default resolver functi #### `schemeResolvers: Readonly<{[scheme: string]: CustomResolver}>` -An object of [custom resolvers](#resolverequest-customresolver) for import specifiers prefixed with a URI scheme, keyed by lowercased scheme name (the part before the first `':'`, without the colon). The scheme parsed from a specifier is lowercased before lookup, so keys must be lowercase (both `Foo:` and `foo:` match the `'foo'` key). Defaults to [`resolver.schemeResolvers`](./Configuration.md#schemeresolvers). +An object of [custom resolvers](#resolverequest-customresolver) for import specifiers prefixed with a URI scheme, keyed by lowercased scheme name (the part before the first `':'`, without the colon). The scheme parsed from a specifier is lowercased before lookup, so keys must be lowercase (both `Foo:` and `foo:` match the `'foo'` key). Defaults to Metro's built-in scheme resolvers (currently `metro:`) merged with [`resolver.schemeResolvers`](./Configuration.md#schemeresolvers), which take precedence. When the default resolver encounters a specifier whose scheme matches a key — for example `my-scheme:foo` matching `'my-scheme'` — it invokes the corresponding resolver with the full specifier. The resolver is passed a `context` whose [`resolveRequest`](#resolverequest-customresolver) is the default resolver, so it can chain back into default resolution (e.g. to resolve a relative path). Relative (`./`, `../`) and subpath (`#…`) imports are handled before scheme dispatch and are never treated as schemes. See [**RESOLVE**](#resolve) step 4 for how scheme dispatch fits into the algorithm, and [`resolver.schemeResolvers`](./Configuration.md#schemeresolvers) for precedence relative to [`resolveRequest`](#resolverequest-customresolver). +#### `unstable_conditionNames: $ReadOnlyArray` + +Additional condition names asserted when resolving the `"exports"` and `"imports"` fields. Defaults to [`resolver.unstable_conditionNames`](./Configuration.md#unstable_conditionnames-experimental). + +#### `unstable_conditionsByPlatform: {[platform: string]: $ReadOnlyArray}` + +Additional condition names asserted for a given platform when resolving the `"exports"` and `"imports"` fields. Defaults to [`resolver.unstable_conditionsByPlatform`](./Configuration.md#unstable_conditionsbyplatform-experimental). + +#### `unstable_enablePackageExports: boolean` + +Whether the `"exports"` field is used for resolution. Defaults to [`resolver.unstable_enablePackageExports`](./Configuration.md#unstable_enablepackageexports-experimental). + #### `dependency: ?Dependency` A dependency descriptor corresponding to the current resolution request. This is provided for diagnostic purposes *only* and may not be used for semantic purposes. See the [Caching](#caching) section for more information. @@ -352,7 +368,7 @@ type Dependency = { // this dependency was encountered. This may be an empty array. locs: $ReadOnlyArray, - asyncType: 'async' | 'prefetch' | 'weak' | null, + asyncType: 'async' | 'maybeSync' | 'prefetch' | 'weak' | null, // Other properties are considered internal and may change in the future. ... @@ -367,6 +383,8 @@ Resolver results may be cached under the following conditions: 1. For given origin module paths _A_ and _B_ and target module name _M_, the resolution for _M_ may be reused if **all** of the following conditions hold: 1. _A_ and _B_ are in the same directory. 2. The contents of [`dev`](#dev-boolean) and [`customResolverOptions`](#customresolveroptions-string-mixed) are equivalent ( = serialize to JSON the same) in both calls to the resolver. + 3. The `platform` is the same in both calls. + 4. [`isESMImport`](#isesmimport-boolean) is the same in both calls. 2. Any cache of resolutions must be invalidated if any file in the project has changed. Custom resolvers must adhere to these assumptions, e.g. they may not return different resolutions for origin modules in the same directory under the same `customResolverOptions`. diff --git a/docs/SourceMapFormat.md b/docs/SourceMapFormat.md index 6172301bea..09b0c90550 100644 --- a/docs/SourceMapFormat.md +++ b/docs/SourceMapFormat.md @@ -51,7 +51,7 @@ The presence of a 3-tuple `(column, nameIndex, line)` means that the _local func The value of the `mappings` field is described by the _Mappings_ production of the grammar detailed below. 1. `Mappings = [ ";" ] LineMappings { ";" { ";" } LineMappings }` -2. `LineMappings = FirstColumnMapping "," ColumnMapping` +2. `LineMappings = FirstColumnMapping { "," ColumnMapping }` 3. `FirstColumnMapping = VLQ VLQ VLQ` 4. `ColumnMapping = VLQ VLQ [ VLQ ]` 5. `VLQ =` _A single Base64-encoded variable-length quantity, as defined in the [source map specification](https://tc39.es/ecma426/#sec-base64-vlq)_. diff --git a/packages/metro-config/src/defaults/getMaxWorkers.js b/packages/metro-config/src/defaults/getMaxWorkers.js index 03c5598a1b..09ae26634f 100644 --- a/packages/metro-config/src/defaults/getMaxWorkers.js +++ b/packages/metro-config/src/defaults/getMaxWorkers.js @@ -11,6 +11,13 @@ import os from 'node:os'; +/** + * Returns the number of transform workers to use. + * + * An integer `workers` is clamped to [1, available cores]. Otherwise, the + * default is about `cores - 1` on small machines, approaching half the cores + * on large ones (e.g. 2 -> 1, 8 -> 6, 64 -> 32), and at least 1. + */ export default function getMaxWorkers(workers: ?number): number { const cores = os.availableParallelism(); return typeof workers === 'number' && Number.isInteger(workers)