从零搭建一个简单的脚手架工具,像vue-cli
一样,一个命令就能变出一个完整结构的初始项目。流行的脚手架工具有很多实用的功能,这里要说的是最基本的一项:通过一个命令快速创建出初始项目。
创建空目录simple-cli
并初始化:
$ mkdir simple-cli && cd simple-cli && yarn init -y
安装相关依赖:
在simple-cli/
:
$ yarn add commander co co-prompt copy-template-dir
在package.json
中添加bin
:
{
"name": "simple-cli",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"co": "^4.6.0",
"co-prompt": "^1.0.0",
"commander": "^2.17.1",
"copy-template-dir": "^1.4.0"
},
"bin": {
// 需要作为命令行调用,要添加 bin
"simple-cli": "./index.js"
}
}
创建相应文件,最后目录结构可以是这样的:
.
├── index.js
├── lib
│ └── init.js
├── node_modules
├── package.json
└── yarn.lock
可以直接使用node ./index.js
进行运行,也可以使用yarn link
后直接通过simple-cli
进行使用(yarn unlink
可以进行取消)。
index.js
:
#!/usr/bin/env node
// 因为是作为命令行调用的,在第一行加上 #!/usr/bin/env node
"use strict";
const program = require("commander");
const packageJSON = require("./package.json");
const init = require("./lib/cmd/init");
program.version(packageJSON.version).usage("<command> [options]");
program
.command("init")
.description("创建新新项目")
.alias("i")
.action(() => {
// 接收到 init 命令执行 init() 方法
init();
});
program.parse(process.argv);
if (program.args.length == 0) {
// 这里是处理没有输入参数命令的时候,显示 help
program.help();
}
./lib/init.js
:
"use strict";
// 导出一个方法
module.exports = () => {};
执行simple-cli
:
$ simple-cli
Usage: simple-cli <command> [options]
Options:
-V, --version output the version number
-h, --help output usage information
Commands:
init|i 创建新新项目
如
yarn link
之后使用simple-cli
提示permission denied: simple-cli
,那么需要对 linked file 执行chmod +x ./index.js
。
根目录添加templates/templateA/
,最终的目录结构:
.
├── index.js
├── lib
│ └── init.js
├── node_modules
├── package.json
├── templates
│ └── templateA
└── yarn.lock
简单添加一个模板templateA
:
.
├── CONTRIBUTING.md
├── README.md
├── _package.json
├── nwb.config.js
├── src
└── tests
# 注意:
# 1. _package.json,_ 开头的文件通过 copy-template-dir 拷贝后会自动删除 _。
# 2. 文件中类似 {{变量名}} 的地方可通过 copy-template-dir 自动注入信息。
init.js
"use strict";
const { exec } = require("child_process");
const co = require("co");
const prompt = require("co-prompt");
const copy = require("copy-template-dir");
const path = require("path");
module.exports = () => {
co(function*() {
// 处理用户输入
const projectName = yield prompt("Project name: ");
const description = yield prompt("Description: ");
// 执行 simple-cli 时所处的路径
const currentPath = path.resolve(process.cwd(), "./");
// 模板路径
const templateDir = path.resolve(__dirname, "../templates/templateA");
// 输出路径
const outDir = path.resolve(currentPath, projectName);
// 粘贴模板时注入的信息
const values = {
name: projectName,
description
};
// 拷贝模板
copy(path.resolve(templateDir), outDir, values, (err, createdFiles) => {
if (err) {
process.exit(1);
}
createdFiles.forEach(filePath => console.log(`Created ${filePath}`));
// Node.js 执行命令
const cmdStr = `cd ${outDir} && yarn`;
console.log(`now excuting: ${cmdStr}`);
exec(cmdStr, error => {
if (error) {
process.exit(1);
}
console.log("\n √ Generation completed!");
process.exit(0);
});
});
});
};
$ simple-cli init
Project name: test
Description: some description
Created /Users/daief/Desktop/test/.travis.yml
Created /Users/daief/Desktop/test/README.md
Created /Users/daief/Desktop/test/CONTRIBUTING.md
Created /Users/daief/Desktop/test/package.json
Created /Users/daief/Desktop/test/nwb.config.js
Created /Users/daief/Desktop/test/tests/.eslintrc
Created /Users/daief/Desktop/test/tests/App-test.js
Created /Users/daief/Desktop/test/src/App.css
Created /Users/daief/Desktop/test/src/App.js
Created /Users/daief/Desktop/test/src/index.css
Created /Users/daief/Desktop/test/src/index.html
Created /Users/daief/Desktop/test/src/index.js
Created /Users/daief/Desktop/test/src/react.svg
now excuting: cd /Users/daief/Desktop/test && yarn
√ Generation completed!
一个炒鸡简单的脚手架就完成了,还同时了解了如何通过 Node.js 构建一个交互式的命令行工具。
另,还可以添加chalk来美化命令行的输出,又或是发布到 npm 分享给小伙伴使用。
项目地址:https://github.com/daief/simple-cli。
Thanks. 😃