Skip to content

dus4nstojanovic/create-files-from-template-cli

Repository files navigation

CFFT - Create Files From Template CLI

CFFT CLI is a simple but powerful CLI to generate a list of files from templates.

Stop copying/pasting and renaming files and folders or creating these manually, repeating the same file structures every time. CFFT CLI generates custom file structure easily and quickly.

You can use this CLI for any language or framework (JavaScript, Node, React, Angular, .NET, Java, Python, ...)

Features

  • Create a custom file structure using a terminal
  • Search and replace - Replace file content with a custom text or file name (text, regex, inject files, ...)
  • Create multiple templates
  • Set options directly as a CLI argument, or answering on CLI questions, or:
  • Set defaults by configuring a CLI using a .config JSON file for each template - cfft.config.json

Releases

See Releases to see what is changed in the latest version.

Table of contents

Getting started (Tutorial)

  1. Install the package globally:
npm install -g @beezydev/create-files-from-template-cli
  1. The next step is to create a configuration file - cfft.config.json. It can be done automatically (recommanded), or you can do it manually. Navigate your terminal to the root folder of your application.

  2. Execute the cfft command to create a configuration file:

cfft

You should see the message that cfft.config.json is created with a default values prefilled:

{
  "defaultTemplateName": "component",
  "templates": [
    {
      "name": "component",
      "options": {
        "templatePath": "/.cfft.templates/component",
        "dirPath": "./{fileName}",
        "fileNameTextToBeReplaced": "component",
        "searchAndReplace": [
          {
            "search": "FileName",
            "replace": "{fileName}"
          }
        ]
      }
    }
  ]
}

You can edit these values however you want. See the Options section where it is explained which options exist.

  1. The next step is to create your templates (you can create one or more templates). Let's use a default templatePath. In your root folder, create the following structure:
├── .cfft.templates
│   ├── component
│   │   ├── component.tsx
│   │   ├── component.styles.ts
│   │   ├── index.ts

Note: You can also create inner folders. For example:

├── .cfft.templates
│   ├── component
│   │   ├── component-inner
│   │   │   ├── component-inner.styles.tsx
│   │   │   ├── component-inner.tsx
│   │   │   ├── index.ts
│   │   ├── component.tsx
│   │   ├── component.styles.ts
│   │   ├── index.ts

Note: If you use GIT, you can ignore cfft.config.json and your templates if you wish to.

  1. Insert some content into your newly created files component.tsx, component.styles.ts and index.ts:

component.styles.ts

export const style = {
  backgroundColor: "black",
  color: "white",
};

component.tsx

import React, { FC } from "react";
import { style } from "./FileName.styles";

const FileName: FC = () => {
  return (
    <div style={style}>
      <p>TODO: FileName</p>
    </div>
  );
};

export default FileName;

index.ts

export { default } from "./FileName";

Doing so, the CFFT CLI is ready for use. Let's test it.

  1. Create a src folder manually:
├── src
  1. Navigate your terminal to the src folder:
cd src
  1. Execute the cfft command:
cfft
  1. Enter a file name. For example MyFile.

  2. CFFT CLI created new files. The src folder has the following structure:

├── src
│   ├── MyFile
│   │   ├── MyFile.tsx
│   │   ├── MyFile.styles.ts
│   │   ├── index.ts

Additionaly, the CLI replaced the FileName text with the entered file name:

MyFile.styles.ts (unchanged)

export const style = {
  backgroundColor: "black",
  color: "white",
};

MyFile.tsx

import React, { FC } from "react";
import { style } from "./MyFile.styles";

const MyFile: FC = () => {
  return (
    <div style={style}>
      <p>TODO: MyFile</p>
    </div>
  );
};

export default MyFile;

index.ts

export { default } from "./MyFile";

Filling the missing configuration options

CFFT CLI is interactive. You can remove all default options from your template, and CLI will ask you for missing information every time you run it.

Example

If you were following the previous example, remove the MyFile folder.

  1. Remove all options from your cfft.config.json file:
{}
  1. Execute the cfft command:
cfft
  1. Answer questions:
? Enter template name: component
? Enter file name: MyFile
? Enter dir path: ./MyFile
? Enter template path: /.cfft.templates/component
? Should replace file name text? Yes
? Enter file name text to be replaced: component
? Should replace text? Yes
? Enter text to be replaced: FileName
? Replace text with: MyFile
  1. The CLI will create files using the provided options.

