generate-react-cli
Advanced tools
Comparing version
108
package.json
{ | ||
"name": "generate-react-cli", | ||
"version": "1.7.5", | ||
"version": "2.0.0", | ||
"description": "A simple React CLI to generate components instantly and more.", | ||
"main": "src/index.js", | ||
"repository": "https://github.com/arminbro/generate-react-cli", | ||
"bugs": "https://github.com/arminbro/generate-react-cli/issues", | ||
"author": "Armin Broubakarian", | ||
"license": "MIT", | ||
"main": "bin/generate-react", | ||
"bin": { | ||
@@ -10,8 +14,12 @@ "generate-react": "bin/generate-react", | ||
}, | ||
"files": [ | ||
"bin/", | ||
"src/", | ||
"README.md", | ||
"CHANGELOG.md", | ||
"LICENSE" | ||
], | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"keywords": [ | ||
@@ -23,4 +31,18 @@ "cli", | ||
], | ||
"author": "Armin Broubakarian", | ||
"license": "MIT", | ||
"engines": { | ||
"node": ">=8.x", | ||
"npm": ">= 5.x" | ||
}, | ||
"browserslist": [ | ||
"maintained node versions" | ||
], | ||
"scripts": { | ||
"cm": "npx git-cz", | ||
"prebuild": "rimraf dist", | ||
"build": "babel src --out-dir dist --ignore 'src/**/*.test.js'", | ||
"build:watch": "npm run build -- --watch", | ||
"test": "jest --coverage", | ||
"test:watch": "npm run test -- --watch", | ||
"release": "standard-version" | ||
}, | ||
"dependencies": { | ||
@@ -30,17 +52,69 @@ "chalk": "^3.0.0", | ||
"deep-keys": "^0.5.0", | ||
"esm": "^3.2.25", | ||
"fs-extra": "^8.1.0", | ||
"inquirer": "^7.0.0", | ||
"lodash-es": "^4.17.15", | ||
"lodash": "^4.17.15", | ||
"replace": "^1.1.1" | ||
}, | ||
"files": [ | ||
"bin/", | ||
"src/", | ||
"templates/" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/arminbro/generate-react-cli" | ||
"devDependencies": { | ||
"@babel/cli": "^7.7.5", | ||
"@babel/core": "^7.7.5", | ||
"@babel/preset-env": "^7.7.6", | ||
"@commitlint/cli": "^8.2.0", | ||
"@commitlint/config-conventional": "^8.2.0", | ||
"babel-eslint": "^10.0.3", | ||
"eslint": "^6.7.2", | ||
"eslint-config-airbnb-base": "^14.0.0", | ||
"eslint-config-prettier": "^6.7.0", | ||
"eslint-plugin-import": "^2.19.1", | ||
"eslint-plugin-jsx-a11y": "^6.2.3", | ||
"eslint-plugin-prettier": "^3.1.1", | ||
"husky": "^3.1.0", | ||
"jest": "^24.9.0", | ||
"prettier": "^1.19.1", | ||
"pretty-quick": "^2.0.1", | ||
"rimraf": "^3.0.0", | ||
"standard-version": "^7.0.1" | ||
}, | ||
"babel": { | ||
"presets": [ | ||
[ | ||
"@babel/preset-env", | ||
{ | ||
"targets": { | ||
"node": true | ||
} | ||
} | ||
] | ||
] | ||
}, | ||
"jest": { | ||
"testEnvironment": "node", | ||
"collectCoverageFrom": [ | ||
"src/**/*.js" | ||
], | ||
"coverageThreshold": { | ||
"global": { | ||
"branches": 0, | ||
"functions": 0, | ||
"lines": 0, | ||
"statements": 0 | ||
} | ||
} | ||
}, | ||
"config": { | ||
"commitizen": { | ||
"path": "cz-conventional-changelog" | ||
} | ||
}, | ||
"commitlint": { | ||
"extends": [ | ||
"@commitlint/config-conventional" | ||
] | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "pretty-quick --staged", | ||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS" | ||
} | ||
} | ||
} |
103
readme.md
# Generate React CLI | ||
[](https://david-dm.org/arminbro/generate-react-cli) | ||
[](https://github.com/arminbro/generate-react-cli/blob/master/LICENSE) | ||
<p align="center"><img src="src/assets/component-cmd.gif?raw=true"/></p> | ||
<p align="center"><img src="docs/assets/component-cmd.gif?raw=true"/></p> | ||
## Why? | ||
To help speed up productivity in React projects and stop copying, pasting, and renaming files each time you want to create a new component. | ||
**_Few notes:_** | ||
- The CLI now supports two testing component library templates [Enzyme](https://airbnb.io/enzyme) & [Testing Library](https://testing-library.com) that work with [Jest](https://jestjs.io/). | ||
- The CLI has an opinion on how files are structured within the project. We follow "[grouping by features](https://reactjs.org/docs/faq-structure.html#grouping-by-features-or-routes)." | ||
[Create React App](https://create-react-app.dev/) and [Gatsby](https://www.gatsbyjs.org/) do a great job of initializing new projects, setting up the development environment, and optimizing the app for production use. Still, they don't have a way to generate new components similar to what [Angular CLI](https://cli.angular.io/) offers, and that's because they both try to stay as non-opinionated as possible and allow the developer to make those decisions. One example would be grouping by features vs. grouping by file type when creating components. | ||
## Install | ||
`npm i -g generate-react-cli` | ||
Generate React CLI focuses on generating new components by running a simple command. It also doesn't care if you run it in an existing [CRA](https://create-react-app.dev/), [Gatsby](https://www.gatsbyjs.org/), or a custom React project you built on your own. | ||
## Usage | ||
It does, however, have an opinion on how component files are structured. 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. | ||
### Generate Component | ||
`generate-react component <ComponentName>` | ||
**_A few notes:_** | ||
#### Shorthand | ||
`g-r c <ComponentName>` | ||
- Now supports React TypeScript 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. | ||
This command will create a folder with your component name within your default (e.g. **src/components**) directory, and its corresponding files. | ||
## You can install it globally and run it like this using npm: | ||
#### Options | ||
|Parameter|Description|Default Value| | ||
|---------|-----------|-------| | ||
| **-p** or<br>**--path** | Value of the path where you want the component to be generated in (e.g. **src/pages**). | **src/components** | ||
| **-t** or<br>**--withTest** | Create a corresponding test file with this component? | **Boolean value selected in "generate-react-cli.json" config file** | ||
| **-s** or<br>**--withStory** | Create a corresponding story file with this component? | **Boolean value selected in "generate-react-cli.json" config file** | ||
| **-l** or<br>**--withLazy** | 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 this component? | **Boolean value selected in "generate-react-cli.json" config file** | ||
``` | ||
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)_ | ||
## 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). | ||
### e.g. **generate-react-cli.json** | ||
```json | ||
@@ -44,4 +49,5 @@ { | ||
"css": { | ||
"preprocessor": "scss", | ||
"module": true | ||
"preprocessor": "css", | ||
"module": false, | ||
"withStyle": true | ||
}, | ||
@@ -52,8 +58,59 @@ "test": { | ||
}, | ||
"withStory": true, | ||
"withStory": false, | ||
"withLazy": false | ||
} | ||
}, | ||
"usesTypeScript": false | ||
} | ||
``` | ||
## 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 the generate-react-cli default config options for one-off commands. So for example, let's say in one of your projects you have set **withTest** to be `true` in your generate-react-cli config file as your default. You can override it for that one-off command like this: | ||
``` | ||
npx generate-react-cli c Box --no-withTest | ||
``` | ||
Or vice versa, if you have set **withTest** to be `false` you can do this: | ||
``` | ||
npx generate-react-cli c Box --withTest | ||
``` | ||
Otherwise, if you don't pass any options, it will just use the default ones from the generate-react-cli config file you have set. | ||
| Parameter | Description | | ||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| **--path** | Value of the path where you want the component to be generated in (e.g. **src/pages**). | | ||
| **--withStyle** | Creates a corresponding stylesheet file with this component. | | ||
| **--no-withStyle** | Creates component without the stylesheet file. | | ||
| **--withTest** | Creates a corresponding test file with this component. | | ||
| **--no-withTest** | Creates a component without the test file. | | ||
| **--withStory** | Creates a corresponding story file with this component. | | ||
| **--no-withStory** | Creates component without the story file. | | ||
| **--withLazy** | Creates 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 this component. | | ||
| **--no-withLazy** | Creates a component without the lazy file. | | ||
<br> | ||
Have fun! |
@@ -1,14 +0,14 @@ | ||
import chalk from 'chalk'; | ||
import { generateComponentTemplates, getTestingLibraryTemplate } from '../services/templateService'; | ||
import componentJsTemplate from '../templates/components/componentJsTemplate'; | ||
import componentLazyTemplate from '../templates/components/componentLazyTemplate'; | ||
import componentCssTemplate from '../templates/components/componentCssTemplate'; | ||
import componentStoryTemplate from '../templates/components/componentStoryTemplate'; | ||
const chalk = require('chalk'); | ||
const { | ||
componentTemplateTypes, | ||
generateComponentTemplates, | ||
getComponentTemplate, | ||
} = require('../services/componentTemplateService'); | ||
export function generateComponent(componentName, cmd, componentConfig) { | ||
const componentPathDir = `${cmd.path}/${componentName}`; | ||
const module = componentConfig.css.module ? '.module' : ''; | ||
let jsTemplate = componentJsTemplate; | ||
function generateComponent(cmd, cliConfigFile, componentName) { | ||
const componentTemplates = []; | ||
const componentTemplateTypeList = Object.values(componentTemplateTypes); | ||
// Make sure component name is valid. | ||
// --- Make sure component name is valid. | ||
if (!componentName.match(/^[$A-Z_][0-9A-Z_$]*$/i)) { | ||
@@ -24,67 +24,23 @@ console.error( | ||
// if test library is not Testing Library. Remove data-testid from jsTemplate | ||
if (componentConfig.test.library !== 'Testing Library') { | ||
jsTemplate = jsTemplate.replace(` data-testid="TemplateName"`, ''); | ||
} | ||
// --- Iterate through componentTemplateTypeList and build a list of componentTemplates. | ||
// if the css module is true make sure to update the componentJsTemplate accordingly | ||
if (module.length) { | ||
jsTemplate = jsTemplate.replace( | ||
`'./TemplateName.module.css'`, | ||
`'./${componentName}${module}.${componentConfig.css.preprocessor}'` | ||
); | ||
} else { | ||
jsTemplate = jsTemplate.replace(`{styles.TemplateName}`, `"${componentName}"`); | ||
jsTemplate = jsTemplate.replace( | ||
`styles from './TemplateName.module.css'`, | ||
`'./${componentName}${module}.${componentConfig.css.preprocessor}'` | ||
); | ||
} | ||
for (let i = 0; i < componentTemplateTypeList.length; i += 1) { | ||
const componentTemplateType = componentTemplateTypeList[i]; | ||
const componentTemplates = [ | ||
{ | ||
template: jsTemplate, | ||
templateType: `Component "${componentName}.js"`, | ||
componentPath: `${componentPathDir}/${componentName}.js`, | ||
componentName, | ||
}, | ||
{ | ||
template: componentCssTemplate, | ||
templateType: `Stylesheet "${componentName}${module}.${componentConfig.css.preprocessor}"`, | ||
componentPath: `${componentPathDir}/${componentName}${module}.${componentConfig.css.preprocessor}`, | ||
componentName, | ||
}, | ||
]; | ||
// --- Only get template if component option (withStyle, withTest, etc..) is true, or if template type is "component" | ||
// converting boolean to string intentionally. | ||
if (cmd.withTest.toString() === 'true') { | ||
componentTemplates.push({ | ||
template: getTestingLibraryTemplate(componentName, componentConfig), | ||
templateType: `Test "${componentName}.test.js"`, | ||
componentPath: `${componentPathDir}/${componentName}.test.js`, | ||
componentName, | ||
}); | ||
} | ||
if (cmd[componentTemplateType] || componentTemplateType === componentTemplateTypes.COMPONENT) { | ||
const template = getComponentTemplate(cmd, cliConfigFile, componentName, componentTemplateType); | ||
// converting boolean to string intentionally. | ||
if (cmd.withStory.toString() === 'true') { | ||
componentTemplates.push({ | ||
template: componentStoryTemplate, | ||
templateType: `Story "${componentName}.stories.js"`, | ||
componentPath: `${componentPathDir}/${componentName}.stories.js`, | ||
componentName, | ||
}); | ||
if (template) { | ||
componentTemplates.push(template); | ||
} | ||
} | ||
} | ||
// converting boolean to string intentionally. | ||
if (cmd.withLazy.toString() === 'true') { | ||
componentTemplates.push({ | ||
template: componentLazyTemplate, | ||
templateType: `Lazy "${componentName}.lazy.js"`, | ||
componentPath: `${componentPathDir}/${componentName}.lazy.js`, | ||
componentName, | ||
}); | ||
} | ||
generateComponentTemplates(componentTemplates); | ||
} | ||
module.exports = { | ||
generateComponent, | ||
}; |
@@ -1,38 +0,39 @@ | ||
import program from 'commander'; | ||
import chalk from 'chalk'; | ||
import pkg from '../package.json'; | ||
import { generateComponent } from './actions/componentActions'; | ||
import { getCLIConfigFile } from './services/configService'; | ||
const program = require('commander'); | ||
const chalk = require('chalk'); | ||
const pkg = require('../package.json'); | ||
const { generateComponent } = require('./actions/componentActions'); | ||
const { getCLIConfigFile } = require('./services/grcConfigService'); | ||
let commandNotFound = true; | ||
export async function cli(args) { | ||
module.exports = async function cli(args) { | ||
const cliConfigFile = await getCLIConfigFile(); | ||
const { component } = cliConfigFile; | ||
let commandNotFound = true; | ||
program.version(pkg.version); | ||
// Generate Component | ||
// --- Generate Component | ||
program | ||
.command('component <name>') | ||
.alias('c') | ||
.option('-p, --path <path>', 'With specified path value', component.path || './src/components') | ||
.option( | ||
'-t, --withTest <withTest>', | ||
'Would you like to create a corresponding test file with this component?', | ||
component.test.withTest | ||
) | ||
.option( | ||
'-s, --withStory <withStory>', | ||
'Would you like to create a corresponding story file with this component?', | ||
component.withStory | ||
) | ||
.option( | ||
'-l, --withLazy <withLazy>', | ||
'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 this component?', | ||
component.withLazy | ||
) | ||
.action((componentName, cmd) => generateComponent(componentName, cmd, component)) | ||
.action(() => (commandNotFound = false)); | ||
.option('-p, --path <path>', 'The path where the component will get genereted in.', component.path) | ||
.option('--withStyle', 'With corresponding test file.', component.css.withStyle) | ||
.option('--no-withStyle', 'Without corresponding test file.') | ||
.option('--withTest', 'With corresponding test file.', component.test.withTest) | ||
.option('--no-withTest', 'Without corresponding test file.') | ||
.option('--withStory', 'With corresponding story file.', component.withStory) | ||
.option('--no-withStory', 'Without corresponding story file.') | ||
.option('--withLazy', 'With corresponding lazy file.', component.withLazy) | ||
.option('--no-withLazy', 'Without corresponding lazy file.') | ||
.action((componentName, cmd) => generateComponent(cmd, cliConfigFile, componentName)) | ||
.action(() => { | ||
commandNotFound = false; | ||
}); | ||
program.parse(args); | ||
@@ -45,2 +46,2 @@ | ||
} | ||
} | ||
}; |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
7
-12.5%22
29.41%504
30.57%1
-50%115
98.28%33918
-77.64%18
Infinity%5
25%1
Infinity%+ Added
- Removed
- Removed
- Removed
- Removed