Skip to content

Commit 3bc3645

Browse files
motiz88facebook-github-bot
authored andcommitted
Fix HmrServer crash when entry point uses virtual URL prefix (facebook#1709)
Summary: D102004228 added support for serving bundles from `Server` with the handling `[metro-watchFolders]/<N>/...` or `[metro-project]/...` virtual URL prefixes, but did not add the same support in `HmrServer`. Here, `HmrServer` constructs its own `ProjectRouteMap` to match `Server`'s behaviour exactly. Changelog: * **[Fix]:** Prevent HMR crash on `[metro-watchFolders]` or `[metro-project]` URLs Differential Revision: D104223071
1 parent e90b783 commit 3bc3645

3 files changed

Lines changed: 74 additions & 15 deletions

File tree

packages/metro/src/HmrServer.js

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import debounceAsyncQueue from './lib/debounceAsyncQueue';
2828
import formatBundlingError from './lib/formatBundlingError';
2929
import getGraphId from './lib/getGraphId';
3030
import parseBundleOptionsFromBundleRequestUrl from './lib/parseBundleOptionsFromBundleRequestUrl';
31+
import ProjectRouteMap from './lib/ProjectRouteMap';
3132
import splitBundleOptions from './lib/splitBundleOptions';
3233
import * as transformHelpers from './lib/transformHelpers';
3334
import {Logger} from 'metro-core';
@@ -71,6 +72,7 @@ export default class HmrServer<TClient extends Client> {
7172
_bundler: IncrementalBundler;
7273
_createModuleId: (path: string) => number;
7374
_clientGroups: Map<RevisionId, ClientGroup>;
75+
_routeMap: ProjectRouteMap;
7476

7577
constructor(
7678
bundler: IncrementalBundler,
@@ -81,6 +83,7 @@ export default class HmrServer<TClient extends Client> {
8183
this._bundler = bundler;
8284
this._createModuleId = createModuleId;
8385
this._clientGroups = new Map();
86+
this._routeMap = new ProjectRouteMap(config);
8487
}
8588

8689
onClientConnect: (
@@ -121,19 +124,18 @@ export default class HmrServer<TClient extends Client> {
121124
transformOptions.platform,
122125
resolverOptions,
123126
);
124-
const resolvedEntryFilePath = resolutionFn(
125-
(this._config.server.unstable_serverRoot ?? this._config.projectRoot) +
126-
'/.',
127-
{
128-
name: entryFile,
129-
data: {
130-
key: entryFile,
131-
asyncType: null,
132-
isESMImport: false,
133-
locs: [],
134-
},
127+
const absolutePath = this._routeMap.filePathOfUrlDecodedPathname(entryFile);
128+
const rootDir = absolutePath != null ? '/' : this._routeMap.serverRootDir;
129+
const resolvedEntryFile = absolutePath ?? entryFile;
130+
const resolvedEntryFilePath = resolutionFn(rootDir + '/.', {
131+
name: resolvedEntryFile,
132+
data: {
133+
key: resolvedEntryFile,
134+
asyncType: null,
135+
isESMImport: false,
136+
locs: [],
135137
},
136-
).filePath;
138+
}).filePath;
137139
const graphId = getGraphId(resolvedEntryFilePath, transformOptions, {
138140
resolverOptions,
139141
shallow: graphOptions.shallow,
@@ -379,8 +381,7 @@ export default class HmrServer<TClient extends Client> {
379381
createModuleId: this._createModuleId,
380382
includeAsyncPaths: group.graphOptions.lazy,
381383
projectRoot: this._config.projectRoot,
382-
serverRoot:
383-
this._config.server.unstable_serverRoot ?? this._config.projectRoot,
384+
serverRoot: this._routeMap.serverRootDir,
384385
});
385386

386387
logger?.point('serialize_end');

packages/metro/src/__tests__/HmrServer-test.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ describe('HmrServer', () => {
130130
unstable_allowRequireContext: false,
131131
},
132132
resolver: {platforms: []},
133+
watchFolders: ['/external/node_modules'],
133134
server: {
134135
rewriteRequestUrl(requrl) {
135136
const rewritten = requrl.replace(
@@ -650,6 +651,60 @@ describe('HmrServer', () => {
650651
]);
651652
});
652653

654+
test('should resolve [metro-watchFolders] prefix in entry point', async () => {
655+
await connect(
656+
'/hot?bundleEntry=./[metro-watchFolders]/0/expo-router/entry.js&platform=ios',
657+
);
658+
659+
expect(getRevisionByGraphIdMock).toBeCalledWith(
660+
getGraphId(
661+
'/external/node_modules/expo-router/entry.js',
662+
{
663+
customTransformOptions: {},
664+
dev: true,
665+
minify: false,
666+
platform: 'ios',
667+
type: 'module',
668+
unstable_transformProfile: 'default',
669+
},
670+
{
671+
shallow: false,
672+
lazy: false,
673+
unstable_allowRequireContext: false,
674+
resolverOptions: {
675+
dev: true,
676+
},
677+
},
678+
),
679+
);
680+
});
681+
682+
test('should resolve [metro-project] prefix in entry point', async () => {
683+
await connect('/hot?bundleEntry=./[metro-project]/src/App.js&platform=ios');
684+
685+
expect(getRevisionByGraphIdMock).toBeCalledWith(
686+
getGraphId(
687+
'/root/src/App.js',
688+
{
689+
customTransformOptions: {},
690+
dev: true,
691+
minify: false,
692+
platform: 'ios',
693+
type: 'module',
694+
unstable_transformProfile: 'default',
695+
},
696+
{
697+
shallow: false,
698+
lazy: false,
699+
unstable_allowRequireContext: false,
700+
resolverOptions: {
701+
dev: true,
702+
},
703+
},
704+
),
705+
);
706+
});
707+
653708
test('should return error messages when there is a transform error', async () => {
654709
jest.useRealTimers();
655710
const sendMessage = jest.fn();

packages/metro/types/HmrServer.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*
77
* @noformat
8-
* @generated SignedSource<<ab4c245134631e14db114a9d49da79d1>>
8+
* @generated SignedSource<<81f86f56137b8992ca4e56f39628d548>>
99
*
1010
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
1111
* Original file: packages/metro/src/HmrServer.js
@@ -25,6 +25,8 @@ import type {
2525
HmrUpdateMessage,
2626
} from 'metro-runtime/src/modules/types';
2727

28+
import ProjectRouteMap from './lib/ProjectRouteMap';
29+
2830
export type Client = {
2931
optedIntoHMR: boolean;
3032
revisionIds: Array<RevisionId>;
@@ -51,6 +53,7 @@ declare class HmrServer<TClient extends Client> {
5153
_bundler: IncrementalBundler;
5254
_createModuleId: (path: string) => number;
5355
_clientGroups: Map<RevisionId, ClientGroup>;
56+
_routeMap: ProjectRouteMap;
5457
constructor(
5558
bundler: IncrementalBundler,
5659
createModuleId: (path: string) => number,

0 commit comments

Comments
 (0)