Filling the missing configuration options using the CLI arguments

CFFT CLI allows you to specify almost all options using the terminal arguments.

Example

If you were following one of the previous examples, remove the MyFile folder.

  1. Update your cfft.config.json file:
{
  "defaultTemplateName": "component",
  "templates": [
    {
      "name": "component",
      "options": {
        "templatePath": "/.cfft.templates/component",
        "dirPath": "./{fileName}",
        "fileNameTextToBeReplaced": "component",
        "shouldReplaceFileContent": true,
        "shouldReplaceFileName": true
      }
    }
  ]
}
  1. Execute the cfft command and add arguments:
cfft --fileName MyFile --textToBeReplaced FileName --replaceTextWith Test
  1. The CLI will create files without asking additional questions - since it has all necessary information.

Search and replace - replace multiple placeholders

CFFT CLI allows you to search and replace multiple placeholders. This is possible by adding the additional items in the searchAndReplace array.

Example

If you were following one of the previous examples, remove the MyFile folder.

  1. Update the searchAndReplace in the cfft.config.json:
{
  "defaultTemplateName": "component",
  "templates": [
    {
      "name": "component",
      "options": {
        "templatePath": "/.cfft.templates/component",
        "dirPath": "./{fileName}",
        "fileNameTextToBeReplaced": "component",
        "searchAndReplace": [
          {
            "search": "FileName",
            "replace": "{fileName}"
          },
          {
            "search": "FC",
            "replace": "FunctionComponent"
          }
        ]
      }
    }
  ]
}
  • An alternative is to enter the list of placeholders and replacement values separated by the searchAndReplaceSeparator character to both textToBeReplaced and replaceTextWith:
{
  "defaultTemplateName": "component",
  "templates": [
    {
      "name": "component",
      "options": {
        "templatePath": "/.cfft.templates/component",
        "dirPath": "./{fileName}",
        "fileNameTextToBeReplaced": "component",
        "searchAndReplaceSeparator": ";",
        "textToBeReplaced": "FileName;FC",
        "replaceTextWith": "{fileName};FunctionComponent"
      }
    }
  ]
}

Note: It is important that textToBeReplaced and replaceTextWith have the same number of segments.

  1. Execute the cfft command:
cfft --fileName MyFile
  1. The CLI will create files and replace both, FileName with MyFile and FC with FunctionComponent.
import React, { FunctionComponent } from "react";
import { style } from "./MyFile.styles";

const MyFile: FunctionComponent = () => {
  return (
    <div style={style}>
      <p>TODO: MyFile</p>
    </div>
  );
};

export default MyFile;

Add additional templates

While you can use only one template, it is also possible to create and use multiple templates. To achieve this, you need to create the template folder and files, to update the cfft.config.json and to use the --template CLI argument.

Note: If the defaultTemplateName is not specified in the configuration, the CLI will ask for the template name.

Example

If you were following one of the previous examples, remove the MyFile folder.

  1. Add a new template folder next to the existing one - css, and add a file inside it main.scss (in the .cfft.templates folder):
├── .cfft.templates
│   ├── component
│   │   ├── component.tsx
│   │   ├── component.styles.ts
│   │   ├── index.ts
│   ├── css
│   │   ├── main.scss
  1. Fill the file content:

main.scss

#root {
  box-sizing: border-box;
}
  1. Update the cfft.config.json file:
{
  "defaultTemplateName": "component",
  "templates": [
    {
      "name": "component",
      "options": {
        "templatePath": "/.cfft.templates/component",
        "dirPath": "./{fileName}",
        "fileNameTextToBeReplaced": "component",
        "searchAndReplace": [
          {
            "search": "FileName",
            "replace": "{fileName}"
          },
          {
            "search": "FC",
            "replace": "FunctionComponent"
          }
        ]
      }
    },
    {
      "name": "css",
      "options": {
        "templatePath": "/.cfft.templates/css",
        "dirPath": "./{fileName}",
        "shouldReplaceFileContent": false,
        "shouldReplaceFileName": false
      }
    }
  ]
}
  1. Execute the cfft command:
cfft --template css --fileName my-styles
  1. The CFFT CLI will generate the following folder and file in your src folder:
├── src
│   ├── my-styles
│   │   ├── main.scss

