create-react-app
Advanced tools
Comparing version 1.2.1 to 1.3.0-alpha.58689133
415
index.js
@@ -42,11 +42,15 @@ #!/usr/bin/env node | ||
var chalk = require('chalk'); | ||
var validateProjectName = require("validate-npm-package-name"); | ||
var currentNodeVersion = process.versions.node; | ||
if (currentNodeVersion.split('.')[0] < 4) { | ||
var semver = currentNodeVersion.split('.'); | ||
var major = semver[0]; | ||
if (major < 6) { | ||
console.error( | ||
chalk.red( | ||
'You are running Node ' + currentNodeVersion + '.\n' + | ||
'Create React App requires Node 4 or higher. \n' + | ||
'Please update your version of Node.' | ||
'You are running Node ' + | ||
currentNodeVersion + | ||
'.\n' + | ||
'Create React App requires Node 6 or higher. \n' + | ||
'Please update your version of Node.' | ||
) | ||
@@ -57,401 +61,2 @@ ); | ||
var commander = require('commander'); | ||
var fs = require('fs-extra'); | ||
var path = require('path'); | ||
var execSync = require('child_process').execSync; | ||
var spawn = require('cross-spawn'); | ||
var semver = require('semver'); | ||
var dns = require('dns'); | ||
var projectName; | ||
var program = commander | ||
.version(require('./package.json').version) | ||
.arguments('<project-directory>') | ||
.usage(chalk.green('<project-directory>') + ' [options]') | ||
.action(function (name) { | ||
projectName = name; | ||
}) | ||
.option('--verbose', 'print additional logs') | ||
.option('--scripts-version <alternative-package>', 'use a non-standard version of react-scripts') | ||
.allowUnknownOption() | ||
.on('--help', function () { | ||
console.log(' Only ' + chalk.green('<project-directory>') + ' is required.'); | ||
console.log(); | ||
console.log(' A custom ' + chalk.cyan('--scripts-version') + ' can be one of:'); | ||
console.log(' - a specific npm version: ' + chalk.green('0.8.2')); | ||
console.log(' - a custom fork published on npm: ' + chalk.green('my-react-scripts')); | ||
console.log(' - a .tgz archive: ' + chalk.green('https://mysite.com/my-react-scripts-0.8.2.tgz')); | ||
console.log(' It is not needed unless you specifically want to use a fork.'); | ||
console.log(); | ||
console.log(' If you have any problems, do not hesitate to file an issue:'); | ||
console.log(' ' + chalk.cyan('https://github.com/facebookincubator/create-react-app/issues/new')); | ||
console.log(); | ||
}) | ||
.parse(process.argv); | ||
if (typeof projectName === 'undefined') { | ||
console.error('Please specify the project directory:'); | ||
console.log(' ' + chalk.cyan(program.name()) + chalk.green(' <project-directory>')); | ||
console.log(); | ||
console.log('For example:'); | ||
console.log(' ' + chalk.cyan(program.name()) + chalk.green(' my-react-app')); | ||
console.log(); | ||
console.log('Run ' + chalk.cyan(program.name() + ' --help') + ' to see all options.'); | ||
process.exit(1); | ||
} | ||
function printValidationResults(results) { | ||
if (typeof results !== 'undefined') { | ||
results.forEach(function (error) { | ||
console.error(chalk.red(' * ' + error)); | ||
}); | ||
} | ||
} | ||
var hiddenProgram = new commander.Command() | ||
.option('--internal-testing-template <path-to-template>', '(internal usage only, DO NOT RELY ON THIS) ' + | ||
'use a non-standard application template') | ||
.parse(process.argv) | ||
createApp(projectName, program.verbose, program.scriptsVersion, hiddenProgram.internalTestingTemplate); | ||
function createApp(name, verbose, version, template) { | ||
var root = path.resolve(name); | ||
var appName = path.basename(root); | ||
checkAppName(appName); | ||
fs.ensureDirSync(name); | ||
if (!isSafeToCreateProjectIn(root)) { | ||
console.log('The directory ' + chalk.green(name) + ' contains files that could conflict.'); | ||
console.log('Try using a new directory name.'); | ||
process.exit(1); | ||
} | ||
console.log( | ||
'Creating a new React app in ' + chalk.green(root) + '.' | ||
); | ||
console.log(); | ||
var packageJson = { | ||
name: appName, | ||
version: '0.1.0', | ||
private: true | ||
}; | ||
fs.writeFileSync( | ||
path.join(root, 'package.json'), | ||
JSON.stringify(packageJson, null, 2) | ||
); | ||
var originalDirectory = process.cwd(); | ||
process.chdir(root); | ||
run(root, appName, version, verbose, originalDirectory, template); | ||
} | ||
function shouldUseYarn() { | ||
try { | ||
execSync('yarnpkg --version', {stdio: 'ignore'}); | ||
return true; | ||
} catch (e) { | ||
return false; | ||
} | ||
} | ||
function install(useYarn, dependencies, verbose, isOnline) { | ||
return new Promise(function(resolve, reject) { | ||
var command; | ||
var args; | ||
if (useYarn) { | ||
command = 'yarnpkg'; | ||
args = [ | ||
'add', | ||
'--exact', | ||
]; | ||
if (!isOnline) { | ||
args.push('--offline'); | ||
} | ||
[].push.apply(args, dependencies); | ||
if (!isOnline) { | ||
console.log(chalk.yellow('You appear to be offline.')); | ||
console.log(chalk.yellow('Falling back to the local Yarn cache.')); | ||
console.log(); | ||
} | ||
} else { | ||
checkNpmVersion(); | ||
command = 'npm'; | ||
args = ['install', '--save', '--save-exact'].concat(dependencies); | ||
} | ||
if (verbose) { | ||
args.push('--verbose'); | ||
} | ||
var child = spawn(command, args, {stdio: 'inherit'}); | ||
child.on('close', function(code) { | ||
if (code !== 0) { | ||
reject({ | ||
command: command + ' ' + args.join(' ') | ||
}); | ||
return; | ||
} | ||
resolve(); | ||
}); | ||
}); | ||
} | ||
function run(root, appName, version, verbose, originalDirectory, template) { | ||
var packageToInstall = getInstallPackage(version); | ||
var packageName = getPackageName(packageToInstall); | ||
var allDependencies = ['react', 'react-dom', packageToInstall]; | ||
console.log('Installing packages. This might take a couple minutes.'); | ||
console.log( | ||
'Installing ' + chalk.cyan('react') + ', ' + chalk.cyan('react-dom') + | ||
', and ' + chalk.cyan(packageName) + '...' | ||
); | ||
console.log(); | ||
var useYarn = shouldUseYarn(); | ||
checkIfOnline(useYarn) | ||
.then(function(isOnline) { | ||
return install(useYarn, allDependencies, verbose, isOnline); | ||
}) | ||
.then(function() { | ||
checkNodeVersion(packageName); | ||
// Since react-scripts has been installed with --save | ||
// we need to move it into devDependencies and rewrite package.json | ||
// also ensure react dependencies have caret version range | ||
fixDependencies(packageName); | ||
var scriptsPath = path.resolve( | ||
process.cwd(), | ||
'node_modules', | ||
packageName, | ||
'scripts', | ||
'init.js' | ||
); | ||
var init = require(scriptsPath); | ||
init(root, appName, verbose, originalDirectory, template); | ||
}) | ||
.catch(function(reason) { | ||
console.log(); | ||
console.log('Aborting installation.'); | ||
if (reason.command) { | ||
console.log(' ' + chalk.cyan(reason.command), 'has failed.') | ||
} | ||
console.log(); | ||
// On 'exit' we will delete these files from target directory. | ||
var knownGeneratedFiles = [ | ||
'package.json', 'npm-debug.log', 'yarn-error.log', 'yarn-debug.log', 'node_modules' | ||
]; | ||
var currentFiles = fs.readdirSync(path.join(root)); | ||
currentFiles.forEach(function (file) { | ||
knownGeneratedFiles.forEach(function (fileToMatch) { | ||
// This will catch `(npm-debug|yarn-error|yarn-debug).log*` files | ||
// and the rest of knownGeneratedFiles. | ||
if ((fileToMatch.match(/.log/g) && file.indexOf(fileToMatch) === 0) || file === fileToMatch) { | ||
console.log('Deleting generated file...', chalk.cyan(file)); | ||
fs.removeSync(path.join(root, file)); | ||
} | ||
}); | ||
}); | ||
var remainingFiles = fs.readdirSync(path.join(root)); | ||
if (!remainingFiles.length) { | ||
// Delete target folder if empty | ||
console.log('Deleting', chalk.cyan(appName + '/'), 'from', chalk.cyan(path.resolve(root, '..'))); | ||
process.chdir(path.resolve(root, '..')); | ||
fs.removeSync(path.join(root)); | ||
} | ||
console.log('Done.'); | ||
process.exit(1); | ||
}); | ||
} | ||
function getInstallPackage(version) { | ||
var packageToInstall = 'react-scripts'; | ||
var validSemver = semver.valid(version); | ||
if (validSemver) { | ||
packageToInstall += '@' + validSemver; | ||
} else if (version) { | ||
// for tar.gz or alternative paths | ||
packageToInstall = version; | ||
} | ||
return packageToInstall; | ||
} | ||
// Extract package name from tarball url or path. | ||
function getPackageName(installPackage) { | ||
if (installPackage.indexOf('.tgz') > -1) { | ||
// The package name could be with or without semver version, e.g. react-scripts-0.2.0-alpha.1.tgz | ||
// However, this function returns package name only without semver version. | ||
return installPackage.match(/^.+\/(.+?)(?:-\d+.+)?\.tgz$/)[1]; | ||
} else if (installPackage.indexOf('git+') === 0) { | ||
// 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 | ||
return installPackage.match(/([^\/]+)\.git(#.*)?$/)[1]; | ||
} else if (installPackage.indexOf('@') > 0) { | ||
// Do not match @scope/ when stripping off @version or @tag | ||
return installPackage.charAt(0) + installPackage.substr(1).split('@')[0]; | ||
} | ||
return installPackage; | ||
} | ||
function checkNpmVersion() { | ||
var isNpm2 = false; | ||
try { | ||
var npmVersion = execSync('npm --version').toString(); | ||
isNpm2 = semver.lt(npmVersion, '3.0.0'); | ||
} catch (err) { | ||
return; | ||
} | ||
if (!isNpm2) { | ||
return; | ||
} | ||
console.log(chalk.yellow('It looks like you are using npm 2.')); | ||
console.log(chalk.yellow( | ||
'We suggest using npm 3 or Yarn for faster install times ' + | ||
'and less disk space usage.' | ||
)); | ||
console.log(); | ||
} | ||
function checkNodeVersion(packageName) { | ||
var packageJsonPath = path.resolve( | ||
process.cwd(), | ||
'node_modules', | ||
packageName, | ||
'package.json' | ||
); | ||
var packageJson = require(packageJsonPath); | ||
if (!packageJson.engines || !packageJson.engines.node) { | ||
return; | ||
} | ||
if (!semver.satisfies(process.version, packageJson.engines.node)) { | ||
console.error( | ||
chalk.red( | ||
'You are running Node %s.\n' + | ||
'Create React App requires Node %s or higher. \n' + | ||
'Please update your version of Node.' | ||
), | ||
process.version, | ||
packageJson.engines.node | ||
); | ||
process.exit(1); | ||
} | ||
} | ||
function checkAppName(appName) { | ||
var validationResult = validateProjectName(appName); | ||
if (!validationResult.validForNewPackages) { | ||
console.error('Could not create a project called ' + chalk.red('"' + appName + '"') + ' because of npm naming restrictions:'); | ||
printValidationResults(validationResult.errors); | ||
printValidationResults(validationResult.warnings); | ||
process.exit(1); | ||
} | ||
// TODO: there should be a single place that holds the dependencies | ||
var dependencies = ['react', 'react-dom']; | ||
var devDependencies = ['react-scripts']; | ||
var allDependencies = dependencies.concat(devDependencies).sort(); | ||
if (allDependencies.indexOf(appName) >= 0) { | ||
console.error( | ||
chalk.red( | ||
'We cannot create a project called ' + 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' | ||
) + | ||
chalk.cyan( | ||
allDependencies.map(function(depName) { | ||
return ' ' + depName; | ||
}).join('\n') | ||
) + | ||
chalk.red('\n\nPlease choose a different project name.') | ||
); | ||
process.exit(1); | ||
} | ||
} | ||
function makeCaretRange(dependencies, name) { | ||
var version = dependencies[name]; | ||
if (typeof version === 'undefined') { | ||
console.error( | ||
chalk.red('Missing ' + name + ' dependency in package.json') | ||
); | ||
process.exit(1); | ||
} | ||
var patchedVersion = '^' + version; | ||
if (!semver.validRange(patchedVersion)) { | ||
console.error( | ||
'Unable to patch ' + name + ' dependency version because version ' + chalk.red(version) + ' will become invalid ' + chalk.red(patchedVersion) | ||
); | ||
patchedVersion = version; | ||
} | ||
dependencies[name] = patchedVersion; | ||
} | ||
function fixDependencies(packageName) { | ||
var packagePath = path.join(process.cwd(), 'package.json'); | ||
var packageJson = require(packagePath); | ||
if (typeof packageJson.dependencies === 'undefined') { | ||
console.error( | ||
chalk.red('Missing dependencies in package.json') | ||
); | ||
process.exit(1); | ||
} | ||
var packageVersion = packageJson.dependencies[packageName]; | ||
if (typeof packageVersion === 'undefined') { | ||
console.error( | ||
chalk.red('Unable to find ' + packageName + ' in package.json') | ||
); | ||
process.exit(1); | ||
} | ||
packageJson.devDependencies = packageJson.devDependencies || {}; | ||
packageJson.devDependencies[packageName] = packageVersion; | ||
delete packageJson.dependencies[packageName]; | ||
makeCaretRange(packageJson.dependencies, 'react'); | ||
makeCaretRange(packageJson.dependencies, 'react-dom'); | ||
fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2)); | ||
} | ||
// If project only contains files generated by GH, it’s safe. | ||
// We also special case IJ-based products .idea because it integrates with CRA: | ||
// https://github.com/facebookincubator/create-react-app/pull/368#issuecomment-243446094 | ||
function isSafeToCreateProjectIn(root) { | ||
var validFiles = [ | ||
'.DS_Store', 'Thumbs.db', '.git', '.gitignore', '.idea', 'README.md', 'LICENSE', 'web.iml' | ||
]; | ||
return fs.readdirSync(root) | ||
.every(function(file) { | ||
return validFiles.indexOf(file) >= 0; | ||
}); | ||
} | ||
function checkIfOnline(useYarn) { | ||
if (!useYarn) { | ||
// Don't ping the Yarn registry. | ||
// We'll just assume the best case. | ||
return Promise.resolve(true); | ||
} | ||
return new Promise(function(resolve) { | ||
dns.resolve('registry.yarnpkg.com', function(err) { | ||
resolve(err === null); | ||
}); | ||
}); | ||
} | ||
require('./createReactApp'); |
{ | ||
"name": "create-react-app", | ||
"version": "1.2.1", | ||
"version": "1.3.0-alpha.58689133", | ||
"keywords": [ | ||
@@ -11,3 +11,3 @@ "react" | ||
"engines": { | ||
"node": ">=4" | ||
"node": ">=6" | ||
}, | ||
@@ -18,3 +18,4 @@ "bugs": { | ||
"files": [ | ||
"index.js" | ||
"index.js", | ||
"createReactApp.js" | ||
], | ||
@@ -29,5 +30,8 @@ "bin": { | ||
"fs-extra": "^1.0.0", | ||
"hyperquest": "^2.1.2", | ||
"semver": "^5.0.3", | ||
"tar-pack": "^3.4.0", | ||
"tmp": "0.0.31", | ||
"validate-npm-package-name": "^3.0.0" | ||
} | ||
} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
20098
4
553
9
2
6
3
+ Addedhyperquest@^2.1.2
+ Addedtar-pack@^3.4.0
+ Addedtmp@0.0.31
+ Addedbalanced-match@1.0.2(transitive)
+ Addedblock-stream@0.0.9(transitive)
+ Addedbrace-expansion@1.1.11(transitive)
+ Addedbuffer-from@0.1.2(transitive)
+ Addedconcat-map@0.0.1(transitive)
+ Addedcore-util-is@1.0.3(transitive)
+ Addeddebug@2.6.9(transitive)
+ Addedduplexer2@0.0.2(transitive)
+ Addedfs.realpath@1.0.0(transitive)
+ Addedfstream@1.0.12(transitive)
+ Addedfstream-ignore@1.0.5(transitive)
+ Addedglob@7.2.3(transitive)
+ Addedhyperquest@2.1.3(transitive)
+ Addedinflight@1.0.6(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedisarray@0.0.11.0.0(transitive)
+ Addedminimatch@3.1.2(transitive)
+ Addedminimist@1.2.8(transitive)
+ Addedmkdirp@0.5.6(transitive)
+ Addedms@2.0.0(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedos-tmpdir@1.0.2(transitive)
+ Addedpath-is-absolute@1.0.1(transitive)
+ Addedprocess-nextick-args@2.0.1(transitive)
+ Addedreadable-stream@1.0.341.1.142.3.8(transitive)
+ Addedrimraf@2.7.1(transitive)
+ Addedsafe-buffer@5.1.2(transitive)
+ Addedstring_decoder@0.10.311.1.1(transitive)
+ Addedtar@2.2.2(transitive)
+ Addedtar-pack@3.4.1(transitive)
+ Addedthrough2@0.6.5(transitive)
+ Addedtmp@0.0.31(transitive)
+ Addeduid-number@0.0.6(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addedwrappy@1.0.2(transitive)
+ Addedxtend@4.0.2(transitive)