Skip to content

Commit 565148d

Browse files
authoredNov 30, 2020
Disallow *.server.js imports from any other files (#20309)
This convention ensures that you can declare that you intend for a file to only be used on the server (even if it technically might resolve on the client).
1 parent e6a0f27 commit 565148d

File tree

5 files changed

+56
-3
lines changed

5 files changed

+56
-3
lines changed
 

‎fixtures/flight/server/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2-
"type": "commonjs"
2+
"type": "commonjs",
3+
"main": "./cli.server.js"
34
}

‎packages/react-transport-dom-webpack/src/ReactFlightWebpackNodeLoader.js

+28-1
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,39 @@ type GetSourceFunction = (
3131

3232
type Source = string | ArrayBuffer | Uint8Array;
3333

34+
let warnedAboutConditionsFlag = false;
35+
3436
export async function resolve(
3537
specifier: string,
3638
context: ResolveContext,
3739
defaultResolve: ResolveFunction,
3840
): Promise<string> {
39-
// TODO: Resolve server-only files.
41+
if (!context.conditions.includes('react-server')) {
42+
context = {
43+
...context,
44+
conditions: [...context.conditions, 'react-server'],
45+
};
46+
if (!warnedAboutConditionsFlag) {
47+
warnedAboutConditionsFlag = true;
48+
// eslint-disable-next-line react-internal/no-production-logging
49+
console.warn(
50+
'You did not run Node.js with the `--conditions react-server` flag. ' +
51+
'Any "react-server" override will only work with ESM imports.',
52+
);
53+
}
54+
}
55+
// We intentionally check the specifier here instead of the resolved file.
56+
// This allows package exports to configure non-server aliases that resolve to server files
57+
// depending on environment. It's probably a bad idea to export a server file as "main" though.
58+
if (specifier.endsWith('.server.js')) {
59+
if (context.parentURL && !context.parentURL.endsWith('.server.js')) {
60+
throw new Error(
61+
`Cannot import "${specifier}" from "${context.parentURL}". ` +
62+
'By react-server convention, .server.js files can only be imported from other .server.js files. ' +
63+
'That way nobody accidentally sends these to the client by indirectly importing it.',
64+
);
65+
}
66+
}
4067
return defaultResolve(specifier, context, defaultResolve);
4168
}
4269

‎packages/react-transport-dom-webpack/src/ReactFlightWebpackNodeRegister.js

+25
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,36 @@
99

1010
const url = require('url');
1111

12+
// $FlowFixMe
13+
const Module = require('module');
14+
1215
module.exports = function register() {
1316
(require: any).extensions['.client.js'] = function(module, path) {
1417
module.exports = {
1518
$$typeof: Symbol.for('react.module.reference'),
1619
name: url.pathToFileURL(path).href,
1720
};
1821
};
22+
23+
const originalResolveFilename = Module._resolveFilename;
24+
25+
Module._resolveFilename = function(request, parent, isMain, options) {
26+
// We intentionally check the request here instead of the resolved file.
27+
// This allows package exports to configure non-server aliases that resolve to server files
28+
// depending on environment. It's probably a bad idea to export a server file as "main" though.
29+
if (request.endsWith('.server.js')) {
30+
if (
31+
parent &&
32+
parent.filename &&
33+
!parent.filename.endsWith('.server.js')
34+
) {
35+
throw new Error(
36+
`Cannot import "${request}" from "${parent.filename}". ` +
37+
'By react-server convention, .server.js files can only be imported from other .server.js files. ' +
38+
'That way nobody accidentally sends these to the client by indirectly importing it.',
39+
);
40+
}
41+
}
42+
return originalResolveFilename.apply(this, arguments);
43+
};
1944
};

‎scripts/rollup/bundles.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ const bundles = [
310310
moduleType: RENDERER_UTILS,
311311
entry: 'react-transport-dom-webpack/node-register',
312312
global: 'ReactFlightWebpackNodeRegister',
313-
externals: ['url'],
313+
externals: ['url', 'module'],
314314
},
315315

316316
/******* React Transport DOM Server Relay *******/

0 commit comments

Comments
 (0)