The main.scss, will be exactly the same, since we were not replacing the file content.

Injecting a file content

Besides replacing the placeholders with text, it is possible to do the replacement with the file content. To inject the file content, set the injectFile to true.

Example

If you were following one of the previous examples, remove the MyFile folder.

  1. Add the table.html file to .cfft.templates directory:
├── .cfft.templates
│   ├── component
│   │   ├── component.tsx
│   │   ├── component.styles.ts
│   │   ├── index.ts
│   ├──table.html

table.html

<table>
  <thead>
    <tr>
      <th>Head 1</th>
      <th>Head 2</th>
      <th>Head 3</th>
      <th>Head 4</th>
      <th>Head 5</th>
    </tr>
  </thead>
  <tr>
    <td>Cell 1.1</td>
    <td>Cell 1.2</td>
    <td>Cell 1.3</td>
    <td>Cell 1.4</td>
    <td>Cell 1.5</td>
  </tr>
  <tr>
    <td>Cell 2.1</td>
    <td>Cell 2.2</td>
    <td>Cell 2.3</td>
    <td>Cell 2.4</td>
    <td>Cell 2.5</td>
  </tr>
</table>
  1. Update the template file:

component.tsx

import { FC } from "react";
import { style } from "./FileName.styles";

const FileName: FC = () => {
  return (
    <div style={style}>
      <p>TODO: FileName</p>

      {table}
    </div>
  );
};

export default FileName;
  1. Update the cfft.config.json:
{
  "defaultTemplateName": "component",
  "templates": [
    {
      "name": "component",
      "options": {
        "templatePath": "/.cfft.templates/component",
        "dirPath": "./{fileName}",
        "fileNameTextToBeReplaced": "component",
        "searchAndReplace": [
          {
            "search": "{table}",
            "replace": "/.cfft.templates/table.html",
            "injectFile": true
          }
        ]
      }
    }
  ]
}
  1. Execute the cfft command:
cfft --fileName MyFile
  1. The CLI will create files and inject the table in the MyFile.tsx file:
import { FC } from "react";
import { style } from "./FileName.styles";

const FileName: FC = () => {
  return (
    <div style={style}>
      <p>TODO: FileName</p>

      <table>
        <thead>
          <tr>
            <th>Head 1</th>
            <th>Head 2</th>
            <th>Head 3</th>
            <th>Head 4</th>
            <th>Head 5</th>
          </tr>
        </thead>
        <tr>
          <td>Cell 1.1</td>
          <td>Cell 1.2</td>
          <td>Cell 1.3</td>
          <td>Cell 1.4</td>
          <td>Cell 1.5</td>
        </tr>
        <tr>
          <td>Cell 2.1</td>
          <td>Cell 2.2</td>
          <td>Cell 2.3</td>
          <td>Cell 2.4</td>
          <td>Cell 2.5</td>
        </tr>
      </table>
    </div>
  );
};

export default FileName;

The order of the search and replace execution

In some cases, the replacement order may matter. For example, you may want to inject file content and after that to replace parts of it.

Default orders

Method Order
textToBeReplaced & replaceTextWith 0
searchAndReplace 1
Special replacement placeholders last

Example

If you were following one of the previous examples, remove the MyFile folder.

  1. Update the cfft.config.json file:
{
  "defaultTemplateName": "component",
  "templates": [
    {
      "name": "component",
      "options": {
        "templatePath": "/.cfft.templates/component",
        "dirPath": "./{fileName}",
        "fileNameTextToBeReplaced": "component",
        "searchAndReplace": [
          {
            "search": "{table}",
            "replace": "/.cfft.templates/table.html",
            "injectFile": true,
            "order": -2
          },
          { "search": "Cell 1.5", "replace": "HELLO!", "order": -1 }
        ]
      }
    }
  ]
}
  1. Execute the cfft command:
cfft --fileName MyFile
  1. The CLI will create files and inject the table in the MyFile.tsx file:
import { FC } from "react";
import { style } from "./FileName.styles";

