9 | );
10 |
11 | templatename.propTypes = {};
12 |
13 | templatename.defaultProps = {};
14 |
15 | export default templatename;
16 | `;
17 |
--------------------------------------------------------------------------------
/.github/workflows/repo-pruner.yml:
--------------------------------------------------------------------------------
1 | name: 'Run Repo Pruner'
2 | on:
3 | schedule:
4 | - cron: '0 0 1 * *' # Runs once a month - At 00:00 on day-of-month 1.
5 | workflow_dispatch:
6 |
7 | jobs:
8 | repo-pruner:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Run Repo Pruner
12 | uses: arminbro/repo-pruner@v2.1.22
13 | env:
14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15 | with:
16 | inactive_days: 30
17 |
--------------------------------------------------------------------------------
/src/templates/component/componentTestTestingLibraryTemplate.js:
--------------------------------------------------------------------------------
1 | export default `import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import '@testing-library/jest-dom';
4 | import templatename from './templatename';
5 |
6 | describe('', () => {
7 | test('it should mount', () => {
8 | render();
9 |
10 | const templateName = screen.getByTestId('templatename');
11 |
12 | expect(templateName).toBeInTheDocument();
13 | });
14 | });`;
15 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish
2 |
3 | on:
4 | push:
5 | branches: [alpha, beta, master]
6 |
7 | jobs:
8 | publish:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Check out repository
12 | uses: actions/checkout@v4
13 | with:
14 | fetch-depth: 0
15 | - name: Set up Node.js
16 | uses: actions/setup-node@v4
17 | with:
18 | node-version: '20.19.5'
19 | - run: npm ci
20 | - run: npx semantic-release
21 | env:
22 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
23 | NPM_TOKEN: ${{secrets.NPM_TOKEN}}
24 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/bin/generate-react.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import cli from '../src/cli.js';
3 |
4 | const isNotValidNodeVersion = () => {
5 | const currentNodeVersion = process.versions.node;
6 | const semver = currentNodeVersion.split('.');
7 | const major = semver[0];
8 |
9 | if (major < 18) {
10 | console.error(
11 | // eslint-disable-next-line
12 | 'You are running Node ' +
13 | currentNodeVersion +
14 | ' Generate React CLI requires Node 18 or higher. Please update your version of Node.'
15 | );
16 |
17 | return true;
18 | }
19 |
20 | return false;
21 | };
22 |
23 | // --- Check if user is running Node 12 or higher.
24 |
25 | if (isNotValidNodeVersion()) {
26 | process.exit(1);
27 | }
28 |
29 | cli(process.argv);
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Machine (please complete the following information):**
24 | - OS: [e.g. macOS Mojave]
25 | - Node Version [e.g. v10.16.1]
26 |
27 | **Additional context**
28 | Add any other context about the problem here.
29 |
30 | **Screenshots**
31 | If applicable, add screenshots to help explain your problem.
32 |
--------------------------------------------------------------------------------
/src/cli.js:
--------------------------------------------------------------------------------
1 | import { program } from 'commander';
2 | import { createRequire } from 'module';
3 | import { config as dotEnvConfig } from 'dotenv';
4 | import path from 'path';
5 |
6 | import initGenerateComponentCommand from './commands/generateComponent.js';
7 | import { getCLIConfigFile } from './utils/grcConfigUtils.js';
8 |
9 | export default async function cli(args) {
10 | const cliConfigFile = await getCLIConfigFile();
11 | const localRequire = createRequire(import.meta.url);
12 | const pkg = localRequire('../package.json');
13 |
14 | // init dotenv
15 |
16 | dotEnvConfig({ path: path.resolve(process.cwd(), '.env.local') });
17 |
18 | // Initialize generate component command
19 |
20 | initGenerateComponentCommand(args, cliConfigFile, program);
21 |
22 | program.version(pkg.version);
23 | program.parse(args);
24 | }
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Armin Broubakarian
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/commands/generateComponent.js:
--------------------------------------------------------------------------------
1 | import {
2 | generateComponent,
3 | getComponentByType,
4 | getCorrespondingComponentFileTypes,
5 | } from '../utils/generateComponentUtils.js';
6 |
7 | export default function initGenerateComponentCommand(args, cliConfigFile, program) {
8 | const selectedComponentType = getComponentByType(args, cliConfigFile);
9 |
10 | const componentCommand = program
11 | .command('component [names...]')
12 | .alias('c')
13 |
14 | // Static component command option defaults.
15 |
16 | .option('-p, --path ', 'The path where the component will get generated in.', selectedComponentType.path)
17 | .option(
18 | '--type ',
19 | 'You can pass a component type that you have configured in your GRC config file.',
20 | 'default'
21 | )
22 | .option(
23 | '-f, --flat',
24 | 'Generate the files in the mentioned path instead of creating new folder for it',
25 | selectedComponentType.flat || false
26 | )
27 | .option('--dry-run', 'Show what will be generated without writing to disk')
28 | .option(
29 | '--customDirectory ',
30 | 'You can pass a cased path template that will be used as the component path for the component being generated.\n' +
31 | 'E.g. this allows you to add a prefix or suffix to the component path, ' +
32 | 'or change the case of the name of the directory holding the components to kebab-case.\n' +
33 | 'Examples:\n' +
34 | '- TemplateName\n' +
35 | '- template-name\n' +
36 | '- TemplateNameSuffix'
37 | );
38 |
39 | // Dynamic component command option defaults.
40 |
41 | const dynamicOptions = getCorrespondingComponentFileTypes(selectedComponentType);
42 |
43 | dynamicOptions.forEach((dynamicOption) => {
44 | componentCommand.option(
45 | `--${dynamicOption} <${dynamicOption}>`,
46 | `With corresponding ${dynamicOption.split('with')[1]} file.`,
47 | selectedComponentType[dynamicOption]
48 | );
49 | });
50 |
51 | // Component command action.
52 |
53 | componentCommand.action((componentNames, cmd) =>
54 | componentNames.forEach((componentName) => generateComponent(componentName, cmd, cliConfigFile))
55 | );
56 | }
57 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generate-react-cli",
3 | "version": "9.1.0",
4 | "description": "A simple React CLI to generate components instantly and more.",
5 | "repository": "https://github.com/arminbro/generate-react-cli",
6 | "bugs": "https://github.com/arminbro/generate-react-cli/issues",
7 | "author": "Armin Broubakarian",
8 | "license": "MIT",
9 | "main": "bin/generate-react",
10 | "bin": {
11 | "generate-react": "bin/generate-react.js"
12 | },
13 | "type": "module",
14 | "files": [
15 | "bin/",
16 | "src/",
17 | "README.md",
18 | "CHANGELOG.md",
19 | "LICENSE"
20 | ],
21 | "publishConfig": {
22 | "access": "public"
23 | },
24 | "keywords": [
25 | "cli",
26 | "react",
27 | "build-tools",
28 | "generate-react-cli"
29 | ],
30 | "engines": {
31 | "node": ">=10.x",
32 | "npm": ">= 6.x"
33 | },
34 | "browserslist": [
35 | "maintained node versions"
36 | ],
37 | "scripts": {
38 | "prepare": "husky install"
39 | },
40 | "dependencies": {
41 | "chalk": "5.6.2",
42 | "commander": "14.0.0",
43 | "deep-keys": "0.5.0",
44 | "dotenv": "16.6.1",
45 | "fs-extra": "11.2.0",
46 | "inquirer": "12.9.4",
47 | "lodash": "4.17.21",
48 | "replace": "1.2.2"
49 | },
50 | "devDependencies": {
51 | "@commitlint/cli": "19.8.1",
52 | "@commitlint/config-conventional": "19.8.1",
53 | "@semantic-release/commit-analyzer": "13.0.1",
54 | "@semantic-release/git": "10.0.1",
55 | "@semantic-release/github": "11.0.5",
56 | "@semantic-release/npm": "12.0.2",
57 | "@semantic-release/release-notes-generator": "14.0.3",
58 | "eslint": "8.57.1",
59 | "eslint-config-airbnb-base": "15.0.0",
60 | "eslint-config-prettier": "9.1.2",
61 | "eslint-plugin-prettier": "5.5.4",
62 | "husky": "9.1.7",
63 | "prettier": "3.6.2",
64 | "pretty-quick": "4.2.2",
65 | "semantic-release": "24.2.7"
66 | },
67 | "prettier": {
68 | "singleQuote": true,
69 | "trailingComma": "es5",
70 | "printWidth": 120
71 | },
72 | "release": {
73 | "plugins": [
74 | "@semantic-release/commit-analyzer",
75 | "@semantic-release/release-notes-generator",
76 | "@semantic-release/npm",
77 | [
78 | "@semantic-release/git",
79 | {
80 | "assets": [
81 | "package.json"
82 | ],
83 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
84 | }
85 | ],
86 | "@semantic-release/github"
87 | ]
88 | },
89 | "commitlint": {
90 | "extends": [
91 | "@commitlint/config-conventional"
92 | ],
93 | "rules": {
94 | "body-max-line-length": [
95 | 0,
96 | "always",
97 | 200
98 | ]
99 | }
100 | },
101 | "eslintConfig": {
102 | "extends": [
103 | "airbnb-base",
104 | "plugin:prettier/recommended"
105 | ],
106 | "env": {
107 | "commonjs": false,
108 | "node": true
109 | },
110 | "parserOptions": {
111 | "ecmaVersion": "latest"
112 | },
113 | "rules": {
114 | "import/extensions": [
115 | {
116 | "js": "always"
117 | }
118 | ]
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/utils/grcConfigUtils.js:
--------------------------------------------------------------------------------
1 | import chalk from 'chalk';
2 |
3 | import deepKeys from 'deep-keys';
4 | import inquirer from 'inquirer';
5 | import merge from 'lodash/merge.js';
6 | import fsExtra from 'fs-extra';
7 |
8 | const { accessSync, constants, outputFileSync, readFileSync } = fsExtra;
9 | const { prompt } = inquirer;
10 |
11 | // Generate React Config file questions.
12 |
13 | // --- project level questions.
14 |
15 | const projectLevelQuestions = [
16 | {
17 | type: 'confirm',
18 | name: 'usesTypeScript',
19 | message: 'Does this project use TypeScript?',
20 | },
21 | {
22 | type: 'confirm',
23 | name: 'usesStyledComponents',
24 | message: 'Does this project use styled-components?',
25 | },
26 | {
27 | type: 'confirm',
28 | when: (answers) => !answers['usesStyledComponents'],
29 | name: 'usesCssModule',
30 | message: 'Does this project use CSS modules?',
31 | },
32 | {
33 | type: 'list',
34 | name: 'cssPreprocessor',
35 | when: (answers) => !answers['usesStyledComponents'],
36 | message: 'Does this project use a CSS Preprocessor?',
37 | choices: ['css', 'scss', 'less', 'styl'],
38 | },
39 | {
40 | type: 'list',
41 | name: 'testLibrary',
42 | message: 'What testing library does your project use?',
43 | choices: ['Testing Library', 'Enzyme', 'None'],
44 | },
45 | ];
46 |
47 | // --- component level questions.
48 |
49 | export const componentLevelQuestions = [
50 | {
51 | type: 'input',
52 | name: 'component.default.path',
53 | message: 'Set the default path directory to where your components will be generated in?',
54 | default: () => 'src/components',
55 | },
56 | {
57 | type: 'confirm',
58 | name: 'component.default.withStyle',
59 | message: 'Would you like to create a corresponding stylesheet file with each component you generate?',
60 | },
61 | {
62 | type: 'confirm',
63 | name: 'component.default.withTest',
64 | message: 'Would you like to create a corresponding test file with each component you generate?',
65 | },
66 | {
67 | type: 'confirm',
68 | name: 'component.default.withStory',
69 | message: 'Would you like to create a corresponding story with each component you generate?',
70 | },
71 | {
72 | type: 'confirm',
73 | name: 'component.default.withLazy',
74 | message:
75 | '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?',
76 | },
77 | ];
78 |
79 | // --- merge all questions together.
80 |
81 | const grcConfigQuestions = [...projectLevelQuestions, ...componentLevelQuestions];
82 |
83 | async function createCLIConfigFile() {
84 | try {
85 | console.log();
86 | console.log(
87 | chalk.cyan(
88 | '--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------'
89 | )
90 | );
91 | console.log(
92 | chalk.cyan("It looks like this is the first time that you're running generate-react-cli within this project.")
93 | );
94 | console.log();
95 | console.log(
96 | chalk.cyan(
97 | 'Answer a few questions to customize generate-react-cli for your project needs (this will create a "generate-react-cli.json" config file on the root level of this project).'
98 | )
99 | );
100 | console.log(
101 | chalk.cyan(
102 | '--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------'
103 | )
104 | );
105 | console.log();
106 |
107 | const answers = await prompt(grcConfigQuestions);
108 |
109 | outputFileSync('generate-react-cli.json', JSON.stringify(answers, null, 2));
110 |
111 | console.log();
112 | console.log(
113 | chalk.cyan(
114 | 'The "generate-react-cli.json" config file has been successfully created on the root level of your project.'
115 | )
116 | );
117 |
118 | console.log('');
119 | console.log(chalk.cyan('You can always go back and update it as needed.'));
120 | console.log('');
121 | console.log(chalk.cyan('Happy Hacking!'));
122 | console.log('');
123 | console.log('');
124 |
125 | return answers;
126 | } catch (e) {
127 | console.error(chalk.red.bold('ERROR: Could not create a "generate-react-cli.json" config file.'));
128 | return e;
129 | }
130 | }
131 |
132 | async function updateCLIConfigFile(missingConfigQuestions, currentConfigFile) {
133 | try {
134 | console.log('');
135 | console.log(
136 | chalk.cyan(
137 | '------------------------------------------------------------------------------------------------------------------------------'
138 | )
139 | );
140 | console.log(
141 | chalk.cyan(
142 | 'Generate React CLI has been updated and has a few new features from the last time you ran it within this project.'
143 | )
144 | );
145 | console.log('');
146 | console.log(chalk.cyan('Please answer a few questions to update the "generate-react-cli.json" config file.'));
147 | console.log(
148 | chalk.cyan(
149 | '------------------------------------------------------------------------------------------------------------------------------'
150 | )
151 | );
152 | console.log('');
153 |
154 | const answers = await prompt(missingConfigQuestions);
155 | const updatedAnswers = merge({}, currentConfigFile, answers);
156 |
157 | outputFileSync('generate-react-cli.json', JSON.stringify(updatedAnswers, null, 2));
158 |
159 | console.log();
160 | console.log(chalk.cyan('The ("generate-react-cli.json") has successfully updated for this project.'));
161 |
162 | console.log();
163 | console.log(chalk.cyan('You can always go back and manually update it as needed.'));
164 | console.log();
165 | console.log(chalk.cyan('Happy Hacking!'));
166 | console.log();
167 | console.log();
168 |
169 | return updatedAnswers;
170 | } catch (e) {
171 | console.error(chalk.red.bold('ERROR: Could not update the "generate-react-cli.json" config file.'));
172 | return e;
173 | }
174 | }
175 |
176 | export async function getCLIConfigFile() {
177 | // --- Make sure the cli commands are running from the root level of the project
178 |
179 | try {
180 | accessSync('./package.json', constants.R_OK);
181 |
182 | // --- Check to see if the config file exists
183 |
184 | try {
185 | accessSync('./generate-react-cli.json', constants.R_OK);
186 | const currentConfigFile = JSON.parse(readFileSync('./generate-react-cli.json'));
187 |
188 | /**
189 | * Check to see if there's a difference between grcConfigQuestions and the currentConfigFile.
190 | * If there is, update the currentConfigFile with the missingConfigQuestions.
191 | */
192 |
193 | const missingConfigQuestions = grcConfigQuestions.filter(
194 | (question) =>
195 | !deepKeys(currentConfigFile).includes(question.name) &&
196 | (question.when ? question.when(currentConfigFile) : true)
197 | );
198 |
199 | if (missingConfigQuestions.length) {
200 | return await updateCLIConfigFile(missingConfigQuestions, currentConfigFile);
201 | }
202 |
203 | return currentConfigFile;
204 | } catch (e) {
205 | return await createCLIConfigFile();
206 | }
207 | } catch (error) {
208 | console.error(
209 | chalk.red.bold(
210 | "ERROR: Please make sure that you're running the generate-react-cli commands from the root level of your React project"
211 | )
212 | );
213 | return process.exit(1);
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/docs/assets/generate-react-cli.svg:
--------------------------------------------------------------------------------
1 |
25 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ## [7.1.0](https://github.com/arminbro/generate-react-cli/compare/v7.0.6...v7.1.0) (2022-03-15)
6 |
7 |
8 | ### Features
9 |
10 | * adds dry-run flag to preview generated paths without writing files ([483aef2](https://github.com/arminbro/generate-react-cli/commit/483aef285a356bb02727d3c512d2e03e22b6493a))
11 |
12 | ### [7.0.6](https://github.com/arminbro/generate-react-cli/compare/v7.0.5...v7.0.6) (2022-02-21)
13 |
14 | ### [7.0.5](https://github.com/arminbro/generate-react-cli/compare/v7.0.4...v7.0.5) (2022-01-17)
15 |
16 | ### [7.0.4](https://github.com/arminbro/generate-react-cli/compare/v7.0.3...v7.0.4) (2021-08-07)
17 |
18 |
19 | ### Bug Fixes
20 |
21 | * **story:** use storybook codemod's preferred way ([b694f76](https://github.com/arminbro/generate-react-cli/commit/b694f767d5619b2880f3d8aa143b7e9f2550ff11))
22 |
23 | ### [7.0.3](https://github.com/arminbro/generate-react-cli/compare/v7.0.2...v7.0.3) (2021-06-12)
24 |
25 | ### [7.0.2](https://github.com/arminbro/generate-react-cli/compare/v7.0.1...v7.0.2) (2021-06-12)
26 |
27 | ### [7.0.1](https://github.com/arminbro/generate-react-cli/compare/v7.0.0...v7.0.1) (2021-06-12)
28 |
29 |
30 | ### Bug Fixes
31 |
32 | * remove camelCase from componentTestTemplateGenerator, it's handled in generateComponent ([7885d22](https://github.com/arminbro/generate-react-cli/commit/7885d22b816de7a60e2adcb3d977c1b541db1ae9)), closes [#43](https://github.com/arminbro/generate-react-cli/issues/43)
33 |
34 | ## [7.0.0](https://github.com/arminbro/generate-react-cli/compare/v6.0.2...v7.0.0) (2021-05-06)
35 |
36 |
37 | ### ⚠ BREAKING CHANGES
38 |
39 | * 🧨 Generate React CLI requires Node 12 or higher now. We no longer support
40 | Node 10.
41 |
42 | * 🤖 update dependencies ([331205f](https://github.com/arminbro/generate-react-cli/commit/331205f3afdc06ecccb9458dee873af9477b8e7f))
43 |
44 | ### [6.0.2](https://github.com/arminbro/generate-react-cli/compare/v6.0.1...v6.0.2) (2021-02-24)
45 |
46 | ### [6.0.1](https://github.com/arminbro/generate-react-cli/compare/v6.0.0...v6.0.1) (2021-02-17)
47 |
48 | ## [6.0.0](https://github.com/arminbro/generate-react-cli/compare/v5.2.3...v6.0.0) (2021-02-17)
49 |
50 |
51 | ### ⚠ BREAKING CHANGES
52 |
53 | * You will need to use the "TemplateName" keyword as your custom template filename if
54 | you want the CLI to replace it with the component name.
55 |
56 | ### Features
57 |
58 | * custom component files ([6373a91](https://github.com/arminbro/generate-react-cli/commit/6373a912d725581571c2cdf01cf9062f3965c06f)), closes [#21](https://github.com/arminbro/generate-react-cli/issues/21) [#22](https://github.com/arminbro/generate-react-cli/issues/22) [#27](https://github.com/arminbro/generate-react-cli/issues/27) [#34](https://github.com/arminbro/generate-react-cli/issues/34) [#36](https://github.com/arminbro/generate-react-cli/issues/36) [#37](https://github.com/arminbro/generate-react-cli/issues/37) [#39](https://github.com/arminbro/generate-react-cli/issues/39)
59 |
60 |
61 | ### Bug Fixes
62 |
63 | * use "TemplateName" keyword for custom templates ([73a308f](https://github.com/arminbro/generate-react-cli/commit/73a308fe38e660f57cde102d3a5ded64c5339fc7))
64 | * use lodash upperFirst to force component name start with uppercase ([e401caf](https://github.com/arminbro/generate-react-cli/commit/e401cafa0e35c85db8d4c26dad5f2425f4269980))
65 |
66 | ### [5.2.3](https://github.com/arminbro/generate-react-cli/compare/v5.2.2...v5.2.3) (2021-01-05)
67 |
68 | ### [5.2.2](https://github.com/arminbro/generate-react-cli/compare/v5.2.1...v5.2.2) (2021-01-05)
69 |
70 | ## [5.2.1](https://github.com/arminbro/generate-react-cli/compare/v5.1.0...v5.2.0) (2021-01-05)
71 |
72 | ### Features
73 |
74 | - allow generation of multiple components at once ([18cd5f0](https://github.com/arminbro/generate-react-cli/commit/18cd5f070c3007947011699d7186b8e259e27b05))
75 |
76 | ### Bug Fixes
77 |
78 | - 🐛 react components must start with a upper case letter. ([4c3bddd](https://github.com/arminbro/generate-react-cli/commit/4c3bdddf9e93c10905f28d6b4babe77fdbf10c4f))
79 |
80 | ## [5.1.0](https://github.com/arminbro/generate-react-cli/compare/v5.0.1...v5.1.0) (2020-09-18)
81 |
82 | ### Features
83 |
84 | - 🎸 Support for custom extension via custom templates ([7f989a6](https://github.com/arminbro/generate-react-cli/commit/7f989a61702f8ff0e612845bafed79146e6a01ef)), closes [#18](https://github.com/arminbro/generate-react-cli/issues/18) [#19](https://github.com/arminbro/generate-react-cli/issues/19) [#25](https://github.com/arminbro/generate-react-cli/issues/25)
85 |
86 | ### [5.0.1](https://github.com/arminbro/generate-react-cli/compare/v5.0.0...v5.0.1) (2020-06-24)
87 |
88 | ## [5.0.0](https://github.com/arminbro/generate-react-cli/compare/v4.3.3...v5.0.0) (2020-05-25)
89 |
90 | ### ⚠ BREAKING CHANGES
91 |
92 | - 🧨 This new "type" option will replace the custom component commands that
93 | you run. Meaning you now can pass the custom component as type option
94 | (e.g npx generate-react-cli component HomePage --type=page ) that you
95 | have configured in your GRC config file.
96 |
97 | ### Features
98 |
99 | - 🎸 Add a new "type" option to the component command ([1a5ce6a](https://github.com/arminbro/generate-react-cli/commit/1a5ce6a3c9d8d19937b201ed8fb1bc5ec6c4fae9))
100 |
101 | ### [4.3.3](https://github.com/arminbro/generate-react-cli/compare/v4.3.2...v4.3.3) (2020-05-10)
102 |
103 | ### [4.3.2](https://github.com/arminbro/generate-react-cli/compare/v4.3.1...v4.3.2) (2020-05-10)
104 |
105 | ### [4.3.1](https://github.com/arminbro/generate-react-cli/compare/v4.3.0...v4.3.1) (2020-05-10)
106 |
107 | ## [4.3.0](https://github.com/arminbro/generate-react-cli/compare/v4.2.2...v4.3.0) (2020-05-10)
108 |
109 | ### Features
110 |
111 | - 🎸 Make 'GRC' more configurable (multi component commands) ([59f1622](https://github.com/arminbro/generate-react-cli/commit/59f1622dc6c6ca5a2b42d870b02c265694bc10eb)), closes [#14](https://github.com/arminbro/generate-react-cli/issues/14)
112 |
113 | ### [4.2.2](https://github.com/arminbro/generate-react-cli/compare/v4.2.1...v4.2.2) (2020-05-03)
114 |
115 | ### [4.2.1](https://github.com/arminbro/generate-react-cli/compare/v4.2.0...v4.2.1) (2020-05-03)
116 |
117 | ## [4.2.0](https://github.com/arminbro/generate-react-cli/compare/v4.1.1...v4.2.0) (2020-05-02)
118 |
119 | ### Features
120 |
121 | - 🎸 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)
122 |
123 | ### [4.1.1](https://github.com/arminbro/generate-react-cli/compare/v4.1.0...v4.1.1) (2020-04-23)
124 |
125 | ## [4.1.0](https://github.com/arminbro/generate-react-cli/compare/v4.0.2...v4.1.0) (2020-04-20)
126 |
127 | ### Features
128 |
129 | - 🎸 add new page command ([3a441de](https://github.com/arminbro/generate-react-cli/commit/3a441dede662bf6a3d65c67072b50900ece46879)), closes [#10](https://github.com/arminbro/generate-react-cli/issues/10)
130 |
131 | ### [4.0.2](https://github.com/arminbro/generate-react-cli/compare/v4.0.1...v4.0.2) (2020-04-05)
132 |
133 | ### Bug Fixes
134 |
135 | - 🐛 audit fix to resolve 1 low vulnerability ([0ac348e](https://github.com/arminbro/generate-react-cli/commit/0ac348ef6f6da6ecc4a72be153e22965894d796b))
136 |
137 | ### [4.0.1](https://github.com/arminbro/generate-react-cli/compare/v4.0.0...v4.0.1) (2020-04-05)
138 |
139 | ## [4.0.0](https://github.com/arminbro/generate-react-cli/compare/v3.0.2...v4.0.0) (2020-03-21)
140 |
141 | ### ⚠ BREAKING CHANGES
142 |
143 | - 🧨 Generate React CLI requires Node 10 or higher
144 |
145 | - 🤖 Generate React CLI requires Node 10 or higher ([bd745f6](https://github.com/arminbro/generate-react-cli/commit/bd745f659d0e538e7abfb875cd1e160c5c6b064c))
146 |
147 | ### [3.0.2](https://github.com/arminbro/generate-react-cli/compare/v3.0.1...v3.0.2) (2020-03-21)
148 |
149 | ### [3.0.1](https://github.com/arminbro/generate-react-cli/compare/v3.0.0...v3.0.1) (2020-03-14)
150 |
151 | ## [3.0.0](https://github.com/arminbro/generate-react-cli/compare/v2.0.2...v3.0.0) (2019-12-14)
152 |
153 | ### ⚠ BREAKING CHANGES
154 |
155 | - Update the way option values are passed in the component command. For
156 | example if you wanted or didn't want a corresponding test file, the old
157 | syntax looked like this: --withTest or --no-withTest. Now with the new
158 | syntax you just do this --withTest=true or --withTest=false this applies
159 | to all the other component options (withStyle, withStory, withLazy).
160 |
161 | - 💄 Update component command options ([c870c7c](https://github.com/arminbro/generate-react-cli/commit/c870c7c5544640e23848f4f22b883e2d0ee755e4))
162 |
163 | ### [2.0.2](https://github.com/arminbro/generate-react-cli/compare/v2.0.1...v2.0.2) (2019-12-13)
164 |
165 | ### [2.0.1](https://github.com/arminbro/generate-react-cli/compare/v2.0.0...v2.0.1) (2019-12-13)
166 |
167 | ## [2.0.0](https://github.com/arminbro/generate-react-cli/compare/v1.8.0...v2.0.0) (2019-12-13)
168 |
169 | ### ⚠ BREAKING CHANGES
170 |
171 | - new command option parameters
172 |
173 | ### Features
174 |
175 | - add TypeScript support ([8d13018](https://github.com/arminbro/generate-react-cli/commit/8d13018fa22042b9ac058cc4b332583d4d8abf80))
176 |
177 | - make stylesheets optional by adding “withStyle” option
178 |
179 | - improve developer experience when updating (“generate-react-cli.json”) the config file. The CLI will only inquire about the new missing properties in the config file the next time generate-react-cli is ran.
180 |
181 | - 🎸 make sure user is running Node 8 or higher ([fe5dba1](https://github.com/arminbro/generate-react-cli/commit/fe5dba19e68cb8914db4ee4fc1f93fbdd808e355))
182 |
183 | * 💡 component command has a few option updates ([67579d3](https://github.com/arminbro/generate-react-cli/commit/67579d3724af1108932670b87dc7084f9b22cbe8))
184 |
185 | ## 1.8.0 (2019-12-12)
186 |
187 | - testing standard-version
188 |
189 | ## 1.7.5 (2019-11-24)
190 |
191 | ### Chores
192 |
193 | - major dependency update (chalk 3.0.0)
194 |
195 | ## 1.7.4 (2019-11-24)
196 |
197 | ### Chores
198 |
199 | - update dependencies
200 |
201 | ## 1.7.3 (2019-11-07)
202 |
203 | ### Chores
204 |
205 | - update readme
206 |
207 | ## 1.7.2 (2019-11-06)
208 |
209 | ### Bug Fixes
210 |
211 | - remove unnecessary use of Fragment in Lazy template
212 |
213 | ## 1.7.1 (2019-11-06)
214 |
215 | ### Chores
216 |
217 | - update dependencies
218 |
219 | ### Bug Fixes
220 |
221 | - remove data-testid from jsTemplate if test library is not Testing Library
222 | - only import style object in jsTemplate if css module is true
223 |
224 | ## 1.7.0 (2019-10-17)
225 |
226 | ### Features
227 |
228 | - (#4) make getByTestId the default
229 |
230 | ### Bug Fixes
231 |
232 | - fix (#3) generated tests always use FollowBtn
233 |
234 | ## 1.6.2 (2019-10-02)
235 |
236 | ### Chores
237 |
238 | - update readme
239 |
240 | ## 1.6.1 (2019-10-02)
241 |
242 | ### Features
243 |
244 | - support different testing component libraries
245 |
246 | ### Chores
247 |
248 | - update dependencies
249 | - update readme
250 |
251 | ## 1.6.0 (2019-09-30)
252 |
253 | ### Chores
254 |
255 | - update dependencies
256 |
257 | ## 1.5.9 (2019-09-29)
258 |
259 | ### Chores
260 |
261 | - reorganize the file structure within the gr-cli
262 |
263 | ## 1.5.8 (2019-08-23)
264 |
265 | ### Chores
266 |
267 | - bump major version of inquirer
268 |
269 | ## 1.5.7 (2019-08-23)
270 |
271 | ### Chores
272 |
273 | - update dependencies
274 |
275 | ## 1.5.6 (2019-08-17)
276 |
277 | ### Chores
278 |
279 | - reorganize
280 |
281 | ## 1.5.5 (2019-08-17)
282 |
283 | ### Chores
284 |
285 | - update readme
286 |
287 | ## 1.5.4 (2019-08-17)
288 |
289 | ### Features
290 |
291 | - add shorthand "g-r" command
292 |
293 | ## 1.5.3 (2019-08-13)
294 |
295 | ### Chores
296 |
297 | - update issue templates for github
298 | - add component cmd gif for readme.md
299 | - update readme
300 |
301 | ## 1.5.2 (2019-08-12)
302 |
303 | ### Chores
304 |
305 | - update readme
306 |
307 | ## 1.5.1 (2019-8-12)
308 |
309 | ### Chores
310 |
311 | - update readme
312 |
313 | ## 1.5.0 (2019-08-12)
314 |
315 | ### Features
316 |
317 | - update GRC config file when needed.
318 | - add lazy template
319 | - add additional options to "component" command (withTest, withStory, withLazy)
320 |
321 | ### Chores
322 |
323 | - update readme
324 |
325 | ## 1.4.1 (2019-08-07)
326 |
327 | ### Bug Fixes
328 |
329 | - use correct preprocessor extension in component
330 |
331 | ## 1.4.0 (2019-08-07)
332 |
333 | ### Features
334 |
335 | - add question inquirer to create generate-react-cli config file
336 |
337 | ### Chores
338 |
339 | - create License
340 |
341 | ## 1.3.3 (2019-08-04)
342 |
343 | ### Chores
344 |
345 | - update readme
346 |
347 | ## 1.3.2 (2019-08-04)
348 |
349 | ### Chores
350 |
351 | - update package description
352 |
353 | ## 1.3.1 (2019-08-04)
354 |
355 | ### Chores
356 |
357 | - update readme
358 |
359 | ## 1.3.0 (2019-08-04)
360 |
361 | ### Chores
362 |
363 | - add templates to files in package.json
364 |
365 | ## 1.2.0 (2019-08-04)
366 |
367 | ### Chores
368 |
369 | - add repository to package.json
370 |
371 | ## 1.1.0 (2019-08-04)
372 |
373 | ### Features
374 |
375 | - initial base features of generate react cli
376 |
377 | ### Chores
378 |
379 | - add readme
380 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Generate React CLI
2 |
3 | [](https://github.com/arminbro/generate-react-cli/blob/master/LICENSE)
4 |
5 |
6 |
7 |
8 |
9 | ## Why?
10 |
11 | To help speed up productivity in React projects and stop copying, pasting, and renaming files each time you want to create a new component.
12 |
13 | A short [article](https://dev.to/arminbro/generate-react-cli-1ooh) goes deeper into why we created GRC if you have the time.
14 |
15 | You can also watch an excellent [video](https://www.youtube.com/watch?v=NEvnt3MWttY) tutorial on how to use GRC by [Eric Murphy](https://www.youtube.com/channel/UC5KDiSAFxrDWhmysBcNqtMA).
16 |
17 | ## Table of Contents:
18 |
19 | - [Config file](#config-file)
20 | - [Generate components](#generate-components)
21 | - [Custom component types](#custom-component-types)
22 | - [Custom component templates](#custom-component-templates)
23 | - [Custom component directory](#custom-component-directory)
24 | - [Custom component files](#custom-component-files)
25 |
26 | ## You can run it using npx like this:
27 |
28 | ```
29 | npx generate-react-cli component Box
30 | ```
31 |
32 | _([npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) is a package runner tool that comes with npm 5.2+)_
33 |
34 | ## Config File
35 |
36 | 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).
37 |
38 | #### Example of the **generate-react-cli.json** config file:
39 |
40 | ```json
41 | {
42 | "usesTypeScript": true,
43 | "usesCssModule": true,
44 | "cssPreprocessor": "scss",
45 | "testLibrary": "Testing Library",
46 | "component": {
47 | "default": {
48 | "path": "src/components",
49 | "withLazy": false,
50 | "withStory": false,
51 | "withStyle": true,
52 | "withTest": true
53 | }
54 | }
55 | }
56 | ```
57 |
58 | ## Generate Components
59 |
60 | ```sh
61 | npx generate-react-cli component Box
62 | ```
63 |
64 | This command will create a folder with your component name within your default (e.g. **src/components**) directory, and its corresponding files.
65 |
66 | #### Example of the component files structure:
67 |
68 | ```
69 | |-- /src
70 | |-- /components
71 | |-- /Box
72 | |-- Box.js
73 | |-- Box.css
74 | |-- Box.test.js
75 | ```
76 |
77 | ### Options
78 |
79 | 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:
80 |
81 | ```sh
82 | npx generate-react-cli component Box --withTest=false
83 | ```
84 |
85 | Or vice versa, if you have set **withTest** to be `false` you can do this:
86 |
87 | ```sh
88 | npx generate-react-cli component Box --withTest=true
89 | ```
90 |
91 | 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`.
92 |
93 |
94 |
95 |
Options
96 |
Description
97 |
Value Type
98 |
Default Value
99 |
100 |
101 |
102 |
--path
103 |
104 | Value of the path where you want the component to be generated in (e.g. src/components).
105 |
106 |
String
107 |
component.default.path
108 |
109 |
110 |
111 |
--type
112 |
113 | 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 custom component types.
114 |
115 |
String
116 |
component.default
117 |
118 |
119 |
120 |
--withLazy
121 |
122 | Creates a corresponding lazy file (a file that lazy-loads your component out of the box and enables code splitting) with this component.
123 |
124 |
Boolean
125 |
component.default.withLazy
126 |
127 |
128 |
129 |
--withStory
130 |
131 | Creates a corresponding (storybook) story file with this component.
132 |
133 |
Boolean
134 |
component.default.withStory
135 |
136 |
137 |
138 |
--withStyle
139 |
140 | Creates a corresponding stylesheet file with this component.
141 |
142 |
Boolean
143 |
component.default.withStyle
144 |
145 |
146 |
147 |
--withTest
148 |
149 | Creates a corresponding test file with this component.
150 |
151 |
Boolean
152 |
component.default.withTest
153 |
154 |
155 |
156 |
--dry-run
157 |
158 | Show what will be generated without writing to disk
159 |
160 |
Boolean
161 |
false
162 |
163 |
164 |
165 |
--flat
166 |
167 | Generate the files in the mentioned path instead of creating new folder for it
168 |
169 |
Boolean
170 |
false
171 |
172 |
173 |
174 |
--customDirectory
175 |
176 | Template value that overrides the name of the directory of the component to be generated in.
177 | See more under custom component directory.
178 |
179 |
String
180 |
null
181 |
182 |
183 |
184 | ### Custom component types
185 |
186 | By default, GRC will use the `component.default` configuration rules when running the component command out of the box.
187 |
188 | What if you wanted to generate other types of components that have their own set of config rules (e.g., **page** or **layout**)?
189 |
190 | You can do so by extending the **generate-react-cli.json** config file like this.
191 |
192 | ```json
193 | {
194 | "usesTypeScript": false,
195 | "usesCssModule": true,
196 | "cssPreprocessor": "scss",
197 | "testLibrary": "Testing Library",
198 | "component": {
199 | "default": {
200 | "path": "src/components",
201 | "withLazy": false,
202 | "withStory": false,
203 | "withStyle": true,
204 | "withTest": true
205 | },
206 | "page": {
207 | "path": "src/pages",
208 | "withLazy": true,
209 | "withStory": false,
210 | "withStyle": true,
211 | "withTest": true
212 | },
213 | "layout": {
214 | "path": "src/layout",
215 | "withLazy": false,
216 | "withStory": false,
217 | "withStyle": false,
218 | "withTest": true
219 | }
220 | }
221 | }
222 | ```
223 |
224 | Now you can generate a component with your custom component types like this:
225 |
226 | ```sh
227 | npx generate-react-cli component HomePage --type=page
228 | ```
229 |
230 | ```sh
231 | npx generate-react-cli component BoxLayout --type=layout
232 | ```
233 |
234 | You can also pass the same [options](#options) to your custom component types as you would for the default component type.
235 |
236 | ### Custom component templates
237 |
238 | 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.
239 |
240 | 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.
241 |
242 | #### Example of the `customTemplates` object:
243 |
244 | ```json
245 | "customTemplates": {
246 | "component": "templates/TemplateName.js",
247 | "lazy": "templates/TemplateName.lazy.js",
248 | "story": "templates/TemplateName.story.js",
249 | "style": "templates/TemplateName.style.scss",
250 | "test": "templates/TemplateName.test.js"
251 | },
252 | ```
253 |
254 | The keys represent the type of file, and the values are the paths that point to where your custom template lives in your project/system. Please note the `TemplateName` keyword in the template filename. GRC will use this keyword and replace it with your component name (in whichever format you typed the component name in the command) as the filename.
255 |
256 | #### Example of using the `customTemplates` object within your generate-react-cli.json config file:
257 |
258 | ```json
259 | {
260 | "usesTypeScript": false,
261 | "usesCssModule": true,
262 | "cssPreprocessor": "scss",
263 | "testLibrary": "Testing Library",
264 | "component": {
265 | "default": {
266 | "customTemplates": {
267 | "component": "templates/component/TemplateName.js",
268 | "style": "templates/component/TemplateName.style.scss",
269 | "test": "templates/component/TemplateName.test.js"
270 | },
271 | "path": "src/components",
272 | "withStyle": true,
273 | "withTest": true,
274 | "withStory": true,
275 | "withLazy": false
276 | },
277 | "page": {
278 | "customTemplates": {
279 | "test": "templates/page/TemplateName.test.js"
280 | },
281 | "path": "src/pages",
282 | "withLazy": true,
283 | "withStory": false,
284 | "withStyle": true,
285 | "withTest": true
286 | }
287 | }
288 | }
289 | ```
290 |
291 | 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.
292 |
293 | #### Example of a custom component template file:
294 |
295 | ```jsx
296 | // templates/component/TemplateName.js
297 |
298 | import React from 'react';
299 | import styles from './TemplateName.module.css';
300 |
301 | const TemplateName = () => (
302 |
303 |
TemplateName component
304 |
305 | );
306 |
307 | export default TemplateName;
308 | ```
309 |
310 | **Important:** You can use the following keywords within your custom templates to format the component name. Note that the built-in GRC templates use `templatename` casing by default:
311 |
312 | | Keyword | Replacement |
313 | | --------------- | ---------------------------------------------------------------------------------------------- |
314 | | `templatename` | component name in raw case (whichever format the user typed the component name in the command) |
315 | | `TemplateName` | component name in PascalCase |
316 | | `templateName` | component name in camelCase |
317 | | `template-name` | component name in kebab-case |
318 | | `template_name` | component name in snake_case |
319 | | `TEMPLATE_NAME` | component name in uppercase SNAKE_CASE |
320 | | `TEMPLATENAME` | component name in full UPPERCASE |
321 |
322 | #### Example of a custom test template file:
323 |
324 | ```jsx
325 | // templates/component/TemplateName.test.js
326 |
327 | import React from 'react';
328 | import ReactDOM from 'react-dom';
329 | import TemplateName from './TemplateName';
330 |
331 | it('It should mount', () => {
332 | const div = document.createElement('div');
333 | ReactDOM.render(, div);
334 | ReactDOM.unmountComponentAtNode(div);
335 | });
336 | ```
337 |
338 | ### Custom component directory
339 |
340 | Using the `customDirectory` you can easily override the directory name for the component generated. For instance, if prefixes are required for particular components or if template names will be mixed, the `customDirectory` option will allow you to override the way that GRC generates the name of the directory where the component files will live.
341 |
342 | The `customDirectory` directive allows all supported casings (see previous section) and can be overridden at the following levels in ascending specific of priority:
343 |
344 | - top
345 | - component.default
346 | - component._type_
347 | - CLI
348 |
349 | #### Example:
350 |
351 | For React Context Providers in a project, the decision has been made to separate Context generation from the visual components.
352 |
353 | In a typical configuration the configuration would look as following:
354 |
355 | ```json
356 | {
357 | "provider": {
358 | "path": "src/components/providers",
359 | "withLazy": false,
360 | "withStory": true,
361 | "withStyle": false,
362 | "withTest": true,
363 | "withTypes": true,
364 | "withContext": true,
365 | "customTemplates": {
366 | "component": "src/components/templates/provider/TemplateName.tsx",
367 | "context": "src/components/templates/provider/TemplateName.context.ts",
368 | "story": "src/components/templates/provider/TemplateName.stories.tsx",
369 | "test": "src/components/templates/provider/TemplateName.test.tsx",
370 | "types": "src/components/templates/provider/TemplateName.types.ts"
371 | }
372 | }
373 | }
374 | ```
375 |
376 | With the configuration above, the component would be required to either follow a full or a minimalistic naming convention.
377 | I.e. the component would either need to be generated as `ThemeProvider` and consequently the context name would be generated as `ThemeProviderContext`, or by renaming the files and templates as `TemplateNameProvider` but with the downside of the component path being generated as `src/components/providers/Theme`. This creates inconsistent naming in the directory containg the component files.
378 |
379 | To work around this, the `customDirectory` option can be used to enforce a particular style.
380 |
381 | ```json
382 | {
383 | ...
384 | "provider": {
385 | "path": "src/components/providers",
386 | "withLazy": false,
387 | "withStory": true,
388 | "withStyle": false,
389 | "withTest": true,
390 | "withTypes": true,
391 | "withContext": true,
392 | "customDirectory": "TemplateNameProvider",
393 | "customTemplates": {
394 | "component": "src/components/templates/provider/TemplateNameProvider.tsx",
395 | "context": "src/components/templates/provider/TemplateName.context.ts",
396 | "story": "src/components/templates/provider/TemplateNameProvider.stories.tsx",
397 | "test": "src/components/templates/provider/TemplateNameProvider.test.tsx",
398 | "types": "src/components/templates/provider/TemplateNameProvider.types.ts"
399 | }
400 | }
401 | ...
402 | }
403 | ```
404 |
405 | The above configuration would allow you to mix and match different template names and keep naming consistent.
406 |
407 | If we executed GRC with the above configuration (`npx generate-react-cli component Theme --type=provider`), the result would look like this:
408 |
409 | ```fs
410 | src/components/providers/ThemeProvider/Theme.context.ts
411 | src/components/providers/ThemeProvider/ThemeProvider.tsx
412 | src/components/providers/ThemeProvider/ThemeProvider.stories.tsx
413 | src/components/providers/ThemeProvider/ThemeProvider.test.tsx
414 | src/components/providers/ThemeProvider/ThemeProvider.types.ts
415 | ```
416 |
417 | Similarly, this construct could be used as a shortcut for generating other named components, like the `BoxLayout` example above, depending on that could be shortened to:
418 |
419 | ```sh
420 | npx generate-react-cli component Box --type=layout --customDir=TemplateNameLayout
421 | ```
422 |
423 | Or it could be used to generate files with a naming convention with `Test`, `Lazy`, `Context`, `Theme`, or `Provider` suffixes. Or even combined with skeleton CSS
424 |
425 | ### Custom component files
426 |
427 | GRC comes with corresponding built-in files for a given component if you need them (i.e., `withStyle`, `withTest`, `withStory`, and `withLazy`).
428 |
429 | What if you wanted to add custom files of your own?
430 |
431 | For example, let's say you wanted to add an `index.js` file for each component, so you don't have to add the additional component name with each import (i.e., `import Box from './components/Box'` instead of `import Box from './components/Box/Box'`).
432 |
433 | Or maybe you need an additional style file for your component stories.
434 |
435 | You can do so by editing your **generate-react-cli.json** config file like so.
436 |
437 | ```json
438 | {
439 | "usesTypeScript": false,
440 | "usesCssModule": false,
441 | "cssPreprocessor": "css",
442 | "testLibrary": "Testing Library",
443 | "component": {
444 | "default": {
445 | "path": "src/components",
446 | "withStyle": true,
447 | "withTest": true,
448 | "withStory": true,
449 | "withLazy": false,
450 | "withIndex": true,
451 | "withStoryStyle": true,
452 | "customTemplates": {
453 | "index": "templates/default/index.js",
454 | "storyStyle": "templates/default/TemplateName.stories.css"
455 | }
456 | }
457 | }
458 | }
459 | ```
460 |
461 | ```jsx
462 | // templates/default/index.js
463 |
464 | export { default } from './TemplateName';
465 | ```
466 |
467 | ```css
468 | /* templates/default/TemplateName.stories.css */
469 |
470 | .TemplateName {
471 | }
472 | ```
473 |
474 | In this case, we added a `withIndex` & `withStoryStyle` to the `component.default`. Note: You can add custom files to any of your custom component types.
475 |
476 | You should also see that we added `index` and `storyStyle` to our `customTemplates` object. That's because custom files require custom templates. Otherwise, you will get an error when you generate a component.
477 |
478 | Also, we used the `TemplateName` keyword for the `storyStyle` custom file. GRC will generate this corresponding file and replace `TemplateName` with the component name.
479 |
480 | ## License
481 |
482 | Generate React CLI is an open source software [licensed as MIT](https://github.com/arminbro/generate-react-cli/blob/master/LICENSE).
483 |
--------------------------------------------------------------------------------
/src/utils/generateComponentUtils.js:
--------------------------------------------------------------------------------
1 | import chalk from 'chalk';
2 | import path from 'path';
3 | import replace from 'replace';
4 | import camelCase from 'lodash/camelCase.js';
5 | import kebabCase from 'lodash/kebabCase.js';
6 | import snakeCase from 'lodash/snakeCase.js';
7 | import startCase from 'lodash/startCase.js';
8 | import fsExtra from 'fs-extra';
9 |
10 | import componentJsTemplate from '../templates/component/componentJsTemplate.js';
11 | import componentTsTemplate from '../templates/component/componentTsTemplate.js';
12 | import componentCssTemplate from '../templates/component/componentCssTemplate.js';
13 | import componentStyledTemplate from '../templates/component/componentStyledTemplate.js';
14 | import componentLazyTemplate from '../templates/component/componentLazyTemplate.js';
15 | import componentTsLazyTemplate from '../templates/component/componentTsLazyTemplate.js';
16 | import componentStoryTemplate from '../templates/component/componentStoryTemplate.js';
17 | import componentTestEnzymeTemplate from '../templates/component/componentTestEnzymeTemplate.js';
18 | import componentTestDefaultTemplate from '../templates/component/componentTestDefaultTemplate.js';
19 | import componentTestTestingLibraryTemplate from '../templates/component/componentTestTestingLibraryTemplate.js';
20 |
21 | const templateNameRE = /.*(template[|_-]?name).*/i;
22 |
23 | const { existsSync, outputFileSync, readFileSync } = fsExtra;
24 |
25 | export function getComponentByType(args, cliConfigFile) {
26 | const hasComponentTypeOption = args.find((arg) => arg.includes('--type'));
27 |
28 | // Check for component type option.
29 |
30 | if (hasComponentTypeOption) {
31 | const componentType = hasComponentTypeOption.split('=')[1]; // get the component type value
32 | const selectedComponentType = cliConfigFile.component[componentType];
33 |
34 | // If the selected component type does not exists in the cliConfigFile under `component` throw an error
35 |
36 | if (!selectedComponentType) {
37 | console.error(
38 | chalk.red(
39 | `
40 | ERROR: Please make sure the component type you're trying to use exists in the
41 | ${chalk.bold('generate-react-cli.json')} config file under the ${chalk.bold('component')} object.
42 | `
43 | )
44 | );
45 |
46 | process.exit(1);
47 | }
48 |
49 | // Otherwise return it.
50 |
51 | return selectedComponentType;
52 | }
53 |
54 | // Otherwise return the default component type.
55 |
56 | return cliConfigFile.component.default;
57 | }
58 |
59 | export function getCorrespondingComponentFileTypes(component) {
60 | return Object.keys(component).filter((key) => key.split('with').length > 1);
61 | }
62 |
63 | function getCustomTemplate(componentName, templatePath) {
64 | // --- Try loading custom template
65 |
66 | try {
67 | const template = readFileSync(templatePath, 'utf8');
68 | const filename = path.basename(templatePath).replace(/template[_-]?name/i, componentName);
69 |
70 | return { template, filename };
71 | } catch (e) {
72 | console.error(
73 | chalk.red(
74 | `
75 | ERROR: The custom template path of "${templatePath}" does not exist.
76 | Please make sure you're pointing to the right custom template path in your generate-react-cli.json config file.
77 | `
78 | )
79 | );
80 |
81 | return process.exit(1);
82 | }
83 | }
84 |
85 | function componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, filename, convertors }) {
86 | let componentPath = cmd.path;
87 |
88 | if (cmd.flat !== true) {
89 | let componentDirectory = componentName;
90 |
91 | const customDirectoryConfigs = [
92 | cliConfigFile.customDirectory,
93 | cliConfigFile.component.default.customDirectory,
94 | cliConfigFile.component[cmd.type].customDirectory,
95 | cmd.customDirectory,
96 | ].filter((e) => Boolean(e) && typeof e === 'string');
97 |
98 | if (customDirectoryConfigs.length > 0) {
99 | const customDirectory = customDirectoryConfigs.slice(-1).toString();
100 |
101 | // Double check if the customDirectory is templatable
102 | if (templateNameRE.exec(customDirectory) == null) {
103 | console.error(
104 | chalk.red(
105 | `customDirectory [${customDirectory}] for ${componentName} does not contain a templatable value.\nPlease check your configuration!`
106 | )
107 | );
108 |
109 | process.exit(-2);
110 | }
111 |
112 | for (const convertor in convertors) {
113 | const re = new RegExp(`.*${convertor}.*`);
114 |
115 | if (re.exec(customDirectory) !== null) {
116 | componentDirectory = customDirectory.replace(convertor, convertors[convertor]);
117 | }
118 | }
119 | }
120 |
121 | componentPath += `/${componentDirectory}`;
122 | }
123 |
124 | componentPath += `/${filename}`;
125 |
126 | return componentPath;
127 | }
128 |
129 | function componentTemplateGenerator({ cmd, componentName, cliConfigFile, convertors }) {
130 | // @ts-ignore
131 | const { usesStyledComponents, cssPreprocessor, testLibrary, usesCssModule, usesTypeScript } = cliConfigFile;
132 | const { customTemplates } = cliConfigFile.component[cmd.type];
133 | let template = null;
134 | let filename = null;
135 |
136 | // Check for a custom component template.
137 |
138 | if (customTemplates && customTemplates.component) {
139 | // --- Load and use the custom component template
140 |
141 | const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
142 | componentName,
143 | customTemplates.component
144 | );
145 |
146 | template = customTemplate;
147 | filename = customTemplateFilename;
148 | } else {
149 | // --- Else use GRC built-in component template
150 |
151 | template = usesTypeScript ? componentTsTemplate : componentJsTemplate;
152 | filename = usesTypeScript ? `${componentName}.tsx` : `${componentName}.js`;
153 |
154 | // --- If test library is not Testing Library or if withTest is false. Remove data-testid from template
155 |
156 | if (testLibrary !== 'Testing Library' || !cmd.withTest) {
157 | template = template.replace(` data-testid="templatename"`, '');
158 | }
159 |
160 | // --- If it has a corresponding stylesheet
161 |
162 | if (cmd.withStyle) {
163 | if (cliConfigFile.usesStyledComponents) {
164 | const cssPath = `${componentName}.styled`;
165 | template = template.replace(
166 | `import styles from './templatename.module.css'`,
167 | `import { templatenameWrapper } from './${cssPath}'`
168 | );
169 | template = template.replace(` className={styles.templatename}`, '');
170 | template = template.replace(`