Skip to content
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

feat: Add ability to customise type displays #363

Open
wants to merge 2 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
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,43 @@ export default function (plop) {
});
};
```
## setActionTypeDisplay
`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 `++`.

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:

``` javascript
import chalk from 'chalk';

export default function (plop) {
// Define your custom action that asynchronously adds a file
plop.setActionType('fetchAndAddAsync', function (answers, config, plop) {
return new Promise((resolve, reject) => {
if (success) {
resolve('success status message');
} else {
reject('error message');
}
});
});

// Use the same action type as 'add' for consistency
plop.setActionTypeDisplay('fetchAndAddAsync', chalk.green('++'));
};
```

By default, the following type displays are set:

``` javascript
const typeDisplay = {
function: chalk.yellow("->"),
add: chalk.green("++"),
addMany: chalk.green("+!"),
modify: `${chalk.green("+")}${chalk.red("-")}`,
append: chalk.green("_+"),
skip: chalk.green("--"),
};
```

## setPrompt
[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).
Expand Down
16 changes: 16 additions & 0 deletions packages/node-plop/src/node-plop.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
const generators = {};
const partials = {};
const actionTypes = {};
const actionTypeDisplays = {};
const helpers = Object.assign(
{
pkg: (propertyPath) => _get(pkgJson, propertyPath, ""),
Expand All @@ -39,6 +40,9 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
const setPartial = (name, str) => {
partials[name] = str;
};
const setActionTypeDisplay = (name, typeDisplay) => {
actionTypeDisplays[name] = typeDisplay;
};
const setActionType = (name, fn) => {
actionTypes[name] = fn;
};
Expand All @@ -57,6 +61,7 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
const getHelper = (name) => helpers[name];
const getPartial = (name) => partials[name];
const getActionType = (name) => actionTypes[name];
const getActionTypeDisplay = (name) => actionTypeDisplays[name];
const getGenerator = (name) => generators[name];
function setGenerator(name = "", config = {}) {
// if no name is provided, use a default
Expand All @@ -75,6 +80,7 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
Object.keys(helpers).filter((h) => !baseHelpers.includes(h));
const getPartialList = () => Object.keys(partials);
const getActionTypeList = () => Object.keys(actionTypes);
const getActionTypeDisplayList = () => Object.keys(actionTypeDisplays);
function getGeneratorList() {
return Object.keys(generators).map(function (name) {
const { description } = generators[name];
Expand Down Expand Up @@ -118,6 +124,7 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
helpers: false,
partials: false,
actionTypes: false,
actionTypeDisplays: false,
},
includeCfg
);
Expand Down Expand Up @@ -147,6 +154,12 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
setActionType,
proxy.getActionType
);
loadAsset(
proxy.getActionTypeDisplayList(),
includeCfg === true || include.actionTypeDisplays,
setActionTypeDisplay,
proxy.getActionTypeDisplay
);
})
);
}
Expand Down Expand Up @@ -202,6 +215,9 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
setActionType,
getActionType,
getActionTypeList,
setActionTypeDisplay,
getActionTypeDisplay,
getActionTypeDisplayList,

// path context methods
setPlopfilePath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,24 @@ describe("imported-custom-action", function () {
true
);
});

test("imported custom action can define a custom type display string", async function () {
const plop = await nodePlop();
const testFilePath = path.resolve(testSrcPath, "test.txt");
plop.setActionType("custom-del", customAction);
plop.setActionTypeDisplay("custom-del", "><");

// add the file
const addTestFile = { type: "add", path: testFilePath };
// remove the file
const deleteTestFile = { type: "custom-del", path: testFilePath };

const generator = plop.setGenerator("", {
actions: [addTestFile, deleteTestFile],
});

expect(typeof plop.getActionType("custom-del")).toBe("function");
expect(typeof plop.getActionTypeDisplay("custom-del")).toBe("string");
expect(plop.getActionTypeDisplay("custom-del")).toBe("><");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,27 @@ describe("load-assets-from-plopfile", function () {
helpers: true,
partials: true,
actionTypes: true,
actionTypeDisplays: true,
}
);

const gNameList = plop.getGeneratorList().map((g) => g.name);
expect(gNameList.length).toBe(3);
expect(plop.getHelperList().length).toBe(3);
expect(plop.getPartialList().length).toBe(3);
expect(plop.getActionTypeList().length).toBe(1);
expect(plop.getActionTypeList().length).toBe(2);
expect(plop.getActionTypeDisplayList().length).toBe(1);
expect(gNameList.includes("test-generator1")).toBe(true);
expect(plop.getHelperList().includes("test-helper2")).toBe(true);
expect(plop.getPartialList().includes("test-partial3")).toBe(true);
expect(plop.getActionTypeList().includes("test-actionType1")).toBe(true);
expect(plop.getActionTypeList().includes("test-actionType2")).toBe(true);
expect(plop.getActionTypeDisplayList().includes("test-actionType1")).toBe(
false
);
expect(plop.getActionTypeDisplayList().includes("test-actionType2")).toBe(
true
);
});