const FileName: FC = () => {
  return (
    <div style={style}>
      <p>TODO: FileName</p>

      <table>
        <thead>
          <tr>
            <th>Head 1</th>
            <th>Head 2</th>
            <th>Head 3</th>
            <th>Head 4</th>
            <th>Head 5</th>
          </tr>
        </thead>
        <tr>
          <td>Cell 1.1</td>
          <td>Cell 1.2</td>
          <td>Cell 1.3</td>
          <td>Cell 1.4</td>
          <td>HELLO!</td>
        </tr>
        <tr>
          <td>Cell 2.1</td>
          <td>Cell 2.2</td>
          <td>Cell 2.3</td>
          <td>Cell 2.4</td>
          <td>Cell 2.5</td>
        </tr>
      </table>
    </div>
  );
};

export default FileName;

Ignoring the case of the letters on text searching

By default, searching by text is case-sensitive. You can change this behavior by using the ignoreCase option. For example:

"searchAndReplace": [
  { "search": "FileName", "replace": "{fileName}", "ignoreCase": true }
]

Using the special replacement placeholders

The special replacement placeholders (for example: {env:ENV_VARIABLE_NAME} or {dateTimeNow:DNS_FORMAT}) are replaced as the last replacement task. See the Special replacement placeholders table for more info. This allows you to add specific or dynamic values during the file creation.

Example

If you were following one of the previous examples, remove the MyFile folder.

  1. Add environment variables or use the existing ones. In this example, we are going to use TEST_ENV=myEnv and ANOTHER_ENV=anotherEnv.
  2. Update your template by adding the following tags:

component.tsx

import { FC } from "react";
import { style } from "./FileName.styles";

const FileName: FC = () => {
  return (
    <div style={style}>
      <p>My env variable: {env:TEST_ENV} and {env:ANOTHER_ENV}</p>
      <p>{dateTimeNow:yyyy-MM-dd}</p>
    </div>
  );
};

export default FileName;
  1. Execute the cfft command:
cfft --fileName MyFile
  1. The CLI will create files and replace the special tags with the values in the MyFile.tsx file:
import { FC } from "react";
import { style } from "./MyFile.styles";

const MyFile: FC = () => {
  return (
    <div style={style}>
      <p>My env variable: myEnv and anotherEnv</p>
      <p>2023-05-06</p>
    </div>
  );
};

export default MyFile;

Custom hooks

onFileCreated

CFFT allows you to specify the custom logic to be executed after each file creation. To achieve so, you should create a custom .js file and implement and export the onFileCreated() function. This can be used for various tasks, but one logical and useful task is to use the prettier.

Example

If you were following one of the previous examples, remove the MyFile folder.

Note: If you want to implement the same behaviour make sure to install and configure the prettier for your project.

  1. Update your package.json by adding the node script:

package.json

"scripts": {
  "prettier:only": "prettier",
}
  1. Update the cfft.config.json file:
{
  "defaultTemplateName": "component",
  "templates": [
    {
      "name": "component",
      "options": {
        "templatePath": "/.cfft.templates/component",
        "dirPath": "./{fileName}",
        "fileNameTextToBeReplaced": "component",
        "hooksPath": "/.cfft.templates/hooks/component.js",
        "searchAndReplace": [
          {
            "search": "{table}",
            "replace": "/.cfft.templates/table.html",
            "injectFile": true,
            "order": -2
          },
          { "search": "Cell 1.5", "replace": "HELLO!", "order": -1 }
        ]
      }
    }
  ]
}
  1. Add the component.js file:
├── .cfft.templates
│   ├── component
│   │   ├── component.tsx
│   │   ├── component.styles.ts
│   │   ├── index.ts
│   ├── hooks
│   │   ├── component.js
│   ├──table.html

component.js

const { execSync } = require("child_process");

const onFileCreated = ({ filePath, templatePath }) => {
  // Run prettier on a created file
  execSync(`npm run prettier:only -- ${filePath} --write`);
};

module.exports = {
  onFileCreated,
};

Note: In this example, we are executing the prettier, but you can specify whatever logic you want.

  1. Execute the cfft command:
cfft --fileName MyFile
  1. The CLI will create files and execute the onFileCreated() hook for each created file!

Creating a single file

It is also useful to create a single file. To achieve that, the templatePath must point to a file. To create a file in the current folder, update the dirPath to ..

Example

  1. Add a component.styles.tsx file to you templates folder:
├── .cfft.templates
│   ├── component
│   │   ├── component.tsx
│   │   ├── component.styles.ts
│   │   ├── index.ts
│   ├── hooks
│   │   ├── component.js
│   ├──table.html
│   ├──component.styles.tsx

component.styles.tsx

