需要用到的包
1.commander:解析用户命令行输入和参数,简化简化命令行开发。
- command -- 定义命令行指令,后面可跟上一个name,用空格隔开,如 .command( 'app [name] ')
- alias -- 定义一个更短的命令行指令 ,如执行命令 $ app m 与之是等价的
- description -- 描述,它会在help里面展示
- option -- 定义参数。它接受四个参数。 第一个参数中,它可输入短名字 -a和长名字--app ,使用 | 或者 , 分隔,在命令行里使用时,这两个是等价的,区别是后者可以在程序里通过回调获取到;
第二个为描述, 会在 help 信息里展示出来;
第三个参数为回调函数,他接收的参数为一个string,有时候我们需要一个命令行创建多个模块,就需要一个回调来处理; 第四个参数为默认值
- action -- 注册一个callback函数,这里需注意目前回调不支持let声明变量
- parse -- 解析命令行 2.chalk:美化命令行的模块。
3.download-git-repo:来下载github库的代码
download(repository, destination, options, callback)
repository 是远程仓库地址;
GitHub - github:owner/name 或者简写为 owner/name github上面的项目只需要 用户名/仓库名
GitLab - gitlab:owner/name
Bitbucket - bitbucket:owner/name
destination 是存放下载的文件路径,也可以写文件名,默认当前目录; options 是选项,比如 { clone:boolean } 表示用 http download 还是 git clone
4.fs-extra:fs-extra是fs的一个扩展,提供了非常多的便利API,并且继承了fs所有方法和为fs方法添加了promise的支持。它应该是 fs 的替代品
5.inquirer:命令行进行交互,通用的命令行用户界面集合,用于和用户进行交互:blog.csdn.net/qq_26733915…
{
type: "input|number|confirm|list|rawlist|expand|checkbox|password|editor".//默认值为输入:input
name: String,//此问题的id,在后面查找此问题的结果时可以直接使用options.example取到此问题的值
message:String | Function,//要打印的问题。如果定义为函数,则第一个参数将是当前查询者会话答案。缺省值为name(后跟冒号)
default:String | Number | Boolean | Array | Function,//如果未输入任何内容,则使用默认值,或者返回默认值的函数。如果定义为函数,则第一个参数将是当前查询者会话答案,
choices:Array|Function,//(Array | Function)Choices数组或返回choices数组的函数。如果定义为函数,则第一个参数将是当前查询者会话答案。数组值可以是simple numbers,strings或objects包含name(显示在列表中),value(保存在上面讲的name中和short(选择后显示)属性
validate:Function,//校验输入的值是否符合要求
filter:Function,//接收用户输入并返回要在程序内部使用的过滤值,过滤的值将会返回到`Answers `中,询问用户是否正确
when:Boolean|Function,//接收当前用户的答案哈希,并应返回true或false取决于是否应询问此问题
pageSize:Number,//当使用list,rawList,expand,checkbox的时候可能存在分页
prefix:String,//更改默认的前缀消息
suffix:String//更改默认的后缀消息
}
6.node-emoji:输入 表情符
7.ora:node.js 命令行环境的 loading效果, 和显示各种状态的图标(美观)下载的时候会有转圈特效
8.read-pkg:读取项目的package.json文件
9.validate-npm-package-name:验证包名是否合法
开始搭建(练手)
1、创建lp-cli文件夹
2、在该目录下执行npm init -y,生成package.json文件
3、安装所需要的包
npm i commander chalk download-git-repo fs-extra inquirer ora read-pkg validate-npm-package-name --save
4、在package.json文件中新建一个bin文件夹,在bin文件夹下新建一个文件lp.js,这个文件夹就是我们脚手架的入口文件,然后写几句代码:
#!/usr/bin/env node
console.log('hello')
注意:#!/usr/bin/env node 表明这是一个可执行的应用,它帮助脚本找到node的脚本解释器,可以理解为调用系统中的node来解析我们的脚本,当 系统看到这行时,能够沿着该路径查找node并执行。所以需要执行的脚本文件第一行都必须写上这句,否者会有错误提示。
在终端执行lp.js: node ./bin/lp.js
5、输入node ./bin/lp执行太过麻烦,我们可以在package.json里定义一个bin命令,bin用来指定每个命令所对应的可执行文件地位置:
然后执行npm link。它将会把lp这个字段复制到npm的全局模块安装文件夹node_modules内,并创建符号链接(symbolic link,软链接),也就是将 lp 的路径加入环境变量 PATH
最后执行 lp和执行 node ./bin/lp命令结果一样:
6、完善入口文件lp.js:在这里面定义command命令
#!/usr/bin/env node
const program = require('commander')
// 定义当前版本
// 定义使用方法
// 定义四个指令
program.version(require('../package.json').version)
.usage('<command> [options]')
.command('add', 'add a new template')
.command('init', 'generate a new project from a template')
// 解析命令行参数
program.parse(process.argv)
定义了两个命令分别是lp add和lp init,但是现在系统只认识lp,所以也要在package.json加入add和init命令以及他们的入口文件:
"bin": {
"lp": "./bin/lp.js",
"lp-add":"./bin/lp-add.js",
"lp-init":"./bin/lp-init.js"
},
最后npm unlink解绑一下,再执行npm link重新绑定命令。
7、在根目录下新建一个template.json文件,内容为{},作为我们存放模版的仓库。
8、lp-add.js
// 交互式命令行
const inquirer = require("inquirer");
// 修改控制台字符串的样式
const chalk = require("chalk");
// node 内置文件模块
const fs = require("fs");
// 读取根目录下的 template.json
const tplObj = require(`${__dirname}/../template`);
// 自定义交互式命令行的问题及简单的校验
let question = [
{
name: "name",
type: "input",
message: "请输入模板名称",
// 验证输入的模板名称
validate(val) {
if (val === "") {
return "Name is required!";
} else if (tplObj[val]) {
return "Template has already existed!";
} else {
return true;
}
},
},
{
name: "url",
type: "input",
message: "请输入模板地址",
validate(val) {
if (val === "") return "The url is required!";
return true;
},
},
{
type: "list",
name: "testchoice",
message: "What do you want to do?",
choices: [
{
name: "PC",
value: "pc",
short: "PC",
},
{
name: "mobile",
value: "mobile",
short: "mobile",
},
],
},
{
type: "list",
name: "pcchoice",
message: "what type do you want to use pc?",
//when据前面问题的回答,判断当前问题是否需要被回答;// 当testchoice为pc的时候才会提问当前问题
when(answer) {
return answer.testchoice === "pc";
},
choices: [
{
name: "PC",
value: "pc",
short: "PC",
},
{
name: "Responsive",
value: "responsive",
short: "Responsive",
},
],
},
];
inquirer.prompt(question).then((answers) => {
console.log("answers", answers);
// answers 就是用户输入的内容,是个对象
let { name, url } = answers;
// 过滤 unicode 字符
tplObj[name] = url.replace(/[\u0000-\u0019]/g, "");
// 把模板信息写入 template.json 文件中
fs.writeFile(
`${__dirname}/../template.json`,
JSON.stringify(tplObj),
"utf-8",
(err) => {
if (err) console.log(err);
console.log("\n");
console.log(chalk.green("Added successfully!\n"));
console.log(chalk.grey("The latest template list is: \n"));
console.log(tplObj);
console.log("\n");
}
);
});
// 其实当我们执行moe add时,commander会尝试在入口文件的目录内寻找可执行文件,找到形如program-command的命令来执行(moe-add)
注意:这里的模版地址,不需要写全部链接,等会根据模板地址去下载项目时,download-git-repo默认去github上下载 如果项目地址是github.com/CavinHuang/… 模板地址只需要:CavinHuang/node-cli-demo
执行:lp add:入口文件定义program.command('add').action(() => {})的时候没有写action这个回调函数,为什么能够执行lp add? 其实当我们执行lp add时,commander会尝试在入口文件的目录内寻找可执行文件,找到形如program-command的命令来执行(lp-add),即找入口文件的可执行文件作为执行这个命令的回调函数
这个时候template.json中也会加上:node-cli-demo :CavinHuang/node-cli-demo
lp-init.js
#!/usr/bin/env node
const program = require("commander");
const chalk = require("chalk");
const ora = require("ora");
const download = require("download-git-repo");
const tplObj = require(`${__dirname}/../template`);
program.usage("<template-name> [project-name]");
program.parse(process.argv);
if (program.args.length < 1) return program.help();
// 好比 vue init webpack project-name 的命令一样,第一个参数是 webpack,第二个参数是 project-name
let templateName = program.args[0];
let projectName = program.args[1];
if (!tplObj[templateName]) {
console.log(chalk.red("\n Template does not exit! \n "));
return;
}
if (!projectName) {
console.log(chalk.red('\n Project should not be empty! \n '))
return
}
url = tplObj[templateName]
console.log("url", url)
console.log(chalk.white('\n Start generating... \n'))
// 出现加载图标
const spinner = ora("Downloading...");
spinner.start();
// 执行下载方法并传入参数
// download(repository, destination, options, callback)
// download第一个参数是github:
// repository 是远程仓库地址;destination 是存放下载的文件路径,也可以写文件名,默认当前目录;options 是选项,比如 { clone:boolean } 表示用 http download 还是 git clone 。
// 比如远程地址是https://github.com/yangzicheng/command-line repository只要是yangzicheng/command-line
download(
url,
projectName,
err=>{
if(err){
spinner.fail();
console.log(chalk.red(`Generation failed. ${err}`))
return
}
// 结束加载图标
spinner.succeed();
console.log(chalk.cyan('\n Generation completed!'))
console.log(chalk.cyan('\n To get started'))
console.log(chalk.cyan(`\n cd ${projectName} \n`))
}
)
执行lp init node-cli-demo demo:
这个时候在当前目录增加了demo文件夹,里面内容是github.com/CavinHuang/…
发布
1、npm login
2、npm pubilsh (去npm上查询是否存在此包名)
使用
npm i -g (包名)
使用命令:lp、lp add、lp init
比较全面的,完整的项目:github.com/lppwlwzj/pr…