Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Lerna monorepos #6599

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions packages/react-scripts/config/lerna.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// @remove-on-eject-begin
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// @remove-on-eject-end
'use strict';

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

const globbySync = require('globby').sync;
const loadJsonFileSync = require('load-json-file').sync;
const ValidationError = require('@lerna/validation-error');
const Package = require('@lerna/package');
const PackageGraph = require('@lerna/package-graph');
const Project = require('@lerna/project');

function flattenResults(results) {
return results.reduce((acc, result) => acc.concat(result), []);
}

// Sync version of Lerna's makeFileFinder
// Heavily inspired by https://github.com/lerna/lerna/blob/62843b04e3a5a03012ceabe465519b39a09fbcc1/core/project/lib/make-file-finder.js
function makeFileFinderSync(rootPath, packageConfigs) {
const globOpts = {
cwd: rootPath,
absolute: true,
followSymlinkedDirectories: false,
// POSIX results always need to be normalized
transform: fp => path.normalize(fp),
};

if (packageConfigs.some(cfg => cfg.indexOf('**') > -1)) {
if (packageConfigs.some(cfg => cfg.indexOf('node_modules') > -1)) {
throw new ValidationError(
'EPKGCONFIG',
'An explicit node_modules package path does not allow globstars (**)'
);
}

globOpts.ignore = [
// allow globs like "packages/**",
// but avoid picking up node_modules/**/package.json
'**/node_modules/**',
];
}

return (fileName, fileMapper, customGlobOpts) => {
const options = Object.assign({}, customGlobOpts, globOpts);
const packages = packageConfigs.sort().map(globPath => {
const results = globbySync(path.join(globPath, fileName), options).sort();

if (fileMapper) {
return fileMapper(results);
}

return results;
});

// always flatten the results
return flattenResults(packages);
};
}

function getPackagesSync(project) {
const mapper = packageConfigPath => {
const packageJson = loadJsonFileSync(packageConfigPath);
return new Package(
packageJson,
path.dirname(packageConfigPath),
project.rootPath
);
};

const finder = makeFileFinderSync(project.rootPath, project.packageConfigs);

return finder('package.json', filePaths => filePaths.map(mapper));
}

module.exports.getAllLocalDependencies = function getAllLocalDependencies(
appName
) {
const project = new Project(process.cwd());
const packages = getPackagesSync(project);
const packageGraph = new PackageGraph(
packages,
'allDependencies',
'forceLocal'
);
const currentNode = packageGraph.get(appName);
if (!currentNode) {
return undefined;
}

const dependencies = new Set(currentNode.localDependencies.keys());
const dependenciesToExplore = new Set(dependencies);
dependenciesToExplore.delete(appName);

while (dependenciesToExplore.size > 0) {
const packageName = dependenciesToExplore.values().next().value;
dependenciesToExplore.delete(packageName);
const node = packageGraph.get(packageName);
const newDependencies = new Set(node.localDependencies.keys());
newDependencies.delete(appName);

for (const d of newDependencies.values()) {
if (!dependencies.has(d)) {
dependenciesToExplore.add(d);
dependencies.add(d);
}
}
}

const additionalSrcPaths = Array.from(dependencies).map(dependencyName => {
const resolvedPath = fs.realpathSync(
packageGraph.get(dependencyName).location
);
return path.resolve(resolvedPath, 'src');
});
return additionalSrcPaths;
};
17 changes: 14 additions & 3 deletions packages/react-scripts/config/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,23 @@
const path = require('path');
const fs = require('fs');
const url = require('url');
const { getAllLocalDependencies } = require('./lerna');

// Make sure any symlinks in the project folder are resolved:
// https://github.com/facebook/create-react-app/issues/637
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);

const appName = require(resolveApp('package.json')).name;

const getAppSrc = appSrc => {
const localDependencies = getAllLocalDependencies(appName);
return {
appSrc,
fullAppSrcs: localDependencies ? localDependencies.concat(appSrc) : appSrc,
};
};

const envPublicUrl = process.env.PUBLIC_URL;

function ensureSlash(inputPath, needsSlash) {
Expand Down Expand Up @@ -82,7 +93,7 @@ module.exports = {
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
...getAppSrc(resolveApp('src')),
appTsConfig: resolveApp('tsconfig.json'),
appJsConfig: resolveApp('jsconfig.json'),
yarnLockFile: resolveApp('yarn.lock'),
Expand All @@ -105,7 +116,7 @@ module.exports = {
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
...getAppSrc(resolveApp('src')),
appTsConfig: resolveApp('tsconfig.json'),
appJsConfig: resolveApp('jsconfig.json'),
yarnLockFile: resolveApp('yarn.lock'),
Expand Down Expand Up @@ -140,7 +151,7 @@ if (
appHtml: resolveOwn('template/public/index.html'),
appIndexJs: resolveModule(resolveOwn, 'template/src/index'),
appPackageJson: resolveOwn('package.json'),
appSrc: resolveOwn('template/src'),
...getAppSrc(resolveOwn('template/src')),
appTsConfig: resolveOwn('template/tsconfig.json'),
appJsConfig: resolveOwn('template/jsconfig.json'),
yarnLockFile: resolveOwn('template/yarn.lock'),
Expand Down
6 changes: 3 additions & 3 deletions packages/react-scripts/config/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ module.exports = function(webpackEnv) {
// 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, [paths.appPackageJson]),
new ModuleScopePlugin(paths.fullAppSrcs, [paths.appPackageJson]),
],
},
resolveLoader: {
Expand Down Expand Up @@ -383,7 +383,7 @@ module.exports = function(webpackEnv) {
loader: require.resolve('eslint-loader'),
},
],
include: paths.appSrc,
include: paths.fullAppSrcs,
},
{
// "oneOf" will traverse all following loaders until one will
Expand All @@ -405,7 +405,7 @@ module.exports = function(webpackEnv) {
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
include: paths.fullAppSrcs,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
Expand Down
5 changes: 5 additions & 0 deletions packages/react-scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
"types": "./lib/react-app.d.ts",
"dependencies": {
"@babel/core": "7.6.0",
"@lerna/package": "^3.16.0",
"@lerna/project": "^3.16.0",
"@lerna/package-graph": "^3.16.0",
"@lerna/validation-error": "^3.13.0",
"@svgr/webpack": "4.3.2",
"@typescript-eslint/eslint-plugin": "^2.2.0",
"@typescript-eslint/parser": "^2.2.0",
Expand All @@ -52,6 +56,7 @@
"eslint-plugin-react-hooks": "^1.6.1",
"file-loader": "3.0.1",
"fs-extra": "7.0.1",
"globby": "^9.2.0",
"html-webpack-plugin": "4.0.0-beta.5",
"identity-obj-proxy": "3.0.0",
"jest": "24.9.0",
Expand Down