
git clone https://github.com/facebook/create-react-app.git --depth=1
cd create-react-app
yarn install
package.json
"scripts": {
+  "create": "node ./packages/create-react-app/index.js",
}
npm run [command] [-- <args>]yarn install  #安装项止依赖和软链接
npm run create -- aaa  #执行创建命令
Installing packages. This might take a couple of minutes. #安装依赖包
Installing react, react-dom, and react-scripts with cra-template... #安装依赖包
Installing template dependencies using yarnpkg... #安装模板依赖
Removing template package using yarnpkg... #移除模板模块
Removing module cra-template...  #移除cra-template模块
Success! Created aaa at C:\aprepare\create-react-app\aaa #成功创建
Inside that directory, you can run several commands: #执行命令
cd aaa
yarn start
.vscode\launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch via NPM",
            "request": "launch",
            "runtimeArgs": [
                "run-script",
                "create"
            ],
            "runtimeExecutable": "npm",
            "skipFiles": [
                "<node_internals>/**"
            ],
            "type": "pwa-node"
        }
    ]
}
package.json
  "scripts": {
+    "version": "node ./packages/create-react-app3/index.js --version",
+    "create": "node ./packages/create-react-app3/index.js aaa"
  }
packages\create-react-app3\package.json
{
+ "main": "./index.js"
}
packages\create-react-app3\index.js
#!/usr/bin/env node
const { init } = require('./createReactApp');
init();
packages\create-react-app3\createReactApp.js
const {Command} = require('commander');
const chalk = require('chalk');
const packageJson = require('./package.json');
let appName;
async function init() {
    new Command(packageJson.name)
        .version(packageJson.version)
        .arguments('<project-directory>')
        .usage(`${chalk.green('<project-directory>')} [options]`)
        .action(projectDirectory => {
            appName = projectDirectory;
        })
        .parse(process.argv);
    console.log('appName=', appName);
}
module.exports = {
    init
}
npm run create
packages\create-react-app3\createReactApp.js
const {Command} = require('commander');
const chalk = require('chalk');
+const fs = require('fs-extra');
+const path = require('path');
const packageJson = require('./package.json');
let appName;
async function init() {
    new Command(packageJson.name)
        .version(packageJson.version)
        .arguments('<project-directory>')
        .usage(`${chalk.green('<project-directory>')} [options]`)
        .action(projectDirectory => {
            appName = projectDirectory;
        })
        .parse(process.argv);
    console.log('appName=', appName);
+   await createApp(appName);
}
+async function createApp(appName) {
+    const root = path.resolve(appName);
+    fs.ensureDirSync(appName);
+    console.log(`Creating a new React app in ${chalk.green(root)}.`);
+    const packageJson = {
+      name: appName,
+      version: '0.1.0',
+      private: true,
+    };
+    fs.writeFileSync(
+      path.join(root, 'package.json'),
+      JSON.stringify(packageJson, null, 2)
+    );
+    const originalDirectory = process.cwd();
+    process.chdir(root);
+    console.log('root',root);
+    console.log('appName',appName);
+    console.log('originalDirectory',originalDirectory);
+  }
module.exports = {
    init
}
packages\create-react-app3\createReactApp.js
const {Command} = require('commander');
const chalk = require('chalk');
const fs = require('fs-extra');
const path = require('path');
+const spawn = require('cross-spawn');
const packageJson = require('./package.json');
let appName;
async function init() {
    new Command(packageJson.name)
        .version(packageJson.version)
        .arguments('<project-directory>')
        .usage(`${chalk.green('<project-directory>')} [options]`)
        .action(projectDirectory => {
            appName = projectDirectory;
        })
        .parse(process.argv);
    console.log('appName=', appName);
    await createApp(appName);
}
async function createApp(appName) {
    const root = path.resolve(appName);
    fs.ensureDirSync(appName);
    console.log(`Creating a new React app in ${chalk.green(root)}.`);
    const packageJson = {
      name: appName,
      version: '0.1.0',
      private: true,
    };
    fs.writeFileSync(
      path.join(root, 'package.json'),
      JSON.stringify(packageJson, null, 2)
    );
    const originalDirectory = process.cwd();
    process.chdir(root);
    console.log('root',root);
    console.log('appName',appName);
    console.log('originalDirectory',originalDirectory);
+   await run(
+        root,
+        appName,
+        originalDirectory
+   );
}
+async function run(root,appName,originalDirectory) {
+    const scriptName = 'react-scripts';
+    const templateName = 'cra-template';
+    const allDependencies = ['react', 'react-dom', scriptName, templateName];
+    console.log('Installing packages. This might take a couple of minutes.');
+    console.log(
+      `Installing ${chalk.cyan('react')}, ${chalk.cyan(
+        'react-dom'
+      )}, and ${chalk.cyan(scriptName)} with ${chalk.cyan(templateName)}`
+    );
+    await install(root, allDependencies);
+}
+function install(root, allDependencies) {
+    return new Promise((resolve) => {
+      const command = 'yarnpkg';
+      const args = ['add', '--exact', ...allDependencies, '--cwd', root];
+      console.log('command:',command,args);
+      const child = spawn(command, args, { stdio: 'inherit' });
+      child.on('close', resolve);
+    });
+}
module.exports = {
    init
}
command: yarnpkg [
  'add',
  '--exact',
  'react',
  'react-dom',
  'react-scripts',
  'cra-template',
  '--cwd',
  'C:\\aprepare\\create-react-app3\\aaa'
]
yarnpkg add --exact react react-dom react-scripts cra-template --cwd C:\\aprepare\\create-react-app3\\aaa
packages\create-react-app3\createReactApp.js
const {Command} = require('commander');
const chalk = require('chalk');
const fs = require('fs-extra');
const path = require('path');
const spawn = require('cross-spawn');
const packageJson = require('./package.json');
let appName;
async function init() {
    new Command(packageJson.name)
        .version(packageJson.version)
        .arguments('<project-directory>')
        .usage(`${chalk.green('<project-directory>')} [options]`)
        .action(projectDirectory => {
            appName = projectDirectory;
        })
        .parse(process.argv);
    console.log('appName=', appName);
    await createApp(appName);
}
async function createApp(appName) {
    const root = path.resolve(appName);
    fs.ensureDirSync(appName);
    console.log(`Creating a new React app in ${chalk.green(root)}.`);
    const packageJson = {
      name: appName,
      version: '0.1.0',
      private: true,
    };
    fs.writeFileSync(
      path.join(root, 'package.json'),
      JSON.stringify(packageJson, null, 2)
    );
    const originalDirectory = process.cwd();
    process.chdir(root);
    console.log('root',root);
    console.log('appName',appName);
    console.log('originalDirectory',originalDirectory);
    await run(
        root,
        appName,
        originalDirectory
    );
}
async function run(root,appName,originalDirectory) {
    const scriptName = 'react-scripts';
    const templateName = 'cra-template';
    const allDependencies = ['react', 'react-dom', scriptName, templateName];
    console.log('Installing packages. This might take a couple of minutes.');
    console.log(
      `Installing ${chalk.cyan('react')}, ${chalk.cyan(
        'react-dom'
      )}, and ${chalk.cyan(scriptName)} with ${chalk.cyan(templateName)}`
    );
    await install(root, allDependencies);
+    let data = [root, appName, true, originalDirectory, templateName];
+    let source = `
+    var init = require('react-scripts/scripts/init.js');
+    init.apply(null, JSON.parse(process.argv[1]));
+  `
+    await executeNodeScript({ cwd: process.cwd() }, data, source);
+    console.log('Done.');
+    process.exit(0);
}
+function executeNodeScript({ cwd }, data, source) {
+  return new Promise((resolve) => {
+    const child = spawn( 
+      process.execPath,
+      ['-e', source, '--', JSON.stringify(data)],
+      { cwd, stdio: 'inherit' }
+    );
+    child.on('close', resolve);
+  });
+}
function install(root, allDependencies) {
    return new Promise((resolve) => {
      const command = 'yarnpkg';
      const args = ['add', '--exact', ...allDependencies, '--cwd', root];
      console.log('command:',command,args);
      const child = spawn(command, args, { stdio: 'inherit' });
      child.on('close', resolve);
    });
}
module.exports = {
    init
}
npm i lerna -g
lerna init
package.json
{
  "name": "root",
  "private": true,
  "devDependencies": {
    "lerna": "^3.22.1"
  }
}
lerna.json
{
  "packages": [
    "packages/*"
  ],
  "version": "0.0.0"
}
yarn workspace允许我们使用 monorepo 的形式来管理项目yarn.lock 文件。子项目也会被 link 到 node_modules 里面,这样就允许我们就可以直接用 import 导入对应的项目yarn.lock文件是自动生成的,也完全Yarn来处理.yarn.lock锁定你安装的每个依赖项的版本,这可以确保你不会意外获得不良依赖package.json
{
  "name": "root",
  "private": true, // 私有的,用来管理整个项目,不会被发布到npm
+  "workspaces": [
+    "packages/*"
+  ],
  "devDependencies": {
    "lerna": "^3.22.1"
  }
}
lerna create create-react-app3
lerna create react-scripts3
lerna create cra-template3
设置加速镜像
yarn config get registry
yarn config set registry http://registry.npm.taobao.org/
yarn config set registry http://registry.npmjs.org/
| 作用 | 命令 | 
|---|---|
| 查看工作空间信息 | yarn workspaces info | 
| 给根空间添加依赖 | yarn add chalk cross-spawn fs-extra --ignore-workspace-root-check | 
| 给某个项目添加依赖 | yarn workspace create-react-app3 add commander | 
| 删除所有的 node_modules | lerna clean 等于 yarn workspaces run clean | 
| 安装和link | yarn install 等于 lerna bootstrap --npm-client yarn --use-workspaces | 
| 重新获取所有的 node_modules | yarn install --force | 
| 查看缓存目录 | yarn cache dir | 
| 清除本地缓存 | yarn cache clean | 
node.js命令行解决方案-V和--versionusage选项可以修改帮助信息的首行提示const chalk = require('chalk');
const {Command} = require('commander');
console.log('process.argv',process.argv);
new Command('create-react-app')
    .version('1.0.0')
    .arguments('<must1> <must2> [optional]')
    .usage(`${chalk.green('<must1> <must2>')} [optional]`)
    .action((must1,must2,optional,...args) => {
       console.log(must1,must2,optional,args);
    })
    .parse(process.argv);
spawn和spawnSync的跨平台解决方案stdio流传给父进程或从父进程传入const spawn = require('cross-spawn');
const child = spawn('node', ['script.js','one','two','three'], { stdio: 'inherit' });
child.on('close',()=>{
    console.log('child is done!');
});
const result = spawn.sync('node', ['script.js','one','two','three'], { stdio: 'inherit' });
console.log(result);