Skip to content

Add feature to support prepend #414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,20 @@ Property | Type | Default | Description
**data** | *Object* | `{}` | *inherited from [ActionConfig](#interface-actionconfig)*
**abortOnFail** | *Boolean* | `true` | *inherited from [ActionConfig](#interface-actionconfig)*

## Prepend
The `prepend` action is a similar to `append`. The only difference is that it used to prepend the data in a file at a particular location.

Property | Type | Default | Description
-------- | ---- | ------- | -----------
**path** | *String* | | handlebars template that (when rendered) is the path of the file to be modified
**pattern** | *RegExp, String* | | regular expression used to match text where the prepend should happen
**unique** | *Boolean* | `true` | whether identical entries should be removed
**separator** | *String* | `new line` | the value that separates entries
**template** | *String* | | handlebars template to be used for the entry
**templateFile** | *String* | | path a file containing the `template`
**data** | *Object* | `{}` | *inherited from [ActionConfig](#interface-actionconfig)*
**abortOnFail** | *Boolean* | `true` | *inherited from [ActionConfig](#interface-actionconfig)*

## Custom (Action Function)
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.
- Custom action functions are executed by plop with the same [CustomAction](#functionsignature-custom-action) function signature.
Expand Down
10 changes: 5 additions & 5 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ import { NodePlopAPI } from 'plop';

export default function (plop: NodePlopAPI) {

  // plop generator code
// plop generator code

};
```
Expand All @@ -184,13 +184,13 @@ export default function (plop: NodePlopAPI) {

module.exports = function (

  /** @type {import('plop').NodePlopAPI} */
/** @type {import('plop').NodePlopAPI} */

  plop
plop

) {

  // plop generator code
// plop generator code

};
```
Expand Down Expand Up @@ -316,7 +316,7 @@ export default function (plop) {
| **prompts** | *Array[[InquirerQuestion](https://github.com/SBoudrias/Inquirer.js/#question)]* | | 需要询问用户的问题 |
| **actions** | *Array[[ActionConfig](#interface-actionconfig)]* | | 需要执行的操作 |

  >如果你的Action列表有动态需求,你可以查看[使用动态action数组](#using-a-dynamic-actions-array)部分内容。
>如果你的Action列表有动态需求,你可以查看[使用动态action数组](#using-a-dynamic-actions-array)部分内容。

### *接口* `ActionConfig`

Expand Down
1 change: 1 addition & 0 deletions packages/node-plop/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lib

# Test mocks
tests/*-mock/src
tests/*/src

# IDE files
.idea
3 changes: 2 additions & 1 deletion packages/node-plop/src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ import add from "./add.js";
import addMany from "./addMany.js";
import modify from "./modify.js";
import append from "./append.js";
import prepend from "./prepend.js";

export { add, addMany, modify, append };
export { add, addMany, modify, append, prepend };
64 changes: 64 additions & 0 deletions packages/node-plop/src/actions/prepend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as fspp from "../fs-promise-proxy.js";

import {
getRenderedTemplate,
getRenderedTemplatePath,
makeDestPath,
throwStringifiedError,
getRelativeToBasePath,
} from "./_common-action-utils.js";

import actionInterfaceTest from "./_common-action-interface-check.js";

const doPrepend = async function (data, cfg, plop, fileData) {
const stringToPrepend = await getRenderedTemplate(data, cfg, plop);
// if the prepended string should be unique (default),
// remove any occurence of it (but only if pattern would match)

const { separator = "\n" } = cfg;
if (cfg.unique !== false) {
// only remove after "pattern", so that we remove not too much accidentally
const parts = fileData.split(cfg.pattern);
const firstPart = parts[0];
const firstPartWithoutDuplicates = firstPart.replace(
new RegExp(stringToPrepend + separator, "g"),
"",
);
fileData = fileData.replace(firstPart, firstPartWithoutDuplicates);
}

// add the prepended string to the start of the "fileData" if "pattern"
// was not provided, i.e. null or false
if (!cfg.pattern) {
// make sure to add a "separator" if "fileData" is not empty
if (fileData.length > 0) {
fileData = separator + fileData;
}
return stringToPrepend + fileData;
}

return fileData.replace(cfg.pattern, stringToPrepend + separator + "$&");
};

export default async function (data, cfg, plop) {
const interfaceTestResult = actionInterfaceTest(cfg);
if (interfaceTestResult !== true) {
throw interfaceTestResult;
}
const fileDestPath = makeDestPath(data, cfg, plop);
try {
// check path
const pathExists = await fspp.fileExists(fileDestPath);
if (!pathExists) {
throw "File does not exist";
} else {
let fileData = await fspp.readFile(fileDestPath);
cfg.templateFile = getRenderedTemplatePath(data, cfg, plop);
fileData = await doPrepend(data, cfg, plop, fileData);
await fspp.writeFile(fileDestPath, fileData);
}
return getRelativeToBasePath(fileDestPath, plop);
} catch (err) {
throwStringifiedError(err);
}
}
11 changes: 11 additions & 0 deletions packages/node-plop/tests/prepend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "prepend-test",
"private": true,
"type": "module",
"config": {
"nested": [
"basic-plopfile-test-propertyPath-value-index-0",
"basic-plopfile-test-propertyPath-value-index-1"
]
}
}
7 changes: 7 additions & 0 deletions packages/node-plop/tests/prepend/plop-templates/list.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Don't remove me: Plop

-- PREPEND ITEMS HERE --

/* PREPEND OTHER ITEMS HERE */

+++++++++++++++++++++++++++++++++++++++
120 changes: 120 additions & 0 deletions packages/node-plop/tests/prepend/plopfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
export default function (plop) {
plop.setGenerator("make-list", {
prompts: [
{
type: "input",
name: "listName",
message: "What's the list name?",
validate: function (value) {
if (/.+/.test(value)) {
return true;
}
return "name is required";
},
},
],
actions: [
{
type: "add",
path: "src/{{listName}}.txt",
templateFile: "plop-templates/list.txt",
},
],
});

plop.setGenerator("prepend-to-list", {
description: "prepend entry to a list",
prompts: [
{
type: "input",
name: "listName",
message: "What's the list name?",
validate: function (value) {
if (/.+/.test(value)) {
return true;
}
return "name is required";
},
},
{
type: "input",
name: "name",
message: "What is your name?",
validate: function (value) {
if (/.+/.test(value)) {
return true;
}
return "name is required";
},
},
{
type: "confirm",
name: "allowDuplicates",
message: "Allow Duplicates?",
},
],
actions: ({ allowDuplicates }) => [
{
type: "prepend",
path: "src/{{listName}}.txt",
pattern: /-- PREPEND ITEMS HERE --/gi,
template: "name: {{name}}1",
unique: !allowDuplicates,
},
{
type: "prepend",
path: "src/{{listName}}.txt",
pattern: "/* PREPEND OTHER ITEMS HERE */",
template: "name: {{name}}2",
unique: !allowDuplicates,
},
],
});

plop.setGenerator("prepend-without-pattern", {
description: "prepend entry to a list without pattern",
prompts: [
{
type: "input",
name: "listName",
message: "What's the list name?",
validate: function (value) {
if (/.+/.test(value)) {
return true;
}
return "name is required";
},
},
{
type: "input",
name: "name",
message: "What is your name?",
validate: function (value) {
if (/.+/.test(value)) {
return true;
}
return "name is required";
},
},
{
type: "confirm",
name: "allowDuplicates",
message: "Allow Duplicates?",
},
],
actions: ({ allowDuplicates }) => [
{
type: "prepend",
path: "src/{{listName}}.txt",
template: "name: {{name}}1",
unique: !allowDuplicates,
},
{
type: "prepend",
path: "src/{{listName}}.txt",
template: "name: {{name}}2",
unique: !allowDuplicates,
},
],
});
}
Loading