test("plop.load passes a config option that can be used to include all the plopfile output", async function () {
Expand All @@ -101,11 +110,19 @@ describe("load-assets-from-plopfile", function () {
expect(gNameList.length).toBe(3);
expect(plop.getHelperList().length).toBe(3);
expect(plop.getPartialList().length).toBe(3);
expect(plop.getActionTypeList().length).toBe(1);
expect(plop.getActionTypeList().length).toBe(2);
expect(plop.getActionTypeDisplayList().length).toBe(1);
expect(gNameList.includes("test-generator1")).toBe(true);
expect(plop.getHelperList().includes("test-helper2")).toBe(true);
expect(plop.getPartialList().includes("test-partial3")).toBe(true);
expect(plop.getActionTypeList().includes("test-actionType1")).toBe(true);
expect(plop.getActionTypeList().includes("test-actionType2")).toBe(true);
expect(plop.getActionTypeDisplayList().includes("test-actionType1")).toBe(
false
);
expect(plop.getActionTypeDisplayList().includes("test-actionType2")).toBe(
true
);
});

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

test("plop.load can include only helpers", async function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export default function (plop, config = {}) {
plop.setPartial(`${cfg.prefix}partial3`, "partial 3: {{name}}");

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

const generatorObject = {
actions: [{ type: "add", path: "src/{{name}}.txt" }],
Expand Down
7 changes: 7 additions & 0 deletions packages/node-plop/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface IncludeDefinitionConfig {
helpers?: boolean;
partials?: boolean;
actionTypes?: boolean;
actionTypeDisplays?: boolean;
}

export type IncludeDefinition = boolean | string[] | IncludeDefinitionConfig;
Expand Down Expand Up @@ -46,6 +47,8 @@ export interface NodePlopAPI {

setActionType(name: string, fn: CustomActionFunction): void;

setActionTypeDisplay(name: string, typeDisplay: string): void;

/**
* This does not include a `CustomActionConfig` for the same reasons
* Listed in the `ActionType` declaration. Please see that JSDoc for more
Expand All @@ -54,6 +57,10 @@ export interface NodePlopAPI {

getActionTypeList(): string[];

getActionTypeDisplay(name: string): string;

getActionTypeDisplayList(): string[];

setPlopfilePath(filePath: string): void;

getPlopfilePath(): string;
Expand Down
Empty file modified packages/plop/bin/plop.js
100644 → 100755
Empty file.
6 changes: 6 additions & 0 deletions packages/plop/src/console-out.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ const typeDisplay = {
append: chalk.green("_+"),
skip: chalk.green("--"),
};

const addToTypeDisplay = (name, characters) => {
typeDisplay[name] = characters;
};

const typeMap = (name, noMap) => {
const dimType = chalk.dim(name);
return noMap ? dimType : typeDisplay[name] || dimType;
Expand All @@ -126,6 +131,7 @@ export {
chooseOptionFromList,
displayHelpScreen,
createInitPlopfile,
addToTypeDisplay,
typeMap,
getHelpMessage,
};
4 changes: 4 additions & 0 deletions packages/plop/src/plop.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ async function run(env, _, passArgsBeforeDashes) {
plop,
passArgsBeforeDashes
);
const actionTypeDisplays = plop.getActionTypeDisplayList();
actionTypeDisplays.forEach((type) => {
out.addToTypeDisplay(type, plop.getActionTypeDisplay(type));
});

// look up a generator and run it with calculated bypass data
const runGeneratorByName = (name) => {
Expand Down
16 changes: 16 additions & 0 deletions packages/plop/tests/actions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ test("Plop to add and rename files", async () => {
const data = fs.readFileSync(expectedFilePath, "utf8");

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

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

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

test.todo("Test modify");
test.todo("Test append");
test.todo("Test built-in helpers");
test.todo("Test custom helpers");

test("Plop to display a custom string for a given action type", async () => {
const expectedFilePath = await getFilePath(
"./examples/custom-action-display/output/out.txt"
);

const { findByText } = await renderPlop(["addFile"], {
cwd: resolve(__dirname, "./examples/custom-action-display"),
});

await waitFor(() => fs.promises.stat(expectedFilePath));

expect(await findByText(">< /output/out.txt")).toBeInTheConsole();
});
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "plop-example-custom-action-display",
"type": "module",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
}
}
14 changes: 14 additions & 0 deletions packages/plop/tests/examples/custom-action-display/plopfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default function (plop) {
plop.setActionTypeDisplay("add", "><");
plop.setGenerator("addFile", {
description: "Add a file",
prompts: [],
actions: [
{
type: "add",
path: "./output/out.txt",
templateFile: "./templates/to-add.txt",
},
],
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello