Skip to content

Commit

Permalink
feat(plopjs#297): added support for typescript files
Browse files Browse the repository at this point in the history
  • Loading branch information
prisis committed Apr 12, 2024
1 parent b797aaf commit 894d91b
Show file tree
Hide file tree
Showing 12 changed files with 450 additions and 4 deletions.
1 change: 1 addition & 0 deletions packages/node-plop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"handlebars": "^4.7.8",
"inquirer": "^9.2.12",
"isbinaryfile": "^5.0.0",
"jiti": "^1.21.0",
"lodash.get": "^4.4.2",
"mkdirp": "^3.0.1",
"resolve": "^1.22.8",
Expand Down
6 changes: 4 additions & 2 deletions packages/node-plop/src/node-plop.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import resolve from "resolve";

import bakedInHelpers from "./baked-in-helpers.js";
import generatorRunner from "./generator-runner.js";
import tryRequire from "./try-require.js";

import { createRequire } from "node:module";
import { pathToFileURL } from "url";
const require = createRequire(import.meta.url);

async function nodePlop(plopfilePath = "", plopCfg = {}) {
Expand Down Expand Up @@ -262,7 +262,9 @@ async function nodePlop(plopfilePath = "", plopCfg = {}) {
loadPackageJson();

const joinedPath = path.join(plopfilePath, plopFileName);
const plopFileExport = await import(pathToFileURL(joinedPath).href);

const plopFileExport = tryRequire(joinedPath, plopfilePath, null);

const plop =
typeof plopFileExport === "function"
? plopFileExport
Expand Down
37 changes: 37 additions & 0 deletions packages/node-plop/src/try-require.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import jiti from "jiti";

/**
* Try to require a module, if it fails, return the errorReturn value
*
* @param {string} id - The module id to require
* @param {string} rootDirectory - The root directory to require the module from
* @param {any} errorReturn - The value to return if the require fails
*
* @returns {any} The required module or the errorReturn value
*/
const tryRequire = (id, rootDirectory, errorReturn) => {
// eslint-disable-next-line @typescript-eslint/naming-convention
const _require = jiti(rootDirectory, {
esmResolve: true,
interopDefault: true,
});

try {
return _require(id);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error) {
if (error.code !== "MODULE_NOT_FOUND") {
console.error(
new Error(`Error trying import ${id} from ${rootDirectory}`, {
cause: error,
}),
);
}

console.log(error)

return errorReturn;
}
};

export default tryRequire;
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import nodePlop from "../../src/index.js";
import { setupMockPath } from "../helpers/path.js";
const { clean, mockPath } = setupMockPath(import.meta.url);

const plopfilePath = path.join(mockPath, "plopfile.js");

describe("load-assets-from-plopfile", function () {
afterEach(clean);

const plopfilePath = path.join(mockPath, "plopfile.js");

/////
// test the various ways to import all or part of a plopfile
//
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
add test
name: {{name}}
upperCase: {{constantCase name}}
{{> salutation}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
the modify option in the test plop should add lines below for each run.
Use modify for things like adding script references to an HTML file.

-- APPEND ITEMS HERE --

+++++++++++++++++++++++++++++++++++++++

-- PREPEND ITEMS HERE --
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
this is prepended! ## replace name here ##: {{age}}
$1
115 changes: 115 additions & 0 deletions packages/node-plop/tests/typescript-plopfile/plopfile.cts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import fs from "fs";
import { join } from "path";

import { PlopCfg, NodePlopAPI } from "../../types";

export default function (plop: NodePlopAPI) {
///////
// helpers are passed through to handlebars and made
// available for use in the generator templates
//

// adds 4 dashes around some text (yes es6/es2015 is supported)
plop.setHelper("dashAround", function (text) {
return "---- " + text + " ----";
});
// plop.setHelper('dashAround', (text) => '---- ' + text + ' ----');

// formats an array of options like you would write
// it if you were speaking (one, two, and three)
plop.setHelper("wordJoin", function (words) {
return words.join(", ").replace(/(:?.*),/, "$1, and");
});

// greet the user using a partial
plop.setPartial(
"salutation",
"my name is {{ properCase name }} and I am {{ age }}.",
);

// setGenerator creates a generator that can be run with "plop generatorName"
plop.setGenerator("basic-add", {
description: "adds a file using a template",
prompts: [
{
type: "input",
name: "name",
message: "What is your name?",
validate: function (value) {
if (/.+/.test(value)) {
return true;
}
return "name is required";
},
},
{
type: "input",
name: "age",
message: "How old are you?",
validate: function (value) {
let digitsOnly = /\d+/;
if (digitsOnly.test(value)) {
return true;
}
return "Invalid age! Must be a number genius!";
},
},
],
actions: [
{
type: "add",
path: "src/{{dashCase name}}.txt",
templateFile: "plop-templates/add.txt",
abortOnFail: true,
},
{
type: "add",
path: "src/_{{constantCase name}}.txt",
template:
'test: {{pkg "name"}}\npropertyPathTest: {{pkg "config.nested[1]"}}\ninline template: {{name}}',
abortOnFail: true,
},
function customAction(answers) {
// move the current working directory to the plop file path
// this allows this action to work even when the generator is
// executed from inside a subdirectory

let plopFilePath = plop.getPlopfilePath();

// custom function can be synchronous or async (by returning a promise)
let copiedMsg = "hey {{name}}, I copied change-me.txt for you",
changeFile = "change-me.txt",
toPath = join(plopFilePath, "src", changeFile),
fromPath = join(plopFilePath, "plop-templates", changeFile);

// you can use plop.renderString to render templates
copiedMsg = plop.renderString(copiedMsg, answers);

if (fs.existsSync(toPath)) {
fs.unlinkSync(toPath);
}

fs.writeFileSync(toPath, fs.readFileSync(fromPath));
return copiedMsg;
},
{
type: "modify",
path: "src/change-me.txt",
pattern: /(-- APPEND ITEMS HERE --)/gi,
template: "$1\r\n{{name}}: {{age}}",
},
{
type: "modify",
path: "src/change-me.txt",
pattern: /(-- PREPEND ITEMS HERE --)/gi,
templateFile: "plop-templates/part.txt",
},
{
type: "modify",
path: "src/change-me.txt",
pattern: /## replace name here ##/gi,
template: "replaced => {{dashCase name}}",
},
],
});
}
115 changes: 115 additions & 0 deletions packages/node-plop/tests/typescript-plopfile/plopfile.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import fs from "fs";
import { join } from "path";

import { PlopCfg, NodePlopAPI } from "../../types";

export default function (plop: NodePlopAPI, config: Partial<PlopCfg> = {}) {
///////
// helpers are passed through to handlebars and made
// available for use in the generator templates
//

// adds 4 dashes around some text (yes es6/es2015 is supported)
plop.setHelper("dashAround", function (text) {
return "---- " + text + " ----";
});
// plop.setHelper('dashAround', (text) => '---- ' + text + ' ----');

// formats an array of options like you would write
// it if you were speaking (one, two, and three)
plop.setHelper("wordJoin", function (words) {
return words.join(", ").replace(/(:?.*),/, "$1, and");
});

// greet the user using a partial
plop.setPartial(
"salutation",
"my name is {{ properCase name }} and I am {{ age }}.",
);

// setGenerator creates a generator that can be run with "plop generatorName"
plop.setGenerator("basic-add", {
description: "adds a file using a template",
prompts: [
{
type: "input",
name: "name",
message: "What is your name?",
validate: function (value) {
if (/.+/.test(value)) {
return true;
}
return "name is required";
},
},
{
type: "input",
name: "age",
message: "How old are you?",
validate: function (value) {
let digitsOnly = /\d+/;
if (digitsOnly.test(value)) {
return true;
}
return "Invalid age! Must be a number genius!";
},
},
],
actions: [
{
type: "add",
path: "src/{{dashCase name}}.txt",
templateFile: "plop-templates/add.txt",
abortOnFail: true,
},
{
type: "add",
path: "src/_{{constantCase name}}.txt",
template:
'test: {{pkg "name"}}\npropertyPathTest: {{pkg "config.nested[1]"}}\ninline template: {{name}}',
abortOnFail: true,
},
function customAction(answers) {
// move the current working directory to the plop file path
// this allows this action to work even when the generator is
// executed from inside a subdirectory

let plopFilePath = plop.getPlopfilePath();

// custom function can be synchronous or async (by returning a promise)
let copiedMsg = "hey {{name}}, I copied change-me.txt for you",
changeFile = "change-me.txt",
toPath = join(plopFilePath, "src", changeFile),
fromPath = join(plopFilePath, "plop-templates", changeFile);

// you can use plop.renderString to render templates
copiedMsg = plop.renderString(copiedMsg, answers);

if (fs.existsSync(toPath)) {
fs.unlinkSync(toPath);
}

fs.writeFileSync(toPath, fs.readFileSync(fromPath));
return copiedMsg;
},
{
type: "modify",
path: "src/change-me.txt",
pattern: /(-- APPEND ITEMS HERE --)/gi,
template: "$1\r\n{{name}}: {{age}}",
},
{
type: "modify",
path: "src/change-me.txt",
pattern: /(-- PREPEND ITEMS HERE --)/gi,
templateFile: "plop-templates/part.txt",
},
{
type: "modify",
path: "src/change-me.txt",
pattern: /## replace name here ##/gi,
template: "replaced => {{dashCase name}}",
},
],
});
}
Loading

0 comments on commit 894d91b

Please sign in to comment.