Skip to content

Fix crash on a non-object exports field in package.json - #2187

Merged
robhogan merged 1 commit into
react:mainfrom
kwy404:fix/resolver-non-object-exports-field
Nov 6, 2026
Merged

robhogan merged 1 commit into
react:mainfrom
kwy404:fix/resolver-non-object-exports-field

Conversation

@kwy404

@kwy404 kwy404 commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Importing a package whose package.json has a non-object exports value, such as false, true or a number, fails the whole resolution with a raw TypeError: Invalid value used as weak map key. It happens with the default config, since unstable_enablePackageExports is on by default. Node.js ignores these values and resolves the same package through main.

{"name": "pkg", "main": "m.js", "exports": false}

normalizeExportsField only special cased strings, which can't be WeakMap keys, before caching its result. Booleans and numbers can't be keys either, but Object.keys() gives an empty list for them, so they reach _normalizedExportsFields.set(), which throws. Because that is a plain TypeError and not an InvalidPackageConfigurationError, the callers in resolve.js rethrow it instead of falling back.

This raises a config error for these values, so the existing handling warns and falls back to file-based resolution, which finds the same file as Node.js. Metro logs a warning where Node.js is silent, as it does for other invalid exports values.

Part of #2166 (the "exports": false row).

Changelog: [Fix] Fall back to file-based resolution, instead of throwing a TypeError, when a package's exports field is not a string, array or object

Test plan

Added a test to package-exports-test.js for false, true and a number. Before this change all three cases failed with TypeError: Invalid value used as weak map key. With the change they resolve to the main file and log one warning.

yarn jest packages/metro-resolver
Test Suites: 10 passed, 10 total
Tests:       327 passed, 327 total

Node.js 24 resolves main from both require.resolve and import.meta.resolve for false, true, 0 and 1.

eslint, prettier --check and flow focus-check pass on the changed files.

Resolving a package whose package.json has a non-object "exports" value
such as false, true or a number failed the whole resolution with
"TypeError: Invalid value used as weak map key".

normalizeExportsField only special cased strings, which are not weakly
referenceable, before caching its result in a WeakMap. Booleans and
numbers aren't either, but they pass through Object.keys() as an empty
key list, so they reach the WeakMap set() call, which throws. This is a
plain TypeError rather than an InvalidPackageConfigurationError, so the
callers in resolve.js rethrow it instead of falling back.

Node.js ignores these values and uses "main". Raise a config error for
them, so that the existing handling warns and falls back to file-based
resolution, which resolves to the same file as Node.js.
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown

Metro review: Looks good

Risk: low · Commit: 8fc741f · 4 commands run, in a restricted sandbox after an automated safety assessment

When a package's exports field is a boolean or a number, normalizeExportsField now throws the existing InvalidPackageConfigurationError. Before, it reached WeakMap.set and crashed with a plain TypeError. All three callers in resolve.js (package root/subpath, self-reference, and the shared resolvePackage path) already catch that error, log a warning and fall back, and null/undefined are filtered out before the call, so the guard covers every value that reaches it. The new tests fail on the base with the reported TypeError and pass on the head. Nothing needs changing.

No findings.

