做者: 珈蓝 from 迅雷前端javascript
原文地址: greenfavo.github.io/blog/docs/0…html
当新建项目的时候,咱们一般须要设计目录结构、配各类配置、处理打包编译,并且每次新建都要重来一遍,或把原来的项目 copy 一份再改改。那能不能本身写个模板,而后还能够支持个性化自动建立呢?今天我就来和你们一块儿分享如何定制一套本身的项目脚手架,提高开发效率。前端
这里须要引入脚手架的概念,什么是脚手架呢?脚手架如同一个项目的模板,能够帮咱们快速开始项目,就像 vue-cli,提供一个终端的交互界面让用户自定义项目内容。vue
Yeoman 是一个通用的脚手架系统,容许建立任何类型的应用程序(Web,Java,Python,C#等)。用 yeoman 写脚手架很是简单, yeoman 提供了 yeoman-generator 让咱们快速生成一个脚手架模板,咱们的主要工做就是把模板文件写好。如今咱们来用 yeoman 写一个生成 javascript 插件的脚手架吧。java
脚手架功能:node
首先须要全局安装 yo 和 generator-generatorgit
npm install yo -g
npm install generator-generator -g
复制代码
生成脚手架模板es6
yo generator
复制代码
在这个终端界面里输入项目名、描述等项目信息。注意项目名称要写成generator-xxx
的格式,这样用户就能够经过yo xxx
安装你的脚手架了。github
生成的脚手架模板目录结构以下:vue-cli
.
├── generators/
│ └── app/
│ ├── index.js
│ └── templates/
│ └── dummyfile.txt
├── .editorconfig
├── .eslintignore
├── .gitattributes
├── .gitignore
├── .travis.yml
├── .yo-rc.json
├── LICENSE
├── README.md
├── package.json
└── __tests__/
└── app.js
复制代码
接下来咱们就在generators/app/index.js
里写脚手架的逻辑。
脚手架所作的事情:
yeoman 提供了一个基本生成器,你能够扩展它以实现本身的行为。这个基础生成器将帮你减轻大部分工做量。在生成器的 index.js 文件中,如下是扩展基本生成器的方法:
var Generator = require("yeoman-generator");
module.exports = class extends Generator {};
复制代码
yeoman 生命周期函数执行顺序以下:
咱们经常使用的就是 initializing、prompting、default、writing、install 这四种生命周期函数。看下例子:
"use strict";
const Generator = require("yeoman-generator");
const chalk = require("chalk"); // 让console.log带颜色输出
const yosay = require("yosay");
const mkdirp = require("mkdirp"); // 建立目录
module.exports = class extends Generator {
initializing() {
this.props = {};
}
// 接受用户输入
prompting() {
// Have Yeoman greet the user.
this.log(
yosay(
`Welcome to the grand ${chalk.red( "generator-javascript-plugin" )} generator!`
)
);
const prompts = [
{
type: "confirm",
name: "someAnswer",
message: "Would you like to enable this option?",
default: true
}
];
return this.prompt(prompts).then(props => {
// To access props later use this.props.someAnswer;
this.props = props;
});
}
// 建立项目目录
default() {
if (path.basename(this.destinationPath()) !== this.props.name) {
this.log(`\nYour generator must be inside a folder named ${this.props.name}\n I will automatically create this folder.\n`);
mkdirp(this.props.name);
this.destinationRoot(this.destinationPath(this.props.name));
}
}
// 写文件
writing() {
// 将templates目录的代码拷贝到目标目录
// templates目录默认路径是generators/app/templates
this.fs.copy(
this.templatePath("dummyfile.txt"),
this.destinationPath("dummyfile.txt")
);
this._writingPackageJSON();
}
// 如下划线_开头的是私有方法
_writingPackageJSON() {
// this.fs.copyTpl(from, to, context)
this.fs.copyTpl(
this.templatePath("_package.json"),
this.destinationPath("package.json"),
{
name: this.props.name,
description: this.props.description,
keywords: this.props.keywords.split(","),
author: this.props.author,
email: this.props.email,
repository: this.props.repository,
homepage: this.props.homepage,
license: this.props.license
}
);
}
// 安装依赖
install() {
this.installDependencies();
}
};
复制代码
前面咱们把一个脚手架的基本框架都写好了,它能够接受用户输入的内容,能够写文件,能够安装依赖,但接收用户输入的数据怎么用?写进什么文件?安装什么依赖呢?这些都是模板文件作的事情。如今就开始最主要的一部分:编写模板文件。
模板文件是你为用户生成的一个项目 demo,让用户看着这些示例代码就能够开工了,用户应该只须要专一于业务逻辑,而不用管打包构建这些事。
首先建好模板目录:
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .babelrc
├── jsdoc.json
├── README.md
├── package.json
├── build/
└── rollup.js
├── src/
└── index.js
├── test/
└── index.js
复制代码
咱们的模板package.json
里已经写好这些命令:
"scripts": {
"prebuild": "npm run lint && npm run test && npm run doc",
"build": "node ./build/rollup.js",
"lint": "eslint --ext .js, src",
"test": "mocha --require babel-register --require babel-polyfill --bail",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
"doc": "jsdoc -c ./jsdoc.json"
}
复制代码
npm run lint
用 eslint 进行语法检查,在编译前就避免语法错误和统一代码风格。npm test
运行单元测试npm run doc
根据注释生成文档npm run changelog
根据git log
生成项目日志,改动记录一目了然npm run prebuild
编译前的语法检查、 运行测试、生成文档npm run build
编译打包咱们可使用<%= name %>
这样的模板语法使用脚手架中的context
上下文,不管是用户输入的数据,仍是程序本身的变量:
{
"name": "<%= name %>",
"description": "<%= description %>",
"version": "1.0.0",
"private": false,
"main": "dist/<%= name %>.umd.js",
"module": "dist/<%= name %>.es.js"
}
复制代码
详细代码请到github查看。
为了保证代码的健壮性,咱们必须进行单元测试。其实咱们用generator
生成的脚手架代码中已经有测试代码示例了,改为本身的逻辑就能够测试咱们的脚手架逻辑了。如今咱们来测试下文件是否生成:
'use strict';
const path = require('path');
const assert = require('yeoman-assert');
const helpers = require('yeoman-test');
describe('generator-javascript-plugin:app', () => {
beforeAll(() => {
return helpers
.run(path.join(__dirname, '../generators/app'))
.withPrompts({ someAnswer: true });
});
it('creates files', () => {
assert.file(['build/rollup.js']);
assert.file(['dist']);
assert.file(['src']);
assert.file(['test']);
assert.file(['package.json']);
assert.file(['.babelrc']);
...
});
});
复制代码
执行命令
npm test
复制代码
到此,咱们的脚手架开发完了,接下来实际运行下看看是否正确。
因为咱们的脚手架仍是本地开发,它还没有做为全局 npm 模块提供。咱们可使用 npm 建立全局模块并将其符号连接到本地模块。在项目根目录运行:
npm link
复制代码
这样就能够调用yo javascript-plugin
运行脚手架了。你能够在终端看到运行过程。
写好的脚手架发布出去才能让更多的人使用,发布脚手架和发布其余 npm 包同样。如何发布?
generator-javascript-plugin
这个脚手架已经发布到npm上,能够下载或访问源码。