1. 步骤
- 创建项目文件夹 create-web-frame
- 在 create-web-frame 目录下执行 npm init -y 创建 package.json
- 安装 commander、inquirer、execa、fs-extra 依赖包
- 在 create-web-frame 文件夹下新建一个 bin 目录
- 进入 bin 目录创建 create-web-frame.js文件
- 编写 node.js 代码搭建cli工具
- 发布到npm(省略)
2. 用到的库的简介
- commander
作用:用于解析progress.argv
- process.argv
process.argv 属性返回数组,其中包含启动 Node.js 进程时传入的命令行参数。 第一个 元素将是 process.execPath。 如果需要访问 argv[0] 的原始值,请参阅 process.argv0。第二个元素将是正在执行的 JavaScript 文件的路径。 其余元素将是任何其他命令行参数。
通过执行node create-web-frame.js打印console.log(process.argv)值如下:
[ 'C:\\Program Files\\nodejs\\node.exe', 'C:\\Users\\86135\\Downloads\\create-web-frame\\bin\\create-web-frame.js', 'app', '--port', '3000']
- inquirer
作用:实现交互式命令行用户界面
- execa
作用:可以调用shell和本地外部程序的javascript封装。会启动子进程执行。
- fs-extra
作用:fs-extra 是 fs 的一个扩展,提供了非常多的便利 API,并且继承了 fs 所有方法和为 fs 方法添加了 promise 的支持。它应该是 fs 的替代品。
-
process.cwd() 方法返回 Node.js 进程的当前工作目录。
-
#!/usr/bin/env node:在写npm包的时候须要在脚本的第一行写上#!/usr/bin/env node ,用于指明该脚本文件要使用node来执行
3. 完整代码
#!/usr/bin/env nodeconst { program } = require("commander");const inquirer = require("inquirer");const execa = require("execa");const fse = require("fs-extra");/** * 引入package.json 文件,需要用到其中的 version 字段 */const packageJson = require("../package.json");program.version(packageJson.version) .argument("[web-server-name]", "web server 应用名称,英文、数字、_、-组成") .option("-f --force", "当项目已存在的时候强制创建项目") .option("-p --port <port>", "项目端口", "8888") .action(async (name, opts, command) => { const options = { rootDirectory: process.cwd(), // Current Work Directory serverName: "app", dependencies: ["koa", "nodemon", "minimist"] // 默认安装的依赖 };
// 问答内容
let promptOpts = [ { type: "checkbox", name: "middlewares", message: "请选择要安装的中间件", choices: ["koa-static-cache", "koa-router", "koa-body"], default: ["koa-static-cache", "koa-router"], } ]; if(name) { options.serverName = name; } else { promptOpts.unshift({ type: "input", name: "serverName", message: "请输入应用名称", default: "app", }) } if(opts) { options.serverPort = opts.port; } else { promptOpts.unshift({ type: "input", name: "serverPort", message: "请输入应用端口", default: 8888, }) }
// res 为用户选择后的返回值
const res = await inquirer.prompt(promptOpts); options.serverName = res.serverName; options.serverPort = res.serverPort; options.rootDirectory += `/${res.serverName}`; options.dependencies = [...options.dependencies, ...res.middlewares]; // 创建目录 fse.mkdirsSync(options.rootDirectory); // 初始化 const cmdInit = `npm init -y`; execa.commandSync(cmdInit, { cwd: options.rootDirectory, }); // 安装 const cmdInstall = `npm install ${options.dependencies.join(" ")}`; execa.commandSync(cmdInstall, { cwd: options.rootDirectory, stdio: ["inherit", "inherit", "inherit"] }); // 创建文件 const content = ` const minimist = require('minimist'); const Koa = require('koa'); const argv = minimist(process.argv.slice(2)); const app = new Koa(); app.use((ctx, next) => { ctx.body = 'Hello'; }); app.listen(argv.port, () => { console.log(\`服务启动成功:http://localhost:\${argv.port}\`); });`; const entryFile = options.rootDirectory + "/app.js"; fse.outputFileSync(entryFile, content, { encoding: "utf-8", }); });program.parse(process.argv);