在Yeoman腳手架使用入門(mén)和Yeoman腳手架核心機(jī)制這兩篇文章中已經(jīng)對(duì)Yeoman腳手架工具的基本使用以及去核心運(yùn)轉(zhuǎn)機(jī)制進(jìn)行了深入的介紹裁赠,這篇文章將以實(shí)例的方式來(lái)教會(huì)如何從零開(kāi)始創(chuàng)建屬于我們自己的generator啦租。
generator創(chuàng)建準(zhǔn)備
這里我們一切從零開(kāi)始,在創(chuàng)建自己的generator之前需要做一些準(zhǔn)備工作募强,比如準(zhǔn)備好yo命令行工具,比如對(duì)生成器生成的項(xiàng)目結(jié)構(gòu)和目錄文件有清晰的規(guī)劃等。
yo命令行工具
在安裝了NodeJS和npm的前提下,可以通過(guò)下面的命令來(lái)安裝yo命令行工具我纪,并檢查安裝是否成功。
$ npm install -g yo
$ yo --version
generator-generator的安裝
創(chuàng)建generator可以完全從零開(kāi)始丐吓,也可以使用Yeoman官方提供的generator引導(dǎo)浅悉,這里我們選擇使用Yeoman官方推薦的方式來(lái)處理。
$ mkdir YeomanTest && cd YeomanTest/
創(chuàng)建新的目錄并進(jìn)入
$ npm install -g generator-generator
安裝Yeoman引導(dǎo)generator
列出具體的執(zhí)行情況
wendingding$ mkdir YeomanTest
wendingding$ cd YeomanTest/
wendingding$ pwd
/Users/文頂頂/Desktop/Yeoman/YeomanTest
wendingding$ npm install -g generator-generator + generator-generator@4.0.2
updated 1 package in 117.639s
╭─────────────────────────────────────╮
│ │
│ Update available 5.5.1 → 6.1.0 │
│ Run npm i -g npm to update │
│ │
╰─────────────────────────────────────╯
執(zhí)行Yeoman官方的引導(dǎo)generator券犁,并處理交互式配置部分术健,下面列出具體的執(zhí)行情況。
wendingding$ yo generator
? Your generator name generator-wendingding
Your generator must be inside a folder named generator-wendingding
I'll automatically create this folder.
? Description 博客文章測(cè)試創(chuàng)建生成器
? Project homepage url http://www.wendingding.com
? Author's Name 文頂頂
? Author's Email 18681537032@163.com
? Author's Homepage http://www.wendingding.com
? Package keywords (comma to split) wendingding
? Send coverage reports to coveralls Yes
? GitHub username or organization flowerField
? Which license do you want to use? Apache 2.0
create package.json
create README.md
create .editorconfig
create .gitattributes
create .gitignore
create generators/app/index.js
create generators/app/templates/dummyfile.txt
create __tests__/app.js
create .travis.yml
create .eslintignore
create LICENSE
I'm all done. Running npm install for you to install the required dependencies.
If this fails, try running the command yourself.
在執(zhí)行g(shù)enerator-generator這個(gè)生成器的過(guò)程中粘衬,會(huì)詢問(wèn)項(xiàng)目名稱荞估、作者、使用協(xié)議稚新、主頁(yè)地址等等信息勘伺,依次選擇填空即可。
注意
:按照約定褂删,Yeoman generator的名字必須以“generator-”的前綴開(kāi)頭娇昙,這是因?yàn)樗械膅enerator其實(shí)都是全局安裝的node模塊,所以Yeoman其實(shí)是完全依靠文件系統(tǒng)來(lái)對(duì)這些生成器進(jìn)行查找操作的笤妙。
當(dāng)上面的命令執(zhí)行完畢后,會(huì)發(fā)現(xiàn)在當(dāng)前的路徑下面生成了generator-wendingding
目錄噪裕,進(jìn)入到generator-wendingding
目錄蹲盘,使用tree命令查看當(dāng)前目錄結(jié)構(gòu),顯示如下:
.
├── LICENSE
├── README.md
├── __tests__
├── generators
├── app
│ ├── index.js
│ └── templates
│ └── dummyfile.txt
├── node_modules
├── package-lock.json
└── package.json
上面目錄結(jié)構(gòu)中雖然有很多文件膳音,但我們真正需要關(guān)注的應(yīng)該是generators路徑下面的app/index.js文件以及templates目錄召衔,其中index文件對(duì)應(yīng)是generators的組裝指令部分,templates路徑用于存放項(xiàng)目所有的模板文件祭陷。
項(xiàng)目模板文件準(zhǔn)備
上面這些準(zhǔn)備工作完成之后苍凛,接下來(lái)我們開(kāi)始著手分析目標(biāo)項(xiàng)目的文件結(jié)構(gòu),即我們使用自己創(chuàng)建的這個(gè)腳手架來(lái)搭建項(xiàng)目兵志,其結(jié)構(gòu)目錄應(yīng)該是怎樣的醇蝴?需要包含哪些文件等等。任何時(shí)候想罕,明確知道你的目標(biāo)悠栓,知道自己正在做什么至關(guān)重要。
下面試著給出目標(biāo)項(xiàng)目的文件結(jié)構(gòu)。
.
├── Gruntfile.js
├── bower.json
├── build
├── dist
├── package.json
└── src
├── css
│ └── style.css
├── index.html
├── js
│ └── index.js
├── libs
│ └── jquery
└── template
我們可以看到該項(xiàng)目應(yīng)該包含bulid
惭适、src
以及dist
三個(gè)目錄笙瑟,其中src目錄中需要?jiǎng)?chuàng)建名為css
、js
癞志、libs
和template
的文件夾往枷,分別用來(lái)保存樣式文件、腳本文件凄杯、依賴的框架以及模板文件等错洁。
除了這些必要的文件外,假設(shè)目標(biāo)項(xiàng)目需要使用bower來(lái)進(jìn)行依賴管理盾舌,使用Grunt來(lái)進(jìn)行自動(dòng)化構(gòu)建墓臭,所以自然還應(yīng)該擁有Gruntfile.js、bower.json以及package.json文件妖谴。
假設(shè)目標(biāo)項(xiàng)目中一定會(huì)使用到j(luò)Query框架窿锉,可能會(huì)使用到bootstrap框架。
現(xiàn)在我們可以開(kāi)始分析生成器中應(yīng)該包含項(xiàng)目模板文件了膝舅,也就是在generators/templates路徑中應(yīng)該包含哪些文件嗡载。
固定文件
index.js
和 style.css
創(chuàng)建空文件即可。
Gruntfile.js文件
因?yàn)閮?nèi)容固定不變仍稀,所以選擇直接從舊項(xiàng)目中拷貝洼滚。
.jshintrc文件
用于js文件語(yǔ)法檢查,內(nèi)容也是固定不變的技潘。
.bowerrc文件
用于重置Bower下載包的安裝路徑遥巴,內(nèi)容為{"directory": "src/libs/"}
靈活文件
package.json文件
中項(xiàng)目名稱整份、作者以及開(kāi)源協(xié)議等需要用戶配置
bower.json文件
的項(xiàng)目名稱杨蛋、作者毅否、開(kāi)源協(xié)議以及依賴框架等需要用戶配置
可選文件
bootstrap框架相關(guān)的部分
為可選文件聋亡,需要根據(jù)用戶配置進(jìn)行處理谱仪。
依賴文件
jQuery框架相關(guān)的部分
為依賴文件开皿,在組裝指令部分通過(guò)在代碼中調(diào)用方法來(lái)下載和安裝椰棘。
根據(jù)上面的分析别洪,我們?cè)趃enerators/templates準(zhǔn)備了多個(gè)模板文件奔坟,下面列出文件結(jié)構(gòu)以及主要文件的具體內(nèi)容:
.
└── app
├── index.js
└── templates
├── Gruntfile.js
├── bower.json
├── css
│ └── style.css
├── index.html
├── js
│ └── index.js
└── package.json
package.json文件內(nèi)容
{
"name": "<%= appName %>",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "<%= appAuthor %>",
"license": "<%= appLicense %>",
"devDependencies": {
"grunt": "^1.0.2",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-cssmin": "^2.2.1",
"grunt-contrib-jshint": "^1.1.0",
"grunt-contrib-uglify": "^3.3.0",
"grunt-contrib-watch": "^1.0.0"
}
}
bower.json文件內(nèi)容
{
"name": "<%= appName %>",
"description": "\"測(cè)試使用\"",
"main": "js/index.js",
"authors": [
"<%= appAuthor %>"
],
"license": "<%= appLicense %>",
"keywords": [
"generator-wendingding",
"yeoman-generator"
],
"homepage": "https://github.com/flowerField/generator-wen",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"jquery": "^3.3.1"<% if(isIncludeBootstrap) { %>,
"bootstrap": "^4.1.1" <% } %>
}
}
index.html文件內(nèi)容
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title><%= appName %></title>
<link rel="stylesheet" href="css/style.css">
<script type="text/javascript" src="js/index.js"></script>
</head>
<body>
</body>
</html>
Gruntfile.js文件內(nèi)容
//包裝函數(shù)
module.exports = function (grunt) {
// 項(xiàng)目配置信息
grunt.config.init({
pkg:grunt.file.readJSON("package.json"),
//代碼合并
concat:{
options:{
stripBanners:true,
banner:'/*項(xiàng)目名稱:<%=pkg.name%> 項(xiàng)目版本:<%=pkg.version%> 項(xiàng)目的作者:<%=pkg.author%> 更新時(shí)間:<%=grunt.template.today("yyyy-mm-dd")%>*/\n'
},
target:{
src:["src/js/*.js"],
dest:'build/js/index.js'
}
},
//js代碼壓縮
uglify:{
target:{
src:"build/js/index.js",
dest:"build/js/index.min.js"
}
},
//css代碼壓縮
cssmin:{
target:{
src:"src/css/style.css",
dest:"build/css/style.min.css"
}
},
//js語(yǔ)法檢查
jshint:{
target:['Gruntfile.js',"dist/js/index.js"],
},
//監(jiān)聽(tīng) 自動(dòng)構(gòu)建
watch:{
target:{
files:["src/js/*.js","src/css/*.css"],
//只要指定路徑的文件(js和css)發(fā)生了變化携栋,就自動(dòng)執(zhí)行tasks中列出的任務(wù)
tasks:["concat","jshint","uglify","cssmin"]
}
}
});
//通過(guò)命令行安裝插件(省略...)
//從node_modules路徑加載插件
grunt.loadNpmTasks("grunt-contrib-concat");
grunt.loadNpmTasks("grunt-contrib-uglify");
grunt.loadNpmTasks("grunt-contrib-cssmin");
grunt.loadNpmTasks("grunt-contrib-jshint");
grunt.loadNpmTasks("grunt-contrib-watch");
//注冊(cè)任務(wù):在執(zhí)行$ grunt命令的時(shí)候依次執(zhí)行代碼的合并|檢查|壓縮等任務(wù)并開(kāi)啟監(jiān)聽(tīng)
grunt.registerTask("default",["concat","jshint","uglify","cssmin","watch"]);
};
注意
:上面部分文件中很多地方使用模板語(yǔ)法來(lái)傳遞參數(shù),Yeoman所用的模板語(yǔ)言是EJS咳秉,具體用法請(qǐng)參考EJS官網(wǎng)
組裝指令
處理完上面這些工作之后婉支,接下來(lái)就是最最核心的部分了,我們需要在app/index.js文件
中編寫(xiě)組裝指令澜建,這部分代碼控制著這個(gè)生成器應(yīng)該怎么執(zhí)行磅摹,包括交互式配置的具體內(nèi)容滋迈、如何復(fù)制文件以及框架依賴和Node模塊下載等內(nèi)容。
下面列出該示例中的index.js文件內(nèi)容
'use strict';
const Generator = require('yeoman-generator');
const chalk = require('chalk');
const yosay = require('yosay');
const mkdirp = require('mkdirp');
module.exports = class extends Generator {
prompting() {
this.log(
// yosay(`Welcome to the transcendent ${chalk.red('generator-wen')} generator!`)
yosay(`歡迎使用\n${chalk.red('generator-wen')} !\n Author:文頂頂`)
);
const prompts = [
{
type : 'input',
name : 'appName',
message : '請(qǐng)輸入項(xiàng)目名稱:',
default : this.appname //appname是內(nèi)置對(duì)象户誓,代表工程名饼灿,這里就是ys
},
{
type : 'input',
name : 'appAuthor',
message : '請(qǐng)輸入作者姓名:',
default : '文頂頂'
},
{
type: 'list',
name: 'appLicense',
message: '請(qǐng)選擇使用的license:',
choices: ['MIT', 'ISC', 'Apache-2.0', 'AGPL-3.0']
},
{
type : 'confirm',
name : 'isIncludeBootstrap',
message : '是否需要使用bootStrap框架?',
default : false
},
];
return this.prompt(prompts).then(props => {
// To access props later use this.props.someAnswer;
this.props = props;
});
}
writing() {
mkdirp("build");
mkdirp("dist");
mkdirp("src/template");
this.fs.copyTpl(
this.templatePath('index.html'),
this.destinationPath('src/index.html'),
{appName: this.props.appName}
);
this.fs.copy(
this.templatePath('css/style.css'),
this.destinationPath('src/css/style.css')
);
this.fs.copy(
this.templatePath('js/index.js'),
this.destinationPath('src/js/index.js')
);
this.fs.copy(
this.templatePath('.bowerrc'),
this.destinationPath('.bowerrc')
);
this.fs.copy(
this.templatePath('Gruntfile.js'),
this.destinationPath('Gruntfile.js')
);
this.fs.copy(
this.templatePath('.jshintrc'),
this.destinationPath('.jshintrc')
);
this.fs.copyTpl(
this.templatePath('package.json'),
this.destinationPath('package.json'),
{appName: this.props.appName,appAuthor:this.props.appAuthor,appLicense:this.props.appLicense}
);
this.fs.copyTpl(
this.templatePath('bower.json'),
this.destinationPath('bower.json'),
{appName: this.props.appName,appAuthor:this.props.appAuthor,appLicense:this.props.appLicense,isIncludeBootstrap:this.props.isIncludeBootstrap}
);
}
install() {
//this.installDependencies();
this.bowerInstall();
}
};
上面的代碼大概由三部分組成帝美,第一部分為prompting函數(shù)用來(lái)處理安裝提示碍彭,第二部分為writing函數(shù)用來(lái)設(shè)置模板文件的復(fù)制操作,第三部分為install函數(shù)用來(lái)處理框架依賴和node包的安裝悼潭。
generator的發(fā)布和測(cè)試
項(xiàng)目模板文件和組裝指令都準(zhǔn)備好了后庇忌,我們就可以發(fā)布自己的generator了,可以先通過(guò)$ npm link
命令以軟連接的方式生成一個(gè)全局的npm包舰褪,測(cè)試使用皆疹。
具體的執(zhí)行細(xì)節(jié)如下
wendingding:generator-wendingding wendingding$ npm link
up to date in 3.897s
/usr/local/lib/node_modules/generator-wendingding -> /Users/文頂頂/Desktop/Yeoman/YeomanTest/generator-wendingding
wendingding:generator-wendingding wendingding$
測(cè)試·使用自己創(chuàng)建的generator來(lái)生成初始化項(xiàng)目
隨便找個(gè)目錄新建文件夾,使用$ yo wendingding
命令即可完成項(xiàng)目的初始化工作占拍。
wendingding:YeomanTest wendingding$ mkdir Demo
wendingding:YeomanTest wendingding$ cd Demo/
wendingding:Demo wendingding$ yo wendingding
_-----_ ╭──────────────────────────╮
| | │ 歡迎使用 │
|--(o)--| │ generator-wen ! │
`---------′ │ Author:文頂頂 │
( _′U`_ ) ╰──────────────────────────╯
/___A___\ /
| ~ |
__'.___.'__
′ ` |° ′ Y `
? 請(qǐng)輸入項(xiàng)目名稱: Demo
? 請(qǐng)輸入作者姓名: 文頂頂
? 請(qǐng)選擇使用的license: Apache-2.0
? 是否需要使用bootStrap框架略就? Yes
create bower.json
create package.json
create src/index.html
create src/css/style.css
create src/js/index.js
create .bowerrc
create Gruntfile.js
create .jshintrc
bower invalid-meta for:/Users/文頂頂/Desktop/Yeoman/YeomanTest/Demo/bower.json
bower invalid-meta The "name" is recommended to be lowercase, can contain digits, dots, dashes
bower cached https://github.com/jquery/jquery-dist.git#3.3.1
bower validate 3.3.1 against https://github.com/jquery/jquery-dist.git#^3.3.1
bower cached https://github.com/twbs/bootstrap.git#4.1.1
bower validate 4.1.1 against https://github.com/twbs/bootstrap.git#^4.1.1
bower install jquery#3.3.1
bower install bootstrap#4.1.1
jquery#3.3.1 src/libs/jquery
bootstrap#4.1.1 src/libs/bootstrap
wendingding:Demo wendingding$ tree -L 3
.
├── Gruntfile.js
├── bower.json
├── build
├── dist
├── package.json
└── src
├── css
│ └── style.css
├── index.html
├── js
│ └── index.js
├── libs
│ ├── bootstrap
│ └── jquery
└── template
9 directories, 6 files
如果需要把這個(gè)生成器發(fā)布到社區(qū),可以先到npm官網(wǎng)注冊(cè)一個(gè)自己的npm賬號(hào)晃酒,然后在該生成器的目錄下執(zhí)行$ npm publish
命令即可表牢。