Skip to content

Commit a61be9c

Browse files
Timergaearon
authored andcommitted
Add module scope plugin (#2189)
* Add module scope plugin * Oops * Add comments * Check windows seps too * More descriptive error * Document it
1 parent e1f22b5 commit a61be9c

File tree

5 files changed

+107
-0
lines changed

5 files changed

+107
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
'use strict';
11+
12+
const chalk = require('chalk');
13+
const path = require('path');
14+
15+
class ModuleScopePlugin {
16+
constructor(appSrc) {
17+
this.appSrc = appSrc;
18+
}
19+
20+
apply(resolver) {
21+
const { appSrc } = this;
22+
resolver.plugin('file', (request, callback) => {
23+
// Unknown issuer, probably webpack internals
24+
if (!request.context.issuer) {
25+
return callback();
26+
}
27+
if (
28+
// If this resolves to a node_module, we don't care what happens next
29+
request.descriptionFileRoot.indexOf('/node_modules/') !== -1 ||
30+
request.descriptionFileRoot.indexOf('\\node_modules\\') !== -1 ||
31+
// Make sure this request was manual
32+
!request.__innerRequest_request
33+
) {
34+
return callback();
35+
}
36+
// Resolve the issuer from our appSrc and make sure it's one of our files
37+
// Maybe an indexOf === 0 would be better?
38+
const relative = path.relative(appSrc, request.context.issuer);
39+
// If it's not in src/ or a subdirectory, not our request!
40+
if (relative[0] === '.') {
41+
return callback();
42+
}
43+
// Find path from src to the requested file
44+
const requestRelative = path.relative(
45+
appSrc,
46+
path.resolve(
47+
path.dirname(request.context.issuer),
48+
request.__innerRequest_request
49+
)
50+
);
51+
// Error if in a parent directory of src/
52+
if (requestRelative[0] === '.') {
53+
callback(
54+
new Error(
55+
`You attempted to import ${chalk.cyan(request.__innerRequest_request)} which falls outside of the project ${chalk.cyan('src/')} directory. ` +
56+
`Relative imports outside of ${chalk.cyan('src/')} are not supported. ` +
57+
`You can either move it inside ${chalk.cyan('src/')}, or add a symlink to it from project's ${chalk.cyan('node_modules/')}.`
58+
),
59+
request
60+
);
61+
} else {
62+
callback();
63+
}
64+
});
65+
}
66+
}
67+
68+
module.exports = ModuleScopePlugin;

packages/react-dev-utils/README.md

+20
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,26 @@ module.exports = {
5656
}
5757
```
5858

59+
60+
#### `new ModuleScopePlugin(appSrc: string)`
61+
62+
This Webpack plugin ensures that relative imports from app's source directory don't reach outside of it.
63+
64+
```js
65+
var path = require('path');
66+
var ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
67+
68+
69+
module.exports = {
70+
// ...
71+
plugins: [
72+
new ModuleScopePlugin(paths.appSrc),
73+
// ...
74+
],
75+
// ...
76+
}
77+
```
78+
5979
#### `new WatchMissingNodeModulesPlugin(nodeModulesPath: string)`
6080

6181
This Webpack plugin ensures `npm install <library>` forces a project rebuild.<br>

packages/react-dev-utils/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"getProcessForPort.js",
2222
"InterpolateHtmlPlugin.js",
2323
"launchEditor.js",
24+
"ModuleScopePlugin.js",
2425
"openBrowser.js",
2526
"openChrome.applescript",
2627
"prepareProxy.js",

packages/react-scripts/config/webpack.config.dev.js

+9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
1818
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
1919
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
2020
const eslintFormatter = require('react-dev-utils/eslintFormatter');
21+
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
2122
const getClientEnvironment = require('./env');
2223
const paths = require('./paths');
2324

@@ -106,6 +107,14 @@ module.exports = {
106107
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
107108
'react-native': 'react-native-web',
108109
},
110+
plugins: [
111+
// Prevents users from importing files from outside of src/ (or node_modules/).
112+
// This often causes confusion because we only process files within src/ with babel.
113+
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
114+
// please link the files into your node_modules/ and let module-resolution kick in.
115+
// Make sure your source files are compiled, as they will not be processed in any way.
116+
new ModuleScopePlugin(paths.appSrc),
117+
],
109118
},
110119
module: {
111120
strictExportPresence: true,

packages/react-scripts/config/webpack.config.prod.js

+9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin');
1818
const ManifestPlugin = require('webpack-manifest-plugin');
1919
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
2020
const eslintFormatter = require('react-dev-utils/eslintFormatter');
21+
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
2122
const paths = require('./paths');
2223
const getClientEnvironment = require('./env');
2324

@@ -103,6 +104,14 @@ module.exports = {
103104
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
104105
'react-native': 'react-native-web',
105106
},
107+
plugins: [
108+
// Prevents users from importing files from outside of src/ (or node_modules/).
109+
// This often causes confusion because we only process files within src/ with babel.
110+
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
111+
// please link the files into your node_modules/ and let module-resolution kick in.
112+
// Make sure your source files are compiled, as they will not be processed in any way.
113+
new ModuleScopePlugin(paths.appSrc),
114+
],
106115
},
107116
module: {
108117
strictExportPresence: true,

0 commit comments

Comments
 (0)