yeoman自定义generator

5 3月

Yeoman是一个通用的脚手架系统,用它来自定义脚手架非常方便。自定义的脚手架以generator-XYZ命名,发布后用yo XYZ就能执行。其他yo命令:

yo --version
yo --generators  // 列出安装过的generator
yo --help

generator是一个可执行的node模块。你可以在官网上搜索想要的generator,也可以自定义。

  • Step1 初始化目录
  • Step2 定义目录结构
  • Step3 继承yeoman-generator
  • Step4 constructor
  • Step5 自定义方法
  • Step6 调试发布

Step1 初始化目录

建一个名为generator-XYZ的文件夹,例如generator-test。名字不能乱起,因为Yeoman依赖文件系统来查找可用的generator。文件夹内用 npm init 初始化项目,package.json 里有几个强约束:

{
  "name": "generator-test",          // 必须是 generator- 开头
  "version": "0.1.0",
  "description": "",
  "files": [
    "generators"                     // generator 的目录
  ],
  "keywords": ["yeoman-generator"],  // keywords 必须有 yeoman-generator
  "dependencies": {
    "yeoman-generator": "^1.0.0"     // 必须安装 yeoman-generator
  }
}

Step2 定义目录结构

Yeoman是根据文件系统来查找可用的generator的,所以每个generator都要包含在同名文件夹中。

yo name默认执行app/目录中的generator。

yo name:subcommand执行同名子目录下的generator。

├───package.json
└───generators/        // 对应package.json里的files属性
    ├───app/
    │   └───templates
    │   └───index.js
    └───app2/
    │   └───templates
        └───index.js

// 执行 yo test      默认执行 app 目录下的 generator
// 执行 yo test:app  效果同上
// 执行 yo test:app2 执行 app2 目录下的 generator

如果不要上面的generators目录,直接将app和app2放到根目录下,也可以。但package.json里的files里就要改成[“app”, “app2”]。总之package.json里的files就是告诉Yeoman去哪里找generator。

Step3 继承yeoman-generator

目录里的index.js里就可以自定义generator。当然不用全部重写,通常继承yeoman-generator,然后拓展想要的功能。

const Generator = require('yeoman-generator');

module.exports = class extends Generator { ... }

Step4 constructor

命令行可以传递参数,例如yo test my-first-yo-project

参数需要在constructor里,通过this.argument()来定义,定义的参数被保存到this.options里

module.exports = class extends Generator {
    constructor(args, opts) {
        super(args, opts);

        // 执行 yo test my-first-yo-project
        // options支持:type,require,default,desc
        this.argument("appname", { type: String, required: true });
        this.log(this.options.appname);   // my-first-yo-project
        ...
    }
    ...
}

// 这样执行 yo test 就会报错,一定要执行 yo test my-first-yo-project 来启动

命令行可以传递options(参数用–前缀),例如yo test –ts

参数需要在constructor里,通过this.option()来定义,定义的option被保存到this.options里

module.exports = class extends Generator {
    constructor(args, opts) {
        super(args, opts);

        // 执行 yo test my-first-yo-project
        // options支持:type,require,default,desc
        this.argument("appname", { type: String, required: true });
        this.log(this.options.appname);   // my-first-yo-project

        // 执行 yo test my-first-yo-project --ts
        // options支持:type,alias,default,desc,hide
        this.option("ts");                
        this.log(this.options.ts);        // true
        ...
    }
}

constructor里通常会设置一些state,做一些通用的工作。例如通过检查src目录是否存在,来判断是否已经初始化。

const Generator = require('yeoman-generator');

module.exports = class extends Generator {
    constructor(args, opts) {
        super(args, opts);
 
        ...
        if (fs.existsSync('src')) {     // 检查脚手架是否已经存在
            this.log(chalk.bold.green('src 目录已存在,资源已经初始化,退出...'));
            process.exit(1);
        }
        this.info = null;
        this.config.save();             // 生成 .yo-rc.json 文件。Yeoman通过这个文件知道该目录是根目录
    }
    ...
}

上面通过this.config.save()在根目录创建.yo-rc.json文件。Yeoman通过这个文件知道该目录是根目录。这里岔开一下,进一步介绍.yo-rc.json文件。

  • 可以通过this.config.save()创建.yo-rc.json文件
  • 可以通过this.config.get() / set() / delete()来操作.yo-rc.json
  • 可以通过this.config.getAll()读取.yo-rc.json里所有内容
  • this.config.defaults()方法可以会检查.yo-rc.json是否已经存在该key-value,存在就什么都不做,不存在就添加进去

Step5 自定义方法

每个被添加到prototype中的方法都会被按顺序执行,会依次执行prompting,writing,end:

module.exports = class extends Generator {
    constructor(args, opts) { ... }

    prompting() {
        this.log('prompting...');
    }

    writing() {
        this.log('writing...');
    }

    end() {
        this.log('end...');
    }
};

Step6 调试发布

你可以将这个仓库发布npm,然后安装这个包。但这样调试太麻烦,可以在开发目录下执行npm link,可以将本地包链接到全局环境下,就好像真的安装了包一样。

最后在你需要用的地方执行yo test就可以调试啦。

发表评论

电子邮件地址不会被公开。 必填项已用*标注