文档版本:plop@3.1.0 - plop@3.1.0
Plop是一个“微生成器框架”——之所以这样定义它,是因为Plop是一个用于生成代码文件或其他纯文本文件的小工具。并且简单、高效、同时保证一致性。在开发中我们的代码往往基于“框架”和“关键字”的组合(路由、控制器、模板、等等)。这些组合往往随着时间的推移和开发的进行不断的会被完善和调整。当你需要创建一个同类型的文件时,你往往很难在浩如烟海的代码仓库中找到当初的那个“最佳实践”。而Plop专为解决此类问题而生,通过使用Plop,你只需要在控制台输入Plop命令,即可创建任何格式的、已经提前编写好的“最佳实现”代码。这不仅节约了你在搜寻合适代码来复制粘贴的时间,同时它也提供给你一种最“正确”也最“方便”的创建新文件的方法。
Plop的核心其实主要是inquirer命令行工具和handlebar模板引擎的整合。
此文档还在编写中,如果你有什么好想法请联系我们。
$ npm install --save-dev plop
$ npm install -g plop
module.exports = function (plop) {
// 创建生成器
plop.setGenerator("basics", {
description: "这是一个基础plopfile模板",
prompts: [], // 确认问题数组
actions: [], // 操作列表
});
};
export default只可以被用在NodeJS下的支持“ESM”标准的文件中。如果你希望使用这种语法,请确保你的使用环境属于以下两种之一:
- 文件后缀名为.js,package.json的“type”字段中定义为“module”
- 文件后缀名为.mjs,此情况不限制package.json的“type”字段
与此同时,你也可以创建一个内容为
module.exports = function (plop)的plopfile。如果使用这种方式引入,需要你的使用环境属于以下两种之一:
- 文件后缀名为.js,package.json的“type”字段中定义为“commonjs”
- 文件后缀名为.js,此情况不限制package.json的“type”字段
Plopfile是一个简单的Node module,并导出一个函数,它接受一个plop对象作为第一个传入参数。
module.exports = function (plop) {};
plop对象暴露了一些plop的api,其中包括setGenerator(name, config)方法,使用此方法可以创建Plop生成器。当在工作目录(或工作目录的子目录)命令行中输入plop命令时,所有的生成器会以列表形式输出。
一个最简单的起始生成器例子可能类似这样:
module.exports = function (plop) {
// controller generator
plop.setGenerator("controller", {
description: "application controller logic",
prompts: [
{
type: "input",
name: "name",
message: "controller name please",
},
],
actions: [
{
type: "add",
path: "src/{{name}}.js",
templateFile: "plop-templates/controller.hbs",
},
],
});
};
我们创建的 controller 生成器会询问我们一个问题,并创建一个文件。这一操作可以进一步扩展,比如询问更多的问题,创建更多的文件。也有一些附加的action可以通过其他的方式操作我们的代码仓库。
Plop使用inquirer.js 库来接受用户输入。更多关于问题类型的信息可以查看inquirer官方文档.
Plop成功安装并且创建了generator之后,你就可以使用命令行运行plop了。直接调用plop命令将会输出所有可用的生成器。你呀可以直接使用plop [生成器名称]来直接调用某一生成器。如果你没有全局安装plop,则需要编写一个npm脚本来帮助运行plop。
// package.json
{
...,
"scripts": {
"plop": "plop"
},
...
}
如果你对一个项目(以及它的生成器)十分了解,你就可能希望在调用生成器时直接传递回答参数了。例如项目中有一个名为component的生成器,其接受一个名为name的参数,可以直接通过plop component "名字"直接传入参数。如果生成器接受更多参数,只要按照这种规则依次传入即可。
例如confirm和list类型的问题会最大程度尝试理解你的输入。比如为confirm类型问题传入参数"y","yes","t",或者"true"都会被解析成布尔值true。同时你可以使用选项的确切值,数组下标,键名,或者名称等等表示从列表中选中某选项。多选问题允许你传入以逗号分隔的值的列表,来表示多选项。

