commander.js 制作简易的 MINA CLI 脚手架

出发点并不是小程序本身,是想要做一个脚手架(command-line interface),看过 VUE / REACT 脚手架,觉得很厉害,但是并不太知道里面是怎么做成的,所以最近研究了研究,看能不能自己做成一个 简单的脚手架。

之所以以小程序未入点来开发一个 cli, 是因为最近在做小程序的时候有点不方便, 比如每次新建一个 page module, 都要 mkdir x, touch x.json, x.wxml, x.js, x.wxss, 很麻烦。比如,我查找 bug 的时候习惯自己新建一个最小重复 bug 的新项目。如果每次执行一个命令行就能生成对应的项目骨架就好了。小程序本身有很好的开发工具, 比如选中 自动压缩等,开发工具会自己帮你压缩。所以这些其实不需要额外的工作量。

考虑到:如果能够生成一个 脚手架, 每次都只用执行比如下面的步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 首次安装此 package
npm install mina-cli --save-dev
 
// 首次 init, 自动生成 项目名称, package.json, gitignore 等
mina-cli init
 
// 首次 你需要的modules
mina-cli create user, logger
 
// 下次如果还需要新建一个module,直接
mina-cli create pageModule
 
// 删除某一个 page module
mina-cli delete pageModule

这只是一个简单的想象,具体的实现过程,我总结了如下。项目地址在这里:mina cli


主要使用的技术

  1. nodejs
  2. commander.js
  3. bluebird
  4. fs-extra
  5. inquirer, chalk

其中 fs-extra 是用来简化 对文件操作的 npm 包。 inquirer 可以用来询问用户的需求,并且得到用户的答复。chalk 美化你的 terminal。最后的效果是:

效果图片效果图片


创建步骤

1.创建程序包清单,也就是 package.json ,说明所有的项目依赖。

1
npm init

 

按照 terminal 的提示一步一步来即可。

2.创建一个入口文件, 比如我们在 bin 目录下,新建一个 script.js 文件, 用于最开始的入口。

1
2
#! /user/bin/env node
console.log('this is an entry');

 

此时执行 bin/script.js,可以看到相应的 log 输出。当然这不是我们最后需要的效果。因为我们最好能以一个命令行 代替这个命令,让用户对我们的项目目录透明。

3.添加 bin 字段,package.json 中有一个 bin 字段,可以指定相应的命令。

1
2
3
"bin": {
"minaapp": "./bin/script.js"
}

4.关联 npm 包。此时,你执行 minaapp 还是没有效果的,我们需要关联一下:

1
npm link

 

npm link 的作用是本地安装这个 你自己开发的包。建立关联,简化你的工作流程。

5.输入 minaapp , 就可以运行了。

上面有些地方,比如 #! /user/bin/env node 和 npm link可能不太好理解,这些我在后面有详细的解释。


内容编写

当我们把入口编写好了以后,流程清楚了,就可以编写具体的内容实现。每一个目录结构,或者说内容都要有它存在的理由和方式。我设置的目录结构如下:

项目结构项目结构


入口文件

script.js 是我们的入口文件,作用当然是接受用户的输入命令,比如上面我们 提到的 init 和 create, 收到不同的命令解析后,再执行不同的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#! /usr/bin/env node
 
const program = require('commander');
 
// 定义当前版本
program
.version( require('../package').version);
 
// 初始化项目
program
.command( 'init')
.description( 'Generate a new project')
.alias( 'i')
.action( () => {
require('../command/init')()
});
 
// 新建modules
program
.command( 'create')
.description( 'Generate new modules')
.alias( 'c')
.action( () => {
require('../command/create')()
});
 
program.parse(process.argv);
 
if(!program.args.length){
program.help()
}

这里我使用了 tj 的 commander.js 来接受用户的输入。 比如 init 和 create 的时候,去到相应 command 目录下的不同入口, 执行不同的内容。注意这里一定要执行一下 parse, 并且最好当用户没有输入正确命令时,给用户一个 提示信息。这里用下 program.help()


命令分支

入口文件其中一个作用就是内容分发。比如 init 命令,到 command/init 下面去执行, create 命令 到 command/create 中执行。command 目录就是具体的命令目录。 如果以后扩展是不是也很方便, 比如增加一个 del 模块。先看下 init 和 create 都执行什么内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 
var path = require('path');
 
var inquirer = require('inquirer');
var fs = require('../lib/file');
var questions = require('../lib/questions');
 
var config = require('../lib/config');
 
module.exports = function () {
// 清空屏幕并显示 npm 信息
fs.showNpmInfo();
 
// 等待用户输入项目信息
inquirer
.prompt(questions.question1)
.then( (args) => {
let dir = path.resolve(process.cwd(), './' + args.appName);
 
// 将 appName 写入配置文件
fs.outPutFile( './lib/config.json', JSON.stringify({
appPath: dir
})).then( flag => {
if(flag) fs.showCreateDirInfo('begin');
});
 
// 保证有用户输入的目录,并开始复制模板进入项目
fs.ensureDir(dir)
.then( (flag) => {
return flag == true
? fs.copy( './template', dir)
: Promise.reject(flag);
 
}).then( (flag) => {
//...
})
.catch( err => {
console.log('err', err);
});
});
};

