前言
最近和室友商量着想写一个自己的开源项目,想到了自己在工作中惯用的项目结构,惯用的库,在新开项目的时候总是要修改配置一番,挺麻烦的。于是觉得自己做个脚手架玩一玩。
已实现的功能
目前第一版实现的功能非常简单。仅仅是输入项目名称,生成固定的项目结构,安装依赖可以运行起来。欢迎大家试玩。
项目名: henri-create-app
项目git地址
npm包地址
实现原理
概括一下说,就是将一个编写好的完整的项目目录复制到新建项目目录里面,然后就可以安装依赖并运行了。下面为大家讲解一下主要实现步骤,带领大家做一个自己的脚手架。当然还是更希望大家能加入我的项目,一起完善henri-create-app。
package.json
命令名称和要执行的代码入口。依赖的包和用处:
glob: 匹配查找指定的文件或目录
path: 处理路径
yargs-parser: 获取命令行输入参数yeomen-generator: 生成器
获取用户输入的项目名
进入./bin/index.js,通过以下代码获取用户输入的项目名参数
const args = yParser(process.argv.slice(2));
const name = args._[0];
node中使用process.argv接收用户输入的参数,这里稍加处理,获取到项目名。即henri-create-app后面输入的字符串。
创建项目的根目录
mkdirp.sync(name);
let cwd = process.cwd();
cwd = path.join(cwd, name);
上述代码,创建了一个名为name的目录。然后通过process.cwd()获取当前工作目录,再通过path获取到刚创建的项目目录。
复制模板文件
通过yeoman-generator复制文件到项目目录。这是一个专门的用来创建项目的库。这里只是简单地用来复制文件,其他更多强大功能等待你去探索。
const generator = new Generator({
name,
env: { cwd },
resolved: require.resolve('../lib/generators'),
args
});
generator.run(() => {
console.log(chalk.green('创建成功!'));
});
创建了一个生成器对象,并执行它。
再来到../lib/generators,看看生成器是如何定义的。
class extends Generator {
constructor(opts) {
super(opts);
this.name = basename(opts.env.cwd);
}
writing() {
glob
.sync('**/*', {
cwd: this.templatePath(),
dot: true
})
.forEach(file => {
const filePath = this.templatePath(file);
if (statSync(filePath).isFile()) {
this.fs.copyTpl(
this.templatePath(filePath),
this.destinationPath(file.replace(/^_/, '.')),
{ name: this.name }
);
}
});
}
};
这里通过继承yeoman的Generator对象,实现一个自己的生成器。
构造函数中定义了一个项目名变量。
重写了writing方法,这是执行run时会调用到的。
writing中只做了一件事,读取templates文件夹下的所有文件或文件夹,输出到项目目录中。
注意到{ name: this.name }代码,这是fs.copyTpl方法传入的context对象,模板中通过<%= name %>引用到此变量。大家可以自己去查看一下templates里面的源码。
结语
粗略地描述了一下实现过程,大家有什么不明白的地方,欢迎提问。我有什么表述错误或不准确的也欢迎指正哦。后期还计划实现更多有趣功能,感兴趣的小伙伴欢迎加入~