UNPKG

monorepo-cli

Version:

基于 yarn v2 berry 的 monorepo 项目的创建

891 lines (889 loc) 34.3 kB
#!/usr/bin/env node "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const fsp = fs.promises; const path = require("path"); const commander = require("commander"); const program = commander.program; const utils = require("ph-utils"); const fileUtils = require("ph-utils/lib/file"); const colors = require("ansi-colors"); const https = require("https"); const Spinner = require("ph-terminal-spinner"); const serverUtils = require("ph-utils/lib/server"); const url_1 = require("url"); const pkg = require("./package.json"); const enquirer = require('enquirer'); const Mustache = require('mustache'); /** 模板文件地址 */ const TEMPLATE_PATH = path.join(__dirname, 'templates'); const devDepends = { typescript: '4.4.3', prettier: '2.4.1', eslint: '7.32.0', 'eslint-config-alloy': '4.3.0', '@typescript-eslint/eslint-plugin': '4.33.0', '@typescript-eslint/parser': '4.33.0', '@types/node': '16.10.3', }; program.version(pkg.version); const spinner = new Spinner(); const eslintConfig = { extends: ['alloy'], rules: { // 自定义你的规则 'no-eq-null': 'off', eqeqeq: ['error', 'always', { null: 'ignore' }], 'no-param-reassign': 'off', 'max-params': ['error', 5], }, }; // eslint prettier git 通用的忽略 const eslintIgnore = ['/.yarn/*', '/**/*.d.ts', '.pnp.js']; const gitignore = ['node_modules', '*.log', '.idea', '.vscode', '/packages/**/LICENSE', '/**/*.d.ts']; /** * 下载更新 yarn 版本到 berry,类似于执行命令 yarn set version berry * @param yarnPath 下载后的 berry 文件保存目录 * @param rc 下载次数,做失败重连处理,最多4次,超过4次则触发下载失败 * @param cb 下载完成后的回调,参数表示是否失败 */ function downloadBerry(yarnPath, rc, cb) { let req = https.get('https://codechina.csdn.net/u011113654/yarn2-berry/-/raw/master/yarn-berry.cjs', (res) => { res.setEncoding('utf-8'); let savePath = path.join(yarnPath, 'yarn-berry.cjs'); let saveStream = fs.createWriteStream(savePath); saveStream.on('close', () => { cb(false); }); res.pipe(saveStream); }); req.on('error', () => { if (rc === 4) { cb(true); // console.log('下载 berry 失败,请参考 https://gitee.com/towardly/yarn2-berry 手动下载!') } else { rc++; downloadBerry(yarnPath, rc, cb); } }); } /** * https GET 请求 * @param {String} url 请求 url 地址 */ let get = function (url) { return new Promise((resolve, reject) => { https .get(url, (res) => { let resData = ''; let statusCode = res.statusCode; res.setEncoding('utf8'); res.on('data', (chunk) => { resData += chunk; }); res.on('end', () => { if (statusCode >= 200 && statusCode < 300) { resolve(JSON.parse(resData)); } else { reject(new Error(statusCode + ' & ' + res.statusText)); } }); }) .on('error', (e) => { reject(e); }); }); }; /** * 根据应用包名获取应用的详细信息 * @param {String} packageName 应用包名 */ function queryPackage(packageName) { // https://registry.npmjs.org/koa/latest return get(`https://registry.npm.taobao.org/${packageName}/latest`).then((r) => { return { name: r.name, version: r.version }; }); } /** * 检查依赖版本项 */ function checkDependencies(pkgs) { return new Promise((resolve) => { let dev1 = []; let dev2 = []; let len = pkgs.length; if (len === 0) { resolve([]); } for (let i = 0; i < len; i++) { let name = pkgs[i]; queryPackage(name) .then((pka) => { pka.start = true; if (name.startsWith('@')) { dev1.push(pka); } else { dev2.push(pka); } }) .catch(() => { if (name.startsWith('@')) { dev1.push({ name, version: devDepends[name], start: true }); } else { dev2.push({ name, version: devDepends[name], start: true }); } }) .finally(() => { if (dev1.length + dev2.length === len) { dev1 = dev1.sort((a, b) => a.name.localeCompare(b.name)); dev2 = dev2.sort((a, b) => a.name.localeCompare(b.name)); let dev = dev1.concat(dev2); dev[dev.length - 1].start = false; resolve(dev); } }); } }); } /** * 初始化工作区 * @param workspaceConfig * @param wsDevs * @returns */ function initWorkspace(workspaceConfig) { return new Promise((resolve) => { let wsDevs = []; // 复制 LICENSE 文件 fs.copyFile(path.join(workspaceConfig.proPath, 'LICENSE'), path.join(workspaceConfig.path, 'LICENSE'), () => { }); // 构造主文件 index.ts fileUtils.write(path.join(workspaceConfig.path, 'index.ts'), `console.log('${workspaceConfig.name}')`); // README.md fileUtils.write(path.join(workspaceConfig.path, 'README.md'), `#${workspaceConfig.name}\r\n---`); if (workspaceConfig.isTs) { let tsConfig = { extends: '../../tsconfig_base.json', compilerOptions: { module: 'CommonJS', outDir: './', }, files: ['index.ts'], }; if (!workspaceConfig.isNode) { tsConfig.compilerOptions = { module: 'es6', resolveJsonModule: false, outDir: './', }; } else { wsDevs.push('@types/node'); } // 写入 tsconfig.json 文件 fileUtils.write(path.join(workspaceConfig.path, 'tsconfig.json'), tsConfig); } resolve(wsDevs); }); } /** * 更新 yarn 版本 * @param proPath * @returns */ function updateYarn(proPath) { return new Promise((resolve, reject) => { spinner.start('更新yarn版本为berry'); // 更新 yarn 版本至 berry const yarnPath = path.join(proPath, '.yarn', 'releases'); fs.mkdir(yarnPath, { recursive: true }, () => { }); fileUtils.write(path.join(proPath, '.yarnrc.yml'), 'yarnPath: ".yarn/releases/yarn-berry.cjs"'); let rc = 0; // 重试次数 // 下载更新 yarn 版本到 berry downloadBerry(yarnPath, rc, (isError) => { if (isError === true) { spinner.fail('更新yarn版本失败'); reject(new utils.BaseError('UpgradeYarnError', 'update yarn to berry error')); } else { spinner.succeed('更新yarn版本成功'); resolve(0); } }); }); } /** * 初始化工程 * @param proPath 工程目录 * @param config 配置项 * @param proDevs 工程依赖 * @returns */ function initProject(proPath, config) { return new Promise((resolve) => { let proDevs = []; if (config.license === true) { fs.copyFile(path.join(TEMPLATE_PATH, 'LICENSE'), path.join(proPath, 'LICENSE'), () => { }); } if (config.ts === true) { proDevs.push('typescript'); // 使用 ts fs.copyFile(path.join(TEMPLATE_PATH, 'tsconfig_base.json'), path.join(proPath, 'tsconfig_base.json'), () => { }); eslintIgnore.push('/**/*.js'); gitignore.push('/**/*.js'); eslintConfig.extends.push('alloy/typescript'); eslintConfig.rules['@typescript-eslint/no-require-imports'] = 'off'; } else { eslintConfig.parser = 'espree'; } if (config.prettier === true) { proDevs.push('prettier'); // 需要 prettier fs.copyFile(path.join(TEMPLATE_PATH, '.prettierrc.js'), path.join(proPath, '.prettierrc.js'), () => { }); fileUtils.write(path.join(proPath, '.prettierignore'), eslintIgnore.join('\r\n')); } if (config.eslint === true) { proDevs.push('eslint'); proDevs.push('eslint-config-alloy'); if (config.ts === true) { proDevs.push('@typescript-eslint/eslint-plugin'); proDevs.push('@typescript-eslint/parser'); } // 需要 eslint fileUtils.write(path.join(proPath, '.eslintignore'), eslintIgnore.join('\r\n')); fileUtils.write(path.join(proPath, '.eslintrc.js'), 'module.exports = ' + JSON.stringify(eslintConfig, null, 2)); } // 新建 package.json // fileUtils.write(path.join(proPath, 'package.json'), pPkg) // .gitignore fileUtils.write(path.join(proPath, '.gitignore'), gitignore.join('\r\n')); resolve(proDevs); }); } /** * 渲染模板文件 * @param from 模板文件路径 * @param data 数据源 * @param to 目标文件 */ function template(from, data, to) { fs.readFile(from, 'utf-8', (err, tpl) => { if (err == null) { fileUtils.write(to, Mustache.render(tpl, data)); } }); return Promise.resolve(0); } // 定义创建项目的命令 program .command('create <name>') .alias('c') .description('创建 monorepo 项目') .option('--ts', '是否使用ts,默认为:true', true) .option('--no-ts', '不使用ts') .option('-e, --eslint', '是否需要 eslint ,默认为:true', true) .option('--no-eslint', '不需要 eslint ') .option('-p, --prettier', '是否需要 prettier 进行格式化,默认为:true', true) .option('--no-prettier', '不需要prettier') .option('-n, --node', '是否是NodeJs(Commonjs)项目,而非 WEB(ES Module),默认为:true', true) .option('--no-node', '不是nodejs项目') .option('-l, --license', '是否需要 LICENSE 文件,默认为:true', true) .option('--no-license', '不需要 license 文件,一般私有项目开发时配置') .option('-t, --tool <tool>', '包管理工具, 默认为:yarn2') .option('-w --workspace <name>', '新建项目的同时创建工作区') .option('-d, --director <director>', '创建项目的目录地址,默认为:执行命令的目录') .action((name, destination) => { destination.tool = destination.tool === 'npm' ? 'npm' : 'yarn'; const spinner = new Spinner(); const projectPath = path.join(destination.director || process.cwd(), name); // 项目目录 let workspacePath = path.join(projectPath, 'packages'); // 工作区目录 if (!utils.isBlank(destination.workspace)) { // 创建项目的同时创建工作区 workspacePath = path.join(workspacePath, destination.workspace); } // 验证文件是否存在 fsp.access(projectPath, fs.constants.F_OK).then(() => { console.error(colors.red(`目录 ${projectPath} 已经存在!`)); }, () => { spinner.start('创建项目目录'); // 创建文件夹 fsp .mkdir(workspacePath, { recursive: true }) .then(() => { spinner.succeed('创建项目目录成功'); spinner.start('构建项目文件'); // 初始化项目 return initProject(projectPath, destination); }) .then((pkgs) => { spinner.succeed('项目文件构建成功'); spinner.start('检查依赖……'); return checkDependencies(pkgs); }) .then((pkgs) => { spinner.succeed('依赖检查成功'); return template(path.join(TEMPLATE_PATH, 'project_package.mtl'), { name, packages: pkgs, }, path.join(projectPath, 'package.json')); }) .then(() => { if (destination.tool === 'yarn') { spinner.start('更新 yarn 版本'); return updateYarn(projectPath); } else { return Promise.resolve(1); } }) .then((s) => { if (s === 0) { spinner.succeed('更新 yarn 版本成功'); } // 初始化 工作区文件 if (!utils.isBlank(destination.workspace)) { spinner.start('初始化工作区文件'); return initWorkspace({ name: destination.workspace, isTs: destination.ts, path: workspacePath, isNode: destination.node, proPath: projectPath, }); } else { return Promise.resolve(0); } }) .then((s) => { if (s !== 0) { spinner.succeed('初始化工作区文件成功'); if (s.length !== 0) { spinner.start('检查工作区依赖'); return checkDependencies(s); } else { return Promise.resolve(0); } } else { return Promise.resolve(0); } }) .then((s) => { if (s !== 0) { spinner.succeed('检查工作区依赖成功'); return template(path.join(TEMPLATE_PATH, 'workspace_package.mtl'), { name: destination.workspace, author: '', projectRepository: '', packages: s, }, path.join(workspacePath, 'package.json')); } else { return Promise.resolve(1); } }) .then((s) => { if (s === 0) { spinner.succeed('工作区依赖检查成功'); } spinner.succeed('添加开发工具成功'); spinner.succeed('初始化项目成功,请先按以下步骤执行,再进行项目开发:'); spinner.stop(); const steps = [ ` 1. 进入目录:${projectPath}: cd ${name}\r\n`, ` 2. 编辑 ${name} -- > package.json 中 repository 和 author 字段以自动填充工作区初始化\r\n`, ' 3. 如果需要 LICENSE 文件的话请在项目根目录下放置一份,这样后续构建工作区的时候会自动拷贝到每一个工作区\r\n', ` 4. 安装依赖:${destination.tool === 'npm' ? 'npm' : 'yarn'} install\r\n`, ]; let i = 5; if (!utils.isBlank(destination.workspace)) { steps.push(` ${i}. 完善 ${destination.workspace} --> package.json 文件\r\n`); i++; } if (destination.tool === 'yarn') { steps.push(` ${i}. 添加开发工具支持:yarn dlx @yarnpkg/pnpify --sdk vscode\r\n`); i++; } console.log(colors.green(steps.join(''))); }) .catch((err) => { console.log(err); spinner.fail('依赖安装失败'); spinner.stop(); }); }); }); /** 测试文件或目录是否存在 */ function accessTs(p) { return new Promise((resolve) => { fs.access(p, fs.constants.F_OK, (err) => { if (err) { resolve(false); } else { resolve(true); } }); }); } // 定义构建工作区的命令 program .command('workspace <name>') .alias('ws') .description('构建 monorepo 项目的工作区') .option('-n, --node', '是否是NodeJs(Commonjs)项目,而非 WEB(ES Module),默认为:true', true) .option('--no-node', '不是nodejs项目') .option('-d, --director <director>', '项目的目录地址,默认为:执行命令的目录') .action((name, config) => { const proPath = config.director || process.cwd(); const workspacePath = path.join(proPath, 'packages', name); const spinner = new Spinner(); fsp .mkdir(workspacePath) .then(() => { spinner.start('构建工作区文件'); // 检查项目根目录是否存在 tsconfig_base.json 和 读取根目录 package.json return accessTs(path.join(proPath, 'tsconfig_base.json')); }) .then((val) => { return initWorkspace({ name, proPath: proPath, path: workspacePath, isTs: val, isNode: config.node, }); }) .then((s) => { spinner.succeed('构建工作区文件成功'); spinner.start('检查工作区依赖'); return Promise.all([checkDependencies(s), fileUtils.readJSON(path.join(proPath, 'package.json'))]); }) .then((vals) => { let projectRepository = ''; if (vals[1].repository != null) { const url = new url_1.URL(vals[1].repository.url); const urlAddr = path.parse(url.pathname); projectRepository = `${url.host}${urlAddr.dir}/${urlAddr.name}`; } return Promise.all([ accessTs(path.join(proPath, '.yarnrc.yml')), template(path.join(TEMPLATE_PATH, 'workspace_package.mtl'), { name, author: vals[1].author, projectRepository, packages: vals[0], }, path.join(workspacePath, 'package.json')), ]); }) .then((vals) => { spinner.succeed('工作区依赖检查成功'); spinner.stop(); const steps = [ ` 1. 安装依赖:${vals[0] ? 'yarn workspace' : 'npm -w'} ${name} install\r\n`, ` 2. 完善 packages/${name} --> package.json 文件\r\n`, ]; console.log(colors.green(steps.join(''))); spinner.stop(); }) .catch(() => { console.error(colors.red('错误的项目目录地址')); spinner.stop(); }); }); /** * 初始化样式文件 * @param proPath * @param config * @param devs * @returns */ function initStyle(proPath, config, devs) { return new Promise((resolve) => { spinner.start('初始化样式文件'); if (config.ts === true) { // 使用 ts eslintIgnore.push('/**/*.js'); gitignore.push('/**/*.js'); eslintConfig.extends.push('alloy/typescript'); eslintConfig.rules['@typescript-eslint/no-require-imports'] = 'off'; devs.push('@typescript-eslint/eslint-plugin @typescript-eslint/parser'); } else { eslintConfig.parser = 'espree'; } if (config.prettier === true) { devs.push('prettier'); // 需要 prettier fs.copyFile(path.join(TEMPLATE_PATH, '.prettierrc.js'), path.join(proPath, '.prettierrc.js'), () => { }); fileUtils.write(path.join(proPath, '.prettierignore'), eslintIgnore.join('\r\n')); } // 需要 eslint fileUtils.write(path.join(proPath, '.eslintignore'), eslintIgnore.join('\r\n')); fileUtils.write(path.join(proPath, '.eslintrc.js'), 'module.exports = ' + JSON.stringify(eslintConfig, null, 2)); spinner.succeed('样式文件初始化成功'); resolve(0); }); } program .command('js-style') .description('为工程添加样式') .option('-p, --prettier', '是否需要 prettier 格式化代码,默认为:true', true) .option('--no-prettier', '不需要 prettier 格式化代码') .option('-d, --director <director>', '项目的目录地址,默认为:执行命令的目录') .addOption(new commander.Option('-t, --tool <package-tool>', '包管理工具') .default('berry', '默认使用 yarn v2 版本 berry 作为包管理工具') .choices(['berry', 'npm'])) .action((config) => { const proPath = config.director || process.cwd(); const devs = ['eslint', 'eslint eslint-config-alloy']; fsp .access(path.join(proPath, 'package.json'), fs.constants.F_OK) .then(() => accessTs(path.join(proPath, 'tsconfig_base.json'))) .then((isTs) => { config.ts = isTs; return initStyle(proPath, config, devs); }) .then(() => { spinner.start('安装依赖'); let cmd = ''; if (config.tool === 'npm') { cmd = `npm install ${devs.join(' ')} --save-dev`; } else { cmd = `yarn add ${devs.join(' ')} --dev`; } return serverUtils.execPromise(cmd, { cwd: proPath, errorName: 'InstallDependsError' }); }) .then(() => { spinner.succeed('依赖安装成功'); spinner.start('添加开发工具支持'); if (config.tool === 'berry') { return serverUtils.execPromise('yarn dlx @yarnpkg/pnpify --sdk vscode', { cwd: proPath, errorName: 'EditorSetupError', }); } else { return Promise.resolve(0); } }) .then(() => { spinner.succeed('添加开发工具支持成功'); console.log(colors.green('\r\n添加 eslint 成功 \r\n')); }) .catch(() => { spinner.fail('添加 eslint 失败'); }); }); function isWorkspace(proPath) { return new Promise((resolve) => { fileUtils .readJSON(path.join(proPath, 'package.json')) .then((proPkgInfo) => { if (proPkgInfo.private === true && proPkgInfo.workspaces != null) { let projectRepository = ''; if (proPkgInfo.repository != null) { const url = new url_1.URL(proPkgInfo.repository.url); const urlAddr = path.parse(url.pathname); projectRepository = `${url.host}${urlAddr.dir}/${urlAddr.name}`; } resolve({ ws: 1, repository: projectRepository, author: proPkgInfo.author }); } else { resolve({ ws: 0 }); } }) .catch(() => { resolve({ ws: -1 }); }); }); } /** * 初始化 fastify app 项目 */ function initFastify(config, deps) { return new Promise((resolve) => { spinner.start('初始化 fastify app 项目'); // 新建存放路径文件的文件夹 fs.mkdir(path.join(config.path, 'routes'), () => { }); // 新建单元测试文件夹 fs.mkdir(path.join(config.path, 'test', 'routes'), { recursive: true }, () => { }); if (config.mongo) { deps.push('mongoose'); deps.push('fastify-mongoose-next'); fs.mkdir(path.join(config.path, 'models'), () => { }); } if (config.static) { // 需要处理静态资源文件, 新建 public 文件夹 fs.mkdir(path.join(config.path, 'public'), () => { }); deps.push('fastify-static'); } if (config.view) { // 需要模板渲染引擎, 新建 views 文件夹 fs.mkdir(path.join(config.path, 'views'), () => { }); deps.push('nunjucks'); deps.push('point-of-view'); } if (config.mysql) { deps.push('mysql2'); deps.push('fastify-knex-sql'); } if (config.session) { deps.push('fastify-cookie'); deps.push('fastify-auth-verify'); deps.push('fastify-pithy-session'); } // 构建 config.js 和 config_demo.js fileUtils.write(path.join(config.path, 'config.js'), `module.exports = {\r\n port: 3000,\r\n mysql: '${config.mysqlConn}',\r\n mongo: '${config.mongoConn}'\r\n};`); fileUtils.write(path.join(config.path, 'config_demo.js'), `module.exports = {\r\n port: 3000,\r\n mysql: '${config.demoMysqlConn}',\r\n mongo: '${config.demoMongoConn}'\r\n};`); // 重新替换 .gitignore fs.readFile(path.join(config.path, '.gitignore'), 'utf-8', (err, gitignoreContent) => { if (err == null) { gitignoreContent += '\r\n.yarn\r\n.pnp.js\r\n.yarnrc.yml\r\nconfig.js'; fileUtils.write(path.join(config.path, '.gitignore'), gitignoreContent); } }); // 复制开发工具配置文件 fs.copyFile(path.join(TEMPLATE_PATH, 'nodemon.json'), path.join(config.path, 'nodemon.json'), () => { }); // 写 server.js 内容 template(path.join(TEMPLATE_PATH, 'fastify_server.txt'), { mysql: config.mysql, mongo: config.mongo, session: config.session, static: config.static, view: config.view, }, path.join(config.path, 'server.js')); // 新建路由文件夹 setTimeout(() => { fs.copyFile(path.join(TEMPLATE_PATH, 'api.js'), path.join(config.path, 'routes/api.js'), () => { }); fs.copyFile(path.join(TEMPLATE_PATH, 'root.js'), path.join(config.path, 'routes/root.js'), () => { }); // 复制单元测试所需文件 fs.copyFile(path.join(TEMPLATE_PATH, '.mocharc.js'), path.join(config.path, '.mocharc.js'), () => { }); fs.copyFile(path.join(TEMPLATE_PATH, 'helper.js'), path.join(config.path, 'test/helper.js'), () => { }); fs.copyFile(path.join(TEMPLATE_PATH, 'api.test.js'), path.join(config.path, 'test/routes/api.test.js'), () => { }); if (config.mongo) { fs.copyFile(path.join(TEMPLATE_PATH, 'mongo_models.js'), path.join(config.path, 'models/index.js'), () => { }); } }, 200); spinner.succeed('初始化fastify app 项目成功'); resolve(0); }); } program .command('fastify <name>') .alias('f') .description('创建 fastify 项目') .option('-e, --eslint', '是否需要 eslint ,默认为:true', true) .option('--no-eslint', '不需要 eslint ') .option('-p, --prettier', '是否需要 prettier 进行格式化,默认为:true', true) .option('--no-prettier', '不需要prettier') .option('-d, --director <director>', '项目的目录地址,默认为:执行命令的目录') .action((name, config) => { let asw1; let mysqlConfig; // mysql 连接选项 let mongoConfig; // mongo 连接选项 let proPath = config.director || process.cwd(); let devs = ['mocha', 'pino-smart', 'nodemon']; let deps = ['fastify']; let isApp = true; let workspaceInfo; enquirer .prompt([ { message: '是否需要处理静态文件?', type: 'confirm', initial: false, name: 'static', }, { name: 'view', type: 'confirm', initial: false, message: '是否需要模板渲染引擎?', }, { name: 'mongo', type: 'confirm', initial: false, message: '是否需要使用 mongoose 数据库?', }, { name: 'mysql', type: 'confirm', initial: false, message: '是否需要使用 mysql 数据库?', }, { name: 'session', type: 'confirm', initial: false, message: '是否需要 session 并加入登录校验?', }, ]) .then((a) => { asw1 = a; if (a.mysql) { return enquirer.prompt([ { header: '-----------------------', name: 'host', type: 'input', message: '请输入mysql数据库host:', }, { name: 'user', type: 'input', message: '请输入mysql数据库user:', }, { name: 'password', type: 'input', message: '请输入mysql数据库password:', }, { name: 'database', type: 'input', message: '请输入mysql数据库database:', }, ]); } else { return Promise.resolve(0); } }) .then((a2) => { if (a2 !== 0) { mysqlConfig = a2; } if (asw1.mongo) { return enquirer.prompt([ { header: '-----------------------', name: 'host', type: 'input', message: '请输入mongodb数据库host:', }, { name: 'user', type: 'input', message: '请输入mongodb数据库user:', }, { name: 'password', type: 'input', message: '请输入mongodb数据库password:', }, { name: 'database', type: 'input', message: '请输入mongodb数据库database:', }, ]); } else { return Promise.resolve(0); } }) .then((a2) => { if (a2 !== 0) { mongoConfig = a2; } return isWorkspace(proPath); // 验证是否是 workspace 项目 }) .then((wsInfo) => { workspaceInfo = wsInfo; if (wsInfo.ws === 0) { console.log(colors.red('目录错误!')); return Promise.reject(new Error('director error')); } else if (wsInfo.ws === -1) { // 非 workspace 项目,新建独立的工程 proPath = path.join(proPath, name); fs.mkdir(proPath, () => { }); return initProject(proPath, { ts: false, license: false, node: true, eslint: config.eslint, prettier: config.prettier, tool: '', }); } else { // workspace 项目 isApp = false; // workspace proPath = path.join(proPath, 'packages', name); fs.mkdir(proPath, () => { }); return Promise.resolve(0); } }) .then(() => initFastify({ ...asw1, path: proPath, mysqlConn: asw1.mysql ? `mysql://${mysqlConfig.user}:${mysqlConfig.password}@${mysqlConfig.host}:3306/${mysqlConfig.database}` : '', demoMysqlConn: asw1.mysql ? `mysql://${mysqlConfig.user}:${mysqlConfig.password}@127.0.0.1:3306/${mysqlConfig.database}` : '', mongoConn: asw1.mongo ? ` mongodb://${mongoConfig.user}:${mongoConfig.password}@${mongoConfig.host}:27017/${mongoConfig.database}` : '', demoMongoConn: asw1.mongo ? `mongodb://${mongoConfig.user}:${mongoConfig.password}@127.0.0.1:27017/${mongoConfig.database}` : '', name, app: isApp, }, deps)) .then(() => { spinner.start('检查依赖……'); return Promise.all([checkDependencies(deps), checkDependencies(devs)]); }) .then((a) => { return template(path.join(TEMPLATE_PATH, isApp ? 'project_package.mtl' : 'workspace_package.mtl'), { name, projectRepository: workspaceInfo.repository, author: workspaceInfo.author, packages: a[1], deps: a[0], scripts: [ { script: '"test": "mocha"', start: true, }, { script: '"dev": "nodemon server.js"', start: false, }, ], }, path.join(proPath, 'package.json')); }) .then(() => { spinner.succeed('依赖检查成功'); spinner.stop(); console.log(colors.green('\r\n项目构建成功, 请按照以下步骤以次执行:\r\n')); const steps = []; if (isApp) { steps.push(` 1. 编辑 ${name} -- > package.json 中 repository 和 author 字段以自动填充工作区初始化`); steps.push(' 2. 如果需要 LICENSE 文件的话请在项目根目录下放置一份,这样后续构建工作区的时候会自动拷贝到每一个工作区'); } else { steps.push(` 1. 完善 packages/${name} --> package.json 文件`); steps.push(` 2. 安装项目依赖`); } console.log(colors.green(steps.join('\r\n'))); }) .catch(() => { spinner.fail('构建失败'); }); }); program.parse(process.argv);