Remove path resolution from internal forks plugin (#23255)
Alternative to #23254 Our build script has a custom plugin to resolve internal module forks. Currently, it uses require.resolve to resolve the path to a real file on disk. Instead, I've updated all the forked module paths to match their location on disk, relative to the project root, to remove the need to resolve them in the build script's runtime. The main motivation is because require.resolve doesn't work with ESM modules, but aside from that, hardcoding the relative paths is more predictable — the Node module resolution algorithm is complicated, and we don't really need its features for this purpose.
Andrew Clark committed
Feb 9, 2022 at 08:44 UTC
274b9fb168eae1b3e5b83db657eb964d365fc45a
2 files changed
+100
-65
scripts/rollup/forks.js
+94
-63
@@ -30,10 +30,22 @@ const __EXPERIMENTAL__ =
30
31
// If you need to replace a file with another file for a specific environment,
32
// add it to this list with the logic for choosing the right replacement.
33
+
34
+// Fork paths are relative to the project root. They must include the full path,
35
+// including the extension. We intentionally don't use Node's module resolution
36
+// algorithm because 1) require.resolve doesn't work with ESM modules, and 2)
37
+// the behavior is easier to predict.
38
const forks = Object.freeze({
39
// Optimization: for UMDs, use a version that we can inline into the React bundle.
40
// Use that from all other bundles.
36
- 'object-assign': (bundleType, entry, dependencies) => {
41
+
42
+ // NOTE: This is hard-coded to the main entry point of the (third-party)
43
+ // object-assign package.
44
+ './node_modules/object-assign/index.js': (
45
+ bundleType,
46
+ entry,
47
+ dependencies
48
+ ) => {
49
if (
50
bundleType !== UMD_DEV &&
51
bundleType !== UMD_PROD &&
@@ -45,7 +57,7 @@ const forks = Object.freeze({
57
}
58
if (entry === 'react' || entry === 'react/unstable-shared-subset') {
59
// Use the forked version that uses ES modules instead of CommonJS.
48
- return 'shared/forks/object-assign.inline-umd.js';
60
+ return './packages/shared/forks/object-assign.inline-umd.js';
61
}
62
if (dependencies.indexOf('react') === -1) {
63
// We can only apply the optimizations to bundle that depend on React
@@ -53,19 +65,25 @@ const forks = Object.freeze({
65
return null;
66
}
67
// We can use the fork that reads the secret export!
56
- return 'shared/forks/object-assign.umd.js';
68
+ return './packages/shared/forks/object-assign.umd.js';
69
},
70
59
- 'react-shallow-renderer': () => {
71
+ // NOTE: This is hard-coded to the main entry point of the (third-party)
72
+ // react-shallow-renderer package.
73
+ './node_modules/react-shallow-renderer/index.js': () => {
74
// Use ESM build of `react-shallow-renderer`.
61
- return 'react-shallow-renderer/esm/index.js';
75
+ return './node_modules/react-shallow-renderer/esm/index.js';
76
},
77
78
// Without this fork, importing `shared/ReactSharedInternals` inside
79
// the `react` package itself would not work due to a cyclical dependency.
66
- 'shared/ReactSharedInternals': (bundleType, entry, dependencies) => {
80
+ './packages/shared/ReactSharedInternals.js': (
81
+ bundleType,
82
+ entry,
83
+ dependencies
84
+ ) => {
85
if (entry === 'react' || entry === 'react/unstable-shared-subset') {
68
- return 'react/src/ReactSharedInternals.js';
86
+ return './packages/react/src/ReactSharedInternals.js';
87
}
88
if (!entry.startsWith('react/') && dependencies.indexOf('react') === -1) {
89
// React internals are unavailable if we can't reference the package.
@@ -82,18 +100,18 @@ const forks = Object.freeze({
100
},
101
102
// We have a few forks for different environments.
85
- 'shared/ReactFeatureFlags': (bundleType, entry) => {
103
+ './packages/shared/ReactFeatureFlags.js': (bundleType, entry) => {
104
switch (entry) {
105
case 'react-native-renderer':
106
switch (bundleType) {
107
case RN_FB_DEV:
108
case RN_FB_PROD:
109
case RN_FB_PROFILING:
92
- return 'shared/forks/ReactFeatureFlags.native-fb.js';
110
+ return './packages/shared/forks/ReactFeatureFlags.native-fb.js';
111
case RN_OSS_DEV:
112
case RN_OSS_PROD:
113
case RN_OSS_PROFILING:
96
- return 'shared/forks/ReactFeatureFlags.native-oss.js';
114
+ return './packages/shared/forks/ReactFeatureFlags.native-oss.js';
115
default:
116
throw Error(
117
`Unexpected entry (${entry}) and bundleType (${bundleType})`
@@ -104,11 +122,11 @@ const forks = Object.freeze({
122
case RN_FB_DEV:
123
case RN_FB_PROD:
124
case RN_FB_PROFILING:
107
- return 'shared/forks/ReactFeatureFlags.native-fb.js';
125
+ return './packages/shared/forks/ReactFeatureFlags.native-fb.js';
126
case RN_OSS_DEV:
127
case RN_OSS_PROD:
128
case RN_OSS_PROFILING:
111
- return 'shared/forks/ReactFeatureFlags.native-oss.js';
129
+ return './packages/shared/forks/ReactFeatureFlags.native-oss.js';
130
default:
131
throw Error(
132
`Unexpected entry (${entry}) and bundleType (${bundleType})`
@@ -122,37 +140,37 @@ const forks = Object.freeze({
140
case RN_OSS_DEV:
141
case RN_OSS_PROD:
142
case RN_OSS_PROFILING:
125
- return 'shared/forks/ReactFeatureFlags.test-renderer.native.js';
143
+ return './packages/shared/forks/ReactFeatureFlags.test-renderer.native.js';
144
case FB_WWW_DEV:
145
case FB_WWW_PROD:
146
case FB_WWW_PROFILING:
129
- return 'shared/forks/ReactFeatureFlags.test-renderer.www.js';
147
+ return './packages/shared/forks/ReactFeatureFlags.test-renderer.www.js';
148
}
131
- return 'shared/forks/ReactFeatureFlags.test-renderer.js';
149
+ return './packages/shared/forks/ReactFeatureFlags.test-renderer.js';
150
case 'react-dom/unstable_testing':
151
switch (bundleType) {
152
case FB_WWW_DEV:
153
case FB_WWW_PROD:
154
case FB_WWW_PROFILING:
137
- return 'shared/forks/ReactFeatureFlags.testing.www.js';
155
+ return './packages/shared/forks/ReactFeatureFlags.testing.www.js';
156
}
139
- return 'shared/forks/ReactFeatureFlags.testing.js';
157
+ return './packages/shared/forks/ReactFeatureFlags.testing.js';
158
default:
159
switch (bundleType) {
160
case FB_WWW_DEV:
161
case FB_WWW_PROD:
162
case FB_WWW_PROFILING:
145
- return 'shared/forks/ReactFeatureFlags.www.js';
163
+ return './packages/shared/forks/ReactFeatureFlags.www.js';
164
case RN_FB_DEV:
165
case RN_FB_PROD:
166
case RN_FB_PROFILING:
149
- return 'shared/forks/ReactFeatureFlags.native-fb.js';
167
+ return './packages/shared/forks/ReactFeatureFlags.native-fb.js';
168
}
169
}
170
return null;
171
},
172
155
- scheduler: (bundleType, entry, dependencies) => {
173
+ './packages/scheduler/index.js': (bundleType, entry, dependencies) => {
174
switch (bundleType) {
175
case UMD_DEV:
176
case UMD_PROD:
@@ -164,28 +182,32 @@ const forks = Object.freeze({
182
}
183
// Optimization: for UMDs, use the API that is already a part of the React
184
// package instead of requiring it to be loaded via a separate <script> tag
167
- return 'shared/forks/Scheduler.umd.js';
185
+ return './packages/shared/forks/Scheduler.umd.js';
186
default:
187
// For other bundles, use the shared NPM package.
188
return null;
189
}
190
},
191
174
- 'scheduler/src/SchedulerFeatureFlags': (bundleType, entry, dependencies) => {
192
+ './packages/scheduler/src/SchedulerFeatureFlags.js': (
193
+ bundleType,
194
+ entry,
195
+ dependencies
196
+ ) => {
197
if (
198
bundleType === FB_WWW_DEV ||
199
bundleType === FB_WWW_PROD ||
200
bundleType === FB_WWW_PROFILING
201
) {
180
- return 'scheduler/src/forks/SchedulerFeatureFlags.www.js';
202
+ return './packages/scheduler/src/forks/SchedulerFeatureFlags.www.js';
203
}
182
- return 'scheduler/src/SchedulerFeatureFlags';
204
+ return './packages/scheduler/src/SchedulerFeatureFlags.js';
205
},
206
185
- 'shared/consoleWithStackDev': (bundleType, entry) => {
207
+ './packages/shared/consoleWithStackDev.js': (bundleType, entry) => {
208
switch (bundleType) {
209
case FB_WWW_DEV:
188
- return 'shared/forks/consoleWithStackDev.www.js';
210
+ return './packages/shared/forks/consoleWithStackDev.www.js';
211
default:
212
return null;
213
}
@@ -193,12 +215,12 @@ const forks = Object.freeze({
215
216
// In FB bundles, we preserve an inline require to ReactCurrentOwner.
217
// See the explanation in FB version of ReactCurrentOwner in www:
196
- 'react/src/ReactCurrentOwner': (bundleType, entry) => {
218
+ './packages/react/src/ReactCurrentOwner.js': (bundleType, entry) => {
219
switch (bundleType) {
220
case FB_WWW_DEV:
221
case FB_WWW_PROD:
222
case FB_WWW_PROFILING:
201
- return 'react/src/forks/ReactCurrentOwner.www.js';
223
+ return './packages/react/src/forks/ReactCurrentOwner.www.js';
224
default:
225
return null;
226
}
@@ -206,41 +228,41 @@ const forks = Object.freeze({
228
229
// Similarly, we preserve an inline require to ReactCurrentDispatcher.
230
// See the explanation in FB version of ReactCurrentDispatcher in www:
209
- 'react/src/ReactCurrentDispatcher': (bundleType, entry) => {
231
+ './packages/react/src/ReactCurrentDispatcher.js': (bundleType, entry) => {
232
switch (bundleType) {
233
case FB_WWW_DEV:
234
case FB_WWW_PROD:
235
case FB_WWW_PROFILING:
214
- return 'react/src/forks/ReactCurrentDispatcher.www.js';
236
+ return './packages/react/src/forks/ReactCurrentDispatcher.www.js';
237
default:
238
return null;
239
}
240
},
241
220
- 'react/src/ReactSharedInternals.js': (bundleType, entry) => {
242
+ './packages/react/src/ReactSharedInternals.js': (bundleType, entry) => {
243
switch (bundleType) {
244
case UMD_DEV:
245
case UMD_PROD:
246
case UMD_PROFILING:
225
- return 'react/src/forks/ReactSharedInternals.umd.js';
247
+ return './packages/react/src/forks/ReactSharedInternals.umd.js';
248
default:
249
return null;
250
}
251
},
252
253
// Different wrapping/reporting for caught errors.
232
- 'shared/invokeGuardedCallbackImpl': (bundleType, entry) => {
254
+ './packages/shared/invokeGuardedCallbackImpl.js': (bundleType, entry) => {
255
switch (bundleType) {
256
case FB_WWW_DEV:
257
case FB_WWW_PROD:
258
case FB_WWW_PROFILING:
237
- return 'shared/forks/invokeGuardedCallbackImpl.www.js';
259
+ return './packages/shared/forks/invokeGuardedCallbackImpl.www.js';
260
default:
261
return null;
262
}
263
},
264
243
- 'react-reconciler/src/ReactFiberReconciler': (
265
+ './packages/react-reconciler/src/ReactFiberReconciler.js': (
266
bundleType,
267
entry,
268
dependencies,
@@ -253,14 +275,14 @@ const forks = Object.freeze({
275
case FB_WWW_PROD:
276
case FB_WWW_PROFILING:
277
// Use the forked version of the reconciler
256
- return 'react-reconciler/src/ReactFiberReconciler.new.js';
278
+ return './packages/react-reconciler/src/ReactFiberReconciler.new.js';
279
}
280
}
281
// Otherwise, use the non-forked version.
260
- return 'react-reconciler/src/ReactFiberReconciler.old.js';
282
+ return './packages/react-reconciler/src/ReactFiberReconciler.old.js';
283
},
284
263
- 'react-reconciler/src/ReactEventPriorities': (
285
+ './packages/react-reconciler/src/ReactEventPriorities.js': (
286
bundleType,
287
entry,
288
dependencies,
@@ -273,14 +295,14 @@ const forks = Object.freeze({
295
case FB_WWW_PROD:
296
case FB_WWW_PROFILING:
297
// Use the forked version of the reconciler
276
- return 'react-reconciler/src/ReactEventPriorities.new.js';
298
+ return './packages/react-reconciler/src/ReactEventPriorities.new.js';
299
}
300
}
301
// Otherwise, use the non-forked version.
280
- return 'react-reconciler/src/ReactEventPriorities.old.js';
302
+ return './packages/react-reconciler/src/ReactEventPriorities.old.js';
303
},
304
283
- 'react-reconciler/src/ReactFiberHotReloading': (
305
+ './packages/react-reconciler/src/ReactFiberHotReloading.js': (
306
bundleType,
307
entry,
308
dependencies,
@@ -293,21 +315,24 @@ const forks = Object.freeze({
315
case FB_WWW_PROD:
316
case FB_WWW_PROFILING:
317
// Use the forked version of the reconciler
296
- return 'react-reconciler/src/ReactFiberHotReloading.new.js';
318
+ return './packages/react-reconciler/src/ReactFiberHotReloading.new.js';
319
}
320
}
321
// Otherwise, use the non-forked version.
300
- return 'react-reconciler/src/ReactFiberHotReloading.old.js';
322
+ return './packages/react-reconciler/src/ReactFiberHotReloading.old.js';
323
},
324
325
// Different dialogs for caught errors.
304
- 'react-reconciler/src/ReactFiberErrorDialog': (bundleType, entry) => {
326
+ './packages/react-reconciler/src/ReactFiberErrorDialog.js': (
327
+ bundleType,
328
+ entry
329
+ ) => {
330
switch (bundleType) {
331
case FB_WWW_DEV:
332
case FB_WWW_PROD:
333
case FB_WWW_PROFILING:
334
// Use the www fork which shows an error dialog.
310
- return 'react-reconciler/src/forks/ReactFiberErrorDialog.www.js';
335
+ return './packages/react-reconciler/src/forks/ReactFiberErrorDialog.www.js';
336
case RN_OSS_DEV:
337
case RN_OSS_PROD:
338
case RN_OSS_PROFILING:
@@ -318,7 +343,7 @@ const forks = Object.freeze({
343
case 'react-native-renderer':
344
case 'react-native-renderer/fabric':
345
// Use the RN fork which plays well with redbox.
321
- return 'react-reconciler/src/forks/ReactFiberErrorDialog.native.js';
346
+ return './packages/react-reconciler/src/forks/ReactFiberErrorDialog.native.js';
347
default:
348
return null;
349
}
@@ -327,7 +352,7 @@ const forks = Object.freeze({
352
}
353
},
354
330
- 'react-reconciler/src/ReactFiberHostConfig': (
355
+ './packages/react-reconciler/src/ReactFiberHostConfig.js': (
356
bundleType,
357
entry,
358
dependencies,
@@ -342,7 +367,7 @@ const forks = Object.freeze({
367
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
368
for (let rendererInfo of inlinedHostConfigs) {
369
if (rendererInfo.entryPoints.indexOf(entry) !== -1) {
345
- return `react-reconciler/src/forks/ReactFiberHostConfig.${rendererInfo.shortName}.js`;
370
+ return `./packages/react-reconciler/src/forks/ReactFiberHostConfig.${rendererInfo.shortName}.js`;
371
}
372
}
373
throw new Error(
@@ -352,7 +377,7 @@ const forks = Object.freeze({
377
);
378
},
379
355
- 'react-server/src/ReactServerStreamConfig': (
380
+ './packages/react-server/src/ReactServerStreamConfig.js': (
381
bundleType,
382
entry,
383
dependencies,
@@ -370,7 +395,7 @@ const forks = Object.freeze({
395
if (!rendererInfo.isServerSupported) {
396
return null;
397
}
373
- return `react-server/src/forks/ReactServerStreamConfig.${rendererInfo.shortName}.js`;
398
+ return `./packages/react-server/src/forks/ReactServerStreamConfig.${rendererInfo.shortName}.js`;
399
}
400
}
401
throw new Error(
@@ -380,7 +405,7 @@ const forks = Object.freeze({
405
);
406
},
407
383
- 'react-server/src/ReactServerFormatConfig': (
408
+ './packages/react-server/src/ReactServerFormatConfig.js': (
409
bundleType,
410
entry,
411
dependencies,
@@ -398,7 +423,7 @@ const forks = Object.freeze({
423
if (!rendererInfo.isServerSupported) {
424
return null;
425
}
401
- return `react-server/src/forks/ReactServerFormatConfig.${rendererInfo.shortName}.js`;
426
+ return `./packages/react-server/src/forks/ReactServerFormatConfig.${rendererInfo.shortName}.js`;
427
}
428
}
429
throw new Error(
@@ -408,7 +433,7 @@ const forks = Object.freeze({
433
);
434
},
435
411
- 'react-server/src/ReactFlightServerConfig': (
436
+ './packages/react-server/src/ReactFlightServerConfig.js': (
437
bundleType,
438
entry,
439
dependencies,
@@ -426,7 +451,7 @@ const forks = Object.freeze({
451
if (!rendererInfo.isServerSupported) {
452
return null;
453
}
429
- return `react-server/src/forks/ReactFlightServerConfig.${rendererInfo.shortName}.js`;
454
+ return `./packages/react-server/src/forks/ReactFlightServerConfig.${rendererInfo.shortName}.js`;
455
}
456
}
457
throw new Error(
@@ -436,7 +461,7 @@ const forks = Object.freeze({
461
);
462
},
463
439
- 'react-client/src/ReactFlightClientHostConfig': (
464
+ './packages/react-client/src/ReactFlightClientHostConfig.js': (
465
bundleType,
466
entry,
467
dependencies,
@@ -454,7 +479,7 @@ const forks = Object.freeze({
479
if (!rendererInfo.isServerSupported) {
480
return null;
481
}
457
- return `react-client/src/forks/ReactFlightClientHostConfig.${rendererInfo.shortName}.js`;
482
+ return `./packages/react-client/src/forks/ReactFlightClientHostConfig.${rendererInfo.shortName}.js`;
483
}
484
}
485
throw new Error(
@@ -465,7 +490,7 @@ const forks = Object.freeze({
490
},
491
492
// We wrap top-level listeners into guards on www.
468
- 'react-dom/src/events/EventListener': (bundleType, entry) => {
493
+ './packages/react-dom/src/events/EventListener.js': (bundleType, entry) => {
494
switch (bundleType) {
495
case FB_WWW_DEV:
496
case FB_WWW_PROD:
@@ -475,28 +500,34 @@ const forks = Object.freeze({
500
return null;
501
} else {
502
// Use the www fork which is integrated with TimeSlice profiling.
478
- return 'react-dom/src/events/forks/EventListener-www.js';
503
+ return './packages/react-dom/src/events/forks/EventListener-www.js';
504
}
505
default:
506
return null;
507
}
508
},
509
485
- 'use-sync-external-store/src/useSyncExternalStore': (bundleType, entry) => {
510
+ './packages/use-sync-external-store/src/useSyncExternalStore.js': (
511
+ bundleType,
512
+ entry
513
+ ) => {
514
if (entry.startsWith('use-sync-external-store/shim')) {
487
- return 'use-sync-external-store/src/forks/useSyncExternalStore.forward-to-shim';
515
+ return './packages/use-sync-external-store/src/forks/useSyncExternalStore.forward-to-shim.js';
516
}
517
if (entry !== 'use-sync-external-store') {
518
// Internal modules that aren't shims should use the native API from the
519
// react package.
492
- return 'use-sync-external-store/src/forks/useSyncExternalStore.forward-to-built-in';
520
+ return './packages/use-sync-external-store/src/forks/useSyncExternalStore.forward-to-built-in.js';
521
}
522
return null;
523
},
524
497
- 'use-sync-external-store/src/isServerEnvironment': (bundleType, entry) => {
525
+ './packages/use-sync-external-store/src/isServerEnvironment.js': (
526
+ bundleType,
527
+ entry
528
+ ) => {
529
if (entry.endsWith('.native')) {
499
- return 'use-sync-external-store/src/forks/isServerEnvironment.native';
530
+ return './packages/use-sync-external-store/src/forks/isServerEnvironment.native.js';
531
}
532
},
533
});
scripts/rollup/plugins/use-forks-plugin.js
+6
-2
@@ -30,15 +30,19 @@ let resolveCache = new Map();
30
function useForks(forks) {
31
let resolvedForks = new Map();
32
Object.keys(forks).forEach(srcModule => {
33
+ // Fork paths are relative to the project root. They must include the full
34
+ // path, including the extension. We intentionally don't use Node's module
35
+ // resolution algorithm because 1) require.resolve doesn't work with ESM
36
+ // modules, and 2) the behavior is easier to predict.
37
const targetModule = forks[srcModule];
38
resolvedForks.set(
35
- require.resolve(srcModule),
39
+ path.resolve(process.cwd(), srcModule),
40
// targetModule could be a string (a file path),
41
// or an error (which we'd throw if it gets used).
42
// Don't try to "resolve" errors, but cache
43
// resolved file paths.
44
typeof targetModule === 'string'
41
- ? require.resolve(targetModule)
45
+ ? path.resolve(process.cwd(), targetModule)
46
: targetModule
47
);
48
});