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 🙏

© 2024 – Pkg Stats / Ryan Hefner

creator-js-cli

v1.0.52

Published

Generate whatever you want

Downloads

131

Readme

CreatorJS

npm version

CreatorJS is a tool for automating boilerplate code creation.

Documentation

Templates examples

Config example

Table of Contents

  1. Installation
  2. Configuration and Usage
  3. Variables
  4. Domains
  5. Questions
  6. Templates
  7. Structure
  8. Advanced concepts
  9. Type support

Installation

You can install CreatorJS using npm:

npm install creator-js-cli --save-dev

or yarn:

yarn add creator-js-cli -D

Configuration and Usage

Create creator.config.js file in the root of your project.

CreatorJS uses ES Modules. If your package.json does not include property type or its value is different from module, then you should create creator.config.mjs file instead.

The minimal configuration looks like this:

export default {
  domains: [
    {
      name: 'components',
      templates: [
          {
              name: 'component.jsx'
          }
      ]
    }
  ]
};

Run the CLI with this simple command:

g

or in case of errors:

./node_modules/.bin/g

After running the CLI and answering initial question with components option, file ./component.jsx should be created.


Variables

variables is a dictionary of any variables that you might want to use in the templates.

There is a list of predefined variables.

| Name | Type | Description | Required | |-------------|:----------|:-------------------------------------------------------------|----------| | root | string | The folder relative to which you want to create files. | optional | | createEmpty | boolean | The flag tells whether to create empty files. | optional | | runLinter | boolean | The flag tells whether to run ESLint after applying changes. | optional |

After running the CLI and answering questions, all variables can be found in answers.variables.


Domains

Overview

domains are scopes, within which questions, templates and structure are defined.

Each domain has the following fields:

| Name | Description | Required | |:------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------|----------| | name | The name of the domain. When started, the CLI will first ask What do you want to create? question and in the list of choices there will be domain names. | required | | questions | Questions that will be asked in the scope of the domain. | optional | | templates | Files that will be created after answering domains questions. | optional | | structure | The folder structure of the part of your application, within which you want to create new files. It does not require full structure replication. | optional | | next | The next domain to be processed after you finish answering questions for the current domain. | optional | | hidden | The flag to hide domain from the initial question. | optional |

Domains chaining

Sometimes you want to create files from one domain and proceed with another. One of examples can be Redux, when you want to create a page and then associate it with the reducer.

To chain domains, use next field:

export default {
  domains: [
    {
      name: 'components',
      templates: [
        // ...
      ],
      questions: [
        // ...
      ],
      next: {
        name: 'redux'
      }
    },
    {
      name: 'redux',
      templates: [
        // ...
      ],
      questions: [
        // ...
      ]
    }
  ]
};

The next object has the following fields:

| Name | Type | Description | Required | |---------------|:-------------------------------------:|:-----------------------------------------------------------------------|----------| | name | string | The name of the next domain. | required | | when | ((answers) => boolean) | boolean | Condition for switching to the next domain. | optional | | skipStructure | boolean | Flag to skip structure and use filePath from the previous domain. | optional |

Sometimes you want to include domains in chaining, but exclude them from initial questions.
For that case, you can hide the domain from the initial question with hidden flag:

{
      name: 'hiddenDomain',
      hidden: true,
      questions: [
        // ...
      ]
    }

Questions

Overview

Questions can be added to provide more details about how to create files. CreatorJS uses inquirer.js to work with questions. If you already familiar with API, that's great. Let's add a simple question to our components domain:

export default {
    domains: [
        {
            name: 'components',
            templates: [
                {
                    name: 'component.jsx'
                }
            ],
            questions: [
                {
                    name: 'componentName',
                    message: 'How to name the component?',
                    type: 'input'
                }
            ]
        },
    ]
};

After running the CLI and answering questions, the answers object will be created.

answers can be used to:

  • conditionally show questions
  • conditionally create or update files
  • set files names
  • populate templates

For example, let's add a dynamic file name to our config:

export default {
  domains: [
    {
      name: 'components',
      templates: [
        {
          name: (answers) => `${answers.components.componentName}.jsx`
        }
      ],
      questions: [
        {
          name: 'componentName',
          message: 'How to name the component?',
          type: 'input'
        }
      ]
    },
  ]
};

After running the CLI and answering questions, the file will be created with a name that you provided when answered a question How to name the component?.

Answers

answers has the following structure:

{
    variables: {
        // variables from creator.config.js variables field
    },
    domain1: {
        // answers for domain 1 questions
    },
    domain2: {
        // answers for domain 2 questions
    },
    // ...
}

For example, in our scenario answers would look like this:

{
    variables: {
        root: './', 
        createEmpty: true
    },
    components: {
        filePath: '.',
        componentName: 'Atom'
    }
}

