Plop Documentation

repository·main·Indexed 27 days ago

https://github.com/plopjs/plop

A micro-generator framework for automating the creation of code files and text-based assets. Plop combines Inquirer.js for interactive prompts and Handlebars.js for templating. It provides a CLI for executing generators and a programmatic API via node-plop for integrating code generation logic directly into Node.js applications, including support for custom actions, prompts, helpers, and partials.

Tokens
7.2K
Snippets
13
Records
41
Agent score
93%

What's inside Plop

  1. Create a plopfile.js

    main

    A plopfile.js is a Node module that exports a function receiving a plop object. This object provides the API to define generators.

    Module Formats:

    • CommonJS: Use module.exports = function (plop) { ... } in .js files (default package.json type).
    • ESM: Use export default function (plop) { ... } in .js files with "type": "module" in package.json, or in .mjs files.
  2. Use Node-Plop to automate code generation in Node.js

    main

    Use node-plop to integrate Plop's code generation logic directly into your Node.js applications or automation scripts without using the CLI. This allows you to trigger generators programmatically, making it easier to automate workflows or test generator logic within a test suite.

    import nodePlop from "node-plop";
    
    // Load an instance of plop from a plopfile
    const plop = await nodePlop(`./path/to/plopfile.js`);
    
    // Get a generator by name
    const basicAdd = plop.getGenerator("basic-add");
    
    // Run all the generator actions using the data specified
    basicAdd.runActions({ name: "this is a test" }).then(function (results) {
      // do something after the actions have run
    });
  3. Wrap Plop to create a custom CLI

    main

    You can wrap plop to build your own custom CLI tool. This requires a plopfile.js, a package.json, and your template files. Use Plop.launch to initialize the environment and run to execute the generators.

    Note: If you use the (env) => run(env, undefined, true) pattern, you can pass generator arguments directly without using the -- separator. If you prefer the standard Plop behavior (requiring -- before generator arguments), use Plop.launch({}, run) instead.

    #!/usr/bin/env node
    const path = require("path");
    const args = process.argv.slice(2);
    const { Plop, run } = require("plop");
    const argv = require("minimist")(args);
    
    Plop.launch(
      {
        cwd: argv.cwd,
        // Use __dirname to ensure plopfile.js is always found regardless of CWD
        configPath: path.join(__dirname, "plopfile.js"),
        require: argv.require,
        completion: argv.completion,
        // This merges plop argv and generator argv, removing the need for '--'
      },
      (env) => run(env, undefined, true),
    );
  4. Configure action settings with ActionConfig

    main

    Every action in the actions array uses an ActionConfig object.

    ActionConfig Interface:

    • type (String): The type of action (e.g., add, modify, addMany, or a custom type created via setActionType).
    • force (Boolean, default: false): Force the action to execute (behavior varies by action type).
    • data (Object | Function): Data to be merged with user input. Can be a function returning an object or a Promise resolving to an object.
    • abortOnFail (Boolean, default: true): If true, subsequent actions are cancelled if this action fails.
    • skip (Function, optional): A function that returns a string reason if the action should be skipped.
  5. Create dynamic actions based on prompt answers

    main

    The actions property in GeneratorConfig can be a function that receives answers as an argument. This allows you to return different sets of actions based on user input.

    module.exports = function (plop) {
      plop.setGenerator("test", {
        prompts: [
          {
            type: "confirm",
            name: "wantTacos",
            message: "Do you want tacos?",
          },
        ],
        actions: function (data) {
          var actions = [];
    
          if (data.wantTacos) {
            actions.push({
              type: "add",
              path: "folder/{{dashCase name}}.txt",
              templateFile: "templates/tacos.txt",
            });
          } else {
            actions.push({
              type: "add",
              path: "folder/{{dashCase name}}.txt",
              templateFile: "templates/burritos.txt",
            });
          }
    
          return actions;
        },
      });
    };
  6. Configure package.json for a custom Plop CLI

    main

    When packaging Plop as a CLI tool, your package.json should define the entry point in the bin field and include plop as a dependency.

    {
      "name": "create-your-name-app",
      "version": "1.0.0",
      "main": "index.js",
      "scripts": {
        "start": "plop"
      },
      "bin": {
        "create-your-name-app": "./index.js"
      },
      "preferGlobal": true,
      "dependencies": {
        "plop": "^2.6.0"
      }
    }
  7. Configure a generator with setGenerator

    main

    Use plop.setGenerator(name, config) to define a generator. The config object (of type GeneratorConfig) must include prompts and actions.

    GeneratorConfig Interface:

    • description (String, optional): A short description of the generator.
    • prompts (Array<InquirerQuestion>): An array of questions to ask the user.
    • actions (Array<ActionConfig>): An array of operations to perform.
  8. Use the `addMany` action to add multiple files

    main
    The addMany action allows you to add multiple files within a single action. Use destination to specify the target folder and templateFiles (a glob pattern) to match the templates to be added. You can use Handlebars syntax in templateFiles to generate specific filenames, such as {{ dashCase name }}.spec.js.
  9. Implement the `bypass` method for custom Inquirer plugins

    main

    To allow a third-party Inquirer plugin to support direct parameter passing in Plop, export a bypass method on the plugin constructor. The bypass method receives rawValue (the user input) and promptConfig (the prompt settings). The value returned by bypass is what gets saved into the Plop data object.

    // My confirmation inquirer plugin
    module.exports = MyConfirmPluginConstructor;
    function MyConfirmPluginConstructor() {
      // ...your main plugin code
      this.bypass = (rawValue, promptConfig) => {
        const lowerVal = rawValue.toString().toLowerCase();
        const trueValues = ["t", "true", "y", "yes"];
        const falseValues = ["f", "false", "n", "no"];
        if (trueValues.includes(lowerVal)) return true;
        if (falseValues.includes(lowerVal)) return false;
        throw Error(`"${rawValue}" is not a valid ${promptConfig.type} value`);
      };
      return this;
    }
  10. Use `plop.load` to import external generators and assets

    main

    Use plop.load to import generators, actionTypes, helpers, and partials from other local plopfiles or from NPM packages (via plop-pack).

    Important for ES6 Modules: If you are using ES6 modules, plop.load is asynchronous and must be called within an async function using await to ensure assets are loaded before use.

  11. Set a custom destination path in a Plop wrapper

    main

    When wrapping Plop, you may want the destination path (dest) to be relative to the current working directory (CWD) of the user calling your CLI. You can achieve this by overriding the dest property in the env object within the Plop.launch callback.

    Plop.launch(
      {
        // config like above
      },
      (env) => {
        const options = {
          ...env,
          dest: process.cwd(), // makes the destination path based on the CWD when calling the wrapper
        };
        return run(options, undefined, true);
      },
    );