@jdbk/book-cli
Version:
Command line interface for front end project
194 lines (168 loc) • 6.49 kB
JavaScript
const chalk = require('chalk');
// const execa = require("execa");
const semver = require('semver'); // 语义化版本规范
const leven = require('leven'); // 测量两字符串之间的差异;
const program = require('commander');
const slash = require('slash'); // 转换 Windows 反斜杠路径转换为正斜杠路径 \ => /
// const minimist = require('minimist'); // 解析命令行选项
const requiredVersion = require('../package.json').engines.node;
// 检查Node版本
function checkNodeVersion(wanted, id) {
if (!semver.satisfies(process.version, wanted, {
includePrerelease: true,
})) {
console.log(chalk.red(
`You are using Node ${process.version}, but this version of ${id
} requires Node ${wanted}.\nPlease upgrade your Node version.`,
));
process.exit(1);
}
}
checkNodeVersion(requiredVersion, '@book/cli');
const EOL_NODE_MAJORS = ['8.x', '9.x', '11.x', '13.x'];
// eslint-disable-next-line no-restricted-syntax
for (const major of EOL_NODE_MAJORS) {
if (semver.satisfies(process.version, major)) {
console.log(
chalk.red(
// eslint-disable-next-line no-useless-escape
`You are using Node ${process.version}.\n Node.js ${major} has already reached end-of-life and will not be supported in future major releases.\n It\'s strongly recommended to use an active LTS version instead.`,
),
);
}
}
// 创建测试存储库时进入调试模式
if (slash(process.cwd()).indexOf('/packages/test') > 0) {
process.env.BOOK_CLI_DEBUG = true;
}
function camelize(str) {
return str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : ''));
}
// 将命令对象本身作为选项传递,只将实际选项提取到新对象中。
function cleanArgs(cmd) {
const args = {};
cmd.options.forEach((o) => {
const key = camelize(o.long.replace(/^--/, ''));
// 如果选项不存在且命令具有同名的方法,则不复制
if (typeof cmd[key] !== 'function' && typeof cmd[key] !== 'undefined') {
args[key] = cmd[key];
}
});
return args;
}
// 指令建议
function suggestCommands(unknownCommand) {
// eslint-disable-next-line no-underscore-dangle
const availableCommands = program.commands.map((cmd) => cmd._name);
let suggestion;
availableCommands.forEach((cmd) => {
const isBestMatch = leven(cmd, unknownCommand) < leven(suggestion || '', unknownCommand);
if (leven(cmd, unknownCommand) < 3 && isBestMatch) {
suggestion = cmd;
}
});
if (suggestion) {
console.log(` ${chalk.red(`Did you mean ${chalk.yellow(suggestion)}?`)}`);
}
}
/**
* init:初始化项目
* list: 展示模板列表
* create:创建一个模板/generator
* add:添加模板
* */
// 基础信息
program
.version(require('../package.json').version)
.usage('<command> [options]');
// list: 展示模板列表
program
.command('list')
.description('look all template list')
// .option('-d, --default', 'Skip prompts and use default preset')
.action(() => {
// eslint-disable-next-line global-require
require('../lib/list');
});
// init:初始化项目
program
.command('init <template-name> [project-name]')
.description('init a new project powered by ')
// .option('-d, --default', 'Skip prompts and use default preset')
.action((tplName, projectName = '', cmd) => {
// console.log('tplName=', tplName);
// console.log('projectName=', projectName);
const options = cleanArgs(cmd);
// console.log('cmd=', options);
// eslint-disable-next-line global-require
require('../lib/init')(tplName, projectName, options);
});
// create:创建一个模板/generator
program
.command('init <app-name>')
.description('create a new project powered by vue-cli-service')
.option('-p, --preset <presetName>', 'Skip prompts and use saved or remote preset')
.option('-d, --default', 'Skip prompts and use default preset')
.option('-i, --inlinePreset <json>', 'Skip prompts and use inline JSON string as preset')
.option('-m, --packageManager <command>', 'Use specified npm client when installing dependencies')
.option('-r, --registry <url>', 'Use specified npm registry when installing dependencies (only for npm)')
.option('-g, --git [message]', 'Force git initialization with initial commit message')
.option('-n, --no-git', 'Skip git initialization')
.option('-f, --force', 'Overwrite target directory if it exists')
.option('--merge', 'Merge target directory if it exists')
.option('-c, --clone', 'Use git clone when fetching remote preset')
.option('-x, --proxy <proxyUrl>', 'Use specified proxy when creating project')
.option('-b, --bare', 'Scaffold project without beginner instructions')
.option('--skipGetStarted', 'Skip displaying "Get started" instructions')
.action((name, cmd) => {
const options = cleanArgs(cmd);
// if (minimist(process.argv.slice(3))._.length > 1) {
// console.log(chalk.yellow('\n Info: You provided more than one argument. The first one will be used as the app\'s name, the rest are ignored.'));
// }
// --git使commander默认git为true
if (process.argv.includes('-g') || process.argv.includes('--git')) {
options.forceGit = true;
}
// eslint-disable-next-line global-require
require('../lib/create')(name, options);
});
// add:添加模板/generator
program
.command('add <template-name>')
.description('add a new template')
// .option('-d, --default', 'Skip prompts and use default preset')
.action((tplName, cmd) => {
const options = cleanArgs(cmd);
// eslint-disable-next-line global-require
require('../lib/add')(tplName, options);
});
// egg: 彩蛋
program
.command('egg')
.description('this is Easteregg!')
.action(() => {
// eslint-disable-next-line global-require
require('../lib/egg');
});
// 输出未知命令的帮助信息
program
.arguments('<command>')
.action((cmd) => {
program.outputHelp();
console.log(` ${chalk.red(`Unknown command ${chalk.yellow(cmd)}.`)}`);
console.log();
suggestCommands(cmd);
});
// 在帮助中添加一些有用的信息
program.on('--help', () => {
console.log();
console.log(` Run ${chalk.cyan('book <command> --help')} for detailed usage of given command.`);
console.log();
});
program.commands.forEach((c) => c.on('--help', () => console.log()));
program.parse(process.argv);
// 调用
if (!process.argv.slice(2).length) {
program.outputHelp();
}