Here variables were set to the default values. And answers for the components domain were put in the answers.components. There is a filePath field, for which we did not specify a question. It is inferred from combining variables.root and answers to structure questions.

The structure of answers above is valid everywhere except for questions.

In questions, answers structure would represent answers for the particular domain. It does not have access to other domains or variables. For example, it will have system fields, like _file_1 or _new-folder_1, which are used for dynamic structure. In the resulting answers these fields are changed with filePath.


Templates

Overview

Templates are .js files that define the contents of the files that we want to create or update.

CreatorJS uses ES Modules. If your package.json does not include property type or its value is different from module, then templates should have .mjs extension.

The minimal configuration for a template file looks like this:

export default (answers) => {
  return {
    init: '',
    updates: []
  };
};

init is a string with the initial content of the file.

updates is an array of special objects that define the updates.

The configuration for the template in creator.config.js has these fields:

| Name | Type | Description | Required | |-------------|:-----------------------------------:|:--------------------------------------------------------------------------------------------|----------| | name | string | The name of the file. You can use complex path to the file together with name. | required | | template | ((answers) => string) | string | The path to the template file. | required | | when | [Operator, string] | boolean | The condition under which the file will be created or updated. | optional | | createEmpty | boolean | The flag tells whether to create empty file. Overrides variables.createEmpty if provided. | optional |

Initialization

First, create a file ./templates/component.js with this initial structure:

// templates/components.js

export default (answers) => {
  return {
    init: `import React from 'react';
    
    export const ${answers.components.componentName} = () => {
      return <div/>
    }
    `,
    updates: []
  };
};

Then update template in the config file:

// creator.config.js

export default {
  domains: [
    {
      name: 'components',
      templates: [
        {
          name: (answers) => `${answers.components.componentName}.jsx`,
          template: './templates/component.js'
        }
      ],
      questions: [
        {
          name: 'componentName',
          message: 'How to name the component?',
          type: 'input'
        }
      ]
    },
  ]
};

We substitute the answer to the componentName question from the components domain to the name of the file and also to the name of the component.

After running the CLI and answering questions, if the component was named "Atom" for example, there will be a file ./Atom.jsx with the contents:

import React from 'react';

export const Atom = () => {
  return <div/>;
};

In thecreator.config.js file the name field of a template is not just a name of the file, but the path to this file. It is possible to extend path with folder, and they will be created:

name: (answers) => `./need/more/folders/${answers.components.componentName}.jsx`

It is also possible to go up the folder structure:

name: (answers) => `../../../${answers.components.componentName}.jsx`

Thus, it is not limited to just file name.

Update

Often we don't only want to create files, but also want to update already existing ones.

To make updates, there is a special array of objects describing in a declarative way how exactly to update the file. Each object represents an update.

The minimal configuration for this object looks like this:

// ./templates/component.js

export default (answers) => {
    return {
        init: `import React from 'react'
    
    export const ${answers.components.componentName} = () => {
      return <div/>
    }
    `,
        updates: [
            // These are two required fields to perform an unpdate
            {
                searchFor: ['includes', 'div'],
                changeWith: 'span'
            }
        ]
    };
};

It literally tells CreatorJS to search for the line that includes div and change it with span.

Running the CLI and answering the questions will modify existing ./Atom.js file:

import React from 'react';

export const Atom = () => {
  return <span/>;
};

Update object has the following structure:

| Name | Type | Description | Required | |------------|:---------------------------------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------|----------| | direction | 'up' | 'down' | Tells, which way to scan the file. Default is down. | optional | | fromLine | [Operator, string] | When direction is down the default value is the first line of the file. When direction is up the default value is the last line of the file. | optional | | toLine | [Operator, string] | When direction is down the default value is the last line of the file. When direction is up the default value is the first line of the file. | optional | | searchFor | [Operator, string] | Searches for a line with a string within bounds based on condition. | required | | changeWith | string | A value that should substitute searchFor. | required | | when | [Operator, string] | boolean | The condition under which the substitution is performed. The condition will be tested on every line within the bounds. | optional | | fallback | update object | When the update could not be performed, the fallback update will be performed if provided. | optional |

  • Operator = 'includes' | 'not includes' | '===' | '!=='

direction, fromLine and toLine together define the bounds within which the look-up will be performed.


Structure

Overview

Structure comes in handy when there is a defined folder structure in the project. Structure is an object that represents this folder structure. Within the domain it is not required to provide the full folder structure. See Working with different structures for more details.

structure adds structural questions prior to the user-defined questions.

Answers to structural questions are not stored in the final answers. Instead, they are merged into a field called filePath, which you may find under domains' answers.

However, you may see explicit answers to structural questions in questions answers.

