Skip to content

Commit e1db8b3

Browse files
committed
feat: Add ability to customise type displays
1 parent e012227 commit e1db8b3

File tree

13 files changed

+156
-15
lines changed

13 files changed

+156
-15
lines changed

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,43 @@ export default function (plop) {
279279
});
280280
};
281281
```
282+
## setActionTypeDisplay
283+
`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 `++`.
284+
285+
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:
286+
287+
``` javascript
288+
import chalk from 'chalk';
289+
290+
export default function (plop) {
291+
// Define your custom action that asynchronously adds a file
292+
plop.setActionType('fetchAndAddAsync', function (answers, config, plop) {
293+
return new Promise((resolve, reject) => {
294+
if (success) {
295+
resolve('success status message');
296+
} else {
297+
reject('error message');
298+
}
299+
});
300+
});
301+
302+
// Use the same action type as 'add' for consistency
303+
plop.setActionTypeDisplay('fetchAndAddAsync', chalk.green('++'));
304+
};
305+
```
306+
307+
By default, the following type displays are set:
308+
309+
``` javascript
310+
const typeDisplay = {
311+
function: chalk.yellow("->"),
312+
add: chalk.green("++"),
313+
addMany: chalk.green("+!"),
314+
modify: `${chalk.green("+")}${chalk.red("-")}`,
315+
append: chalk.green("_+"),
316+
skip: chalk.green("--"),
317+
};
318+
```
282319

283320
## setPrompt
284321
[Inquirer](https://github.com/SBoudrias/Inquirer.js/blob/master/packages/inquirer/README.md) 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/blob/master/packages/inquirer/README.md#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: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ 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, ""),
2728
},
28-
bakedInHelpers,
29+
bakedInHelpers
2930
);
3031
const baseHelpers = Object.keys(helpers);
3132

@@ -39,16 +40,19 @@ 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
};
4549

4650
function renderString(template, data) {
4751
Object.keys(helpers).forEach((h) =>
48-
handlebars.registerHelper(h, helpers[h]),
52+
handlebars.registerHelper(h, helpers[h])
4953
);
5054
Object.keys(partials).forEach((p) =>
51-
handlebars.registerPartial(p, partials[p]),
55+
handlebars.registerPartial(p, partials[p])
5256
);
5357
return handlebars.compile(template)(data);
5458
}
@@ -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];
@@ -103,7 +109,7 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
103109
{
104110
destBasePath: getDestBasePath(),
105111
},
106-
loadCfg,
112+
loadCfg
107113
);
108114

109115
await Promise.all(
@@ -118,36 +124,43 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
118124
helpers: false,
119125
partials: false,
120126
actionTypes: false,
127+
actionTypeDisplays: false,
121128
},
122-
includeCfg,
129+
includeCfg
123130
);
124131

125132
const genNameList = proxy.getGeneratorList().map((g) => g.name);
126133
loadAsset(
127134
genNameList,
128135
includeCfg === true || include.generators,
129136
setGenerator,
130-
(proxyName) => ({ proxyName, proxy }),
137+
(proxyName) => ({ proxyName, proxy })
131138
);
132139
loadAsset(
133140
proxy.getPartialList(),
134141
includeCfg === true || include.partials,
135142
setPartial,
136-
proxy.getPartial,
143+
proxy.getPartial
137144
);
138145
loadAsset(
139146
proxy.getHelperList(),
140147
includeCfg === true || include.helpers,
141148
setHelper,
142-
proxy.getHelper,
149+
proxy.getHelper
143150
);
144151
loadAsset(
145152
proxy.getActionTypeList(),
146153
includeCfg === true || include.actionTypes,
147154
setActionType,
148-
proxy.getActionType,
155+
proxy.getActionType
156+
);
157+
loadAsset(
158+
proxy.getActionTypeDisplayList(),
159+
includeCfg === true || include.actionTypeDisplays,
160+
setActionTypeDisplay,
161+
proxy.getActionTypeDisplay
149162
);
150-
}),
163+
})
151164
);
152165
}
153166

@@ -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: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ describe("load-assets-from-plopfile", function () {
5757
generator1: "gen1",
5858
generator3: "bob",
5959
},
60-
},
60+
}
6161
);
6262

6363
const gNameList = plop.getGeneratorList().map((g) => g.name);
@@ -79,18 +79,27 @@ describe("load-assets-from-plopfile", function () {
7979
helpers: true,
8080
partials: true,
8181
actionTypes: true,
82-
},
82+
actionTypeDisplays: true,
83+
}
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 () {
@@ -118,12 +135,13 @@ describe("load-assets-from-plopfile", function () {
118135
helpers: true,
119136
partials: true,
120137
actionTypes: true,
121-
},
138+
}
122139
);
123140

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
@@ -133,6 +133,11 @@ const typeDisplay = {
133133
append: chalk.green("_+"),
134134
skip: chalk.green("--"),
135135
};
136+
137+
const addToTypeDisplay = (name, characters) => {
138+
typeDisplay[name] = characters;
139+
};
140+
136141
const typeMap = (name, noMap) => {
137142
const dimType = chalk.dim(name);
138143
return noMap ? dimType : typeDisplay[name] || dimType;
@@ -142,6 +147,7 @@ export {
142147
chooseOptionFromList,
143148
displayHelpScreen,
144149
createInitPlopfile,
150+
addToTypeDisplay,
145151
typeMap,
146152
getHelpMessage,
147153
};

packages/plop/src/plop.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ async function run(env, _, passArgsBeforeDashes) {
6868
plop,
6969
passArgsBeforeDashes,
7070
);
71+
const actionTypeDisplays = plop.getActionTypeDisplayList();
72+
actionTypeDisplays.forEach((type) => {
73+
out.addToTypeDisplay(type, plop.getActionTypeDisplay(type));
74+
});
7175

7276
// look up a generator and run it with calculated bypass data
7377
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)