Skip to content

Commit

Permalink
Add module scope plugin (facebook#2189)
Browse files Browse the repository at this point in the history
* Add module scope plugin

* Oops

* Add comments

* Check windows seps too

* More descriptive error

* Document it
  • Loading branch information
Timer authored and romaindso committed Jul 10, 2017
1 parent d8c0485 commit 48133d8
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 0 deletions.
68 changes: 68 additions & 0 deletions packages/react-dev-utils/ModuleScopePlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

'use strict';

const chalk = require('chalk');
const path = require('path');

class ModuleScopePlugin {
constructor(appSrc) {
this.appSrc = appSrc;
}

apply(resolver) {
const { appSrc } = this;
resolver.plugin('file', (request, callback) => {
// Unknown issuer, probably webpack internals
if (!request.context.issuer) {
return callback();
}
if (
// If this resolves to a node_module, we don't care what happens next
request.descriptionFileRoot.indexOf('/node_modules/') !== -1 ||
request.descriptionFileRoot.indexOf('\\node_modules\\') !== -1 ||
// Make sure this request was manual
!request.__innerRequest_request
) {
return callback();
}
// Resolve the issuer from our appSrc and make sure it's one of our files
// Maybe an indexOf === 0 would be better?
const relative = path.relative(appSrc, request.context.issuer);
// If it's not in src/ or a subdirectory, not our request!
if (relative[0] === '.') {
return callback();
}
// Find path from src to the requested file
const requestRelative = path.relative(
appSrc,
path.resolve(
path.dirname(request.context.issuer),
request.__innerRequest_request
)
);
// Error if in a parent directory of src/
if (requestRelative[0] === '.') {
callback(
new Error(
`You attempted to import ${chalk.cyan(request.__innerRequest_request)} which falls outside of the project ${chalk.cyan('src/')} directory. ` +
`Relative imports outside of ${chalk.cyan('src/')} are not supported. ` +
`You can either move it inside ${chalk.cyan('src/')}, or add a symlink to it from project's ${chalk.cyan('node_modules/')}.`
),
request
);
} else {
callback();
}
});
}
}

module.exports = ModuleScopePlugin;
20 changes: 20 additions & 0 deletions packages/react-dev-utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ module.exports = {
}
```


#### `new ModuleScopePlugin(appSrc: string)`

This Webpack plugin ensures that relative imports from app's source directory don't reach outside of it.

```js
var path = require('path');
var ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');


module.exports = {
// ...
plugins: [
new ModuleScopePlugin(paths.appSrc),
// ...
],
// ...
}
```

#### `new WatchMissingNodeModulesPlugin(nodeModulesPath: string)`

This Webpack plugin ensures `npm install <library>` forces a project rebuild.<br>
Expand Down
1 change: 1 addition & 0 deletions packages/react-dev-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"getProcessForPort.js",
"InterpolateHtmlPlugin.js",
"launchEditor.js",
"ModuleScopePlugin.js",
"openBrowser.js",
"openChrome.applescript",
"prepareProxy.js",
Expand Down
9 changes: 9 additions & 0 deletions packages/react-scripts/config/webpack.config.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getClientEnvironment = require('./env');
const paths = require('./paths');

Expand Down Expand Up @@ -106,6 +107,14 @@ module.exports = {
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
},
plugins: [
// Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel.
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc),
],
},
module: {
strictExportPresence: true,
Expand Down
9 changes: 9 additions & 0 deletions packages/react-scripts/config/webpack.config.prod.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const paths = require('./paths');
const getClientEnvironment = require('./env');

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

0 comments on commit 48133d8

Please sign in to comment.