Verified
  • ✅ yarn jest --no-watchman packages/metro-resolver (head) — 10 suites, 327 tests passed, matching the PR's test plan.
  • ✅ yarn jest --no-watchman packages/metro-resolver/src/__tests__/package-exports-test.js (base, with the PR's test file copied in) (failed as expected) — All three new cases (false, true, 1) fail with TypeError: Invalid value used as weak map key. The base test file was restored afterwards.
  • ✅ yarn eslint packages/metro-resolver/src/PackageExportsResolve.js packages/metro-resolver/src/__tests__/package-exports-test.js
  • ✅ yarn flow check — No errors.

Not run: I didn't run the PR's claim that Node.js 24 resolves main for exports values of false, true, 0 and 1: the session's permission policy blocked my Node command because it used a shell variable. I didn't benchmark: the change adds one typeof check that only rejects invalid values, and it runs before the existing cache lookup, so it can't plausibly change resolver cost. I didn't run Windows: the logic doesn't depend on the platform.

Automated review by Claude. Advisory only: it never approves or blocks, and a maintainer makes the call.

@robhogan
robhogan merged commit 3030eda into react:main Nov 6, 2026
16 checks passed
robhogan added a commit that referenced this pull request Nov 6, 2026
Summary:
Publish Metro 0.88.0 from `main`.

This is a draft until we choose the cut. These breaking PRs are still open, and can land first or wait for the next minor:

 - #2186 (`globalPrefix` for all module system globals)
 - #2158 (`/symbolicate` stops reading `req.rawBody`)
 - #2157 (remove `functionMap` from the Babel transformer result)
 - #1990 (keep the file extension in async bundle paths, approved and held for 0.88)
 - #1985 and #2156 (drafts, decoded source maps)

Approved non-breaking PRs that could also make it: #2185, #2040, #2034 and #2150 (which turns a missing watch folder into an immediate startup error, rather than a 240s hang). I'll rebase this and extend the notes as they land.

Changelog: [Internal]

Test plan:
```
node scripts/updateVersion.js 0.88.0
sl diff --stat
```

Only the 17 `packages/*/package.json` manifests change, and every `0.87.3` reference is replaced.

Notable breakage in third parties, for the notes or a heads-up before React Native adopts this:

 - `@rnx-kit/metro-config` 2.2.6 adds `path.dirname(resolver.emptyModulePath)` as a watch folder when there are none, which is now `metro:runtime/modules`, so the dev server never serves a bundle for a single-package app.
 - `@tamagui/metro-plugin` 3.x refuses `metro-resolver` outside 0.84-0.87, and then imports the removed `metro-resolver/private/createDefaultContext`.
 - `@rnx-kit/metro-resolver-symlinks` calls the removed `context.redirectModulePath`.

End to end (AI-driven), with the packed tarballs against 0.87.3 from npm, in a React Native 0.89.0-rc.0 template app with a few popular dependencies: dev and release bundles, source maps and assets for iOS and Android are byte-identical, except where #2174 changes resolution. `rxjs/operators` now resolves to the same ESM build as `rxjs`, dropping 190 duplicate modules, and `require('nanoid/non-secure')` gets nanoid's CJS build. Release builds reach the first screen on a Galaxy A16 and the iOS simulator (built before #2184 landed, which leaves the prelude and module system byte-identical). The dev server passes the same checks on both, with and without Watchman (edits, added and deleted files, HMR, `/symbolicate`, assets and lazy `import()`), and Fast Refresh works on the simulator.

Draft release notes (from the 14 commits in v0.87.3...main - internal-only changes omitted):

```
 - **[Breaking]**: Resolver: Read `"exports"` from the package root, and apply `"browser"` redirections to the file it resolves to, by default. Set `resolver.unstable_resolveExportsFromPackageRoot` and `resolver.unstable_applyBrowserSpecAfterExports` to `false` for the previous behavior (#2174 by @robhogan)
 - **[Breaking]**: Default `transformer.asyncRequireModulePath` and `resolver.emptyModulePath` to `metro:runtime/...`. A custom `resolver.resolveRequest` must delegate `metro:` specifiers it doesn't handle to `context.resolveRequest`, and code that reads either option as a file path must resolve it instead (#2180 by @robhogan)
 - **[Breaking]**: Resolve the module system via `metro:runtime/polyfills/require`. A custom `resolver.resolveRequest` must delegate `metro:` specifiers it doesn't handle to `context.resolveRequest` (#2184 by @robhogan)
 - **[Breaking]**: Remove the deprecated `redirectModulePath` from `ResolutionContext`. A custom resolver should drop any call to it and delegate to `context.resolveRequest` or `resolve`, which already apply `"browser"` redirections and return `{type: 'empty'}` for `false` (#2181 by @robhogan)
 - **[Breaking]**: Remove the deprecated `doesFileExist` from `ResolutionContext`, and the `DoesFileExist` type from `metro-resolver` (use `fileSystemLookup`) (#2181 by @robhogan)
 - **[Breaking]**: `ResolutionContext.isESMImport` is required (#2182 by @robhogan)
 - **[Breaking]**: Source and JSON files containing the reserved string `_$$_METRO_`, including in comments and string literals, now fail to transform (#2155 by @robhogan)
 - **[Breaking]**: Remove the deprecated, always-`true` `hot` option from `getTransformOptions` (#2159 by @robhogan)
 - **[Feature]**: Custom serializers receive the reserved string as `options.reservedString` (#2155 by @robhogan)
 - **[Fix]**: Fall back to file-based resolution, instead of throwing a `TypeError`, when a package's `"exports"` field is not a string, array or object (#2187 by @kwy404)
 - **[Fix]**: Fix relative symlinks on a different Windows drive than the project root (#2161 by @ramya-2196)
 - **[Performance]**: ~5-10% faster resolution (#2181 by @robhogan)

> NOTE: Experimental features are not covered by semver and can change at any time.

 - **[Experimental]**: Remove `unstable_incrementalResolution` from `ResolutionContext`, and `metro-resolver/private/createDefaultContext` (#2181 by @robhogan)

 **Full Changelog**: v0.87.3...v0.88.0
```
robhogan added a commit that referenced this pull request Nov 6, 2026
Summary:
Publish Metro 0.88.0 from `main`.

This is a draft until we choose the cut. These breaking PRs are still open, and can land first or wait for the next minor:

 - #2158 (`/symbolicate` stops reading `req.rawBody`)
 - #2157 (remove `functionMap` from the Babel transformer result)
 - #1990 (keep the file extension in async bundle paths, approved and held for 0.88)
 - #1985 and #2156 (drafts, decoded source maps)

Approved non-breaking PRs that could also make it: #2185, #2040, #2034 and #2150 (which turns a missing watch folder into an immediate startup error, rather than a 240s hang). I'll rebase this and extend the notes as they land.

Changelog: [Internal]

Test plan:
```
node scripts/updateVersion.js 0.88.0
sl diff --stat
```

Only the 17 `packages/*/package.json` manifests change, and every `0.87.3` reference is replaced.

Notable breakage in third parties, for the notes or a heads-up before React Native adopts this:

 - `@rnx-kit/metro-config` 2.2.6 adds `path.dirname(resolver.emptyModulePath)` as a watch folder when there are none, which is now `metro:runtime/modules`, so the dev server never serves a bundle for a single-package app.
 - `@tamagui/metro-plugin` 3.x refuses `metro-resolver` outside 0.84-0.87, and then imports the removed `metro-resolver/private/createDefaultContext`.
 - `@rnx-kit/metro-resolver-symlinks` calls the removed `context.redirectModulePath`.

End to end (AI-driven), with the packed tarballs against 0.87.3 from npm, in a React Native 0.89.0-rc.0 template app with a few popular dependencies: dev and release bundles, source maps and assets for iOS and Android are byte-identical, except where #2174 changes resolution. `rxjs/operators` now resolves to the same ESM build as `rxjs`, dropping 190 duplicate modules, and `require('nanoid/non-secure')` gets nanoid's CJS build. With #2186, only the module system and `asyncRequire` differ besides those. Release builds reach the first screen on a Galaxy A16 and the iOS simulator. The dev server passes the same checks on both, with and without Watchman (edits, added and deleted files, HMR, `/symbolicate`, assets and lazy `import()`), and Fast Refresh works on the simulator.

Draft release notes (from the 15 commits in v0.87.3...main - internal-only changes omitted):

```
 - **[Breaking]**: Resolver: Read `"exports"` from the package root, and apply `"browser"` redirections to the file it resolves to, by default. Set `resolver.unstable_resolveExportsFromPackageRoot` and `resolver.unstable_applyBrowserSpecAfterExports` to `false` for the previous behavior (#2174 by @robhogan)
 - **[Breaking]**: Default `transformer.asyncRequireModulePath` and `resolver.emptyModulePath` to `metro:runtime/...`. A custom `resolver.resolveRequest` must delegate `metro:` specifiers it doesn't handle to `context.resolveRequest`, and code that reads either option as a file path must resolve it instead (#2180 by @robhogan)
 - **[Breaking]**: Resolve the module system via `metro:runtime/polyfills/require`. A custom `resolver.resolveRequest` must delegate `metro:` specifiers it doesn't handle to `context.resolveRequest` (#2184 by @robhogan)
 - **[Breaking]**: `transformer.globalPrefix` now also prefixes `__r`, `__c`, `__registerSegment` and `__accept`, and the default run statement. Code that calls these by name must apply the prefix (#2186 by @robhogan)
 - **[Breaking]**: Remove the deprecated `redirectModulePath` from `ResolutionContext`. A custom resolver should drop any call to it and delegate to `context.resolveRequest` or `resolve`, which already apply `"browser"` redirections and return `{type: 'empty'}` for `false` (#2181 by @robhogan)
 - **[Breaking]**: Remove the deprecated `doesFileExist` from `ResolutionContext`, and the `DoesFileExist` type from `metro-resolver` (use `fileSystemLookup`) (#2181 by @robhogan)
 - **[Breaking]**: `ResolutionContext.isESMImport` is required (#2182 by @robhogan)
 - **[Breaking]**: Source and JSON files containing the reserved string `_$$_METRO_`, including in comments and string literals, now fail to transform (#2155 by @robhogan)
 - **[Breaking]**: Remove the deprecated, always-`true` `hot` option from `getTransformOptions` (#2159 by @robhogan)
 - **[Feature]**: Custom serializers receive the reserved string as `options.reservedString` (#2155 by @robhogan)
 - **[Fix]**: Fall back to file-based resolution, instead of throwing a `TypeError`, when a package's `"exports"` field is not a string, array or object (#2187 by @kwy404)
 - **[Fix]**: Fix relative symlinks on a different Windows drive than the project root (#2161 by @ramya-2196)
 - **[Performance]**: ~5-10% faster resolution (#2181 by @robhogan)

> NOTE: Experimental features are not covered by semver and can change at any time.

 - **[Experimental]**: Remove `unstable_incrementalResolution` from `ResolutionContext`, and `metro-resolver/private/createDefaultContext` (#2181 by @robhogan)

 **Full Changelog**: v0.87.3...v0.88.0
```
robhogan added a commit that referenced this pull request Nov 6, 2026
Summary:
Publish Metro 0.88.0 from `main`.

Changelog: [Internal]

Test plan:
```
node scripts/updateVersion.js 0.88.0
sl diff --stat
```

Only the 17 `packages/*/package.json` manifests change, and every `0.87.3` reference is replaced. Flow, ESLint, Prettier, TypeScript, API snapshots, Markdown lint and Jest pass on this commit.

Notable breakage in third parties, for the notes or a heads-up before React Native adopts this:

 - `@rnx-kit/metro-config` 2.2.6 adds `path.dirname(resolver.emptyModulePath)` as a watch folder when there are none, which is now `metro:runtime/modules`, so the dev server never serves a bundle for a single-package app.
 - `@tamagui/metro-plugin` 3.x refuses `metro-resolver` outside 0.84-0.87, and then imports the removed `metro-resolver/private/createDefaultContext`.
 - `@rnx-kit/metro-resolver-symlinks` calls the removed `context.redirectModulePath`.

End to end (AI-driven), with the packed tarballs against 0.87.3 from npm, in a React Native 0.89.0-rc.0 template app with a few popular dependencies: dev and release bundles, source maps and assets for iOS and Android are byte-identical, except where #2174 changes resolution. `rxjs/operators` now resolves to the same ESM build as `rxjs`, dropping 190 duplicate modules, and `require('nanoid/non-secure')` gets nanoid's CJS build. With #2186, only the module system and `asyncRequire` differ besides those. Release builds reach the first screen on a Galaxy A16 and the iOS simulator. The dev server passes the same checks on both, with and without Watchman (edits, added and deleted files, HMR, `/symbolicate`, assets and lazy `import()`), and Fast Refresh works on the simulator.

The end-to-end checks above were on 9de42bbed3b2, before #2170, #2157, #2158 and #1990 landed. I'm re-running them now and will update this.

Draft release notes (from the 19 commits in v0.87.3...main - internal-only changes omitted):

```
 - **[Breaking]**: Resolver: Read `"exports"` from the package root, and apply `"browser"` redirections to the file it resolves to, by default. Set `resolver.unstable_resolveExportsFromPackageRoot` and `resolver.unstable_applyBrowserSpecAfterExports` to `false` for the previous behavior (#2174 by @robhogan)
 - **[Breaking]**: Default `transformer.asyncRequireModulePath` and `resolver.emptyModulePath` to `metro:runtime/...`. A custom `resolver.resolveRequest` must delegate `metro:` specifiers it doesn't handle to `context.resolveRequest`, and code that reads either option as a file path must resolve it instead (#2180 by @robhogan)
 - **[Breaking]**: Resolve the module system via `metro:runtime/polyfills/require`. A custom `resolver.resolveRequest` must delegate `metro:` specifiers it doesn't handle to `context.resolveRequest` (#2184 by @robhogan)
 - **[Breaking]**: `transformer.globalPrefix` now also prefixes `__r`, `__c`, `__registerSegment` and `__accept`, and the default run statement. Code that calls these by name must apply the prefix (#2186 by @robhogan)
 - **[Breaking]**: Remove the deprecated `redirectModulePath` from `ResolutionContext`. A custom resolver should drop any call to it and delegate to `context.resolveRequest` or `resolve`, which already apply `"browser"` redirections and return `{type: 'empty'}` for `false` (#2181 by @robhogan)
 - **[Breaking]**: Remove the deprecated `doesFileExist` from `ResolutionContext`, and the `DoesFileExist` type from `metro-resolver` (use `fileSystemLookup`) (#2181 by @robhogan)
 - **[Breaking]**: `ResolutionContext.isESMImport` is required (#2182 by @robhogan)
 - **[Breaking]**: Async bundle and HMR module URLs keep the module's file extension (`foo.js.bundle`) and carry the bundle's `platform`, fixing lazy loading and HMR source maps for files whose extension isn't in `sourceExts` (e.g. `.cjs`, `.mjs`) or that share a name with a sibling (e.g. `foo.json` next to `foo.js`). Old URLs still work (#1990 by @dasjideepak)
 - **[Breaking]**: Babel transformers can no longer return a `functionMap`. Apply the `plugins` passed by Metro and return the resulting Babel `metadata` to keep function names in stack traces (#2157 by @robhogan)
 - **[Breaking]**: `/symbolicate` no longer reads `req.rawBody`, and fails if a middleware has already read the request body (#2158 by @robhogan)
 - **[Breaking]**: Remove the `bundle_transform_progressed_throttled` event from `TerminalReportableEvent`. `TerminalReporter` subclasses should handle `bundle_transform_progressed`, which their `_log` now receives for every progress update (#2170 by @vzaidman)
 - **[Breaking]**: Source and JSON files containing the reserved string `_$$_METRO_`, including in comments and string literals, now fail to transform (#2155 by @robhogan)
 - **[Breaking]**: Remove the deprecated, always-`true` `hot` option from `getTransformOptions` (#2159 by @robhogan)
 - **[Feature]**: Custom serializers receive the reserved string as `options.reservedString` (#2155 by @robhogan)
 - **[Fix]**: Don't print the terminal status in the middle of bursts of output written outside of it, and don't wait for a pending status update before exiting (#2170 by @vzaidman)
 - **[Fix]**: Fall back to file-based resolution, instead of throwing a `TypeError`, when a package's `"exports"` field is not a string, array or object (#2187 by @kwy404)
 - **[Fix]**: Fix relative symlinks on a different Windows drive than the project root (#2161 by @ramya-2196)
 - **[Performance]**: ~5-10% faster resolution (#2181 by @robhogan)

> NOTE: Experimental features are not covered by semver and can change at any time.

 - **[Experimental]**: Remove `unstable_incrementalResolution` from `ResolutionContext`, and `metro-resolver/private/createDefaultContext` (#2181 by @robhogan)

 **Full Changelog**: v0.87.3...v0.88.0
```
robhogan added a commit that referenced this pull request Nov 6, 2026
Summary:
Publish Metro 0.88.0 from `main`.

Changelog: [Internal]

Test plan:
```
node scripts/updateVersion.js 0.88.0
sl diff --stat
```

Only the 17 `packages/*/package.json` manifests change, and every `0.87.3` reference is replaced. Flow, ESLint, Prettier, TypeScript, API snapshots, Markdown lint and Jest pass on this commit.

Notable breakage in third parties, for the notes or a heads-up before React Native adopts this:

 - `@rnx-kit/metro-config` 2.2.6 adds `path.dirname(resolver.emptyModulePath)` as a watch folder when there are none, which is now `metro:runtime/modules`, so the dev server never serves a bundle for a single-package app.
 - `@tamagui/metro-plugin` 3.x refuses `metro-resolver` outside 0.84-0.87, and then imports the removed `metro-resolver/private/createDefaultContext`.
 - `@rnx-kit/metro-resolver-symlinks` calls the removed `context.redirectModulePath`.

End to end (AI-driven), with the packed tarballs against 0.87.3 from npm, in a React Native 0.89.0-rc.0 template app with a few popular dependencies: dev and release bundles, source maps and assets for iOS and Android are byte-identical, except where #2174 changes resolution. `rxjs/operators` now resolves to the same ESM build as `rxjs`, dropping 190 duplicate modules, and `require('nanoid/non-secure')` gets nanoid's CJS build. With #2186, only the module system and `asyncRequire` differ besides those. Release builds reach the first screen on a Galaxy A16 and the iOS simulator. The dev server passes the same checks on both, with and without Watchman (edits, added and deleted files, HMR, `/symbolicate`, assets and lazy `import()`), and Fast Refresh works on the simulator.

The end-to-end checks above were on 9de42bbed3b2, before #2170, #2157, #2158 and #1990 landed. I'm re-running them now and will update this.

Draft release notes (from the 19 commits in v0.87.3...main - internal-only changes omitted):

```
 - **[Breaking]**: Resolver: Read `"exports"` from the package root, and apply `"browser"` redirections to the file it resolves to, by default. Set `resolver.unstable_resolveExportsFromPackageRoot` and `resolver.unstable_applyBrowserSpecAfterExports` to `false` for the previous behavior (#2174 by @robhogan)
 - **[Breaking]**: Default `transformer.asyncRequireModulePath` and `resolver.emptyModulePath` to `metro:runtime/...`. A custom `resolver.resolveRequest` must delegate `metro:` specifiers it doesn't handle to `context.resolveRequest`, and code that reads either option as a file path must resolve it instead (#2180 by @robhogan)
 - **[Breaking]**: Resolve the module system via `metro:runtime/polyfills/require`. A custom `resolver.resolveRequest` must delegate `metro:` specifiers it doesn't handle to `context.resolveRequest` (#2184 by @robhogan)
 - **[Breaking]**: `transformer.globalPrefix` now also prefixes `__r`, `__c`, `__registerSegment` and `__accept`, and the default run statement. Code that calls these by name must apply the prefix (#2186 by @robhogan)
 - **[Breaking]**: Remove the deprecated `redirectModulePath` from `ResolutionContext`. A custom resolver should drop any call to it and delegate to `context.resolveRequest` or `resolve`, which already apply `"browser"` redirections and return `{type: 'empty'}` for `false` (#2181 by @robhogan)
 - **[Breaking]**: Remove the deprecated `doesFileExist` from `ResolutionContext`, and the `DoesFileExist` type from `metro-resolver` (use `fileSystemLookup`) (#2181 by @robhogan)
 - **[Breaking]**: `ResolutionContext.isESMImport` is required (#2182 by @robhogan)
 - **[Breaking]**: Async bundle and HMR module URLs keep the module's file extension (`foo.js.bundle`) and carry the bundle's `platform`, fixing lazy loading and HMR source maps for files whose extension isn't in `sourceExts` (e.g. `.cjs`, `.mjs`) or that share a name with a sibling (e.g. `foo.json` next to `foo.js`). Old URLs still work (#1990 by @dasjideepak)
 - **[Breaking]**: Babel transformers can no longer return a `functionMap`. Apply the `plugins` passed by Metro and return the resulting Babel `metadata` to keep function names in stack traces (#2157 by @robhogan)
 - **[Breaking]**: `/symbolicate` no longer reads `req.rawBody`, and fails if a middleware has already read the request body (#2158 by @robhogan)
 - **[Breaking]**: Remove the `bundle_transform_progressed_throttled` event from `TerminalReportableEvent`. `TerminalReporter` subclasses should handle `bundle_transform_progressed`, which their `_log` now receives for every progress update (#2170 by @vzaidman)
 - **[Breaking]**: Source and JSON files containing the reserved string `_$$_METRO_`, including in comments and string literals, now fail to transform (#2155 by @robhogan)
 - **[Breaking]**: Remove the deprecated, always-`true` `hot` option from `getTransformOptions` (#2159 by @robhogan)
 - **[Feature]**: Custom serializers receive the reserved string as `options.reservedString` (#2155 by @robhogan)
 - **[Fix]**: Don't print the terminal status in the middle of bursts of output written outside of it, and don't wait for a pending status update before exiting (#2170 by @vzaidman)
 - **[Fix]**: Fall back to file-based resolution, instead of throwing a `TypeError`, when a package's `"exports"` field is not a string, array or object (#2187 by @kwy404)
 - **[Fix]**: Fix relative symlinks on a different Windows drive than the project root (#2161 by @ramya-2196)
 - **[Performance]**: ~5-10% faster resolution (#2181 by @robhogan)

> NOTE: Experimental features are not covered by semver and can change at any time.

 - **[Experimental]**: Remove `unstable_incrementalResolution` from `ResolutionContext`, and `metro-resolver/private/createDefaultContext` (#2181 by @robhogan)

 **Full Changelog**: v0.87.3...v0.88.0
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants