与脚手架大战:回合2
youngwind opened this issue · 0 comments
youngwind commented
再接再厉
继回合1 #46 之后,开始准备回合2。这次我重新整理了一下思路。
- 我要实现的是一个简单的脚手架生成器,它只需要能够生成我指定的那两三种项目。
- 生成的方式与上一次思考的相同,也就是(拷贝文件→装包→构建→启动服务),而非大量地写文件。
- 终端交互采取inquirer,获取到参数之后传递给生成模块去处理
- 这次需要重点解决的问题有。
- 如何让shell认识我定义的命令?
- 如何优雅地调用shell的命令?
寻找方案
针对第一个难点,经人指点,我知道应该在package.json的bin字段中定义我这个命令。其实平常我们输入cd,ls这些命令,shell之所以认识,是因为它们在环境变量$path中能找到。但是全局安装的npm module并不在$path中啊。npm的解决方案是,对于全局安装的,有bin字段的包,**在安装的时候会主动建立一个链接,从$path指向该module的某个可执行文件。**就像如图所示的第二行箭头那样。
其实这个bin字段还可以配置多个命令,详细的可以参考这里。ok,第一个难题可以解决了。
针对第二个难点,我搜索到了shell.js,关于shell.js,可以参考阮一峰老师的教程。
开始敲代码
ok,解决方案敲定之后就开始撸起袖子敲代码了。
先写终端交互的cli.js,主要涉及inquirer.js
#!/usr/bin/env node
'use strict';
var inquirer = require("inquirer");
var lazySmart = require("./lazy-smart");
var shell = require("shelljs");
var questions = [
// 项目名称
{
type: "input",
name: "name",
message: "input your project name",
validate: function (value) {
if (!value) {
return "project name can not be null"
}
// 检查文件夹是否已存在
var ls = shell.ls();
if (ls.indexOf(value) !== -1) {
return "File exists, please select another project name.."
} else {
return true;
}
}
},
// 选择项目架构类型
{
type: "list",
name: "architecture",
message: "select your project architecture",
choices: ["ejs+gulp", "ejs+webpack"]
},
// git仓库名称
{
type: "input",
name: "gitName",
message: "input the repository name of git project.(make sure the repository is created and empty)",
default: function (answer) {
return answer.name;
}
},
// git仓库所有者名称
{
type: "input",
name: "gitOwner",
message: "input the owner of git project.",
default: function () {
var userName = shell.exec('git config --global --get user.name').output;
userName = userName.substring(0, userName.length - 1);
return userName;
}
}
];
inquirer.prompt(questions, function (answers) {
//把用户输入的参数传递给生成模块
lazySmart.init(answers);
});
然后编写生成模块lazy-smart.js
// 调用执行命令行
var shell = require("shelljs");
// 支持在脚本中直接执行命令
require('shelljs/global');
// 解析获取命令行参数
var argv = require('yargs').argv;
// 初始化
exports.init = function (options) {
exports.copy(options);
exports.initGit(options);
exports.install(options);
exports.build(options);
exports.run(options);
};
....
把功能模块划分好之后,剩下函数的编写就不难了,完整的代码在这里
至此,已经完成了第一套项目脚手架的搭建,之后第二,第三套就跟脚手架没啥关系了。通过这次自己手动写脚手架,主要有两个收获。
- 通过分析问题找准解决方案之后,敲代码才能效率高。
- 跟终端交互还是很有趣的。
遗留问题点:
- npm包的本地调试和发布流程不是非常顺畅,而且有个极其坑爹的.gitignore变成.npmignore的问题还不知道为啥。暂时找到的参考资料在这里。
- shell.js的exce是串行执行的,当我想在在一个js文件调用多个不会结束自动结束的进程的时候容易出问题,比如先后开启npm run start 和gulp watch这些。有什么办法能够直接打开新终端窗口吗?或者结合child_process?这个可是每个都是并行执行的。