在执行 minaapp init 的时候,首先接受一个用户的参数,这里用到 inquirer, inquirer 很方便的得到用户的输入结果。question 是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const question1 = [
{
name: 'appName',
type: 'input',
message: 'Enter your App\'s name: ',
validate( value ) {
if (value.length) {
return true;
} else {
return 'Please Enter your App\'s name';
}
}
}
];

 

用户输入项目名称,然后我利用 args 接受到对应的参数。得到后,再创建项目目录。创建目录的过程就是创建项目结构的时候,这个时候,因为模板内容比较多,我就直接放在了npm 包里,然后直接把目录拷贝过来就可以了。 所以如果有的项目团队有自己的项目结构,可以直接修改 template 里面的目录,执行目录可以直接修改。每次创建出来的目录和 template 中的目录相同。


其他模块

执行 create 目录当然就是 创建 pages 中的内容,这里肯定就比较容易,用下我们自己封装的 fs 文件,然后创建对应的 json,wxml,wxss,js 文件即可。

lib 目录中可以定义你自己的库。比如我定义的 file.js 封装了一层 promise。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
module.exports = {
 
ensureDir(dir) {
return new Promise ((resolve, reject) => {
fs.ensureDir(dir, err => {
if(!err) resolve(true);
 
reject(err);
})
})
},
 
copy( from ,to) {
return new Promise((resolve, reject) => {
fs.copy( from ,to, err => {
if(!err) resolve(true);
 
reject(err);
})
})
},
 
 
//.....
}

大概的过程就是上面这样,这里只是一个简单的流程说明。记录下开发的过程。具体的代码地址可以在sevencai’s mina cli中看到。


几点说明理解

上面创建项目的过程中是一个简单的过程, 中间有几点想解释下:


#! /user/bin/env node

我们刚刚在代码的开头第一行,写了 #! /user/bin/env node ,这是为了指定我们的脚本执行所需要的解释程序。我们这里是利用的 nodejs, 所以使用 node 来作为脚本的解释程序,而#! /usr/bin/env node这样写的目的是使用env来找到node,并使用node来作为程序的解释程序。

那这里的 env 是啥 ? env 中规定了 很多系统的环境变量, 包括安装的一些环境路径等。不同的操作系统, node 的安装路径不一定相同, 但是环境变量都会存在 env 里, 所以我们这里可以用 env 来找到 node, 从而用 node 作为解释程序。 总结下,这里的 env 就是为了让我们的脚本在不同的操作系统能够上都可以被正常的解释和执行。


npm link 也是我这次新学到的东西。我看到了这样一篇文章:npm命令解析,觉得讲的很清晰明了。下面我把这段文字摘录下来。感谢。

开发Npm模块的时候,有时我们会希望,边开发边试用。但是,常规情况下,使用一个模块,需要将其安装到node_modules目录之中,这对于开发中的模块,显然非常不方便。npm link就能起到这个作用,建立一个符号链接,在全局的node_modules目录之中,生成一个符号链接,指向模块的本地目录。

为了理解npm link,请设想这样一个场景。你开发了一个模块myModule,目录为src/myModule,你自己的项目myProject要用到这个模块,项目目录为src/myProject。每一次,你更新myModule,就要用npm publish命令发布,然后切换到项目目录,使用npm update更新模块。这样显然很不方便,如果我们可以从项目目录建立一个符号链接,直接连到模块目录,就省去了中间步骤,项目可以直接使用最新版的模块

具体的使用方法是:

1.首先,在模块目录(src/myModule)下运行npm link命令。

2.src/myModule$ npm link 上面的命令会在Npm的全局模块目录内,生成一个符号链接文件,该文件的名字就是package.json文件中指定的文件名。

3./path/to/global/node_modules/myModule -> src/myModule 这个时候,已经可以全局调用myModule模块了。但是,如果我们要让这个模块安装在项目内,还要进行下面的步骤。

4.切换到项目目录,再次运行npm link命令,并指定模块名。

5.src/myProject$ npm link myModule 上面命令等同于生成了本地模块的符号链接。

6.src/myProject/node_modules/myModule -> /path/to/global/node_modules/myModule 然后,就可以在你的项目中,加载该模块了。

7.var myModule = require(‘myModule’); 这样一来,myModule的任何变化,都可以直接反映在myProject项目之中。但是,这样也出现了风险,任何在myProject目录中对myModule的修改,都会反映到模块的源码中。

8.如果你的项目不再需要该模块,可以在项目目录内使用npm unlink命令,删除符号链接。

9.src/myProject$ npm unlink myModule


总结

了解了创建一个 cli 的简单流程,了解了一些常用的 npm 库。知道了 npm link 是干什么用的, 以后创建小程序也更简单方便了。项目整体的功能很简单,我也不准备再多做别的命令解析,因为学习目的已经达到了。下面推荐一些阅读的文章, 感谢:

1.command-line-with-node
2.javascript-command-line-interface-cli-node-js
3.npm命令解析
4.nodejs 中相对路径和绝对路径
5.msh-using-npm-manage-node.js-dependence
6.前端扫盲-之打造一个Node命令行工具

转载于:https://www.cnblogs.com/sunshq/p/7574926.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值