For example, let's say we have this question with condition:

// creator.config.js

export default {
    domains: [
        {
            name: 'components',
            structure: {
                components: ''
            },
            questions: [
                {
                    name: 'componentName',
                    message: 'How to name the component?',
                    type: 'input',
                    when: (answers) => // some condition
                }
            ]
        },
    ]
};

This answers will look like this:

{
  create: 'components',
  _file_1: 'components'
}

In questions, answers structure would represent answers for the particular domain. It does not have access to other domains or variables. For example, it will have system fields, like _file_1 or _new-folder_1, which are used for dynamic structure.

One more thing to be explicitly mentioned, is that filePath is a combination of:

<variables.root>/<structural answers>/<template name>.

Static structure

Let's say we have this folder structure:

components
----shared
----features
----pages

In terms of CreatorJS structure would look like this:

// creator.config.js

export default {
    domains: [
        {
            name: 'components',
            structure: {
                components: {
                    shared: '',
                    features: '',
                    pages: ''
                }
            },
            templates: [
                {
                    name: (answers) => `${answers.components.componentName}.jsx`,
                    template: './templates/component.template.js'
                }
            ],
            questions: [
                {
                    name: 'componentName',
                    message: 'How to name the component?',
                    type: 'input'
                }
            ]
        },
    ]
};

With structure provided, before asking questions from questions the CLI will walk you through the structure. structure impacts the path where files will be created. For example, if you select components -> shared, then the file Atom.jsx will have path ./components/shared/Atom.jsx.

When the value of the field is a string, the structure questions terminate, and the user-defined questions questions begin.

Dynamic structure

There are cases when we want to dynamically create folders. To make the folder dynamic, use $ prefix.

Let's say we want to create features on the run. Add an object to the features with a $feature field equal to an empty string.

// creator.config.js

export default {
    domains: [
        {
            name: 'components',
            structure: {
                components: {
                    shared: '',
                    features: {
                        $feature: ''
                    },
                    pages: ''
                }
            },
            templates: [
                {
                    name: (answers) => `${answers.components.componentName}.jsx`,
                    template: './templates/component.template.js'
                }
            ],
            questions: [
                {
                    name: 'componentName',
                    message: 'How to name the component?',
                    type: 'input'
                }
            ]
        },
    ]
};

Now when you get to the features folder, CreatorJS will ask you to create a new folder or select an existing one.

The name after $ does not impact anything.

Structural questions for domain chaining

In the domains chaining section, we learned that it is possible to merge domains into a single questions flow.

If domains, that we want to merge, contain structure, each question set will start with structural questions.

It is possible to pass structural questions answers to the next domain by using next.skipStructure flag:

// creator.config.js

export default {
    domains: [
        {
            name: 'components',
            // ...
            next: {
                name: 'redux',
                skipStructure: true
            }
        },
        {
            name: 'redux',
            // ...
        }
    ]
};

Working with different structures

Different domains can have different structures.

Consider this folder structure:

components
----shared
----features
--------feature-1
--------feature-2
----pages

redux
----reducer-1
----reducer-2

It can be represented like this:

// creator.config.js

export default {
    domains: [
        {
            name: 'components',
            structure: {
                components: {
                    shared: '',
                    features: {
                        $feature: ''
                    },
                    pages: ''
                }
            },
            // ...
        },
        {
            name: 'components',
            structure: {
                redux: {
                    $reducer: ''
                },
                // ...
            }
        }
    ]
};

It is not required to replicate the full folder structure. Create one that is suitable for the particular domain.


Advanced concepts

Type import and export

When working with typescript, you might want to create new interfaces or types and import them into files. However, there are primitives in JavaScript, which names are reserved. We don't want to end up importing a string or a number. Also, when creating an array type, we don't want to have brackets [] in the interfaces/type or import statement.

CreatorJS solves this task under the hood for you so you don't need to think about complicated conditions in the templates.

For example, if you have in your template:

export default (answers) => {
    return {
        init: `export interface ${answers.someDomain.type} {}`
    };
};

you can answer the type question with something like number[] and this template will not be created. Or if you answer with ISomething[], it will resolve to:

export interface ISomething {}

CreatorJS ignores primitives and array brackets for export interfaces, export type and import statements.

Built-in methods

CreatorJS comes with a few useful methods for making the templates.

| Name | Type | Description | |----------------------|:-------------------------------------------:|:---------------------------------------------------| | capitalize | (str: string) => string | Make the first letter of the string capital. | | getTypeValue | ((type: string) => string) | undefined | Returns the dummy value for the provided type. |


Type support

To support config types, use JSDoc @type notation:

/** @type { import('creator-js-cli/dist/index').CreatorConfig } */
const config = {
  // ...
}

export default config;