Skip to content

Commit 953cb48

Browse files
committed
feat: Add ability to customise type displays
1 parent 4433a20 commit 953cb48

File tree

13 files changed

+143
-2
lines changed

13 files changed

+143
-2
lines changed

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,43 @@ export default function (plop) {
245245
});
246246
};
247247
```
248+
## setActionTypeDisplay
249+
`setActionTypeDisplay` allows you to change the characters shown on the output of a specific action. For instance, the `add` action's output is prefixed with `++`.
250+
251+
You may need to write a custom action that fetches a file from an API and adds it, and you may want to use the `++` prefix for consistency. This could be done with the following:
252+
253+
``` javascript
254+
import chalk from 'chalk';
255+
256+
export default function (plop) {
257+
// Define your custom action that asynchronously adds a file
258+
plop.setActionType('fetchAndAddAsync', function (answers, config, plop) {
259+
return new Promise((resolve, reject) => {
260+
if (success) {
261+
resolve('success status message');
262+
} else {
263+
reject('error message');
264+
}
265+
});
266+
});
267+
268+
// Use the same action type as 'add' for consistency
269+
plop.setActionTypeDisplay('fetchAndAddAsync', chalk.green('++'));
270+
};
271+
```
272+
273+
By default, the following type displays are set:
274+
275+
``` javascript
276+
const typeDisplay = {
277+
function: chalk.yellow("->"),
278+
add: chalk.green("++"),
279+
addMany: chalk.green("+!"),
280+
modify: `${chalk.green("+")}${chalk.red("-")}`,
281+
append: chalk.green("_+"),
282+
skip: chalk.green("--"),
283+
};
284+
```
248285

249286
## setPrompt
250287
[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).

packages/node-plop/src/node-plop.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
2121
const generators = {};
2222
const partials = {};
2323
const actionTypes = {};
24+
const actionTypeDisplays = {};
2425
const helpers = Object.assign(
2526
{
2627
pkg: (propertyPath) => _get(pkgJson, propertyPath, ""),
@@ -39,6 +40,9 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
3940
const setPartial = (name, str) => {
4041
partials[name] = str;
4142
};
43+
const setActionTypeDisplay = (name, typeDisplay) => {
44+
actionTypeDisplays[name] = typeDisplay;
45+
};
4246
const setActionType = (name, fn) => {
4347
actionTypes[name] = fn;
4448
};
@@ -57,6 +61,7 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
5761
const getHelper = (name) => helpers[name];
5862
const getPartial = (name) => partials[name];
5963
const getActionType = (name) => actionTypes[name];
64+
const getActionTypeDisplay = (name) => actionTypeDisplays[name];
6065
const getGenerator = (name) => generators[name];
6166
function setGenerator(name = "", config = {}) {
6267
// if no name is provided, use a default
@@ -75,6 +80,7 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
7580
Object.keys(helpers).filter((h) => !baseHelpers.includes(h));
7681
const getPartialList = () => Object.keys(partials);
7782
const getActionTypeList = () => Object.keys(actionTypes);
83+
const getActionTypeDisplayList = () => Object.keys(actionTypeDisplays);
7884
function getGeneratorList() {
7985
return Object.keys(generators).map(function (name) {
8086
const { description } = generators[name];
@@ -118,6 +124,7 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
118124
helpers: false,
119125
partials: false,
120126
actionTypes: false,
127+
actionTypeDisplays: false,
121128
},
122129
includeCfg
123130
);
@@ -147,6 +154,12 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
147154
setActionType,
148155
proxy.getActionType
149156
);
157+
loadAsset(
158+
proxy.getActionTypeDisplayList(),
159+
includeCfg === true || include.actionTypeDisplays,
160+
setActionTypeDisplay,
161+
proxy.getActionTypeDisplay
162+
);
150163
})
151164
);
152165
}
@@ -202,6 +215,9 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
202215
setActionType,
203216
getActionType,
204217
getActionTypeList,
218+
setActionTypeDisplay,
219+
getActionTypeDisplay,
220+
getActionTypeDisplayList,
205221

206222
// path context methods
207223
setPlopfilePath,

packages/node-plop/tests/imported-custom-action/imported-custom-action.spec.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,24 @@ describe("imported-custom-action", function () {
6262
true
6363
);
6464
});
65+
66+
test("imported custom action can define a custom type display string", async function () {
67+
const plop = await nodePlop();
68+
const testFilePath = path.resolve(testSrcPath, "test.txt");
69+
plop.setActionType("custom-del", customAction);
70+
plop.setActionTypeDisplay("custom-del", "><");
71+
72+
// add the file
73+
const addTestFile = { type: "add", path: testFilePath };
74+
// remove the file
75+
const deleteTestFile = { type: "custom-del", path: testFilePath };
76+
77+
const generator = plop.setGenerator("", {
78+
actions: [addTestFile, deleteTestFile],
79+
});
80+
81+
expect(typeof plop.getActionType("custom-del")).toBe("function");
82+
expect(typeof plop.getActionTypeDisplay("custom-del")).toBe("string");
83+
expect(plop.getActionTypeDisplay("custom-del")).toBe("><");
84+
});
6585
});

packages/node-plop/tests/load-assets-from-plopfile/load-assets-from-plopfile.spec.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,27 @@ describe("load-assets-from-plopfile", function () {
7979
helpers: true,
8080
partials: true,
8181
actionTypes: true,
82+
actionTypeDisplays: true,
8283
}
8384
);
8485

8586
const gNameList = plop.getGeneratorList().map((g) => g.name);
8687
expect(gNameList.length).toBe(3);
8788
expect(plop.getHelperList().length).toBe(3);
8889
expect(plop.getPartialList().length).toBe(3);
89-
expect(plop.getActionTypeList().length).toBe(1);
90+
expect(plop.getActionTypeList().length).toBe(2);
91+
expect(plop.getActionTypeDisplayList().length).toBe(1);
9092
expect(gNameList.includes("test-generator1")).toBe(true);
9193
expect(plop.getHelperList().includes("test-helper2")).toBe(true);
9294
expect(plop.getPartialList().includes("test-partial3")).toBe(true);
9395
expect(plop.getActionTypeList().includes("test-actionType1")).toBe(true);
96+
expect(plop.getActionTypeList().includes("test-actionType2")).toBe(true);
97+
expect(plop.getActionTypeDisplayList().includes("test-actionType1")).toBe(
98+
false
99+
);
100+
expect(plop.getActionTypeDisplayList().includes("test-actionType2")).toBe(
101+
true
102+
);
94103
});
95104

96105
test("plop.load passes a config option that can be used to include all the plopfile output", async function () {
@@ -101,11 +110,19 @@ describe("load-assets-from-plopfile", function () {
101110
expect(gNameList.length).toBe(3);
102111
expect(plop.getHelperList().length).toBe(3);
103112
expect(plop.getPartialList().length).toBe(3);
104-
expect(plop.getActionTypeList().length).toBe(1);
113+
expect(plop.getActionTypeList().length).toBe(2);
114+
expect(plop.getActionTypeDisplayList().length).toBe(1);
105115
expect(gNameList.includes("test-generator1")).toBe(true);
106116
expect(plop.getHelperList().includes("test-helper2")).toBe(true);
107117
expect(plop.getPartialList().includes("test-partial3")).toBe(true);
108118
expect(plop.getActionTypeList().includes("test-actionType1")).toBe(true);
119+
expect(plop.getActionTypeList().includes("test-actionType2")).toBe(true);
120+
expect(plop.getActionTypeDisplayList().includes("test-actionType1")).toBe(
121+
false
122+
);
123+
expect(plop.getActionTypeDisplayList().includes("test-actionType2")).toBe(
124+
true
125+
);
109126
});
110127

111128
test("plop.load should import functioning assets", async function () {
@@ -124,6 +141,7 @@ describe("load-assets-from-plopfile", function () {
124141
expect(plop.getHelper("test-helper2")("test")).toBe("helper 2: test");
125142
expect(plop.getPartial("test-partial3")).toBe("partial 3: {{name}}");
126143
expect(plop.getActionType("test-actionType1")()).toBe("test");
144+
expect(plop.getActionType("test-actionType2")()).toBe("test");
127145
});
128146

129147
test("plop.load can include only helpers", async function () {

packages/node-plop/tests/load-assets-from-plopfile/plopfile.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export default function (plop, config = {}) {
1010
plop.setPartial(`${cfg.prefix}partial3`, "partial 3: {{name}}");
1111

1212
plop.setActionType(`${cfg.prefix}actionType1`, () => "test");
13+
plop.setActionType(`${cfg.prefix}actionType2`, () => "test");
14+
plop.setActionTypeDisplay(`${cfg.prefix}actionType2`, "><");
1315

1416
const generatorObject = {
1517
actions: [{ type: "add", path: "src/{{name}}.txt" }],

packages/plop/bin/plop.js

100644100755
File mode changed.

packages/plop/src/console-out.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ const typeDisplay = {
117117
append: chalk.green("_+"),
118118
skip: chalk.green("--"),
119119
};
120+
121+
const addToTypeDisplay = (name, characters) => {
122+
typeDisplay[name] = characters;
123+
};
124+
120125
const typeMap = (name, noMap) => {
121126
const dimType = chalk.dim(name);
122127
return noMap ? dimType : typeDisplay[name] || dimType;
@@ -126,6 +131,7 @@ export {
126131
chooseOptionFromList,
127132
displayHelpScreen,
128133
createInitPlopfile,
134+
addToTypeDisplay,
129135
typeMap,
130136
getHelpMessage,
131137
};

packages/plop/src/plop.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ async function run(env, _, passArgsBeforeDashes) {
7070
plop,
7171
passArgsBeforeDashes
7272
);
73+
const actionTypeDisplays = plop.getActionTypeDisplayList();
74+
actionTypeDisplays.forEach((type) => {
75+
out.addToTypeDisplay(type, plop.getActionTypeDisplay(type));
76+
});
7377

7478
// look up a generator and run it with calculated bypass data
7579
const runGeneratorByName = (name) => {

packages/plop/tests/actions.spec.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ test("Plop to add and rename files", async () => {
2727
const data = fs.readFileSync(expectedFilePath, "utf8");
2828

2929
expect(data).toMatch(/Hello/);
30+
expect(await findByText("++ /output/new-output.txt")).toBeInTheConsole();
3031
});
3132

3233
test("Plop to add and change file contents", async () => {
@@ -48,9 +49,24 @@ test("Plop to add and change file contents", async () => {
4849
const data = await fs.promises.readFile(expectedFilePath, "utf8");
4950

5051
expect(data).toMatch(/Hi Corbin!/);
52+
expect(await findByText("++ /output/new-output.txt")).toBeInTheConsole();
5153
});
5254

5355
test.todo("Test modify");
5456
test.todo("Test append");
5557
test.todo("Test built-in helpers");
5658
test.todo("Test custom helpers");
59+
60+
test("Plop to display a custom string for a given action type", async () => {
61+
const expectedFilePath = await getFilePath(
62+
"./examples/custom-action-display/output/out.txt"
63+
);
64+
65+
const { findByText } = await renderPlop(["addFile"], {
66+
cwd: resolve(__dirname, "./examples/custom-action-display"),
67+
});
68+
69+
await waitFor(() => fs.promises.stat(expectedFilePath));
70+
71+
expect(await findByText(">< /output/out.txt")).toBeInTheConsole();
72+
});

packages/plop/tests/examples/custom-action-display/output/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)