@expo/cli 56.1.5 → 56.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/bin/cli +1 -1
- package/build/src/api/rest/client.js +27 -12
- package/build/src/api/rest/client.js.map +1 -1
- package/build/src/api/user/UserSettings.js +17 -4
- package/build/src/api/user/UserSettings.js.map +1 -1
- package/build/src/events/index.js +1 -1
- package/build/src/export/embed/exportEmbedAsync.js +3 -3
- package/build/src/export/embed/exportEmbedAsync.js.map +1 -1
- package/build/src/export/embed/exportServer.js +1 -1
- package/build/src/export/embed/exportServer.js.map +1 -1
- package/build/src/export/exportApp.js +1 -1
- package/build/src/export/exportApp.js.map +1 -1
- package/build/src/export/publicFolder.js +19 -1
- package/build/src/export/publicFolder.js.map +1 -1
- package/build/src/install/utils/checkPackagesCompatibility.js +32 -16
- package/build/src/install/utils/checkPackagesCompatibility.js.map +1 -1
- package/build/src/login/index.js +25 -5
- package/build/src/login/index.js.map +1 -1
- package/build/src/prebuild/resolveTemplate.js +10 -5
- package/build/src/prebuild/resolveTemplate.js.map +1 -1
- package/build/src/run/android/resolveLaunchProps.js +4 -1
- package/build/src/run/android/resolveLaunchProps.js.map +1 -1
- package/build/src/start/doctor/dependencies/reactNativeTv.js +149 -0
- package/build/src/start/doctor/dependencies/reactNativeTv.js.map +1 -0
- package/build/src/start/doctor/dependencies/validateDependenciesVersions.js +28 -3
- package/build/src/start/doctor/dependencies/validateDependenciesVersions.js.map +1 -1
- package/build/src/start/platforms/AppIdResolver.js +4 -0
- package/build/src/start/platforms/AppIdResolver.js.map +1 -1
- package/build/src/start/platforms/android/adb.js +16 -15
- package/build/src/start/platforms/android/adb.js.map +1 -1
- package/build/src/start/server/DevToolsPlugin.js +26 -1
- package/build/src/start/server/DevToolsPlugin.js.map +1 -1
- package/build/src/start/server/DevToolsPluginCliExtensionExecutor.js +57 -22
- package/build/src/start/server/DevToolsPluginCliExtensionExecutor.js.map +1 -1
- package/build/src/start/server/DevToolsPluginCliExtensionResults.js +29 -0
- package/build/src/start/server/DevToolsPluginCliExtensionResults.js.map +1 -1
- package/build/src/start/server/MCPDevToolsPluginCLIExtensions.js +15 -5
- package/build/src/start/server/MCPDevToolsPluginCLIExtensions.js.map +1 -1
- package/build/src/start/server/UrlCreator.js +14 -1
- package/build/src/start/server/UrlCreator.js.map +1 -1
- package/build/src/start/server/createMCPDevToolsExtensionSchema.js +13 -1
- package/build/src/start/server/createMCPDevToolsExtensionSchema.js.map +1 -1
- package/build/src/start/server/getStaticRenderFunctions.js +2 -1
- package/build/src/start/server/getStaticRenderFunctions.js.map +1 -1
- package/build/src/start/server/metro/MetroBundlerDevServer.js +44 -0
- package/build/src/start/server/metro/MetroBundlerDevServer.js.map +1 -1
- package/build/src/start/server/metro/createServerComponentsMiddleware.js +13 -13
- package/build/src/start/server/metro/createServerComponentsMiddleware.js.map +1 -1
- package/build/src/start/server/metro/debugging/createDebugMiddleware.js +5 -4
- package/build/src/start/server/metro/debugging/createDebugMiddleware.js.map +1 -1
- package/build/src/start/server/metro/debugging/messageHandlers/NetworkResponse.js +17 -1
- package/build/src/start/server/metro/debugging/messageHandlers/NetworkResponse.js.map +1 -1
- package/build/src/start/server/metro/dev-server/createMessageSocket.js +13 -2
- package/build/src/start/server/metro/dev-server/createMessageSocket.js.map +1 -1
- package/build/src/start/server/metro/dev-server/createMetroMiddleware.js +9 -2
- package/build/src/start/server/metro/dev-server/createMetroMiddleware.js.map +1 -1
- package/build/src/start/server/metro/instantiateMetro.js +8 -5
- package/build/src/start/server/metro/instantiateMetro.js.map +1 -1
- package/build/src/start/server/metro/metroErrorInterface.js +5 -2
- package/build/src/start/server/metro/metroErrorInterface.js.map +1 -1
- package/build/src/start/server/metro/router.js +10 -1
- package/build/src/start/server/metro/router.js.map +1 -1
- package/build/src/start/server/metro/withMetroMultiPlatform.js +8 -0
- package/build/src/start/server/metro/withMetroMultiPlatform.js.map +1 -1
- package/build/src/start/server/middleware/InterstitialPageMiddleware.js +7 -4
- package/build/src/start/server/middleware/InterstitialPageMiddleware.js.map +1 -1
- package/build/src/start/server/middleware/OpenMiddleware.js +150 -0
- package/build/src/start/server/middleware/OpenMiddleware.js.map +1 -0
- package/build/src/start/server/middleware/RuntimeRedirectMiddleware.js +13 -4
- package/build/src/start/server/middleware/RuntimeRedirectMiddleware.js.map +1 -1
- package/build/src/start/server/middleware/ServeStaticMiddleware.js +2 -9
- package/build/src/start/server/middleware/ServeStaticMiddleware.js.map +1 -1
- package/build/src/start/server/middleware/inspector/createJsInspectorMiddleware.js +14 -24
- package/build/src/start/server/middleware/inspector/createJsInspectorMiddleware.js.map +1 -1
- package/build/src/start/server/middleware/openHandlers.js +157 -0
- package/build/src/start/server/middleware/openHandlers.js.map +1 -0
- package/build/src/start/server/webTemplate.js +3 -5
- package/build/src/start/server/webTemplate.js.map +1 -1
- package/build/src/utils/codesigning.js +6 -0
- package/build/src/utils/codesigning.js.map +1 -1
- package/build/src/utils/env.js +29 -6
- package/build/src/utils/env.js.map +1 -1
- package/build/src/utils/net.js +20 -1
- package/build/src/utils/net.js.map +1 -1
- package/build/src/utils/open.js +2 -5
- package/build/src/utils/open.js.map +1 -1
- package/build/src/utils/tar.js +2 -2
- package/build/src/utils/tar.js.map +1 -1
- package/build/src/utils/telemetry/clients/FetchClient.js +1 -1
- package/build/src/utils/telemetry/utils/context.js +1 -1
- package/build/src/utils/url.js +0 -12
- package/build/src/utils/url.js.map +1 -1
- package/package.json +22 -22
- package/static/loading-page/index.html +10 -2
|
@@ -28,6 +28,13 @@ function _paths() {
|
|
|
28
28
|
};
|
|
29
29
|
return data;
|
|
30
30
|
}
|
|
31
|
+
function _router() {
|
|
32
|
+
const data = require("@expo/router-server/build/rsc/router");
|
|
33
|
+
_router = function() {
|
|
34
|
+
return data;
|
|
35
|
+
};
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
31
38
|
function _assert() {
|
|
32
39
|
const data = /*#__PURE__*/ _interop_require_default(require("assert"));
|
|
33
40
|
_assert = function() {
|
|
@@ -72,7 +79,7 @@ function _interop_require_default(obj) {
|
|
|
72
79
|
const debug = require('debug')('expo:rsc');
|
|
73
80
|
const getMetroServerRootMemo = (0, _fn.memoize)(_paths().getMetroServerRoot);
|
|
74
81
|
function createServerComponentsMiddleware(projectRoot, { rscPath, instanceMetroOptions, ssrLoadModule, ssrLoadModuleArtifacts, useClientRouter, createModuleId, routerOptions }) {
|
|
75
|
-
const routerModule =
|
|
82
|
+
const routerModule = (0, _router().resolveRouterModule)(useClientRouter);
|
|
76
83
|
const rscMiddleware = (0, _private().getRscMiddleware)({
|
|
77
84
|
config: {},
|
|
78
85
|
// Disabled in development
|
|
@@ -365,23 +372,14 @@ function createServerComponentsMiddleware(projectRoot, { rscPath, instanceMetroO
|
|
|
365
372
|
rscRendererCache.set(platform, renderer);
|
|
366
373
|
return renderer;
|
|
367
374
|
}
|
|
368
|
-
const rscRenderContext = new Map();
|
|
369
|
-
function getRscRenderContext(platform) {
|
|
370
|
-
// NOTE(EvanBacon): We memoize this now that there's a persistent server storage cache for Server Actions.
|
|
371
|
-
if (rscRenderContext.has(platform)) {
|
|
372
|
-
return rscRenderContext.get(platform);
|
|
373
|
-
}
|
|
374
|
-
const context = {};
|
|
375
|
-
rscRenderContext.set(platform, context);
|
|
376
|
-
return context;
|
|
377
|
-
}
|
|
378
375
|
async function renderRscToReadableStream({ input, headers, method, platform, body, engine, contentType, ssrManifest, decodedBody, routerOptions }, isExporting = instanceMetroOptions.isExporting) {
|
|
379
376
|
(0, _assert().default)(isExporting != null, 'The server must be started before calling renderRscToReadableStream.');
|
|
380
377
|
if (method === 'POST') {
|
|
381
378
|
(0, _assert().default)(body, 'Server request must be provided when method is POST (server actions)');
|
|
382
379
|
}
|
|
383
|
-
const context =
|
|
384
|
-
|
|
380
|
+
const context = {
|
|
381
|
+
__expo_requestHeaders: headers
|
|
382
|
+
};
|
|
385
383
|
const { renderRsc } = await getRscRendererAsync(platform);
|
|
386
384
|
return renderRsc({
|
|
387
385
|
body,
|
|
@@ -389,6 +387,8 @@ function createServerComponentsMiddleware(projectRoot, { rscPath, instanceMetroO
|
|
|
389
387
|
context,
|
|
390
388
|
config: {},
|
|
391
389
|
input,
|
|
390
|
+
method,
|
|
391
|
+
headers: Object.fromEntries(headers.entries()),
|
|
392
392
|
contentType
|
|
393
393
|
}, {
|
|
394
394
|
isExporting,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/start/server/metro/createServerComponentsMiddleware.ts"],"sourcesContent":["/**\n * Copyright © 2022 650 Industries.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\nimport { getMetroServerRoot } from '@expo/config/paths';\nimport type { SerialAsset } from '@expo/metro-config/build/serializer/serializerAssets';\nimport type { EntriesDev } from '@expo/router-server/build/rsc/server';\nimport assert from 'assert';\nimport { getRscMiddleware } from 'expo-server/private';\nimport path from 'node:path';\nimport url from 'node:url';\n\nimport { IS_METRO_BUNDLE_ERROR_SYMBOL, logMetroError } from './metroErrorInterface';\nimport { isPossiblyUnableToResolveError } from '../../../export/embed/xcodeCompilerLogger';\nimport type { ExportAssetMap } from '../../../export/saveAssets';\nimport { stripAnsi } from '../../../utils/ansi';\nimport { toPosixPath } from '../../../utils/filePath';\nimport { memoize } from '../../../utils/fn';\nimport { getIpAddressAsync } from '../../../utils/ip';\nimport { streamToStringAsync } from '../../../utils/stream';\nimport {\n createBundleUrlSearchParams,\n type ExpoMetroOptions,\n getMetroOptionsFromUrl,\n} from '../middleware/metroOptions';\n\nconst debug = require('debug')('expo:rsc') as typeof console.log;\n\ntype SSRLoadModuleArtifactsFunc = (\n filePath: string,\n specificOptions?: Partial<ExpoMetroOptions>\n) => Promise<{ artifacts: SerialAsset[]; src: string }>;\n\ntype SSRLoadModuleFunc = <T extends Record<string, any>>(\n filePath: string | null,\n specificOptions?: Partial<ExpoMetroOptions>,\n extras?: { hot?: boolean }\n) => Promise<T>;\n\nconst getMetroServerRootMemo = memoize(getMetroServerRoot);\n\n// TODO(@hassankhan): Rename this to `createRscRenderer()`\nexport function createServerComponentsMiddleware(\n projectRoot: string,\n {\n rscPath,\n instanceMetroOptions,\n ssrLoadModule,\n ssrLoadModuleArtifacts,\n useClientRouter,\n createModuleId,\n routerOptions,\n }: {\n rscPath: string;\n instanceMetroOptions: Partial<ExpoMetroOptions>;\n ssrLoadModule: SSRLoadModuleFunc;\n ssrLoadModuleArtifacts: SSRLoadModuleArtifactsFunc;\n useClientRouter: boolean;\n createModuleId: (\n filePath: string,\n context: { platform: string; environment: string }\n ) => string | number;\n routerOptions: Record<string, any>;\n }\n) {\n const routerModule = useClientRouter\n ? require.resolve('@expo/router-server/build/rsc/router/noopRouter')\n : require.resolve('@expo/router-server/build/rsc/router/expo-definedRouter');\n\n const rscMiddleware = getRscMiddleware({\n config: {},\n // Disabled in development\n baseUrl: '',\n rscPath,\n onError: console.error,\n renderRsc: async (args) => {\n // In development we should add simulated versions of common production headers.\n if (args.headers['x-real-ip'] == null) {\n args.headers['x-real-ip'] = await getIpAddressAsync();\n }\n if (args.headers['x-forwarded-for'] == null) {\n args.headers['x-forwarded-for'] = args.headers['x-real-ip'];\n }\n if (args.headers['x-forwarded-proto'] == null) {\n args.headers['x-forwarded-proto'] = 'http';\n }\n\n // Dev server-only implementation.\n try {\n return await renderRscToReadableStream({\n ...args,\n headers: new Headers(args.headers),\n body: args.body!,\n routerOptions,\n });\n } catch (error: any) {\n // If you get a codeFrame error during SSR like when using a Class component in React Server Components, then this\n // will throw with:\n // {\n // rawObject: {\n // type: 'TransformError',\n // lineNumber: 0,\n // errors: [ [Object] ],\n // name: 'SyntaxError',\n // message: '...',\n // }\n // }\n\n // TODO: Revisit all error handling now that we do direct metro bundling...\n await logMetroError(projectRoot, { error });\n\n if (error[IS_METRO_BUNDLE_ERROR_SYMBOL]) {\n throw new Response(JSON.stringify(error), {\n status: isPossiblyUnableToResolveError(error) ? 404 : 500,\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n }\n\n const sanitizedServerMessage = stripAnsi(error.message) ?? error.message;\n throw new Response(sanitizedServerMessage, {\n status: 500,\n headers: {\n 'Content-Type': 'text/plain',\n },\n });\n }\n },\n });\n\n let rscPathPrefix = rscPath;\n if (rscPathPrefix !== '/') {\n rscPathPrefix += '/';\n }\n\n async function exportServerActionsAsync(\n {\n platform,\n entryPoints,\n domRoot,\n }: { platform: string; entryPoints: string[]; domRoot?: string },\n files: ExportAssetMap\n ): Promise<{\n clientBoundaries: string[];\n manifest: Record<string, [string, string]>;\n }> {\n const uniqueEntryPoints = [...new Set(entryPoints)];\n // TODO: Support multiple entry points in a single split server bundle...\n const manifest: Record<string, [string, string]> = {};\n const nestedClientBoundaries: string[] = [];\n const nestedServerBoundaries: string[] = [];\n const processedEntryPoints = new Set<string>();\n async function processEntryPoint(entryPoint: string) {\n processedEntryPoints.add(entryPoint);\n\n const contents = await ssrLoadModuleArtifacts(entryPoint, {\n environment: 'react-server',\n platform,\n // Ignore the metro runtime to avoid overwriting the original in the API route.\n modulesOnly: true,\n // Required\n runModule: true,\n // Required to ensure assets load as client boundaries.\n domRoot,\n });\n\n const reactClientReferences = contents.artifacts\n .filter((a) => a.type === 'js')[0]\n ?.metadata.reactClientReferences?.map((ref) => fileURLToFilePath(ref));\n\n if (reactClientReferences) {\n nestedClientBoundaries.push(...reactClientReferences!);\n }\n const reactServerReferences = contents.artifacts\n .filter((a) => a.type === 'js')[0]\n ?.metadata.reactServerReferences?.map((ref) => fileURLToFilePath(ref));\n\n if (reactServerReferences) {\n nestedServerBoundaries.push(...reactServerReferences!);\n }\n\n // Naive check to ensure the module runtime is not included in the server action bundle.\n if (contents.src.includes('The experimental Metro feature')) {\n throw new Error(\n 'Internal error: module runtime should not be included in server action bundles: ' +\n entryPoint\n );\n }\n\n const relativeName = createModuleId(entryPoint, {\n platform,\n environment: 'react-server',\n });\n const safeName = path.basename(contents.artifacts.find((a) => a.type === 'js')!.filename!);\n\n const outputName = `_expo/rsc/${platform}/${safeName}`;\n // While we're here, export the router for the server to dynamically render RSC.\n files.set(outputName, {\n targetDomain: 'server',\n contents: wrapBundle(contents.src),\n });\n\n // Match babel plugin.\n const publicModuleId = './' + toPosixPath(path.relative(projectRoot, entryPoint));\n\n // Import relative to `dist/server/_expo/rsc/web/router.js`\n manifest[publicModuleId] = [String(relativeName), outputName];\n }\n\n async function processEntryPoints(entryPoints: string[], recursions = 0) {\n // Arbitrary recursion limit to prevent infinite loops.\n if (recursions > 10) {\n throw new Error('Recursion limit exceeded while processing server boundaries');\n }\n\n for (const entryPoint of entryPoints) {\n await processEntryPoint(entryPoint);\n }\n\n // When a server action has other server actions inside of it, we need to process those as well to ensure all entry points are in the manifest and accounted for.\n let uniqueNestedServerBoundaries = [...new Set(nestedServerBoundaries)];\n // Filter out values that have already been processed.\n uniqueNestedServerBoundaries = uniqueNestedServerBoundaries.filter(\n (value) => !processedEntryPoints.has(value)\n );\n if (uniqueNestedServerBoundaries.length) {\n debug('bundling nested server action boundaries', uniqueNestedServerBoundaries);\n return processEntryPoints(uniqueNestedServerBoundaries, recursions + 1);\n }\n }\n\n await processEntryPoints(uniqueEntryPoints);\n\n // Save the SSR manifest so we can perform more replacements in the server renderer and with server actions.\n files.set(`_expo/rsc/${platform}/action-manifest.js`, {\n targetDomain: 'server',\n contents: 'module.exports = ' + JSON.stringify(manifest),\n });\n\n return { manifest, clientBoundaries: nestedClientBoundaries };\n }\n\n async function getExpoRouterClientReferencesAsync(\n { platform, domRoot }: { platform: string; domRoot?: string },\n files: ExportAssetMap\n ): Promise<{\n reactClientReferences: string[];\n reactServerReferences: string[];\n cssModules: SerialAsset[];\n }> {\n const contents = await ssrLoadModuleArtifacts(routerModule, {\n environment: 'react-server',\n platform,\n modulesOnly: true,\n domRoot,\n });\n\n // Extract the global CSS modules that are imported from the router.\n // These will be injected in the head of the HTML document for the website.\n const cssModules = contents.artifacts.filter((a) => a.type.startsWith('css'));\n\n const reactServerReferences = contents.artifacts\n .filter((a) => a.type === 'js')[0]\n ?.metadata.reactServerReferences?.map((ref) => fileURLToFilePath(ref));\n\n if (!reactServerReferences) {\n throw new Error(\n 'Static server action references were not returned from the Metro SSR bundle for definedRouter'\n );\n }\n debug('React client boundaries:', reactServerReferences);\n\n const reactClientReferences = contents.artifacts\n .filter((a) => a.type === 'js')[0]\n ?.metadata.reactClientReferences?.map((ref) => fileURLToFilePath(ref));\n\n if (!reactClientReferences) {\n throw new Error(\n 'Static client references were not returned from the Metro SSR bundle for definedRouter'\n );\n }\n debug('React client boundaries:', reactClientReferences);\n\n // While we're here, export the router for the server to dynamically render RSC.\n files.set(`_expo/rsc/${platform}/router.js`, {\n targetDomain: 'server',\n contents: wrapBundle(contents.src),\n });\n\n return { reactClientReferences, reactServerReferences, cssModules };\n }\n\n const routerCache = new Map<string, EntriesDev>();\n\n async function getExpoRouterRscEntriesGetterAsync({\n platform,\n routerOptions,\n }: {\n platform: string;\n routerOptions: Record<string, any>;\n }) {\n await ensureMemo();\n // We can only cache this if we're using the client router since it doesn't change or use HMR\n if (routerCache.has(platform) && useClientRouter) {\n return routerCache.get(platform)!;\n }\n\n const router = await ssrLoadModule<\n typeof import('@expo/router-server/build/rsc/router/expo-definedRouter')\n >(\n routerModule,\n {\n environment: 'react-server',\n modulesOnly: true,\n platform,\n },\n {\n hot: !useClientRouter,\n }\n );\n\n const entries = router.default({\n redirects: routerOptions?.redirects,\n rewrites: routerOptions?.rewrites,\n });\n\n routerCache.set(platform, entries);\n return entries;\n }\n\n function getResolveClientEntry(context: {\n platform: string;\n engine?: 'hermes' | null;\n ssrManifest?: Map<string, string | null>;\n }): (\n file: string,\n isServer: boolean\n ) => {\n id: string;\n chunks: string[];\n } {\n const serverRoot = getMetroServerRootMemo(projectRoot);\n\n const {\n mode,\n minify = false,\n isExporting,\n baseUrl,\n routerRoot,\n asyncRoutes,\n preserveEnvVars,\n reactCompiler,\n lazy,\n } = instanceMetroOptions;\n\n assert(\n isExporting != null &&\n baseUrl != null &&\n mode != null &&\n routerRoot != null &&\n asyncRoutes != null,\n `The server must be started. (isExporting: ${isExporting}, baseUrl: ${baseUrl}, mode: ${mode}, routerRoot: ${routerRoot}, asyncRoutes: ${asyncRoutes})`\n );\n\n return (file: string, isServer: boolean) => {\n const filePath = path.join(\n projectRoot,\n file.startsWith('file://') ? fileURLToFilePath(file) : file\n );\n\n if (isExporting) {\n assert(context.ssrManifest, 'SSR manifest must exist when exporting');\n\n const relativeFilePath = toPosixPath(path.relative(serverRoot, filePath));\n\n assert(\n context.ssrManifest.has(relativeFilePath),\n `SSR manifest is missing client boundary \"${relativeFilePath}\"`\n );\n\n const chunk = context.ssrManifest.get(relativeFilePath);\n\n return {\n id: String(\n createModuleId(filePath, { platform: context.platform, environment: 'client' })\n ),\n chunks: chunk != null ? [chunk] : [],\n };\n }\n\n const environment = isServer ? 'react-server' : 'client';\n const searchParams = createBundleUrlSearchParams({\n mainModuleName: '',\n platform: context.platform,\n mode,\n minify,\n lazy,\n preserveEnvVars,\n asyncRoutes,\n baseUrl,\n routerRoot,\n isExporting,\n reactCompiler: !!reactCompiler,\n engine: context.engine ?? undefined,\n bytecode: false,\n clientBoundaries: [],\n inlineSourceMap: false,\n environment,\n modulesOnly: true,\n runModule: false,\n });\n\n searchParams.set('resolver.clientboundary', String(true));\n\n const clientReferenceUrl = new URL('http://a');\n\n // TICKLE: Handshake 1\n searchParams.set('xRSC', '1');\n\n clientReferenceUrl.search = searchParams.toString();\n\n const relativeFilePath = path.relative(serverRoot, filePath);\n\n clientReferenceUrl.pathname = relativeFilePath;\n\n // Ensure url.pathname ends with '.bundle'\n if (!clientReferenceUrl.pathname.endsWith('.bundle')) {\n clientReferenceUrl.pathname += '.bundle';\n }\n\n // Return relative URLs to help Android fetch from wherever it was loaded from since it doesn't support localhost.\n const chunkName = clientReferenceUrl.pathname + clientReferenceUrl.search;\n\n return {\n id: String(createModuleId(filePath, { platform: context.platform, environment })),\n chunks: [chunkName],\n };\n };\n }\n\n const rscRendererCache = new Map<\n string,\n typeof import('@expo/router-server/build/rsc/rsc-renderer')\n >();\n\n let ensurePromise: Promise<unknown> | null = null;\n async function ensureSSRReady() {\n // TODO: Extract CSS Modules / Assets from the bundler process\n await ssrLoadModule(null, {\n environment: 'react-server',\n platform: 'web',\n });\n }\n const ensureMemo = () => {\n ensurePromise ??= ensureSSRReady();\n return ensurePromise;\n };\n\n async function getRscRendererAsync(platform: string) {\n await ensureMemo();\n // NOTE(EvanBacon): We memoize this now that there's a persistent server storage cache for Server Actions.\n if (rscRendererCache.has(platform)) {\n return rscRendererCache.get(platform)!;\n }\n\n // TODO: Extract CSS Modules / Assets from the bundler process\n const renderer = await ssrLoadModule<\n typeof import('@expo/router-server/build/rsc/rsc-renderer')\n >(require.resolve('@expo/router-server/build/rsc/rsc-renderer'), {\n environment: 'react-server',\n platform,\n });\n\n rscRendererCache.set(platform, renderer);\n return renderer;\n }\n\n const rscRenderContext = new Map<string, any>();\n\n function getRscRenderContext(platform: string) {\n // NOTE(EvanBacon): We memoize this now that there's a persistent server storage cache for Server Actions.\n if (rscRenderContext.has(platform)) {\n return rscRenderContext.get(platform)!;\n }\n\n const context = {};\n\n rscRenderContext.set(platform, context);\n return context;\n }\n\n async function renderRscToReadableStream(\n {\n input,\n headers,\n method,\n platform,\n body,\n engine,\n contentType,\n ssrManifest,\n decodedBody,\n routerOptions,\n }: {\n input: string;\n headers: Headers;\n method: 'POST' | 'GET';\n platform: string;\n body?: ReadableStream<Uint8Array>;\n engine?: 'hermes' | null;\n contentType?: string;\n ssrManifest?: Map<string, string | null>;\n decodedBody?: unknown;\n routerOptions: Record<string, any>;\n },\n isExporting: boolean | undefined = instanceMetroOptions.isExporting\n ) {\n assert(\n isExporting != null,\n 'The server must be started before calling renderRscToReadableStream.'\n );\n\n if (method === 'POST') {\n assert(body, 'Server request must be provided when method is POST (server actions)');\n }\n\n const context = getRscRenderContext(platform);\n\n context['__expo_requestHeaders'] = headers;\n\n const { renderRsc } = await getRscRendererAsync(platform);\n\n return renderRsc(\n {\n body,\n decodedBody,\n context,\n config: {},\n input,\n contentType,\n },\n {\n isExporting,\n entries: await getExpoRouterRscEntriesGetterAsync({ platform, routerOptions }),\n resolveClientEntry: getResolveClientEntry({ platform, engine, ssrManifest }),\n async loadServerModuleRsc(urlFragment) {\n const serverRoot = getMetroServerRootMemo(projectRoot);\n\n debug('[SSR] loadServerModuleRsc:', urlFragment);\n\n const options = getMetroOptionsFromUrl(urlFragment);\n\n return ssrLoadModule(\n path.join(serverRoot, options.mainModuleName),\n\n options,\n {\n hot: true,\n }\n );\n },\n }\n );\n }\n\n return {\n // Get the static client boundaries (no dead code elimination allowed) for the production export.\n getExpoRouterClientReferencesAsync,\n exportServerActionsAsync,\n\n // Expose the RSC handler directly for use with `createRouteHandlerMiddleware()`\n handler: rscMiddleware,\n\n async exportRoutesAsync(\n {\n platform,\n ssrManifest,\n routerOptions,\n }: {\n platform: string;\n ssrManifest: Map<string, string | null>;\n routerOptions: Record<string, any>;\n },\n files: ExportAssetMap\n ) {\n // TODO: When we add web SSR support, we need to extract CSS Modules / Assets from the bundler process to prevent FLOUC.\n const { getBuildConfig } = (\n await getExpoRouterRscEntriesGetterAsync({ platform, routerOptions })\n ).default;\n\n // Get all the routes to render.\n const buildConfig = await getBuildConfig!(async () =>\n // TODO: Rework prefetching code to use Metro runtime.\n []\n );\n\n await Promise.all(\n Array.from(buildConfig).map(async ({ entries }) => {\n for (const { input, isStatic } of entries || []) {\n if (!isStatic) {\n debug('Skipping static export for route', { input });\n continue;\n }\n const destRscFile = path.join('_flight', platform, encodeInput(input));\n\n const pipe = await renderRscToReadableStream(\n {\n input,\n method: 'GET',\n platform,\n headers: new Headers(),\n ssrManifest,\n routerOptions,\n },\n true\n );\n\n const rsc = await streamToStringAsync(pipe);\n debug('RSC Payload', { platform, input, rsc });\n\n files.set(destRscFile, {\n contents: rsc,\n targetDomain: 'client',\n rscId: input,\n });\n }\n })\n );\n },\n\n onReloadRscEvent: (platform: string) => {\n // NOTE: We cannot clear the renderer context because it would break the mounted context state.\n\n rscRendererCache.delete(platform);\n routerCache.delete(platform);\n },\n };\n}\n\nexport const fileURLToFilePath = (fileURL: string) => {\n try {\n return url.fileURLToPath(fileURL);\n } catch (error) {\n if (error instanceof TypeError) {\n throw Error(`Invalid URL: ${fileURL}`, { cause: error });\n }\n throw error;\n }\n};\n\nconst encodeInput = (input: string) => {\n if (input === '') {\n return 'index.txt';\n }\n if (input === 'index') {\n throw new Error('Input should not be `index`');\n }\n if (input.startsWith('/')) {\n throw new Error('Input should not start with `/`');\n }\n if (input.endsWith('/')) {\n throw new Error('Input should not end with `/`');\n }\n return input + '.txt';\n};\n\nfunction wrapBundle(str: string) {\n // Skip the metro runtime so debugging is a bit easier.\n // Replace the __r() call with an export statement.\n // Use gm to apply to the last require line. This is needed when the bundle has side-effects.\n return str.replace(/^(__r\\(.*\\);)$/gm, 'module.exports = $1');\n}\n"],"names":["createServerComponentsMiddleware","fileURLToFilePath","debug","require","getMetroServerRootMemo","memoize","getMetroServerRoot","projectRoot","rscPath","instanceMetroOptions","ssrLoadModule","ssrLoadModuleArtifacts","useClientRouter","createModuleId","routerOptions","routerModule","resolve","rscMiddleware","getRscMiddleware","config","baseUrl","onError","console","error","renderRsc","args","headers","getIpAddressAsync","renderRscToReadableStream","Headers","body","logMetroError","IS_METRO_BUNDLE_ERROR_SYMBOL","Response","JSON","stringify","status","isPossiblyUnableToResolveError","sanitizedServerMessage","stripAnsi","message","rscPathPrefix","exportServerActionsAsync","platform","entryPoints","domRoot","files","uniqueEntryPoints","Set","manifest","nestedClientBoundaries","nestedServerBoundaries","processedEntryPoints","processEntryPoint","entryPoint","contents","add","environment","modulesOnly","runModule","reactClientReferences","artifacts","filter","a","type","metadata","map","ref","push","reactServerReferences","src","includes","Error","relativeName","safeName","path","basename","find","filename","outputName","set","targetDomain","wrapBundle","publicModuleId","toPosixPath","relative","String","processEntryPoints","recursions","uniqueNestedServerBoundaries","value","has","length","clientBoundaries","getExpoRouterClientReferencesAsync","cssModules","startsWith","routerCache","Map","getExpoRouterRscEntriesGetterAsync","ensureMemo","get","router","hot","entries","default","redirects","rewrites","getResolveClientEntry","context","serverRoot","mode","minify","isExporting","routerRoot","asyncRoutes","preserveEnvVars","reactCompiler","lazy","assert","file","isServer","filePath","join","ssrManifest","relativeFilePath","chunk","id","chunks","searchParams","createBundleUrlSearchParams","mainModuleName","engine","undefined","bytecode","inlineSourceMap","clientReferenceUrl","URL","search","toString","pathname","endsWith","chunkName","rscRendererCache","ensurePromise","ensureSSRReady","getRscRendererAsync","renderer","rscRenderContext","getRscRenderContext","input","method","contentType","decodedBody","resolveClientEntry","loadServerModuleRsc","urlFragment","options","getMetroOptionsFromUrl","handler","exportRoutesAsync","getBuildConfig","buildConfig","Promise","all","Array","from","isStatic","destRscFile","encodeInput","pipe","rsc","streamToStringAsync","rscId","onReloadRscEvent","delete","fileURL","url","fileURLToPath","TypeError","cause","str","replace"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/start/server/metro/createServerComponentsMiddleware.ts"],"sourcesContent":["/**\n * Copyright © 2022 650 Industries.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\nimport { getMetroServerRoot } from '@expo/config/paths';\nimport type { SerialAsset } from '@expo/metro-config/build/serializer/serializerAssets';\nimport { resolveRouterModule, type RouterModule } from '@expo/router-server/build/rsc/router';\nimport type { EntriesDev } from '@expo/router-server/build/rsc/server';\nimport assert from 'assert';\nimport { getRscMiddleware } from 'expo-server/private';\nimport path from 'node:path';\nimport url from 'node:url';\n\nimport { IS_METRO_BUNDLE_ERROR_SYMBOL, logMetroError } from './metroErrorInterface';\nimport { isPossiblyUnableToResolveError } from '../../../export/embed/xcodeCompilerLogger';\nimport type { ExportAssetMap } from '../../../export/saveAssets';\nimport { stripAnsi } from '../../../utils/ansi';\nimport { toPosixPath } from '../../../utils/filePath';\nimport { memoize } from '../../../utils/fn';\nimport { getIpAddressAsync } from '../../../utils/ip';\nimport { streamToStringAsync } from '../../../utils/stream';\nimport {\n createBundleUrlSearchParams,\n type ExpoMetroOptions,\n getMetroOptionsFromUrl,\n} from '../middleware/metroOptions';\n\nconst debug = require('debug')('expo:rsc') as typeof console.log;\n\ntype SSRLoadModuleArtifactsFunc = (\n filePath: string,\n specificOptions?: Partial<ExpoMetroOptions>\n) => Promise<{ artifacts: SerialAsset[]; src: string }>;\n\ntype SSRLoadModuleFunc = <T extends Record<string, any>>(\n filePath: string | null,\n specificOptions?: Partial<ExpoMetroOptions>,\n extras?: { hot?: boolean }\n) => Promise<T>;\n\nconst getMetroServerRootMemo = memoize(getMetroServerRoot);\n\n// TODO(@hassankhan): Rename this to `createRscRenderer()`\nexport function createServerComponentsMiddleware(\n projectRoot: string,\n {\n rscPath,\n instanceMetroOptions,\n ssrLoadModule,\n ssrLoadModuleArtifacts,\n useClientRouter,\n createModuleId,\n routerOptions,\n }: {\n rscPath: string;\n instanceMetroOptions: Partial<ExpoMetroOptions>;\n ssrLoadModule: SSRLoadModuleFunc;\n ssrLoadModuleArtifacts: SSRLoadModuleArtifactsFunc;\n useClientRouter: boolean;\n createModuleId: (\n filePath: string,\n context: { platform: string; environment: string }\n ) => string | number;\n routerOptions: Record<string, any>;\n }\n) {\n const routerModule = resolveRouterModule(useClientRouter);\n\n const rscMiddleware = getRscMiddleware({\n config: {},\n // Disabled in development\n baseUrl: '',\n rscPath,\n onError: console.error,\n renderRsc: async (args) => {\n // In development we should add simulated versions of common production headers.\n if (args.headers['x-real-ip'] == null) {\n args.headers['x-real-ip'] = await getIpAddressAsync();\n }\n if (args.headers['x-forwarded-for'] == null) {\n args.headers['x-forwarded-for'] = args.headers['x-real-ip'];\n }\n if (args.headers['x-forwarded-proto'] == null) {\n args.headers['x-forwarded-proto'] = 'http';\n }\n\n // Dev server-only implementation.\n try {\n return await renderRscToReadableStream({\n ...args,\n headers: new Headers(args.headers),\n body: args.body!,\n routerOptions,\n });\n } catch (error: any) {\n // If you get a codeFrame error during SSR like when using a Class component in React Server Components, then this\n // will throw with:\n // {\n // rawObject: {\n // type: 'TransformError',\n // lineNumber: 0,\n // errors: [ [Object] ],\n // name: 'SyntaxError',\n // message: '...',\n // }\n // }\n\n // TODO: Revisit all error handling now that we do direct metro bundling...\n await logMetroError(projectRoot, { error });\n\n if (error[IS_METRO_BUNDLE_ERROR_SYMBOL]) {\n throw new Response(JSON.stringify(error), {\n status: isPossiblyUnableToResolveError(error) ? 404 : 500,\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n }\n\n const sanitizedServerMessage = stripAnsi(error.message) ?? error.message;\n throw new Response(sanitizedServerMessage, {\n status: 500,\n headers: {\n 'Content-Type': 'text/plain',\n },\n });\n }\n },\n });\n\n let rscPathPrefix = rscPath;\n if (rscPathPrefix !== '/') {\n rscPathPrefix += '/';\n }\n\n async function exportServerActionsAsync(\n {\n platform,\n entryPoints,\n domRoot,\n }: { platform: string; entryPoints: string[]; domRoot?: string },\n files: ExportAssetMap\n ): Promise<{\n clientBoundaries: string[];\n manifest: Record<string, [string, string]>;\n }> {\n const uniqueEntryPoints = [...new Set(entryPoints)];\n // TODO: Support multiple entry points in a single split server bundle...\n const manifest: Record<string, [string, string]> = {};\n const nestedClientBoundaries: string[] = [];\n const nestedServerBoundaries: string[] = [];\n const processedEntryPoints = new Set<string>();\n async function processEntryPoint(entryPoint: string) {\n processedEntryPoints.add(entryPoint);\n\n const contents = await ssrLoadModuleArtifacts(entryPoint, {\n environment: 'react-server',\n platform,\n // Ignore the metro runtime to avoid overwriting the original in the API route.\n modulesOnly: true,\n // Required\n runModule: true,\n // Required to ensure assets load as client boundaries.\n domRoot,\n });\n\n const reactClientReferences = contents.artifacts\n .filter((a) => a.type === 'js')[0]\n ?.metadata.reactClientReferences?.map((ref) => fileURLToFilePath(ref));\n\n if (reactClientReferences) {\n nestedClientBoundaries.push(...reactClientReferences!);\n }\n const reactServerReferences = contents.artifacts\n .filter((a) => a.type === 'js')[0]\n ?.metadata.reactServerReferences?.map((ref) => fileURLToFilePath(ref));\n\n if (reactServerReferences) {\n nestedServerBoundaries.push(...reactServerReferences!);\n }\n\n // Naive check to ensure the module runtime is not included in the server action bundle.\n if (contents.src.includes('The experimental Metro feature')) {\n throw new Error(\n 'Internal error: module runtime should not be included in server action bundles: ' +\n entryPoint\n );\n }\n\n const relativeName = createModuleId(entryPoint, {\n platform,\n environment: 'react-server',\n });\n const safeName = path.basename(contents.artifacts.find((a) => a.type === 'js')!.filename!);\n\n const outputName = `_expo/rsc/${platform}/${safeName}`;\n // While we're here, export the router for the server to dynamically render RSC.\n files.set(outputName, {\n targetDomain: 'server',\n contents: wrapBundle(contents.src),\n });\n\n // Match babel plugin.\n const publicModuleId = './' + toPosixPath(path.relative(projectRoot, entryPoint));\n\n // Import relative to `dist/server/_expo/rsc/web/router.js`\n manifest[publicModuleId] = [String(relativeName), outputName];\n }\n\n async function processEntryPoints(entryPoints: string[], recursions = 0) {\n // Arbitrary recursion limit to prevent infinite loops.\n if (recursions > 10) {\n throw new Error('Recursion limit exceeded while processing server boundaries');\n }\n\n for (const entryPoint of entryPoints) {\n await processEntryPoint(entryPoint);\n }\n\n // When a server action has other server actions inside of it, we need to process those as well to ensure all entry points are in the manifest and accounted for.\n let uniqueNestedServerBoundaries = [...new Set(nestedServerBoundaries)];\n // Filter out values that have already been processed.\n uniqueNestedServerBoundaries = uniqueNestedServerBoundaries.filter(\n (value) => !processedEntryPoints.has(value)\n );\n if (uniqueNestedServerBoundaries.length) {\n debug('bundling nested server action boundaries', uniqueNestedServerBoundaries);\n return processEntryPoints(uniqueNestedServerBoundaries, recursions + 1);\n }\n }\n\n await processEntryPoints(uniqueEntryPoints);\n\n // Save the SSR manifest so we can perform more replacements in the server renderer and with server actions.\n files.set(`_expo/rsc/${platform}/action-manifest.js`, {\n targetDomain: 'server',\n contents: 'module.exports = ' + JSON.stringify(manifest),\n });\n\n return { manifest, clientBoundaries: nestedClientBoundaries };\n }\n\n async function getExpoRouterClientReferencesAsync(\n { platform, domRoot }: { platform: string; domRoot?: string },\n files: ExportAssetMap\n ): Promise<{\n reactClientReferences: string[];\n reactServerReferences: string[];\n cssModules: SerialAsset[];\n }> {\n const contents = await ssrLoadModuleArtifacts(routerModule, {\n environment: 'react-server',\n platform,\n modulesOnly: true,\n domRoot,\n });\n\n // Extract the global CSS modules that are imported from the router.\n // These will be injected in the head of the HTML document for the website.\n const cssModules = contents.artifacts.filter((a) => a.type.startsWith('css'));\n\n const reactServerReferences = contents.artifacts\n .filter((a) => a.type === 'js')[0]\n ?.metadata.reactServerReferences?.map((ref) => fileURLToFilePath(ref));\n\n if (!reactServerReferences) {\n throw new Error(\n 'Static server action references were not returned from the Metro SSR bundle for definedRouter'\n );\n }\n debug('React client boundaries:', reactServerReferences);\n\n const reactClientReferences = contents.artifacts\n .filter((a) => a.type === 'js')[0]\n ?.metadata.reactClientReferences?.map((ref) => fileURLToFilePath(ref));\n\n if (!reactClientReferences) {\n throw new Error(\n 'Static client references were not returned from the Metro SSR bundle for definedRouter'\n );\n }\n debug('React client boundaries:', reactClientReferences);\n\n // While we're here, export the router for the server to dynamically render RSC.\n files.set(`_expo/rsc/${platform}/router.js`, {\n targetDomain: 'server',\n contents: wrapBundle(contents.src),\n });\n\n return { reactClientReferences, reactServerReferences, cssModules };\n }\n\n const routerCache = new Map<string, EntriesDev>();\n\n async function getExpoRouterRscEntriesGetterAsync({\n platform,\n routerOptions,\n }: {\n platform: string;\n routerOptions: Record<string, any>;\n }) {\n await ensureMemo();\n // We can only cache this if we're using the client router since it doesn't change or use HMR\n if (routerCache.has(platform) && useClientRouter) {\n return routerCache.get(platform)!;\n }\n\n const router = await ssrLoadModule<RouterModule>(\n routerModule,\n {\n environment: 'react-server',\n modulesOnly: true,\n platform,\n },\n {\n hot: !useClientRouter,\n }\n );\n\n const entries = router.default({\n redirects: routerOptions?.redirects,\n rewrites: routerOptions?.rewrites,\n });\n\n routerCache.set(platform, entries);\n return entries;\n }\n\n function getResolveClientEntry(context: {\n platform: string;\n engine?: 'hermes' | null;\n ssrManifest?: Map<string, string | null>;\n }): (\n file: string,\n isServer: boolean\n ) => {\n id: string;\n chunks: string[];\n } {\n const serverRoot = getMetroServerRootMemo(projectRoot);\n\n const {\n mode,\n minify = false,\n isExporting,\n baseUrl,\n routerRoot,\n asyncRoutes,\n preserveEnvVars,\n reactCompiler,\n lazy,\n } = instanceMetroOptions;\n\n assert(\n isExporting != null &&\n baseUrl != null &&\n mode != null &&\n routerRoot != null &&\n asyncRoutes != null,\n `The server must be started. (isExporting: ${isExporting}, baseUrl: ${baseUrl}, mode: ${mode}, routerRoot: ${routerRoot}, asyncRoutes: ${asyncRoutes})`\n );\n\n return (file: string, isServer: boolean) => {\n const filePath = path.join(\n projectRoot,\n file.startsWith('file://') ? fileURLToFilePath(file) : file\n );\n\n if (isExporting) {\n assert(context.ssrManifest, 'SSR manifest must exist when exporting');\n\n const relativeFilePath = toPosixPath(path.relative(serverRoot, filePath));\n\n assert(\n context.ssrManifest.has(relativeFilePath),\n `SSR manifest is missing client boundary \"${relativeFilePath}\"`\n );\n\n const chunk = context.ssrManifest.get(relativeFilePath);\n\n return {\n id: String(\n createModuleId(filePath, { platform: context.platform, environment: 'client' })\n ),\n chunks: chunk != null ? [chunk] : [],\n };\n }\n\n const environment = isServer ? 'react-server' : 'client';\n const searchParams = createBundleUrlSearchParams({\n mainModuleName: '',\n platform: context.platform,\n mode,\n minify,\n lazy,\n preserveEnvVars,\n asyncRoutes,\n baseUrl,\n routerRoot,\n isExporting,\n reactCompiler: !!reactCompiler,\n engine: context.engine ?? undefined,\n bytecode: false,\n clientBoundaries: [],\n inlineSourceMap: false,\n environment,\n modulesOnly: true,\n runModule: false,\n });\n\n searchParams.set('resolver.clientboundary', String(true));\n\n const clientReferenceUrl = new URL('http://a');\n\n // TICKLE: Handshake 1\n searchParams.set('xRSC', '1');\n\n clientReferenceUrl.search = searchParams.toString();\n\n const relativeFilePath = path.relative(serverRoot, filePath);\n\n clientReferenceUrl.pathname = relativeFilePath;\n\n // Ensure url.pathname ends with '.bundle'\n if (!clientReferenceUrl.pathname.endsWith('.bundle')) {\n clientReferenceUrl.pathname += '.bundle';\n }\n\n // Return relative URLs to help Android fetch from wherever it was loaded from since it doesn't support localhost.\n const chunkName = clientReferenceUrl.pathname + clientReferenceUrl.search;\n\n return {\n id: String(createModuleId(filePath, { platform: context.platform, environment })),\n chunks: [chunkName],\n };\n };\n }\n\n const rscRendererCache = new Map<\n string,\n typeof import('@expo/router-server/build/rsc/rsc-renderer')\n >();\n\n let ensurePromise: Promise<unknown> | null = null;\n async function ensureSSRReady() {\n // TODO: Extract CSS Modules / Assets from the bundler process\n await ssrLoadModule(null, {\n environment: 'react-server',\n platform: 'web',\n });\n }\n const ensureMemo = () => {\n ensurePromise ??= ensureSSRReady();\n return ensurePromise;\n };\n\n async function getRscRendererAsync(platform: string) {\n await ensureMemo();\n // NOTE(EvanBacon): We memoize this now that there's a persistent server storage cache for Server Actions.\n if (rscRendererCache.has(platform)) {\n return rscRendererCache.get(platform)!;\n }\n\n // TODO: Extract CSS Modules / Assets from the bundler process\n const renderer = await ssrLoadModule<\n typeof import('@expo/router-server/build/rsc/rsc-renderer')\n >(require.resolve('@expo/router-server/build/rsc/rsc-renderer'), {\n environment: 'react-server',\n platform,\n });\n\n rscRendererCache.set(platform, renderer);\n return renderer;\n }\n\n async function renderRscToReadableStream(\n {\n input,\n headers,\n method,\n platform,\n body,\n engine,\n contentType,\n ssrManifest,\n decodedBody,\n routerOptions,\n }: {\n input: string;\n headers: Headers;\n method: 'POST' | 'GET';\n platform: string;\n body?: ReadableStream<Uint8Array>;\n engine?: 'hermes' | null;\n contentType?: string;\n ssrManifest?: Map<string, string | null>;\n decodedBody?: unknown;\n routerOptions: Record<string, any>;\n },\n isExporting: boolean | undefined = instanceMetroOptions.isExporting\n ) {\n assert(\n isExporting != null,\n 'The server must be started before calling renderRscToReadableStream.'\n );\n\n if (method === 'POST') {\n assert(body, 'Server request must be provided when method is POST (server actions)');\n }\n\n const context = { __expo_requestHeaders: headers };\n const { renderRsc } = await getRscRendererAsync(platform);\n\n return renderRsc(\n {\n body,\n decodedBody,\n context,\n config: {},\n input,\n method,\n headers: Object.fromEntries(headers.entries()),\n contentType,\n },\n {\n isExporting,\n entries: await getExpoRouterRscEntriesGetterAsync({ platform, routerOptions }),\n resolveClientEntry: getResolveClientEntry({ platform, engine, ssrManifest }),\n async loadServerModuleRsc(urlFragment) {\n const serverRoot = getMetroServerRootMemo(projectRoot);\n\n debug('[SSR] loadServerModuleRsc:', urlFragment);\n\n const options = getMetroOptionsFromUrl(urlFragment);\n\n return ssrLoadModule(\n path.join(serverRoot, options.mainModuleName),\n\n options,\n {\n hot: true,\n }\n );\n },\n }\n );\n }\n\n return {\n // Get the static client boundaries (no dead code elimination allowed) for the production export.\n getExpoRouterClientReferencesAsync,\n exportServerActionsAsync,\n\n // Expose the RSC handler directly for use with `createRouteHandlerMiddleware()`\n handler: rscMiddleware,\n\n async exportRoutesAsync(\n {\n platform,\n ssrManifest,\n routerOptions,\n }: {\n platform: string;\n ssrManifest: Map<string, string | null>;\n routerOptions: Record<string, any>;\n },\n files: ExportAssetMap\n ) {\n // TODO: When we add web SSR support, we need to extract CSS Modules / Assets from the bundler process to prevent FLOUC.\n const { getBuildConfig } = (\n await getExpoRouterRscEntriesGetterAsync({ platform, routerOptions })\n ).default;\n\n // Get all the routes to render.\n const buildConfig = await getBuildConfig!(async () =>\n // TODO: Rework prefetching code to use Metro runtime.\n []\n );\n\n await Promise.all(\n Array.from(buildConfig).map(async ({ entries }) => {\n for (const { input, isStatic } of entries || []) {\n if (!isStatic) {\n debug('Skipping static export for route', { input });\n continue;\n }\n const destRscFile = path.join('_flight', platform, encodeInput(input));\n\n const pipe = await renderRscToReadableStream(\n {\n input,\n method: 'GET',\n platform,\n headers: new Headers(),\n ssrManifest,\n routerOptions,\n },\n true\n );\n\n const rsc = await streamToStringAsync(pipe);\n debug('RSC Payload', { platform, input, rsc });\n\n files.set(destRscFile, {\n contents: rsc,\n targetDomain: 'client',\n rscId: input,\n });\n }\n })\n );\n },\n\n onReloadRscEvent: (platform: string) => {\n // NOTE: We cannot clear the renderer context because it would break the mounted context state.\n\n rscRendererCache.delete(platform);\n routerCache.delete(platform);\n },\n };\n}\n\nexport const fileURLToFilePath = (fileURL: string) => {\n try {\n return url.fileURLToPath(fileURL);\n } catch (error) {\n if (error instanceof TypeError) {\n throw Error(`Invalid URL: ${fileURL}`, { cause: error });\n }\n throw error;\n }\n};\n\nconst encodeInput = (input: string) => {\n if (input === '') {\n return 'index.txt';\n }\n if (input === 'index') {\n throw new Error('Input should not be `index`');\n }\n if (input.startsWith('/')) {\n throw new Error('Input should not start with `/`');\n }\n if (input.endsWith('/')) {\n throw new Error('Input should not end with `/`');\n }\n return input + '.txt';\n};\n\nfunction wrapBundle(str: string) {\n // Skip the metro runtime so debugging is a bit easier.\n // Replace the __r() call with an export statement.\n // Use gm to apply to the last require line. This is needed when the bundle has side-effects.\n return str.replace(/^(__r\\(.*\\);)$/gm, 'module.exports = $1');\n}\n"],"names":["createServerComponentsMiddleware","fileURLToFilePath","debug","require","getMetroServerRootMemo","memoize","getMetroServerRoot","projectRoot","rscPath","instanceMetroOptions","ssrLoadModule","ssrLoadModuleArtifacts","useClientRouter","createModuleId","routerOptions","routerModule","resolveRouterModule","rscMiddleware","getRscMiddleware","config","baseUrl","onError","console","error","renderRsc","args","headers","getIpAddressAsync","renderRscToReadableStream","Headers","body","logMetroError","IS_METRO_BUNDLE_ERROR_SYMBOL","Response","JSON","stringify","status","isPossiblyUnableToResolveError","sanitizedServerMessage","stripAnsi","message","rscPathPrefix","exportServerActionsAsync","platform","entryPoints","domRoot","files","uniqueEntryPoints","Set","manifest","nestedClientBoundaries","nestedServerBoundaries","processedEntryPoints","processEntryPoint","entryPoint","contents","add","environment","modulesOnly","runModule","reactClientReferences","artifacts","filter","a","type","metadata","map","ref","push","reactServerReferences","src","includes","Error","relativeName","safeName","path","basename","find","filename","outputName","set","targetDomain","wrapBundle","publicModuleId","toPosixPath","relative","String","processEntryPoints","recursions","uniqueNestedServerBoundaries","value","has","length","clientBoundaries","getExpoRouterClientReferencesAsync","cssModules","startsWith","routerCache","Map","getExpoRouterRscEntriesGetterAsync","ensureMemo","get","router","hot","entries","default","redirects","rewrites","getResolveClientEntry","context","serverRoot","mode","minify","isExporting","routerRoot","asyncRoutes","preserveEnvVars","reactCompiler","lazy","assert","file","isServer","filePath","join","ssrManifest","relativeFilePath","chunk","id","chunks","searchParams","createBundleUrlSearchParams","mainModuleName","engine","undefined","bytecode","inlineSourceMap","clientReferenceUrl","URL","search","toString","pathname","endsWith","chunkName","rscRendererCache","ensurePromise","ensureSSRReady","getRscRendererAsync","renderer","resolve","input","method","contentType","decodedBody","__expo_requestHeaders","Object","fromEntries","resolveClientEntry","loadServerModuleRsc","urlFragment","options","getMetroOptionsFromUrl","handler","exportRoutesAsync","getBuildConfig","buildConfig","Promise","all","Array","from","isStatic","destRscFile","encodeInput","pipe","rsc","streamToStringAsync","rscId","onReloadRscEvent","delete","fileURL","url","fileURLToPath","TypeError","cause","str","replace"],"mappings":"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"}
|
|
@@ -95,9 +95,10 @@ function makeLogger(reporter, level) {
|
|
|
95
95
|
const wss = new (_ws()).WebSocketServer({
|
|
96
96
|
noServer: true,
|
|
97
97
|
perMessageDeflate: true,
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
|
|
98
|
+
// Bounded so an unauthenticated client cannot exhaust dev-server memory
|
|
99
|
+
// with one oversized frame. 16 MiB covers realistic response bodies
|
|
100
|
+
// (image previews, large JSON dumps, base64-inflated assets).
|
|
101
|
+
maxPayload: 16 * 1024 * 1024
|
|
101
102
|
});
|
|
102
103
|
wss.on('connection', (networkSocket)=>{
|
|
103
104
|
networkSocket.on('message', (data)=>{
|
|
@@ -107,7 +108,7 @@ function makeLogger(reporter, level) {
|
|
|
107
108
|
if (message.method === 'Expo(Network.receivedResponseBody)' && message.params) {
|
|
108
109
|
// If its a response body, write it to the global storage
|
|
109
110
|
const { requestId, ...requestInfo } = message.params;
|
|
110
|
-
_NetworkResponse.
|
|
111
|
+
(0, _NetworkResponse.recordNetworkResponse)(requestId, requestInfo);
|
|
111
112
|
} else if (message.method.startsWith('Network.')) {
|
|
112
113
|
// Otherwise, directly re-broadcast the Network events to all connected debuggers
|
|
113
114
|
debuggerWebsocket.clients.forEach((debuggerSocket)=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/start/server/metro/debugging/createDebugMiddleware.ts"],"sourcesContent":["import type { NextHandleFunction } from 'connect';\nimport { WebSocketServer } from 'ws';\n\nimport { createHandlersFactory } from './createHandlersFactory';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/start/server/metro/debugging/createDebugMiddleware.ts"],"sourcesContent":["import type { NextHandleFunction } from 'connect';\nimport { WebSocketServer } from 'ws';\n\nimport { createHandlersFactory } from './createHandlersFactory';\nimport { recordNetworkResponse } from './messageHandlers/NetworkResponse';\nimport { env, envIsHeadless } from '../../../../utils/env';\nimport { isLocalSocket, isMatchingOrigin } from '../../../../utils/net';\nimport type { TerminalReporter } from '../TerminalReporter';\n\nconst debug = require('debug')('expo:metro:debugging:middleware') as typeof console.log;\n\ninterface DebugMiddleware {\n debugMiddleware: NextHandleFunction;\n debugWebsocketEndpoints: Record<string, WebSocketServer>;\n}\n\ninterface DebugMiddlewareParams {\n serverBaseUrl: string;\n reporter: TerminalReporter;\n}\n\nexport function createDebugMiddleware({\n serverBaseUrl,\n reporter,\n}: DebugMiddlewareParams): DebugMiddleware {\n // Load the React Native debugging tools from project\n // TODO: check if this works with isolated modules\n const { createDevMiddleware } =\n require('@react-native/dev-middleware') as typeof import('@react-native/dev-middleware');\n\n const { middleware, websocketEndpoints } = createDevMiddleware({\n // TODO: Check with cedric why this can be removed\n // https://github.com/facebook/react-native/pull/53921\n // projectRoot: metroBundler.projectRoot,\n serverBaseUrl,\n logger: createLogger(reporter),\n unstable_customInspectorMessageHandler: createHandlersFactory(),\n // TODO: Forward all events to the shared Metro log reporter. Do this when we have opinions on how all logs should be presented.\n // unstable_eventReporter: {\n // logEvent(event) {\n // reporter.update(event);\n // },\n // },\n unstable_experiments: {\n // Enable the Network tab in React Native DevTools\n enableNetworkInspector: true,\n // Only enable opening the browser version of React Native DevTools when debugging.\n // This is useful when debugging the React Native DevTools by going to `/open-debugger` in the browser.\n enableOpenDebuggerRedirect: env.EXPO_DEBUG,\n // The standalone fusebox shell (@react-native/debugger-shell) starts installing almost\n // immediately in the background when the dev middleware is created, which we don't\n // always want to happen\n enableStandaloneFuseboxShell: !envIsHeadless(),\n },\n });\n\n const debuggerWebsocketEndpoint = websocketEndpoints['/inspector/debug'] as WebSocketServer;\n\n // NOTE(cedric): add a temporary websocket to handle Network-related CDP events\n websocketEndpoints['/inspector/network'] = createNetworkWebsocket(debuggerWebsocketEndpoint);\n\n // Explicitly limit debugger websocket to loopback requests\n debuggerWebsocketEndpoint.on('connection', (socket, request) => {\n if (!isLocalSocket(request.socket) || !isMatchingOrigin(request, serverBaseUrl)) {\n // NOTE: `socket.close` nicely closes the websocket, which will still allow incoming messages\n // `socket.terminate` instead forcefully closes down the socket\n socket.terminate();\n }\n });\n\n return {\n debugMiddleware(req, res, next) {\n // The debugger middleware is skipped entirely if the connection isn't a loopback request\n if (isLocalSocket(req.socket)) {\n return middleware(req, res, next);\n } else {\n return next();\n }\n },\n debugWebsocketEndpoints: websocketEndpoints,\n };\n}\n\nfunction createLogger(\n reporter: TerminalReporter\n): Parameters<typeof import('@react-native/dev-middleware').createDevMiddleware>[0]['logger'] {\n return {\n info: makeLogger(reporter, 'info'),\n warn: makeLogger(reporter, 'warn'),\n error: makeLogger(reporter, 'error'),\n };\n}\n\nfunction makeLogger(reporter: TerminalReporter, level: 'info' | 'warn' | 'error') {\n return (...data: any[]) =>\n reporter.update({\n type: 'unstable_server_log',\n level,\n data,\n });\n}\n\n/**\n * This adds a dedicated websocket connection that handles Network-related CDP events.\n * It's a temporary solution until Fusebox either implements the Network CDP domain,\n * or allows external domain agents that can send messages over the CDP socket to the debugger.\n * The Network websocket rebroadcasts events on the debugger CDP connections.\n */\nfunction createNetworkWebsocket(debuggerWebsocket: WebSocketServer) {\n const wss = new WebSocketServer({\n noServer: true,\n perMessageDeflate: true,\n // Bounded so an unauthenticated client cannot exhaust dev-server memory\n // with one oversized frame. 16 MiB covers realistic response bodies\n // (image previews, large JSON dumps, base64-inflated assets).\n maxPayload: 16 * 1024 * 1024,\n });\n\n wss.on('connection', (networkSocket) => {\n networkSocket.on('message', (data) => {\n try {\n // Parse the network message, to determine how the message should be handled\n const message = JSON.parse(data.toString());\n\n if (message.method === 'Expo(Network.receivedResponseBody)' && message.params) {\n // If its a response body, write it to the global storage\n const { requestId, ...requestInfo } = message.params;\n recordNetworkResponse(requestId, requestInfo);\n } else if (message.method.startsWith('Network.')) {\n // Otherwise, directly re-broadcast the Network events to all connected debuggers\n debuggerWebsocket.clients.forEach((debuggerSocket) => {\n if (debuggerSocket.readyState === debuggerSocket.OPEN) {\n debuggerSocket.send(data.toString());\n }\n });\n }\n } catch (error) {\n debug('Failed to handle Network CDP event', error);\n }\n });\n });\n\n return wss;\n}\n"],"names":["createDebugMiddleware","debug","require","serverBaseUrl","reporter","createDevMiddleware","middleware","websocketEndpoints","logger","createLogger","unstable_customInspectorMessageHandler","createHandlersFactory","unstable_experiments","enableNetworkInspector","enableOpenDebuggerRedirect","env","EXPO_DEBUG","enableStandaloneFuseboxShell","envIsHeadless","debuggerWebsocketEndpoint","createNetworkWebsocket","on","socket","request","isLocalSocket","isMatchingOrigin","terminate","debugMiddleware","req","res","next","debugWebsocketEndpoints","info","makeLogger","warn","error","level","data","update","type","debuggerWebsocket","wss","WebSocketServer","noServer","perMessageDeflate","maxPayload","networkSocket","message","JSON","parse","toString","method","params","requestId","requestInfo","recordNetworkResponse","startsWith","clients","forEach","debuggerSocket","readyState","OPEN","send"],"mappings":"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"}
|
|
@@ -14,15 +14,31 @@ _export(exports, {
|
|
|
14
14
|
},
|
|
15
15
|
get NetworkResponseHandler () {
|
|
16
16
|
return NetworkResponseHandler;
|
|
17
|
+
},
|
|
18
|
+
get recordNetworkResponse () {
|
|
19
|
+
return recordNetworkResponse;
|
|
17
20
|
}
|
|
18
21
|
});
|
|
19
22
|
const _MessageHandler = require("../MessageHandler");
|
|
20
23
|
const NETWORK_RESPONSE_STORAGE = new Map();
|
|
24
|
+
// Bounded so an unauthenticated client on `/inspector/network` cannot grow this
|
|
25
|
+
// map without limit. `Map` preserves insertion order, so the first key is the
|
|
26
|
+
// oldest entry — drop it on overflow (FIFO).
|
|
27
|
+
const MAX_NETWORK_RESPONSES = 512;
|
|
28
|
+
function recordNetworkResponse(requestId, info) {
|
|
29
|
+
NETWORK_RESPONSE_STORAGE.set(requestId, info);
|
|
30
|
+
if (NETWORK_RESPONSE_STORAGE.size > MAX_NETWORK_RESPONSES) {
|
|
31
|
+
const oldestKey = NETWORK_RESPONSE_STORAGE.keys().next().value;
|
|
32
|
+
if (oldestKey !== undefined) {
|
|
33
|
+
NETWORK_RESPONSE_STORAGE.delete(oldestKey);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
21
37
|
class NetworkResponseHandler extends _MessageHandler.MessageHandler {
|
|
22
38
|
handleDeviceMessage(message) {
|
|
23
39
|
if (message.method === 'Expo(Network.receivedResponseBody)') {
|
|
24
40
|
const { requestId, ...requestInfo } = message.params;
|
|
25
|
-
|
|
41
|
+
recordNetworkResponse(requestId, requestInfo);
|
|
26
42
|
return true;
|
|
27
43
|
}
|
|
28
44
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/start/server/metro/debugging/messageHandlers/NetworkResponse.ts"],"sourcesContent":["import type { Protocol } from 'devtools-protocol';\n\nimport { MessageHandler } from '../MessageHandler';\nimport type {\n CdpMessage,\n DeviceRequest,\n DebuggerRequest,\n DebuggerResponse,\n DeviceResponse,\n} from '../types';\n\n/**\n * The global network response storage, as a workaround for the network inspector.\n * @see createDebugMiddleware#createNetworkWebsocket\n */\nexport const NETWORK_RESPONSE_STORAGE = new Map<\n string,\n DebuggerResponse<NetworkGetResponseBody>['result']\n>();\n\nexport class NetworkResponseHandler extends MessageHandler {\n /** All known responses, mapped by request id */\n storage = NETWORK_RESPONSE_STORAGE;\n\n handleDeviceMessage(message: DeviceRequest<NetworkReceivedResponseBody>) {\n if (message.method === 'Expo(Network.receivedResponseBody)') {\n const { requestId, ...requestInfo } = message.params;\n
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/start/server/metro/debugging/messageHandlers/NetworkResponse.ts"],"sourcesContent":["import type { Protocol } from 'devtools-protocol';\n\nimport { MessageHandler } from '../MessageHandler';\nimport type {\n CdpMessage,\n DeviceRequest,\n DebuggerRequest,\n DebuggerResponse,\n DeviceResponse,\n} from '../types';\n\n/**\n * The global network response storage, as a workaround for the network inspector.\n * @see createDebugMiddleware#createNetworkWebsocket\n */\nexport const NETWORK_RESPONSE_STORAGE = new Map<\n string,\n DebuggerResponse<NetworkGetResponseBody>['result']\n>();\n\n// Bounded so an unauthenticated client on `/inspector/network` cannot grow this\n// map without limit. `Map` preserves insertion order, so the first key is the\n// oldest entry — drop it on overflow (FIFO).\nconst MAX_NETWORK_RESPONSES = 512;\n\nexport function recordNetworkResponse(\n requestId: string,\n info: DebuggerResponse<NetworkGetResponseBody>['result']\n) {\n NETWORK_RESPONSE_STORAGE.set(requestId, info);\n if (NETWORK_RESPONSE_STORAGE.size > MAX_NETWORK_RESPONSES) {\n const oldestKey = NETWORK_RESPONSE_STORAGE.keys().next().value;\n if (oldestKey !== undefined) {\n NETWORK_RESPONSE_STORAGE.delete(oldestKey);\n }\n }\n}\n\nexport class NetworkResponseHandler extends MessageHandler {\n /** All known responses, mapped by request id */\n storage = NETWORK_RESPONSE_STORAGE;\n\n handleDeviceMessage(message: DeviceRequest<NetworkReceivedResponseBody>) {\n if (message.method === 'Expo(Network.receivedResponseBody)') {\n const { requestId, ...requestInfo } = message.params;\n recordNetworkResponse(requestId, requestInfo);\n return true;\n }\n\n return false;\n }\n\n handleDebuggerMessage(message: DebuggerRequest<NetworkGetResponseBody>) {\n if (\n message.method === 'Network.getResponseBody' &&\n this.storage.has(message.params.requestId)\n ) {\n return this.sendToDebugger<DeviceResponse<NetworkGetResponseBody>>({\n id: message.id,\n result: this.storage.get(message.params.requestId)!,\n });\n }\n\n return false;\n }\n}\n\n/** Custom message to transfer the response body data to the proxy */\nexport type NetworkReceivedResponseBody = CdpMessage<\n 'Expo(Network.receivedResponseBody)',\n Protocol.Network.GetResponseBodyRequest & Protocol.Network.GetResponseBodyResponse,\n never\n>;\n\n/** @see https://chromedevtools.github.io/devtools-protocol/1-2/Network/#method-getResponseBody */\nexport type NetworkGetResponseBody = CdpMessage<\n 'Network.getResponseBody',\n Protocol.Network.GetResponseBodyRequest,\n Protocol.Network.GetResponseBodyResponse\n>;\n"],"names":["NETWORK_RESPONSE_STORAGE","NetworkResponseHandler","recordNetworkResponse","Map","MAX_NETWORK_RESPONSES","requestId","info","set","size","oldestKey","keys","next","value","undefined","delete","MessageHandler","handleDeviceMessage","message","method","requestInfo","params","handleDebuggerMessage","storage","has","sendToDebugger","id","result","get"],"mappings":";;;;;;;;;;;QAeaA;eAAAA;;QAuBAC;eAAAA;;QAbGC;eAAAA;;;gCAvBe;AAaxB,MAAMF,2BAA2B,IAAIG;AAK5C,gFAAgF;AAChF,8EAA8E;AAC9E,6CAA6C;AAC7C,MAAMC,wBAAwB;AAEvB,SAASF,sBACdG,SAAiB,EACjBC,IAAwD;IAExDN,yBAAyBO,GAAG,CAACF,WAAWC;IACxC,IAAIN,yBAAyBQ,IAAI,GAAGJ,uBAAuB;QACzD,MAAMK,YAAYT,yBAAyBU,IAAI,GAAGC,IAAI,GAAGC,KAAK;QAC9D,IAAIH,cAAcI,WAAW;YAC3Bb,yBAAyBc,MAAM,CAACL;QAClC;IACF;AACF;AAEO,MAAMR,+BAA+Bc,8BAAc;IAIxDC,oBAAoBC,OAAmD,EAAE;QACvE,IAAIA,QAAQC,MAAM,KAAK,sCAAsC;YAC3D,MAAM,EAAEb,SAAS,EAAE,GAAGc,aAAa,GAAGF,QAAQG,MAAM;YACpDlB,sBAAsBG,WAAWc;YACjC,OAAO;QACT;QAEA,OAAO;IACT;IAEAE,sBAAsBJ,OAAgD,EAAE;QACtE,IACEA,QAAQC,MAAM,KAAK,6BACnB,IAAI,CAACI,OAAO,CAACC,GAAG,CAACN,QAAQG,MAAM,CAACf,SAAS,GACzC;YACA,OAAO,IAAI,CAACmB,cAAc,CAAyC;gBACjEC,IAAIR,QAAQQ,EAAE;gBACdC,QAAQ,IAAI,CAACJ,OAAO,CAACK,GAAG,CAACV,QAAQG,MAAM,CAACf,SAAS;YACnD;QACF;QAEA,OAAO;IACT;;QA1BK,gBACL,8CAA8C,QAC9CiB,UAAUtB;;AAyBZ"}
|
|
@@ -25,6 +25,12 @@ function _ws() {
|
|
|
25
25
|
const _createSocketBroadcaster = require("./utils/createSocketBroadcaster");
|
|
26
26
|
const _createSocketMap = require("./utils/createSocketMap");
|
|
27
27
|
const _socketMessages = require("./utils/socketMessages");
|
|
28
|
+
const _net = require("../../../../utils/net");
|
|
29
|
+
const debug = require('debug')('expo:metro:devserver:messageSocket');
|
|
30
|
+
const CLIENT_BROADCAST_ALLOWED_METHODS = new Set([
|
|
31
|
+
'reload',
|
|
32
|
+
'devMenu'
|
|
33
|
+
]);
|
|
28
34
|
function createMessagesSocket(options) {
|
|
29
35
|
const clients = (0, _createSocketMap.createSocketMap)();
|
|
30
36
|
const broadcast = (0, _createSocketBroadcaster.createBroadcaster)(clients.map);
|
|
@@ -33,6 +39,7 @@ function createMessagesSocket(options) {
|
|
|
33
39
|
});
|
|
34
40
|
server.on('connection', (socket, req)=>{
|
|
35
41
|
const client = clients.registerSocket(socket);
|
|
42
|
+
const isTrustedClient = (0, _net.isLocalSocket)(req.socket) && (0, _net.isMatchingOrigin)(req, options.serverBaseUrl);
|
|
36
43
|
// Assign the query parameters to the socket, used for `getpeers` requests
|
|
37
44
|
// NOTE(cedric): this looks like a legacy feature, might be able to drop it
|
|
38
45
|
if (req.url) {
|
|
@@ -44,7 +51,7 @@ function createMessagesSocket(options) {
|
|
|
44
51
|
socket.on('close', client.terminate);
|
|
45
52
|
socket.on('error', client.terminate);
|
|
46
53
|
// Register message handler
|
|
47
|
-
socket.on('message', createClientMessageHandler(socket, client.id, clients, broadcast));
|
|
54
|
+
socket.on('message', createClientMessageHandler(socket, client.id, clients, broadcast, isTrustedClient));
|
|
48
55
|
});
|
|
49
56
|
return {
|
|
50
57
|
endpoint: '/message',
|
|
@@ -60,7 +67,7 @@ function createMessagesSocket(options) {
|
|
|
60
67
|
}
|
|
61
68
|
};
|
|
62
69
|
}
|
|
63
|
-
function createClientMessageHandler(socket, clientId, clients, broadcast) {
|
|
70
|
+
function createClientMessageHandler(socket, clientId, clients, broadcast, isTrustedClient) {
|
|
64
71
|
function handleServerRequest(message) {
|
|
65
72
|
// Ignore messages without identifiers, unable to link responses
|
|
66
73
|
if (!message.id) return;
|
|
@@ -88,6 +95,10 @@ function createClientMessageHandler(socket, clientId, clients, broadcast) {
|
|
|
88
95
|
if (!message) return;
|
|
89
96
|
// Handle broadcast messages
|
|
90
97
|
if (messageIsBroadcast(message)) {
|
|
98
|
+
if (!isTrustedClient || !CLIENT_BROADCAST_ALLOWED_METHODS.has(message.method)) {
|
|
99
|
+
debug(`Refused broadcast message from untrusted client (method: ${message.method})`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
91
102
|
return broadcast(null, data.toString());
|
|
92
103
|
}
|
|
93
104
|
// Handle incoming requests from clients
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/start/server/metro/dev-server/createMessageSocket.ts"],"sourcesContent":["import { parse } from 'node:url';\nimport { type WebSocket, WebSocketServer, type RawData as WebSocketRawData } from 'ws';\n\nimport { createBroadcaster } from './utils/createSocketBroadcaster';\nimport { createSocketMap, type SocketId } from './utils/createSocketMap';\nimport { parseRawMessage, serializeMessage } from './utils/socketMessages';\n\ntype MessageSocketOptions = {\n logger: {\n warn: (message: string) => any;\n };\n};\n\n/**\n * Client \"command\" server that dispatches basic commands to connected clients.\n * This basic client to client communication, reload, or open dev menu cli commands.\n */\nexport function createMessagesSocket(options: MessageSocketOptions) {\n const clients = createSocketMap();\n const broadcast = createBroadcaster(clients.map);\n\n const server = new WebSocketServer({ noServer: true });\n\n server.on('connection', (socket, req) => {\n const client = clients.registerSocket(socket);\n\n // Assign the query parameters to the socket, used for `getpeers` requests\n // NOTE(cedric): this looks like a legacy feature, might be able to drop it\n if (req.url) {\n Object.defineProperty(socket, '_upgradeQuery', {\n value: parse(req.url).query,\n });\n }\n\n // Register disconnect handlers\n socket.on('close', client.terminate);\n socket.on('error', client.terminate);\n // Register message handler\n socket.on('message', createClientMessageHandler(socket, client.id, clients, broadcast));\n });\n\n return {\n endpoint: '/message' as const,\n server,\n broadcast: (method: BroadcastMessage['method'], params?: BroadcastMessage['params']) => {\n if (clients.map.size === 0) {\n return options.logger.warn(\n `No apps connected. Sending \"${method}\" to all React Native apps failed. Make sure your app is running in the simulator or on a phone connected via USB.`\n );\n }\n\n broadcast(null, serializeMessage({ method, params }));\n },\n };\n}\n\nfunction createClientMessageHandler(\n socket: WebSocket,\n clientId: SocketId,\n clients: ReturnType<typeof createSocketMap>,\n broadcast: ReturnType<typeof createBroadcaster>\n) {\n function handleServerRequest(message: RequestMessage) {\n // Ignore messages without identifiers, unable to link responses\n if (!message.id) return;\n\n if (message.method === 'getid') {\n return socket.send(serializeMessage({ id: message.id, result: clientId }));\n }\n\n if (message.method === 'getpeers') {\n const peers: Record<string, any> = {};\n clients.map.forEach((peerSocket, peerSocketId) => {\n if (peerSocketId !== clientId) {\n peers[peerSocketId] = '_upgradeQuery' in peerSocket ? peerSocket._upgradeQuery : {};\n }\n });\n return socket.send(serializeMessage({ id: message.id, result: peers }));\n }\n }\n\n return (data: WebSocketRawData, isBinary: boolean) => {\n const message = parseRawMessage<IncomingMessage>(data, isBinary);\n if (!message) return;\n\n // Handle broadcast messages\n if (messageIsBroadcast(message)) {\n return broadcast(null, data.toString());\n }\n\n // Handle incoming requests from clients\n if (messageIsRequest(message)) {\n if (message.target === 'server') {\n return handleServerRequest(message);\n }\n\n return clients.findSocket(message.target)?.send(\n serializeMessage({\n method: message.method,\n params: message.params,\n id: !message.id\n ? undefined\n : {\n requestId: message.id,\n clientId,\n },\n })\n );\n }\n\n // Handle incoming responses\n if (messageIsResponse(message)) {\n return clients.findSocket(message.id.clientId)?.send(\n serializeMessage({\n id: message.id.requestId,\n result: message.result,\n error: message.error,\n })\n );\n }\n };\n}\n\ntype MessageId = {\n requestId: string;\n clientId: SocketId;\n};\n\ntype IncomingMessage = BroadcastMessage | RequestMessage | ResponseMessage;\n\ntype BroadcastMessage = {\n method: string;\n params?: Record<string, any>;\n};\n\ntype RequestMessage = {\n method: string;\n params?: Record<string, any>;\n target: string;\n id?: string;\n};\n\ntype ResponseMessage = {\n result?: any;\n error?: Error;\n id: MessageId;\n};\n\nfunction messageIsBroadcast(message: IncomingMessage): message is BroadcastMessage {\n return (\n 'method' in message &&\n typeof message.method === 'string' &&\n (!('id' in message) || message.id === undefined) &&\n (!('target' in message) || message.target === undefined)\n );\n}\n\nfunction messageIsRequest(message: IncomingMessage): message is RequestMessage {\n return (\n 'method' in message &&\n typeof message.method === 'string' &&\n 'target' in message &&\n typeof message.target === 'string'\n );\n}\n\nfunction messageIsResponse(message: IncomingMessage): message is ResponseMessage {\n return (\n 'id' in message &&\n typeof message.id === 'object' &&\n typeof message.id.requestId !== 'undefined' &&\n typeof message.id.clientId === 'string' &&\n (('result' in message && !!message.result) || ('error' in message && !!message.error))\n );\n}\n"],"names":["createMessagesSocket","options","clients","createSocketMap","broadcast","createBroadcaster","map","server","WebSocketServer","noServer","on","socket","req","client","registerSocket","url","Object","defineProperty","value","parse","query","terminate","createClientMessageHandler","id","endpoint","method","params","size","logger","warn","serializeMessage","clientId","handleServerRequest","message","send","result","peers","forEach","peerSocket","peerSocketId","_upgradeQuery","data","isBinary","parseRawMessage","messageIsBroadcast","toString","messageIsRequest","target","findSocket","undefined","requestId","messageIsResponse","error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/start/server/metro/dev-server/createMessageSocket.ts"],"sourcesContent":["import { parse } from 'node:url';\nimport { type WebSocket, WebSocketServer, type RawData as WebSocketRawData } from 'ws';\n\nimport { createBroadcaster } from './utils/createSocketBroadcaster';\nimport { createSocketMap, type SocketId } from './utils/createSocketMap';\nimport { parseRawMessage, serializeMessage } from './utils/socketMessages';\nimport { isLocalSocket, isMatchingOrigin } from '../../../../utils/net';\n\ntype MessageSocketOptions = {\n logger: {\n warn: (message: string) => any;\n };\n serverBaseUrl: string;\n};\n\nconst debug = require('debug')('expo:metro:devserver:messageSocket') as typeof console.log;\n\nconst CLIENT_BROADCAST_ALLOWED_METHODS = new Set(['reload', 'devMenu']);\n\n/**\n * Client \"command\" server that dispatches basic commands to connected clients.\n * This basic client to client communication, reload, or open dev menu cli commands.\n */\nexport function createMessagesSocket(options: MessageSocketOptions) {\n const clients = createSocketMap();\n const broadcast = createBroadcaster(clients.map);\n\n const server = new WebSocketServer({ noServer: true });\n\n server.on('connection', (socket, req) => {\n const client = clients.registerSocket(socket);\n const isTrustedClient =\n isLocalSocket(req.socket) && isMatchingOrigin(req, options.serverBaseUrl);\n\n // Assign the query parameters to the socket, used for `getpeers` requests\n // NOTE(cedric): this looks like a legacy feature, might be able to drop it\n if (req.url) {\n Object.defineProperty(socket, '_upgradeQuery', {\n value: parse(req.url).query,\n });\n }\n\n // Register disconnect handlers\n socket.on('close', client.terminate);\n socket.on('error', client.terminate);\n // Register message handler\n socket.on(\n 'message',\n createClientMessageHandler(socket, client.id, clients, broadcast, isTrustedClient)\n );\n });\n\n return {\n endpoint: '/message' as const,\n server,\n broadcast: (method: BroadcastMessage['method'], params?: BroadcastMessage['params']) => {\n if (clients.map.size === 0) {\n return options.logger.warn(\n `No apps connected. Sending \"${method}\" to all React Native apps failed. Make sure your app is running in the simulator or on a phone connected via USB.`\n );\n }\n\n broadcast(null, serializeMessage({ method, params }));\n },\n };\n}\n\nfunction createClientMessageHandler(\n socket: WebSocket,\n clientId: SocketId,\n clients: ReturnType<typeof createSocketMap>,\n broadcast: ReturnType<typeof createBroadcaster>,\n isTrustedClient: boolean\n) {\n function handleServerRequest(message: RequestMessage) {\n // Ignore messages without identifiers, unable to link responses\n if (!message.id) return;\n\n if (message.method === 'getid') {\n return socket.send(serializeMessage({ id: message.id, result: clientId }));\n }\n\n if (message.method === 'getpeers') {\n const peers: Record<string, any> = {};\n clients.map.forEach((peerSocket, peerSocketId) => {\n if (peerSocketId !== clientId) {\n peers[peerSocketId] = '_upgradeQuery' in peerSocket ? peerSocket._upgradeQuery : {};\n }\n });\n return socket.send(serializeMessage({ id: message.id, result: peers }));\n }\n }\n\n return (data: WebSocketRawData, isBinary: boolean) => {\n const message = parseRawMessage<IncomingClientMessage>(data, isBinary);\n if (!message) return;\n\n // Handle broadcast messages\n if (messageIsBroadcast(message)) {\n if (!isTrustedClient || !CLIENT_BROADCAST_ALLOWED_METHODS.has(message.method)) {\n debug(`Refused broadcast message from untrusted client (method: ${message.method})`);\n return;\n }\n return broadcast(null, data.toString());\n }\n\n // Handle incoming requests from clients\n if (messageIsRequest(message)) {\n if (message.target === 'server') {\n return handleServerRequest(message);\n }\n\n return clients.findSocket(message.target)?.send(\n serializeMessage({\n method: message.method,\n params: message.params,\n id: !message.id\n ? undefined\n : {\n requestId: message.id,\n clientId,\n },\n })\n );\n }\n\n // Handle incoming responses\n if (messageIsResponse(message)) {\n return clients.findSocket(message.id.clientId)?.send(\n serializeMessage({\n id: message.id.requestId,\n result: message.result,\n error: message.error,\n })\n );\n }\n };\n}\n\ntype MessageId = {\n requestId: string;\n clientId: SocketId;\n};\n\ntype IncomingClientMessage = BroadcastMessage | RequestMessage | ResponseMessage;\n\ntype BroadcastMessage = {\n method: string;\n params?: Record<string, any>;\n};\n\ntype RequestMessage = {\n method: string;\n params?: Record<string, any>;\n target: string;\n id?: string;\n};\n\ntype ResponseMessage = {\n result?: any;\n error?: Error;\n id: MessageId;\n};\n\nfunction messageIsBroadcast(message: IncomingClientMessage): message is BroadcastMessage {\n return (\n 'method' in message &&\n typeof message.method === 'string' &&\n (!('id' in message) || message.id === undefined) &&\n (!('target' in message) || message.target === undefined)\n );\n}\n\nfunction messageIsRequest(message: IncomingClientMessage): message is RequestMessage {\n return (\n 'method' in message &&\n typeof message.method === 'string' &&\n 'target' in message &&\n typeof message.target === 'string'\n );\n}\n\nfunction messageIsResponse(message: IncomingClientMessage): message is ResponseMessage {\n return (\n 'id' in message &&\n typeof message.id === 'object' &&\n typeof message.id.requestId !== 'undefined' &&\n typeof message.id.clientId === 'string' &&\n (('result' in message && !!message.result) || ('error' in message && !!message.error))\n );\n}\n"],"names":["createMessagesSocket","debug","require","CLIENT_BROADCAST_ALLOWED_METHODS","Set","options","clients","createSocketMap","broadcast","createBroadcaster","map","server","WebSocketServer","noServer","on","socket","req","client","registerSocket","isTrustedClient","isLocalSocket","isMatchingOrigin","serverBaseUrl","url","Object","defineProperty","value","parse","query","terminate","createClientMessageHandler","id","endpoint","method","params","size","logger","warn","serializeMessage","clientId","handleServerRequest","message","send","result","peers","forEach","peerSocket","peerSocketId","_upgradeQuery","data","isBinary","parseRawMessage","messageIsBroadcast","has","toString","messageIsRequest","target","findSocket","undefined","requestId","messageIsResponse","error"],"mappings":"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"}
|
|
@@ -47,7 +47,9 @@ const _compression = require("./compression");
|
|
|
47
47
|
const _createEventSocket = require("./createEventSocket");
|
|
48
48
|
const _createMessageSocket = require("./createMessageSocket");
|
|
49
49
|
const _log = require("../../../../log");
|
|
50
|
+
const _dir = require("../../../../utils/dir");
|
|
50
51
|
const _editor = require("../../../../utils/editor");
|
|
52
|
+
const _net = require("../../../../utils/net");
|
|
51
53
|
function _interop_require_default(obj) {
|
|
52
54
|
return obj && obj.__esModule ? obj : {
|
|
53
55
|
default: obj
|
|
@@ -55,7 +57,8 @@ function _interop_require_default(obj) {
|
|
|
55
57
|
}
|
|
56
58
|
function createMetroMiddleware(metroConfig, options) {
|
|
57
59
|
const messages = (0, _createMessageSocket.createMessagesSocket)({
|
|
58
|
-
logger: _log.Log
|
|
60
|
+
logger: _log.Log,
|
|
61
|
+
serverBaseUrl: options.serverBaseUrl
|
|
59
62
|
});
|
|
60
63
|
const events = (0, _createEventSocket.createEventsSocket)(messages);
|
|
61
64
|
const middleware = (0, _connect().default)().use(noCacheMiddleware).use(_compression.compression)// Support opening stack frames from clients directly in the editor
|
|
@@ -111,6 +114,10 @@ function createMetroOpenStackFrameMiddleware(metroConfig) {
|
|
|
111
114
|
res.statusCode = 400;
|
|
112
115
|
return res.end('Open stack frame requires target file to be in server root');
|
|
113
116
|
}
|
|
117
|
+
if ((0, _net.shouldThrottleRemoteDevCall)()) {
|
|
118
|
+
res.statusCode = 429;
|
|
119
|
+
return res.end();
|
|
120
|
+
}
|
|
114
121
|
try {
|
|
115
122
|
await (0, _editor.openInEditorAsync)(file, frame.lineNumber);
|
|
116
123
|
return res.end('OK');
|
|
@@ -126,7 +133,7 @@ const ensureFileInRootDirectory = async (root, file)=>{
|
|
|
126
133
|
file = await _nodefs().default.promises.realpath(file);
|
|
127
134
|
// Cannot be accessed using Metro's server API, we need to move the file
|
|
128
135
|
// into the project root and try again.
|
|
129
|
-
if (
|
|
136
|
+
if ((0, _dir.isPathInside)(file, root)) {
|
|
130
137
|
return file;
|
|
131
138
|
} else {
|
|
132
139
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/start/server/metro/dev-server/createMetroMiddleware.ts"],"sourcesContent":["import { getMetroServerRoot } from '@expo/config/paths';\nimport type { MetroConfig } from '@expo/metro/metro';\nimport type MetroBundler from '@expo/metro/metro/Bundler';\nimport connect from 'connect';\nimport { Body } from 'fetch-nodeshim';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { compression } from './compression';\nimport { createEventsSocket } from './createEventSocket';\nimport { createMessagesSocket } from './createMessageSocket';\nimport { Log } from '../../../../log';\nimport { openInEditorAsync } from '../../../../utils/editor';\n\ninterface MetroMiddlewareOptions {\n getMetroBundler(): MetroBundler;\n}\n\ninterface StackFrame {\n file: string;\n lineNumber?: number | undefined;\n}\n\nexport function createMetroMiddleware(\n metroConfig: Pick<MetroConfig, 'projectRoot'>,\n options: MetroMiddlewareOptions\n) {\n const messages = createMessagesSocket({ logger: Log });\n const events = createEventsSocket(messages);\n\n const middleware = connect()\n .use(noCacheMiddleware)\n .use(compression)\n // Support opening stack frames from clients directly in the editor\n .use('/open-stack-frame', createMetroOpenStackFrameMiddleware(metroConfig))\n // Support status check to detect if the packager needs to be started from the native side\n .use('/status', createMetroStatusMiddleware(metroConfig, options));\n\n return {\n middleware,\n messagesSocket: messages,\n eventsSocket: events,\n websocketEndpoints: {\n [messages.endpoint]: messages.server,\n [events.endpoint]: events.server,\n },\n };\n}\n\nconst noCacheMiddleware: connect.NextHandleFunction = (_req, res, next) => {\n res.setHeader('Surrogate-Control', 'no-store');\n res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');\n res.setHeader('Pragma', 'no-cache');\n res.setHeader('Expires', '0');\n next();\n};\n\nfunction createMetroStatusMiddleware(\n metroConfig: Pick<MetroConfig, 'projectRoot'>,\n options: MetroMiddlewareOptions\n): connect.NextHandleFunction {\n return async (_req, res) => {\n res.setHeader('X-React-Native-Project-Root', encodeURI(metroConfig.projectRoot!));\n res.flushHeaders();\n await options.getMetroBundler().ready();\n res.end('packager-status:running');\n };\n}\n\nfunction createMetroOpenStackFrameMiddleware(\n metroConfig: Pick<MetroConfig, 'projectRoot'>\n): connect.NextHandleFunction {\n return async (req, res, next) => {\n if (req.method !== 'POST') {\n return next();\n }\n\n let frame: StackFrame | undefined;\n try {\n const json = await new Body(req).json();\n if (typeof json === 'object' && json != null && typeof json.file === 'string') {\n frame = {\n file: json.file,\n lineNumber:\n typeof json.lineNumber === 'number' && Number.isSafeInteger(json.lineNumber)\n ? json.lineNumber\n : undefined,\n };\n }\n } catch {}\n if (!frame) {\n res.statusCode = 400;\n return res.end('Open stack frame requires the JSON stack frame as request body');\n }\n\n const root = getMetroServerRoot(metroConfig.projectRoot!);\n const file = await ensureFileInRootDirectory(root, frame.file);\n if (!file) {\n res.statusCode = 400;\n return res.end('Open stack frame requires target file to be in server root');\n }\n\n try {\n await openInEditorAsync(file, frame.lineNumber);\n return res.end('OK');\n } catch {\n res.statusCode = 5006;\n return res.end('Open stack frame failed to open local editor');\n }\n };\n}\n\nconst ensureFileInRootDirectory = async (root: string, file: string): Promise<string | null> => {\n try {\n file = path.resolve(root, file);\n file = await fs.promises.realpath(file);\n // Cannot be accessed using Metro's server API, we need to move the file\n // into the project root and try again.\n if (
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/start/server/metro/dev-server/createMetroMiddleware.ts"],"sourcesContent":["import { getMetroServerRoot } from '@expo/config/paths';\nimport type { MetroConfig } from '@expo/metro/metro';\nimport type MetroBundler from '@expo/metro/metro/Bundler';\nimport connect from 'connect';\nimport { Body } from 'fetch-nodeshim';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { compression } from './compression';\nimport { createEventsSocket } from './createEventSocket';\nimport { createMessagesSocket } from './createMessageSocket';\nimport { Log } from '../../../../log';\nimport { isPathInside } from '../../../../utils/dir';\nimport { openInEditorAsync } from '../../../../utils/editor';\nimport { shouldThrottleRemoteDevCall } from '../../../../utils/net';\n\ninterface MetroMiddlewareOptions {\n getMetroBundler(): MetroBundler;\n serverBaseUrl: string;\n}\n\ninterface StackFrame {\n file: string;\n lineNumber?: number | undefined;\n}\n\nexport function createMetroMiddleware(\n metroConfig: Pick<MetroConfig, 'projectRoot'>,\n options: MetroMiddlewareOptions\n) {\n const messages = createMessagesSocket({ logger: Log, serverBaseUrl: options.serverBaseUrl });\n const events = createEventsSocket(messages);\n\n const middleware = connect()\n .use(noCacheMiddleware)\n .use(compression)\n // Support opening stack frames from clients directly in the editor\n .use('/open-stack-frame', createMetroOpenStackFrameMiddleware(metroConfig))\n // Support status check to detect if the packager needs to be started from the native side\n .use('/status', createMetroStatusMiddleware(metroConfig, options));\n\n return {\n middleware,\n messagesSocket: messages,\n eventsSocket: events,\n websocketEndpoints: {\n [messages.endpoint]: messages.server,\n [events.endpoint]: events.server,\n },\n };\n}\n\nconst noCacheMiddleware: connect.NextHandleFunction = (_req, res, next) => {\n res.setHeader('Surrogate-Control', 'no-store');\n res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');\n res.setHeader('Pragma', 'no-cache');\n res.setHeader('Expires', '0');\n next();\n};\n\nfunction createMetroStatusMiddleware(\n metroConfig: Pick<MetroConfig, 'projectRoot'>,\n options: MetroMiddlewareOptions\n): connect.NextHandleFunction {\n return async (_req, res) => {\n res.setHeader('X-React-Native-Project-Root', encodeURI(metroConfig.projectRoot!));\n res.flushHeaders();\n await options.getMetroBundler().ready();\n res.end('packager-status:running');\n };\n}\n\nfunction createMetroOpenStackFrameMiddleware(\n metroConfig: Pick<MetroConfig, 'projectRoot'>\n): connect.NextHandleFunction {\n return async (req, res, next) => {\n if (req.method !== 'POST') {\n return next();\n }\n\n let frame: StackFrame | undefined;\n try {\n const json = await new Body(req).json();\n if (typeof json === 'object' && json != null && typeof json.file === 'string') {\n frame = {\n file: json.file,\n lineNumber:\n typeof json.lineNumber === 'number' && Number.isSafeInteger(json.lineNumber)\n ? json.lineNumber\n : undefined,\n };\n }\n } catch {}\n if (!frame) {\n res.statusCode = 400;\n return res.end('Open stack frame requires the JSON stack frame as request body');\n }\n\n const root = getMetroServerRoot(metroConfig.projectRoot!);\n const file = await ensureFileInRootDirectory(root, frame.file);\n if (!file) {\n res.statusCode = 400;\n return res.end('Open stack frame requires target file to be in server root');\n }\n\n if (shouldThrottleRemoteDevCall()) {\n res.statusCode = 429;\n return res.end();\n }\n\n try {\n await openInEditorAsync(file, frame.lineNumber);\n return res.end('OK');\n } catch {\n res.statusCode = 5006;\n return res.end('Open stack frame failed to open local editor');\n }\n };\n}\n\nconst ensureFileInRootDirectory = async (root: string, file: string): Promise<string | null> => {\n try {\n file = path.resolve(root, file);\n file = await fs.promises.realpath(file);\n // Cannot be accessed using Metro's server API, we need to move the file\n // into the project root and try again.\n if (isPathInside(file, root)) {\n return file;\n } else {\n return null;\n }\n } catch {\n return null;\n }\n};\n"],"names":["createMetroMiddleware","metroConfig","options","messages","createMessagesSocket","logger","Log","serverBaseUrl","events","createEventsSocket","middleware","connect","use","noCacheMiddleware","compression","createMetroOpenStackFrameMiddleware","createMetroStatusMiddleware","messagesSocket","eventsSocket","websocketEndpoints","endpoint","server","_req","res","next","setHeader","encodeURI","projectRoot","flushHeaders","getMetroBundler","ready","end","req","method","frame","json","Body","file","lineNumber","Number","isSafeInteger","undefined","statusCode","root","getMetroServerRoot","ensureFileInRootDirectory","shouldThrottleRemoteDevCall","openInEditorAsync","path","resolve","fs","promises","realpath","isPathInside"],"mappings":"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"}
|
|
@@ -350,15 +350,16 @@ async function instantiateMetroAsync(metroBundler, options, { isExporting, exp =
|
|
|
350
350
|
isExporting,
|
|
351
351
|
getMetroBundler
|
|
352
352
|
});
|
|
353
|
-
// Create the core middleware stack for Metro, including websocket listeners
|
|
354
|
-
const { middleware, messagesSocket, eventsSocket, websocketEndpoints } = (0, _createMetroMiddleware.createMetroMiddleware)(metroConfig, {
|
|
355
|
-
getMetroBundler
|
|
356
|
-
});
|
|
357
353
|
// Get local URL to Metro bundler server (typically configured as 127.0.0.1:8081)
|
|
358
354
|
const serverBaseUrl = metroBundler.getUrlCreator().constructUrl({
|
|
359
355
|
scheme: 'http',
|
|
360
356
|
hostType: 'localhost'
|
|
361
357
|
});
|
|
358
|
+
// Create the core middleware stack for Metro, including websocket listeners
|
|
359
|
+
const { middleware, messagesSocket, eventsSocket, websocketEndpoints } = (0, _createMetroMiddleware.createMetroMiddleware)(metroConfig, {
|
|
360
|
+
getMetroBundler,
|
|
361
|
+
serverBaseUrl
|
|
362
|
+
});
|
|
362
363
|
if (!isExporting) {
|
|
363
364
|
// Enable correct CORS headers for Expo Router features
|
|
364
365
|
(0, _mutations.prependMiddleware)(middleware, (0, _CorsMiddleware.createCorsMiddleware)(exp));
|
|
@@ -369,7 +370,9 @@ async function instantiateMetroAsync(metroBundler, options, { isExporting, exp =
|
|
|
369
370
|
});
|
|
370
371
|
Object.assign(websocketEndpoints, debugWebsocketEndpoints);
|
|
371
372
|
middleware.use(debugMiddleware);
|
|
372
|
-
middleware.use('/_expo/debugger', (0, _createJsInspectorMiddleware.createJsInspectorMiddleware)(
|
|
373
|
+
middleware.use('/_expo/debugger', (0, _createJsInspectorMiddleware.createJsInspectorMiddleware)({
|
|
374
|
+
serverBaseUrl
|
|
375
|
+
}));
|
|
373
376
|
// TODO(cedric): `enhanceMiddleware` is deprecated, but is currently used to unify the middleware stacks
|
|
374
377
|
// See: https://github.com/facebook/metro/commit/22e85fde85ec454792a1b70eba4253747a2587a9
|
|
375
378
|
// See: https://github.com/facebook/metro/commit/d0d554381f119bb80ab09dbd6a1d310b54737e52
|