如果你想直接传入参数的问题是第二个问题,第一个仍需手动填写或选择,可以使用“_“来跳过某个参数(例如
plop component _ "input for second prompt").
Plop已经内置常规问题的直接传入参数逻辑了,不过你也可以自定义一些特殊问题解析用户输入的逻辑。如果你发布了inquirer的第三方插件并想支持plop的直接传入参数逻辑,可以查看本文档的相关内容.
你也可以直接传入参数 -- 然后提供每个问题的参数来直接传入参数,例子如下.
## 直接传入问题1和2的参数
$ plop component "my component" react
$ plop component -- --name "my component" --type react
## 直接传入问题2的参数 (name 属性依然会正常询问)
$ plop component _ react
$ plop component -- --type react
通常情况下Plop的action在发现执行可以操作时会执行失败,以此来尽可能确保文件安全。最常见的情况可能是不允许add 操作覆盖一个已存在文件。但是Plop支持特殊的force属性,你也可以在命令行输入的指令后加上--force选项来开启强制模式,在这一模式下所有操作均会强制完成。不惜一切代价...🕷
这一机制可以使得你可以专注于编写模板,而不必同时考虑代码逻辑问题。这一机制可以为你和你的团队在面对重复逻辑时(路由,组件,辅助函数,测试,界面层,等等)节省大量的时间,这真的很重要!
与此同时,对于程序猿来说在不同任务之间反复横跳真的很浪费时间,很有可能还没等你进入编写新逻辑的状态你就忍不住去摸鱼了...所以一次专注一件事真的可以大大提高工作效率!而且工程化运作的优势远不止于此!
Plopfile api是一系列plop对象暴露出的方法,其实setGenerator可以解决大部分的问题。但是在这部分文档中你可能会找到一些其他的有用信息以充实和完善你的plopfile。
plop内置了Typescript类型定义,无论你是否需要使用Typescript编写plopfile,这一特定都可以方便大部分的代码编辑器提供代码提示。
// plopfile.ts
import { NodePlopAPI } from 'plop';
export default function (plop: NodePlopAPI) {
// plop generator code
};
// plopfile.js
module.exports = function (
/** @type {import('plop').NodePlopAPI} */
plop,
) {
// plop generator code
};
这些是创建plopfile时常用的方法。其他主要供内部使用的方法在其他方法部分列出。
| 方法名 | 入参 | 返回值 | 描述 |
|---|---|---|---|
| setGenerator | String, GeneratorConfig | GeneratorConfig | 创建一个生成器 |
| setHelper | String, Function | 创建一个helper | |
| setPartial | String, String | 创建一个Partial | |
| setActionType | String, CustomAction | 创建一个Action类型 | |
| setPrompt | String, InquirerPrompt | 使用inquirer注册自定义问题类型 | |
| load | Array[String], Object, Object | 从另一plopfile或者 npm module中载入生成器,helper,和/或 partials |
setHelper 等同于 handlebars 的方法 registerHelper。 如果你熟悉 handlebars helpers,那你就已经对此很熟悉了。
export default function (plop) {
plop.setHelper("upperCase", function (text) {
return text.toUpperCase();
});
// or in es6/es2015
plop.setHelper("upperCase", (txt) => txt.toUpperCase());
}
setPartial 等同于 handlebars 的方法 registerPartial。如果你熟悉 handlebars partials,那你就已经对此很熟悉了。
export default function (plop) {
plop.setPartial("myTitlePartial", "<h1>{{titleCase name}}</h1>");
// used in template as {{> myTitlePartial }}
}
setActionType允许你创建自定义actions (类似 add 或 modify) 完善你的plopfile。这些方法通常都是高度可复用的的 自定义函数。
| 参数 | 类型 | 描述 |
|---|---|---|
| answers | Object | 生成器问题的回答 |
| config | ActionConfig | 生成器“actions”数组中的对象 |
| plop | PlopfileApi | Action运作时的plop api |
export default function (plop) {
plop.setActionType("doTheThing", function (answers, config, plop) {
// do something
doSomething(config.configProp);
// if something went wrong
throw "error message";
// otherwise
return "success status message";
});
// or do async things inside of an action
plop.setActionType("doTheAsyncThing", function (answers, config, plop) {
// do something
return new Promise((resolve, reject) => {
if (success) {
resolve("success status message");
} else {
reject("error message");
}
});
});
// use the custom action
plop.setGenerator("test", {
prompts: [],
actions: [
{
type: "doTheThing",
configProp: "available from the config param",
},
{
type: "doTheAsyncThing",
speed: "slow",
},
],
});
}
Inquirer 提供了许多开箱即用的问题类型,但是其也允许开发者构建第三方插件丰富问题类型。如果你需要使用第三方问题差价插件,你可以使用setPrompt。更多细节可以查看Inquirer关于注册问题的官方文档。 你也可以查看plop社区提供的自定问题列表.
import autocompletePrompt from 'inquirer-autocomplete-prompt';
export default function (plop) {
plop.setPrompt('autocomplete', autocompletePrompt);
plop.setGenerator('test', {
prompts: [{
type: 'autocomplete',
...
}]
});
};
这一部分的配置对象需要包含prompts 和 actions(description是可选项)。Prompts数组会被传递到inquirer,actions数组是一系列将要进行的操作的数组。(详细文档参阅下文)
GeneratorConfig| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| description | [String] | 此生成器功能的简短描述 | |
| prompts | Array[InquirerQuestion] | 需要询问用户的问题 | |
| actions | Array[ActionConfig] | 需要执行的操作 |
>如果你的Action列表有动态需求,你可以查看使用动态action数组部分内容。
ActionConfig下列参数是plop内部需要使用的基本参数,其他参数的需求取决于action的类型,关于这部分可以查看内置actions。
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| type | String | action的类型 (add, modify, addMany, 等等) |
|
| force | Boolean | false |
强制执行forcefully (在不同action下有不同表现) |
| data | Object / Function | {} |
指定需要与用户输入参数进行合并的数据 |
| abortOnFail | Boolean | true |
如果本操作因任何原因失败则取消后续操作 |
| skip | Function | 可选项,标记这个action是否会被执行 |
ActionConfig的data属性也可以为一个返回对象的函数或者一个返回resolve内容为函数的Promise。
ActionConfig的skip属性方法是可选项,其应该返回一个字符串,内容是逃过action执行的原因。
Action除了可以使用对象写法,也可以使用函数写法
| 方法 | 参数 | 返回 | 描述 |
|---|---|---|---|
| getHelper | String | Function | 获取helper函数 |
| getHelperList | Array[String] | 获取helper函数名称列表 | |
| getPartial | String | String | 通过名称获取handlebars partial |
| getPartialList | Array[String] | 获取partial名称列表 | |
| getActionType | String | CustomAction | 通过名称获取action 类型 |
| getActionTypeList | Array[String] | 获取actionType名称列表 | |
| setWelcomeMessage | String | 自定义运行plop时提示选择generator的提示标语 |
|
| getGenerator | String | GeneratorConfig | 根据名称获取GeneratorConfig |
| getGeneratorList | Array[Object] | 获取generator名称和描述的列表 | |
| setPlopfilePath | String | 更改内部用于定位资源和模板文件的plopfilePath值 |
|
| getPlopfilePath | String | 返回plopfile的绝对路径 |