Yeoman学习笔记:常用 cheatsheet

yeoman 常用操作记录,如何编写一个项目中使用的脚手架,安装更新使用
更新于: 2022-09-02 13:18:56

安装与更新

npm i -g yo
npm update -g yo

示例 .yo-rc 文件

{
  "@jswork/generator-react-app": {
    "component": {
      "prefix": "",
      "components_dir": "src/components",
      "component_type": "functional",
      "export_type": "const",
      "file_type": "tsx"
    },
    "hook": {
      "prefix": "use-",
      "components_dir": "src/hooks",
      "export_type": "const",
      "file_type": "tsx"
    }
  }
}

常用操作

名称命令备注
templates目录里的所有文件
const srcFiles = globby.sync(this.templatePath('**'), { dot: true });
const dstPath = this.destinationPath();
this.fs.copy(srcFiles, dstPath);
文件copy到目标位置
生成相关的 prompts
// es6
import genp from '@jswork/generator-prompts';
const prompts = genp(["scope", "registry", "project_name", "description"]);
// es5
const getp = require("@jswork/generator-prompts");
const prompts = getp(["scope", "registry", "project_name", "description"]);
参考 repositry
取得 option 配置
// 取得 hook 配置
this.config.get('hook');
// 取得所有的配置
this.config.getAll();
this._config 是 内部变量,不要在项目中使用
设置/获取 option
// 设置 option
this.argument("styles", { type: String, default: "./src/assets/styles" });
// 获取 option
const styles = this.options.styles;
yo @jswork/tailwind --styles="./styles"
yo @jswork/tailwind --styles ./styles
copy单个文件
this.fs.copy(
  this.templatePath("tailwind.config.js"),
  this.destinationPath(resolve("tailwind.config.js"))
);
 
copy并填props
this.fs.copyTpl(
  globby.sync(this.templatePath("**"), { dot: true }),
  this.destinationPath(),
  this.props
);
 
copy 并带 ctx 
// 可用用来处理 case 转换等场景
// import <%- ctx.camelize(project_name) %> from '@<%= scope %>/<%= project_name %>';

this.fs.copyTpl(
  globby.sync(this.templatePath("**"), { dot: true }),
  this.destinationPath(),
  { ...this.props, ctx: yoHelper.ctx }
);
可以用来处理各种转换场景
 
copy目录
this.fs.copy(
  globby.sync(this.templatePath("tailwind/**"), { dot: true }),
  this.destinationPath(this.options.styles)
);
 
copy 并且 rename
const { project_name } = this.props;
const ctx = yoHelper.ctx;
this.fs.copyTpl(
  globby.sync(this.templatePath("**"), { dot: true }),
  this.destinationPath(),
  { ...this.props, ctx },
  null,
  {
    processDestinationPath: (filePath) => {
      return filePath.replace("jsw_nx", ctx.underscored(project_name));
    },
  }
);
 
对package.json的操作
const pkgJson = {
  devDependencies: {
    eslint: "^3.15.0"
  },
  dependencies: {
    react: "^16.2.0"
  }
};

// Extend or create package.json file in destination path
this.fs.extendJSON(this.destinationPath("package.json"), pkgJson);
 

常见的 cli 交互类型

类型代码备注
input
{
  type: 'input',
  name: 'description',
  message: 'Your description?',
  validate: Boolean
}
 
input(store)
{
  type    : 'input',
  name    : 'username',
  message : 'What\'s your Github username',
  store   : true
}
 
confirm
{
  type: 'confirm',
  name: 'cool',
  message: 'Would you like to enable the Cool feature?'
}
Yes/No 类型
checkbox
{
  type: 'checkbox',
  name: 'database',
  message: 'Select Database support:',
  choices: [
    {
      name: 'H2',
      value: 'h2'
    },
    {
      name: 'HSQLDB',
      value: 'hsqldb'
    }
  ]
}
 

Copy所有 templates 中的文件

  • 没有任何的逻辑
  • 几乎没有 prompts
'use strict';
const Generator = require('yeoman-generator');
const globby = require('globby');

module.exports = class extends Generator {
  prompting() {
    const prompts = [];
    return this.prompt(prompts).then((props) => {
      this.props = props;
    });
  }

  writing() {
    this.fs.copyTpl(
      globby.sync(this.templatePath('**'), { dot: true }),
      this.destinationPath(),
      this.props
    );
  }
};

Case 在 yeoman 中使用 JS 函数

  • 实现转换功能:<%- ctx.classify(model_name) %>
  • 实现单复数功能:<%- ctx.pluralize(model_name) %>
# from orator import Model
from .abstract_model import AbstractModel


class <%- ctx.classify(model_name) %>(AbstractModel):
    __table__ = '<%- ctx.pluralize(model_name) %>'
    pass
"use strict";
const Generator = require("yeoman-generator");
const globby = require("globby");
const yoHelper = require("@jswork/yeoman-generator-helper");

module.exports = class extends Generator {
  async prompting() {
    const prompts = [
      {
        type: "input",
        name: "model_name",
        message: "Your model_name?",
        validate: Boolean
      }
    ];

    this.props = await this.prompt(prompts);
  }

  writing() {
    const { model_name } = this.props;
    const dirs = globby.sync("**/models", { onlyDirectories: true });
    const target = dirs.find(item => item.endsWith("models"));

    this.fs.copyTpl(
      this.templatePath("model.py"),
      this.destinationPath(`${target}/${model_name}.py`),
      { ...this.props, ctx: yoHelper.ctx }
    );
  }
};

增加 package.json 里的内容

'use strict';
const Generator = require('yeoman-generator');
const globby = require('globby');

// yo @jswork/dotfiles:prettier --ideable
module.exports = class extends Generator {
  writing() {
    const pattern = this.options.ideable ? '**' : '.pre*';
    const opts = { dot: true };
    this.fs.copyTpl(
      globby.sync(this.templatePath(pattern), opts),
      this.destinationPath(),
      this.props
    );
  }

  installDeps() {
    const pkgJson = {
      devDependencies: {
        prettier: '^2.5.1'
      }
    };
    this.fs.extendJSON(this.destinationPath('package.json'), pkgJson);
  }
};

重命名问题

几个基本的阶段

  1. initializing - Your initialization methods (checking current project state, getting configs, etc)
  2. prompting - Where you prompt users for options (where you’d call this.prompt())
  3. configuring - Saving configurations and configure the project (creating .editorconfig files and other metadata files)
  4. default - If the method name doesn’t match a priority, it will be pushed to this group.
  5. writing - Where you write the generator specific files (routes, controllers, etc)
  6. conflicts - Where conflicts are handled (used internally)
  7. install - Where installations are run (npm, bower)
  8. end - Called last, cleanup, say good bye, etc

参考