You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 4-6.RSVP

generate-react-cli

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

generate-react-cli - npm Package Compare versions

Comparing version

to
5.0.0

@@ -5,2 +5,15 @@ # Changelog

## [5.0.0](https://github.com/arminbro/generate-react-cli/compare/v4.3.3...v5.0.0) (2020-05-25)
### ⚠ BREAKING CHANGES
- 🧨 This new "type" option will replace the custom component commands that
you run. Meaning you now can pass the custom component as type option
(e.g npx generate-react-cli component HomePage --type=page ) that you
have configured in your GRC config file.
### Features
- 🎸 Add a new "type" option to the component command ([1a5ce6a](https://github.com/arminbro/generate-react-cli/commit/1a5ce6a3c9d8d19937b201ed8fb1bc5ec6c4fae9))
### [4.3.3](https://github.com/arminbro/generate-react-cli/compare/v4.3.2...v4.3.3) (2020-05-10)

@@ -7,0 +20,0 @@

{
"name": "generate-react-cli",
"version": "4.3.3",
"version": "5.0.0",
"description": "A simple React CLI to generate components instantly and more.",

@@ -48,3 +48,3 @@ "repository": "https://github.com/arminbro/generate-react-cli",

"chalk": "^4.0.0",
"commander": "^5.0.0",
"commander": "^5.1.0",
"deep-keys": "^0.5.0",

@@ -54,14 +54,14 @@ "fs-extra": "^9.0.0",

"lodash": "^4.17.15",
"replace": "^1.1.5"
"replace": "^1.2.0"
},
"devDependencies": {
"@babel/cli": "^7.8.4",
"@babel/core": "^7.9.0",
"@babel/preset-env": "^7.9.5",
"@babel/core": "^7.9.6",
"@babel/preset-env": "^7.9.6",
"@commitlint/cli": "^8.3.5",
"@commitlint/config-conventional": "^8.3.4",
"babel-eslint": "^10.1.0",
"eslint": "^6.8.0",
"eslint": "^7.1.0",
"eslint-config-airbnb-base": "^14.1.0",
"eslint-config-prettier": "^6.10.1",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.20.2",

@@ -71,7 +71,7 @@ "eslint-plugin-jsx-a11y": "^6.2.3",

"husky": "^4.2.5",
"jest": "^25.4.0",
"prettier": "^2.0.4",
"jest": "^26.0.1",
"prettier": "^2.0.5",
"pretty-quick": "^2.0.1",
"rimraf": "^3.0.2",
"standard-version": "^7.1.0"
"standard-version": "^8.0.0"
},

@@ -78,0 +78,0 @@ "babel": {

@@ -16,26 +16,19 @@ # Generate React CLI

- Now supports custom component commands ([read more](#custom-component-commands)). 🎉
- Now supports custom templates ([read more](#custom-templates)). 🎉
- Now supports custom component types ([read more](#custom-component-types)). 🎉
- Now supports custom component templates ([read more](#custom-component-templates)). 🎉
- Supports React [TypeScript](https://www.typescriptlang.org/) projects.
- Supports two different component testing libraries - [Testing Library](https://testing-library.com) and [Enzyme](https://airbnb.io/enzyme) - that work with [Jest](https://jestjs.io/). We assume that you have these libraries already configured in your React project.
- It follows [grouping by feature](https://reactjs.org/docs/faq-structure.html#grouping-by-file-type) because we believe when you look at a component, you should see all of its corresponding files (i.e., stylesheet, test, and component) under one folder with the component name. We feel this approach provides a better developer experience.
- It follows [grouping by feature](https://reactjs.org/docs/faq-structure.html#grouping-by-file-type) because we believe when you look at a component, you should see all of its corresponding files (i.e., stylesheet, test, and component) under one folder with the feature name. We feel this approach provides a better developer experience.
## You can install it globally and run it using npm:
## You can run it using npx like this:
```
npm i -g generate-react-cli
generate-react component Box
```
## Or you can just run it using npx like this:
```
npx generate-react-cli component Box
```
_([npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) is a package runner tool that comes with npm 5.2+ and higher)_
_([npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) is a package runner tool that comes with npm 5.2+)_
## Config File
When you run generate-react-cli within your project the first time, it will ask you a series of questions to customize the cli for your project needs (this will create a "generate-react-cli.json" config file).
When you run GRC within your project the first time, it will ask you a series of questions to customize the cli for your project needs (this will create a "generate-react-cli.json" config file).

@@ -51,7 +44,9 @@ ### Example of the **generate-react-cli.json** config file:

"component": {
"path": "src/components",
"withLazy": false,
"withStory": false,
"withStyle": true,
"withTest": true
"default": {
"path": "src/components",
"withLazy": false,
"withStory": false,
"withStyle": true,
"withTest": true
}
}

@@ -61,44 +56,37 @@ }

### Custom component commands:
## Generate Components
By default, GRC comes with this [component](#generate-component) command out of the box.
```
npx generate-react-cli component Box
```
What if you wanted to generate components with your own custom commands, like for example **page** or **layout** that have their own set of component config rules?
This command will create a folder with your component name within your default (e.g. **src/components**) directory, and its corresponding files.
You can do so by extending the **generate-react-cli.json** config file like this.
### Example of the component files structure:
```json
{
"usesTypeScript": false,
"usesCssModule": true,
"cssPreprocessor": "scss",
"testLibrary": "Testing Library",
"component": {
"path": "src/components",
"withLazy": false,
"withStory": false,
"withStyle": true,
"withTest": true
},
"page": {
"path": "src/pages",
"withLazy": true,
"withStory": false,
"withStyle": true,
"withTest": true
},
"layout": {
"path": "src/layout",
"withLazy": false,
"withStory": false,
"withStyle": false,
"withTest": true
}
}
```
|-- /src
|-- /components
|-- /Box
|-- Box.js
|-- Box.css
|-- Box.test.js
```
Make sure to include the required properties listed below when creating the custom component commands. Otherwise, GRC will not register them as a custom component command.
### Options
The required properties are as follows:
You can also override some of the GRC component config rules using one-off commands. So for example, let's say you have set **withTest** to be `true` in the `component.default` property. You can override it like this:
```
npx generate-react-cli component Box --withTest=false
```
Or vice versa, if you have set **withTest** to be `false` you can do this:
```
npx generate-react-cli component Box --withTest=true
```
Otherwise, if you don't pass any options, it will just use the default values that you have set in the GRC config file under `component.default`.
<table>

@@ -109,2 +97,3 @@ <tr align="left">

<th>Value Type</th>
<th>Default Value</th>
</tr>

@@ -117,5 +106,15 @@ <tr>

<td width="20%">String</td>
<td width="20%"><code>component.default.path<code></td>
</tr>
<tr>
<td width="20%"><b>--type</b></td>
<td width="60%">
You can pass a custom component type that you have configured in the GRC config file that has its own set of component config rules. Read more about <a href="#custom-component-types">custom component types</a>.
</td>
<td width="20%">String</td>
<td width="20%"><code>component.default<code></td>
</tr>
<tr>
<td width="20%"><b>--withLazy</b></td>

@@ -126,2 +125,3 @@ <td width="60%">

<td width="20%">Boolean</td>
<td width="20%"><code>component.default.withLazy<code></td>
</tr>

@@ -135,2 +135,3 @@

<td width="20%">Boolean</td>
<td width="20%"><code>component.default.withStory<code></td>
</tr>

@@ -144,2 +145,3 @@

<td width="20%">Boolean</td>
<td width="20%"><code>component.default.withStyle<code></td>
</tr>

@@ -153,24 +155,65 @@

<td width="20%">Boolean</td>
<td width="20%"><code>component.default.withTest<code></td>
</tr>
</table>
Once you have done that, you should be able to run the custom component commands like this:
### Custom component types:
By default, GRC will use the `component.default` configuration rules when running the component command out of the box.
What if you wanted to generate other types of components that have their own set of config rules (e.g., **page** or **layout**)?
You can do so by extending the **generate-react-cli.json** config file like this.
```json
{
"usesTypeScript": false,
"usesCssModule": true,
"cssPreprocessor": "scss",
"testLibrary": "Testing Library",
"component": {
"default": {
"path": "src/components",
"withLazy": false,
"withStory": false,
"withStyle": true,
"withTest": true
},
"page": {
"path": "src/pages",
"withLazy": true,
"withStory": false,
"withStyle": true,
"withTest": true
},
"layout": {
"path": "src/layout",
"withLazy": false,
"withStory": false,
"withStyle": false,
"withTest": true
}
}
}
```
npx generate-react-cli page HomePage
Now you can generate a component with your custom component types like this:
```
npx generate-react-cli component HomePage --type=page
```
```
npx generate-react-cli layout BoxLayout
npx generate-react-cli component BoxLayout --type=layout
```
You can also pass the same [options](#options) to your custom component commands as you would for the default **component** command that comes with GRC.
You can also pass the same [options](#options) to your custom component types as you would for the default component type.
## Custom Templates
### Custom component templates
You can now create custom templates that Generate React CLI can use instead of the built-in templates that come with it. We hope this will provide more flexibility for your components and pages you want to generate.
You can also create your own custom templates that GRC can use instead of the built-in templates that come with it. We hope this will provide more flexibility for your components that you want to generate.
Both the `component` and `page` properties (within the **generate-react-cli.json** config file) can accept an optional `customTemplates` object property.
There is an optional `customTemplates` object that you can pass to the `component.default` or any of your custom component types within your **generate-react-cli.json** config file.
### Example of the `customTemplates` object:
#### Example of the `customTemplates` object:

@@ -189,3 +232,3 @@ ```json

### Example of using the `customTemplates` property in the generate-react-cli.json config file:
### Example of using the `customTemplates` object within your generate-react-cli.json config file:

@@ -199,22 +242,24 @@ ```json

"component": {
"customTemplates": {
"component": "templates/component/component.js",
"style": "templates/component/style.scss",
"test": "templates/component/test.js"
"default": {
"customTemplates": {
"component": "templates/component/component.js",
"style": "templates/component/style.scss",
"test": "templates/component/test.js"
},
"path": "src/components",
"withStyle": true,
"withTest": true,
"withStory": true,
"withLazy": false
},
"path": "src/components",
"withStyle": true,
"withTest": true,
"withStory": true,
"withLazy": false
},
"page": {
"customTemplates": {
"test": "templates/page/test.js"
},
"path": "src/pages",
"withStyle": true,
"withTest": true,
"withStory": false,
"withLazy": true
"page": {
"customTemplates": {
"test": "templates/page/test.js"
},
"path": "src/pages",
"withLazy": true,
"withStory": false,
"withStyle": true,
"withTest": true
}
}

@@ -224,5 +269,5 @@ }

Notice in the `page.customTemplates` that we only specified the "test" custom template type. That's because all the custom template types are optional. If you don't set the other types, the CLI will default to using the built-in templates it comes with.
Notice in the `page.customTemplates` that we only specified the `test` custom template type. That's because all the custom template types are optional. If you don't set the other types, GRC will default to using the built-in templates it comes with.
### Example of a custom component template file:
#### Example of a custom component template file:

@@ -244,5 +289,5 @@ ```jsx

**Important** - Make sure to use the `TemplateName` keyword in your templates. The CLI will use this keyword to replace it with your component name.
**Important** - Make sure to use the `TemplateName` keyword in your templates. GRC will use this keyword to replace it with your component name.
### Example of a custom test template file:
#### Example of a custom test template file:

@@ -263,88 +308,4 @@ ```jsx

## Usage
### Generate Component
```
npx generate-react-cli component Box
```
This command will create a folder with your component name within your default (e.g. **src/components**) directory, and its corresponding files.
#### **Example of the component files structure**
```
|-- /src
|-- /components
|-- /Box
|-- Box.js
|-- Box.css
|-- Box.test.js
```
#### Options
You can also override some of the generate-react-cli default component config options using one-off commands. So for example, let's say you have set **withTest** to be `true` in the component config property. You can override it like this:
```
npx generate-react-cli component Box --withTest=false
```
Or vice versa, if you have set **withTest** to be `false` you can do this:
```
npx generate-react-cli component Box --withTest=true
```
Otherwise, if you don't pass any options, it will just use the default values from the generate-react-cli config file you have set.
<table>
<tr align="left">
<th>Options</th>
<th>Description</th>
<th>Value Type</th>
</tr>
<tr>
<td width="20%"><b>--path</b></td>
<td width="60%">
Value of the path where you want the component to be generated in (e.g. <b>src/components</b>).
</td>
<td width="20%">String</td>
</tr>
<tr>
<td width="20%"><b>--withLazy</b></td>
<td width="60%">
Creates a corresponding lazy file (a file that lazy-loads your component out of the box and enables <a href="https://reactjs.org/docs/code-splitting.html#code-splitting">code splitting</a>) with this component.
</td>
<td width="20%">Boolean</td>
</tr>
<tr>
<td width="20%"><b>--withStory</b></td>
<td width="60%">
Creates a corresponding (<a href="https://storybook.js.org">storybook</a>) story file with this component.
</td>
<td width="20%">Boolean</td>
</tr>
<tr>
<td width="20%"><b>--withStyle</b></td>
<td width="60%">
Creates a corresponding stylesheet file with this component.
</td>
<td width="20%">Boolean</td>
</tr>
<tr>
<td width="20%"><b>--withTest</b></td>
<td width="60%">
Creates a corresponding test file with this component.
</td>
<td width="20%">Boolean</td>
</tr>
</table>
## License
Generate React CLI is open source software [licensed as MIT](https://github.com/arminbro/generate-react-cli/blob/master/LICENSE).

@@ -10,3 +10,2 @@ const chalk = require('chalk');

const componentTemplates = [];
const componentTemplateTypeList = Object.values(componentTemplateTypes);

@@ -25,7 +24,5 @@ // --- Make sure component name is valid.

// --- Iterate through componentTemplateTypeList and build a list of componentTemplates.
// --- Iterate through componentTemplateTypes and build a list of componentTemplates.
for (let i = 0; i < componentTemplateTypeList.length; i += 1) {
const componentTemplateType = componentTemplateTypeList[i];
Object.values(componentTemplateTypes).forEach((componentTemplateType) => {
// --- Only get template if component option (withStyle, withTest, etc..) is true, or if template type is "component"

@@ -43,3 +40,3 @@

}
}
});

@@ -46,0 +43,0 @@ generateComponentTemplates(componentTemplates);

@@ -0,46 +1,86 @@

const chalk = require('chalk');
const program = require('commander');
const pkg = require('../package.json');
const { generateComponent } = require('./actions/componentActions');
const { getCLIConfigFile } = require('./services/grcConfigService');
const { componentLevelQuestions, getCLIConfigFile } = require('./services/grcConfigService');
module.exports = async function cli(args) {
const cliConfigFile = await getCLIConfigFile();
const isComponentCmd = args.find((arg) => arg.includes('c') || arg.includes('component'));
program.version(pkg.version);
// --- Generate component commands
// --- Generate component
Object.keys(cliConfigFile).forEach((configKey) => {
const configValue = cliConfigFile[configKey];
if (isComponentCmd) {
const hasComponentTypeOption = args.find((arg) => arg.includes('type'));
let component = null;
/**
* Check if each configValue is a component config
* and if it is, register it as a component command.
*/
if (hasComponentTypeOption) {
const componentType = hasComponentTypeOption.split('=')[1]; // get the component type value
if (
typeof configValue === 'object' &&
configValue.path !== undefined &&
configValue.withLazy !== undefined &&
configValue.withStory !== undefined &&
configValue.withStyle !== undefined &&
configValue.withTest !== undefined
) {
const commandName = configKey;
const commandOptions = configValue;
// if the component type does not exists in the cliConfigFile under `component` throw an error
program
.command(`${commandName} <name>`)
if (!cliConfigFile.component[componentType]) {
console.error(
chalk.red(
`
ERROR: Please make sure the component type you're trying to use exists in the
${chalk.bold('generate-react-cli.json')} config file under the ${chalk.bold('component')} object.
`
)
);
.option('-p, --path <path>', 'The path where the component will get genereted in.', commandOptions.path)
.option('--withStyle <withStyle>', 'With corresponding stylesheet file.', commandOptions.withStyle)
.option('--withTest <withTest>', 'With corresponding test file.', commandOptions.withTest)
.option('--withStory <withStory>', 'With corresponding story file.', commandOptions.withStory)
.option('--withLazy <withLazy>', 'With corresponding lazy file.', commandOptions.withLazy)
process.exit(1);
}
.action((componentName, cmd) => generateComponent(cmd, cliConfigFile, componentName));
/**
* A component type should have the same default componentLevelQuestions properties.
* Loop through each default componentLevelQuestion and make sure the custom component type
* is not missing any required properties.
*/
componentLevelQuestions.forEach((componentLevelQuestion) => {
const requiredComponentTypeProperty = componentLevelQuestion.name.split('component.default.')[1];
// if the component type has a missing required property throw an error
if (cliConfigFile.component[componentType][requiredComponentTypeProperty] === undefined) {
console.error(
chalk.red(`
ERROR: "${chalk.bold(requiredComponentTypeProperty)}" is a required property, and it is missing
from the "${chalk.bold(componentType)}" component type. Please make sure to set it.
Otherwise you won't be able to use your "${chalk.bold(componentType)}" component type.
`)
);
process.exit(1);
}
});
component = cliConfigFile.component[componentType];
} else {
component = cliConfigFile.component.default;
}
});
program
.command('component <name>')
.alias('c')
.option('-p, --path <path>', 'The path where the component will get genereted in.', component.path)
.option('--withStyle <withStyle>', 'With corresponding stylesheet file.', component.withStyle)
.option('--withTest <withTest>', 'With corresponding test file.', component.withTest)
.option('--withStory <withStory>', 'With corresponding story file.', component.withStory)
.option('--withLazy <withLazy>', 'With corresponding lazy file.', component.withLazy)
.option(
'--type <type>',
'You can pass a component type that you have configured in your GRC config file.',
'default'
)
.action((componentName, cmd) => generateComponent(cmd, cliConfigFile, componentName));
}
program.parse(args);
};

@@ -28,4 +28,4 @@ const chalk = require('chalk');

`
ERROR: The custom template path of "${templatePath}" does not exist.
Please make sure you're pointing to the right custom template path in your generate-react-cli.json config file.
ERROR: The custom template path of "${templatePath}" does not exist.
Please make sure you're pointing to the right custom template path in your generate-react-cli.json config file.
`

@@ -41,3 +41,3 @@ )

const { cssPreprocessor, testLibrary, usesCssModule, usesTypeScript } = cliConfigFile;
const { customTemplates } = cliConfigFile[cmd['_name']]; // get config property by command name
const { customTemplates } = cliConfigFile.component[cmd.type];
const fileExtension = usesTypeScript ? 'tsx' : 'js';

@@ -80,3 +80,3 @@ let template = null;

template = template.replace(`className={styles.TemplateName} `, '');
template = template.replace(` className={styles.TemplateName}`, '');
template = template.replace(`import styles from './TemplateName.module.css';`, '');

@@ -95,3 +95,3 @@ }

function getComponentStyleTemplate({ cliConfigFile, cmd, componentName, componentPathDir }) {
const { customTemplates } = cliConfigFile[cmd['_name']]; // get config property by command name
const { customTemplates } = cliConfigFile.component[cmd.type];
const { cssPreprocessor, usesCssModule } = cliConfigFile;

@@ -123,3 +123,3 @@ const module = usesCssModule ? '.module' : '';

function getComponentTestTemplate({ cliConfigFile, cmd, componentName, componentPathDir }) {
const { customTemplates } = cliConfigFile[cmd['_name']]; // get config property by command name
const { customTemplates } = cliConfigFile.component[cmd.type];
const { testLibrary, usesTypeScript } = cliConfigFile;

@@ -155,3 +155,3 @@ const fileExtension = usesTypeScript ? 'tsx' : 'js';

const { usesTypeScript } = cliConfigFile;
const { customTemplates } = cliConfigFile[cmd['_name']]; // get config property by command name
const { customTemplates } = cliConfigFile.component[cmd.type];
const fileExtension = usesTypeScript ? 'tsx' : 'js';

@@ -182,3 +182,3 @@ let template = null;

const { usesTypeScript } = cliConfigFile;
const { customTemplates } = cliConfigFile[cmd['_name']]; // get config property by command name
const { customTemplates } = cliConfigFile.component[cmd.type];
const fileExtension = usesTypeScript ? 'tsx' : 'js';

@@ -185,0 +185,0 @@ let template = null;

@@ -7,4 +7,2 @@ const chalk = require('chalk');

// private
// Generate React Config file questions.

@@ -44,3 +42,3 @@

type: 'input',
name: 'component.path',
name: 'component.default.path',
message: 'Set the default path directory to where your components will be generated in?',

@@ -51,3 +49,3 @@ default: () => 'src/components',

type: 'confirm',
name: 'component.withStyle',
name: 'component.default.withStyle',
message: 'Would you like to create a corresponding stylesheet file with each component you generate?',

@@ -57,3 +55,3 @@ },

type: 'confirm',
name: 'component.withTest',
name: 'component.default.withTest',
message: 'Would you like to create a corresponding test file with each component you generate?',

@@ -63,3 +61,3 @@ },

type: 'confirm',
name: 'component.withStory',
name: 'component.default.withStory',
message: 'Would you like to create a corresponding story with each component you generate?',

@@ -69,3 +67,3 @@ },

type: 'confirm',
name: 'component.withLazy',
name: 'component.default.withLazy',
message:

@@ -173,45 +171,2 @@ 'Would you like to create a corresponding lazy file (a file that lazy-loads your component out of the box and enables code splitting: https://reactjs.org/docs/code-splitting.html#code-splitting) with each component you generate?',

function cleanOldPropsFromCLIConfigFile(currentConfigFile) {
// new updated config file instance
const updatedConfigFile = { ...currentConfigFile };
// new component config instance
const component = {
path: currentConfigFile.component.path,
withLazy: currentConfigFile.component.withLazy,
withStory: currentConfigFile.component.withStory,
};
// get old component css property values and reassign them to new properties
if (currentConfigFile.component.css) {
component.withStyle = currentConfigFile.component.css.withStyle;
updatedConfigFile.usesCssModule = currentConfigFile.component.css.module;
updatedConfigFile.cssPreprocessor = currentConfigFile.component.css.preprocessor;
}
// get old component test property values and reassign them to new properties
if (currentConfigFile.component.test) {
component.withTest = currentConfigFile.component.test.withTest;
updatedConfigFile.testLibrary = currentConfigFile.component.test.library;
}
// reassign new component config instance to updatedConfigFile
updatedConfigFile.component = component;
// update generate-react-cli.json with the updatedConfigFile
outputFileSync('./generate-react-cli.json', JSON.stringify(updatedConfigFile, null, 2));
// return updatedConfigFile
return updatedConfigFile;
}
// public
async function getCLIConfigFile() {

@@ -227,10 +182,4 @@ // --- Make sure the cli commands are running from the root level of the project

accessSync('./generate-react-cli.json', constants.R_OK);
let currentConfigFile = JSON.parse(readFileSync('./generate-react-cli.json'));
const currentConfigFile = JSON.parse(readFileSync('./generate-react-cli.json'));
// --- Check to see if there are old properties in the component object and clean them up if there are
if (currentConfigFile.component.css || currentConfigFile.component.test) {
currentConfigFile = cleanOldPropsFromCLIConfigFile(currentConfigFile);
}
/**

@@ -264,3 +213,4 @@ * Check to see if there's a difference between grcConfigQuestions and the currentConfigFile.

module.exports = {
componentLevelQuestions,
getCLIConfigFile,
};