转载

上手 yeoman generator

最近折腾脚手架相关的一些事情。说到脚手架,不得不谈的就是 yeoman 了。

是什么

yeoman 是一个脚手架生成工具。

yeoman generator 则是 yeoman 的精髓所在。

从我的理解来看。 yeoman 就是一个工具外壳,它定制了如何调用 generator ,给 generator 提供了运行环境。 yeoman generator 则是解耦出来的核心部分,负责完成一个脚手架应该做的事。

线上已经有很多 generator ,可以满足我们一大波需求。不过要做到真正灵活,完全符合自己的需求、业务中的需要就要自己自定义 generator 了。

怎么做

yeoman 的强大之处在于它提供了一套非常强大的编写自定义 generatorAPI ,而且上手非常容易。只要按照特定的约束,很快就可以定制一套自己的 generator 。话不多说,马上一起来看看怎么做。

目录结构

|- app
    |- index.js
    |- template
        |- 模板文件
|- package.json (主入口为app/index.js)

初始化一个npm包,定制目录结构如上,这样就简单完成 generator 的目录结构啦。当然你可以用 generator-generator 生成符合规范的 generator ,这样更加快捷。

index.js结构示例

package.json 不多说,注意主入口写好就行。index.js 也是按照具体的约束,一个简单的示例:

var generators = require('yeoman-generator')

module.exports = generators.Base.extend({  
  constructor: function () {
    generators.Base.apply(this, arguments)
  }

  // 方法A

  // 方法B
})

一个 Yeoman Generator 被创建后,会依次调用它原型上的方法,调用的顺序如下:

initializing - 初始化一些状态之类的,通常是和用户输入的 options 或者 arguments 打交道,这个后面说。
prompting - 和用户交互的时候(命令行问答之类的)调用。
configuring - 保存配置文件(如 .babelrc 等)。
default - 其他方法都会在这里按顺序统一调用。
writing - 在这里写一些模板文件。
conflicts - 处理文件冲突,比如当前目录下已经有了同名文件。
install - 开始安装依赖。

也可以自定义方法,比如demo里方法A会先于方法B执行。下面具体介绍下每个方法的一些作用。

prompting

用于做命令行的交互,这个应该是最常用的一个功能。用于在命令行和用户交互,用户提一些问题,我们的 generator 收集问题的结果。一个简单的例子:

prompting: function () {
        let models = [{
            name: 'eslint',
            checked: true
        }, {
            name: 'sass-lint',
            checked: true
        }];

        const prompts = [{
            type: 'checkbox',
            name: 'enable',
            message: '开启哪些功能?',
            choices: models
        }, {
            type: 'confirm',
            name: 'installDependencies',
            message: '安装相关依赖?',
            when: (props) => {
                return props.enable.length;
            }
        }, {
            type: 'list',
            name: 'tool',
            message: '使用npm/tnpm?',
            choices: [
                CFG.CHOICE.TOOL.NPM,
                CFG.CHOICE.TOOL.TNPM
            ],
            when: (props) => {
                return props.installDependencies;
            }
        }];

        return this.prompt(prompts).then((options) => {
            this.userOptions = options;
        });
    },

这里不对代码细解释,只需要知道这里可以做用户命令行交互,具体每个参数有什么意义, github 上搜索一下 Inquirer.js 就很清晰了。

writing

这里用于文件拷贝,读文件,写文件。一个简单的例子:

writing: function () {
        const options = this.userOptions;
        const imlintrcPath = this.cfg.imPath;
        const imlintrcJson = {
            config: options
        };
        const enableModules = options.enable || [];
        const MODULES = CFG.MODULES;
        const pkgPath = this.cfg.pkgPath;
        let pkgJson;

        /** 1. 复制基础样板文件 */
        this.cfg.files.forEach((item) => {
            this.fs.copy(
                this.templatePath(item),
                this.destinationPath(item)
            );
        });

        /** 2. 创建imlintrc文件 */
        this.fs.writeJSON(imlintrcPath, imlintrcJson);

        /** 3. 修改package.json scripts配置 */
        try {
            pkgJson = this.fs.readJSON(pkgPath);
        } catch (ex) {
            console.log('imlint: package.json不合法');
            process.exit();
        }

        pkgJson.devDependencies = pkgJson.devDependencies || {};
        pkgJson.scripts = pkgJson.scripts || {};
        Object.assign(pkgJson.scripts, this.cfg.scripts || {});

        /** 4. 部署对应模块,包括:1. 迁移文件 2. 修改package.json devDependencies配置 */
        enableModules.forEach((item) => {
            const cur = MODULES[item];

            if (!cur) {
                return;
            }

            Object.assign(pkgJson.devDependencies, cur.pkgs);

            if (cur.files) {
                cur.files.forEach((file) => {
                    this.fs.copy(
                        this.templatePath(file),
                        this.destinationPath(file)
                    );
                });
            }
        });

        /** 5. 写package.json文件 */
        this.fs.writeJSON(pkgPath, pkgJson);
    },

具体文件API的意义可参见 mem-fs-editor 这个库

install

用于安装依赖,比如npm install一个lodash

install() {
    this.npmInstall(['lodash'], { 'save-dev': true });
}

发布

index.js 写完,一个简单的 generator 就ok了。上面这个 DEMO 具体的详细例子,可以看 generator-imlint-init

将上面这个 npm 包发布后,就可以按如下方法安装使用了~~

npm install -g yo
npm install -g generator-imlint-init

yo imlint-init

小结

例子比较简单,方法也只说了下 generator 最常用的三个方法。更多的功能参见 yeoman 官方文档~~

原文  http://imweb.io/topic/586766b5b3ce6d8e3f9f99b1
正文到此结束
Loading...