generate-react-cli
Advanced tools
Comparing version
@@ -5,2 +5,8 @@ # Changelog | ||
## [4.2.0](https://github.com/arminbro/generate-react-cli/compare/v4.1.1...v4.2.0) (2020-05-02) | ||
### Features | ||
- 🎸 Allow custom file templates ([6104241](https://github.com/arminbro/generate-react-cli/commit/610424136989b1f18de1e6fa9a04084114cde64b)), closes [#12](https://github.com/arminbro/generate-react-cli/issues/12) | ||
### [4.1.1](https://github.com/arminbro/generate-react-cli/compare/v4.1.0...v4.1.1) (2020-04-23) | ||
@@ -7,0 +13,0 @@ |
{ | ||
"name": "generate-react-cli", | ||
"version": "4.1.1", | ||
"version": "4.2.0", | ||
"description": "A simple React CLI to generate components instantly and more.", | ||
@@ -5,0 +5,0 @@ "repository": "https://github.com/arminbro/generate-react-cli", |
@@ -16,3 +16,4 @@ # Generate React CLI | ||
- Now supports React [TypeScript](https://www.typescriptlang.org/) projects. | ||
- Now supports custom templates ([read more](#custom-templates)). 🎉 | ||
- Now 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. | ||
@@ -40,3 +41,3 @@ - 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. | ||
### e.g. **generate-react-cli.json** | ||
### Example of the **generate-react-cli.json** config file: | ||
@@ -66,2 +67,92 @@ ```json | ||
## Custom 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. | ||
Both the `component` and `page` properties (within the **generate-react-cli.json** config file) can accept an optional `customTemplates` object property. | ||
### Example of the `customTemplates` object: | ||
```json | ||
"customTemplates": { | ||
"component": "templates/component.js", | ||
"lazy": "templates/lazy.js", | ||
"story": "templates/story.js", | ||
"style": "templates/style.scss", | ||
"test": "templates/test.js" | ||
}, | ||
``` | ||
The keys represent the type of template, and the values are the paths that point to where your custom template lives in your project/system. | ||
### Example of using the `customTemplates` property in the generate-react-cli.json config file: | ||
```json | ||
{ | ||
"usesTypeScript": false, | ||
"usesCssModule": true, | ||
"cssPreprocessor": "scss", | ||
"testLibrary": "Testing Library", | ||
"component": { | ||
"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 | ||
}, | ||
"page": { | ||
"customTemplates": { | ||
"test": "templates/page/test.js" | ||
}, | ||
"path": "src/pages", | ||
"withStyle": true, | ||
"withTest": true, | ||
"withStory": false, | ||
"withLazy": true | ||
} | ||
} | ||
``` | ||
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. | ||
### Example of a custom component template file: | ||
```jsx | ||
// templates/component/component.js | ||
import React from 'react'; | ||
import styles from './TemplateName.module.css'; | ||
const TemplateName = () => ( | ||
<div className={styles.TemplateName} data-testid="TemplateName"> | ||
<h1>TemplateName component</h1> | ||
</div> | ||
); | ||
export default TemplateName; | ||
``` | ||
**Important** - Make sure to use the `TemplateName` keyword in your templates. The CLI will use this keyword to replace it with your component name. | ||
### Example of a custom test template file: | ||
```jsx | ||
// templates/component/test.js | ||
import React from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
import TemplateName from './TemplateName'; | ||
it('It should mount', () => { | ||
const div = document.createElement('div'); | ||
ReactDOM.render(<TemplateName />, div); | ||
ReactDOM.unmountComponentAtNode(div); | ||
}); | ||
``` | ||
## Usage | ||
@@ -68,0 +159,0 @@ |
const chalk = require('chalk'); | ||
const replace = require('replace'); | ||
const { camelCase } = require('lodash'); | ||
const { existsSync, outputFileSync } = require('fs-extra'); | ||
const { existsSync, outputFileSync, readFileSync } = require('fs-extra'); | ||
const componentJsTemplate = require('../templates/component/componentJsTemplate'); | ||
@@ -17,32 +17,66 @@ const componentTsTemplate = require('../templates/component/componentTsTemplate'); | ||
function loadCustomTemplate(templatePath) { | ||
// --- Try loading custom template | ||
try { | ||
const template = readFileSync(templatePath, 'utf8'); | ||
return template; | ||
} catch (e) { | ||
console.error( | ||
chalk.red( | ||
` | ||
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. | ||
` | ||
) | ||
); | ||
return process.exit(1); | ||
} | ||
} | ||
function getComponentScriptTemplate({ cmd, cliConfigFile, componentName, componentPathDir }) { | ||
const { cssPreprocessor, testLibrary, usesCssModule, usesTypeScript } = cliConfigFile; | ||
const { customTemplates } = cliConfigFile[cmd['_name']]; // get config property by command name | ||
const fileExtension = usesTypeScript ? 'tsx' : 'js'; | ||
let template = usesTypeScript ? componentTsTemplate : componentJsTemplate; | ||
let template = null; | ||
// --- If test library is not Testing Library or if withTest is false. Remove data-testid from template | ||
// Check for a custom component template. | ||
if (testLibrary !== 'Testing Library' || !cmd.withTest) { | ||
template = template.replace(` data-testid="TemplateName"`, ''); | ||
} | ||
if (customTemplates && customTemplates.component) { | ||
// --- Load and use the custom component template | ||
// --- If it has a corresponding stylesheet | ||
template = loadCustomTemplate(customTemplates.component); | ||
} else { | ||
// --- Else use GRC built-in component template | ||
if (cmd.withStyle) { | ||
const module = usesCssModule ? '.module' : ''; | ||
const cssPath = `${componentName}${module}.${cssPreprocessor}`; | ||
template = usesTypeScript ? componentTsTemplate : componentJsTemplate; | ||
// --- If the css module is true make sure to update the template accordingly | ||
// --- If test library is not Testing Library or if withTest is false. Remove data-testid from template | ||
if (module.length) { | ||
template = template.replace(`'./TemplateName.module.css'`, `'./${cssPath}'`); | ||
if (testLibrary !== 'Testing Library' || !cmd.withTest) { | ||
template = template.replace(` data-testid="TemplateName"`, ''); | ||
} | ||
// --- If it has a corresponding stylesheet | ||
if (cmd.withStyle) { | ||
const module = usesCssModule ? '.module' : ''; | ||
const cssPath = `${componentName}${module}.${cssPreprocessor}`; | ||
// --- If the css module is true make sure to update the template accordingly | ||
if (module.length) { | ||
template = template.replace(`'./TemplateName.module.css'`, `'./${cssPath}'`); | ||
} else { | ||
template = template.replace(`{styles.TemplateName}`, `"${componentName}"`); | ||
template = template.replace(`styles from './TemplateName.module.css'`, `'./${cssPath}'`); | ||
} | ||
} else { | ||
template = template.replace(`{styles.TemplateName}`, `"${componentName}"`); | ||
template = template.replace(`styles from './TemplateName.module.css'`, `'./${cssPath}'`); | ||
// --- If no stylesheet, remove className attribute and style import from jsTemplate | ||
template = template.replace(`className={styles.TemplateName} `, ''); | ||
template = template.replace(`import styles from './TemplateName.module.css';`, ''); | ||
} | ||
} else { | ||
// --- If no stylesheet, remove className attribute and style import from jsTemplate | ||
template = template.replace(`className={styles.TemplateName} `, ''); | ||
template = template.replace(`import styles from './TemplateName.module.css';`, ''); | ||
} | ||
@@ -58,9 +92,23 @@ | ||
function getComponentStyleTemplate({ cliConfigFile, componentName, componentPathDir }) { | ||
function getComponentStyleTemplate({ cliConfigFile, cmd, componentName, componentPathDir }) { | ||
const { customTemplates } = cliConfigFile[cmd['_name']]; // get config property by command name | ||
const { cssPreprocessor, usesCssModule } = cliConfigFile; | ||
const module = usesCssModule ? '.module' : ''; | ||
const cssPath = `${componentName}${module}.${cssPreprocessor}`; | ||
let template = null; | ||
// Check for a custom style template. | ||
if (customTemplates && customTemplates.style) { | ||
// --- Load and use the custom style template | ||
template = loadCustomTemplate(customTemplates.style); | ||
} else { | ||
// --- Else use GRC built-in style template | ||
template = componentCssTemplate; | ||
} | ||
return { | ||
template: componentCssTemplate, | ||
template, | ||
templateType: `Stylesheet "${cssPath}"`, | ||
@@ -72,3 +120,4 @@ componentPath: `${componentPathDir}/${cssPath}`, | ||
function getComponentTestTemplate({ cliConfigFile, componentName, componentPathDir }) { | ||
function getComponentTestTemplate({ cliConfigFile, cmd, componentName, componentPathDir }) { | ||
const { customTemplates } = cliConfigFile[cmd['_name']]; // get config property by command name | ||
const { testLibrary, usesTypeScript } = cliConfigFile; | ||
@@ -78,5 +127,11 @@ const fileExtension = usesTypeScript ? 'tsx' : 'js'; | ||
// --- Get test template based on test library type | ||
// Check for a custom test template. | ||
if (testLibrary === 'Enzyme') { | ||
if (customTemplates && customTemplates.test) { | ||
// --- Load and use the custom test template | ||
template = loadCustomTemplate(customTemplates.test); | ||
} else if (testLibrary === 'Enzyme') { | ||
// --- Else use GRC built-in test template based on test library type | ||
template = componentTestEnzymeTemplate; | ||
@@ -97,8 +152,22 @@ } else if (testLibrary === 'Testing Library') { | ||
function getComponentStoryTemplate({ cliConfigFile, componentName, componentPathDir }) { | ||
function getComponentStoryTemplate({ cliConfigFile, cmd, componentName, componentPathDir }) { | ||
const { usesTypeScript } = cliConfigFile; | ||
const { customTemplates } = cliConfigFile[cmd['_name']]; // get config property by command name | ||
const fileExtension = usesTypeScript ? 'tsx' : 'js'; | ||
let template = null; | ||
// Check for a custom story template. | ||
if (customTemplates && customTemplates.story) { | ||
// --- Load and use the custom story template | ||
template = loadCustomTemplate(customTemplates.story); | ||
} else { | ||
// --- Else use GRC built-in story template | ||
template = componentStoryTemplate; | ||
} | ||
return { | ||
template: componentStoryTemplate, | ||
template, | ||
templateType: `Story "${componentName}.stories.${fileExtension}"`, | ||
@@ -110,8 +179,22 @@ componentPath: `${componentPathDir}/${componentName}.stories.${fileExtension}`, | ||
function getComponentLazyTemplate({ cliConfigFile, componentName, componentPathDir }) { | ||
function getComponentLazyTemplate({ cliConfigFile, cmd, componentName, componentPathDir }) { | ||
const { usesTypeScript } = cliConfigFile; | ||
const { customTemplates } = cliConfigFile[cmd['_name']]; // get config property by command name | ||
const fileExtension = usesTypeScript ? 'tsx' : 'js'; | ||
let template = null; | ||
// Check for a custom lazy template. | ||
if (customTemplates && customTemplates.lazy) { | ||
// --- Load and use the custom lazy template | ||
template = loadCustomTemplate(customTemplates.lazy); | ||
} else { | ||
// --- Else use GRC built-in lazy template | ||
template = usesTypeScript ? componentTsLazyTemplate : componentLazyTemplate; | ||
} | ||
return { | ||
template: usesTypeScript ? componentTsLazyTemplate : componentLazyTemplate, | ||
template, | ||
templateType: `Lazy "${componentName}.lazy.${fileExtension}"`, | ||
@@ -118,0 +201,0 @@ componentPath: `${componentPathDir}/${componentName}.lazy.${fileExtension}`, |
45348
14%637
10.59%324
39.06%