npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

faber-cli

v0.2.0

Published

A CLI to easily reuse your own configurable boilerplate projects.

Readme

Faber CLI

Faber is a CLI that helps you create/scaffold new projects using custom boilerplates.

You can prepare your own boilerplates to make them configurable for creating new projects with Faber, and pass custom parameters, data or actions to execute in the scaffolding of your new project.

Summary

Getting Started

Requirements

To use the Faber CLI, you will need Node.js and NPM on your machine.

Your project doesn't need to use Node.js for you to use Faber with it. You can use it on boilerplates with any kind of framework or stack. Once the project is created, Faber's work is done.

However, for being a JS library, Faber configurations are currently written with JavaScript.

Installation

Install the CLI globally on your machine with:

npm install -g faber-cli

Demo

For having a quick demonstration of how Faber is used, try the faber-demo example repository.

Usage

Before diving into details, here is a quick overview of how you can use Faber on your projects:

If you want to prepare a boilerplate for using Faber:

  1. Create a faberconfig file at the root of your boilerplate repository;
  2. Write the actions to be executed on the boilerplate when creating a new project with it;
  3. Prepare the data you want to use in your actions;
  4. Test your actions with the faber execute CLI command.

Make sure you stage your changes in Git before running your actions. Learn more.

If you want to use an existing boilerplate to create a new project:

  1. Prepare the data you want to use for your project as a minified JSON;
  2. Choose a repository of a boilerplate to use as a template;
  3. Use the faber create CLI command to bootstrap a new project.

Configuring faberconfig

To use Faber CLI in a boilerplate, you need to create a faberconfig.js file at the root of your boilerplate project.

