初始化项目
首先是先创建一个文件夹, 然后用npm初始化项目生成package.json
$ mkdir my-node-cli
$ cd my-node-cli
$ npm init # 生成 package.json 文件
package.json 里名字可以起项目想要的名字,比如我起的就是yodaoCli
main是你要执行的脚本
bin这个可以自定义脚本,比如我想要的框架执行是 yodao create 项目名 然后生成一个项目
那么就写成
"bin": {
"yodaocli": "./index.js"
},
index.js是你要执行的内容,可在根目录新生成一个空js文件,然后里面写
#!/usr/bin/env node
console.log('测试cli11111')
#!/usr/bin/env node ,简单的理解,就是输入命令后,会有在一个新建的 shell 中执行指定的脚本,在执行这个脚本的时候,我们需要来指定这个脚本的解释程序是 node。
然后在执行npm link,把项目链接到本地,就可以直接用命令行执行 yodao打印出你的conosole了
配置命令行选项
这里使用的是commander.js
yarn add commander --save
// index.js 中
program.option('-ig,--initgit', 'init git');
console.log('Options: ', program.opts()); // 可以得到选项值
第一个命令
/** 主程序 **/
const { program } = require('commander');
program
// .command()用于配置命令及参数,其中<>表示参数是必须的,[]表示参数是可选的;
.command('create <name> [destination]')
// .description()添加命令描述
.description('create a project')
// .action()用于添加操作函数,入参就是配置命令时候的参数
.action((name, destination) => {
handleCreate({ name, destination }, program.opts());
});
program.option('-ig,--initgit', 'init git');
program.parse(process.argv);
创建用户交互
这里用户交互使用的是 inquirer.js, 根据官网的描述, 我们想要使用require方法引入的话,就不能使用v9以上的inquirer,所以这里推荐安装v8.0.0
/** 用户交互 **/
const inquirer = require('inquirer')
const handleCreate = (params, options) => {
console.log(55555, params, options);
inquirer
// 用户交互
.prompt([
{
type: 'input',
name: 'author',
message: 'author name?'
},
{
type: 'list',
name: 'template',
message: 'choose a template',
choices: ['tpl-1', 'tpl-2']
}
])
.then((answers) => {
//根据回答以及选项,参数来生成项目文件
console.log(111, answers)
genFiles({ ...answers, ...params, ...options });
})
.catch((error) => {
console.error(error);
});
};
这里面answers就是上面的回答,是个对象形式。比如我会获得{author: 'yodao2', template: 'tpl-1'}
拿到了用户选择的配置以后,我们就可以初始化项目文件
按需生成项目文件
这里用到了metalsmith和path, 可以
yarn add metalsmith path然后编写生成文件的逻辑
/** 按需生成项目文件 **/
// 获得命令运行时所在的路径
const getCwd = () => process.cwd();
// 这里用到Metalsmith,可以很方便地复制文件到指定目录
const Metalsmith = require('metalsmith');
const path = require('path');
const genFiles = (options) => {
// 模版的目录 options.template就是上面用户交互的时候选择的模板文件夹
const templateSrc = path.resolve(__dirname, `./template/${options.template}`);
// 项目指定生成目录,如果命令中没有有配置目录,则在当前命令运行的目录下生成以项目名称为名字的新目录
const destination = options.destination
? path.resolve(options.destination)
: path.resolve(getCwd(), options.name);
Metalsmith(__dirname)
.source(templateSrc)
.destination(destination)
.build((err) => {
if (err) {
console.error(err);
}
});
};
// .source()和.destination()分别配置复制源目录和目标目录,最好使用绝对路径
在根目录新建目录及文件
+-- template
| +-- tpl-1
| +-- index1.js
| +-- tpl-2
| +-- index2.js
然后就可以执行命令yodao create yodao1,一路选择下来就能看到youdao1文件夹已经创建在命令行指定的目录,里面含有你想要复制的指定文件夹里面里的文件。
模板语言替换
首先你要在声明需要替换的文件,如renderPathList所写的文件名,数组里可以有多个文件。
安装模板语言依赖yarn add ejs
其次你要替换的文件里需要有模板语,比如在上面,你声明了项目名称,和作者名,你要复制的package.json里需要用模板语言声明:
"name": "<%= name %>",
"author": "<%= author %>",
然后就是在destination和build过程中做替换行为
/** 按需生成项目文件 **/
// 获得命令运行时所在的路径
const getCwd = () => process.cwd();
// 这里用到Metalsmith,可以很方便地复制文件到指定目录
const Metalsmith = require('metalsmith');
const path = require('path');
const ejs = require('ejs');
const genFiles = (options) => {
// 模版的目录 options.template就是上面用户交互的时候选择的模板文件夹
const templateSrc = path.resolve(__dirname, `./template/${options.template}`);
// 项目指定生成目录,如果命令中没有有配置目录,则在当前命令运行的目录下生成以项目名称为名字的新目录
const destination = options.destination
? path.resolve(options.destination)
: path.resolve(getCwd(), options.name);
// 需要动态生成的文件, 是个数据,可以填多个
const renderPathList = ['package.json']
/** 修改模板文件 **/
Metalsmith(__dirname)
.source(templateSrc)
.destination(destination)
.use((files) => {
Object.keys(files).forEach((key) => {
// 指定的文件动态生成
if (renderPathList.includes(key)) {
const file = files[key];
// 原内容
const str = file.contents.toString();
// 新内容
const newContents = ejs.render(str, options);
// 将新内容写到文件中
file.contents = Buffer.from(newContents);
}
});
})
.build((err) => {
if (err) {
console.error(err);
}
});
};
// .source()和.destination()分别配置复制源目录和目标目录,最好使用绝对路径
上传
上传前记得切换回npm源
npm config set registry https://registry.npmjs.org
npm login // 登录你在npm的账号
npm publish // 上传, 非首次上传记得改版本号
如果想上传到npm组,需要强制public
npm publish --access public
使用
npm install -g yodaocli
youdaocli create <项目名>