diff --git a/packages/react-scripts/config/getTopLevelModules.js b/packages/react-scripts/config/getTopLevelModules.js new file mode 100644 index 00000000000..13818f196c6 --- /dev/null +++ b/packages/react-scripts/config/getTopLevelModules.js @@ -0,0 +1,50 @@ +'use strict'; + +const path = require('path'); +const paths = require('./paths'); + +const mapDependenciesToFolder = (dependencies, folder) => + dependencies.reduce( + (accumulator, dependency) => + Object.assign( + {}, + { + [dependency]: path.resolve(folder, dependency), + }, + accumulator + ), + {} + ); + +const topLevelModuleNames = () => { + const config = process.env.TOP_LEVEL_MODULES; + if (config) { + if (config.toLowerCase() === 'true') { + // return defaults + return ['react', 'react-dom']; + } + + let json; + try { + json = JSON.parse(config); + } catch (e) { + // continue regardless of error + } + + if (Array.isArray(json)) { + return json; + } else { + return config; + } + } +}; + +const getTopLevelModules = () => { + const modulesNames = topLevelModuleNames(); + if (modulesNames) { + mapDependenciesToFolder(modulesNames, paths.appNodeModules); + } + return {}; +}; + +module.exports = getTopLevelModules; diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index a6be022ba25..dc1e3c61880 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -19,6 +19,7 @@ const eslintFormatter = require('react-dev-utils/eslintFormatter'); const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); const getClientEnvironment = require('./env'); const paths = require('./paths'); +const getTopLevelModules = require('./getTopLevelModules'); // Webpack uses `publicPath` to determine where the app is being served from. // In development, we always serve from the root. This makes config easier. @@ -91,20 +92,23 @@ module.exports = { // `web` extension prefixes have been added for better support // for React Native Web. extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'], - alias: { - // @remove-on-eject-begin - // Resolve Babel runtime relative to react-scripts. - // It usually still works on npm 3 without this but it would be - // unfortunate to rely on, as react-scripts could be symlinked, - // and thus babel-runtime might not be resolvable from the source. - 'babel-runtime': path.dirname( - require.resolve('babel-runtime/package.json') - ), - // @remove-on-eject-end - // Support React Native Web - // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ - 'react-native': 'react-native-web', - }, + alias: Object.assign( + { + // @remove-on-eject-begin + // Resolve Babel runtime relative to react-scripts. + // It usually still works on npm 3 without this but it would be + // unfortunate to rely on, as react-scripts could be symlinked, + // and thus babel-runtime might not be resolvable from the source. + 'babel-runtime': path.dirname( + require.resolve('babel-runtime/package.json') + ), + // @remove-on-eject-end + // Support React Native Web + // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ + 'react-native': 'react-native-web', + }, + getTopLevelModules() + ), 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. diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 457a9672892..983fd285dbe 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -20,6 +20,7 @@ const eslintFormatter = require('react-dev-utils/eslintFormatter'); const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); const paths = require('./paths'); const getClientEnvironment = require('./env'); +const getTopLevelModules = require('./getTopLevelModules'); // Webpack uses `publicPath` to determine where the app is being served from. // It requires a trailing slash, or the file assets will get an incorrect path. @@ -97,20 +98,23 @@ module.exports = { // `web` extension prefixes have been added for better support // for React Native Web. extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'], - alias: { - // @remove-on-eject-begin - // Resolve Babel runtime relative to react-scripts. - // It usually still works on npm 3 without this but it would be - // unfortunate to rely on, as react-scripts could be symlinked, - // and thus babel-runtime might not be resolvable from the source. - 'babel-runtime': path.dirname( - require.resolve('babel-runtime/package.json') - ), - // @remove-on-eject-end - // Support React Native Web - // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ - 'react-native': 'react-native-web', - }, + alias: Object.assign( + { + // @remove-on-eject-begin + // Resolve Babel runtime relative to react-scripts. + // It usually still works on npm 3 without this but it would be + // unfortunate to rely on, as react-scripts could be symlinked, + // and thus babel-runtime might not be resolvable from the source. + 'babel-runtime': path.dirname( + require.resolve('babel-runtime/package.json') + ), + // @remove-on-eject-end + // Support React Native Web + // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ + 'react-native': 'react-native-web', + }, + getTopLevelModules() + ), 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.