版博士V2.0程序
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

1 год назад
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. Plop
  2. ======
  3. Micro-generator framework that makes it easy for an entire team to create files with a level of uniformity.
  4. ![plop demo](https://i.imgur.com/penUFkr.gif)
  5. > [Documentation also available on plopjs.com](https://plopjs.com/documentation/)
  6. # Getting Started
  7. [![npm](https://img.shields.io/npm/dm/plop.svg)](https://www.npmjs.com/package/plop)
  8.  
  9. [![npm](https://img.shields.io/npm/v/plop.svg)](https://www.npmjs.com/package/plop)
  10.  
  11. [![plop on slack](https://img.shields.io/badge/slack-join%20workspace-green)](https://join.slack.com/t/plopjs/shared_invite/zt-ehh69el1-2_DjgZRuMbpC9RnEa4M8cA)
  12. ## What is Plop?
  13. Plop is what I like to call a "micro-generator framework." Now, I call it that because it is a small tool that gives you a simple way to generate code or any other type of flat text files in a consistent way. You see, we all create structures and patterns in our code (routes, controllers, components, helpers, etc). These patterns change and improve over time so when you need to create a NEW *insert-name-of-pattern-here*, it's not always easy to locate the files in your codebase that represent the current "best practice." That's where plop saves you. With plop, you have your "best practice" method of creating any given pattern in CODE. Code that can easily be run from the terminal by typing `plop`. Not only does this save you from hunting around in your codebase for the right files to copy, but it also turns "the right way" into "the easiest way" to make new files.
  14. If you boil plop down to its core, it is basically glue code between [inquirer](https://github.com/SBoudrias/Inquirer.js/) prompts and [handlebar](https://github.com/wycats/handlebars.js/) templates.
  15. > This documentation is a work in progress. If you have great ideas, I'd love to hear them.
  16. ## Installation
  17. ### 1. Add plop to your project
  18. ```
  19. $ npm install --save-dev plop
  20. ```
  21. ### 2. Install plop globally (optional, but recommended for easy access)
  22. ```
  23. $ npm install -g plop
  24. ```
  25. ### 3. Create a plopfile.js at the root of your project
  26. ``` javascript
  27. export default function (plop) {
  28. // create your generators here
  29. plop.setGenerator('basics', {
  30. description: 'this is a skeleton plopfile',
  31. prompts: [], // array of inquirer prompts
  32. actions: [] // array of actions
  33. });
  34. };
  35. ```
  36. > `export default` is only allowed in NodeJS inside "ESM" supported files.
  37. > To use this syntax, your `plopfile` must be either:
  38. > - An ESM .js file with type: "module" in package.json
  39. > - An ESM .mjs file with any type declared in package.json
  40. >
  41. > Alternatively, you can have a `plopfile` with `module.exports = function (plop)` instead.
  42. > For _this_ syntax, your `plopfile` must be either:
  43. > - A CommonJS .js file with type: "commonjs" in package.json
  44. > - A CommonJS .cjs file with any type declared in package.json
  45. ## Your First Plopfile
  46. A plopfile starts its life as a node module that exports a function which accepts the `plop` object as its first parameter.
  47. ``` javascript
  48. export default function (plop) {};
  49. ```
  50. The `plop` object exposes the plop API object which contains the `setGenerator(name, config)` function. This is the function that you use to (wait for it) create a generator for this plopfile. When `plop` is run from the terminal in this directory (or any sub-directory), a list of these generators will be displayed.
  51. Let's try setting up a basic generator to see how that looks.
  52. ``` javascript
  53. export default function (plop) {
  54. // controller generator
  55. plop.setGenerator('controller', {
  56. description: 'application controller logic',
  57. prompts: [{
  58. type: 'input',
  59. name: 'name',
  60. message: 'controller name please'
  61. }],
  62. actions: [{
  63. type: 'add',
  64. path: 'src/{{name}}.js',
  65. templateFile: 'plop-templates/controller.hbs'
  66. }]
  67. });
  68. };
  69. ```
  70. The *controller* generator we created above will ask us 1 question, and create 1 file. This can be expanded to ask as many questions as needed, and create as many files as needed. There are also additional actions that can be used to alter our codebase in different ways.
  71. ## Using Prompts
  72. Plop uses the [inquirer.js](https://github.com/SBoudrias/Inquirer.js) library to gather user data. A list of [prompt types](https://github.com/SBoudrias/Inquirer.js/#prompt-types) can be found on the inquirer official website.
  73. ## CLI Usage
  74. Once plop is installed, and you have created a generator, you are ready to run plop from the terminal. Running `plop` with no parameters will present you with a list of generators to pick from. You can also run `plop [generatorName]` to trigger a generator directly. If you did not install plop globally, you will need to setup an npm script to run plop for you.
  75. ```javascript
  76. // package.json
  77. {
  78. ...,
  79. "scripts": {
  80. "plop": "plop"
  81. },
  82. ...
  83. }
  84. ```
  85. ### Bypassing Prompts
  86. Once you get to know a project (and its generators) well, you may want to provide answers to the prompts when you run the generator. If I have (for instance) a `component` generator that has one prompt (name), I can run that generator using `plop component "some component name"` and it will immediately execute as though I had typed "some component name" into the prompt. If that same generator had a second prompt, the same input would have resulted in the user being prompted for the second value.
  87. Prompts like `confirm` and `list` try to make sense of your input as best they can. For instance entering "y", "yes", "t", or "true" for a confirm prompt will result in a boolean `true` value. You can select items from a list using their value, index, key, or name. Checkbox prompts can accept a comma separated list of values in order to select multiples.
  88. ![plop bypass demo](https://media.giphy.com/media/3ov9jQ38ypmX4SuT60/giphy.gif)
  89. > If you want to provide bypass input for the second prompt but not the first, you can use an underscore "\_" to skip the bypass (ie `plop component _ "input for second prompt"`).
  90. Plop comes with bypass logic built-in for standard inquirer prompts, but there are also ways to provide custom logic for how to handle user input for a specific prompt.
  91. If you have published a 3rd party inquirer prompt plugin and would like to support bypass functionality for plop users out of the box, that is covered in [another section of this documentation](#3rd-party-prompt-bypass).
  92. ### Bypassing Prompts (by Name)
  93. You can also bypass prompts by name using `--` and then providing arguments for each prompt that you'd like to bypass. Examples [below](#bypass-examples).
  94. #### Bypass Examples
  95. ```
  96. ## Bypassing both prompt 1 and 2
  97. $ plop component "my component" react
  98. $ plop component -- --name "my component" --type react
  99. ## Bypassing only prompt 2 (will be prompted for name)
  100. $ plop component _ react
  101. $ plop component -- --type react
  102. ```
  103. ### Running a Generator Forcefully
  104. By default Plop actions keep your files safe by failing when things look fishy. The most obvious example of this is not allowing an [`add`](#add) action to overwrite a file that already exists. Plop actions individually support the `force` property but you can also use the `--force` flag when running Plop from the terminal. Using the `--force` flag will tell every action to run forcefully. With great power...🕷
  105. ## Why Generators?
  106. Because when you create your boilerplate separate from your code, you naturally put more time and thought into it.
  107. Because saving your team (or yourself) 5-15 minutes when creating every route, component, controller, helper, test, view, etc... [really adds up](https://xkcd.com/1205/).
  108. Because [context switching is expensive](https://www.petrikainulainen.net/software-development/processes/the-cost-of-context-switching/) and saving time is not the only [benefit to automating workflows](https://kentcdodds.com/blog/automation)
  109. # Plopfile API
  110. The plopfile api is the collection of methods that are exposed by the `plop` object. Most of the work is done by [`setGenerator`](#setgenerator) but this section documents the other methods that you may also find useful in your plopfile.
  111. ## TypeScript Declarations
  112. `plop` bundles TypeScript declarations. Whether or not you write your plopfile in TypeScript, many editors will offer code assistance via these declarations.
  113. ```javascript
  114. // plopfile.ts
  115. import {NodePlopAPI} from 'plop';
  116. export default function (plop: NodePlopAPI) {
  117. // plop generator code
  118. };
  119. ```
  120. ```javascript
  121. // plopfile.js
  122. export default function (
  123. /** @type {import('plop').NodePlopAPI} */
  124. plop
  125. ) {
  126. // plop generator code
  127. };
  128. ```
  129. ## Main Methods
  130. These are the methods you will commonly use when creating a plopfile. Other methods that are mostly for internal use are list in the [other methods](#other-methods) section.
  131. Method | Parameters | Returns | Description
  132. ------ | ---------- | ------- | -----------
  133. [**setGenerator**](#setgenerator) | *String, [GeneratorConfig](#interface-generatorconfig)* | *[PlopGenerator](#interface-plopgenerator)* | setup a generator
  134. [**setHelper**](#sethelper) | *String, Function* | | setup handlebars helper
  135. [**setPartial**](#setpartial) | *String, String* | | setup a handlebars partial
  136. [**setActionType**](#setactiontype) | *String, [CustomAction](#functionsignature-custom-action)* | | register a custom action type
  137. [**setPrompt**](#setprompt) | *String, InquirerPrompt* | | registers a custom prompt type with inquirer
  138. [**load**](https://github.com/plopjs/plop/blob/main/plop-load.md) | *Array[String], Object, Object* | | loads generators, helpers and/or partials from another plopfile or npm module
  139. ## setHelper
  140. `setHelper` directly corresponds to the handlebars method `registerHelper`. So if you are familiar with [handlebars helpers](https://handlebarsjs.com/guide/expressions.html#helpers), then you already know how this works.
  141. ``` javascript
  142. export default function (plop) {
  143. plop.setHelper('upperCase', function (text) {
  144. return text.toUpperCase();
  145. });
  146. // or in es6/es2015
  147. plop.setHelper('upperCase', (txt) => txt.toUpperCase());
  148. };
  149. ```
  150. ## setPartial
  151. `setPartial` directly corresponds to the handlebars method `registerPartial`. So if you are familiar with [handlebars partials](https://handlebarsjs.com/guide/partials.html), then you already know how this works.
  152. ``` javascript
  153. export default function (plop) {
  154. plop.setPartial('myTitlePartial', '<h1>{{titleCase name}}</h1>');
  155. // used in template as {{> myTitlePartial }}
  156. };
  157. ```
  158. ## setActionType
  159. `setActionType` allows you to create your own actions (similar to `add` or `modify`) that can be used in your plopfiles. These are basically highly reusable [custom action function](#custom-action-function)s.
  160. ### *FunctionSignature* Custom Action
  161. Parameters | Type | Description
  162. ---------- | ---- | -----------
  163. **answers** | *Object* | Answers to the generator prompts
  164. **config** | *[ActionConfig](#interface-actionconfig)* | The object in the "actions" array for the generator
  165. **plop** | *[PlopfileApi](#plopfile-api)* | The plop api for the plopfile where this action is being run
  166. ``` javascript
  167. export default function (plop) {
  168. plop.setActionType('doTheThing', function (answers, config, plop) {
  169. // do something
  170. doSomething(config.configProp);
  171. // if something went wrong
  172. throw 'error message';
  173. // otherwise
  174. return 'success status message';
  175. });
  176. // or do async things inside of an action
  177. plop.setActionType('doTheAsyncThing', function (answers, config, plop) {
  178. // do something
  179. return new Promise((resolve, reject) => {
  180. if (success) {
  181. resolve('success status message');
  182. } else {
  183. reject('error message');
  184. }
  185. });
  186. });
  187. // use the custom action
  188. plop.setGenerator('test', {
  189. prompts: [],
  190. actions: [{
  191. type: 'doTheThing',
  192. configProp: 'available from the config param'
  193. }, {
  194. type: 'doTheAsyncThing',
  195. speed: 'slow'
  196. }]
  197. });
  198. };
  199. ```
  200. ## setPrompt
  201. [Inquirer](https://github.com/SBoudrias/Inquirer.js) provides many types of prompts out of the box, but it also allows developers to build prompt plugins. If you'd like to use a prompt plugin, you can register it with `setPrompt`. For more details see the [Inquirer documentation for registering prompts](https://github.com/SBoudrias/Inquirer.js#inquirerregisterpromptname-prompt). Also check out the [plop community driven list of custom prompts](https://github.com/plopjs/awesome-plop#inquirer-prompts).
  202. ``` javascript
  203. import autocompletePrompt from 'inquirer-autocomplete-prompt';
  204. export default function (plop) {
  205. plop.setPrompt('autocomplete', autocompletePrompt);
  206. plop.setGenerator('test', {
  207. prompts: [{
  208. type: 'autocomplete',
  209. ...
  210. }]
  211. });
  212. };
  213. ```
  214. ## setGenerator
  215. The config object needs to include `prompts` and `actions` (`description` is optional). The prompts array is passed to [inquirer](https://github.com/SBoudrias/Inquirer.js/#objects). The `actions` array is a list of actions to take (described in greater detail below)
  216. ### *Interface* `GeneratorConfig`
  217. Property | Type | Default | Description
  218. -------- | ---- | ------- | -----------
  219. **description** | *[String]* | | short description of what this generator does
  220. **prompts** | *Array[[InquirerQuestion](https://github.com/SBoudrias/Inquirer.js/#question)]* | | questions to ask the user
  221. **actions** | *Array[[ActionConfig](#interface-actionconfig)]* | | actions to perform
  222. > If your list of actions needs to be dynamic, take a look at [using a dynamic actions array.](#using-a-dynamic-actions-array)
  223. ### *Interface* `PlopGenerator`
  224. Property | Type | Default | Description
  225. -------- | ---- | ------- | -----------
  226. **runPrompts** | *Function* | | a function to run the prompts within a generator
  227. **runActions** | *Function* | | a function to run the actions within a generator
  228. > This interface also contains all properties from [GeneratorConfig](#interface-generatorconfig)
  229. ### *Interface* `ActionConfig`
  230. The following properties are the standard properties that plop handles internally. Other properties will be required depending on the *type* of action. Also take a look at the [built-in actions](#built-in-actions).
  231. Property | Type | Default | Description
  232. -------- | ---- | ------- | -----------
  233. **type** | *String* | | the type of action ([`add`](#add), [`modify`](#modify), [`addMany`](#addmany), [etc](#setactiontype))
  234. **force** | *Boolean* | `false` | performs the action [forcefully](#running-a-generator-forcefully) (means different things depending on the action)
  235. **data** | *Object / Function* | `{}` | specifies data that should be mixed with user prompt answers when running this action
  236. **abortOnFail** | *Boolean* | `true` | if this action fails for any reason abort all future actions
  237. **skip** | *Function* | | an optional function that specifies if the action should run
  238. > The `data` property on any `ActionConfig` can also be a `Function` that returns an `Object` or a `Function` that returns a `Promise` that resolves with an `Object`.
  239. > The `skip` function on any `ActionConfig` is optional and should return a string if the action should be skipped. The return value is the reason to skip the action.
  240. > Instead of an Action Object, a [function can also be used](#custom-action-function)
  241. ## Other Methods
  242. Method | Parameters | Returns | Description
  243. ------ | ---------- | ------- | -----------
  244. **getHelper** | *String* | *Function* | get the helper function
  245. **getHelperList** | | *Array[String]* | get a list of helper names
  246. **getPartial** | *String* | *String* | get a handlebars partial by name
  247. **getPartialList** | | *Array[String]* | get a list of partial names
  248. **getActionType** | *String* | *[CustomAction](#functionsignature-custom-action)* | get an actionType by name
  249. **getActionTypeList** | | *Array[String]* | get a list of actionType names
  250. **setWelcomeMessage** | *String* | | Customizes the displayed message that asks you to choose a generator when you run `plop`.
  251. **getGenerator** | *String* | *[GeneratorConfig](#interface-generatorconfig)* | get the [PlopGenerator](#interface-plopgenerator) by name
  252. **getGeneratorList** | | *Array[Object]* | gets an array of generator names and descriptions
  253. **setPlopfilePath** | *String* | | set the `plopfilePath` value which is used internally to locate resources like template files
  254. **getPlopfilePath** | | *String* | returns the absolute path to the plopfile in use
  255. **getDestBasePath** | | *String* | returns the base path that is used when creating files
  256. **setDefaultInclude** | *Object* | *Object* | sets the default config that will be used for this plopfile if it is consumed by another plopfile using `plop.load()`
  257. **getDefaultInclude** | *String* | *Object* | gets the default config that will be used for this plopfile if it is consumed by another plopfile using `plop.load()`
  258. **renderString** | *String, Object* | *String* | Runs the first parameter (*String*) through the handlebars template renderer using the second parameter (*Object*) as the data. Returns the rendered template.
  259. # Built-In Actions
  260. There are several types of built-in actions you can use in your [GeneratorConfig](#interface-generatorconfig). You specify which `type` of action (all paths are based on the location of the plopfile), and a template to use.
  261. > The `Add`, `AddMany` and `Modify` actions have an optional `transform` method that can be used to transform the template result before it is written to disk. The `transform` function receives the template result or file contents as a `string` and the action data as arguments. It must return a `string` or a `Promise` that resolves to a `string`.
  262. ## Add
  263. The `add` action is used to (you guessed it) add a file to your project. The path property is a handlebars template that will be used to create the file by name. The file contents will be determined by the `template` or `templateFile` property.
  264. Property | Type | Default | Description
  265. -------- | ---- | ------- | -----------
  266. **path** | *String* | | a handlebars template that (when rendered) is the path of the new file
  267. **template** | *String* | | a handlebars template that should be used to build the new file
  268. **templateFile** | *String* | | a path a file containing the `template`
  269. **skipIfExists** | *Boolean* | `false` | skips a file if it already exists (instead of failing)
  270. **transform** | *Function* | | [an optional function](#built-in-actions) that can be used to transform the template result before writing the file to disk
  271. **skip** | *Function* | | *inherited from [ActionConfig](#interface-actionconfig)*
  272. **force** | *Boolean* | `false` | *inherited from [ActionConfig](#interface-actionconfig)* (overwrites files if they exist)
  273. **data** | *Object* | `{}` | *inherited from [ActionConfig](#interface-actionconfig)*
  274. **abortOnFail** | *Boolean* | `true` | *inherited from [ActionConfig](#interface-actionconfig)*
  275. ## AddMany
  276. The `addMany` action can be used to add multiple files to your project with a single action. The `destination` property is a handlebars template that will be used to identify the folder that the generated files should go into. The `base` property can be used to alter what section of the template paths should be omitted when creating files. The paths located by the `templateFiles` glob can use handlebars syntax in their file/folder names if you'd like the added file names to be unique (example: `{{ dashCase name }}.spec.js`).
  277. Property | Type | Default | Description
  278. -------- | ---- | ------- | -----------
  279. **destination** | *String* | | a handlebars template that (when rendered) is the destination folder for the new files
  280. **base** | *String* | | the section of the path that should be excluded when adding files to the `destination` folder
  281. **templateFiles** | *[Glob](https://github.com/sindresorhus/globby#globbing-patterns)* | | glob pattern that matches multiple template files to be added
  282. **stripExtensions** | *[String]* | `['hbs']` | file extensions that should be stripped from `templateFiles` files names while being added to the `destination`
  283. **globOptions** | *[Object](https://github.com/sindresorhus/globby#options)* | | glob options that change how to match to the template files to be added
  284. **verbose** | *Boolean* | `true` | print each successfully added file path
  285. **transform** | *Function* | | [an optional function](#built-in-actions) that can be used to transform the template result before writing each file to disk
  286. **skip** | *Function* | | *inherited from [ActionConfig](#interface-actionconfig)*
  287. **skipIfExists** | *Boolean* | `false` | *inherited from [Add](#add)* (skips a file if it already exists)
  288. **force** | *Boolean* | `false` | *inherited from [ActionConfig](#interface-actionconfig)* (overwrites files if they exist)
  289. **data** | *Object* | `{}` | *inherited from [ActionConfig](#interface-actionconfig)*
  290. **abortOnFail** | *Boolean* | `true` | *inherited from [ActionConfig](#interface-actionconfig)*
  291. ## Modify
  292. The `modify` action can be used two ways. You can use a `pattern` property to find/replace text in the file located at the `path` specified, or you can use a `transform` function to transform the file contents. Both `pattern` and `transform` can be used at the same time (`transform` will happen last). More details on modify can be found in the example folder.
  293. Property | Type | Default | Description
  294. -------- | ---- | ------- | -----------
  295. **path** | *String* | | handlebars template that (when rendered) is the path of the file to be modified
  296. **pattern** | *RegExp* | _end&#x2011;of&#x2011;file_ | regular expression used to match text that should be replaced
  297. **template** | *String* | | handlebars template that should replace what was matched by the `pattern`. capture groups are available as $1, $2, etc
  298. **templateFile** | *String* | | path a file containing the `template`
  299. **transform** | *Function* | | [an optional function](#built-in-actions) that can be used to transform the file before writing it to disk
  300. **skip** | *Function* | | *inherited from [ActionConfig](#interface-actionconfig)*
  301. **data** | *Object* | `{}` | *inherited from [ActionConfig](#interface-actionconfig)*
  302. **abortOnFail** | *Boolean* | `true` | *inherited from [ActionConfig](#interface-actionconfig)*
  303. ## Append
  304. The `append` action is a commonly used subset of `modify`. It is used to append data in a file at a particular location.
  305. Property | Type | Default | Description
  306. -------- | ---- | ------- | -----------
  307. **path** | *String* | | handlebars template that (when rendered) is the path of the file to be modified
  308. **pattern** | *RegExp, String* | | regular expression used to match text where the append should happen
  309. **unique** | *Boolean* | `true` | whether identical entries should be removed
  310. **separator** | *String* | `new line` | the value that separates entries
  311. **template** | *String* | | handlebars template to be used for the entry
  312. **templateFile** | *String* | | path a file containing the `template`
  313. **data** | *Object* | `{}` | *inherited from [ActionConfig](#interface-actionconfig)*
  314. **abortOnFail** | *Boolean* | `true` | *inherited from [ActionConfig](#interface-actionconfig)*
  315. ## Custom (Action Function)
  316. The `Add` and `Modify` actions will take care of almost every case that plop is designed to handle. However, plop does offer custom action functions for the node/js guru. A custom action function is a function that is provided in the actions array.
  317. - Custom action functions are executed by plop with the same [CustomAction](#functionsignature-custom-action) function signature.
  318. - Plop will wait for the custom action to complete before executing the next action.
  319. - The function must let plop known what’s happening through the return value. If you return a `Promise`, we won’t start other actions until the promise resolves. If you return a message (*String*), we know that the action is done and we’ll report the message in the status of the action.
  320. - A custom action fails if the promise is rejected, or the function throws an `Exception`
  321. _See the [example plopfile](https://github.com/plopjs/plop/blob/main/packages/plop/tests/examples/javascript/plopfile.js) for a sample synchronous custom action._
  322. ## Comments
  323. Comment lines can be added to the actions array by adding a string in place of an action config object. Comments are printed to the screen when plop comes to them and have no functionality of their own.
  324. # Built-In Helpers
  325. There are a few helpers that I have found useful enough to include with plop. They are mostly case modifiers, but here is the complete list.
  326. ## Case Modifiers
  327. - **camelCase**: changeFormatToThis
  328. - **snakeCase**: change_format_to_this
  329. - **dashCase/kebabCase**: change-format-to-this
  330. - **dotCase**: change.format.to.this
  331. - **pathCase**: change/format/to/this
  332. - **properCase/pascalCase**: ChangeFormatToThis
  333. - **lowerCase**: change format to this
  334. - **sentenceCase**: Change format to this,
  335. - **constantCase**: CHANGE_FORMAT_TO_THIS
  336. - **titleCase**: Change Format To This
  337. ## Other Helpers
  338. - **pkg**: look up a property from a package.json file in the same folder as the plopfile.
  339. # Taking it Further
  340. There is not a lot needed to get up and running on some basic generators. However, if you want to take your plop-fu further, read on young padawan.
  341. ## Using a Dynamic Actions Array
  342. Alternatively, the `actions` property of the [GeneratorConfig](#interface-generatorconfig) can itself be a function that takes the answers data as a parameter and returns the actions array.
  343. This allows you to adapt the actions array based on provided answers:
  344. ``` javascript
  345. export default function (plop) {
  346. plop.setGenerator('test', {
  347. prompts: [{
  348. type: 'confirm',
  349. name: 'wantTacos',
  350. message: 'Do you want tacos?'
  351. }],
  352. actions: function(data) {
  353. var actions = [];
  354. if(data.wantTacos) {
  355. actions.push({
  356. type: 'add',
  357. path: 'folder/{{dashCase name}}.txt',
  358. templateFile: 'templates/tacos.txt'
  359. });
  360. } else {
  361. actions.push({
  362. type: 'add',
  363. path: 'folder/{{dashCase name}}.txt',
  364. templateFile: 'templates/burritos.txt'
  365. });
  366. }
  367. return actions;
  368. }
  369. });
  370. };
  371. ```
  372. ## 3rd Party Prompt Bypass
  373. If you have written an inquirer prompt plugin and want to support plop's bypass functionality, the process is pretty simple. The plugin object that your prompt exports should have a `bypass` function. This `bypass` function will be run by plop with the user's input as the first parameter and the prompt config object as the second parameter. The value that this function returns will be added to the answer data object for that prompt.
  374. ``` javascript
  375. // My confirmation inquirer plugin
  376. export default MyConfirmPluginConstructor;
  377. function MyConfirmPluginConstructor() {
  378. // ...your main plugin code
  379. this.bypass = (rawValue, promptConfig) => {
  380. const lowerVal = rawValue.toString().toLowerCase();
  381. const trueValues = ['t', 'true', 'y', 'yes'];
  382. const falseValues = ['f', 'false', 'n', 'no'];
  383. if (trueValues.includes(lowerVal)) return true;
  384. if (falseValues.includes(lowerVal)) return false;
  385. throw Error(`"${rawValue}" is not a valid ${promptConfig.type} value`);
  386. };
  387. return this;
  388. }
  389. ```
  390. > For the above example, the bypass function takes the user's text input and turns it into a `Boolean` value that will be used as the prompt answer data.
  391. ### Adding Bypass Support to Your Plopfile
  392. If the 3rd party prompt plugin you are using does not support bypass by default, you can add the `bypass` function above to your prompt's config object and plop will use it for handling bypass data for that prompt.
  393. ## Wrapping Plop
  394. Plop provides a lot of powerful functionality "for free". This utility is so powerful, in fact, that you can even wrap `plop`
  395. into your own CLI project. To do so, you only need a `plopfile.js`, a `package.json`, and a template to reference.
  396. Your `index.js` file should look like the following:
  397. ```javascript
  398. #!/usr/bin/env node
  399. import path from "node:path";
  400. import minimist from "minimist";
  401. import { Plop, run } from "plop";
  402. const args = process.argv.slice(2);
  403. const argv = minimist(args);
  404. import { dirname } from "node:path";
  405. import { fileURLToPath } from "node:url";
  406. const __dirname = dirname(fileURLToPath(import.meta.url));
  407. Plop.prepare({
  408. cwd: argv.cwd,
  409. configPath: path.join(__dirname, 'plopfile.js'),
  410. preload: argv.preload || [],
  411. completion: argv.completion
  412. }, env => Plop.execute(env, run));
  413. ```
  414. And your `package.json` should look like the following:
  415. ```json
  416. {
  417. "name": "create-your-name-app",
  418. "version": "1.0.0",
  419. "main": "index.js",
  420. "scripts": {
  421. "start": "plop",
  422. },
  423. "bin": {
  424. "create-your-name-app": "./index.js"
  425. },
  426. "preferGlobal": true,
  427. "dependencies": {
  428. "plop": "^3.0.0"
  429. }
  430. }
  431. ```
  432. ### Setting the base destination path for the wrapper
  433. When wrapping plop, you might want to have the destination path to be based on the cwd when running the wrapper. You can configure the `dest` base path like this:
  434. ```javascript
  435. Plop.prepare({
  436. // config like above
  437. }, env =>
  438. Plop.execute(env, (env) => {
  439. const options = {
  440. ...env,
  441. dest: process.cwd() // this will make the destination path to be based on the cwd when calling the wrapper
  442. }
  443. return run(options, undefined, true)
  444. })
  445. )
  446. ```
  447. ### Adding General CLI Actions
  448. Many CLI utilities handle some actions for you, such as running `git init` or `npm install` once the template is generated.
  449. While we'd like to provide these actions, we also want to keep the core actions limited in scope. As such, we maintain a collection of libraries built to add these actions to Plop in [our Awesome Plop list](https://github.com/plopjs/awesome-plop). There, you'll be able to find options for those actions, or even build your own and add it to the list!
  450. ### Further Customization
  451. While `plop` provides a great level of customization for CLI utility wrappers, there may be usecases where you simply
  452. want more control over the CLI experience while also utilizing the template generation code.
  453. Luckily, [`node-plop`](https://github.com/plopjs/node-plop/) may be for you! It's what the `plop` CLI itself is built
  454. upon and can be easily extended for other usage in the CLI. However, be warned, documentation is not quite as fleshed out
  455. for integration with `node-plop`. That is to say `Thar be dragons`.
  456. > We note lackluster documentation on `node-plop` integration not as a point of pride, but rather a word of warning.
  457. > If you'd like to contribute documentation to the project, please do so! We always welcome and encourage contributions!