Usually, this file uses the .js extension, but depending on your preferences and your project settings (package.json, if existing on the project's root) you might want to use a different extension.

To use CommonJS, the file must be either:

  • .js with type: "commonjs" in your package.json;
  • .cjs with any type in your package.json.
  • .js without having a package.json in the root;

To use ESM, the file must be either:

  • .js with type: "module" in your package.json;
  • .mjs with any type in your package.json.

See below a basic example using CommonJS and ESM.

CommonJS

module.exports = function (faber) {
  faber.setActions((data) => {
    return [
      // Add your actions here
      // ...
    ];
  });
};

ESM

export default (faber) => {
  faber.setActions((data) => {
    return [
      // Add your actions here
      // ...
    ];
  });
};

Passing Data

During the faber create and faber execute commands, the CLI asks for a minified (optionally encoded) JSON data. This data is passed to the setActions() function, allowing you to use it in the actions.

Here is an example of JSON data:

{
  "name": "My Project",
  "client": "The Client",
  "isMultilanguage": false
}

Due to the nature of how terminals (command prompts) work, there are some limitations on how to pass the data to the CLI. See below how to do it properly.

Encoded JSON (recommended)

The most reliable way of passing data is using a Base64 encoded JSON. Encoding guarantees the content consistency while working with any method of passing data to Faber.

You can encode the JSON to Base64 how you prefer, or use our online JSON encoder that works seamlessly with Faber.

Here is an example of a Base64 encoded JSON:

eyJuYW1lIjoiTXkgUHJvamVjdCIsImNsaWVudCI6IlRoZSBDbGllbnQiLCJpc011bHRpbGFuZ3VhZ2UiOmZhbHNlfQ==

Tip: Minifying the JSON before encoding it helps generate a smaller string.

Minified JSON (less reliable)

Although we encourage using the encoding approach, you can also pass a minified JSON directly. It might be easier than encoding depending on your workflow, but has some caveats:

When asked by CLI

By default, the CLI will prompt you to paste the JSON data during its execution.

If not encoded, the JSON can be passed directly, as long as it does not contain line breaks, as in the example below:

$ Paste the project data: {"name":"My Project","client":"The Client","isMultilanguage":false}

You can also use our online JSON encoder to minify it without encoding.

Using --data argument

If you prefer to pass the data directly in the terminal via command, you can use the --data argument, passing the JSON as value.

In this case, if not encoded, the JSON must be minified and then stringified.

However, some terminals might misinterpret JSONs that include spaces, or ignore escaped characters, which would break the JSON anyway. To avoid this, encoding would be a more reliable option.

Here is a usage example (this might or not work depending on your system):

faber create my-project --data "{\"name\":\"My Project\",\"client\":\"The Client\",\"isMultilanguage\":false}"

You can use an online tool like jsonformatter.org to stringify the JSON after minifying it.

Reserved Properties

When using the create task, the name of the project passed as an argument for the command (i.e. faber create my-project) is added to the data object as the _dirName parameter. Similarly, when using the execute task, it gets the name of the folder where you are running the command.

{
  _dirName: 'my-project';
}

Actions

Actions are defined on the faberconfig file of the boilerplate using the faber.setActions() function.

You can use the project's data from the provided JSON (requested at faber create or faber execute commands) on any action.

See below the available actions that you can use:

Replace

Replaces text or patterns on files or glob patterns.

| Property | Type | Required | Description | | -------- | --------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------- | | type | String | Yes | Should be 'replace' for this action. | | files | String,[String] | Yes | Path to the files where the replace should happen. It can be an array of paths, and can use glob patterns. | | ignore | String,[String] | No | Path to the files where the replace shouldn't happen. It can be an array of paths, and can use glob patterns. | | from | String,[String],RegExp,[RegExp] | Yes | Text(s) or pattern(s) to look for in the files. | | to | String,[String],RegExp,[RegExp] | Yes | The replacement text(s). If array is provided, should match the same length as the from array. |

Usage examples

faber.setActions((data) => {
  return [
    // Replace all occurrences of a string in single file.
    {
      type: 'replace',
      files: 'README.md',
      from: 'PROJECT_NAME',
      to: data.projectName,
    },
    // Replace occurrences of multiple strings in multiple files,
    // using glob patterns, and defining paths to ignore.
    {
      type: 'replace',
      files: ['README.md', 'package.json', 'src/*'],
      ignore: ['src/node_modules', '.git'],
      from: [/\bAUTHOR_NAME\b/g, /\bAUTHOR_URI\b/g],
      to: [data.authorName, data.authorUri],
    },
  ];
});

Considerations

  • When passing string in the from property, it's automatically converted to a global regex to replace all occurrences instead of just the first one (JavaScript default). However, if a RegExp is passed, it is left unchanged, requiring the g flag to replace all occurrences (if wanted).
  • This action uses the replace-in-file package for the replacements. For more details about the from, to and ignore parameters, please visit its documentation.

Move (or Rename)

Can be used to move or rename files and folders.

| Property | Type | Required | Description | | -------- | ----------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------- | | type | String | Yes | Should be 'move' for this action. | | from | String,[String] | Yes | Path(s) to the source files or folders to move. | | to | String,[String] | Yes | Destination path(s) to the files or folders to move or rename. If array is provided, should match the same length as the from array. |

Usage examples

faber.setActions((data) => {
  return [
    // Move a single file to another directory
    {
      type: 'move',
      from: 'file.txt',
      to: 'folder/file.txt',
    },
    // Rename a folder and move and rename a file
    {
      type: 'move',
      from: ['folder', 'file.txt'],
      to: [data.newFolderName, `dir/${data.newFileName}`],
    },
  ];
});

Considerations

  • When moving a file to another directory, if the destination (to) directory doesn't exist yet, it is created automatically.
  • If a file/folder with the destination (to) name already exists, the existing one will be overriden.
  • If the source (from) file/folder doesn't exist, an error is thrown.
  • This action uses the move-file package for moving/renaming files. Please visit its documentation if needed.

Delete

Deletes files or entire folders by defined paths or glob patterns.

| Property | Type | Required | Description | | -------- | ----------------- | -------- | ----------------------------------------------------------------------------------------------- | | type | String | Yes | Should be 'delete' for this action. | | paths | String,[String] | Yes | Path to the files or folders to delete. It can be an array of paths, and can use glob patterns. |

Usage examples

faber.setActions((data) => {
  return [
    // Delete a single file
    {
      type: 'delete',
      files: 'file.txt',
    },
    // Delete files and folders using glob pattern and variable
    {
      type: 'delete',
      files: ['**/*.txt', data.folderToDelete],
    },
  ];
});

Considerations

  • If the file/folder doesn't exist, it's just ignored.
  • This action uses the del package for deleting files/folders. Please visit its documentation if needed.

Conditional

Update files' content based on conditional rules. Useful to keep/remove text according to conditions and the provided data.

| Property | Type | Required | Description | | ------------ | ----------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | type | String | Yes | Should be 'conditional' for this action. | | files | String,[String] | Yes | Path to the files where the conditional updates should happen. It can be an array of paths. | | ignore | String,[String] | No | Path to the files where the replace shouldn't happen. It can be an array of paths, and can use glob pattern. | | identifier | String | Yes | A token to identify the content to be kept/removed. | | condition | Boolean | Yes | The condition to keep/remove the content. When the condition is true, the content is kept, and removed when false. However, when the block uses a negative token (!), the block is kept when the condition is false, and removed when true. |

Usage examples

faber.setActions((data) => {
  return [
    // Keep/Remove content on single file
    {
      type: 'conditional',
      files: 'file.txt',
      identifier: 'is-multilanguage',
      condition: data.isMultiLanguage,
    },
  ];
});

On the file content, you can wrap the block of content to keep/remove with Faber conditional comments.

This is a file with instructions of the project.

<!-- @faber-if: is-multilanguage -->
This paragraph is only kept if the condition is truthy.

<!-- @faber-endif: is-multilanguage -->

In the above example, the 2 lines between the @faber-if and @faber-endif HTML comments are kept when the condition to the is-multilanguage identifier is true, and deleted when the condition is false.

Negative match

This prints the /* @faber-if: !is-multisite */not /* @faber-endif: !is-multisite */word if the condition is falsy.

In this example, by prefixing the identifier with an exclamation mark (!), we invert the rule, keeping the not word when the condition is false and deleting it when true.

Supported comments

Currently, you can use the following commenting styles for conditional replacements:

  • Block comments:
    • <!-- -->
    • /** */
    • /* */
    • ''' '''
    • """ """
  • Line comments:
    • //
    • ///
    • #

Considerations

  • The conditional content is everything between the @faber-if: identifier and @faber-endif: identifier comments;
  • The identifier is required for both @faber-if and @faber-endif for the action to work correctly;
  • There is no @faber-else logic yet;
  • The identifier is not a variable, it's just a string that links the conditional blocks to the configured action;
  • You can have multiple conditional blocks using the same identifier;
  • Prefix the identifier with an exclamation mark ! to keep the block when the condition is false instead of true;

Run

Run shell commands.

| Property | Type | Required | Description | | ---------- | ----------------- | -------- | -------------------------------------------------------------------------------------------- | | type | String | Yes | Should be 'run' for this action. | | commands | String,[String] | Yes | Command(s) to run sequentially. | | silent | Boolean | No | If false, logs the command(s) output on the console. Default is true (omits the output). |

Usage examples

faber.setActions((data) => {
  return [
    // Runs a single command
    {
      type: 'run',
      command: 'mkdir testing',
    },
    // Runs command without silent mode
    {
      type: 'run',
      command: 'echo "Hello World!"',
      silent: false,
    },
    // Runs multiple commands
    {
      type: 'run',
      command: ['npm i', 'npm run start'],
    },
    // Same as above, but using command separators
    {
      type: 'run',
      command: 'npm i && npm run start', // or 'npm i; npm run start'
    },
  ];
});

Changing directory

Each run action is executed at the initial directory, where faberconfig is found.

When a cd command is used to change the current directory, this navigation persists only within the current action, to the next commands from the same action.

When a run action completes, the cd navigation is restored to the initial directory for the next action.

See the example below:

[
  // Creates a directory `foo` at ./subfolder
  {
    type: 'run',
    command: ['cd subfolder', 'mkdir foo'], // or using && intead
  },
  // Creates a directory `bar` at ./
  // ignoring navigation from previous actions
  {
    type: 'run',
    command: 'mkdir bar',
  },
];

Considerations

  • Using command separators (&& or ;) has the same behavior as using an array with multiple commands. It's just a matter of preference;
  • When changing directories (i.e. cd path/to/folder), the navigation persists for other commands in the current action;
  • By default, nothing is logged from the executed commands. To display the commands' output in the terminal, set the silent option as false;
  • This action uses the shelljs library for executing the commands, using the exec() function for running the commands, and the cd() function for cd commands (to change directory).

Commands (CLI)

The CLI has commands to create new projects, test boilerplates, and also manage available repository aliases on your local machine.

In a nutshell, you will use:

  • faber create – To create new projects from a pre-configured boilerplate. This command clones the repo and then executes the faber execute command inside of it.
  • faber execute – To execute the configured actions on the current folder. When preparing a boilerplate, you can use this command to test the actions you are writing.
  • faber ls|add|rm – To manage aliases to your boilerplates for ease of usage when using the faber create command.

faber create

Creates a new project using a pre-configured boilerplate.

Usage example

$ faber create my-project

Including URL to clone repository and other flags:

$ faber create my-project https://github.com/path/example.git --branch main --use-existing

Arguments

faber create <name> [clone_url]

  • <name> – Name of the project's root folder.
  • [clone_url] – The URL for cloning the repository (can be SSL or HTTPS, depending on your permissions and authentication).

Flags (optional)

  • --use-existing (bool) – If the folder already exists, skip the prompt and continue with the existing folder, without cloning any repository. In this case, make sure you back up before executing.
  • --override-existing (bool) – If the folder already exists, skip the prompt and delete the existing folder before cloning the repository.
  • --branch (string) – Name of the git branch to retrieve from the repository. If not defined, the default branch is used.
  • --keep-git (bool) – Prevent deleting the existing Git history from the new cloned folder, removed by default.
  • --keep-config (bool) – Prevent deleting the faberconfig file from the new cloned folder, removed by default.

Also includes all flags available to the faber execute command:

  • --data (string) – JSON data (preferrably encoded) to be passed to the actions.
  • --no-preview (bool) – Does not show the JSON data preview.
  • --deep-preview (bool) – Shows the JSON data preview with all the properties and array items expanded.
  • --no-results (bool) – Does not show the actions results.

What does it do?

  1. Clones the boilerplate repository in the current directory into a new folder with the provided name.
  2. Run the steps from the faber execute command.
  3. Deletes the .git folder from the repository (when not using the --keep-git flag);
  4. Deletes the faberconfig.* file (when not using the --keep-config flag);

Notice: You need to have permission to read from the boilerplate repository. When using private repositories, you need to authenticate via SSH or HTTPS as you normally would when cloning.

faber execute

Executes the configured actions on the current directory. Useful for configuring and testing actions.

A faberconfig file should be present on the directory.

Usage examples

$ faber execute

Including JSON data and other flags:

$ faber execute --data "{title:\"Example\"}" --no-preview

Flags (optional)

  • --data (string) – JSON data (preferrably encoded) to be passed to the actions.
  • --no-preview (bool) – Does not show the JSON data preview.
  • --deep-preview (bool) – Shows the JSON data preview with all the properties and array items expanded.
  • --no-results (bool) – Does not show the actions results.

What does it do?

  1. Read the faberconfig file from the directory;
  2. Ask for the JSON data to pass to the actions (when not provided with the --data flag);
  3. Display a preview of the data (when not using the --no-preview flag);
  4. Execute the actions from faberconfig (when not using the --dry flag);

Back up before executing

⚠️ To avoid losing your original code when running faber execute, make sure you have a backup with the current state of your codebase to go back to when testing your actions.

One way to do that is by staging your changes with Git before any test, which also allows you to easily diff the changes your actions made in the files.

Here is a quick example on how to do it in a repository with Git initialized:

  1. Make your changes to faberconfig and other files;
  2. Stage your changes with git add ., or specifying which files to add;
  3. Run faber execute to run your configured actions;
  4. Run git diff to compare what your actions has changed (or use your favorite visual tool 😉);
  5. Undo your actions with git restore --worktree . && git clean -fd (which will revert, or delete, unstaged and untracked files and folders);

faber ls

List your registered repository aliases.

Usage example

$ faber ls

faber add

Adds a repository alias to your list of available boilerplates.

Usage example

$ faber add my-boilerplate https://github.com/path/example.git 'My Boilerplate'

Arguments

faber add <alias> <repository> [name]

  • <alias> (mandatory) – Used to reference this boilerplate on other commands. It should consist only of letters, numbers, dashes and underscores.
  • <repository> (mandatory) – URL to clone the repository. It usually ends with .git.
  • [name] (optional) – A name for this boilerplate to display when using the faber create command.

faber rm

Removes a repository alias from your list of available boilerplates.

Usage example

$ faber rm my-boilerplate

Arguments

faber rm <alias>

  • <alias> (mandatory) – The reference to the boilerplate to remove from your list.

Mentions

This documentation was inspired by the Plop documentation.

Plop is an amazing micro-framework with a similar goal as Faber, however, while Plop is great for generating code inside your project using Handlebars as template engine, Faber is fully focused on starting new projects, with no defined template engine, so that you can run the boilerplate project on its own, for testing or development, while still making it a living boilerplate to clone with Faber.

P.S. You can use both together in your projects to make your life easier. 😉