Minor refactors in create-react-app (#8178)

- Remove templates version minimum stopgap.
- Replace indexOf with more idiomatic alternatives.
- Inline errorLogFilePatterns.
- Alphabetize validFiles.
- Simplify log file removal code.
- Move unconditional console.log() call outside of isSafe.
- Differentiate conflicting directories from files.
- Document yarn version special case.
- Inline printValidationResults.
- Standardize checkAppName output on failure.
- Add link for checkThatNpmCanReadCwd.

Functionally the code should be exactly the same.
This commit is contained in:
Alex Guerra
2019-12-16 14:56:37 -06:00
committed by GitHub
parent 1a13b594ae
commit 30eaab4ef2

View File

@@ -53,14 +53,6 @@ const validateProjectName = require('validate-npm-package-name');
const packageJson = require('./package.json');
// These files should be allowed to remain on a failed install,
// but then silently removed during the next create.
const errorLogFilePatterns = [
'npm-debug.log',
'yarn-error.log',
'yarn-debug.log',
];
let projectName;
const program = new commander.Command(packageJson.name)
@@ -192,14 +184,6 @@ if (typeof projectName === 'undefined') {
process.exit(1);
}
function printValidationResults(results) {
if (typeof results !== 'undefined') {
results.forEach(error => {
console.error(chalk.red(` * ${error}`));
});
}
}
createApp(
projectName,
program.verbose,
@@ -247,6 +231,7 @@ function createApp(
if (!isSafeToCreateProjectIn(root, name)) {
process.exit(1);
}
console.log();
console.log(`Creating a new React app in ${chalk.green(root)}.`);
console.log();
@@ -448,10 +433,7 @@ function run(
.then(({ isOnline, packageInfo, templateInfo }) => {
let packageVersion = semver.coerce(packageInfo.version);
// This environment variable can be removed post-release.
const templatesVersionMinimum = process.env.CRA_INTERNAL_TEST
? '3.2.0'
: '3.3.0';
const templatesVersionMinimum = '3.3.0';
// Assume compatibility if we can't test the version.
if (!semver.valid(packageVersion)) {
@@ -591,7 +573,7 @@ function getInstallPackage(version, originalDirectory) {
if (validSemver) {
packageToInstall += `@${validSemver}`;
} else if (version) {
if (version[0] === '@' && version.indexOf('/') === -1) {
if (version[0] === '@' && !version.includes('/')) {
packageToInstall += version;
} else if (version.match(/^file:/)) {
packageToInstall = `file:${path.resolve(
@@ -743,7 +725,7 @@ function getPackageInfo(installPackage) {
);
return Promise.resolve({ name: assumedProjectName });
});
} else if (installPackage.indexOf('git+') === 0) {
} else if (installPackage.startsWith('git+')) {
// Pull package name out of git urls e.g:
// git+https://github.com/mycompany/react-scripts.git
// git+ssh://github.com/mycompany/react-scripts.git#v1.2.3
@@ -785,17 +767,25 @@ function checkNpmVersion() {
}
function checkYarnVersion() {
const minYarnPnp = '1.12.0';
let hasMinYarnPnp = false;
let yarnVersion = null;
try {
yarnVersion = execSync('yarnpkg --version')
.toString()
.trim();
let trimmedYarnVersion = /^(.+?)[-+].+$/.exec(yarnVersion);
if (trimmedYarnVersion) {
trimmedYarnVersion = trimmedYarnVersion.pop();
if (semver.valid(yarnVersion)) {
hasMinYarnPnp = semver.gte(yarnVersion, minYarnPnp);
} else {
// Handle non-semver compliant yarn version strings, which yarn currently
// uses for nightly builds. The regex truncates anything after the first
// dash. See #5362.
const trimmedYarnVersionMatch = /^(.+?)[-+].+$/.exec(yarnVersion);
if (trimmedYarnVersionMatch) {
const trimmedYarnVersion = trimmedYarnVersionMatch.pop();
hasMinYarnPnp = semver.gte(trimmedYarnVersion, minYarnPnp);
}
}
hasMinYarnPnp = semver.gte(trimmedYarnVersion || yarnVersion, '1.12.0');
} catch (err) {
// ignore
}
@@ -840,22 +830,29 @@ function checkAppName(appName) {
const validationResult = validateProjectName(appName);
if (!validationResult.validForNewPackages) {
console.error(
`Could not create a project called ${chalk.red(
`"${appName}"`
)} because of npm naming restrictions:`
chalk.red(
`Cannot create a project named ${chalk.green(
`"${appName}"`
)} because of npm naming restrictions:\n`
)
);
printValidationResults(validationResult.errors);
printValidationResults(validationResult.warnings);
[
...(validationResult.errors || []),
...(validationResult.warnings || []),
].forEach(error => {
console.error(chalk.red(` * ${error}`));
});
console.error(chalk.red('\nPlease choose a different project name.'));
process.exit(1);
}
// TODO: there should be a single place that holds the dependencies
const dependencies = ['react', 'react-dom', 'react-scripts'].sort();
if (dependencies.indexOf(appName) >= 0) {
if (dependencies.includes(appName)) {
console.error(
chalk.red(
`We cannot create a project called ${chalk.green(
appName
`Cannot create a project named ${chalk.green(
`"${appName}"`
)} because a dependency with the same name exists.\n` +
`Due to the way npm works, the following names are not allowed:\n\n`
) +
@@ -917,23 +914,32 @@ function setCaretRangeForRuntimeDeps(packageName) {
function isSafeToCreateProjectIn(root, name) {
const validFiles = [
'.DS_Store',
'Thumbs.db',
'.git',
'.gitignore',
'.idea',
'README.md',
'LICENSE',
'.hg',
'.hgignore',
'.hgcheck',
'.npmignore',
'mkdocs.yml',
'docs',
'.travis.yml',
'.gitlab-ci.yml',
'.gitattributes',
'.gitignore',
'.gitlab-ci.yml',
'.hg',
'.hgcheck',
'.hgignore',
'.idea',
'.npmignore',
'.travis.yml',
'docs',
'LICENSE',
'README.md',
'mkdocs.yml',
'Thumbs.db',
];
console.log();
// These files should be allowed to remain on a failed install, but then
// silently removed during the next create.
const errorLogFilePatterns = [
'npm-debug.log',
'yarn-error.log',
'yarn-debug.log',
];
const isErrorLog = file => {
return errorLogFilePatterns.some(pattern => file.startsWith(pattern));
};
const conflicts = fs
.readdirSync(root)
@@ -941,9 +947,7 @@ function isSafeToCreateProjectIn(root, name) {
// IntelliJ IDEA creates module files before CRA is launched
.filter(file => !/\.iml$/.test(file))
// Don't treat log files from previous installation as conflicts
.filter(
file => !errorLogFilePatterns.some(pattern => file.indexOf(pattern) === 0)
);
.filter(file => !isErrorLog(file));
if (conflicts.length > 0) {
console.log(
@@ -951,7 +955,16 @@ function isSafeToCreateProjectIn(root, name) {
);
console.log();
for (const file of conflicts) {
console.log(` ${file}`);
try {
const stats = fs.lstatSync(path.join(root, file));
if (stats.isDirectory()) {
console.log(` ${chalk.blue(`${file}/`)}`);
} else {
console.log(` ${file}`);
}
} catch (e) {
console.log(` ${file}`);
}
}
console.log();
console.log(
@@ -961,15 +974,11 @@ function isSafeToCreateProjectIn(root, name) {
return false;
}
// Remove any remnant files from a previous installation
const currentFiles = fs.readdirSync(path.join(root));
currentFiles.forEach(file => {
errorLogFilePatterns.forEach(errorLogFilePattern => {
// This will catch `(npm-debug|yarn-error|yarn-debug).log*` files
if (file.indexOf(errorLogFilePattern) === 0) {
fs.removeSync(path.join(root, file));
}
});
// Remove any log files from a previous installation.
fs.readdirSync(root).forEach(file => {
if (isErrorLog(file)) {
fs.removeSync(path.join(root, file));
}
});
return true;
}
@@ -989,6 +998,8 @@ function getProxy() {
}
}
}
// See https://github.com/facebook/create-react-app/pull/3355
function checkThatNpmCanReadCwd() {
const cwd = process.cwd();
let childOutput = null;
@@ -1013,7 +1024,7 @@ function checkThatNpmCanReadCwd() {
// "; cwd = C:\path\to\current\dir" (unquoted)
// I couldn't find an easier way to get it.
const prefix = '; cwd = ';
const line = lines.find(line => line.indexOf(prefix) === 0);
const line = lines.find(line => line.startsWith(prefix));
if (typeof line !== 'string') {
// Fail gracefully. They could remove it.
return true;