The create - react - app (v3.7.2)
react
cli
Initialization command
// All CRA commands are as follows
/** NPM package commander: Command base tool */
const program = new commander.Command(packageJson.name)
.version(packageJson.version)
.arguments('<project-directory>')
.usage(`${chalk.green('<project-directory>')} [options]`)
.action(name= > {
projectName = name;
})
.option('--verbose'.'print additional logs')
.option('--info'.'print environment debug info')
.option(
'--scripts-version <alternative-package>'.'use a non-standard version of react-scripts'
)
.option(
'--template <path-to-template>'.'specify a template for the created project'
)
.option('--use-npm')
.option('--use-pnp')
// TODO: Remove this in next major release.
.option(
'--typescript'.'(this option will be removed in favour of templates in the next major release of create-react-app)'
)
.allowUnknownOption()
.on('--help', () = > {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 specific npm tag: ${chalk.green('@next')}`);
console.log(
` - a custom fork published on npm: ${chalk.green(
'my-react-scripts'
)}`
);
console.log(
` - a local path relative to the current working directory: ${chalk.green(
'file:.. /my-react-scripts'
)}`
);
console.log(
` - a .tgz archive: ${chalk.green(
'https://mysite.com/my-react-scripts-0.8.2.tgz'
)}`
);
console.log(
` - a .tar.gz archive: ${chalk.green(
'https://mysite.com/my-react-scripts-0.8.2.tar.gz'
)}`
);
console.log(
` It is not needed unless you specifically want to use a fork.`
);
console.log();
console.log(` A custom ${chalk.cyan('--template')} can be one of:`);
console.log(
` - a custom fork published on npm: ${chalk.green(
'cra-template-typescript'
)}`
);
console.log(
` - a local path relative to the current working directory: ${chalk.green(
'file:.. /my-custom-template'
)}`
);
console.log(
` - a .tgz archive: ${chalk.green(
'https://mysite.com/my-custom-template-0.8.2.tgz'
)}`
);
console.log(
` - a .tar.gz archive: ${chalk.green(
'https://mysite.com/my-custom-template-0.8.2.tar.gz'
)}`
);
console.log();
console.log(
` If you have any problems, do not hesitate to file an issue:`
);
console.log(
` ${chalk.cyan(
'https://github.com/facebook/create-react-app/issues/new'
)}`
);
console.log();
})
.parse(process.argv);
Copy the code
-v, –version Indicates the version number
// The current create-react-app version is output
new commander.Command(packageJson.name)
.version(packageJson.version) // The command option is already generated by default
Copy the code
–verbose displays detailed logs
–info Displays information about the current system and environment
// In the source code, if the command has this parameter, it will be executed
/** NPM package: envinfo: Quick access to information about the current various software environments */
return envinfo
.run(
{
System: ['OS'.'CPU'].Binaries: ['Node'.'npm'.'Yarn'].Browsers: ['Chrome'.'Edge'.'Internet Explorer'.'Firefox'.'Safari'].npmPackages: ['react'.'react-dom'.'react-scripts'].npmGlobalPackages: ['create-react-app'],}, {duplicates: true.showNotFound: true,
}
)
.then(console.log);
Copy the code
–scripts-version Specifies a specific react-scripts script to run
–template Specifies the project template. You can specify a template of your own
— Use PNP –> what is PNP
Typescript uses TS development. Later versions will remove this option
This option will soon be deprecated and can be replaced with –template typescript
if (useTypeScript) {
console.log(
chalk.yellow(
'The --typescript option has been deprecated and will be removed in a future release.'));console.log(
chalk.yellow(
`In future, please use ${chalk.cyan('--template typescript')}. `));console.log();
if(! template) { template ='typescript'; }}Copy the code
Start creating the project
The createApp method is called to create the project. Node version >=8.10.0 is required
createApp
The createApp method is called first
createApp(
projectName, // Project name
program.verbose, // --verbose
program.scriptsVersion, // --scripts-version
program.template, // --template
program.useNpm, // --use-npm
program.usePnp, // --use-pnp
program.typescript // --typescript
);
Copy the code
Create package. Json
const packageJson = {
name: appName,
version: '0.1.0 from'.private: true}; fs.writeFileSync( path.join(root,'package.json'),
JSON.stringify(packageJson, null.2) + os.EOL
);
Copy the code
If yarn is used, copy the yarn.lock.cached file from the current directory to the root directory of the project and name it yarn.lock
if (useYarn) {
let yarnUsesDefaultRegistry = true;
try {
yarnUsesDefaultRegistry =
execSync('yarnpkg config get registry')
.toString()
.trim() === 'https://registry.yarnpkg.com';
} catch (e) {
// ignore
}
if (yarnUsesDefaultRegistry) {
fs.copySync(
require.resolve('./yarn.lock.cached'),
path.join(root, 'yarn.lock')); }}Copy the code
run
We then call run to continue creating the new project
/** NPM package semver: a tool library for checking and comparing version numbers */
run(
root,
appName,
version, // scriptsVersion
verbose,
originalDirectory,
template,
useYarn,
usePnp
);
Copy the code
Handles react-scripts reference scripts and –template input arguments
// ...
let packageToInstall = 'react-scripts';
// ...
// The dependency collection that will be used
const allDependencies = ['react'.'react-dom', packageToInstall];
Promise.all([
getInstallPackage(version, originalDirectory),
getTemplateInstallPackage(template, originalDirectory),
])
Copy the code
GetInstallPackage is called to handle the use of react-scripts.
- Standard version number: ‘1.2.3’, ‘@x.x.x’, etc., specified to use
react-scripts
The version of the – >[email protected]
- Specify a local custom file: ‘file: XXX/XXX ‘and return a local absolute path
- Alternative paths (for tar.gz or Alternative Paths) : Can be an on-line NPM package that returns this path directly due to default support
typescript
Template, if specifiedscriptsVersion
forreact-scripts-ts
, there will be a confirmation prompt
/** NPM package inquirer: input/output interactive processing tool */
const scriptsToWarn = [
{
name: 'react-scripts-ts'.message: chalk.yellow(
`The react-scripts-ts package is deprecated. TypeScript is now supported natively in Create React App. You can use the ${chalk.green(
'--template typescript'
)}option instead when generating your app to include TypeScript support. Would you like to continue using react-scripts-ts? `),},];for (const script of scriptsToWarn) {
if (packageToInstall.startsWith(script.name)) {
return inquirer
.prompt({
type: 'confirm'.name: 'useScript'.message: script.message,
default: false,
})
.then(answer= > {
if(! answer.useScript) { process.exit(0);
}
returnpackageToInstall; }); }}Copy the code
Call getTemplateInstallPackage processing – the use of the template
- Specify the template as a local file starting with file:
- Links without protocols
: / /
ortgz|tar.gz
Compressed package - similar
@xxx/xxx/xxxx
or@xxxx
The specified path or template name of
const packageMatch = template.match(/ ^ @ ^ /] + \ /)? (. +) $/);
const scope = packageMatch[1] | |' ';
const templateName = packageMatch[2];
if (
templateName === templateToInstall ||
templateName.startsWith(`${templateToInstall}- `)) {// Covers:
// - cra-template
// - @SCOPE/cra-template
// - cra-template-NAME
// - @SCOPE/cra-template-NAME
templateToInstall = `${scope}${templateName}`;
} else if (templateName.startsWith(The '@')) {
// Covers using @SCOPE only
templateToInstall = `${templateName}/${templateToInstall}`;
} else {
// Covers templates without the `cra-template` prefix:
// - NAME
// - @SCOPE/NAME
templateToInstall = `${scope}${templateToInstall}-${templateName}`;
}
// cra-template: This is the official base template for Create React App.
Copy the code
@ XXX /cra-template, @ XXX /cra-template-xxx, crA-template-xxx, crA-template, The two officially specified templates are cra-template-typescript and CRA-template. Template specific content we can go to the official warehouse to view, you can customize or magic change some things
Then get the installation package information after the –scripts-version and –template processing
/** NPM package tem: Used to create temporary files and directories in node.js environment. Hyperquest: Convert HTTP requests to streams of output tar-pack: tar/gz compression or decompression */
Promise.all([
getPackageInfo(packageToInstall),
getPackageInfo(templateToInstall),
])
// getPackageInfo is a useful utility function
// Extract package name from tarball url or path.
function getPackageInfo(installPackage) {
if (installPackage.match(/^.+\.(tgz|tar\.gz)$/)) {
return getTemporaryDirectory()
.then(obj= > {
let stream;
if (/^http/.test(installPackage)) {
stream = hyperquest(installPackage);
} else {
stream = fs.createReadStream(installPackage);
}
return extractStream(stream, obj.tmpdir).then((a)= > obj);
})
.then(obj= > {
const { name, version } = require(path.join(
obj.tmpdir,
'package.json'
));
obj.cleanup();
return { name, version };
})
.catch(err= > {
// 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.
console.log(
`Could not extract the package name from the archive: ${err.message}`
);
const assumedProjectName = installPackage.match(
/ ^. + \ / (. +?) (? :-\d+.+)? \.(tgz|tar\.gz)$/) [1];
console.log(
`Based on the filename, assuming it is "${chalk.cyan( assumedProjectName )}"`
);
return Promise.resolve({ name: assumedProjectName });
});
} 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
return Promise.resolve({
name: installPackage.match(/([^/]+)\.git(#.*)? $/) [1]}); }else if (installPackage.match(+ @ /. /)) {
// Do not match @scope/ when stripping off @version or @tag
return Promise.resolve({
name: installPackage.charAt(0) + installPackage.substr(1).split(The '@') [0].version: installPackage.split(The '@') [1]}); }else if (installPackage.match(/^file:/)) {
const installPackagePath = installPackage.match(/^file:(.*)? $/) [1];
const { name, version } = require(path.join(
installPackagePath,
'package.json'
));
return Promise.resolve({ name, version });
}
return Promise.resolve({ name: installPackage });
}
function extractStream(stream, dest) {
return new Promise((resolve, reject) = > {
stream.pipe(
unpack(dest, err => {
if (err) {
reject(err);
} else{ resolve(dest); }})); }); }Copy the code
The main job of the run method is to process the packages provided by –scripts-version and –template, collecting project dependencies
install
The install method is called after the run process collects dependencies
return install(
root, // Project name
useYarn,
usePnp,
allDependencies,
verbose,
isOnline // If YARN is used, dns.lookup Checks whether the registry.yarnpkg.com is normal
)
// Install mainly deals with some command parameter processing before installation and the above collection of dependencies installation
function install(root, useYarn, usePnp, dependencies, verbose, isOnline) {
return new Promise((resolve, reject) = > {
let command;
let args;
if (useYarn) {
command = 'yarnpkg';
args = ['add'.'--exact'];
if(! isOnline) { args.push('--offline');
}
if (usePnp) {
args.push('--enable-pnp');
}
[].push.apply(args, dependencies);
args.push('--cwd');
args.push(root);
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 {
command = 'npm';
args = [
'install'.'--save'.'--save-exact'.'--loglevel'.'error',
].concat(dependencies);
if (usePnp) {
console.log(chalk.yellow("NPM doesn't support PnP."));
console.log(chalk.yellow('Falling back to the regular installs.'));
console.log(); }}if (verbose) {
args.push('--verbose');
}
const child = spawn(command, args, { stdio: 'inherit' });
child.on('close', code => {
if(code ! = =0) {
reject({
command: `${command} ${args.join(' ')}`});return;
}
resolve();
});
});
}
Copy the code
After installing dependencies, check whether the react-scripts version matches the current Node version. Check whether the react and react-dom versions are correctly installed. Rewrite the dependencies to package.json
await executeNodeScript(
{
cwd: process.cwd(),
args: nodeArgs,
},
[root, appName, verbose, originalDirectory, templateName],
`
var init = require('${packageName}/scripts/init.js');
init.apply(null, JSON.parse(process.argv[1]));
`
);
function executeNodeScript({ cwd, args }, data, source) {
return new Promise((resolve, reject) = > {
const child = spawn(
process.execPath,
[...args, '-e', source, The '-'.JSON.stringify(data)],
{ cwd, stdio: 'inherit'}); child.on('close', code => {
if(code ! = =0) {
reject({
command: `node ${args.join(' ')}`});return;
}
resolve();
});
});
}
Copy the code
After checking for dependencies correctly, init initialization under the provided scripts script will be performed:
- for
package.json
addscripts
/eslintConfig
/browserslist
Such as configuration, - If there is
README.md
, rename itREADME.old.md
- Copy the template to the project directory, depending on whether it is used
yarn
, the templateREADME.md
The command is explained to theyarn
- Install template dependencies if yes
ts
The project initializes the associated configuration (verifyTypeScriptSetup
) - remove
node_modules
The template - Initialize the
git
related
At this point, the entire project is created
comb
- Initialize scaffold various commands
- create
package.json
- Process and validate command parameters and collect dependencies
- Pre-installation dependencies
- To deal with
package.json
thereact/react-dom
Depend on the version number and verifynode
Whether the version meets requirements - Verify what is provided
react-scripts
Dependency, and calls the dependent under the child processreact-scripts/scripts/init.js
To initialize the project template - To install template dependencies
ts
Initializes its configuration - Verify the code warehouse, do compatibility processing; If no warehouse is added, initialize it
git
Write in the last
Create-react-app is an example of how to use the command-line interface (CLI). Wen Zheng if there is incorrect place welcome to criticize!