import { styled } from "@mui/material";

const COMPONENT_NAME = "FileName";

export const FileNameRoot = styled("div", {
  name: COMPONENT_NAME,
  slot: "Root",
})(({ theme }) => ({}));
  1. Add a new template to the cfft.config.json file:
{
  "defaultTemplateName": "component-styles-file",
  "templates": [
    {
      "name": "component-styles-file",
      "options": {
        "templatePath": "/.cfft.templates/component.styles.tsx",
        "dirPath": ".",
        "fileNameTextToBeReplaced": "component",
        "searchAndReplace": [{ "search": "FileName", "replace": "{fileName}" }]
      }
    }
  ]
}
  1. Execute the cfft command:
cfft -t component-styles-file -n MyComponent
  1. The CLI will create the MyComponent.styles.tsx file and replace the FileName with the provided name (MyComponent) file:

MyComponent.styles.tsx

import { styled } from "@mui/material";

const COMPONENT_NAME = "MyComponent";

export const MyComponentRoot = styled("div", {
  name: COMPONENT_NAME,
  slot: "Root",
})(({ theme }) => ({}));

List all templates

To list all registered template, you could use the --list or -l command.

To add description to your template and list all registered templates including descriptions, use the --listDetailed command.

Example

  1. Update the cfft.config.json file:
{
  "defaultTemplateName": "component",
  "templates": [
    {
      "name": "component",
      "description": "Creates a component",
      "options": {
        "templatePath": "/.cfft.templates/component",
        "dirPath": "./{fileName}",
        "fileNameTextToBeReplaced": "component",
        "searchAndReplace": [
          {
            "search": "FileName",
            "replace": "{fileName}"
          },
          {
            "search": "FC",
            "replace": "FunctionComponent"
          }
        ]
      }
    },
    {
      "name": "css",
      "description": "Creates a css file",
      "options": {
        "templatePath": "/.cfft.templates/css",
        "dirPath": "./{fileName}",
        "shouldReplaceFileContent": false,
        "shouldReplaceFileName": false
      }
    }
  ]
}
  1. Execute the --list command to list all templates:
cfft --list
  1. The result:
component
css
  1. Now execute the --listDetailed command to list all templates including description:
cfft --listDetailed
  1. The result:
┌───────────┬─────────────────────┐
│ Name      │ Description         │
├───────────┼─────────────────────┤
│ component │ Creates a component │
├───────────┼─────────────────────┤
│ css       │ Creates a css file  │
└───────────┴─────────────────────┘

Options

Description Command Alias Default CLI cfft.config
File name to be used fileName -n
Path to the location where to generate files dirPath ./{fileName}
Name of the template to use template -t
Path to the specific template folder templatePath /.cfft.templates/component
Default template name to be used every time when --template is not specified defaultTemplateName component
Should or not CLI replace a file name shouldReplaceFileName true
Wich part of the file name should be replaced fileNameTextToBeReplaced component
Should or not CLI replace a file content shouldReplaceFileContent true
Text (or regex) to be replaced separated by a search and replace separator textToBeReplaced FileName
Text to be used for replacement separated by a separator replaceTextWith {fileName}
Custom separator for search and replace searchAndReplaceSeparator ;
Add additional search and replace items throug config (with extended options) searchAndReplace
Path to the hooks file relative to cfft.config.json hooksPath
Show additional logs debug
List all templates list -l
List all templates with additional info listDetailed
See the package version version -v
Get help help

Note: When specifing an option in a terminal, always add -- as prefix. Example:

cfft --template MyTemplate

Search and replace options

Description Field required default
Text (or regex) to be replaced search
Text to be used for replacement - or path to the file when injectFile=true replace
Should ignore the letters case ignoreCase false
Should inject a file content at the found placeholder injectFile false
In which order to do the search and replace (lower order has precedence) order 1

Special replacement placeholders

Tag Description
{env:ENV_VARIABLE_NAME} Replaces the placeholder with the specified environment variable
{dateTimeNow:DATE_FNS_FORMAT} Replaces the placeholder with the current date and time using the date-fns format. See: date-fns format

Placeholders

  • {fileName} is a special value to indicate that specific text should be replaced with a specified file name (--fileName)

License

MIT

About

A simple but powerful CLI to generate a list of files.

Resources

Stars

Watchers

Forks

Packages

No packages published