|  | const { | 
        
          |  | readFileSync, | 
        
          |  | writeFileSync, | 
        
          |  | mkdirSync, | 
        
          |  | existsSync, | 
        
          |  | readdirSync, | 
        
          |  | } = require("node:fs"); | 
        
          |  | const path = require("node:path"); | 
        
          |  |  | 
        
          |  | /* -------------------------------------------------------------------------- */ | 
        
          |  | /*            Cli util to generate components, layout and services            */ | 
        
          |  | /* -------------------------------------------------------------------------- */ | 
        
          |  | module.exports = function (/** @type {import('plop').NodePlopAPI} */ plop) { | 
        
          |  | /* -------------------------------- Helpers; -------------------------------- */ | 
        
          |  | function isChild(text) { | 
        
          |  | return text.split("/").length >= 2; | 
        
          |  | } | 
        
          |  | /* ---------------------------------- Plop Helpers --------------------------------- */ | 
        
          |  | // helper to give me component name from cli input, even handles child component like my-component/sub-component | 
        
          |  | plop.setHelper("parseName", function (text) { | 
        
          |  | return text.split("/").pop() || text; | 
        
          |  | }); | 
        
          |  |  | 
        
          |  | /* ------------------------------ Custom action ----------------------------- */ | 
        
          |  | plop.setActionType("sort-index", function (_, { path }) { | 
        
          |  | const sortedIndex = | 
        
          |  | Array.from( | 
        
          |  | readFileSync(path, "utf8") | 
        
          |  | .split("\n") | 
        
          |  | .filter((line) => line.trim().length > 0) | 
        
          |  | .sort() | 
        
          |  | .reduce((acc, line) => { | 
        
          |  | return acc.add(line); | 
        
          |  | }, new Set()) | 
        
          |  | ).join("\n") + "\n"; | 
        
          |  |  | 
        
          |  | writeFileSync(path, sortedIndex); | 
        
          |  | }); | 
        
          |  |  | 
        
          |  | plop.setActionType("barrel", function ({ destination }, _) { | 
        
          |  | const generatedIndex = | 
        
          |  | readdirSync(destination) | 
        
          |  | .filter( | 
        
          |  | (file) => | 
        
          |  | !( | 
        
          |  | file.match(".test") || | 
        
          |  | file.match(".story") || | 
        
          |  | file.match("index.ts") || | 
        
          |  | file.match("layouts") | 
        
          |  | ) | 
        
          |  | ) | 
        
          |  | .map((file) => `export * from "./${path.parse(file).name}";`) | 
        
          |  | .sort() | 
        
          |  | .join("\n") + "\n"; | 
        
          |  |  | 
        
          |  | writeFileSync(`${destination}/index.ts`, generatedIndex); | 
        
          |  | }); | 
        
          |  |  | 
        
          |  | /* ---------------------------- Create generator ---------------------------- */ | 
        
          |  | const supportedTypes = [ | 
        
          |  | "component", | 
        
          |  | "function", | 
        
          |  | "layout", | 
        
          |  | "provider", | 
        
          |  | "service", | 
        
          |  | "session", | 
        
          |  | ]; | 
        
          |  |  | 
        
          |  | const serverTypes = ["service", "session"]; | 
        
          |  |  | 
        
          |  | function createGenerator(type, destination) { | 
        
          |  | if (!supportedTypes.includes(type)) | 
        
          |  | throw new Error(`Type ${type} is not supported`); | 
        
          |  | if (!destination) throw new Error(`Destination is missing`); | 
        
          |  |  | 
        
          |  | // create the initial index if not exists | 
        
          |  | if (!existsSync(destination)) { | 
        
          |  | mkdirSync(destination); | 
        
          |  | } | 
        
          |  | writeFileSync(`${destination}/index.ts`, "", { | 
        
          |  | flag: "a", | 
        
          |  | }); | 
        
          |  |  | 
        
          |  | return { | 
        
          |  | description: `Generate ${type} in ${destination}`, | 
        
          |  | prompts: [ | 
        
          |  | { | 
        
          |  | type: "input", | 
        
          |  | name: "name", | 
        
          |  | message: `${type} name`, | 
        
          |  | }, | 
        
          |  | ], | 
        
          |  | actions: [ | 
        
          |  | { | 
        
          |  | type: "addMany", | 
        
          |  | destination: `${destination}/{{name}}`, | 
        
          |  | base: `templates/${type}`, | 
        
          |  | templateFiles: [`templates/${type}/*`], | 
        
          |  | }, | 
        
          |  | { | 
        
          |  | type: "add", | 
        
          |  | path: `${destination}/{{name}}/index.ts`, | 
        
          |  | templateFile: `${ | 
        
          |  | serverTypes.includes(type) | 
        
          |  | ? "templates/index.server.hbs" | 
        
          |  | : "templates/index.hbs" | 
        
          |  | }`, | 
        
          |  | }, | 
        
          |  | { | 
        
          |  | type: "append", | 
        
          |  | path: `${destination}/index.ts`, | 
        
          |  | templateFile: `templates/index.hbs`, | 
        
          |  | separator: "", | 
        
          |  | skip: ({ name }) => | 
        
          |  | isChild(name) | 
        
          |  | ? `skip append in ${destination}/index.ts because it's a child ${type}` | 
        
          |  | : false, | 
        
          |  | }, | 
        
          |  | { | 
        
          |  | type: "sort-index", | 
        
          |  | path: `${destination}/index.ts`, | 
        
          |  | skip: ({ name }) => | 
        
          |  | isChild(name) | 
        
          |  | ? `skip sorting ${destination}/index.ts because it's a child ${type}` | 
        
          |  | : false, | 
        
          |  | }, | 
        
          |  | ], | 
        
          |  | }; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | /* ------------------------------ Plop commands ----------------------------- */ | 
        
          |  | // npx plop new component my-component | 
        
          |  | plop.setGenerator( | 
        
          |  | "new component", | 
        
          |  | createGenerator("component", "app/components") | 
        
          |  | ); | 
        
          |  | // npx plop new layout provider my-layout/my-provider | 
        
          |  | plop.setGenerator( | 
        
          |  | "new component provider", | 
        
          |  | createGenerator("provider", "app/components") | 
        
          |  | ); | 
        
          |  | // npx plop new layout my-layout | 
        
          |  | plop.setGenerator("new layout", createGenerator("layout", "app/components")); | 
        
          |  | // npx plop new service my-service | 
        
          |  | plop.setGenerator("new service", createGenerator("service", "app/services")); | 
        
          |  | // npx plop new hook my-hook | 
        
          |  | plop.setGenerator("new hook", createGenerator("function", "app/hooks")); | 
        
          |  | // npx plop new util my-util | 
        
          |  | plop.setGenerator("new util", createGenerator("function", "app/utils")); | 
        
          |  | // npx plop new session my-session | 
        
          |  | plop.setGenerator("new session", createGenerator("session", "app/sessions")); | 
        
          |  | // npx plop new helper my-helper | 
        
          |  | plop.setGenerator("new helper", createGenerator("function", "app/helpers")); | 
        
          |  | // npx plop barrel app/folder-to-migrate | 
        
          |  | plop.setGenerator("barrel", { | 
        
          |  | description: `barrel a path`, | 
        
          |  | prompts: [ | 
        
          |  | { | 
        
          |  | type: "input", | 
        
          |  | name: "destination", | 
        
          |  | message: `destination to barrel`, | 
        
          |  | }, | 
        
          |  | ], | 
        
          |  | actions: [ | 
        
          |  | { | 
        
          |  | type: "barrel", | 
        
          |  | }, | 
        
          |  | ], | 
        
          |  | }); | 
        
          |  | }; | 
  
This is my template needs. Make yours ⚡️