From 094c60ba5b3f2ee92cb7f7c1d5c18a316095b6c4 Mon Sep 17 00:00:00 2001 From: Aman Mittal Date: Fri, 8 May 2026 21:26:04 +0530 Subject: [PATCH 01/16] [docs] Fix Vale errors and warnings (#45544) --- docs/.vale/writing-styles/expo-docs/HeadingCase.yml | 4 +++- docs/pages/guides/using-clerk.mdx | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/.vale/writing-styles/expo-docs/HeadingCase.yml b/docs/.vale/writing-styles/expo-docs/HeadingCase.yml index 434cc979b80887..37df95970a44ff 100644 --- a/docs/.vale/writing-styles/expo-docs/HeadingCase.yml +++ b/docs/.vale/writing-styles/expo-docs/HeadingCase.yml @@ -59,6 +59,7 @@ exceptions: - '.*CLI.*' - '.*TTI.*' - '.*Classic Updates.*' + - '.*Clerk.*' - '.*CNG.*' - '.*CocoaPods.*' - '.*CodePush.*' @@ -83,8 +84,8 @@ exceptions: - '.*EAS Insights.*' - '.*Element Inspector.*' - '.*Environment Variables.*' - - '.*EXIF.*' - '.*ESLint.*' + - '.*EXIF.*' - '.*Expo.*' - '.*ExpoKit.*' - '.*Expo Application Services.*' @@ -314,3 +315,4 @@ exceptions: - HorizontalCenteredHeroCarousel - HorizontalUncontainedCarousel - Serving Markdown to AI Agents + - Clerk Publishable Key diff --git a/docs/pages/guides/using-clerk.mdx b/docs/pages/guides/using-clerk.mdx index c6a2bd8f70692c..1621dd2d99d257 100644 --- a/docs/pages/guides/using-clerk.mdx +++ b/docs/pages/guides/using-clerk.mdx @@ -89,12 +89,8 @@ The plugin configures the iOS URL scheme for native Sign in with Google (when `E -{/* vale off */} - ### Add your Clerk Publishable Key -{/* vale on */} - Copy your Publishable Key from the [API keys](https://dashboard.clerk.com/last-active?path=api-keys) page in the Clerk Dashboard, then add it to a **.env** file in the root of your project: ```text .env From e0d41ec90d940657cbdc76be99bdab5aee938864 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Fri, 8 May 2026 16:59:12 +0100 Subject: [PATCH 02/16] perf(cli,metro-config,require-utils): Reduce memory impact of server-side source-map support (#45507) # Why This replaces `source-map-support` with Node's native source-map support, which reduces overhead that our current implementation adds, the non-wasm version of `source-map` adds, and the retention of larger source maps. This also drops `sourcesContent` from the server-side source-maps, which had a large impact on memory usage too and is basically pure overhead. Anecdotally, in `apps/router-e2e`'s `static-rendering` start-up, a single render ends up at a steady 365MB heap size rather than 522MB. The reduction compounds as more server-side code is evaluated. # How - Add `excludeSource` serializer option and thread it through to source-map creation - Default to `excludeSource: true` for server-side (`node` & `react-server`) environments - Add source-map support to `@expo/require-utils`' `evalModule` - Drop and replace `source-map-support` and other manual symbolication code from `serverLogLikeMetro` in the CLI # Test Plan - Manually ran `pnpm start:server-rendering` in `apps/router-e2e`, then edited `__e2e__/static-rendering/app/index.tsx` to add `throw new Error('test')` to the component, and validated that the terminal output remains identical - Also, throw an error in the `start:headers` API route test and check that the stack trace is symbolicated and formatted - Manually evaluated that the `@expo/require-utils` approach works in Node 20 and Node 22 - We'll need to double-check Windows support manually separately # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- packages/@expo/cli/CHANGELOG.md | 1 + packages/@expo/cli/package.json | 2 - .../start/server/getStaticRenderFunctions.ts | 36 +- .../server/metro/MetroBundlerDevServer.ts | 17 +- .../__tests__/withMetroMultiPlatform.test.ts | 34 - .../server/metro/withMetroMultiPlatform.ts | 6 +- .../start/server/middleware/metroOptions.ts | 7 +- .../src/start/server/serverLogLikeMetro.ts | 34 +- packages/@expo/metro-config/CHANGELOG.md | 2 + .../build/serializer/fork/baseJSBundle.d.ts | 1 + .../build/serializer/fork/baseJSBundle.js.map | 2 +- .../build/serializer/withExpoSerializers.js | 4 +- .../serializer/withExpoSerializers.js.map | 2 +- .../src/serializer/fork/baseJSBundle.ts | 1 + .../src/serializer/withExpoSerializers.ts | 10 +- packages/@expo/require-utils/CHANGELOG.md | 2 + packages/@expo/require-utils/build/load.d.ts | 4 + packages/@expo/require-utils/build/load.js | 106 ++- .../@expo/require-utils/build/load.js.map | 2 +- .../@expo/require-utils/build/stacktrace.d.ts | 42 ++ .../@expo/require-utils/build/stacktrace.js | 246 +++++++ .../require-utils/build/stacktrace.js.map | 1 + .../src/__tests__/stacktrace-test.ts | 604 ++++++++++++++++++ packages/@expo/require-utils/src/load.ts | 112 +++- .../@expo/require-utils/src/stacktrace.ts | 259 ++++++++ pnpm-lock.yaml | 13 - 26 files changed, 1407 insertions(+), 143 deletions(-) create mode 100644 packages/@expo/require-utils/build/stacktrace.d.ts create mode 100644 packages/@expo/require-utils/build/stacktrace.js create mode 100644 packages/@expo/require-utils/build/stacktrace.js.map create mode 100644 packages/@expo/require-utils/src/__tests__/stacktrace-test.ts create mode 100644 packages/@expo/require-utils/src/stacktrace.ts diff --git a/packages/@expo/cli/CHANGELOG.md b/packages/@expo/cli/CHANGELOG.md index 43f3e03d522bfb..39344143a23e1a 100644 --- a/packages/@expo/cli/CHANGELOG.md +++ b/packages/@expo/cli/CHANGELOG.md @@ -21,6 +21,7 @@ - Replace deprecated `url.parse()` with WHATWG `URL` API in Metro dev server. ([#45524](https://github.com/expo/expo/pull/45524) by [@EvanBacon](https://github.com/EvanBacon)) - Remove pinned dependencies ([#45520](https://github.com/expo/expo/pull/45520) by [@kitten](https://githun.com/kitten)) - Adopt `experiments.onDemandFilesystem` on `@expo/config-types` ([#45555](https://github.com/expo/expo/pull/45555) by [@kitten](https://github.com/kitten)) +- Replace `source-map-support` package with `@expo/require-utils`'s built-in Node source-map support for reduced memory usage ([#45507](https://github.com/expo/expo/pull/45507) by [@kitten](https://github.com/kitten)) ## 56.0.6 — 2026-05-07 diff --git a/packages/@expo/cli/package.json b/packages/@expo/cli/package.json index 2642ed9625845b..99dc01fd45ecd7 100644 --- a/packages/@expo/cli/package.json +++ b/packages/@expo/cli/package.json @@ -100,7 +100,6 @@ "semver": "^7.6.0", "send": "^0.19.0", "slugify": "^1.3.4", - "source-map-support": "~0.5.21", "stacktrace-parser": "^0.1.10", "structured-headers": "^0.4.1", "terminal-link": "^2.1.1", @@ -154,7 +153,6 @@ "@types/resolve": "^1.20.2", "@types/semver": "^7.5.8", "@types/send": "^0.17.1", - "@types/source-map-support": "^0.5.10", "@types/webpack": "~4.41.32", "@types/webpack-dev-server": "^3.11.0", "@types/wrap-ansi": "^3.0.0", diff --git a/packages/@expo/cli/src/start/server/getStaticRenderFunctions.ts b/packages/@expo/cli/src/start/server/getStaticRenderFunctions.ts index a3199cff25883e..792feba872f1e1 100644 --- a/packages/@expo/cli/src/start/server/getStaticRenderFunctions.ts +++ b/packages/@expo/cli/src/start/server/getStaticRenderFunctions.ts @@ -23,30 +23,6 @@ const debug = require('debug')('expo:start:server:getStaticRenderFunctions') as /** The list of input keys will become optional, everything else will remain the same. */ export type PickPartial = Omit & Partial>; -export const cachedSourceMaps: Map = new Map(); - -// Support unhandled rejections - -declare global { - namespace NodeJS { - interface Process { - isBun?: boolean; - } - } -} - -// Detect if running in Bun -if (!process.isBun) { - require('source-map-support').install({ - retrieveSourceMap(source: string) { - if (cachedSourceMaps.has(source)) { - return cachedSourceMaps.get(source); - } - return null; - }, - }); -} - async function ensureFileInRootDirectory(projectRoot: string, otherFile: string) { // Cannot be accessed using Metro's server API, we need to move the file // into the project root and try again. @@ -99,10 +75,11 @@ export function evalMetroAndWrapFunctions>( projectRoot: string, script: string, filename: string, + sourceMap: string | undefined, isExporting: boolean ): T { // TODO: Add back stack trace logic that hides traces from metro-runtime and other internal modules. - const contents = evalMetroNoHandling(projectRoot, script, filename); + const contents = evalMetroNoHandling(projectRoot, script, filename, sourceMap); if (!contents) { // This can happen if ErrorUtils isn't working correctly on web and failing to throw an error when a module throws. @@ -136,7 +113,12 @@ export function evalMetroAndWrapFunctions>( }, {} as any); } -export function evalMetroNoHandling(projectRoot: string, src: string, filename: string) { +export function evalMetroNoHandling( + projectRoot: string, + src: string, + filename: string, + sourceMap?: string +) { augmentLogs(projectRoot); // NOTE(@kitten): `require-from-string` derives a base path from the filename we pass it, @@ -148,5 +130,5 @@ export function evalMetroNoHandling(projectRoot: string, src: string, filename: debug(`evalMetroNoHandling received filename outside of the project root: ${filename}`); } - return profile(evalModule, 'eval-metro-bundle')(src, filename); + return profile(evalModule, 'eval-metro-bundle')(src, filename, { sourceMap }); } diff --git a/packages/@expo/cli/src/start/server/metro/MetroBundlerDevServer.ts b/packages/@expo/cli/src/start/server/metro/MetroBundlerDevServer.ts index 502e1c9d5b20c5..eb26b8d699b8d6 100644 --- a/packages/@expo/cli/src/start/server/metro/MetroBundlerDevServer.ts +++ b/packages/@expo/cli/src/start/server/metro/MetroBundlerDevServer.ts @@ -78,11 +78,7 @@ import { getEnvFiles, reloadEnvFiles } from '../../../utils/nodeEnv'; import { getFreePortAsync } from '../../../utils/port'; import type { BundlerStartOptions, DevServerInstance } from '../BundlerDevServer'; import { BundlerDevServer } from '../BundlerDevServer'; -import { - cachedSourceMaps, - evalMetroAndWrapFunctions, - evalMetroNoHandling, -} from '../getStaticRenderFunctions'; +import { evalMetroAndWrapFunctions, evalMetroNoHandling } from '../getStaticRenderFunctions'; import { fromRuntimeManifestRoute, fromServerManifestRoute, @@ -831,6 +827,7 @@ export class MetroBundlerDevServer extends BundlerDevServer { this.projectRoot, res.src, res.filename, + res.map, specificOptions.isExporting ?? this.instanceMetroOptions.isExporting! ); }; @@ -936,6 +933,7 @@ export class MetroBundlerDevServer extends BundlerDevServer { runModule: expoBundleOptions.runModule ?? true, sourceUrl: expoBundleOptions.sourceUrl!, sourceMapUrl: extraOptions.sourceMapUrl ?? expoBundleOptions.sourceMapUrl!, + excludeSource: expoBundleOptions.serializerOptions.excludeSource ?? false, }, transformOptions, }); @@ -985,13 +983,6 @@ export class MetroBundlerDevServer extends BundlerDevServer { const { filename, bundle, map, ...rest } = await this.metroLoadModuleContents(filePath, opts); const scriptContents = wrapBundle(bundle); - if (map) { - debug('Registering SSR source map for:', filename); - cachedSourceMaps.set(filename, { url: this.projectRoot, map }); - } else { - debug('No SSR source map found for:', filename); - } - return { ...rest, src: scriptContents, @@ -1811,7 +1802,7 @@ export class MetroBundlerDevServer extends BundlerDevServer { if (!apiRoute?.src) { return null; } - return evalMetroNoHandling(this.projectRoot, apiRoute.src, apiRoute.filename); + return evalMetroNoHandling(this.projectRoot, apiRoute.src, apiRoute.filename, apiRoute.map); } catch (error) { // Format any errors that were thrown in the global scope of the evaluation. if (error instanceof Error) { diff --git a/packages/@expo/cli/src/start/server/metro/__tests__/withMetroMultiPlatform.test.ts b/packages/@expo/cli/src/start/server/metro/__tests__/withMetroMultiPlatform.test.ts index 3bb8de46834196..372640069ef66b 100644 --- a/packages/@expo/cli/src/start/server/metro/__tests__/withMetroMultiPlatform.test.ts +++ b/packages/@expo/cli/src/start/server/metro/__tests__/withMetroMultiPlatform.test.ts @@ -750,8 +750,6 @@ describe(withExtendedResolver, () => { }); [ - 'source-map-support', - 'source-map-support/register.js', 'react', '@radix-ui/accordion', '@babel/runtime/helpers/interopRequireDefault', @@ -858,38 +856,6 @@ describe(withExtendedResolver, () => { expect(getResolveFunc()).toHaveBeenCalledTimes(1); }); - - it(`does not extern source-map-support in server environments that are bundling for standalone exports`, async () => { - const result = getModifiedConfig({ isExporting: true }).resolver.resolveRequest!( - getNodeResolverContext({ - customResolverOptions: { - exporting: true, - }, - }), - 'source-map-support', - 'web' - ); - - expect(result).toEqual({ - type: 'empty', - }); - - expect(getResolveFunc()).toHaveBeenCalledTimes(1); - }); - - it(`does not extern source-map-support in client environment`, async () => { - const result = getModifiedConfig().resolver.resolveRequest!( - getResolverContext(), - 'source-map-support', - 'web' - ); - - expect(result).toEqual({ - type: 'empty', - }); - - expect(getResolveFunc()).toHaveBeenCalledTimes(1); - }); }); describe('with fallback module resolver', () => { diff --git a/packages/@expo/cli/src/start/server/metro/withMetroMultiPlatform.ts b/packages/@expo/cli/src/start/server/metro/withMetroMultiPlatform.ts index e0fda146d9b99c..18dd804af43a44 100644 --- a/packages/@expo/cli/src/start/server/metro/withMetroMultiPlatform.ts +++ b/packages/@expo/cli/src/start/server/metro/withMetroMultiPlatform.ts @@ -315,19 +315,19 @@ export function withExtendedResolver( if (context.customResolverOptions?.environment === 'react-server') { // Ensure these non-react-server modules are excluded when bundling for React Server Components in development. - return /^(source-map-support(\/.*)?|@babel\/runtime\/.+|debug|metro-runtime\/src\/modules\/HMRClient|metro|acorn-loose|acorn|chalk|ws|ansi-styles|supports-color|color-convert|has-flag|utf-8-validate|color-name|react-refresh\/runtime|@remix-run\/node\/.+)$/.test( + return /^(@babel\/runtime\/.+|debug|metro-runtime\/src\/modules\/HMRClient|metro|acorn-loose|acorn|chalk|ws|ansi-styles|supports-color|color-convert|has-flag|utf-8-validate|color-name|react-refresh\/runtime|@remix-run\/node\/.+)$/.test( moduleName ); } // TODO: Windows doesn't support externals somehow. if (process.platform === 'win32') { - return /^(source-map-support(\/.*)?)$/.test(moduleName); + return false; } // Extern these modules in standard Node.js environments in development to prevent API routes side-effects // from leaking into the dev server process. - return /^(source-map-support(\/.*)?|react|@radix-ui\/.+|@babel\/runtime\/.+|react-dom(\/.+)?|debug|acorn-loose|acorn|css-in-js-utils\/lib\/.+|hyphenate-style-name|color|color-string|color-convert|color-name|fontfaceobserver|fast-deep-equal|query-string|escape-string-regexp|invariant|postcss-value-parser|memoize-one|nullthrows|strict-uri-encode|decode-uri-component|split-on-first|filter-obj|warn-once|simple-swizzle|is-arrayish|inline-style-prefixer\/.+)$/.test( + return /^(react|@radix-ui\/.+|@babel\/runtime\/.+|react-dom(\/.+)?|debug|acorn-loose|acorn|css-in-js-utils\/lib\/.+|hyphenate-style-name|color|color-string|color-convert|color-name|fontfaceobserver|fast-deep-equal|query-string|escape-string-regexp|invariant|postcss-value-parser|memoize-one|nullthrows|strict-uri-encode|decode-uri-component|split-on-first|filter-obj|warn-once|simple-swizzle|is-arrayish|inline-style-prefixer\/.+)$/.test( moduleName ); }, diff --git a/packages/@expo/cli/src/start/server/middleware/metroOptions.ts b/packages/@expo/cli/src/start/server/middleware/metroOptions.ts index ab8331abe42cb1..a2a07398e1b810 100644 --- a/packages/@expo/cli/src/start/server/middleware/metroOptions.ts +++ b/packages/@expo/cli/src/start/server/middleware/metroOptions.ts @@ -53,6 +53,9 @@ export type ExpoMetroOptions = { modulesOnly?: boolean; runModule?: boolean; + /** When true, omits `sourcesContent` from generated source maps (saves ~80x memory for SSR). */ + excludeSource?: boolean; + /** Should assets be exported for hosting. Always true on web. Always false for embedded builds. Optional for native exports. */ hosted?: boolean; /** Disable live bindings (enabled by default, required for circular deps) in experimental import export support. */ @@ -108,6 +111,7 @@ function withDefaults({ lazy: !props.isExporting && lazy, environment: environment === 'client' ? undefined : environment, liveBindings: env.EXPO_UNSTABLE_LIVE_BINDINGS, + excludeSource: isServerEnvironment(environment), ...props, }; } @@ -175,6 +179,7 @@ export function getMetroDirectBundleOptions(options: ExpoMetroOptions) { hosted, liveBindings, isLoaderBundle, + excludeSource, } = withDefaults(options); const dev = mode !== 'production'; @@ -250,7 +255,7 @@ export function getMetroDirectBundleOptions(options: ExpoMetroOptions) { output: serializerOutput, includeSourceMaps: serializerIncludeMaps, exporting: isExporting || undefined, - excludeSource: Server.DEFAULT_BUNDLE_OPTIONS.excludeSource, + excludeSource: excludeSource ?? Server.DEFAULT_BUNDLE_OPTIONS.excludeSource, }, // TODO(@kitten): See comments in MetroBundlerDevServer.ts; should all defaults be added and the logic // from `src/start/server/middleware/metroOptions.ts` that adds default be moved here? diff --git a/packages/@expo/cli/src/start/server/serverLogLikeMetro.ts b/packages/@expo/cli/src/start/server/serverLogLikeMetro.ts index 1280288fe181de..358a33514b3abb 100644 --- a/packages/@expo/cli/src/start/server/serverLogLikeMetro.ts +++ b/packages/@expo/cli/src/start/server/serverLogLikeMetro.ts @@ -4,11 +4,9 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import type { SourcePosition } from '@expo/metro/metro-source-map/Consumer/types'; import { INTERNAL_CALLSITES_REGEX } from '@expo/metro-config'; import chalk from 'chalk'; import path from 'path'; -import { mapSourcePosition } from 'source-map-support'; import * as stackTraceParser from 'stacktrace-parser'; import type { StackFrame } from './metro/log-box/LogBoxSymbolication'; @@ -194,35 +192,13 @@ function augmentLogsInternal(projectRoot: string) { const customStack = args[1]; try { + // `error.stack` is already symbolicated by Node's source map support + // (registered by `@expo/require-utils` when the SSR bundle is compiled), so the + // parsed frames already point at original sources. We just reformat them. const parsedStack = parseErrorStack(customStack); - const symbolicatedStack = parsedStack.map((line) => { - // TODO(@kitten): Is there overlap here with metro-source-map? - const mapped = mapSourcePosition({ - // TODO(@kitten): Check if these non-null casts are correct and cannot fail - source: line.file!, - line: line.lineNumber!, - column: line.column!, - }) as SourcePosition; - - const fallbackName = mapped.name ?? ''; - return { - file: mapped.source ?? null, - lineNumber: mapped.line ?? null, - column: mapped.column ?? null, - // Attempt to preserve the react component name if possible. - methodName: line.methodName - ? line.methodName === '' - ? fallbackName - : line.methodName - : fallbackName, - arguments: line.arguments ?? [], - }; - }); - - // Replace args[1] with the formatted stack. - args[1] = '\n' + formatParsedStackLikeMetro(projectRoot, symbolicatedStack, true); + args[1] = '\n' + formatParsedStackLikeMetro(projectRoot, parsedStack, true); } catch { - // If symbolication fails, log the original stack. + // If parsing fails, log the original stack. args.push('\n' + formatStackLikeMetro(projectRoot, customStack)); } } else { diff --git a/packages/@expo/metro-config/CHANGELOG.md b/packages/@expo/metro-config/CHANGELOG.md index 5236ce533528d6..67864a32f4f2b2 100644 --- a/packages/@expo/metro-config/CHANGELOG.md +++ b/packages/@expo/metro-config/CHANGELOG.md @@ -10,6 +10,8 @@ ### 💡 Others +- Thread `excludeSource` through to source map serialization ([#45507](https://github.com/expo/expo/pull/45507) by [@kitten](https://github.com/kitten)) + ## 56.0.4 — 2026-05-07 ### 💡 Others diff --git a/packages/@expo/metro-config/build/serializer/fork/baseJSBundle.d.ts b/packages/@expo/metro-config/build/serializer/fork/baseJSBundle.d.ts index 5bf24afde8b302..0173683bc73aa6 100644 --- a/packages/@expo/metro-config/build/serializer/fork/baseJSBundle.d.ts +++ b/packages/@expo/metro-config/build/serializer/fork/baseJSBundle.d.ts @@ -25,6 +25,7 @@ export type ExpoSerializerOptions = SerializerOptions & { output?: string; includeSourceMaps?: boolean; exporting?: boolean; + excludeSource?: boolean; }; debugId?: string; }; diff --git a/packages/@expo/metro-config/build/serializer/fork/baseJSBundle.js.map b/packages/@expo/metro-config/build/serializer/fork/baseJSBundle.js.map index a5fd0e3a962e1a..1acf8781565767 100644 --- a/packages/@expo/metro-config/build/serializer/fork/baseJSBundle.js.map +++ b/packages/@expo/metro-config/build/serializer/fork/baseJSBundle.js.map @@ -1 +1 @@ -{"version":3,"file":"baseJSBundle.js","sourceRoot":"","sources":["../../../src/serializer/fork/baseJSBundle.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"baseJSBundle.js","sourceRoot":"","sources":["../../../src/serializer/fork/baseJSBundle.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/@expo/metro-config/build/serializer/withExpoSerializers.js b/packages/@expo/metro-config/build/serializer/withExpoSerializers.js index 609b53797190e2..93b704f9214643 100644 --- a/packages/@expo/metro-config/build/serializer/withExpoSerializers.js +++ b/packages/@expo/metro-config/build/serializer/withExpoSerializers.js @@ -136,9 +136,7 @@ function createDefaultExportCustomSerializer(config, configOptions = {}) { } const getEnsuredMaps = () => { bundleMap ??= getSourceMapString()([...premodulesToBundle, ...(0, serializeChunks_1.getSortedModules)([...graph.dependencies.values()], options)], { - // TODO: Surface this somehow. - excludeSource: false, - // excludeSource: options.serializerOptions?.excludeSource, + excludeSource: options.serializerOptions?.excludeSource ?? false, processModuleFilter: options.processModuleFilter, shouldAddToIgnoreList: options.shouldAddToIgnoreList, }); diff --git a/packages/@expo/metro-config/build/serializer/withExpoSerializers.js.map b/packages/@expo/metro-config/build/serializer/withExpoSerializers.js.map index 91835dadd6b600..e6788ded63f881 100644 --- a/packages/@expo/metro-config/build/serializer/withExpoSerializers.js.map +++ b/packages/@expo/metro-config/build/serializer/withExpoSerializers.js.map @@ -1 +1 @@ -{"version":3,"file":"withExpoSerializers.js","sourceRoot":"","sources":["../../src/serializer/withExpoSerializers.ts"],"names":[],"mappings":";;;;;AAoFA,kDAiBC;AAID,sDAqBC;AAED,kFAqJC;AAkHD,oFAoBC;AAlZD,0FAAkE;AAElE,+CAAyD;AAEzD,uCAAyC;AACzC,+FAG+C;AAE/C,uDAA+E;AAC/E,gCAA6B;AAI7B,mGAAmG;AACnG,IAAI,gBAA6G,CAAC;AAClH,SAAS,kBAAkB;IACzB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,gBAAgB;YACd,OAAO,CAAC,4DAA4D,CAAC,CAAC,eAAe,CAAC;IAC1F,CAAC;IACD,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED,2EAA2E;AAC3E,IAAI,mCAA6H,CAAC;AAClI,SAAS,qCAAqC;IAC5C,IAAI,CAAC,mCAAmC,EAAE,CAAC;QACzC,mCAAmC;YACjC,OAAO,CAAC,sCAAsC,CAAC,CAAC,kCAAkC,CAAC;IACvF,CAAC;IACD,OAAO,mCAAmC,CAAC;AAC7C,CAAC;AAED,IAAI,oBAAsF,CAAC;AAC3F,SAAS,sBAAsB;IAC7B,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC1B,oBAAoB,GAAG,OAAO,CAAC,6BAA6B,CAAC,CAAC,mBAAmB,CAAC;IACpF,CAAC;IACD,OAAO,oBAAoB,CAAC;AAC9B,CAAC;AAED,+FAA+F;AAC/F,IAAI,aAAgE,CAAC;AACrE,SAAS,eAAe;IACtB,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,aAAa,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC,YAAY,CAAC;IAC9D,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC;AAyBD,SAAgB,mBAAmB,CACjC,MAAc,EACd,UAAmC,EAAE;IAErC,MAAM,UAAU,GAAuB,EAAE,CAAC;IAC1C,UAAU,CAAC,IAAI,CAAC,mEAA6B,CAAC,CAAC;IAC/C,IAAI,CAAC,SAAG,CAAC,uBAAuB,EAAE,CAAC;QACjC,UAAU,CAAC,IAAI,CAAC,yEAAmC,CAAC,CAAC;IACvD,CAAC;IAED,+BAA+B;IAC/B,UAAU,CAAC,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC;IAE1C,uDAAuD;IACvD,UAAU,CAAC,IAAI,CAAC,qCAAqC,EAAE,CAAC,CAAC;IAEzD,OAAO,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;AAC5D,CAAC;AAED,iFAAiF;AACjF,qBAAqB;AACrB,SAAgB,qBAAqB,CACnC,MAAc,EACd,UAA8B,EAC9B,UAAmC,EAAE;IAErC,MAAM,cAAc,GAAG,oCAAoC,CACzD,MAAM,EACN,UAAU,EACV,MAAM,CAAC,UAAU,EAAE,gBAAgB,IAAI,IAAI,EAC3C,OAAO,CACR,CAAC;IAEF,mFAAmF;IACnF,qFAAqF;IAErF,yFAAyF;IACzF,MAAM,CAAC,UAAU,KAAK,EAAE,CAAC;IACzB,yFAAyF;IACzF,MAAM,CAAC,UAAU,CAAC,gBAAgB,GAAG,cAAc,CAAC;IAEpD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAgB,mCAAmC,CACjD,MAA4B,EAC5B,gBAAyC,EAAE;IAE3C,OAAO,KAAK,EACV,UAAkB,EAClB,UAA0C,EAC1C,KAAiC,EACjC,YAA+B,EACkB,EAAE;QACnD,8HAA8H;QAC9H,MAAM,aAAa,GACjB,KAAK,CAAC,gBAAgB,CAAC,GAAG;YAC1B,KAAK,CAAC,gBAAgB,CAAC,sBAAsB,EAAE,WAAW,KAAK,MAAM,CAAC;QACxE,mGAAmG;QACnG,MAAM,aAAa,GAAG,YAAY,CAAC,eAAe,KAAK,IAAI,IAAI,CAAC,aAAa,CAAC;QAE9E,MAAM,OAAO,GAAG;YACd,QAAQ,EAAE,KAAK,CAAC,gBAAgB,EAAE,QAAQ;YAC1C,WAAW,EAAE,KAAK,CAAC,gBAAgB,EAAE,sBAAsB,EAAE,WAAW,IAAI,QAAQ;SACrF,CAAC;QAEF,MAAM,OAAO,GAAsB;YACjC,GAAG,YAAY;YACf,cAAc,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE;gBACrC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACrB,OAAO,YAAY,CAAC,cAAc,CAAC,QAAQ,EAAE,GAAG,KAAK,CAAC,CAAC;gBACzD,CAAC;gBAED,OAAO,YAAY,CAAC,cAAc,CAChC,QAAQ;gBACR,gGAAgG;gBAChG,OAAO,CACR,CAAC;YACJ,CAAC;SACF,CAAC;QAEF,IAAI,OAA2B,CAAC;QAChC,MAAM,WAAW,GAAG,GAAG,EAAE;YACvB,IAAI,CAAC,aAAa,IAAI,OAAO,EAAE,CAAC;gBAC9B,OAAO,OAAO,CAAC;YACjB,CAAC;YAED,8BAA8B;YAC9B,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE;gBAC9D,GAAG,OAAO;gBACV,OAAO,EAAE,SAAS;aACnB,CAAC,CAAC;YACH,MAAM,UAAU,GAAG,IAAA,wBAAc,EAAC,MAAM,CAAC,CAAC,IAAI,CAAC;YAC/C,OAAO,GAAG,IAAA,sBAAY,EAAC,UAAU,CAAC,CAAC;YACnC,OAAO,OAAO,CAAC;QACjB,CAAC,CAAC;QAEF,IAAI,kBAAkB,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC;QAEzC,IAAI,UAAU,GAAkB,IAAI,CAAC;QACrC,IAAI,SAAS,GAAkB,IAAI,CAAC;QAEpC,+DAA+D;QAC/D,yGAAyG;QACzG,MAAM,wBAAwB,GAAG,wBAAwB,CAAC,MAAM,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;QAC/F,IAAI,wBAAwB,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAAC,UAAU,EAAE,kBAAkB,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YAC9F,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;gBAC/B,UAAU,GAAG,MAAM,CAAC;YACtB,CAAC;iBAAM,CAAC;gBACN,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC;gBACzB,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC;YACzB,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;YAC9B,IAAI,aAAa,CAAC,wCAAwC,EAAE,CAAC;gBAC3D,KAAK,MAAM,MAAM,IAAI,aAAa,CAAC,wCAAwC,EAAE,CAAC;oBAC5E,kBAAkB,GAAG,MAAM,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,GAAG,kBAAkB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;gBACvF,CAAC;YACH,CAAC;YACD,UAAU,GAAG,IAAA,wBAAc,EACzB,eAAe,EAAE,CAAC,UAAU,EAAE,kBAAkB,EAAE,KAAK,EAAE;gBACvD,GAAG,OAAO;gBACV,OAAO;aACR,CAAC,CACH,CAAC,IAAI,CAAC;QACT,CAAC;QAED,MAAM,cAAc,GAAG,GAAG,EAAE;YAC1B,SAAS,KAAK,kBAAkB,EAAE,CAChC,CAAC,GAAG,kBAAkB,EAAE,GAAG,IAAA,kCAAgB,EAAC,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,EACvF;gBACE,8BAA8B;gBAC9B,aAAa,EAAE,KAAK;gBACpB,2DAA2D;gBAC3D,mBAAmB,EAAE,OAAO,CAAC,mBAAmB;gBAChD,qBAAqB,EAAE,OAAO,CAAC,qBAAqB;aACrD,CACF,CAAC;YAEF,OAAO,SAAS,CAAC;QACnB,CAAC,CAAC;QAEF,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACpC,MAAM,GAAG,GAAG,IAAA,2BAAY,EAAC,OAAO,CAAC,SAAS,CAAC;gBACzC,CAAC,CAAC,IAAA,0BAAW,EAAC,OAAO,CAAC,SAAS,CAAC;gBAChC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;YACtB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;YAC/C,2CAA2C;YAC3C,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACrC,OAAO;oBACL,IAAI,EAAE,UAAU;oBAChB,GAAG,EAAE,cAAc,EAAE;iBACtB,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,aAAa,EAAE,CAAC;YAClB,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;gBACtB,OAAO,UAAU,CAAC;YACpB,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,UAAU;gBAChB,GAAG,EAAE,SAAS;aACf,CAAC;QACJ,CAAC;QAED,cAAc;QAEd,SAAS,KAAK,cAAc,EAAE,CAAC;QAE/B,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,0BAA0B,GAAG,CAAC,SAAiB,EAAE,EAAE;gBACvD,qHAAqH;gBACrH,8DAA8D;gBAC9D,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBAC9C,eAAe,CAAC,OAAO,GAAG,WAAW,EAAE,CAAC;gBACxC,4CAA4C;gBAC5C,sCAAsC;gBACtC,OAAO,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;YACzC,CAAC,CAAC;YAEF,OAAO;gBACL,IAAI,EAAE,UAAU;gBAChB,GAAG,EAAE,0BAA0B,CAAC,SAAS,CAAC;aAC3C,CAAC;QACJ,CAAC;QAED,OAAO;YACL,IAAI,EAAE,UAAU;YAChB,GAAG,EAAE,SAAS;SACf,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAC3B,MAAmB,EACnB,kBAAsC,EACtC,gBAAyC,EAAE;IAE3C,MAAM,iBAAiB,GACrB,kBAAkB,IAAI,mCAAmC,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAEnF,MAAM,cAAc,GAAG,KAAK,EAC1B,UAAkB,EAClB,UAA0C,EAC1C,KAAiC,EACjC,YAAmC,EACc,EAAE;QACnD,MAAM,OAAO,GAAG;YACd,QAAQ,EAAE,KAAK,CAAC,gBAAgB,EAAE,QAAQ;YAC1C,WAAW,EAAE,KAAK,CAAC,gBAAgB,EAAE,sBAAsB,EAAE,WAAW,IAAI,QAAQ;SACrF,CAAC;QAEF,MAAM,cAAc,GAClB,KAAK,CAAC,gBAAgB,EAAE,sBAAsB,EAAE,cAAc,KAAK,MAAM,CAAC;QAC5E,MAAM,WAAW,GAAG,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,EAAU,CAAC;QAE5F,MAAM,OAAO,GAA0B;YACrC,GAAG,YAAY;YACf,cAAc,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE;gBACrC,0EAA0E;gBAC1E,+EAA+E;gBAC/E,kFAAkF;gBAClF,IAAI,UAAU,GAAG,QAAQ,CAAC;gBAC1B,IAAI,cAAc,IAAI,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAChD,UAAU,GAAG,GAAG,QAAQ,SAAS,CAAC;gBACpC,CAAC;gBAED,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACrB,OAAO,YAAY,CAAC,cAAc,CAAC,UAAU,EAAE,GAAG,KAAK,CAAC,CAAC;gBAC3D,CAAC;gBACD,OAAO,YAAY,CAAC,cAAc,CAChC,UAAU;gBACV,gGAAgG;gBAChG,OAAO,CACR,CAAC;YACJ,CAAC;SACF,CAAC;QAEF,MAAM,uBAAuB,GAAG,YAAY,CAAC,iBAAiB,CAAC;QAE/D,uEAAuE;QACvE,gHAAgH;QAChH,MAAM,uBAAuB,GAAG,CAAC,CAAC,uBAAuB,EAAE,MAAM,CAAC;QAElE,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE;YAC9B,IAAI,uBAAuB,EAAE,CAAC;gBAC5B,OAAO;oBACL,UAAU,EAAE,uBAAuB,CAAC,MAAM;oBAC1C,WAAW,EAAE,uBAAuB,CAAC,WAAW;oBAChD,WAAW,EAAE,uBAAuB,CAAC,WAAW;oBAChD,iBAAiB,EAAE,uBAAuB,CAAC,iBAAiB;iBAC7D,CAAC;YACJ,CAAC;YACD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBACtB,MAAM,SAAS,GAAG,IAAA,2BAAY,EAAC,OAAO,CAAC,SAAS,CAAC;oBAC/C,CAAC,CAAC,IAAA,0BAAW,EAAC,OAAO,CAAC,SAAS,CAAC;oBAChC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;gBAEtB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;gBAEnD,OAAO;oBACL,UAAU,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,mBAAmB,CAAC;oBACrD,WAAW,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,wBAAwB,CAAC,KAAK,MAAM;oBACtE,WAAW,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,wBAAwB,CAAC,KAAK,MAAM;oBACtE,iBAAiB,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,gBAAgB,CAAC,KAAK,MAAM;iBACrE,CAAC;YACJ,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,EAAE,CAAC;QAEL,IAAI,iBAAiB,EAAE,UAAU,KAAK,QAAQ,EAAE,CAAC;YAC/C,OAAO,iBAAiB,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QACnE,CAAC;QAED,yDAAyD;QACzD,OAAO,CAAC,iBAAiB,GAAG;YAC1B,GAAG,OAAO,CAAC,iBAAiB;YAC5B,GAAG,iBAAiB;SACrB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAA,0CAAwB,EAC3C,MAAM,EACN;YACE,iBAAiB,EAAE,CAAC,CAAC,iBAAiB,CAAC,iBAAiB;YACxD,WAAW,EAAE,CAAC,CAAC,iBAAiB,CAAC,WAAW;YAC5C,GAAG,aAAa;SACjB,EACD,UAAU,EACV,UAAU,EACV,KAAK,EAEL,OAAO,CACR,CAAC;QAEF,IAAI,uBAAuB,EAAE,CAAC;YAC5B,qFAAqF;YACrF,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC,CAAC;IAEF,OAAO,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,SAAgB,oCAAoC,CAClD,MAAmB,EACnB,UAA4C,EAC5C,kBAAqC,EACrC,UAAmC,EAAE;IAErC,MAAM,eAAe,GAAG,oBAAoB,CAAC,MAAM,EAAE,kBAAkB,EAAE,OAAO,CAAC,CAAC;IAElF,OAAO,0BAA0B,CAC/B,kBAAkB,EAClB,KAAK,EAAE,GAAG,KAA2B,EAA0B,EAAE;QAC/D,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,IAAI,SAAS,EAAE,CAAC;gBACd,KAAK,GAAG,MAAM,SAAS,CAAC,GAAG,KAAK,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;QAED,OAAO,eAAe,CAAC,GAAG,KAAK,CAAC,CAAC;IACnC,CAAC,CACF,CAAC;AACJ,CAAC;AAED,SAAS,0BAA0B,CAAC,QAA2B,EAAE,IAAgB;IAC/E,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,oBAAoB,EAAE,QAAQ,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,wBAAwB,CAAC,UAA8B;IAC9D,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,sBAAsB,IAAI,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IACxE,OAAO,UAAU,CAAC,oBAAyC,CAAC;AAC9D,CAAC;AAED;;;GAGG;AACH,SAAS,cAAc,CAAC,YAAkC;IACxD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,KAAK,MAAM,MAAM,IAAI,YAAY,CAAC,MAAM,EAAE,EAAE,CAAC;QAC3C,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YACnC,qCAAqC;YACrC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAW,CAAC;YAChC,IAAI,iBAAiB,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,eAAe,KAAK,QAAQ,EAAE,CAAC;gBAC1E,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC"} \ No newline at end of file +{"version":3,"file":"withExpoSerializers.js","sourceRoot":"","sources":["../../src/serializer/withExpoSerializers.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/@expo/metro-config/src/serializer/fork/baseJSBundle.ts b/packages/@expo/metro-config/src/serializer/fork/baseJSBundle.ts index c89a606a8fe09c..b6a8cc468a042f 100644 --- a/packages/@expo/metro-config/src/serializer/fork/baseJSBundle.ts +++ b/packages/@expo/metro-config/src/serializer/fork/baseJSBundle.ts @@ -45,6 +45,7 @@ export type ExpoSerializerOptions = SerializerOptions & { output?: string; includeSourceMaps?: boolean; exporting?: boolean; + excludeSource?: boolean; }; // Chunk-based stable identifier for the bundle that is used for identifying the bundle. // https://sentry.engineering/blog/the-case-for-debug-ids diff --git a/packages/@expo/metro-config/src/serializer/withExpoSerializers.ts b/packages/@expo/metro-config/src/serializer/withExpoSerializers.ts index 057a0045f353f3..a80f9df7883a80 100644 --- a/packages/@expo/metro-config/src/serializer/withExpoSerializers.ts +++ b/packages/@expo/metro-config/src/serializer/withExpoSerializers.ts @@ -6,7 +6,7 @@ */ import type { MetroConfig } from '@expo/metro/metro'; import type { Module, ReadOnlyGraph, MixedOutput } from '@expo/metro/metro/DeltaBundler'; -import type { ReadOnlyDependencies, SerializerOptions } from '@expo/metro/metro/DeltaBundler/types'; +import type { ReadOnlyDependencies } from '@expo/metro/metro/DeltaBundler/types'; import bundleToString from '@expo/metro/metro/lib/bundleToString'; import type { ConfigT, InputConfigT } from '@expo/metro/metro-config'; import { isJscSafeUrl, toNormalUrl } from 'jsc-safe-url'; @@ -134,7 +134,7 @@ export function createDefaultExportCustomSerializer( entryPoint: string, preModules: readonly Module[], graph: ReadOnlyGraph, - inputOptions: SerializerOptions + inputOptions: ExpoSerializerOptions ): Promise => { // NOTE(@kitten): My guess is that this was supposed to always be disabled for `node` since we set `hot: true` manually for it const isPossiblyDev = @@ -148,7 +148,7 @@ export function createDefaultExportCustomSerializer( environment: graph.transformOptions?.customTransformOptions?.environment ?? 'client', }; - const options: SerializerOptions = { + const options: ExpoSerializerOptions = { ...inputOptions, createModuleId: (moduleId, ...props) => { if (props.length > 0) { @@ -214,9 +214,7 @@ export function createDefaultExportCustomSerializer( bundleMap ??= getSourceMapString()( [...premodulesToBundle, ...getSortedModules([...graph.dependencies.values()], options)], { - // TODO: Surface this somehow. - excludeSource: false, - // excludeSource: options.serializerOptions?.excludeSource, + excludeSource: options.serializerOptions?.excludeSource ?? false, processModuleFilter: options.processModuleFilter, shouldAddToIgnoreList: options.shouldAddToIgnoreList, } diff --git a/packages/@expo/require-utils/CHANGELOG.md b/packages/@expo/require-utils/CHANGELOG.md index cf4de8ff6ba5fa..8841f2612f39cf 100644 --- a/packages/@expo/require-utils/CHANGELOG.md +++ b/packages/@expo/require-utils/CHANGELOG.md @@ -6,6 +6,8 @@ ### 🎉 New features +- Add Node built-in source-map support to `evalModule` ([#45507](https://github.com/expo/expo/pull/45507) by [@kitten](https://github.com/kitten)) + ### 🐛 Bug fixes ### 💡 Others diff --git a/packages/@expo/require-utils/build/load.d.ts b/packages/@expo/require-utils/build/load.d.ts index 335d7407828b10..795c17ff79a6eb 100644 --- a/packages/@expo/require-utils/build/load.d.ts +++ b/packages/@expo/require-utils/build/load.d.ts @@ -6,11 +6,15 @@ declare global { interface Module { _compile(code: string, filename: string, format?: 'module' | 'commonjs' | 'commonjs-typescript' | 'module-typescript' | 'typescript'): unknown; } + interface Process { + isBun?: boolean; + } } } type Format = 'commonjs' | 'module' | 'module-typescript' | 'commonjs-typescript' | 'typescript'; export interface ModuleOptions { paths?: string[]; + sourceMap?: string; } declare function evalModule(code: string, filename: string, opts?: ModuleOptions, format?: Format): any; declare function loadModule(filename: string): Promise; diff --git a/packages/@expo/require-utils/build/load.js b/packages/@expo/require-utils/build/load.js index 4b806cdfde9c83..8786ae0d3a0aef 100644 --- a/packages/@expo/require-utils/build/load.js +++ b/packages/@expo/require-utils/build/load.js @@ -20,6 +20,13 @@ function nodeModule() { }; return data; } +function _nodeOs() { + const data = _interopRequireDefault(require("node:os")); + _nodeOs = function () { + return data; + }; + return data; +} function _nodePath() { const data = _interopRequireDefault(require("node:path")); _nodePath = function () { @@ -41,6 +48,13 @@ function _codeframe() { }; return data; } +function _stacktrace() { + const data = require("./stacktrace"); + _stacktrace = function () { + return data; + }; + return data; +} function _transform() { const data = require("./transform"); _transform = function () { @@ -118,6 +132,33 @@ function toRealDirname(filePath) { return normalized; } } +const hasModuleSourceMapsSupport = typeof nodeModule().setSourceMapsSupport === 'function'; +function getSourceMapsState() { + return typeof nodeModule().getSourceMapsSupport === 'function' ? nodeModule().getSourceMapsSupport() : { + enabled: !!process.sourceMapsEnabled + }; +} +function setSourceMapsState(state) { + if (hasModuleSourceMapsSupport) { + nodeModule().setSourceMapsSupport(state.enabled, { + nodeModules: state.nodeModules ?? false, + generatedCode: state.generatedCode ?? false + }); + } else { + process.setSourceMapsEnabled(state.enabled); + } +} +function makeSourceMapTempPath(filename) { + let basename = _nodePath().default.basename(filename); + const queryIdx = basename.search(/[?#]/); + if (queryIdx >= 0) { + basename = basename.slice(0, queryIdx); + } + return _nodePath().default.join(_nodeOs().default.tmpdir(), `require-utils-${process.pid}-${basename}.map`); +} +function stripSourceMappingURL(code) { + return code.replace(/^[ \t]*\/\/[#@][ \t]+sourceMappingURL=.*$/gm, ''); +} function compileModule(code, filename, opts) { const format = toFormat(filename, false); const prependPaths = opts.paths ?? []; @@ -126,19 +167,74 @@ function compileModule(code, filename, opts) { const basePath = toRealDirname(filename); const nodeModulePaths = nodeModule()._nodeModulePaths(basePath); const paths = [...prependPaths, ...nodeModulePaths]; + let inputCode = code; + + // We may get a Metro SSR relative path here, which isn't a valid absolute path, and we need to normalize + // the filename before proceeding + let compileFilename = filename; + if (opts.sourceMap) { + const queryIdx = filename.search(/[?#]/); + const basePart = queryIdx >= 0 ? filename.slice(0, queryIdx) : filename; + const queryPart = queryIdx >= 0 ? filename.slice(queryIdx) : ''; + if (!_nodePath().default.isAbsolute(basePart)) { + compileFilename = _nodePath().default.resolve(basePart) + queryPart; + } + } + let mapPath; + let priorSourceMapsState; + if (opts.sourceMap && !process.isBun) { + try { + mapPath = makeSourceMapTempPath(compileFilename); + _nodeFs().default.writeFileSync(mapPath, opts.sourceMap); + } catch (error) { + mapPath = undefined; + // If we fail to write the source map, we can still continue without it, but log a warning since it's likely a misconfiguration + console.warn(`Warning: Failed to write source map for ${filename} to ${mapPath}. Source maps will be unavailable for this module.\n${error?.message || error}`); + } + if (mapPath) { + inputCode = stripSourceMappingURL(code); + // NOTE This needs to be a plain absolute path because Node rejects file: URLs + inputCode += `\n//# sourceMappingURL=${mapPath}`; + priorSourceMapsState = getSourceMapsState(); + (0, _stacktrace().installSourceMapStackTrace)(); + setSourceMapsState({ + enabled: true, + nodeModules: true + }); + } + } try { - const mod = Object.assign(new (nodeModule().Module)(filename, parent), { - filename, + const mod = Object.assign(new (nodeModule().Module)(compileFilename, parent), { + filename: compileFilename, paths }); - mod._compile(code, filename, format != null ? format : undefined); + mod._compile(inputCode, compileFilename, format != null ? format : undefined); mod.loaded = true; - require.cache[filename] = mod; + require.cache[compileFilename] = mod; + if (compileFilename !== filename) { + require.cache[filename] = mod; + } parent?.children?.splice(parent.children.indexOf(mod), 1); return mod; } catch (error) { - delete require.cache[filename]; + delete require.cache[compileFilename]; + if (compileFilename !== filename) { + delete require.cache[filename]; + } throw error; + } finally { + if (mapPath) { + // Restore, so subsequent requires of node_modules won't have their source-maps read + setSourceMapsState(priorSourceMapsState ?? { + enabled: false + }); + // Node parses source maps eagerly during _compile, so the file can be removed now. + try { + _nodeFs().default.unlinkSync(mapPath); + } catch { + /* noop */ + } + } } } const hasStripTypeScriptTypes = typeof nodeModule().stripTypeScriptTypes === 'function'; diff --git a/packages/@expo/require-utils/build/load.js.map b/packages/@expo/require-utils/build/load.js.map index 86741ffe2c4100..e9ad0d04a7ba6e 100644 --- a/packages/@expo/require-utils/build/load.js.map +++ b/packages/@expo/require-utils/build/load.js.map @@ -1 +1 @@ -{"version":3,"file":"load.js","names":["_nodeFs","data","_interopRequireDefault","require","nodeModule","_interopRequireWildcard","_nodePath","_nodeUrl","_codeframe","_transform","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_ts","loadTypescript","undefined","error","code","parent","module","tsExtensionMapping","maybeReadFileSync","filename","fs","readFileSync","toFormat","isLegacy","endsWith","toRealDirname","filePath","normalized","path","resolve","realpathSync","dirname","compileModule","opts","format","prependPaths","paths","basePath","nodeModulePaths","_nodeModulePaths","mod","assign","Module","_compile","loaded","cache","children","splice","indexOf","hasStripTypeScriptTypes","stripTypeScriptTypes","evalModule","inputCode","inputFilename","diagnostic","ts","ModuleKind","CommonJS","ESNext","Preserve","output","transpileModule","fileName","reportDiagnostics","compilerOptions","moduleResolution","ModuleResolutionKind","Bundler","verbatimModuleSyntax","target","ScriptTarget","newLine","NewLineKind","LineFeed","inlineSourceMap","esModuleInterop","outputText","diagnostics","length","mode","sourceMap","ext","extname","inputExt","join","basename","toCommonJS","exports","diagnosticError","formatDiagnostic","annotateError","requireOrImport","Promise","isAbsolute","url","pathToFileURL","toString","then","s","loadModule","loadModuleSync","isTypeScript"],"sources":["../src/load.ts"],"sourcesContent":["import fs from 'node:fs';\nimport * as nodeModule from 'node:module';\nimport path from 'node:path';\nimport url from 'node:url';\nimport type * as ts from 'typescript';\n\nimport { annotateError, formatDiagnostic } from './codeframe';\nimport { toCommonJS } from './transform';\n\ndeclare module 'node:module' {\n export function _nodeModulePaths(base: string): readonly string[];\n}\n\ndeclare global {\n namespace NodeJS {\n export interface Module {\n _compile(\n code: string,\n filename: string,\n format?: 'module' | 'commonjs' | 'commonjs-typescript' | 'module-typescript' | 'typescript'\n ): unknown;\n }\n }\n}\n\nlet _ts: typeof import('typescript') | null | undefined;\nfunction loadTypescript() {\n if (_ts === undefined) {\n try {\n _ts = require('typescript');\n } catch (error: any) {\n if (error.code !== 'MODULE_NOT_FOUND') {\n throw error;\n } else {\n _ts = null;\n }\n }\n }\n return _ts;\n}\n\nconst parent = module;\n\nconst tsExtensionMapping: Record = {\n '.ts': '.js',\n '.cts': '.cjs',\n '.mts': '.mjs',\n};\n\nfunction maybeReadFileSync(filename: string) {\n try {\n return fs.readFileSync(filename, 'utf8');\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n return null;\n }\n throw error;\n }\n}\n\ntype Format = 'commonjs' | 'module' | 'module-typescript' | 'commonjs-typescript' | 'typescript';\n\nfunction toFormat(filename: string, isLegacy: true): Format;\nfunction toFormat(filename: string, isLegacy: false): Format | null;\nfunction toFormat(filename: string, isLegacy: boolean): Format | null {\n if (filename.endsWith('.cjs')) {\n return 'commonjs';\n } else if (filename.endsWith('.mjs')) {\n return 'module';\n } else if (filename.endsWith('.js')) {\n return isLegacy ? 'commonjs' : null;\n } else if (filename.endsWith('.mts')) {\n return 'module-typescript';\n } else if (filename.endsWith('.cts')) {\n return 'commonjs-typescript';\n } else if (filename.endsWith('.ts')) {\n return isLegacy ? 'commonjs-typescript' : 'typescript';\n } else {\n return null;\n }\n}\n\nexport interface ModuleOptions {\n paths?: string[];\n}\n\nfunction toRealDirname(filePath: string): string {\n let normalized = path.resolve(filePath);\n // Try resolving the filename itself first\n try {\n normalized = fs.realpathSync(normalized);\n return path.dirname(normalized);\n } catch (error: any) {\n normalized = path.dirname(normalized);\n // If we're getting another error than an ENOENT, return the dirname unchanged\n if (error?.code !== 'ENOENT') {\n return normalized;\n }\n }\n // Alternatively, if it's a fake path, resolve the directory directly instead\n try {\n return fs.realpathSync(normalized);\n } catch {\n return normalized;\n }\n}\n\nfunction compileModule(code: string, filename: string, opts: ModuleOptions) {\n const format = toFormat(filename, false);\n const prependPaths = opts.paths ?? [];\n // See: https://github.com/nodejs/node/blob/ff080948666f28fbd767548d26bea034d30bc277/lib/internal/modules/cjs/loader.js#L767\n // If we get a symlinked path instead of the realpath, we assume the realpath is needed for Node module resolution\n const basePath = toRealDirname(filename);\n const nodeModulePaths = nodeModule._nodeModulePaths(basePath);\n const paths = [...prependPaths, ...nodeModulePaths];\n try {\n const mod = Object.assign(new nodeModule.Module(filename, parent), { filename, paths });\n mod._compile(code, filename, format != null ? format : undefined);\n mod.loaded = true;\n require.cache[filename] = mod;\n parent?.children?.splice(parent.children.indexOf(mod), 1);\n return mod;\n } catch (error: any) {\n delete require.cache[filename];\n throw error;\n }\n}\n\nconst hasStripTypeScriptTypes = typeof nodeModule.stripTypeScriptTypes === 'function';\n\nfunction evalModule(\n code: string,\n filename: string,\n opts: ModuleOptions = {},\n format: Format = toFormat(filename, true)\n) {\n let inputCode = code;\n let inputFilename = filename;\n let diagnostic: ts.Diagnostic | undefined;\n if (\n format === 'typescript' ||\n format === 'module-typescript' ||\n format === 'commonjs-typescript'\n ) {\n const ts = loadTypescript();\n\n if (ts) {\n let module: ts.ModuleKind;\n if (format === 'commonjs-typescript') {\n module = ts.ModuleKind.CommonJS;\n } else if (format === 'module-typescript') {\n module = ts.ModuleKind.ESNext;\n } else {\n // NOTE(@kitten): We can \"preserve\" the output, meaning, it can either be ESM or CJS\n // and stop TypeScript from either transpiling it to CommonJS or adding an `export {}`\n // if no exports are used. This allows the user to choose if this file is CJS or ESM\n // (but not to mix both)\n module = ts.ModuleKind.Preserve;\n }\n const output = ts.transpileModule(code, {\n fileName: filename,\n reportDiagnostics: true,\n compilerOptions: {\n module,\n moduleResolution: ts.ModuleResolutionKind.Bundler,\n // `verbatimModuleSyntax` needs to be off, to erase as many imports as possible\n verbatimModuleSyntax: false,\n target: ts.ScriptTarget.ESNext,\n newLine: ts.NewLineKind.LineFeed,\n inlineSourceMap: true,\n esModuleInterop: true,\n },\n });\n inputCode = output?.outputText || inputCode;\n if (output?.diagnostics?.length) {\n diagnostic = output.diagnostics[0];\n }\n }\n\n if (hasStripTypeScriptTypes && inputCode === code) {\n // This may throw its own error, but this contains a code-frame already\n inputCode = nodeModule.stripTypeScriptTypes(code, {\n mode: 'transform',\n sourceMap: true,\n });\n }\n\n if (inputCode !== code) {\n const ext = path.extname(filename);\n const inputExt = tsExtensionMapping[ext] ?? ext;\n if (inputExt !== ext) {\n inputFilename = path.join(path.dirname(filename), path.basename(filename, ext) + inputExt);\n }\n }\n } else if (format === 'commonjs') {\n inputCode = toCommonJS(filename, code);\n }\n\n try {\n const mod = compileModule(inputCode, inputFilename, opts);\n if (inputFilename !== filename) {\n require.cache[filename] = mod;\n }\n return mod.exports;\n } catch (error: any) {\n // If we have a diagnostic from TypeScript, we issue its error with a codeframe first,\n // since it's likely more useful than the eval error\n const diagnosticError = formatDiagnostic(diagnostic);\n if (diagnosticError) {\n throw diagnosticError;\n }\n throw annotateError(code, filename, error) ?? error;\n }\n}\n\nasync function requireOrImport(filename: string) {\n try {\n return require(filename);\n } catch {\n return await import(\n path.isAbsolute(filename) ? url.pathToFileURL(filename).toString() : filename\n );\n }\n}\n\nasync function loadModule(filename: string) {\n try {\n return await requireOrImport(filename);\n } catch (error: any) {\n if (error.code === 'ERR_UNKNOWN_FILE_EXTENSION' || error.code === 'MODULE_NOT_FOUND') {\n return loadModuleSync(filename);\n } else {\n throw error;\n }\n }\n}\n\n/** Require module or evaluate with TypeScript\n * NOTE: Requiring ESM has been added in all LTS versions (Node 20.19+, 22.12+, 24).\n * This already forms the minimum required Node version as of Expo SDK 54 */\nfunction loadModuleSync(filename: string) {\n const format = toFormat(filename, true);\n const isTypeScript =\n format === 'module-typescript' || format === 'commonjs-typescript' || format === 'typescript';\n try {\n if (format !== 'module' && !isTypeScript) {\n return require(filename);\n }\n } catch (error: any) {\n if (error.code === 'MODULE_NOT_FOUND') {\n throw error;\n } else if (format == null) {\n const code = maybeReadFileSync(filename);\n throw annotateError(code, filename, error) || error;\n }\n // We fallback to always evaluating the entrypoint module\n // This is out of safety, since we're not trusting the requiring ESM feature\n // and evaluating the module manually bypasses the error when it's flagged off\n }\n\n // Load from cache manually, if `loaded` is set and exports are defined, to avoid\n // double transform or double evaluation\n if (require.cache[filename]?.exports && require.cache[filename].loaded) {\n return require.cache[filename].exports;\n }\n\n const code = fs.readFileSync(filename, 'utf8');\n return evalModule(code, filename, {}, format);\n}\n\nexport { evalModule, loadModule, loadModuleSync };\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"load.js","names":["_nodeFs","data","_interopRequireDefault","require","nodeModule","_interopRequireWildcard","_nodeOs","_nodePath","_nodeUrl","_codeframe","_stacktrace","_transform","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_ts","loadTypescript","undefined","error","code","parent","module","tsExtensionMapping","maybeReadFileSync","filename","fs","readFileSync","toFormat","isLegacy","endsWith","toRealDirname","filePath","normalized","path","resolve","realpathSync","dirname","hasModuleSourceMapsSupport","setSourceMapsSupport","getSourceMapsState","getSourceMapsSupport","enabled","process","sourceMapsEnabled","setSourceMapsState","state","nodeModules","generatedCode","setSourceMapsEnabled","makeSourceMapTempPath","basename","queryIdx","search","slice","join","os","tmpdir","pid","stripSourceMappingURL","replace","compileModule","opts","format","prependPaths","paths","basePath","nodeModulePaths","_nodeModulePaths","inputCode","compileFilename","sourceMap","basePart","queryPart","isAbsolute","mapPath","priorSourceMapsState","isBun","writeFileSync","console","warn","message","installSourceMapStackTrace","mod","assign","Module","_compile","loaded","cache","children","splice","indexOf","unlinkSync","hasStripTypeScriptTypes","stripTypeScriptTypes","evalModule","inputFilename","diagnostic","ts","ModuleKind","CommonJS","ESNext","Preserve","output","transpileModule","fileName","reportDiagnostics","compilerOptions","moduleResolution","ModuleResolutionKind","Bundler","verbatimModuleSyntax","target","ScriptTarget","newLine","NewLineKind","LineFeed","inlineSourceMap","esModuleInterop","outputText","diagnostics","length","mode","ext","extname","inputExt","toCommonJS","exports","diagnosticError","formatDiagnostic","annotateError","requireOrImport","Promise","url","pathToFileURL","toString","then","s","loadModule","loadModuleSync","isTypeScript"],"sources":["../src/load.ts"],"sourcesContent":["import fs from 'node:fs';\nimport * as nodeModule from 'node:module';\nimport os from 'node:os';\nimport path from 'node:path';\nimport url from 'node:url';\nimport type * as ts from 'typescript';\n\nimport { annotateError, formatDiagnostic } from './codeframe';\nimport { installSourceMapStackTrace } from './stacktrace';\nimport { toCommonJS } from './transform';\n\ndeclare module 'node:module' {\n export function _nodeModulePaths(base: string): readonly string[];\n}\n\ndeclare global {\n namespace NodeJS {\n export interface Module {\n _compile(\n code: string,\n filename: string,\n format?: 'module' | 'commonjs' | 'commonjs-typescript' | 'module-typescript' | 'typescript'\n ): unknown;\n }\n export interface Process {\n isBun?: boolean;\n }\n }\n}\n\nlet _ts: typeof import('typescript') | null | undefined;\nfunction loadTypescript() {\n if (_ts === undefined) {\n try {\n _ts = require('typescript');\n } catch (error: any) {\n if (error.code !== 'MODULE_NOT_FOUND') {\n throw error;\n } else {\n _ts = null;\n }\n }\n }\n return _ts;\n}\n\nconst parent = module;\n\nconst tsExtensionMapping: Record = {\n '.ts': '.js',\n '.cts': '.cjs',\n '.mts': '.mjs',\n};\n\nfunction maybeReadFileSync(filename: string) {\n try {\n return fs.readFileSync(filename, 'utf8');\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n return null;\n }\n throw error;\n }\n}\n\ntype Format = 'commonjs' | 'module' | 'module-typescript' | 'commonjs-typescript' | 'typescript';\n\nfunction toFormat(filename: string, isLegacy: true): Format;\nfunction toFormat(filename: string, isLegacy: false): Format | null;\nfunction toFormat(filename: string, isLegacy: boolean): Format | null {\n if (filename.endsWith('.cjs')) {\n return 'commonjs';\n } else if (filename.endsWith('.mjs')) {\n return 'module';\n } else if (filename.endsWith('.js')) {\n return isLegacy ? 'commonjs' : null;\n } else if (filename.endsWith('.mts')) {\n return 'module-typescript';\n } else if (filename.endsWith('.cts')) {\n return 'commonjs-typescript';\n } else if (filename.endsWith('.ts')) {\n return isLegacy ? 'commonjs-typescript' : 'typescript';\n } else {\n return null;\n }\n}\n\nexport interface ModuleOptions {\n paths?: string[];\n sourceMap?: string;\n}\n\nfunction toRealDirname(filePath: string): string {\n let normalized = path.resolve(filePath);\n // Try resolving the filename itself first\n try {\n normalized = fs.realpathSync(normalized);\n return path.dirname(normalized);\n } catch (error: any) {\n normalized = path.dirname(normalized);\n // If we're getting another error than an ENOENT, return the dirname unchanged\n if (error?.code !== 'ENOENT') {\n return normalized;\n }\n }\n // Alternatively, if it's a fake path, resolve the directory directly instead\n try {\n return fs.realpathSync(normalized);\n } catch {\n return normalized;\n }\n}\n\nconst hasModuleSourceMapsSupport = typeof nodeModule.setSourceMapsSupport === 'function';\n\ninterface SourceMapsState {\n enabled: boolean;\n nodeModules?: boolean;\n generatedCode?: boolean;\n}\n\nfunction getSourceMapsState(): SourceMapsState {\n return typeof nodeModule.getSourceMapsSupport === 'function'\n ? nodeModule.getSourceMapsSupport()\n : { enabled: !!process.sourceMapsEnabled };\n}\n\nfunction setSourceMapsState(state: SourceMapsState): void {\n if (hasModuleSourceMapsSupport) {\n nodeModule.setSourceMapsSupport(state.enabled, {\n nodeModules: state.nodeModules ?? false,\n generatedCode: state.generatedCode ?? false,\n });\n } else {\n process.setSourceMapsEnabled(state.enabled);\n }\n}\n\nfunction makeSourceMapTempPath(filename: string) {\n let basename = path.basename(filename);\n const queryIdx = basename.search(/[?#]/);\n if (queryIdx >= 0) {\n basename = basename.slice(0, queryIdx);\n }\n return path.join(os.tmpdir(), `require-utils-${process.pid}-${basename}.map`);\n}\n\nfunction stripSourceMappingURL(code: string): string {\n return code.replace(/^[ \\t]*\\/\\/[#@][ \\t]+sourceMappingURL=.*$/gm, '');\n}\n\nfunction compileModule(code: string, filename: string, opts: ModuleOptions) {\n const format = toFormat(filename, false);\n const prependPaths = opts.paths ?? [];\n // See: https://github.com/nodejs/node/blob/ff080948666f28fbd767548d26bea034d30bc277/lib/internal/modules/cjs/loader.js#L767\n // If we get a symlinked path instead of the realpath, we assume the realpath is needed for Node module resolution\n const basePath = toRealDirname(filename);\n const nodeModulePaths = nodeModule._nodeModulePaths(basePath);\n const paths = [...prependPaths, ...nodeModulePaths];\n\n let inputCode = code;\n\n // We may get a Metro SSR relative path here, which isn't a valid absolute path, and we need to normalize\n // the filename before proceeding\n let compileFilename = filename;\n if (opts.sourceMap) {\n const queryIdx = filename.search(/[?#]/);\n const basePart = queryIdx >= 0 ? filename.slice(0, queryIdx) : filename;\n const queryPart = queryIdx >= 0 ? filename.slice(queryIdx) : '';\n if (!path.isAbsolute(basePart)) {\n compileFilename = path.resolve(basePart) + queryPart;\n }\n }\n\n let mapPath: string | undefined;\n let priorSourceMapsState: SourceMapsState | undefined;\n if (opts.sourceMap && !process.isBun) {\n try {\n mapPath = makeSourceMapTempPath(compileFilename);\n fs.writeFileSync(mapPath, opts.sourceMap);\n } catch (error: any) {\n mapPath = undefined;\n // If we fail to write the source map, we can still continue without it, but log a warning since it's likely a misconfiguration\n console.warn(\n `Warning: Failed to write source map for ${filename} to ${mapPath}. Source maps will be unavailable for this module.\\n${error?.message || error}`\n );\n }\n\n if (mapPath) {\n inputCode = stripSourceMappingURL(code);\n // NOTE This needs to be a plain absolute path because Node rejects file: URLs\n inputCode += `\\n//# sourceMappingURL=${mapPath}`;\n\n priorSourceMapsState = getSourceMapsState();\n installSourceMapStackTrace();\n setSourceMapsState({ enabled: true, nodeModules: true });\n }\n }\n\n try {\n const mod = Object.assign(new nodeModule.Module(compileFilename, parent), {\n filename: compileFilename,\n paths,\n });\n mod._compile(inputCode, compileFilename, format != null ? format : undefined);\n mod.loaded = true;\n require.cache[compileFilename] = mod;\n if (compileFilename !== filename) {\n require.cache[filename] = mod;\n }\n parent?.children?.splice(parent.children.indexOf(mod), 1);\n return mod;\n } catch (error: any) {\n delete require.cache[compileFilename];\n if (compileFilename !== filename) {\n delete require.cache[filename];\n }\n throw error;\n } finally {\n if (mapPath) {\n // Restore, so subsequent requires of node_modules won't have their source-maps read\n setSourceMapsState(priorSourceMapsState ?? { enabled: false });\n // Node parses source maps eagerly during _compile, so the file can be removed now.\n try {\n fs.unlinkSync(mapPath);\n } catch {\n /* noop */\n }\n }\n }\n}\n\nconst hasStripTypeScriptTypes = typeof nodeModule.stripTypeScriptTypes === 'function';\n\nfunction evalModule(\n code: string,\n filename: string,\n opts: ModuleOptions = {},\n format: Format = toFormat(filename, true)\n) {\n let inputCode = code;\n let inputFilename = filename;\n let diagnostic: ts.Diagnostic | undefined;\n if (\n format === 'typescript' ||\n format === 'module-typescript' ||\n format === 'commonjs-typescript'\n ) {\n const ts = loadTypescript();\n\n if (ts) {\n let module: ts.ModuleKind;\n if (format === 'commonjs-typescript') {\n module = ts.ModuleKind.CommonJS;\n } else if (format === 'module-typescript') {\n module = ts.ModuleKind.ESNext;\n } else {\n // NOTE(@kitten): We can \"preserve\" the output, meaning, it can either be ESM or CJS\n // and stop TypeScript from either transpiling it to CommonJS or adding an `export {}`\n // if no exports are used. This allows the user to choose if this file is CJS or ESM\n // (but not to mix both)\n module = ts.ModuleKind.Preserve;\n }\n const output = ts.transpileModule(code, {\n fileName: filename,\n reportDiagnostics: true,\n compilerOptions: {\n module,\n moduleResolution: ts.ModuleResolutionKind.Bundler,\n // `verbatimModuleSyntax` needs to be off, to erase as many imports as possible\n verbatimModuleSyntax: false,\n target: ts.ScriptTarget.ESNext,\n newLine: ts.NewLineKind.LineFeed,\n inlineSourceMap: true,\n esModuleInterop: true,\n },\n });\n inputCode = output?.outputText || inputCode;\n if (output?.diagnostics?.length) {\n diagnostic = output.diagnostics[0];\n }\n }\n\n if (hasStripTypeScriptTypes && inputCode === code) {\n // This may throw its own error, but this contains a code-frame already\n inputCode = nodeModule.stripTypeScriptTypes(code, {\n mode: 'transform',\n sourceMap: true,\n });\n }\n\n if (inputCode !== code) {\n const ext = path.extname(filename);\n const inputExt = tsExtensionMapping[ext] ?? ext;\n if (inputExt !== ext) {\n inputFilename = path.join(path.dirname(filename), path.basename(filename, ext) + inputExt);\n }\n }\n } else if (format === 'commonjs') {\n inputCode = toCommonJS(filename, code);\n }\n\n try {\n const mod = compileModule(inputCode, inputFilename, opts);\n if (inputFilename !== filename) {\n require.cache[filename] = mod;\n }\n return mod.exports;\n } catch (error: any) {\n // If we have a diagnostic from TypeScript, we issue its error with a codeframe first,\n // since it's likely more useful than the eval error\n const diagnosticError = formatDiagnostic(diagnostic);\n if (diagnosticError) {\n throw diagnosticError;\n }\n throw annotateError(code, filename, error) ?? error;\n }\n}\n\nasync function requireOrImport(filename: string) {\n try {\n return require(filename);\n } catch {\n return await import(\n path.isAbsolute(filename) ? url.pathToFileURL(filename).toString() : filename\n );\n }\n}\n\nasync function loadModule(filename: string) {\n try {\n return await requireOrImport(filename);\n } catch (error: any) {\n if (error.code === 'ERR_UNKNOWN_FILE_EXTENSION' || error.code === 'MODULE_NOT_FOUND') {\n return loadModuleSync(filename);\n } else {\n throw error;\n }\n }\n}\n\n/** Require module or evaluate with TypeScript\n * NOTE: Requiring ESM has been added in all LTS versions (Node 20.19+, 22.12+, 24).\n * This already forms the minimum required Node version as of Expo SDK 54 */\nfunction loadModuleSync(filename: string) {\n const format = toFormat(filename, true);\n const isTypeScript =\n format === 'module-typescript' || format === 'commonjs-typescript' || format === 'typescript';\n try {\n if (format !== 'module' && !isTypeScript) {\n return require(filename);\n }\n } catch (error: any) {\n if (error.code === 'MODULE_NOT_FOUND') {\n throw error;\n } else if (format == null) {\n const code = maybeReadFileSync(filename);\n throw annotateError(code, filename, error) || error;\n }\n // We fallback to always evaluating the entrypoint module\n // This is out of safety, since we're not trusting the requiring ESM feature\n // and evaluating the module manually bypasses the error when it's flagged off\n }\n\n // Load from cache manually, if `loaded` is set and exports are defined, to avoid\n // double transform or double evaluation\n if (require.cache[filename]?.exports && require.cache[filename].loaded) {\n return require.cache[filename].exports;\n }\n\n const code = fs.readFileSync(filename, 'utf8');\n return evalModule(code, filename, {}, format);\n}\n\nexport { evalModule, loadModule, loadModuleSync };\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/packages/@expo/require-utils/build/stacktrace.d.ts b/packages/@expo/require-utils/build/stacktrace.d.ts new file mode 100644 index 00000000000000..20de1f72cf64fe --- /dev/null +++ b/packages/@expo/require-utils/build/stacktrace.d.ts @@ -0,0 +1,42 @@ +/** + * Install a JS-level `Error.prepareStackTrace` that symbolicates frames using Node's + * source map cache (`module.findSourceMap`). Idempotent — calling more than once is a no-op. + * + * Background: Node's automatic stack symbolication is gated on source maps being enabled. + * `compileModule` toggles source maps on only during `_compile` (to keep the cache scoped + * to our bundles rather than every `require()`'d package), so by the time an error from + * the bundle is formatted, automatic symbolication is off. This shim restores it. + * + * Implementation closely follows + * https://github.com/evanw/node-source-map-support/blob/master/source-map-support.js, + * the canonical reference for this kind of `prepareStackTrace` shim: + * + * - Source-mapped frames are wrapped in cloned plain-object call sites that override + * `getFileName`, `getLineNumber`, `getColumnNumber`, `getFunctionName`, + * `getScriptNameOrSourceURL`, and `toString`. V8's native + * `CallSite.prototype.toString` is implemented in C++ and reads internal slots + * directly, ignoring overridden JS getters; so we reimplement `toString` in JS + * (`callSiteToString`) on the clone, which consults the overridden getters. + * + * - The stack is processed in reverse so we can use the source map's `name` from the + * *next* frame (the caller side) when overriding the *current* frame's function + * name. Source map V3's `name` at a position identifies the symbol being *called* + * at that position — i.e. the function whose body lives at the next-out frame — + * which is more accurate than V8's `getFunctionName()` for that frame. + * + * - Non-source-mapped frames pass through unchanged; their native + * `CallSite.prototype.toString` is invoked via template-literal coercion in + * `prepareStackTrace`. This preserves V8's canonical formatting for every frame + * variant we don't symbolicate (top-level `at file:line:col`, `at TypeName.method`, + * `at new Class`, `at fn [as alias]`, native frames, eval frames, etc.). + */ +export declare function installSourceMapStackTrace(): void; +/** + * V8's `CallSite.prototype.toString`, reimplemented in JS so it consults overridden + * getters on a cloned call site. Mirrors the C++ implementation in V8's `messages.cc` + * (which is also what `evanw/node-source-map-support` ports). + * + * Exported for tests, which need a working `toString` on mock plain-object call sites + * (real V8 CallSites have one natively; plain objects don't). + */ +export declare function callSiteToString(this: NodeJS.CallSite): string; diff --git a/packages/@expo/require-utils/build/stacktrace.js b/packages/@expo/require-utils/build/stacktrace.js new file mode 100644 index 00000000000000..b564091b6bb7fc --- /dev/null +++ b/packages/@expo/require-utils/build/stacktrace.js @@ -0,0 +1,246 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.callSiteToString = callSiteToString; +exports.installSourceMapStackTrace = installSourceMapStackTrace; +function nodeModule() { + const data = _interopRequireWildcard(require("node:module")); + nodeModule = function () { + return data; + }; + return data; +} +function _nodeUrl() { + const data = _interopRequireDefault(require("node:url")); + _nodeUrl = function () { + return data; + }; + return data; +} +function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } +function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } +// MIT License, Copyright (c) 2014 Evan Wallace +// Derived from: https://github.com/evanw/node-source-map-support/blob/7b5b81e/source-map-support.js + +let stackTraceInstalled = false; + +/** + * Install a JS-level `Error.prepareStackTrace` that symbolicates frames using Node's + * source map cache (`module.findSourceMap`). Idempotent — calling more than once is a no-op. + * + * Background: Node's automatic stack symbolication is gated on source maps being enabled. + * `compileModule` toggles source maps on only during `_compile` (to keep the cache scoped + * to our bundles rather than every `require()`'d package), so by the time an error from + * the bundle is formatted, automatic symbolication is off. This shim restores it. + * + * Implementation closely follows + * https://github.com/evanw/node-source-map-support/blob/master/source-map-support.js, + * the canonical reference for this kind of `prepareStackTrace` shim: + * + * - Source-mapped frames are wrapped in cloned plain-object call sites that override + * `getFileName`, `getLineNumber`, `getColumnNumber`, `getFunctionName`, + * `getScriptNameOrSourceURL`, and `toString`. V8's native + * `CallSite.prototype.toString` is implemented in C++ and reads internal slots + * directly, ignoring overridden JS getters; so we reimplement `toString` in JS + * (`callSiteToString`) on the clone, which consults the overridden getters. + * + * - The stack is processed in reverse so we can use the source map's `name` from the + * *next* frame (the caller side) when overriding the *current* frame's function + * name. Source map V3's `name` at a position identifies the symbol being *called* + * at that position — i.e. the function whose body lives at the next-out frame — + * which is more accurate than V8's `getFunctionName()` for that frame. + * + * - Non-source-mapped frames pass through unchanged; their native + * `CallSite.prototype.toString` is invoked via template-literal coercion in + * `prepareStackTrace`. This preserves V8's canonical formatting for every frame + * variant we don't symbolicate (top-level `at file:line:col`, `at TypeName.method`, + * `at new Class`, `at fn [as alias]`, native frames, eval frames, etc.). + */ +function installSourceMapStackTrace() { + if (stackTraceInstalled) { + return; + } else { + stackTraceInstalled = true; + } + Error.prepareStackTrace = (error, callSites) => { + const errorString = formatErrorHeader(error); + if (callSites.length === 0) { + return errorString; + } + const state = { + nextPosition: null, + curPosition: null + }; + const lines = []; + for (let i = callSites.length - 1; i >= 0; i--) { + lines.push('\n at ' + wrapCallSite(callSites[i], state)); + state.nextPosition = state.curPosition; + } + lines.reverse(); + return errorString + lines.join(''); + }; +} +function formatErrorHeader(error) { + // Match V8's default `Error.prototype.toString` semantics. We can't call it directly + // because `error` may not actually be an Error instance. + const name = error?.name === undefined ? 'Error' : String(error.name); + const message = error?.message === undefined ? '' : String(error.message); + if (!name) return message; + if (!message) return name; + return `${name}: ${message}`; +} +function wrapCallSite(site, state) { + if (site.isNative()) { + state.curPosition = null; + return String(site); + } + + // Most call sites surface their script identity via `getFileName()`. Code passed to + // `eval()` (or otherwise compiled with a `//# sourceURL=...` directive) only exposes + // it via `getScriptNameOrSourceURL()` — falling through to that catches those cases. + const scriptName = site.getFileName() ?? site.getScriptNameOrSourceURL(); + if (!scriptName) { + state.curPosition = null; + return String(site); + } + const lineNumber = site.getLineNumber(); + const columnNumber = site.getColumnNumber(); + if (lineNumber == null || columnNumber == null) { + state.curPosition = null; + return String(site); + } + const sm = nodeModule().findSourceMap(scriptName); + if (!sm) { + state.curPosition = null; + return String(site); + } + const entry = sm.findEntry(lineNumber - 1, columnNumber - 1); + if (!entry || !('originalSource' in entry) || !entry.originalSource) { + state.curPosition = null; + return String(site); + } + + // `originalSource` is a `file://` URL. `fileURLToPath` correctly handles drive letters + // and percent-encoded characters; a naive `file://` strip would yield `/C:/foo/bar.ts` + // on Windows. + const originalSource = entry.originalSource.startsWith('file://') ? _nodeUrl().default.fileURLToPath(entry.originalSource) : entry.originalSource; + + // Node's runtime exposes a `name` field on the source map entry even though + // `@types/node` omits it from `SourceMapping`. Read it defensively. + const mappedName = entry.name ?? null; + const position = { + source: originalSource, + line: entry.originalLine + 1, + column: entry.originalColumn + 1, + name: mappedName + }; + state.curPosition = position; + + // Build a wrapped call site whose JS getters and `toString` agree on the mapped + // position. The function-name override consults `state.nextPosition` first because + // a source map's `name` at a position names the symbol being *called*, which lives + // one frame outward. + const wrapped = cloneCallSite(site); + const originalGetFunctionName = wrapped.getFunctionName; + wrapped.getFunctionName = function () { + const nameFromNext = state.nextPosition?.name; + if (nameFromNext) return nameFromNext; + return originalGetFunctionName(); + }; + wrapped.getFileName = () => position.source; + wrapped.getLineNumber = () => position.line; + wrapped.getColumnNumber = () => position.column; + wrapped.getScriptNameOrSourceURL = () => position.source; + return String(wrapped); +} +function cloneCallSite(site) { + // We need a plain object whose JS getters mirror the original site's, but whose + // `toString` we can replace. Subclassing or proxying the native `CallSite` doesn't + // work: `CallSite.prototype.toString` is implemented in C++ and rejects any receiver + // that isn't the original V8-tagged object (`Method toString called on incompatible + // receiver`). The only working approach is to build a plain object that re-exposes + // the `is*`/`get*` methods (bound to the original) and carries our JS toString. + const clone = {}; + const proto = Object.getPrototypeOf(site); + for (const name of Object.getOwnPropertyNames(proto)) { + const value = site[name]; + if (/^(?:is|get)/.test(name) && typeof value === 'function') { + clone[name] = value.bind(site); + } else { + clone[name] = value; + } + } + clone.toString = callSiteToString; + return clone; +} + +/** + * V8's `CallSite.prototype.toString`, reimplemented in JS so it consults overridden + * getters on a cloned call site. Mirrors the C++ implementation in V8's `messages.cc` + * (which is also what `evanw/node-source-map-support` ports). + * + * Exported for tests, which need a working `toString` on mock plain-object call sites + * (real V8 CallSites have one natively; plain objects don't). + */ +function callSiteToString() { + let fileLocation = ''; + if (this.isNative()) { + fileLocation = 'native'; + } else { + const scriptName = this.getScriptNameOrSourceURL(); + if (!scriptName && this.isEval()) { + fileLocation = (this.getEvalOrigin() ?? '') + ', '; + } + if (scriptName) { + fileLocation += scriptName; + } else { + fileLocation += ''; + } + const lineNumber = this.getLineNumber(); + if (lineNumber != null) { + fileLocation += ':' + lineNumber; + const columnNumber = this.getColumnNumber(); + if (columnNumber) { + fileLocation += ':' + columnNumber; + } + } + } + let line = ''; + const functionName = this.getFunctionName(); + let addSuffix = true; + const isConstructor = this.isConstructor(); + const isMethodCall = !(this.isToplevel() || isConstructor); + if (isMethodCall) { + let typeName = this.getTypeName(); + // Older Node versions can return `[object Object]` here; normalize to `null`. + if (typeName === '[object Object]') { + typeName = 'null'; + } + const methodName = this.getMethodName(); + if (functionName) { + if (typeName && functionName.indexOf(typeName) !== 0) { + line += typeName + '.'; + } + line += functionName; + if (methodName && functionName.indexOf('.' + methodName) !== functionName.length - methodName.length - 1) { + line += ' [as ' + methodName + ']'; + } + } else { + line += typeName + '.' + (methodName ?? ''); + } + } else if (isConstructor) { + line += 'new ' + (functionName ?? ''); + } else if (functionName) { + line += functionName; + } else { + line += fileLocation; + addSuffix = false; + } + if (addSuffix) { + line += ' (' + fileLocation + ')'; + } + return line; +} +//# sourceMappingURL=stacktrace.js.map \ No newline at end of file diff --git a/packages/@expo/require-utils/build/stacktrace.js.map b/packages/@expo/require-utils/build/stacktrace.js.map new file mode 100644 index 00000000000000..d716e0c8436c37 --- /dev/null +++ b/packages/@expo/require-utils/build/stacktrace.js.map @@ -0,0 +1 @@ +{"version":3,"file":"stacktrace.js","names":["nodeModule","data","_interopRequireWildcard","require","_nodeUrl","_interopRequireDefault","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","stackTraceInstalled","installSourceMapStackTrace","Error","prepareStackTrace","error","callSites","errorString","formatErrorHeader","length","state","nextPosition","curPosition","lines","push","wrapCallSite","reverse","join","name","undefined","String","message","site","isNative","scriptName","getFileName","getScriptNameOrSourceURL","lineNumber","getLineNumber","columnNumber","getColumnNumber","sm","findSourceMap","entry","findEntry","originalSource","startsWith","url","fileURLToPath","mappedName","position","source","line","originalLine","column","originalColumn","wrapped","cloneCallSite","originalGetFunctionName","getFunctionName","nameFromNext","clone","proto","getPrototypeOf","getOwnPropertyNames","value","test","bind","toString","callSiteToString","fileLocation","isEval","getEvalOrigin","functionName","addSuffix","isConstructor","isMethodCall","isToplevel","typeName","getTypeName","methodName","getMethodName","indexOf"],"sources":["../src/stacktrace.ts"],"sourcesContent":["// MIT License, Copyright (c) 2014 Evan Wallace\n// Derived from: https://github.com/evanw/node-source-map-support/blob/7b5b81e/source-map-support.js\n\nimport * as nodeModule from 'node:module';\nimport url from 'node:url';\n\nlet stackTraceInstalled = false;\n\n/**\n * Install a JS-level `Error.prepareStackTrace` that symbolicates frames using Node's\n * source map cache (`module.findSourceMap`). Idempotent — calling more than once is a no-op.\n *\n * Background: Node's automatic stack symbolication is gated on source maps being enabled.\n * `compileModule` toggles source maps on only during `_compile` (to keep the cache scoped\n * to our bundles rather than every `require()`'d package), so by the time an error from\n * the bundle is formatted, automatic symbolication is off. This shim restores it.\n *\n * Implementation closely follows\n * https://github.com/evanw/node-source-map-support/blob/master/source-map-support.js,\n * the canonical reference for this kind of `prepareStackTrace` shim:\n *\n * - Source-mapped frames are wrapped in cloned plain-object call sites that override\n * `getFileName`, `getLineNumber`, `getColumnNumber`, `getFunctionName`,\n * `getScriptNameOrSourceURL`, and `toString`. V8's native\n * `CallSite.prototype.toString` is implemented in C++ and reads internal slots\n * directly, ignoring overridden JS getters; so we reimplement `toString` in JS\n * (`callSiteToString`) on the clone, which consults the overridden getters.\n *\n * - The stack is processed in reverse so we can use the source map's `name` from the\n * *next* frame (the caller side) when overriding the *current* frame's function\n * name. Source map V3's `name` at a position identifies the symbol being *called*\n * at that position — i.e. the function whose body lives at the next-out frame —\n * which is more accurate than V8's `getFunctionName()` for that frame.\n *\n * - Non-source-mapped frames pass through unchanged; their native\n * `CallSite.prototype.toString` is invoked via template-literal coercion in\n * `prepareStackTrace`. This preserves V8's canonical formatting for every frame\n * variant we don't symbolicate (top-level `at file:line:col`, `at TypeName.method`,\n * `at new Class`, `at fn [as alias]`, native frames, eval frames, etc.).\n */\nexport function installSourceMapStackTrace(): void {\n if (stackTraceInstalled) {\n return;\n } else {\n stackTraceInstalled = true;\n }\n Error.prepareStackTrace = (error, callSites) => {\n const errorString = formatErrorHeader(error);\n if (callSites.length === 0) {\n return errorString;\n }\n const state: WalkState = { nextPosition: null, curPosition: null };\n const lines: string[] = [];\n for (let i = callSites.length - 1; i >= 0; i--) {\n lines.push('\\n at ' + wrapCallSite(callSites[i]!, state));\n state.nextPosition = state.curPosition;\n }\n lines.reverse();\n return errorString + lines.join('');\n };\n}\n\nfunction formatErrorHeader(error: unknown): string {\n // Match V8's default `Error.prototype.toString` semantics. We can't call it directly\n // because `error` may not actually be an Error instance.\n const name =\n (error as { name?: unknown })?.name === undefined\n ? 'Error'\n : String((error as { name: unknown }).name);\n const message =\n (error as { message?: unknown })?.message === undefined\n ? ''\n : String((error as { message: unknown }).message);\n if (!name) return message;\n if (!message) return name;\n return `${name}: ${message}`;\n}\n\ninterface MappedPosition {\n source: string;\n line: number;\n column: number; // 1-based\n name: string | null;\n}\n\ninterface WalkState {\n nextPosition: MappedPosition | null;\n curPosition: MappedPosition | null;\n}\n\nfunction wrapCallSite(site: NodeJS.CallSite, state: WalkState): string {\n if (site.isNative()) {\n state.curPosition = null;\n return String(site);\n }\n\n // Most call sites surface their script identity via `getFileName()`. Code passed to\n // `eval()` (or otherwise compiled with a `//# sourceURL=...` directive) only exposes\n // it via `getScriptNameOrSourceURL()` — falling through to that catches those cases.\n const scriptName = site.getFileName() ?? site.getScriptNameOrSourceURL();\n if (!scriptName) {\n state.curPosition = null;\n return String(site);\n }\n\n const lineNumber = site.getLineNumber();\n const columnNumber = site.getColumnNumber();\n if (lineNumber == null || columnNumber == null) {\n state.curPosition = null;\n return String(site);\n }\n\n const sm = nodeModule.findSourceMap(scriptName);\n if (!sm) {\n state.curPosition = null;\n return String(site);\n }\n\n const entry = sm.findEntry(lineNumber - 1, columnNumber - 1);\n if (!entry || !('originalSource' in entry) || !entry.originalSource) {\n state.curPosition = null;\n return String(site);\n }\n\n // `originalSource` is a `file://` URL. `fileURLToPath` correctly handles drive letters\n // and percent-encoded characters; a naive `file://` strip would yield `/C:/foo/bar.ts`\n // on Windows.\n const originalSource = entry.originalSource.startsWith('file://')\n ? url.fileURLToPath(entry.originalSource)\n : entry.originalSource;\n\n // Node's runtime exposes a `name` field on the source map entry even though\n // `@types/node` omits it from `SourceMapping`. Read it defensively.\n const mappedName = (entry as { name?: string }).name ?? null;\n\n const position: MappedPosition = {\n source: originalSource,\n line: entry.originalLine + 1,\n column: entry.originalColumn + 1,\n name: mappedName,\n };\n state.curPosition = position;\n\n // Build a wrapped call site whose JS getters and `toString` agree on the mapped\n // position. The function-name override consults `state.nextPosition` first because\n // a source map's `name` at a position names the symbol being *called*, which lives\n // one frame outward.\n const wrapped = cloneCallSite(site);\n const originalGetFunctionName = wrapped.getFunctionName;\n wrapped.getFunctionName = function () {\n const nameFromNext = state.nextPosition?.name;\n if (nameFromNext) return nameFromNext;\n return originalGetFunctionName();\n };\n wrapped.getFileName = () => position.source;\n wrapped.getLineNumber = () => position.line;\n wrapped.getColumnNumber = () => position.column;\n wrapped.getScriptNameOrSourceURL = () => position.source;\n return String(wrapped);\n}\n\ninterface ClonedCallSite extends NodeJS.CallSite {\n toString(): string;\n}\n\nfunction cloneCallSite(site: NodeJS.CallSite): ClonedCallSite {\n // We need a plain object whose JS getters mirror the original site's, but whose\n // `toString` we can replace. Subclassing or proxying the native `CallSite` doesn't\n // work: `CallSite.prototype.toString` is implemented in C++ and rejects any receiver\n // that isn't the original V8-tagged object (`Method toString called on incompatible\n // receiver`). The only working approach is to build a plain object that re-exposes\n // the `is*`/`get*` methods (bound to the original) and carries our JS toString.\n const clone = {} as Record;\n const proto = Object.getPrototypeOf(site) as Record;\n for (const name of Object.getOwnPropertyNames(proto)) {\n const value = (site as unknown as Record)[name];\n if (/^(?:is|get)/.test(name) && typeof value === 'function') {\n clone[name] = (value as (...args: unknown[]) => unknown).bind(site);\n } else {\n clone[name] = value;\n }\n }\n clone.toString = callSiteToString;\n return clone as unknown as ClonedCallSite;\n}\n\n/**\n * V8's `CallSite.prototype.toString`, reimplemented in JS so it consults overridden\n * getters on a cloned call site. Mirrors the C++ implementation in V8's `messages.cc`\n * (which is also what `evanw/node-source-map-support` ports).\n *\n * Exported for tests, which need a working `toString` on mock plain-object call sites\n * (real V8 CallSites have one natively; plain objects don't).\n */\nexport function callSiteToString(this: NodeJS.CallSite): string {\n let fileLocation = '';\n if (this.isNative()) {\n fileLocation = 'native';\n } else {\n const scriptName = this.getScriptNameOrSourceURL();\n if (!scriptName && this.isEval()) {\n fileLocation = (this.getEvalOrigin() ?? '') + ', ';\n }\n if (scriptName) {\n fileLocation += scriptName;\n } else {\n fileLocation += '';\n }\n const lineNumber = this.getLineNumber();\n if (lineNumber != null) {\n fileLocation += ':' + lineNumber;\n const columnNumber = this.getColumnNumber();\n if (columnNumber) {\n fileLocation += ':' + columnNumber;\n }\n }\n }\n\n let line = '';\n const functionName = this.getFunctionName();\n let addSuffix = true;\n const isConstructor = this.isConstructor();\n const isMethodCall = !(this.isToplevel() || isConstructor);\n\n if (isMethodCall) {\n let typeName: string | null = this.getTypeName();\n // Older Node versions can return `[object Object]` here; normalize to `null`.\n if (typeName === '[object Object]') {\n typeName = 'null';\n }\n const methodName = this.getMethodName();\n if (functionName) {\n if (typeName && functionName.indexOf(typeName) !== 0) {\n line += typeName + '.';\n }\n line += functionName;\n if (\n methodName &&\n functionName.indexOf('.' + methodName) !== functionName.length - methodName.length - 1\n ) {\n line += ' [as ' + methodName + ']';\n }\n } else {\n line += typeName + '.' + (methodName ?? '');\n }\n } else if (isConstructor) {\n line += 'new ' + (functionName ?? '');\n } else if (functionName) {\n line += functionName;\n } else {\n line += fileLocation;\n addSuffix = false;\n }\n\n if (addSuffix) {\n line += ' (' + fileLocation + ')';\n }\n return line;\n}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/packages/@expo/require-utils/src/__tests__/stacktrace-test.ts b/packages/@expo/require-utils/src/__tests__/stacktrace-test.ts new file mode 100644 index 00000000000000..077be81454f92c --- /dev/null +++ b/packages/@expo/require-utils/src/__tests__/stacktrace-test.ts @@ -0,0 +1,604 @@ +// `jest.spyOn` and direct property assignment on the `node:module` namespace import +// don't intercept the production module's call sites: with Babel's `__importStar` the +// two `import * as nodeModule` references are distinct wrapper objects. `jest.mock` +// replaces module resolution for all importers. +import * as nodeModule from 'node:module'; + +import { callSiteToString, installSourceMapStackTrace } from '../stacktrace'; + +jest.mock('node:module', () => ({ + ...jest.requireActual('node:module'), + findSourceMap: jest.fn(), +})); + +const findSourceMap = nodeModule.findSourceMap as unknown as jest.Mock; + +installSourceMapStackTrace(); +const prepareStackTrace = Error.prepareStackTrace as ( + error: unknown, + callSites: NodeJS.CallSite[] +) => string; + +// Real V8 CallSites have a native `toString`, plain objects don't, so coercion would +// otherwise produce `[object Object]`. +const callSiteProto: Record = { + isNative: () => false, + isToplevel: () => false, + isEval: () => false, + isConstructor: () => false, + isAsync: () => false, + isPromiseAll: () => false, + getFileName: () => null, + getLineNumber: () => null, + getColumnNumber: () => null, + getFunctionName: () => null, + getMethodName: () => null, + getTypeName: () => null, + getScriptNameOrSourceURL: () => null, + getEvalOrigin: () => null, + getThis: () => undefined, + getFunction: () => undefined, + toString: callSiteToString, +}; + +type CallSiteOverrides = { + isNative?: boolean; + isToplevel?: boolean; + isEval?: boolean; + isConstructor?: boolean; + fileName?: string | null; + lineNumber?: number | null; + columnNumber?: number | null; + functionName?: string | null; + methodName?: string | null; + typeName?: string | null; + scriptNameOrSourceURL?: string | null; + evalOrigin?: string | null; +}; + +function mockCallSite(overrides: CallSiteOverrides = {}): NodeJS.CallSite { + const site = Object.create(callSiteProto) as Record; + if ('isNative' in overrides) site.isNative = () => overrides.isNative; + if ('isToplevel' in overrides) site.isToplevel = () => overrides.isToplevel; + if ('isEval' in overrides) site.isEval = () => overrides.isEval; + if ('isConstructor' in overrides) site.isConstructor = () => overrides.isConstructor; + if ('fileName' in overrides) site.getFileName = () => overrides.fileName; + if ('lineNumber' in overrides) site.getLineNumber = () => overrides.lineNumber; + if ('columnNumber' in overrides) site.getColumnNumber = () => overrides.columnNumber; + if ('functionName' in overrides) site.getFunctionName = () => overrides.functionName; + if ('methodName' in overrides) site.getMethodName = () => overrides.methodName; + if ('typeName' in overrides) site.getTypeName = () => overrides.typeName; + if ('scriptNameOrSourceURL' in overrides) { + site.getScriptNameOrSourceURL = () => overrides.scriptNameOrSourceURL; + } + if ('evalOrigin' in overrides) site.getEvalOrigin = () => overrides.evalOrigin; + return site as unknown as NodeJS.CallSite; +} + +describe('error header', () => { + it('formats Error with name and message', () => { + expect(prepareStackTrace(new Error('boom'), [])).toMatchInlineSnapshot(`"Error: boom"`); + }); + + it('formats subclass Error with custom name', () => { + class CustomError extends Error { + override name = 'CustomError'; + } + expect(prepareStackTrace(new CustomError('bad'), [])).toMatchInlineSnapshot( + `"CustomError: bad"` + ); + }); + + it('omits separator when message is empty', () => { + const err = new Error(''); + expect(prepareStackTrace(err, [])).toMatchInlineSnapshot(`"Error"`); + }); + + it('omits separator when name is empty', () => { + const err = new Error('only-message'); + err.name = ''; + expect(prepareStackTrace(err, [])).toMatchInlineSnapshot(`"only-message"`); + }); + + it('handles plain object that is not an Error', () => { + expect(prepareStackTrace({ message: 'plain' }, [])).toMatchInlineSnapshot(`"Error: plain"`); + }); + + it('handles a thrown primitive', () => { + expect(prepareStackTrace('string error', [])).toMatchInlineSnapshot(`"Error"`); + }); + + it('handles null', () => { + expect(prepareStackTrace(null, [])).toMatchInlineSnapshot(`"Error"`); + }); +}); + +describe('non-source-mapped frames (V8 default formatting)', () => { + it('top-level frame with function name and location', () => { + const site = mockCallSite({ + isToplevel: true, + fileName: '/proj/index.js', + scriptNameOrSourceURL: '/proj/index.js', + lineNumber: 5, + columnNumber: 10, + functionName: 'main', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at main (/proj/index.js:5:10)" +`); + }); + + it('top-level frame without function name (no parens)', () => { + const site = mockCallSite({ + isToplevel: true, + fileName: '/proj/index.js', + scriptNameOrSourceURL: '/proj/index.js', + lineNumber: 5, + columnNumber: 10, + functionName: null, + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at /proj/index.js:5:10" +`); + }); + + it('constructor frame with class name', () => { + const site = mockCallSite({ + isConstructor: true, + fileName: '/proj/widget.js', + scriptNameOrSourceURL: '/proj/widget.js', + lineNumber: 12, + columnNumber: 7, + functionName: 'Widget', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at new Widget (/proj/widget.js:12:7)" +`); + }); + + it('constructor frame without class name', () => { + const site = mockCallSite({ + isConstructor: true, + fileName: '/proj/anon.js', + scriptNameOrSourceURL: '/proj/anon.js', + lineNumber: 1, + columnNumber: 1, + functionName: null, + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at new (/proj/anon.js:1:1)" +`); + }); + + it('method call: typeName + functionName (matching prefix)', () => { + const site = mockCallSite({ + fileName: '/proj/cls.js', + scriptNameOrSourceURL: '/proj/cls.js', + lineNumber: 3, + columnNumber: 4, + functionName: 'Foo.bar', + methodName: 'bar', + typeName: 'Foo', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at Foo.bar (/proj/cls.js:3:4)" +`); + }); + + it('method call: typeName + functionName (no matching prefix)', () => { + const site = mockCallSite({ + fileName: '/proj/cls.js', + scriptNameOrSourceURL: '/proj/cls.js', + lineNumber: 3, + columnNumber: 4, + functionName: 'helper', + methodName: 'bar', + typeName: 'Foo', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at Foo.helper [as bar] (/proj/cls.js:3:4)" +`); + }); + + it('method call: functionName matches methodName, no [as ...] suffix', () => { + const site = mockCallSite({ + fileName: '/proj/cls.js', + scriptNameOrSourceURL: '/proj/cls.js', + lineNumber: 3, + columnNumber: 4, + functionName: 'Foo.bar', + methodName: 'bar', + typeName: 'Foo', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at Foo.bar (/proj/cls.js:3:4)" +`); + }); + + it('method call: no functionName, falls back to typeName.methodName', () => { + const site = mockCallSite({ + fileName: '/proj/cls.js', + scriptNameOrSourceURL: '/proj/cls.js', + lineNumber: 3, + columnNumber: 4, + functionName: null, + methodName: 'bar', + typeName: 'Foo', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at Foo.bar (/proj/cls.js:3:4)" +`); + }); + + it('method call: no functionName and no methodName', () => { + const site = mockCallSite({ + fileName: '/proj/cls.js', + scriptNameOrSourceURL: '/proj/cls.js', + lineNumber: 3, + columnNumber: 4, + functionName: null, + methodName: null, + typeName: 'Foo', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at Foo. (/proj/cls.js:3:4)" +`); + }); + + it('method call: typeName "[object Object]" normalized', () => { + const site = mockCallSite({ + fileName: '/proj/cls.js', + scriptNameOrSourceURL: '/proj/cls.js', + lineNumber: 3, + columnNumber: 4, + functionName: 'fn', + typeName: '[object Object]', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at null.fn (/proj/cls.js:3:4)" +`); + }); + + it('native frame', () => { + const site = mockCallSite({ + isNative: true, + isToplevel: true, + functionName: 'apply', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at apply (native)" +`); + }); + + it('native frame without function name', () => { + const site = mockCallSite({ + isNative: true, + isToplevel: true, + functionName: null, + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at native" +`); + }); + + it('eval frame with origin', () => { + const site = mockCallSite({ + isEval: true, + isToplevel: true, + fileName: null, + scriptNameOrSourceURL: null, + evalOrigin: 'eval at (/proj/host.js:1:1)', + lineNumber: 1, + columnNumber: 1, + functionName: 'evaledFn', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at evaledFn (eval at (/proj/host.js:1:1), :1:1)" +`); + }); + + it('frame with line but no column emits "file:line"', () => { + const site = mockCallSite({ + isToplevel: true, + fileName: '/proj/x.js', + scriptNameOrSourceURL: '/proj/x.js', + lineNumber: 42, + columnNumber: null, + functionName: 'fn', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at fn (/proj/x.js:42)" +`); + }); + + it('multiple frames format together', () => { + const sites = [ + mockCallSite({ + fileName: '/proj/a.js', + scriptNameOrSourceURL: '/proj/a.js', + lineNumber: 1, + columnNumber: 1, + functionName: 'inner', + methodName: 'inner', + typeName: 'Foo', + }), + mockCallSite({ + isToplevel: true, + fileName: '/proj/b.js', + scriptNameOrSourceURL: '/proj/b.js', + lineNumber: 2, + columnNumber: 2, + functionName: null, + }), + ]; + expect(prepareStackTrace(new Error('e'), sites)).toMatchInlineSnapshot(` +"Error: e + at Foo.inner (/proj/a.js:1:1) + at /proj/b.js:2:2" +`); + }); +}); + +describe('source-mapped frames', () => { + type MapEntry = Partial<{ + originalSource: string; + originalLine: number; + originalColumn: number; + name: string | null; + }>; + + beforeEach(() => { + findSourceMap.mockReset(); + }); + + function mockSourceMap(entries: Record): void { + findSourceMap.mockImplementation((file: string) => { + const entry = entries[file]; + if (!entry) return undefined; + return { findEntry: () => entry }; + }); + } + + it('rewrites a mapped frame to its original source position', () => { + mockSourceMap({ + '/bundle.js': { + originalSource: '/orig/Component.tsx', + originalLine: 41, + originalColumn: 9, + name: null, + }, + }); + const site = mockCallSite({ + isToplevel: true, + fileName: '/bundle.js', + scriptNameOrSourceURL: '/bundle.js', + lineNumber: 1, + columnNumber: 100, + functionName: 'render', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at render (/orig/Component.tsx:42:10)" +`); + }); + + it('converts file:// originalSource via fileURLToPath', () => { + mockSourceMap({ + '/bundle.js': { + originalSource: 'file:///orig/Component.tsx', + originalLine: 0, + originalColumn: 0, + name: null, + }, + }); + const site = mockCallSite({ + isToplevel: true, + fileName: '/bundle.js', + scriptNameOrSourceURL: '/bundle.js', + lineNumber: 1, + columnNumber: 1, + functionName: 'render', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at render (/orig/Component.tsx:1:1)" +`); + }); + + it('falls back to V8 format when findSourceMap returns null', () => { + mockSourceMap({}); + const site = mockCallSite({ + isToplevel: true, + fileName: '/bundle.js', + scriptNameOrSourceURL: '/bundle.js', + lineNumber: 1, + columnNumber: 1, + functionName: 'render', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at render (/bundle.js:1:1)" +`); + }); + + it('falls back to V8 format when findEntry returns no originalSource', () => { + findSourceMap.mockReturnValue({ findEntry: () => ({}) } as ReturnType< + typeof nodeModule.findSourceMap + >); + const site = mockCallSite({ + isToplevel: true, + fileName: '/bundle.js', + scriptNameOrSourceURL: '/bundle.js', + lineNumber: 1, + columnNumber: 1, + functionName: 'render', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at render (/bundle.js:1:1)" +`); + }); + + it('uses sourceURL when getFileName is null', () => { + mockSourceMap({ + 'http://localhost:8081/bundle.js': { + originalSource: '/orig/x.tsx', + originalLine: 0, + originalColumn: 0, + name: null, + }, + }); + const site = mockCallSite({ + isToplevel: true, + fileName: null, + scriptNameOrSourceURL: 'http://localhost:8081/bundle.js', + lineNumber: 1, + columnNumber: 1, + functionName: 'render', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at render (/orig/x.tsx:1:1)" +`); + }); + + it('falls back to V8 format when scriptName is missing entirely', () => { + findSourceMap.mockReturnValue(undefined); + const site = mockCallSite({ + isToplevel: true, + fileName: null, + scriptNameOrSourceURL: null, + lineNumber: 1, + columnNumber: 1, + functionName: 'render', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at render (:1:1)" +`); + expect(findSourceMap).not.toHaveBeenCalled(); + }); + + it('falls back to V8 format when line or column is null', () => { + findSourceMap.mockReturnValue(undefined); + const site = mockCallSite({ + isToplevel: true, + fileName: '/bundle.js', + scriptNameOrSourceURL: '/bundle.js', + lineNumber: null, + columnNumber: 1, + functionName: 'render', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at render (/bundle.js)" +`); + expect(findSourceMap).not.toHaveBeenCalled(); + }); + + it('skips mapping for native frames without consulting findSourceMap', () => { + findSourceMap.mockReturnValue(undefined); + const site = mockCallSite({ + isNative: true, + isToplevel: true, + fileName: '/bundle.js', + scriptNameOrSourceURL: '/bundle.js', + lineNumber: 1, + columnNumber: 1, + functionName: 'apply', + }); + expect(prepareStackTrace(new Error('e'), [site])).toMatchInlineSnapshot(` +"Error: e + at apply (native)" +`); + expect(findSourceMap).not.toHaveBeenCalled(); + }); + + it("carries source-map name forward as the previous frame's function name", () => { + mockSourceMap({ + '/bundle.js': { + originalSource: '/orig/App.tsx', + originalLine: 9, + originalColumn: 4, + name: 'render', + }, + }); + const innerSite = mockCallSite({ + isToplevel: true, + fileName: '/bundle.js', + scriptNameOrSourceURL: '/bundle.js', + lineNumber: 1, + columnNumber: 1, + functionName: 'minified_a', + }); + const outerSite = mockCallSite({ + isToplevel: true, + fileName: '/bundle.js', + scriptNameOrSourceURL: '/bundle.js', + lineNumber: 2, + columnNumber: 2, + functionName: 'minified_b', + }); + expect(prepareStackTrace(new Error('e'), [innerSite, outerSite])).toMatchInlineSnapshot(` +"Error: e + at render (/orig/App.tsx:10:5) + at minified_b (/orig/App.tsx:10:5)" +`); + }); + + it('mixes mapped and unmapped frames in a single stack', () => { + mockSourceMap({ + '/bundle.js': { + originalSource: '/orig/App.tsx', + originalLine: 41, + originalColumn: 9, + name: null, + }, + }); + const sites = [ + mockCallSite({ + isToplevel: true, + fileName: '/bundle.js', + scriptNameOrSourceURL: '/bundle.js', + lineNumber: 100, + columnNumber: 50, + functionName: 'render', + }), + mockCallSite({ + isToplevel: false, + fileName: 'node:internal/modules/cjs/loader', + scriptNameOrSourceURL: 'node:internal/modules/cjs/loader', + lineNumber: 1234, + columnNumber: 14, + functionName: '_compile', + methodName: '_compile', + typeName: 'Module', + }), + ]; + expect(prepareStackTrace(new Error('e'), sites)).toMatchInlineSnapshot(` +"Error: e + at render (/orig/App.tsx:42:10) + at Module._compile (node:internal/modules/cjs/loader:1234:14)" +`); + }); +}); + +describe('empty stack', () => { + it('returns just the error header', () => { + expect(prepareStackTrace(new Error('boom'), [])).toMatchInlineSnapshot(`"Error: boom"`); + }); +}); diff --git a/packages/@expo/require-utils/src/load.ts b/packages/@expo/require-utils/src/load.ts index bc37b98f4ff669..96734ed8f27bd6 100644 --- a/packages/@expo/require-utils/src/load.ts +++ b/packages/@expo/require-utils/src/load.ts @@ -1,10 +1,12 @@ import fs from 'node:fs'; import * as nodeModule from 'node:module'; +import os from 'node:os'; import path from 'node:path'; import url from 'node:url'; import type * as ts from 'typescript'; import { annotateError, formatDiagnostic } from './codeframe'; +import { installSourceMapStackTrace } from './stacktrace'; import { toCommonJS } from './transform'; declare module 'node:module' { @@ -20,6 +22,9 @@ declare global { format?: 'module' | 'commonjs' | 'commonjs-typescript' | 'module-typescript' | 'typescript' ): unknown; } + export interface Process { + isBun?: boolean; + } } } @@ -82,6 +87,7 @@ function toFormat(filename: string, isLegacy: boolean): Format | null { export interface ModuleOptions { paths?: string[]; + sourceMap?: string; } function toRealDirname(filePath: string): string { @@ -105,6 +111,44 @@ function toRealDirname(filePath: string): string { } } +const hasModuleSourceMapsSupport = typeof nodeModule.setSourceMapsSupport === 'function'; + +interface SourceMapsState { + enabled: boolean; + nodeModules?: boolean; + generatedCode?: boolean; +} + +function getSourceMapsState(): SourceMapsState { + return typeof nodeModule.getSourceMapsSupport === 'function' + ? nodeModule.getSourceMapsSupport() + : { enabled: !!process.sourceMapsEnabled }; +} + +function setSourceMapsState(state: SourceMapsState): void { + if (hasModuleSourceMapsSupport) { + nodeModule.setSourceMapsSupport(state.enabled, { + nodeModules: state.nodeModules ?? false, + generatedCode: state.generatedCode ?? false, + }); + } else { + process.setSourceMapsEnabled(state.enabled); + } +} + +function makeSourceMapTempPath(filename: string) { + let basename = path.basename(filename); + const queryIdx = basename.search(/[?#]/); + if (queryIdx >= 0) { + basename = basename.slice(0, queryIdx); + } + return path.join(os.tmpdir(), `require-utils-${process.pid}-${basename}.map`); +} + +function stripSourceMappingURL(code: string): string { + return code.replace(/^[ \t]*\/\/[#@][ \t]+sourceMappingURL=.*$/gm, ''); +} + function compileModule(code: string, filename: string, opts: ModuleOptions) { const format = toFormat(filename, false); const prependPaths = opts.paths ?? []; @@ -113,16 +157,76 @@ function compileModule(code: string, filename: string, opts: ModuleOptions) { const basePath = toRealDirname(filename); const nodeModulePaths = nodeModule._nodeModulePaths(basePath); const paths = [...prependPaths, ...nodeModulePaths]; + + let inputCode = code; + + // We may get a Metro SSR relative path here, which isn't a valid absolute path, and we need to normalize + // the filename before proceeding + let compileFilename = filename; + if (opts.sourceMap) { + const queryIdx = filename.search(/[?#]/); + const basePart = queryIdx >= 0 ? filename.slice(0, queryIdx) : filename; + const queryPart = queryIdx >= 0 ? filename.slice(queryIdx) : ''; + if (!path.isAbsolute(basePart)) { + compileFilename = path.resolve(basePart) + queryPart; + } + } + + let mapPath: string | undefined; + let priorSourceMapsState: SourceMapsState | undefined; + if (opts.sourceMap && !process.isBun) { + try { + mapPath = makeSourceMapTempPath(compileFilename); + fs.writeFileSync(mapPath, opts.sourceMap); + } catch (error: any) { + mapPath = undefined; + // If we fail to write the source map, we can still continue without it, but log a warning since it's likely a misconfiguration + console.warn( + `Warning: Failed to write source map for ${filename} to ${mapPath}. Source maps will be unavailable for this module.\n${error?.message || error}` + ); + } + + if (mapPath) { + inputCode = stripSourceMappingURL(code); + // NOTE This needs to be a plain absolute path because Node rejects file: URLs + inputCode += `\n//# sourceMappingURL=${mapPath}`; + + priorSourceMapsState = getSourceMapsState(); + installSourceMapStackTrace(); + setSourceMapsState({ enabled: true, nodeModules: true }); + } + } + try { - const mod = Object.assign(new nodeModule.Module(filename, parent), { filename, paths }); - mod._compile(code, filename, format != null ? format : undefined); + const mod = Object.assign(new nodeModule.Module(compileFilename, parent), { + filename: compileFilename, + paths, + }); + mod._compile(inputCode, compileFilename, format != null ? format : undefined); mod.loaded = true; - require.cache[filename] = mod; + require.cache[compileFilename] = mod; + if (compileFilename !== filename) { + require.cache[filename] = mod; + } parent?.children?.splice(parent.children.indexOf(mod), 1); return mod; } catch (error: any) { - delete require.cache[filename]; + delete require.cache[compileFilename]; + if (compileFilename !== filename) { + delete require.cache[filename]; + } throw error; + } finally { + if (mapPath) { + // Restore, so subsequent requires of node_modules won't have their source-maps read + setSourceMapsState(priorSourceMapsState ?? { enabled: false }); + // Node parses source maps eagerly during _compile, so the file can be removed now. + try { + fs.unlinkSync(mapPath); + } catch { + /* noop */ + } + } } } diff --git a/packages/@expo/require-utils/src/stacktrace.ts b/packages/@expo/require-utils/src/stacktrace.ts new file mode 100644 index 00000000000000..945155dc7f6b0b --- /dev/null +++ b/packages/@expo/require-utils/src/stacktrace.ts @@ -0,0 +1,259 @@ +// MIT License, Copyright (c) 2014 Evan Wallace +// Derived from: https://github.com/evanw/node-source-map-support/blob/7b5b81e/source-map-support.js + +import * as nodeModule from 'node:module'; +import url from 'node:url'; + +let stackTraceInstalled = false; + +/** + * Install a JS-level `Error.prepareStackTrace` that symbolicates frames using Node's + * source map cache (`module.findSourceMap`). Idempotent — calling more than once is a no-op. + * + * Background: Node's automatic stack symbolication is gated on source maps being enabled. + * `compileModule` toggles source maps on only during `_compile` (to keep the cache scoped + * to our bundles rather than every `require()`'d package), so by the time an error from + * the bundle is formatted, automatic symbolication is off. This shim restores it. + * + * Implementation closely follows + * https://github.com/evanw/node-source-map-support/blob/master/source-map-support.js, + * the canonical reference for this kind of `prepareStackTrace` shim: + * + * - Source-mapped frames are wrapped in cloned plain-object call sites that override + * `getFileName`, `getLineNumber`, `getColumnNumber`, `getFunctionName`, + * `getScriptNameOrSourceURL`, and `toString`. V8's native + * `CallSite.prototype.toString` is implemented in C++ and reads internal slots + * directly, ignoring overridden JS getters; so we reimplement `toString` in JS + * (`callSiteToString`) on the clone, which consults the overridden getters. + * + * - The stack is processed in reverse so we can use the source map's `name` from the + * *next* frame (the caller side) when overriding the *current* frame's function + * name. Source map V3's `name` at a position identifies the symbol being *called* + * at that position — i.e. the function whose body lives at the next-out frame — + * which is more accurate than V8's `getFunctionName()` for that frame. + * + * - Non-source-mapped frames pass through unchanged; their native + * `CallSite.prototype.toString` is invoked via template-literal coercion in + * `prepareStackTrace`. This preserves V8's canonical formatting for every frame + * variant we don't symbolicate (top-level `at file:line:col`, `at TypeName.method`, + * `at new Class`, `at fn [as alias]`, native frames, eval frames, etc.). + */ +export function installSourceMapStackTrace(): void { + if (stackTraceInstalled) { + return; + } else { + stackTraceInstalled = true; + } + Error.prepareStackTrace = (error, callSites) => { + const errorString = formatErrorHeader(error); + if (callSites.length === 0) { + return errorString; + } + const state: WalkState = { nextPosition: null, curPosition: null }; + const lines: string[] = []; + for (let i = callSites.length - 1; i >= 0; i--) { + lines.push('\n at ' + wrapCallSite(callSites[i]!, state)); + state.nextPosition = state.curPosition; + } + lines.reverse(); + return errorString + lines.join(''); + }; +} + +function formatErrorHeader(error: unknown): string { + // Match V8's default `Error.prototype.toString` semantics. We can't call it directly + // because `error` may not actually be an Error instance. + const name = + (error as { name?: unknown })?.name === undefined + ? 'Error' + : String((error as { name: unknown }).name); + const message = + (error as { message?: unknown })?.message === undefined + ? '' + : String((error as { message: unknown }).message); + if (!name) return message; + if (!message) return name; + return `${name}: ${message}`; +} + +interface MappedPosition { + source: string; + line: number; + column: number; // 1-based + name: string | null; +} + +interface WalkState { + nextPosition: MappedPosition | null; + curPosition: MappedPosition | null; +} + +function wrapCallSite(site: NodeJS.CallSite, state: WalkState): string { + if (site.isNative()) { + state.curPosition = null; + return String(site); + } + + // Most call sites surface their script identity via `getFileName()`. Code passed to + // `eval()` (or otherwise compiled with a `//# sourceURL=...` directive) only exposes + // it via `getScriptNameOrSourceURL()` — falling through to that catches those cases. + const scriptName = site.getFileName() ?? site.getScriptNameOrSourceURL(); + if (!scriptName) { + state.curPosition = null; + return String(site); + } + + const lineNumber = site.getLineNumber(); + const columnNumber = site.getColumnNumber(); + if (lineNumber == null || columnNumber == null) { + state.curPosition = null; + return String(site); + } + + const sm = nodeModule.findSourceMap(scriptName); + if (!sm) { + state.curPosition = null; + return String(site); + } + + const entry = sm.findEntry(lineNumber - 1, columnNumber - 1); + if (!entry || !('originalSource' in entry) || !entry.originalSource) { + state.curPosition = null; + return String(site); + } + + // `originalSource` is a `file://` URL. `fileURLToPath` correctly handles drive letters + // and percent-encoded characters; a naive `file://` strip would yield `/C:/foo/bar.ts` + // on Windows. + const originalSource = entry.originalSource.startsWith('file://') + ? url.fileURLToPath(entry.originalSource) + : entry.originalSource; + + // Node's runtime exposes a `name` field on the source map entry even though + // `@types/node` omits it from `SourceMapping`. Read it defensively. + const mappedName = (entry as { name?: string }).name ?? null; + + const position: MappedPosition = { + source: originalSource, + line: entry.originalLine + 1, + column: entry.originalColumn + 1, + name: mappedName, + }; + state.curPosition = position; + + // Build a wrapped call site whose JS getters and `toString` agree on the mapped + // position. The function-name override consults `state.nextPosition` first because + // a source map's `name` at a position names the symbol being *called*, which lives + // one frame outward. + const wrapped = cloneCallSite(site); + const originalGetFunctionName = wrapped.getFunctionName; + wrapped.getFunctionName = function () { + const nameFromNext = state.nextPosition?.name; + if (nameFromNext) return nameFromNext; + return originalGetFunctionName(); + }; + wrapped.getFileName = () => position.source; + wrapped.getLineNumber = () => position.line; + wrapped.getColumnNumber = () => position.column; + wrapped.getScriptNameOrSourceURL = () => position.source; + return String(wrapped); +} + +interface ClonedCallSite extends NodeJS.CallSite { + toString(): string; +} + +function cloneCallSite(site: NodeJS.CallSite): ClonedCallSite { + // We need a plain object whose JS getters mirror the original site's, but whose + // `toString` we can replace. Subclassing or proxying the native `CallSite` doesn't + // work: `CallSite.prototype.toString` is implemented in C++ and rejects any receiver + // that isn't the original V8-tagged object (`Method toString called on incompatible + // receiver`). The only working approach is to build a plain object that re-exposes + // the `is*`/`get*` methods (bound to the original) and carries our JS toString. + const clone = {} as Record; + const proto = Object.getPrototypeOf(site) as Record; + for (const name of Object.getOwnPropertyNames(proto)) { + const value = (site as unknown as Record)[name]; + if (/^(?:is|get)/.test(name) && typeof value === 'function') { + clone[name] = (value as (...args: unknown[]) => unknown).bind(site); + } else { + clone[name] = value; + } + } + clone.toString = callSiteToString; + return clone as unknown as ClonedCallSite; +} + +/** + * V8's `CallSite.prototype.toString`, reimplemented in JS so it consults overridden + * getters on a cloned call site. Mirrors the C++ implementation in V8's `messages.cc` + * (which is also what `evanw/node-source-map-support` ports). + * + * Exported for tests, which need a working `toString` on mock plain-object call sites + * (real V8 CallSites have one natively; plain objects don't). + */ +export function callSiteToString(this: NodeJS.CallSite): string { + let fileLocation = ''; + if (this.isNative()) { + fileLocation = 'native'; + } else { + const scriptName = this.getScriptNameOrSourceURL(); + if (!scriptName && this.isEval()) { + fileLocation = (this.getEvalOrigin() ?? '') + ', '; + } + if (scriptName) { + fileLocation += scriptName; + } else { + fileLocation += ''; + } + const lineNumber = this.getLineNumber(); + if (lineNumber != null) { + fileLocation += ':' + lineNumber; + const columnNumber = this.getColumnNumber(); + if (columnNumber) { + fileLocation += ':' + columnNumber; + } + } + } + + let line = ''; + const functionName = this.getFunctionName(); + let addSuffix = true; + const isConstructor = this.isConstructor(); + const isMethodCall = !(this.isToplevel() || isConstructor); + + if (isMethodCall) { + let typeName: string | null = this.getTypeName(); + // Older Node versions can return `[object Object]` here; normalize to `null`. + if (typeName === '[object Object]') { + typeName = 'null'; + } + const methodName = this.getMethodName(); + if (functionName) { + if (typeName && functionName.indexOf(typeName) !== 0) { + line += typeName + '.'; + } + line += functionName; + if ( + methodName && + functionName.indexOf('.' + methodName) !== functionName.length - methodName.length - 1 + ) { + line += ' [as ' + methodName + ']'; + } + } else { + line += typeName + '.' + (methodName ?? ''); + } + } else if (isConstructor) { + line += 'new ' + (functionName ?? ''); + } else if (functionName) { + line += functionName; + } else { + line += fileLocation; + addSuffix = false; + } + + if (addSuffix) { + line += ' (' + fileLocation + ')'; + } + return line; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5346e41ffb153..f22aabf925c715 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1932,9 +1932,6 @@ importers: slugify: specifier: ^1.3.4 version: 1.6.8 - source-map-support: - specifier: ~0.5.21 - version: 0.5.21 stacktrace-parser: specifier: ^0.1.10 version: 0.1.11 @@ -2035,9 +2032,6 @@ importers: '@types/send': specifier: ^0.17.1 version: 0.17.6 - '@types/source-map-support': - specifier: ^0.5.10 - version: 0.5.10 '@types/webpack': specifier: ~4.41.32 version: 4.41.40 @@ -9427,9 +9421,6 @@ packages: '@types/source-list-map@0.1.6': resolution: {integrity: sha512-5JcVt1u5HDmlXkwOD2nslZVllBBc7HDuOICfiZah2Z0is8M8g+ddAEawbmd3VjedfDHBzxCaXLs07QEmb7y54g==} - '@types/source-map-support@0.5.10': - resolution: {integrity: sha512-tgVP2H469x9zq34Z0m/fgPewGhg/MLClalNOiPIzQlXrSS2YrKu/xCdSCKnEDwkFha51VKEKB6A9wW26/ZNwzA==} - '@types/ssri@7.1.5': resolution: {integrity: sha512-odD/56S3B51liILSk5aXJlnYt99S6Rt9EFDDqGtJM26rKHApHcwyU/UoYHrzKkdkHMAIquGWCuHtQTbes+FRQw==} @@ -19547,10 +19538,6 @@ snapshots: '@types/source-list-map@0.1.6': {} - '@types/source-map-support@0.5.10': - dependencies: - source-map: 0.6.1 - '@types/ssri@7.1.5': dependencies: '@types/node': 22.19.15 From 646160474d9e332cb5e27935b75ef7083a4a6829 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Fri, 8 May 2026 17:01:20 +0100 Subject: [PATCH 03/16] perf(metro-config): Asynchronously delete qualifying Metro caches in OS tmpdir (#45553) # Why Clearing the Metro caches for any sizable project is painfully slow right now and hangs on startup. The problem is that a cache can be quite large and a recursive `rmSync` can be very slow. # How If the cache directory is in the OS `tmpdir` (and will be cleaned up automatically presumably), we can be less careful and rename synchronously, then try deleting asynchronously. This gets rid of the startup performance penalty with `--clear` # Test Plan - Manually tested on `apps/bare-expo` by running `--clear` after a full `expo start` + bundle before # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- packages/@expo/metro-config/CHANGELOG.md | 1 + .../@expo/metro-config/build/file-store.d.ts | 5 ++ .../@expo/metro-config/build/file-store.js | 55 ++++++++++++++++++ .../metro-config/build/file-store.js.map | 2 +- packages/@expo/metro-config/src/file-store.ts | 58 +++++++++++++++++++ 5 files changed, 120 insertions(+), 1 deletion(-) diff --git a/packages/@expo/metro-config/CHANGELOG.md b/packages/@expo/metro-config/CHANGELOG.md index 67864a32f4f2b2..6f1a80da9a9e74 100644 --- a/packages/@expo/metro-config/CHANGELOG.md +++ b/packages/@expo/metro-config/CHANGELOG.md @@ -11,6 +11,7 @@ ### 💡 Others - Thread `excludeSource` through to source map serialization ([#45507](https://github.com/expo/expo/pull/45507) by [@kitten](https://github.com/kitten)) +- Rename cache then asynchronously delete to remove `--clear` penalty ([#45553](https://github.com/expo/expo/pull/45553) by [@kitten](https://github.com/kitten)) ## 56.0.4 — 2026-05-07 diff --git a/packages/@expo/metro-config/build/file-store.d.ts b/packages/@expo/metro-config/build/file-store.d.ts index 3d1965daca828f..f5c8b31e3f7bd0 100644 --- a/packages/@expo/metro-config/build/file-store.d.ts +++ b/packages/@expo/metro-config/build/file-store.d.ts @@ -1,4 +1,9 @@ import UpstreamFileStore from '@expo/metro/metro-cache/stores/FileStore'; export declare class FileStore extends UpstreamFileStore { + private readonly _root; + constructor(options: { + root: string; + }); set(key: Buffer, value: any): Promise; + clear(): void; } diff --git a/packages/@expo/metro-config/build/file-store.js b/packages/@expo/metro-config/build/file-store.js index b412fc72d7e547..e9b08c0e2773b6 100644 --- a/packages/@expo/metro-config/build/file-store.js +++ b/packages/@expo/metro-config/build/file-store.js @@ -5,8 +5,58 @@ var __importDefault = (this && this.__importDefault) || function (mod) { Object.defineProperty(exports, "__esModule", { value: true }); exports.FileStore = void 0; const FileStore_1 = __importDefault(require("@expo/metro/metro-cache/stores/FileStore")); +const fs_1 = __importDefault(require("fs")); +const os_1 = __importDefault(require("os")); +const path_1 = __importDefault(require("path")); const debug = require('debug')('expo:metro:cache'); +// On macOS `os.tmpdir()` returns `/var/folders/...` while its realpath is +// `/private/var/folders/...`; accept either form so callers that resolved +// symlinks aren't excluded. +function isInsideOsTmpdir(target) { + const resolved = path_1.default.resolve(target); + const tmp = path_1.default.resolve(os_1.default.tmpdir()); + if (resolved !== tmp && resolved.startsWith(tmp + path_1.default.sep)) { + return true; + } + let tmpReal; + try { + tmpReal = fs_1.default.realpathSync(tmp); + } + catch { + return false; + } + return resolved !== tmpReal && resolved.startsWith(tmpReal + path_1.default.sep); +} +// Renames `root` to a sibling tombstone and deletes it in the background. +// Returns false if the caller should fall back to a synchronous remove. +// `maxRetries` covers the Windows case where files just closed can briefly +// fail to delete with EBUSY/EPERM. +function tryRenameAndDeleteAsync(root) { + if (!isInsideOsTmpdir(root)) { + return false; + } + const tombstone = `${root}.delete-${process.pid}-${Date.now()}`; + try { + fs_1.default.renameSync(root, tombstone); + } + catch (err) { + if (err?.code === 'ENOENT') { + return true; + } + debug('Cache rename failed, falling back to recursive remove:', err); + return false; + } + fs_1.default.promises.rm(tombstone, { recursive: true, force: true, maxRetries: 3 }).catch((err) => { + debug('Failed to remove cache tombstone:', tombstone, err); + }); + return true; +} class FileStore extends FileStore_1.default { + _root; + constructor(options) { + super(options); + this._root = options.root; + } async set(key, value) { // Prevent caching of CSS files that have the skipCache flag set. if (value?.output?.[0]?.data?.css?.skipCache) { @@ -15,6 +65,11 @@ class FileStore extends FileStore_1.default { } return await super.set(key, value); } + clear() { + if (!tryRenameAndDeleteAsync(this._root)) { + super.clear(); + } + } } exports.FileStore = FileStore; //# sourceMappingURL=file-store.js.map \ No newline at end of file diff --git a/packages/@expo/metro-config/build/file-store.js.map b/packages/@expo/metro-config/build/file-store.js.map index 3fea9c6971f137..765a7c0ec8e221 100644 --- a/packages/@expo/metro-config/build/file-store.js.map +++ b/packages/@expo/metro-config/build/file-store.js.map @@ -1 +1 @@ -{"version":3,"file":"file-store.js","sourceRoot":"","sources":["../src/file-store.ts"],"names":[],"mappings":";;;;;;AAAA,yFAAyE;AAEzE,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,kBAAkB,CAAuB,CAAC;AAEzE,MAAa,SAAa,SAAQ,mBAAoB;IACpD,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAU;QAC/B,iEAAiE;QACjE,IAAI,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;YAC7C,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QACD,OAAO,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;CACF;AATD,8BASC"} \ No newline at end of file +{"version":3,"file":"file-store.js","sourceRoot":"","sources":["../src/file-store.ts"],"names":[],"mappings":";;;;;;AAAA,yFAAyE;AACzE,4CAAoB;AACpB,4CAAoB;AACpB,gDAAwB;AAExB,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,kBAAkB,CAAuB,CAAC;AAEzE,0EAA0E;AAC1E,0EAA0E;AAC1E,4BAA4B;AAC5B,SAAS,gBAAgB,CAAC,MAAc;IACtC,MAAM,QAAQ,GAAG,cAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,cAAI,CAAC,OAAO,CAAC,YAAE,CAAC,MAAM,EAAE,CAAC,CAAC;IACtC,IAAI,QAAQ,KAAK,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,GAAG,cAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5D,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,YAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,QAAQ,KAAK,OAAO,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,GAAG,cAAI,CAAC,GAAG,CAAC,CAAC;AACzE,CAAC;AAED,0EAA0E;AAC1E,wEAAwE;AACxE,2EAA2E;AAC3E,mCAAmC;AACnC,SAAS,uBAAuB,CAAC,IAAY;IAC3C,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,SAAS,GAAG,GAAG,IAAI,WAAW,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IAChE,IAAI,CAAC;QACH,YAAE,CAAC,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,GAAG,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,CAAC,wDAAwD,EAAE,GAAG,CAAC,CAAC;QACrE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,YAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACvF,KAAK,CAAC,mCAAmC,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAa,SAAa,SAAQ,mBAAoB;IACnC,KAAK,CAAS;IAE/B,YAAY,OAAyB;QACnC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAU;QAC/B,iEAAiE;QACjE,IAAI,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;YAC7C,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QACD,OAAO,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACzC,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;IACH,CAAC;CACF;AAtBD,8BAsBC"} \ No newline at end of file diff --git a/packages/@expo/metro-config/src/file-store.ts b/packages/@expo/metro-config/src/file-store.ts index 87836c6954ec2e..b02896d3aa95ce 100644 --- a/packages/@expo/metro-config/src/file-store.ts +++ b/packages/@expo/metro-config/src/file-store.ts @@ -1,8 +1,60 @@ import UpstreamFileStore from '@expo/metro/metro-cache/stores/FileStore'; +import fs from 'fs'; +import os from 'os'; +import path from 'path'; const debug = require('debug')('expo:metro:cache') as typeof console.log; +// On macOS `os.tmpdir()` returns `/var/folders/...` while its realpath is +// `/private/var/folders/...`; accept either form so callers that resolved +// symlinks aren't excluded. +function isInsideOsTmpdir(target: string): boolean { + const resolved = path.resolve(target); + const tmp = path.resolve(os.tmpdir()); + if (resolved !== tmp && resolved.startsWith(tmp + path.sep)) { + return true; + } + let tmpReal: string; + try { + tmpReal = fs.realpathSync(tmp); + } catch { + return false; + } + return resolved !== tmpReal && resolved.startsWith(tmpReal + path.sep); +} + +// Renames `root` to a sibling tombstone and deletes it in the background. +// Returns false if the caller should fall back to a synchronous remove. +// `maxRetries` covers the Windows case where files just closed can briefly +// fail to delete with EBUSY/EPERM. +function tryRenameAndDeleteAsync(root: string): boolean { + if (!isInsideOsTmpdir(root)) { + return false; + } + const tombstone = `${root}.delete-${process.pid}-${Date.now()}`; + try { + fs.renameSync(root, tombstone); + } catch (err: any) { + if (err?.code === 'ENOENT') { + return true; + } + debug('Cache rename failed, falling back to recursive remove:', err); + return false; + } + fs.promises.rm(tombstone, { recursive: true, force: true, maxRetries: 3 }).catch((err) => { + debug('Failed to remove cache tombstone:', tombstone, err); + }); + return true; +} + export class FileStore extends UpstreamFileStore { + private readonly _root: string; + + constructor(options: { root: string }) { + super(options); + this._root = options.root; + } + async set(key: Buffer, value: any): Promise { // Prevent caching of CSS files that have the skipCache flag set. if (value?.output?.[0]?.data?.css?.skipCache) { @@ -11,4 +63,10 @@ export class FileStore extends UpstreamFileStore { } return await super.set(key, value); } + + clear(): void { + if (!tryRenameAndDeleteAsync(this._root)) { + super.clear(); + } + } } From 9d713b7078ccaba8ef6c35cd6a51f63284c839cd Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Fri, 8 May 2026 17:02:18 +0100 Subject: [PATCH 04/16] perf(cli): Pre-optimize `ignorePattern` passed to `@expo/metro-file-map` (#45552) # Why #45463 has demonstrated that `resolver.blockList` can easily become a footgun since it's not well documented what happens to these regular expressions. Also patterns such as `.*` explicitly become un-anchored, defeating some of Irregexp's optimizations. # How We can do slightly bettern by stripping out anti-patterns that don't alter the match e.g. leading and trailing `.*` and the likes, and pre-sorting the regular expression. **Note:** This is largely LLM-generated with slight instructions on how Irregexp functions and what we'd like to strip out. # Test Plan - Captures snapshots of patterns of what RegExp patterns can be optimized # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- packages/@expo/cli/CHANGELOG.md | 1 + .../start/server/metro/createFileMap-fork.ts | 30 +---- .../composeMetroIgnorePatterns-test.ts | 122 ++++++++++++++++++ .../src/utils/composeMetroIgnorePatterns.ts | 81 ++++++++++++ 4 files changed, 207 insertions(+), 27 deletions(-) create mode 100644 packages/@expo/cli/src/utils/__tests__/composeMetroIgnorePatterns-test.ts create mode 100644 packages/@expo/cli/src/utils/composeMetroIgnorePatterns.ts diff --git a/packages/@expo/cli/CHANGELOG.md b/packages/@expo/cli/CHANGELOG.md index 39344143a23e1a..edda6414fa63cb 100644 --- a/packages/@expo/cli/CHANGELOG.md +++ b/packages/@expo/cli/CHANGELOG.md @@ -18,6 +18,7 @@ ### 💡 Others +- Slightly optimize `ignorePattern` passed to `@expo/metro-file-map` ([#45552](https://github.com/expo/expo/pull/45552) by [@kitten](https://github.com/kitten)) - Replace deprecated `url.parse()` with WHATWG `URL` API in Metro dev server. ([#45524](https://github.com/expo/expo/pull/45524) by [@EvanBacon](https://github.com/EvanBacon)) - Remove pinned dependencies ([#45520](https://github.com/expo/expo/pull/45520) by [@kitten](https://githun.com/kitten)) - Adopt `experiments.onDemandFilesystem` on `@expo/config-types` ([#45555](https://github.com/expo/expo/pull/45555) by [@kitten](https://github.com/kitten)) diff --git a/packages/@expo/cli/src/start/server/metro/createFileMap-fork.ts b/packages/@expo/cli/src/start/server/metro/createFileMap-fork.ts index aad6b183532095..c72709d0406849 100644 --- a/packages/@expo/cli/src/start/server/metro/createFileMap-fork.ts +++ b/packages/@expo/cli/src/start/server/metro/createFileMap-fork.ts @@ -10,35 +10,11 @@ import FileMap, { DependencyPlugin, DiskCacheManager, HastePlugin } from '@expo/ import ciInfo from 'ci-info'; import path from 'node:path'; +import { composeMetroIgnorePatterns } from '../../../utils/composeMetroIgnorePatterns'; + function getIgnorePattern(config: ConfigT): RegExp { const { blockList, blacklistRE } = config.resolver; - const ignorePattern = blacklistRE || blockList; - if (!ignorePattern) { - return / ^/; - } - const combine = (regexes: RegExp[]) => - new RegExp( - regexes - .map((regex, index) => { - if (regex.flags !== regexes[0]!.flags) { - throw new Error( - 'Cannot combine blockList patterns, because they have different flags:\n' + - ' - Pattern 0: ' + - regexes[0]!.toString() + - '\n' + - ` - Pattern ${index}: ` + - regexes[index]!.toString() - ); - } - return '(' + regex.source + ')'; - }) - .join('|'), - regexes[0]?.flags ?? '' - ); - if (Array.isArray(ignorePattern)) { - return combine(ignorePattern); - } - return ignorePattern; + return composeMetroIgnorePatterns(blacklistRE || blockList); } interface CreateFileMapOptions { diff --git a/packages/@expo/cli/src/utils/__tests__/composeMetroIgnorePatterns-test.ts b/packages/@expo/cli/src/utils/__tests__/composeMetroIgnorePatterns-test.ts new file mode 100644 index 00000000000000..c6502ddc87bc2b --- /dev/null +++ b/packages/@expo/cli/src/utils/__tests__/composeMetroIgnorePatterns-test.ts @@ -0,0 +1,122 @@ +import { composeMetroIgnorePatterns } from '../composeMetroIgnorePatterns'; + +describe(composeMetroIgnorePatterns, () => { + it.each([[null], [undefined], [[]]])('returns a never-matching regex for %p', (input) => { + expect(composeMetroIgnorePatterns(input).test('foo')).toBe(false); + expect(composeMetroIgnorePatterns(input).test('')).toBe(false); + }); + + it('returns an equivalent regex for a single regex input', () => { + const composed = composeMetroIgnorePatterns(/foo/); + expect(composed.test('xfoox')).toBe(true); + expect(composed.test('bar')).toBe(false); + }); + + it('unwraps a single-element array', () => { + const composed = composeMetroIgnorePatterns([/foo/]); + expect(composed.test('xfoox')).toBe(true); + expect(composed.test('bar')).toBe(false); + }); + + it('matches the union of multiple patterns', () => { + const composed = composeMetroIgnorePatterns([/foo/, /bar/, /^baz/]); + expect(composed.test('xfoox')).toBe(true); + expect(composed.test('xbarx')).toBe(true); + expect(composed.test('baz123')).toBe(true); + expect(composed.test('123baz')).toBe(false); + expect(composed.test('quux')).toBe(false); + }); + + it('uses non-capturing groups when composing', () => { + expect(composeMetroIgnorePatterns([/foo/, /bar/]).source).toMatchInlineSnapshot( + `"(?:foo)|(?:bar)"` + ); + }); + + it('throws when patterns have different flags', () => { + expect(() => composeMetroIgnorePatterns([/foo/i, /bar/])).toThrow(/different flags/); + }); + + it('preserves shared flags', () => { + const composed = composeMetroIgnorePatterns([/foo/i, /bar/i]); + expect(composed.flags).toBe('i'); + expect(composed.test('FOO')).toBe(true); + expect(composed.test('BAR')).toBe(true); + }); + + describe('decoration stripping', () => { + it.each<[RegExp, string]>([ + [/.*foo/, 'foo'], + [/.*?foo/, 'foo'], + [/^.*foo/, 'foo'], + [/^.*?foo/, 'foo'], + [/[\s\S]*foo/, 'foo'], + [/[\s\S]*?foo/, 'foo'], + [/foo.*/, 'foo'], + [/foo.*?/, 'foo'], + [/foo.*$/, 'foo'], + [/foo.*?$/, 'foo'], + [/foo[\s\S]*/, 'foo'], + [/foo[\s\S]*?$/, 'foo'], + [/.*foo.*/, 'foo'], + [/^.*foo.*$/, 'foo'], + [/[\s\S]*foo[\s\S]*/, 'foo'], + ])('strips redundant decoration: %p -> %p', (input, expected) => { + expect(composeMetroIgnorePatterns(input).source).toBe(expected); + }); + + it.each<[RegExp, string]>([ + [/foo\.*/, 'foo\\.*'], + [/foo\\.*/, 'foo\\\\'], + [/foo[a-z*]/, 'foo[a-z*]'], + ])('respects escapes and character classes: %p -> %p', (input, expected) => { + expect(composeMetroIgnorePatterns(input).source).toBe(expected); + }); + + it.each<[RegExp]>([[/^.*$/], [/.*/], [/^.*/], [/.*$/]])( + 'returns the original source when stripping would be degenerate: %p', + (input) => { + expect(composeMetroIgnorePatterns(input).source).toBe(input.source); + } + ); + + it('produces a regex semantically equivalent to the original', () => { + const before = /.*bare-expo\/e2e.*/; + const after = composeMetroIgnorePatterns(before); + const inputs = [ + 'apps/bare-expo/e2e/test.js', + 'apps/bare-expo/e2e', + 'apps/other/file.js', + '', + 'bare-expo/e2e', + ]; + for (const input of inputs) { + expect(after.test(input)).toBe(before.test(input)); + } + }); + + it('preserves union semantics across the array after stripping', () => { + const inputs = [/.*foo.*/, /^bar/, /baz\/.*$/]; + const composed = composeMetroIgnorePatterns(inputs); + const samples = ['xfoox', 'bar123', '123bar', 'baz/x', 'baz', 'qux', 'foo']; + for (const sample of samples) { + const expected = inputs.some((re) => re.test(sample)); + expect(composed.test(sample)).toBe(expected); + } + }); + }); + + describe('ordering', () => { + it('places start-anchored alternatives first', () => { + expect(composeMetroIgnorePatterns([/foo/, /^bar/, /baz/]).source).toMatchInlineSnapshot( + `"(?:^bar)|(?:foo)|(?:baz)"` + ); + }); + + it('places leading-wildcard alternatives last when they cannot be stripped', () => { + expect(composeMetroIgnorePatterns([/.*$/, /^bar/, /baz/]).source).toMatchInlineSnapshot( + `"(?:^bar)|(?:baz)|(?:.*$)"` + ); + }); + }); +}); diff --git a/packages/@expo/cli/src/utils/composeMetroIgnorePatterns.ts b/packages/@expo/cli/src/utils/composeMetroIgnorePatterns.ts new file mode 100644 index 00000000000000..d083e6f583b9d6 --- /dev/null +++ b/packages/@expo/cli/src/utils/composeMetroIgnorePatterns.ts @@ -0,0 +1,81 @@ +const NEVER_MATCHES = /(?!)/; + +function isEscapedAt(source: string, dotIdx: number): boolean { + let backslashes = 0; + for (let i = dotIdx - 1; i >= 0 && source.charCodeAt(i) === 92; i--) { + backslashes++; + } + return backslashes % 2 === 1; +} + +function stripUnanchoredDecoration(source: string): string { + let s = source; + + if (s.startsWith('^.*?')) s = s.slice(4); + else if (s.startsWith('^.*')) s = s.slice(3); + else if (s.startsWith('^[\\s\\S]*?')) s = s.slice(9); + else if (s.startsWith('^[\\s\\S]*')) s = s.slice(8); + else if (s.startsWith('.*?')) s = s.slice(3); + else if (s.startsWith('.*')) s = s.slice(2); + else if (s.startsWith('[\\s\\S]*?')) s = s.slice(8); + else if (s.startsWith('[\\s\\S]*')) s = s.slice(7); + + if (s.endsWith('.*?$') && !isEscapedAt(s, s.length - 4)) s = s.slice(0, -4); + else if (s.endsWith('.*$') && !isEscapedAt(s, s.length - 3)) s = s.slice(0, -3); + else if (s.endsWith('[\\s\\S]*?$')) s = s.slice(0, -9); + else if (s.endsWith('[\\s\\S]*$')) s = s.slice(0, -8); + else if (s.endsWith('.*?') && !isEscapedAt(s, s.length - 3)) s = s.slice(0, -3); + else if (s.endsWith('.*') && !isEscapedAt(s, s.length - 2)) s = s.slice(0, -2); + else if (s.endsWith('[\\s\\S]*?')) s = s.slice(0, -8); + else if (s.endsWith('[\\s\\S]*')) s = s.slice(0, -7); + + if (s === '' || s === '^' || s === '$' || s === '^$') return source; + return s; +} + +function rankForOrdering(source: string): number { + if (source.startsWith('^')) return 0; + if (source.startsWith('.*') || source.startsWith('.*?') || source.startsWith('[\\s\\S]*')) { + return 2; + } + return 1; +} + +/** + * Composes Metro `blockList` regexes into a single ignore pattern, normalizing + * leading/trailing `.*` decoration so V8 can extract a literal prefilter from + * each alternative. + */ +export function composeMetroIgnorePatterns( + input: RegExp | readonly RegExp[] | null | undefined +): RegExp { + if (!input) return NEVER_MATCHES; + if (!Array.isArray(input)) { + const regex = input as RegExp; + return new RegExp(stripUnanchoredDecoration(regex.source), regex.flags); + } + + const patterns = input as readonly RegExp[]; + if (patterns.length === 0) return NEVER_MATCHES; + if (patterns.length === 1) { + const regex = patterns[0]!; + return new RegExp(stripUnanchoredDecoration(regex.source), regex.flags); + } + + const flags = patterns[0]!.flags; + for (let i = 1; i < patterns.length; i++) { + if (patterns[i]!.flags !== flags) { + throw new Error( + 'Cannot combine blockList patterns, because they have different flags:\n' + + ` - Pattern 0: ${patterns[0]!.toString()}\n` + + ` - Pattern ${i}: ${patterns[i]!.toString()}` + ); + } + } + + const sources = patterns + .map((regex) => stripUnanchoredDecoration(regex.source)) + .sort((a, b) => rankForOrdering(a) - rankForOrdering(b)) + .map((s) => '(?:' + s + ')'); + return new RegExp(sources.join('|'), flags); +} From 7c15baf825fc981781f4ad05ceaad3264cefa704 Mon Sep 17 00:00:00 2001 From: Jakub Tkacz <32908614+Ubax@users.noreply.github.com> Date: Fri, 8 May 2026 18:33:53 +0200 Subject: [PATCH 05/16] [expo-cli][expo-codemod] Improve the visiblity of errors for native-stack and drawer imports (#45546) # Why When `@react-navigation/native-stack` or `@react-navigation/drawer` is directly imported in the code users see general error message: image In this case, we can provide more context by pointing out that they should migrate to the `Stack` or `Drawer` navigators from Router. # How 1. Add a check to metro plugin for `moduleName === '@react-navigation/native-stack' || moduleName === '@react-navigation/drawer'`. In such case throw a more descriptive error 2. Add a check to codemod to notify users they should manually change these imports # Test Plan 1. CI 2. Local project **codemod** Screenshot 2026-05-08 at 15 13 03 **metro** Screenshot 2026-05-08 at 15 26 42 # Checklist - [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- packages/@expo/cli/CHANGELOG.md | 1 + .../server/metro/withMetroMultiPlatform.ts | 18 ++ packages/expo-codemod/CHANGELOG.md | 2 + ...-expo-router-react-navigation-replace.d.ts | 11 - ...56-expo-router-react-navigation-replace.js | 70 ++++++- ...xpo-router-react-navigation-replace.js.map | 2 +- ...po-router-react-navigation-replace-test.ts | 189 +++++++++++++++--- ...56-expo-router-react-navigation-replace.ts | 71 ++++++- 8 files changed, 314 insertions(+), 50 deletions(-) diff --git a/packages/@expo/cli/CHANGELOG.md b/packages/@expo/cli/CHANGELOG.md index edda6414fa63cb..32d16bf0d959f0 100644 --- a/packages/@expo/cli/CHANGELOG.md +++ b/packages/@expo/cli/CHANGELOG.md @@ -7,6 +7,7 @@ ### 🎉 New features - Prefix web client logs with platform name in Metro terminal output. ([#45516](https://github.com/expo/expo/pull/45516) by [@EvanBacon](https://github.com/EvanBacon)) +- Improve the visiblity of errors for native-stack and drawer imports ([#45546](https://github.com/expo/expo/pull/45546) by [@Ubax](https://github.com/Ubax)) ### 🐛 Bug fixes diff --git a/packages/@expo/cli/src/start/server/metro/withMetroMultiPlatform.ts b/packages/@expo/cli/src/start/server/metro/withMetroMultiPlatform.ts index 18dd804af43a44..4c219fd0b3773f 100644 --- a/packages/@expo/cli/src/start/server/metro/withMetroMultiPlatform.ts +++ b/packages/@expo/cli/src/start/server/metro/withMetroMultiPlatform.ts @@ -634,6 +634,24 @@ export function withExtendedResolver( if (isExpoRouterInstalled && moduleName.startsWith('@react-navigation/')) { const filePath = context.originModulePath; if (!filePath.includes('node_modules')) { + if ( + moduleName === '@react-navigation/native-stack' || + moduleName === '@react-navigation/drawer' + ) { + throw new Error( + [ + 'As of SDK 56, expo-router is no longer compatible with react-navigation.', + '', + `Instead of ${moduleName}, use Stack or Drawer from expo-router instead:`, + '', + " import { Stack } from 'expo-router';", + " import { Drawer } from 'expo-router/drawer';", + '', + 'For more information, see https://docs.expo.dev/router/migrate/sdk-55-to-56/.', + 'You can disable this check by setting the environment variable EXPO_ROUTER_DISABLE_RN_NAVIGATION_CHECK=1.', + ].join('\n') + ); + } throw new Error( 'As of SDK 56, expo-router is no longer compatible with react-navigation. For more information, see https://docs.expo.dev/router/migrate/sdk-55-to-56/. You can disable this check by setting the environment variable EXPO_ROUTER_DISABLE_RN_NAVIGATION_CHECK=1.' ); diff --git a/packages/expo-codemod/CHANGELOG.md b/packages/expo-codemod/CHANGELOG.md index e18543a372d6ca..d2aef07d8b4b55 100644 --- a/packages/expo-codemod/CHANGELOG.md +++ b/packages/expo-codemod/CHANGELOG.md @@ -6,6 +6,8 @@ ### 🎉 New features +- Improve the visiblity of errors for native-stack and drawer imports ([#45546](https://github.com/expo/expo/pull/45546) by [@Ubax](https://github.com/Ubax)) + ### 🐛 Bug fixes ### 💡 Others diff --git a/packages/expo-codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.d.ts b/packages/expo-codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.d.ts index ea8cf0dd988cdf..19f70e83c8cd44 100644 --- a/packages/expo-codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.d.ts +++ b/packages/expo-codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.d.ts @@ -1,14 +1,3 @@ -/** - * Codemod: Replace @react-navigation/* imports with expo-router equivalents. - * - * Mapping: - * @react-navigation/native → expo-router - * @react-navigation/stack → expo-router/js-stack - * @react-navigation/bottom-tabs → expo-router/js-tabs - * @react-navigation/material-top-tabs → expo-router/js-top-tabs - * - * After replacement, duplicate `expo-router` imports are merged into one. - */ import type { Transform } from 'jscodeshift'; declare const transform: Transform; export default transform; diff --git a/packages/expo-codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.js b/packages/expo-codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.js index 237dbd3c2d9ea1..3c0aa80c815022 100644 --- a/packages/expo-codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.js +++ b/packages/expo-codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.js @@ -1,5 +1,24 @@ "use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Codemod: Replace @react-navigation/* imports with expo-router equivalents. + * + * Mapping: + * @react-navigation/native → expo-router + * @react-navigation/stack → expo-router/js-stack + * @react-navigation/bottom-tabs → expo-router/js-tabs + * @react-navigation/material-top-tabs → expo-router/js-top-tabs + * + * Unsupported (no direct equivalent — throws to surface the migration step): + * @react-navigation/native-stack → use the `Stack` layout from expo-router + * @react-navigation/drawer → use the `Drawer` layout from expo-router + * + * After replacement, duplicate `expo-router` imports are merged into one. + */ +const chalk_1 = __importDefault(require("chalk")); const IMPORT_MAP = { '@react-navigation/native': 'expo-router/react-navigation', '@react-navigation/elements': 'expo-router/react-navigation', @@ -9,10 +28,21 @@ const IMPORT_MAP = { '@react-navigation/bottom-tabs': 'expo-router/js-tabs', '@react-navigation/material-top-tabs': 'expo-router/js-top-tabs', }; +const UNSUPPORTED_PACKAGES = { + '@react-navigation/native-stack': 'Use the `Stack` layout (https://docs.expo.dev/router/advanced/stack/) instead', + '@react-navigation/drawer': 'Use the `Drawer` layout (https://docs.expo.dev/router/advanced/drawer/) instead', +}; const UNSUPPORTED_SPECIFIERS = { ImportDefaultSpecifier: 'default import', ImportNamespaceSpecifier: 'namespace import (import * as ...)', }; +// Prints a prominent, color-formatted error block to stderr. +const printErrorBlock = (title, lines) => { + const divider = chalk_1.default.red.bold('━'.repeat(78)); + const heading = chalk_1.default.red.bold(` ${title}`); + const body = lines.map((line) => chalk_1.default.red(` ${line}`)); + console.error(['', divider, heading, divider, '', ...body, ''].join('\n')); +}; const isTypeOnlyImport = (path) => path.node.importKind === 'type'; /** * Clone the specifier, changing its importKind to be type, for example: @@ -23,6 +53,24 @@ const markAsInlineType = (spec) => { clone.importKind = 'type'; return clone; }; +/** + * Collects errors for imports from packages that have no direct expo-router + * equivalent (e.g. `@react-navigation/native-stack`, `@react-navigation/drawer`). + * These require a structural migration to the file-based `Stack`/`Drawer` + * layouts and cannot be rewritten automatically. + */ +const collectUnsupportedPackageErrors = (filePath, paths) => { + const errors = []; + for (const declarationPath of paths) { + const sourceModule = declarationPath.node.source.value; + const message = UNSUPPORTED_PACKAGES[sourceModule]; + if (!message) + continue; + const line = declarationPath.node.loc?.start.line ?? '?'; + errors.push(`${filePath}:${line} - import from "${sourceModule}" cannot be migrated. ${message}`); + } + return errors; +}; /** * Collects errors for unsupported import styles in the given declarations: * default imports (`import A from "b"`) and namespace imports @@ -39,8 +87,11 @@ const collectUnsupportedImportStyleErrors = (filePath, paths) => { continue; const line = declarationPath.node.loc?.start.line ?? '?'; const sourceModule = declarationPath.node.source.value; - errors.push(`${filePath}:${line} - ${label} from "${sourceModule}" is not supported. ` + - `Replace with named imports before running this codemod.`); + errors.push([ + `${filePath}:${line} - ${label} from "${sourceModule}" is not supported.`, + 'Only named imports can be rewritten by this codemod.', + 'Replace this import with named imports and re-run the codemod.', + ].join('\n')); } } return errors; @@ -80,6 +131,14 @@ const mergeGroup = (j, groupPaths) => { const transform = (fileInfo, api) => { const j = api.jscodeshift; const root = j(fileInfo.source); + const unsupportedPackagePaths = root + .find(j.ImportDeclaration) + .filter((path) => path.node.source.value in UNSUPPORTED_PACKAGES) + .paths(); + const unsupportedPackageErrors = collectUnsupportedPackageErrors(fileInfo.path, unsupportedPackagePaths); + if (unsupportedPackageErrors.length) { + printErrorBlock('Migration required — manual change needed', unsupportedPackageErrors); + } const mappablePaths = root .find(j.ImportDeclaration) .filter((path) => path.node.source.value in IMPORT_MAP) @@ -87,8 +146,11 @@ const transform = (fileInfo, api) => { if (mappablePaths.length === 0) return undefined; const errors = collectUnsupportedImportStyleErrors(fileInfo.path, mappablePaths); - if (errors.length > 0) { - throw new Error(`Unsupported import style(s) found:\n${errors.join('\n')}`); + if (errors.length) { + printErrorBlock('Unsupported import style — manual change needed', errors); + } + if (unsupportedPackageErrors.length || errors.length) { + return undefined; } for (const path of mappablePaths) { const sourceModule = path.node.source.value; diff --git a/packages/expo-codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.js.map b/packages/expo-codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.js.map index e68164b853cb2a..db09a2557422a6 100644 --- a/packages/expo-codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.js.map +++ b/packages/expo-codemod/build/transforms/sdk-56-expo-router-react-navigation-replace.js.map @@ -1 +1 @@ -{"version":3,"file":"sdk-56-expo-router-react-navigation-replace.js","sourceRoot":"","sources":["../../src/transforms/sdk-56-expo-router-react-navigation-replace.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"sdk-56-expo-router-react-navigation-replace.js","sourceRoot":"","sources":["../../src/transforms/sdk-56-expo-router-react-navigation-replace.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/expo-codemod/src/transforms/__tests__/sdk-56-expo-router-react-navigation-replace-test.ts b/packages/expo-codemod/src/transforms/__tests__/sdk-56-expo-router-react-navigation-replace-test.ts index c3df789a2e2ed3..c0763947e88c5c 100644 --- a/packages/expo-codemod/src/transforms/__tests__/sdk-56-expo-router-react-navigation-replace-test.ts +++ b/packages/expo-codemod/src/transforms/__tests__/sdk-56-expo-router-react-navigation-replace-test.ts @@ -2,6 +2,15 @@ import { applyTransform } from 'jscodeshift/dist/testUtils'; import transform from '../sdk-56-expo-router-react-navigation-replace'; +let errorSpy: jest.Mock; +beforeEach(() => { + errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); +}); + +afterEach(() => { + jest.restoreAllMocks(); +}); + // Default parser (babel): covers plain JS and syntactically-unambiguous TS. const run = (source: string): string => applyTransform(transform, {}, { source, path: 'this/is/test.tsx' }); @@ -200,56 +209,86 @@ describe('merging duplicate imports', () => { describe('unsupported import styles', () => { test('throws on default import from @react-navigation/native', () => { const input = `import Navigation from '@react-navigation/native';`; - expect(() => run(input)).toThrow( - 'Unsupported import style(s) found:\n' + - 'this/is/test.tsx:1 - default import from "@react-navigation/native" is not supported. ' + - 'Replace with named imports before running this codemod.' + run(input); + expect(errorSpy).toHaveBeenCalledTimes(1); + expect(errorSpy.mock.calls[0]).toHaveLength(1); + const message = errorSpy.mock.calls[0][0] as string; + expect(message).toContain('Unsupported import style'); + expect(message).toContain( + 'this/is/test.tsx:1 - default import from "@react-navigation/native" is not supported.' ); + expect(message).toContain('Only named imports can be rewritten by this codemod.'); + expect(message).toContain('Replace this import with named imports and re-run the codemod.'); }); test('throws on namespace import from @react-navigation/native', () => { const input = `import * as Nav from '@react-navigation/native';`; - expect(() => run(input)).toThrow( - 'Unsupported import style(s) found:\n' + - 'this/is/test.tsx:1 - namespace import (import * as ...) from "@react-navigation/native" is not supported. ' + - 'Replace with named imports before running this codemod.' + run(input); + expect(errorSpy).toHaveBeenCalledTimes(1); + expect(errorSpy.mock.calls[0]).toHaveLength(1); + const message = errorSpy.mock.calls[0][0] as string; + expect(message).toContain('Unsupported import style'); + expect(message).toContain( + 'this/is/test.tsx:1 - namespace import (import * as ...) from "@react-navigation/native" is not supported.' ); + expect(message).toContain('Only named imports can be rewritten by this codemod.'); + expect(message).toContain('Replace this import with named imports and re-run the codemod.'); }); test('throws on default import from @react-navigation/stack', () => { const input = `import Stack from '@react-navigation/stack';`; - expect(() => run(input)).toThrow( - 'Unsupported import style(s) found:\n' + - 'this/is/test.tsx:1 - default import from "@react-navigation/stack" is not supported. ' + - 'Replace with named imports before running this codemod.' + run(input); + expect(errorSpy).toHaveBeenCalledTimes(1); + expect(errorSpy.mock.calls[0]).toHaveLength(1); + const message = errorSpy.mock.calls[0][0] as string; + expect(message).toContain('Unsupported import style'); + expect(message).toContain( + 'this/is/test.tsx:1 - default import from "@react-navigation/stack" is not supported.' ); + expect(message).toContain('Only named imports can be rewritten by this codemod.'); + expect(message).toContain('Replace this import with named imports and re-run the codemod.'); }); test('throws on namespace import from @react-navigation/bottom-tabs', () => { const input = `import * as Tabs from '@react-navigation/bottom-tabs';`; - expect(() => run(input)).toThrow( - 'Unsupported import style(s) found:\n' + - 'this/is/test.tsx:1 - namespace import (import * as ...) from "@react-navigation/bottom-tabs" is not supported. ' + - 'Replace with named imports before running this codemod.' + run(input); + expect(errorSpy).toHaveBeenCalledTimes(1); + expect(errorSpy.mock.calls[0]).toHaveLength(1); + const message = errorSpy.mock.calls[0][0] as string; + expect(message).toContain('Unsupported import style'); + expect(message).toContain( + 'this/is/test.tsx:1 - namespace import (import * as ...) from "@react-navigation/bottom-tabs" is not supported.' ); + expect(message).toContain('Only named imports can be rewritten by this codemod.'); + expect(message).toContain('Replace this import with named imports and re-run the codemod.'); }); test('throws on default import from @react-navigation/material-top-tabs', () => { const input = `import TopTabs from '@react-navigation/material-top-tabs';`; - expect(() => run(input)).toThrow( - 'Unsupported import style(s) found:\n' + - 'this/is/test.tsx:1 - default import from "@react-navigation/material-top-tabs" is not supported. ' + - 'Replace with named imports before running this codemod.' + run(input); + expect(errorSpy).toHaveBeenCalledTimes(1); + expect(errorSpy.mock.calls[0]).toHaveLength(1); + const message = errorSpy.mock.calls[0][0] as string; + expect(message).toContain('Unsupported import style'); + expect(message).toContain( + 'this/is/test.tsx:1 - default import from "@react-navigation/material-top-tabs" is not supported.' ); + expect(message).toContain('Only named imports can be rewritten by this codemod.'); + expect(message).toContain('Replace this import with named imports and re-run the codemod.'); }); test('throws when default import is mixed with named imports', () => { const input = `import Navigation, { useNavigation } from '@react-navigation/native';`; - expect(() => run(input)).toThrow( - 'Unsupported import style(s) found:\n' + - 'this/is/test.tsx:1 - default import from "@react-navigation/native" is not supported. ' + - 'Replace with named imports before running this codemod.' + run(input); + expect(errorSpy).toHaveBeenCalledTimes(1); + expect(errorSpy.mock.calls[0]).toHaveLength(1); + const message = errorSpy.mock.calls[0][0] as string; + expect(message).toContain('Unsupported import style'); + expect(message).toContain( + 'this/is/test.tsx:1 - default import from "@react-navigation/native" is not supported.' ); + expect(message).toContain('Only named imports can be rewritten by this codemod.'); + expect(message).toContain('Replace this import with named imports and re-run the codemod.'); }); test('collects all errors when multiple unsupported imports exist', () => { @@ -257,12 +296,16 @@ describe('unsupported import styles', () => { `import Navigation from '@react-navigation/native';`, `import * as Stack from '@react-navigation/stack';`, ].join('\n'); - expect(() => run(input)).toThrow( - 'Unsupported import style(s) found:\n' + - 'this/is/test.tsx:1 - default import from "@react-navigation/native" is not supported. ' + - 'Replace with named imports before running this codemod.\n' + - 'this/is/test.tsx:2 - namespace import (import * as ...) from "@react-navigation/stack" is not supported. ' + - 'Replace with named imports before running this codemod.' + run(input); + expect(errorSpy).toHaveBeenCalledTimes(1); + expect(errorSpy.mock.calls[0]).toHaveLength(1); + const message = errorSpy.mock.calls[0][0] as string; + expect(message).toContain('Unsupported import style'); + expect(message).toContain( + 'this/is/test.tsx:1 - default import from "@react-navigation/native" is not supported.' + ); + expect(message).toContain( + 'this/is/test.tsx:2 - namespace import (import * as ...) from "@react-navigation/stack" is not supported.' ); }); @@ -277,6 +320,92 @@ describe('unsupported import styles', () => { }); }); +describe('unsupported packages (no direct equivalent)', () => { + test('throws on import from @react-navigation/native-stack', () => { + const input = `import { createNativeStackNavigator } from '@react-navigation/native-stack';`; + run(input); + expect(errorSpy).toHaveBeenCalledTimes(1); + expect(errorSpy.mock.calls[0]).toHaveLength(1); + const message = errorSpy.mock.calls[0][0] as string; + expect(message).toContain('Migration required'); + expect(message).toContain( + 'this/is/test.tsx:1 - import from "@react-navigation/native-stack" cannot be migrated.' + ); + expect(message).toContain( + 'Use the `Stack` layout (https://docs.expo.dev/router/advanced/stack/) instead' + ); + }); + + test('throws on import from @react-navigation/drawer', () => { + const input = `import { createDrawerNavigator } from '@react-navigation/drawer';`; + run(input); + expect(errorSpy).toHaveBeenCalledTimes(1); + expect(errorSpy.mock.calls[0]).toHaveLength(1); + const message = errorSpy.mock.calls[0][0] as string; + expect(message).toContain('Migration required'); + expect(message).toContain( + 'this/is/test.tsx:1 - import from "@react-navigation/drawer" cannot be migrated.' + ); + expect(message).toContain( + 'Use the `Drawer` layout (https://docs.expo.dev/router/advanced/drawer/) instead' + ); + }); + + test('throws on type-only import from @react-navigation/native-stack', () => { + const input = `import type { NativeStackScreenProps } from '@react-navigation/native-stack';`; + runTS(input); + expect(errorSpy).toHaveBeenCalledTimes(1); + expect(errorSpy.mock.calls[0]).toHaveLength(1); + const message = errorSpy.mock.calls[0][0] as string; + expect(message).toContain('Migration required'); + expect(message).toContain( + 'undefined:1 - import from "@react-navigation/native-stack" cannot be migrated.' + ); + }); + + test('throws on default import from @react-navigation/drawer', () => { + // Unsupported package check runs before unsupported import-style check. + const input = `import Drawer from '@react-navigation/drawer';`; + run(input); + expect(errorSpy).toHaveBeenCalledTimes(1); + expect(errorSpy.mock.calls[0]).toHaveLength(1); + const message = errorSpy.mock.calls[0][0] as string; + expect(message).toContain('Migration required'); + expect(message).toContain( + 'this/is/test.tsx:1 - import from "@react-navigation/drawer" cannot be migrated.' + ); + }); + + test('reports all unsupported packages found in a file', () => { + const input = [ + `import { createNativeStackNavigator } from '@react-navigation/native-stack';`, + `import { createDrawerNavigator } from '@react-navigation/drawer';`, + ].join('\n'); + run(input); + expect(errorSpy).toHaveBeenCalledTimes(1); + expect(errorSpy.mock.calls[0]).toHaveLength(1); + const message = errorSpy.mock.calls[0][0] as string; + expect(message).toContain( + 'this/is/test.tsx:1 - import from "@react-navigation/native-stack" cannot be migrated.' + ); + expect(message).toContain( + 'this/is/test.tsx:2 - import from "@react-navigation/drawer" cannot be migrated.' + ); + }); + + test('throws even when other react-navigation imports would otherwise be rewritten', () => { + const input = [ + `import { useNavigation } from '@react-navigation/native';`, + `import { createDrawerNavigator } from '@react-navigation/drawer';`, + ].join('\n'); + run(input); + expect(errorSpy).toHaveBeenCalledTimes(1); + expect(errorSpy.mock.calls[0]).toHaveLength(1); + const message = errorSpy.mock.calls[0][0] as string; + expect(message).toContain('import from "@react-navigation/drawer" cannot be migrated'); + }); +}); + describe('type imports', () => { test('replaces import type from @react-navigation/native', () => { const output = runTS(`import type { ScreenProps } from '@react-navigation/native';`); diff --git a/packages/expo-codemod/src/transforms/sdk-56-expo-router-react-navigation-replace.ts b/packages/expo-codemod/src/transforms/sdk-56-expo-router-react-navigation-replace.ts index c4901cb54f9997..d2b75bd235a490 100644 --- a/packages/expo-codemod/src/transforms/sdk-56-expo-router-react-navigation-replace.ts +++ b/packages/expo-codemod/src/transforms/sdk-56-expo-router-react-navigation-replace.ts @@ -7,8 +7,13 @@ * @react-navigation/bottom-tabs → expo-router/js-tabs * @react-navigation/material-top-tabs → expo-router/js-top-tabs * + * Unsupported (no direct equivalent — throws to surface the migration step): + * @react-navigation/native-stack → use the `Stack` layout from expo-router + * @react-navigation/drawer → use the `Drawer` layout from expo-router + * * After replacement, duplicate `expo-router` imports are merged into one. */ +import chalk from 'chalk'; import type { ASTPath, ImportDeclaration, @@ -38,11 +43,26 @@ const IMPORT_MAP: Record = { '@react-navigation/material-top-tabs': 'expo-router/js-top-tabs', }; +const UNSUPPORTED_PACKAGES: Record = { + '@react-navigation/native-stack': + 'Use the `Stack` layout (https://docs.expo.dev/router/advanced/stack/) instead', + '@react-navigation/drawer': + 'Use the `Drawer` layout (https://docs.expo.dev/router/advanced/drawer/) instead', +}; + const UNSUPPORTED_SPECIFIERS: Partial> = { ImportDefaultSpecifier: 'default import', ImportNamespaceSpecifier: 'namespace import (import * as ...)', }; +// Prints a prominent, color-formatted error block to stderr. +const printErrorBlock = (title: string, lines: string[]): void => { + const divider = chalk.red.bold('━'.repeat(78)); + const heading = chalk.red.bold(` ${title}`); + const body = lines.map((line) => chalk.red(` ${line}`)); + console.error(['', divider, heading, divider, '', ...body, ''].join('\n')); +}; + const isTypeOnlyImport = (path: ASTPath): boolean => path.node.importKind === 'type'; @@ -56,6 +76,29 @@ const markAsInlineType = (spec: T): T => { return clone; }; +/** + * Collects errors for imports from packages that have no direct expo-router + * equivalent (e.g. `@react-navigation/native-stack`, `@react-navigation/drawer`). + * These require a structural migration to the file-based `Stack`/`Drawer` + * layouts and cannot be rewritten automatically. + */ +const collectUnsupportedPackageErrors = ( + filePath: string, + paths: ASTPath[] +): string[] => { + const errors: string[] = []; + for (const declarationPath of paths) { + const sourceModule = declarationPath.node.source.value as string; + const message = UNSUPPORTED_PACKAGES[sourceModule]; + if (!message) continue; + const line = declarationPath.node.loc?.start.line ?? '?'; + errors.push( + `${filePath}:${line} - import from "${sourceModule}" cannot be migrated. ${message}` + ); + } + return errors; +}; + /** * Collects errors for unsupported import styles in the given declarations: * default imports (`import A from "b"`) and namespace imports @@ -75,8 +118,11 @@ const collectUnsupportedImportStyleErrors = ( const line = declarationPath.node.loc?.start.line ?? '?'; const sourceModule = declarationPath.node.source.value as string; errors.push( - `${filePath}:${line} - ${label} from "${sourceModule}" is not supported. ` + - `Replace with named imports before running this codemod.` + [ + `${filePath}:${line} - ${label} from "${sourceModule}" is not supported.`, + 'Only named imports can be rewritten by this codemod.', + 'Replace this import with named imports and re-run the codemod.', + ].join('\n') ); } } @@ -130,6 +176,19 @@ const transform: Transform = (fileInfo, api) => { const j = api.jscodeshift; const root = j(fileInfo.source); + const unsupportedPackagePaths = root + .find(j.ImportDeclaration) + .filter((path) => (path.node.source.value as string) in UNSUPPORTED_PACKAGES) + .paths(); + + const unsupportedPackageErrors = collectUnsupportedPackageErrors( + fileInfo.path, + unsupportedPackagePaths + ); + if (unsupportedPackageErrors.length) { + printErrorBlock('Migration required — manual change needed', unsupportedPackageErrors); + } + const mappablePaths = root .find(j.ImportDeclaration) .filter((path) => (path.node.source.value as string) in IMPORT_MAP) @@ -138,8 +197,12 @@ const transform: Transform = (fileInfo, api) => { if (mappablePaths.length === 0) return undefined; const errors = collectUnsupportedImportStyleErrors(fileInfo.path, mappablePaths); - if (errors.length > 0) { - throw new Error(`Unsupported import style(s) found:\n${errors.join('\n')}`); + if (errors.length) { + printErrorBlock('Unsupported import style — manual change needed', errors); + } + + if (unsupportedPackageErrors.length || errors.length) { + return undefined; } for (const path of mappablePaths) { From b0c7e8347a035803d419c12a5a6dc7bbb9333d64 Mon Sep 17 00:00:00 2001 From: Jakub Tkacz <32908614+Ubax@users.noreply.github.com> Date: Fri, 8 May 2026 18:34:40 +0200 Subject: [PATCH 06/16] [expo-router] export useRoute hook from expo-router (#45557) # Why I think this hook is also useful as part of public API. # How Reexport it from react-navigation # Test Plan # Checklist - [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- docs/public/static/data/unversioned/expo-router.json | 2 +- .../static/data/unversioned/expo-router/native-tabs.json | 2 +- docs/public/static/data/v56.0.0/expo-router.json | 2 +- docs/public/static/data/v56.0.0/expo-router/native-tabs.json | 2 +- packages/expo-router/CHANGELOG.md | 2 ++ packages/expo-router/build/exports.d.ts | 1 + packages/expo-router/build/exports.d.ts.map | 2 +- packages/expo-router/build/exports.js | 4 +++- packages/expo-router/build/exports.js.map | 2 +- packages/expo-router/build/react-navigation/core/index.d.ts | 3 +-- .../expo-router/build/react-navigation/core/index.d.ts.map | 2 +- packages/expo-router/build/react-navigation/core/index.js | 3 +-- packages/expo-router/build/react-navigation/core/index.js.map | 2 +- packages/expo-router/src/exports.ts | 1 + packages/expo-router/src/react-navigation/core/index.tsx | 3 +-- 15 files changed, 18 insertions(+), 15 deletions(-) diff --git a/docs/public/static/data/unversioned/expo-router.json b/docs/public/static/data/unversioned/expo-router.json index 84b039588125fb..18acb8e9581942 100644 --- a/docs/public/static/data/unversioned/expo-router.json +++ b/docs/public/static/data/unversioned/expo-router.json @@ -1 +1 @@ -{"schemaVersion":"2.0","name":"expo-router","variant":"project","kind":1,"children":[{"name":"Badge","variant":"declaration","kind":64,"signatures":[{"name":"Badge","variant":"signature","kind":4096,"parameters":[{"name":"props","variant":"param","kind":32768,"type":{"type":"reference","name":"BadgeProps","package":"expo-router"}}],"type":{"type":"literal","value":null}}]},{"name":"BadgeProps","variant":"declaration","kind":2097152,"type":{"type":"union","types":[{"type":"reference","target":{"packageName":"expo-router","packagePath":"src/native-tabs/common/elements.tsx","qualifiedName":"NativeTabsTriggerBadgeProps"},"name":"NativeTabsTriggerBadgeProps","package":"expo-router"},{"type":"reference","target":{"packageName":"expo-router","packagePath":"src/layouts/stack-utils/toolbar/toolbar-primitives.tsx","qualifiedName":"StackToolbarBadgeProps"},"name":"StackToolbarBadgeProps","package":"expo-router"}]}},{"name":"DarkTheme","variant":"declaration","kind":32,"flags":{"isConst":true},"type":{"type":"reference","target":{"packageName":"expo-router","packagePath":"src/react-navigation/native/types.tsx","qualifiedName":"Theme"},"name":"Theme","package":"expo-router"},"defaultValue":"..."},{"name":"DefaultTheme","variant":"declaration","kind":32,"flags":{"isConst":true},"type":{"type":"reference","target":{"packageName":"expo-router","packagePath":"src/react-navigation/native/types.tsx","qualifiedName":"Theme"},"name":"Theme","package":"expo-router"},"defaultValue":"..."},{"name":"EffectCallback","variant":"declaration","kind":2097152,"comment":{"summary":[{"kind":"text","text":"Memoized callback containing the effect, should optionally return a cleanup function."}]},"type":{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"signatures":[{"name":"__type","variant":"signature","kind":4096,"type":{"type":"union","types":[{"type":"intrinsic","name":"undefined"},{"type":"intrinsic","name":"void"},{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"signatures":[{"name":"__type","variant":"signature","kind":4096,"type":{"type":"intrinsic","name":"void"}}]}}]}}]}}},{"name":"ErrorBoundary","variant":"declaration","kind":64,"signatures":[{"name":"ErrorBoundary","variant":"signature","kind":4096,"parameters":[{"name":"__namedParameters","variant":"param","kind":32768,"type":{"type":"reference","name":"ErrorBoundaryProps","package":"expo-router"}}],"type":{"type":"reference","target":{"packageName":"@types/react","packagePath":"jsx-runtime.d.ts","qualifiedName":"JSX.Element"},"name":"Element","package":"@types/react","qualifiedName":"JSX.Element"}}]},{"name":"ErrorBoundaryProps","variant":"declaration","kind":2097152,"comment":{"summary":[{"kind":"text","text":"Props passed to a page's "},{"kind":"code","text":"`ErrorBoundary`"},{"kind":"text","text":" export."}]},"children":[{"name":"error","variant":"declaration","kind":1024,"comment":{"summary":[{"kind":"text","text":"The error that was thrown."}]},"type":{"type":"reference","target":{"packageName":"typescript","packagePath":"lib/lib.es5.d.ts","qualifiedName":"Error"},"name":"Error","package":"typescript"}},{"name":"retry","variant":"declaration","kind":1024,"comment":{"summary":[{"kind":"text","text":"A function that will re-render the route component by clearing the "},{"kind":"code","text":"`error`"},{"kind":"text","text":" state."}]},"type":{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"signatures":[{"name":"__type","variant":"signature","kind":4096,"type":{"type":"reference","target":{"packageName":"typescript","packagePath":"lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"void"}],"name":"Promise","package":"typescript"}}]}}}]},{"name":"ExperimentalStack","variant":"declaration","kind":32,"flags":{"isConst":true},"comment":{"summary":[{"kind":"text","text":"Renders the new "},{"kind":"code","text":"`react-native-screens/experimental`"},{"kind":"text","text":" native stack.\n\nSibling to "},{"kind":"code","text":"`Stack`"},{"kind":"text","text":". Native-only — on web it falls back to the standard "},{"kind":"code","text":"`Stack`"},{"kind":"text","text":".\nOpt-in per navigator: replace "},{"kind":"code","text":"``"},{"kind":"text","text":" with "},{"kind":"code","text":"``"},{"kind":"text","text":" in the\nspecific layout you want to migrate."}],"modifierTags":["@experimental"]},"type":{"type":"intersection","types":[{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"signatures":[{"name":"__type","variant":"signature","kind":4096,"parameters":[{"name":"props","variant":"param","kind":32768,"type":{"type":"intersection","types":[{"type":"reference","target":{"packageName":"typescript","packagePath":"lib/lib.es5.d.ts","qualifiedName":"Omit"},"typeArguments":[{"type":"intersection","types":[{"type":"reference","target":{"packageName":"typescript","packagePath":"lib/lib.es5.d.ts","qualifiedName":"Omit"},"typeArguments":[{"type":"reference","target":{"packageName":"expo-router","packagePath":"src/layouts/experimental-stack/types.ts","qualifiedName":"ExperimentalStackNavigatorProps"},"name":"ExperimentalStackNavigatorProps","package":"expo-router"},{"type":"union","types":[{"type":"literal","value":"children"},{"type":"literal","value":"initialRouteName"},{"type":"literal","value":"layout"},{"type":"literal","value":"screenListeners"},{"type":"literal","value":"screenOptions"},{"type":"literal","value":"screenLayout"},{"type":"literal","value":"UNSTABLE_router"},{"type":"literal","value":"UNSTABLE_routeNamesChangeBehavior"},{"type":"literal","value":"id"}]}],"name":"Omit","package":"typescript"},{"type":"reference","target":{"packageName":"expo-router","packagePath":"src/react-navigation/routers/types.tsx","qualifiedName":"DefaultRouterOptions"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"DefaultRouterOptions","package":"expo-router"},{"type":"unknown","name":"{ children: ReactNode; layout?: ((props: { state: StackNavigationState; navigation: NavigationHelpers; descriptors: Record<...>; children: ReactNode; }) => ReactElement<...>) | undefined; ... 4 more ...; UNSTABLE_routeNamesChangeBehavior?: \"firstMatch\" | ... 1 more ... | undefined; ..."}]},{"type":"literal","value":"children"}],"name":"Omit","package":"typescript"},{"type":"reference","target":{"packageName":"typescript","packagePath":"lib/lib.es5.d.ts","qualifiedName":"Partial"},"typeArguments":[{"type":"reference","target":{"packageName":"typescript","packagePath":"lib/lib.es5.d.ts","qualifiedName":"Pick"},"typeArguments":[{"type":"intersection","types":[{"type":"reference","target":{"packageName":"typescript","packagePath":"lib/lib.es5.d.ts","qualifiedName":"Omit"},"typeArguments":[{"type":"reference","target":{"packageName":"expo-router","packagePath":"src/layouts/experimental-stack/types.ts","qualifiedName":"ExperimentalStackNavigatorProps"},"name":"ExperimentalStackNavigatorProps","package":"expo-router"},{"type":"union","types":[{"type":"literal","value":"children"},{"type":"literal","value":"initialRouteName"},{"type":"literal","value":"layout"},{"type":"literal","value":"screenListeners"},{"type":"literal","value":"screenOptions"},{"type":"literal","value":"screenLayout"},{"type":"literal","value":"UNSTABLE_router"},{"type":"literal","value":"UNSTABLE_routeNamesChangeBehavior"},{"type":"literal","value":"id"}]}],"name":"Omit","package":"typescript"},{"type":"reference","target":{"packageName":"expo-router","packagePath":"src/react-navigation/routers/types.tsx","qualifiedName":"DefaultRouterOptions"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"DefaultRouterOptions","package":"expo-router"},{"type":"unknown","name":"{ children: ReactNode; layout?: ((props: { state: StackNavigationState; navigation: NavigationHelpers; descriptors: Record<...>; children: ReactNode; }) => ReactElement<...>) | undefined; ... 4 more ...; UNSTABLE_routeNamesChangeBehavior?: \"firstMatch\" | ... 1 more ... | undefined; ..."}]},{"type":"literal","value":"children"}],"name":"Pick","package":"typescript"}],"name":"Partial","package":"typescript"},{"type":"reference","target":{"packageName":"@types/react","packagePath":"index.d.ts","qualifiedName":"React.RefAttributes"},"typeArguments":[{"type":"intrinsic","name":"unknown"}],"name":"RefAttributes","package":"@types/react","qualifiedName":"React.RefAttributes"}]}}],"type":{"type":"reference","target":{"packageName":"@types/react","packagePath":"jsx-runtime.d.ts","qualifiedName":"JSX.Element"},"name":"Element","package":"@types/react","qualifiedName":"JSX.Element"}}]}},{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"children":[{"name":"Protected","variant":"declaration","kind":1024,"type":{"type":"reference","target":{"packageName":"@types/react","packagePath":"index.d.ts","qualifiedName":"React.FunctionComponent"},"typeArguments":[{"type":"reference","target":{"packageName":"expo-router","packagePath":"src/views/Protected.tsx","qualifiedName":"ProtectedProps"},"name":"ProtectedProps","package":"expo-router"}],"name":"FunctionComponent","package":"@types/react","qualifiedName":"React.FunctionComponent"}},{"name":"Screen","variant":"declaration","kind":1024,"type":{"type":"intersection","types":[{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"signatures":[{"name":"__type","variant":"signature","kind":4096,"parameters":[{"name":"__namedParameters","variant":"param","kind":32768,"type":{"type":"reference","target":{"packageName":"expo-router","packagePath":"src/layouts/stack-utils/StackScreen.tsx","qualifiedName":"StackScreenProps"},"name":"StackScreenProps","package":"expo-router"}}],"type":{"type":"reference","target":{"packageName":"@types/react","packagePath":"jsx-runtime.d.ts","qualifiedName":"JSX.Element"},"name":"Element","package":"@types/react","qualifiedName":"JSX.Element"}}]}},{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"children":[{"name":"BackButton","variant":"declaration","kind":1024,"type":{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"signatures":[{"name":"__type","variant":"signature","kind":4096,"comment":{"summary":[{"kind":"text","text":"Component to configure the back button.\n\nCan be used inside Stack.Screen in a layout or directly inside a screen component."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```tsx\nimport { Stack } from 'expo-router';\n\nexport default function Layout() {\n return (\n \n \n Back\n \n \n );\n}\n```"}]},{"tag":"@example","content":[{"kind":"code","text":"```tsx\nimport { Stack } from 'expo-router';\n\nexport default function Page() {\n return (\n <>\n