generate-react-cli 10.1.0 → 11.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/generate-react.js +0 -1
- package/package.json +4 -4
- package/readme.md +115 -314
- package/src/templates/component/componentJsTemplate.js +1 -7
- package/src/templates/component/componentLazyTemplate.js +2 -2
- package/src/templates/component/componentStoryTemplate.js +4 -8
- package/src/templates/component/componentTestDefaultTemplate.js +6 -6
- package/src/templates/component/componentTestTestingLibraryTemplate.js +2 -3
- package/src/templates/component/componentTestVitestTemplate.js +15 -0
- package/src/templates/component/componentTsLazyTemplate.js +2 -2
- package/src/templates/component/componentTsTemplate.js +1 -1
- package/src/utils/generateComponentUtils.js +36 -103
- package/src/utils/grcConfigUtils.js +8 -9
- package/src/utils/messagesUtils.js +15 -4
- package/src/templates/component/componentTestEnzymeTemplate.js +0 -16
package/bin/generate-react.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "generate-react-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.0.1",
|
|
4
4
|
"description": "A simple React CLI to generate components instantly and more.",
|
|
5
5
|
"author": "Armin Broubakarian",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
"chalk": "5.6.2",
|
|
44
44
|
"commander": "14.0.2",
|
|
45
45
|
"fs-extra": "11.3.3",
|
|
46
|
-
"inquirer": "13.2.
|
|
47
|
-
"lodash": "4.17.
|
|
46
|
+
"inquirer": "13.2.2",
|
|
47
|
+
"lodash": "4.17.23",
|
|
48
48
|
"replace": "1.2.2"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@antfu/eslint-config": "7.0
|
|
51
|
+
"@antfu/eslint-config": "7.2.0",
|
|
52
52
|
"@commitlint/cli": "20.3.1",
|
|
53
53
|
"@commitlint/config-conventional": "20.3.1",
|
|
54
54
|
"@semantic-release/commit-analyzer": "13.0.1",
|
package/readme.md
CHANGED
|
@@ -6,36 +6,41 @@
|
|
|
6
6
|
<img src="https://raw.githubusercontent.com/arminbro/generate-react-cli/master/docs/assets/generate-react-cli.svg?raw=true"/>
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
21
|
-
- [Custom
|
|
22
|
-
- [Custom
|
|
23
|
-
- [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
9
|
+
A CLI tool to speed up productivity in React projects by generating components instantly with configurable templates.
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Quick Start](#quick-start)
|
|
14
|
+
- [Requirements](#requirements)
|
|
15
|
+
- [Config File](#config-file)
|
|
16
|
+
- [Generate Components](#generate-components)
|
|
17
|
+
- [Options](#options)
|
|
18
|
+
- [Custom Component Types](#custom-component-types)
|
|
19
|
+
- [Custom Component Templates](#custom-component-templates)
|
|
20
|
+
- [Template Keywords](#template-keywords)
|
|
21
|
+
- [Custom Component Files](#custom-component-files)
|
|
22
|
+
- [Advanced: Custom Directory](#advanced-custom-directory)
|
|
23
|
+
- [License](#license)
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Generate your first component (creates config on first run)
|
|
29
|
+
npx generate-react-cli component Box
|
|
30
|
+
|
|
31
|
+
# Or install globally
|
|
32
|
+
npm i -g generate-react-cli
|
|
33
|
+
generate-react component Button
|
|
30
34
|
```
|
|
31
35
|
|
|
32
|
-
|
|
36
|
+
## Requirements
|
|
33
37
|
|
|
34
|
-
|
|
38
|
+
- Node.js 22 or higher
|
|
39
|
+
- npm 10 or higher
|
|
35
40
|
|
|
36
|
-
|
|
41
|
+
## Config File
|
|
37
42
|
|
|
38
|
-
|
|
43
|
+
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).
|
|
39
44
|
|
|
40
45
|
```json
|
|
41
46
|
{
|
|
@@ -55,15 +60,21 @@ When you run GRC within your project the first time, it will ask you a series of
|
|
|
55
60
|
}
|
|
56
61
|
```
|
|
57
62
|
|
|
63
|
+
**Test library options:**
|
|
64
|
+
|
|
65
|
+
| Option | Description |
|
|
66
|
+
|--------|-------------|
|
|
67
|
+
| `Testing Library` | Uses [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) |
|
|
68
|
+
| `Vitest` | Uses [Vitest](https://vitest.dev/) with React Testing Library |
|
|
69
|
+
| `None` | Basic tests using React's createRoot API |
|
|
70
|
+
|
|
58
71
|
## Generate Components
|
|
59
72
|
|
|
60
73
|
```sh
|
|
61
|
-
|
|
74
|
+
npx generate-react-cli component Box
|
|
62
75
|
```
|
|
63
76
|
|
|
64
|
-
This command will create a folder with your component name within your default (e.g
|
|
65
|
-
|
|
66
|
-
#### Example of the component files structure:
|
|
77
|
+
This command will create a folder with your component name within your default (e.g., `src/components`) directory, and its corresponding files.
|
|
67
78
|
|
|
68
79
|
```
|
|
69
80
|
|-- /src
|
|
@@ -74,120 +85,29 @@ This command will create a folder with your component name within your default (
|
|
|
74
85
|
|-- Box.test.js
|
|
75
86
|
```
|
|
76
87
|
|
|
77
|
-
|
|
88
|
+
## Options
|
|
78
89
|
|
|
79
|
-
You can
|
|
90
|
+
You can override config rules using command-line options:
|
|
80
91
|
|
|
81
92
|
```sh
|
|
82
|
-
|
|
93
|
+
npx generate-react-cli component Box --withTest=false
|
|
83
94
|
```
|
|
84
95
|
|
|
85
|
-
|
|
96
|
+
| Option | Description | Default |
|
|
97
|
+
|--------|-------------|---------|
|
|
98
|
+
| `--path` | Output directory for the component | Config value |
|
|
99
|
+
| `--type` | [Custom component type](#custom-component-types) to use | `default` |
|
|
100
|
+
| `--withLazy` | Generate a [lazy-loading](https://react.dev/reference/react/lazy) wrapper file | Config value |
|
|
101
|
+
| `--withStory` | Generate a [Storybook](https://storybook.js.org) story file | Config value |
|
|
102
|
+
| `--withStyle` | Generate a stylesheet file | Config value |
|
|
103
|
+
| `--withTest` | Generate a test file | Config value |
|
|
104
|
+
| `--dry-run` | Preview what will be generated without writing files | `false` |
|
|
105
|
+
| `--flat` | Generate files directly in path without creating a folder | `false` |
|
|
106
|
+
| `--customDirectory` | Override the component's folder name ([see below](#advanced-custom-directory)) | Component name |
|
|
86
107
|
|
|
87
|
-
|
|
88
|
-
npx generate-react-cli component Box --withTest=true
|
|
89
|
-
```
|
|
108
|
+
## Custom Component Types
|
|
90
109
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
<table>
|
|
94
|
-
<tr align="left">
|
|
95
|
-
<th>Options</th>
|
|
96
|
-
<th>Description</th>
|
|
97
|
-
<th>Value Type</th>
|
|
98
|
-
<th>Default Value</th>
|
|
99
|
-
</tr>
|
|
100
|
-
|
|
101
|
-
<tr>
|
|
102
|
-
<td width="20%"><b>--path</b></td>
|
|
103
|
-
<td width="60%">
|
|
104
|
-
Value of the path where you want the component to be generated in (e.g. <b>src/components</b>).
|
|
105
|
-
</td>
|
|
106
|
-
<td width="20%">String</td>
|
|
107
|
-
<td width="20%"><code>component.default.path<code></td>
|
|
108
|
-
</tr>
|
|
109
|
-
|
|
110
|
-
<tr>
|
|
111
|
-
<td width="20%"><b>--type</b></td>
|
|
112
|
-
<td width="60%">
|
|
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 <a href="#custom-component-types">custom component types</a>.
|
|
114
|
-
</td>
|
|
115
|
-
<td width="20%">String</td>
|
|
116
|
-
<td width="20%"><code>component.default<code></td>
|
|
117
|
-
</tr>
|
|
118
|
-
|
|
119
|
-
<tr>
|
|
120
|
-
<td width="20%"><b>--withLazy</b></td>
|
|
121
|
-
<td width="60%">
|
|
122
|
-
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.
|
|
123
|
-
</td>
|
|
124
|
-
<td width="20%">Boolean</td>
|
|
125
|
-
<td width="20%"><code>component.default.withLazy<code></td>
|
|
126
|
-
</tr>
|
|
127
|
-
|
|
128
|
-
<tr>
|
|
129
|
-
<td width="20%"><b>--withStory</b></td>
|
|
130
|
-
<td width="60%">
|
|
131
|
-
Creates a corresponding (<a href="https://storybook.js.org">storybook</a>) story file with this component.
|
|
132
|
-
</td>
|
|
133
|
-
<td width="20%">Boolean</td>
|
|
134
|
-
<td width="20%"><code>component.default.withStory<code></td>
|
|
135
|
-
</tr>
|
|
136
|
-
|
|
137
|
-
<tr>
|
|
138
|
-
<td width="20%"><b>--withStyle</b></td>
|
|
139
|
-
<td width="60%">
|
|
140
|
-
Creates a corresponding stylesheet file with this component.
|
|
141
|
-
</td>
|
|
142
|
-
<td width="20%">Boolean</td>
|
|
143
|
-
<td width="20%"><code>component.default.withStyle<code></td>
|
|
144
|
-
</tr>
|
|
145
|
-
|
|
146
|
-
<tr>
|
|
147
|
-
<td width="20%"><b>--withTest</b></td>
|
|
148
|
-
<td width="60%">
|
|
149
|
-
Creates a corresponding test file with this component.
|
|
150
|
-
</td>
|
|
151
|
-
<td width="20%">Boolean</td>
|
|
152
|
-
<td width="20%"><code>component.default.withTest<code></td>
|
|
153
|
-
</tr>
|
|
154
|
-
|
|
155
|
-
<tr>
|
|
156
|
-
<td width="20%"><b>--dry-run</b></td>
|
|
157
|
-
<td width="60%">
|
|
158
|
-
Show what will be generated without writing to disk
|
|
159
|
-
</td>
|
|
160
|
-
<td width="20%">Boolean</td>
|
|
161
|
-
<td width="20%"><code>false<code></td>
|
|
162
|
-
</tr>
|
|
163
|
-
|
|
164
|
-
<tr>
|
|
165
|
-
<td width="20%"><b>--flat</b></td>
|
|
166
|
-
<td width="60%">
|
|
167
|
-
Generate the files in the mentioned path instead of creating new folder for it
|
|
168
|
-
</td>
|
|
169
|
-
<td width="20%">Boolean</td>
|
|
170
|
-
<td width="20%"><code>false<code></td>
|
|
171
|
-
</tr>
|
|
172
|
-
|
|
173
|
-
<tr>
|
|
174
|
-
<td width="20%"><b>--customDirectory</b></td>
|
|
175
|
-
<td width="60%">
|
|
176
|
-
Template value that overrides the name of the directory of the component to be generated in.<br />
|
|
177
|
-
See more under <a href="#custom-component-directory">custom component directory</a>.
|
|
178
|
-
</td>
|
|
179
|
-
<td width="20%">String</td>
|
|
180
|
-
<td width="20%"><code>null</code></td>
|
|
181
|
-
</tr>
|
|
182
|
-
</table>
|
|
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.
|
|
110
|
+
By default, GRC uses the `component.default` configuration. You can define additional component types with their own rules:
|
|
191
111
|
|
|
192
112
|
```json
|
|
193
113
|
{
|
|
@@ -221,81 +141,39 @@ You can do so by extending the **generate-react-cli.json** config file like this
|
|
|
221
141
|
}
|
|
222
142
|
```
|
|
223
143
|
|
|
224
|
-
|
|
144
|
+
Generate components with custom types:
|
|
225
145
|
|
|
226
146
|
```sh
|
|
227
|
-
|
|
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
|
-
},
|
|
147
|
+
npx generate-react-cli component HomePage --type=page
|
|
148
|
+
npx generate-react-cli component Sidebar --type=layout
|
|
252
149
|
```
|
|
253
150
|
|
|
254
|
-
|
|
151
|
+
## Custom Component Templates
|
|
255
152
|
|
|
256
|
-
|
|
153
|
+
Create your own templates that GRC uses instead of the built-in ones. Add a `customTemplates` object to any component type:
|
|
257
154
|
|
|
258
155
|
```json
|
|
259
156
|
{
|
|
260
|
-
"usesTypeScript": false,
|
|
261
|
-
"usesCssModule": true,
|
|
262
|
-
"cssPreprocessor": "scss",
|
|
263
|
-
"testLibrary": "Testing Library",
|
|
264
157
|
"component": {
|
|
265
158
|
"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
159
|
"path": "src/components",
|
|
272
160
|
"withStyle": true,
|
|
273
161
|
"withTest": true,
|
|
274
|
-
"withStory": true,
|
|
275
|
-
"withLazy": false
|
|
276
|
-
},
|
|
277
|
-
"page": {
|
|
278
162
|
"customTemplates": {
|
|
279
|
-
"
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
"withStory": false,
|
|
284
|
-
"withStyle": true,
|
|
285
|
-
"withTest": true
|
|
163
|
+
"component": "templates/TemplateName.js",
|
|
164
|
+
"style": "templates/TemplateName.module.css",
|
|
165
|
+
"test": "templates/TemplateName.test.js"
|
|
166
|
+
}
|
|
286
167
|
}
|
|
287
168
|
}
|
|
288
169
|
}
|
|
289
170
|
```
|
|
290
171
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
#### Example of a custom component template file:
|
|
172
|
+
Example custom component template:
|
|
294
173
|
|
|
295
174
|
```jsx
|
|
296
|
-
// templates/
|
|
175
|
+
// templates/TemplateName.js
|
|
297
176
|
|
|
298
|
-
import React from 'react';
|
|
299
177
|
import styles from './TemplateName.module.css';
|
|
300
178
|
|
|
301
179
|
const TemplateName = () => (
|
|
@@ -307,176 +185,99 @@ const TemplateName = () => (
|
|
|
307
185
|
export default TemplateName;
|
|
308
186
|
```
|
|
309
187
|
|
|
310
|
-
|
|
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:
|
|
188
|
+
Example custom test template:
|
|
323
189
|
|
|
324
190
|
```jsx
|
|
325
|
-
// templates/
|
|
191
|
+
// templates/TemplateName.test.js
|
|
326
192
|
|
|
327
|
-
import
|
|
328
|
-
import ReactDOM from 'react-dom';
|
|
193
|
+
import { createRoot } from 'react-dom/client';
|
|
329
194
|
import TemplateName from './TemplateName';
|
|
330
195
|
|
|
331
|
-
it('
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
196
|
+
it('should mount', () => {
|
|
197
|
+
const container = document.createElement('div');
|
|
198
|
+
const root = createRoot(container);
|
|
199
|
+
root.render(<TemplateName />);
|
|
200
|
+
root.unmount();
|
|
335
201
|
});
|
|
336
202
|
```
|
|
337
203
|
|
|
338
|
-
|
|
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:
|
|
204
|
+
All template types are optional. If you don't specify a custom template for a file type, GRC uses its built-in template.
|
|
343
205
|
|
|
344
|
-
|
|
345
|
-
- component.default
|
|
346
|
-
- component._type_
|
|
347
|
-
- CLI
|
|
206
|
+
## Template Keywords
|
|
348
207
|
|
|
349
|
-
|
|
208
|
+
Use these keywords in your custom templates and filenames. GRC replaces them with the component name in various formats:
|
|
350
209
|
|
|
351
|
-
|
|
210
|
+
| Keyword | Output Format | Example (`Box`) |
|
|
211
|
+
|---------|--------------|-----------------|
|
|
212
|
+
| `templatename` | raw (as typed) | `Box` |
|
|
213
|
+
| `TemplateName` | PascalCase | `Box` |
|
|
214
|
+
| `templateName` | camelCase | `box` |
|
|
215
|
+
| `template-name` | kebab-case | `box` |
|
|
216
|
+
| `template_name` | snake_case | `box` |
|
|
217
|
+
| `TEMPLATE_NAME` | UPPER_SNAKE | `BOX` |
|
|
218
|
+
| `TEMPLATENAME` | UPPERCASE | `BOX` |
|
|
352
219
|
|
|
353
|
-
|
|
220
|
+
## Custom Component Files
|
|
354
221
|
|
|
355
|
-
|
|
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
|
-
```
|
|
222
|
+
Add custom files beyond the built-in options (`withStyle`, `withTest`, `withStory`, `withLazy`).
|
|
375
223
|
|
|
376
|
-
|
|
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.
|
|
224
|
+
Example: Adding an `index.js` barrel file for cleaner imports:
|
|
380
225
|
|
|
381
226
|
```json
|
|
382
227
|
{
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
"
|
|
387
|
-
"withStory": true,
|
|
388
|
-
"withStyle": false,
|
|
228
|
+
"component": {
|
|
229
|
+
"default": {
|
|
230
|
+
"path": "src/components",
|
|
231
|
+
"withStyle": true,
|
|
389
232
|
"withTest": true,
|
|
390
|
-
"
|
|
391
|
-
"withContext": true,
|
|
392
|
-
"customDirectory": "TemplateNameProvider",
|
|
233
|
+
"withIndex": true,
|
|
393
234
|
"customTemplates": {
|
|
394
|
-
|
|
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"
|
|
235
|
+
"index": "templates/index.js"
|
|
399
236
|
}
|
|
237
|
+
}
|
|
400
238
|
}
|
|
401
|
-
...
|
|
402
239
|
}
|
|
403
240
|
```
|
|
404
241
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
|
242
|
+
```jsx
|
|
243
|
+
// templates/index.js
|
|
244
|
+
export { default } from './TemplateName';
|
|
421
245
|
```
|
|
422
246
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
### Custom component files
|
|
247
|
+
Custom files require corresponding custom templates in `customTemplates`.
|
|
426
248
|
|
|
427
|
-
|
|
249
|
+
## Advanced: Custom Directory
|
|
428
250
|
|
|
429
|
-
|
|
251
|
+
Override the generated component's folder name using `customDirectory`. This is useful when you need naming conventions that differ from the component name.
|
|
430
252
|
|
|
431
|
-
|
|
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.
|
|
253
|
+
Example: Generate a `Theme` provider where files live in a `ThemeProvider` folder:
|
|
436
254
|
|
|
437
255
|
```json
|
|
438
256
|
{
|
|
439
|
-
"usesTypeScript": false,
|
|
440
|
-
"usesCssModule": false,
|
|
441
|
-
"cssPreprocessor": "css",
|
|
442
|
-
"testLibrary": "Testing Library",
|
|
443
257
|
"component": {
|
|
444
|
-
"
|
|
445
|
-
"path": "src/
|
|
446
|
-
"withStyle": true,
|
|
258
|
+
"provider": {
|
|
259
|
+
"path": "src/providers",
|
|
447
260
|
"withTest": true,
|
|
448
|
-
"
|
|
449
|
-
"withLazy": false,
|
|
450
|
-
"withIndex": true,
|
|
451
|
-
"withStoryStyle": true,
|
|
261
|
+
"customDirectory": "TemplateNameProvider",
|
|
452
262
|
"customTemplates": {
|
|
453
|
-
"
|
|
454
|
-
"storyStyle": "templates/default/TemplateName.stories.css"
|
|
263
|
+
"component": "templates/TemplateNameProvider.tsx"
|
|
455
264
|
}
|
|
456
265
|
}
|
|
457
266
|
}
|
|
458
267
|
}
|
|
459
268
|
```
|
|
460
269
|
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
export { default } from './TemplateName';
|
|
270
|
+
```sh
|
|
271
|
+
npx generate-react-cli component Theme --type=provider
|
|
272
|
+
# Creates: src/providers/ThemeProvider/ThemeProvider.tsx
|
|
465
273
|
```
|
|
466
274
|
|
|
467
|
-
|
|
468
|
-
/* templates/default/TemplateName.stories.css */
|
|
275
|
+
You can also pass it as a CLI option:
|
|
469
276
|
|
|
470
|
-
|
|
471
|
-
|
|
277
|
+
```sh
|
|
278
|
+
npx generate-react-cli component Box --customDirectory=TemplateNameLayout
|
|
472
279
|
```
|
|
473
280
|
|
|
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
281
|
## License
|
|
481
282
|
|
|
482
|
-
Generate React CLI is
|
|
283
|
+
Generate React CLI is open source software [licensed as MIT](https://github.com/arminbro/generate-react-cli/blob/master/LICENSE).
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
export default `import
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import styles from './templatename.module.css';
|
|
1
|
+
export default `import styles from './templatename.module.css';
|
|
4
2
|
|
|
5
3
|
const templatename = () => (
|
|
6
4
|
<div className={styles.templatename} data-testid="templatename">
|
|
@@ -8,9 +6,5 @@ const templatename = () => (
|
|
|
8
6
|
</div>
|
|
9
7
|
);
|
|
10
8
|
|
|
11
|
-
templatename.propTypes = {};
|
|
12
|
-
|
|
13
|
-
templatename.defaultProps = {};
|
|
14
|
-
|
|
15
9
|
export default templatename;
|
|
16
10
|
`;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export default `import
|
|
1
|
+
export default `import { lazy, Suspense } from 'react';
|
|
2
2
|
|
|
3
3
|
const Lazytemplatename = lazy(() => import('./templatename'));
|
|
4
4
|
|
|
5
|
-
const templatename = props => (
|
|
5
|
+
const templatename = (props) => (
|
|
6
6
|
<Suspense fallback={null}>
|
|
7
7
|
<Lazytemplatename {...props} />
|
|
8
8
|
</Suspense>
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
export default
|
|
2
|
-
import templatename from './templatename';
|
|
1
|
+
export default `import templatename from './templatename';
|
|
3
2
|
|
|
4
3
|
export default {
|
|
5
|
-
title:
|
|
4
|
+
title: 'templatename',
|
|
5
|
+
component: templatename,
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
export const Default =
|
|
9
|
-
|
|
10
|
-
Default.story = {
|
|
11
|
-
name: 'default',
|
|
12
|
-
};
|
|
8
|
+
export const Default = {};
|
|
13
9
|
`;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export default `import
|
|
2
|
-
import ReactDOM from 'react-dom';
|
|
1
|
+
export default `import { createRoot } from 'react-dom/client';
|
|
3
2
|
import templatename from './templatename';
|
|
4
3
|
|
|
5
|
-
it('
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
it('should mount', () => {
|
|
5
|
+
const container = document.createElement('div');
|
|
6
|
+
const root = createRoot(container);
|
|
7
|
+
root.render(<templatename />);
|
|
8
|
+
root.unmount();
|
|
9
9
|
});`;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
export default `import
|
|
2
|
-
import { render, screen } from '@testing-library/react';
|
|
1
|
+
export default `import { render, screen } from '@testing-library/react';
|
|
3
2
|
import '@testing-library/jest-dom';
|
|
4
3
|
import templatename from './templatename';
|
|
5
4
|
|
|
6
5
|
describe('<templatename />', () => {
|
|
7
|
-
test('
|
|
6
|
+
test('should mount', () => {
|
|
8
7
|
render(<templatename />);
|
|
9
8
|
|
|
10
9
|
const templateName = screen.getByTestId('templatename');
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default `import { describe, test, expect } from 'vitest';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom/vitest';
|
|
4
|
+
import templatename from './templatename';
|
|
5
|
+
|
|
6
|
+
describe('<templatename />', () => {
|
|
7
|
+
test('should mount', () => {
|
|
8
|
+
render(<templatename />);
|
|
9
|
+
|
|
10
|
+
const templateName = screen.getByTestId('templatename');
|
|
11
|
+
|
|
12
|
+
expect(templateName).toBeInTheDocument();
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
`;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export default `import
|
|
1
|
+
export default `import { lazy, Suspense, ComponentProps } from 'react';
|
|
2
2
|
|
|
3
3
|
const Lazytemplatename = lazy(() => import('./templatename'));
|
|
4
4
|
|
|
5
|
-
const templatename = (props:
|
|
5
|
+
const templatename = (props: ComponentProps<typeof Lazytemplatename>) => (
|
|
6
6
|
<Suspense fallback={null}>
|
|
7
7
|
<Lazytemplatename {...props} />
|
|
8
8
|
</Suspense>
|
|
@@ -12,13 +12,13 @@ import componentLazyTemplate from '../templates/component/componentLazyTemplate.
|
|
|
12
12
|
import componentStoryTemplate from '../templates/component/componentStoryTemplate.js';
|
|
13
13
|
import componentStyledTemplate from '../templates/component/componentStyledTemplate.js';
|
|
14
14
|
import componentTestDefaultTemplate from '../templates/component/componentTestDefaultTemplate.js';
|
|
15
|
-
import componentTestEnzymeTemplate from '../templates/component/componentTestEnzymeTemplate.js';
|
|
16
15
|
import componentTestTestingLibraryTemplate from '../templates/component/componentTestTestingLibraryTemplate.js';
|
|
16
|
+
import componentTestVitestTemplate from '../templates/component/componentTestVitestTemplate.js';
|
|
17
17
|
import componentTsLazyTemplate from '../templates/component/componentTsLazyTemplate.js';
|
|
18
18
|
import componentTsTemplate from '../templates/component/componentTsTemplate.js';
|
|
19
|
-
import { error, fileSummary } from './messagesUtils.js';
|
|
19
|
+
import { error, exitWithError, fileSummary } from './messagesUtils.js';
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const TEMPLATE_NAME_REGEX = /template[-_]?name/i;
|
|
22
22
|
|
|
23
23
|
const { existsSync, outputFileSync, readFileSync } = fsExtra;
|
|
24
24
|
|
|
@@ -35,15 +35,13 @@ export function getComponentByType(args, cliConfigFile) {
|
|
|
35
35
|
|
|
36
36
|
if (!selectedComponentType) {
|
|
37
37
|
const availableTypes = Object.keys(cliConfigFile.component).join(', ');
|
|
38
|
-
|
|
38
|
+
exitWithError(`Unknown component type "${componentType}"`, {
|
|
39
39
|
details: `Available types: ${availableTypes}`,
|
|
40
40
|
suggestions: [
|
|
41
41
|
`Use one of the available types: ${availableTypes}`,
|
|
42
42
|
'Add this component type to your generate-react-cli.json config',
|
|
43
43
|
],
|
|
44
44
|
});
|
|
45
|
-
|
|
46
|
-
process.exit(1);
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
// Otherwise return it.
|
|
@@ -61,7 +59,7 @@ export function getCorrespondingComponentFileTypes(component) {
|
|
|
61
59
|
}
|
|
62
60
|
|
|
63
61
|
function getCustomTemplate(componentName, templatePath) {
|
|
64
|
-
//
|
|
62
|
+
// Try loading custom template
|
|
65
63
|
|
|
66
64
|
try {
|
|
67
65
|
const template = readFileSync(templatePath, 'utf8');
|
|
@@ -69,15 +67,13 @@ function getCustomTemplate(componentName, templatePath) {
|
|
|
69
67
|
|
|
70
68
|
return { template, filename };
|
|
71
69
|
} catch {
|
|
72
|
-
|
|
70
|
+
exitWithError(`Custom template not found: "${templatePath}"`, {
|
|
73
71
|
suggestions: [
|
|
74
72
|
'Verify the template path in your generate-react-cli.json config',
|
|
75
73
|
'Check that the file exists and is readable',
|
|
76
74
|
'Use an absolute path or a path relative to project root',
|
|
77
75
|
],
|
|
78
76
|
});
|
|
79
|
-
|
|
80
|
-
return process.exit(1);
|
|
81
77
|
}
|
|
82
78
|
}
|
|
83
79
|
|
|
@@ -97,17 +93,15 @@ function componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, fi
|
|
|
97
93
|
if (customDirectoryConfigs.length > 0) {
|
|
98
94
|
const customDirectory = customDirectoryConfigs.slice(-1).toString();
|
|
99
95
|
|
|
100
|
-
//
|
|
101
|
-
if (
|
|
102
|
-
|
|
96
|
+
// Check if the customDirectory contains a template placeholder
|
|
97
|
+
if (!TEMPLATE_NAME_REGEX.test(customDirectory)) {
|
|
98
|
+
exitWithError(`Invalid customDirectory: "${customDirectory}"`, {
|
|
103
99
|
details: 'customDirectory must contain a template placeholder',
|
|
104
100
|
suggestions: [
|
|
105
101
|
'Use templatename, TemplateName, template-name, or template_name',
|
|
106
102
|
'Example: "{{templatename}}" or "TemplateName"',
|
|
107
103
|
],
|
|
108
104
|
});
|
|
109
|
-
|
|
110
|
-
process.exit(-2);
|
|
111
105
|
}
|
|
112
106
|
|
|
113
107
|
for (const convertor in convertors) {
|
|
@@ -137,7 +131,7 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile, convert
|
|
|
137
131
|
// Check for a custom component template.
|
|
138
132
|
|
|
139
133
|
if (customTemplates && customTemplates.component) {
|
|
140
|
-
//
|
|
134
|
+
// Load and use the custom component template
|
|
141
135
|
|
|
142
136
|
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
|
|
143
137
|
componentName,
|
|
@@ -147,18 +141,19 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile, convert
|
|
|
147
141
|
template = customTemplate;
|
|
148
142
|
filename = customTemplateFilename;
|
|
149
143
|
} else {
|
|
150
|
-
//
|
|
144
|
+
// Else use GRC built-in component template
|
|
151
145
|
|
|
152
146
|
template = usesTypeScript ? componentTsTemplate : componentJsTemplate;
|
|
153
147
|
filename = usesTypeScript ? `${componentName}.tsx` : `${componentName}.js`;
|
|
154
148
|
|
|
155
|
-
//
|
|
149
|
+
// If test library doesn't use data-testid or if withTest is false. Remove data-testid from template
|
|
156
150
|
|
|
157
|
-
|
|
151
|
+
const usesTestId = testLibrary === 'Testing Library' || testLibrary === 'Vitest';
|
|
152
|
+
if (!usesTestId || !cmd.withTest) {
|
|
158
153
|
template = template.replace(` data-testid="templatename"`, '');
|
|
159
154
|
}
|
|
160
155
|
|
|
161
|
-
//
|
|
156
|
+
// If it has a corresponding stylesheet
|
|
162
157
|
|
|
163
158
|
if (cmd.withStyle) {
|
|
164
159
|
if (cliConfigFile.usesStyledComponents) {
|
|
@@ -174,7 +169,7 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile, convert
|
|
|
174
169
|
const module = usesCssModule ? '.module' : '';
|
|
175
170
|
const cssPath = `${componentName}${module}.${cssPreprocessor}`;
|
|
176
171
|
|
|
177
|
-
//
|
|
172
|
+
// If the css module is true make sure to update the template accordingly
|
|
178
173
|
|
|
179
174
|
if (module.length) {
|
|
180
175
|
template = template.replace(`'./templatename.module.css'`, `'./${cssPath}'`);
|
|
@@ -184,7 +179,7 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile, convert
|
|
|
184
179
|
}
|
|
185
180
|
}
|
|
186
181
|
} else {
|
|
187
|
-
//
|
|
182
|
+
// If no stylesheet, remove className attribute and style import from jsTemplate
|
|
188
183
|
|
|
189
184
|
template = template.replace(` className={styles.templatename}`, '');
|
|
190
185
|
template = template.replace(`import styles from './templatename.module.css';`, '');
|
|
@@ -206,7 +201,7 @@ function componentStyleTemplateGenerator({ cliConfigFile, cmd, componentName, co
|
|
|
206
201
|
// Check for a custom style template.
|
|
207
202
|
|
|
208
203
|
if (customTemplates && customTemplates.style) {
|
|
209
|
-
//
|
|
204
|
+
// Load and use the custom style template
|
|
210
205
|
|
|
211
206
|
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
|
|
212
207
|
componentName,
|
|
@@ -224,7 +219,7 @@ function componentStyleTemplateGenerator({ cliConfigFile, cmd, componentName, co
|
|
|
224
219
|
const module = usesCssModule ? '.module' : '';
|
|
225
220
|
const cssFilename = `${componentName}${module}.${cssPreprocessor}`;
|
|
226
221
|
|
|
227
|
-
//
|
|
222
|
+
// Else use GRC built-in style template
|
|
228
223
|
|
|
229
224
|
template = componentCssTemplate;
|
|
230
225
|
filename = cssFilename;
|
|
@@ -247,7 +242,7 @@ function componentTestTemplateGenerator({ cliConfigFile, cmd, componentName, con
|
|
|
247
242
|
// Check for a custom test template.
|
|
248
243
|
|
|
249
244
|
if (customTemplates && customTemplates.test) {
|
|
250
|
-
//
|
|
245
|
+
// Load and use the custom test template
|
|
251
246
|
|
|
252
247
|
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
|
|
253
248
|
componentName,
|
|
@@ -259,12 +254,10 @@ function componentTestTemplateGenerator({ cliConfigFile, cmd, componentName, con
|
|
|
259
254
|
} else {
|
|
260
255
|
filename = usesTypeScript ? `${componentName}.test.tsx` : `${componentName}.test.js`;
|
|
261
256
|
|
|
262
|
-
if (testLibrary === '
|
|
263
|
-
// --- Else use GRC built-in test template based on test library type
|
|
264
|
-
|
|
265
|
-
template = componentTestEnzymeTemplate;
|
|
266
|
-
} else if (testLibrary === 'Testing Library') {
|
|
257
|
+
if (testLibrary === 'Testing Library') {
|
|
267
258
|
template = componentTestTestingLibraryTemplate;
|
|
259
|
+
} else if (testLibrary === 'Vitest') {
|
|
260
|
+
template = componentTestVitestTemplate;
|
|
268
261
|
} else {
|
|
269
262
|
template = componentTestDefaultTemplate;
|
|
270
263
|
}
|
|
@@ -286,7 +279,7 @@ function componentStoryTemplateGenerator({ cliConfigFile, cmd, componentName, co
|
|
|
286
279
|
// Check for a custom story template.
|
|
287
280
|
|
|
288
281
|
if (customTemplates && customTemplates.story) {
|
|
289
|
-
//
|
|
282
|
+
// Load and use the custom story template
|
|
290
283
|
|
|
291
284
|
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
|
|
292
285
|
componentName,
|
|
@@ -296,7 +289,7 @@ function componentStoryTemplateGenerator({ cliConfigFile, cmd, componentName, co
|
|
|
296
289
|
template = customTemplate;
|
|
297
290
|
filename = customTemplateFilename;
|
|
298
291
|
} else {
|
|
299
|
-
//
|
|
292
|
+
// Else use GRC built-in story template
|
|
300
293
|
|
|
301
294
|
template = componentStoryTemplate;
|
|
302
295
|
filename = usesTypeScript ? `${componentName}.stories.tsx` : `${componentName}.stories.js`;
|
|
@@ -318,7 +311,7 @@ function componentLazyTemplateGenerator({ cmd, componentName, cliConfigFile, con
|
|
|
318
311
|
// Check for a custom lazy template.
|
|
319
312
|
|
|
320
313
|
if (customTemplates && customTemplates.lazy) {
|
|
321
|
-
//
|
|
314
|
+
// Load and use the custom lazy template
|
|
322
315
|
|
|
323
316
|
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
|
|
324
317
|
componentName,
|
|
@@ -328,7 +321,7 @@ function componentLazyTemplateGenerator({ cmd, componentName, cliConfigFile, con
|
|
|
328
321
|
template = customTemplate;
|
|
329
322
|
filename = customTemplateFilename;
|
|
330
323
|
} else {
|
|
331
|
-
//
|
|
324
|
+
// Else use GRC built-in lazy template
|
|
332
325
|
|
|
333
326
|
template = usesTypeScript ? componentTsLazyTemplate : componentLazyTemplate;
|
|
334
327
|
filename = usesTypeScript ? `${componentName}.lazy.tsx` : `${componentName}.lazy.js`;
|
|
@@ -350,18 +343,16 @@ function customFileTemplateGenerator({ componentName, cmd, cliConfigFile, compon
|
|
|
350
343
|
// Check for a valid custom template for the corresponding custom component file.
|
|
351
344
|
|
|
352
345
|
if (!customTemplates || !customTemplates[fileType]) {
|
|
353
|
-
|
|
346
|
+
exitWithError(`Missing custom template for "${fileType}"`, {
|
|
354
347
|
details: 'Custom component files require a matching custom template',
|
|
355
348
|
suggestions: [
|
|
356
349
|
`Add a "${fileType}" template path to customTemplates in your config`,
|
|
357
350
|
'Check that the template file exists at the specified path',
|
|
358
351
|
],
|
|
359
352
|
});
|
|
360
|
-
|
|
361
|
-
return process.exit(1);
|
|
362
353
|
}
|
|
363
354
|
|
|
364
|
-
//
|
|
355
|
+
// Load and use the custom component template.
|
|
365
356
|
|
|
366
357
|
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
|
|
367
358
|
componentName,
|
|
@@ -378,7 +369,7 @@ function customFileTemplateGenerator({ componentName, cmd, cliConfigFile, compon
|
|
|
378
369
|
};
|
|
379
370
|
}
|
|
380
371
|
|
|
381
|
-
//
|
|
372
|
+
// Built in component file types
|
|
382
373
|
|
|
383
374
|
const buildInComponentFileTypes = {
|
|
384
375
|
COMPONENT: 'component',
|
|
@@ -388,7 +379,7 @@ const buildInComponentFileTypes = {
|
|
|
388
379
|
LAZY: 'withLazy',
|
|
389
380
|
};
|
|
390
381
|
|
|
391
|
-
//
|
|
382
|
+
// Generate component template map
|
|
392
383
|
|
|
393
384
|
const componentTemplateGeneratorMap = {
|
|
394
385
|
[buildInComponentFileTypes.COMPONENT]: componentTemplateGenerator,
|
|
@@ -404,7 +395,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
404
395
|
let basePath = '';
|
|
405
396
|
|
|
406
397
|
componentFileTypes.forEach((componentFileType) => {
|
|
407
|
-
//
|
|
398
|
+
// Generate templates only if the component options (withStyle, withTest, etc..) are true,
|
|
408
399
|
// or if the template type is "component"
|
|
409
400
|
|
|
410
401
|
if (
|
|
@@ -436,7 +427,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
436
427
|
basePath = path.dirname(componentPath);
|
|
437
428
|
}
|
|
438
429
|
|
|
439
|
-
//
|
|
430
|
+
// Make sure the component does not already exist in the path directory.
|
|
440
431
|
|
|
441
432
|
if (existsSync(componentPath)) {
|
|
442
433
|
generatedFiles.push({ filename, status: 'skipped', path: componentPath });
|
|
@@ -445,67 +436,9 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
445
436
|
if (!cmd.dryRun) {
|
|
446
437
|
outputFileSync(componentPath, template);
|
|
447
438
|
|
|
448
|
-
//
|
|
449
|
-
|
|
450
|
-
regex:
|
|
451
|
-
replacement: convertors.templatename,
|
|
452
|
-
paths: [componentPath],
|
|
453
|
-
recursive: false,
|
|
454
|
-
silent: true,
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
// Will replace the TemplateName in PascalCase
|
|
458
|
-
replace({
|
|
459
|
-
regex: 'TemplateName',
|
|
460
|
-
replacement: convertors.TemplateName,
|
|
461
|
-
paths: [componentPath],
|
|
462
|
-
recursive: false,
|
|
463
|
-
silent: true,
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
// Will replace the templateName in camelCase
|
|
467
|
-
replace({
|
|
468
|
-
regex: 'templateName',
|
|
469
|
-
replacement: convertors.templateName,
|
|
470
|
-
paths: [componentPath],
|
|
471
|
-
recursive: false,
|
|
472
|
-
silent: true,
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
// Will replace the template-name in kebab-case
|
|
476
|
-
replace({
|
|
477
|
-
regex: 'template-name',
|
|
478
|
-
replacement: convertors['template-name'],
|
|
479
|
-
paths: [componentPath],
|
|
480
|
-
recursive: false,
|
|
481
|
-
silent: true,
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
// Will replace the template_name in snake_case
|
|
485
|
-
replace({
|
|
486
|
-
regex: 'template_name',
|
|
487
|
-
replacement: convertors.template_name,
|
|
488
|
-
paths: [componentPath],
|
|
489
|
-
recursive: false,
|
|
490
|
-
silent: true,
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
// Will replace the TEMPLATE_NAME in uppercase SNAKE_CASE
|
|
494
|
-
replace({
|
|
495
|
-
regex: 'TEMPLATE_NAME',
|
|
496
|
-
replacement: convertors.TEMPLATE_NAME,
|
|
497
|
-
paths: [componentPath],
|
|
498
|
-
recursive: false,
|
|
499
|
-
silent: true,
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
// Will replace the TEMPLATENAME in uppercase SNAKE_CASE
|
|
503
|
-
replace({
|
|
504
|
-
regex: 'TEMPLATENAME',
|
|
505
|
-
replacement: convertors.TEMPLATENAME,
|
|
506
|
-
paths: [componentPath],
|
|
507
|
-
recursive: false,
|
|
508
|
-
silent: true,
|
|
439
|
+
// Replace all template placeholders with their corresponding component name formats
|
|
440
|
+
Object.entries(convertors).forEach(([pattern, replacement]) => {
|
|
441
|
+
replace({ regex: pattern, replacement, paths: [componentPath], recursive: false, silent: true });
|
|
509
442
|
});
|
|
510
443
|
}
|
|
511
444
|
|
|
@@ -3,7 +3,7 @@ import inquirer from 'inquirer';
|
|
|
3
3
|
|
|
4
4
|
import merge from 'lodash/merge.js';
|
|
5
5
|
import deepKeys from './deepKeysUtils.js';
|
|
6
|
-
import { blank, error, header, outro, success } from './messagesUtils.js';
|
|
6
|
+
import { blank, error, exitWithError, header, outro, success } from './messagesUtils.js';
|
|
7
7
|
|
|
8
8
|
const { accessSync, constants, outputFileSync, readFileSync } = fsExtra;
|
|
9
9
|
const { prompt } = inquirer;
|
|
@@ -40,7 +40,7 @@ const projectLevelQuestions = [
|
|
|
40
40
|
type: 'select',
|
|
41
41
|
name: 'testLibrary',
|
|
42
42
|
message: 'What testing library does your project use?',
|
|
43
|
-
choices: ['Testing Library', '
|
|
43
|
+
choices: ['Testing Library', 'Vitest', 'None'],
|
|
44
44
|
},
|
|
45
45
|
];
|
|
46
46
|
|
|
@@ -76,7 +76,7 @@ export const componentLevelQuestions = [
|
|
|
76
76
|
type: 'confirm',
|
|
77
77
|
name: 'component.default.withLazy',
|
|
78
78
|
message:
|
|
79
|
-
'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://
|
|
79
|
+
'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://react.dev/reference/react/lazy) with each component you generate?',
|
|
80
80
|
},
|
|
81
81
|
];
|
|
82
82
|
|
|
@@ -99,9 +99,9 @@ async function createCLIConfigFile() {
|
|
|
99
99
|
outputFileSync('generate-react-cli.json', JSON.stringify(answers, null, 2));
|
|
100
100
|
|
|
101
101
|
blank();
|
|
102
|
-
success('
|
|
102
|
+
success('Created the generate-react-cli.json config file');
|
|
103
103
|
blank();
|
|
104
|
-
outro('You can always update
|
|
104
|
+
outro('You can always update the config file manually. Happy Hacking!');
|
|
105
105
|
|
|
106
106
|
return answers;
|
|
107
107
|
} catch (e) {
|
|
@@ -132,9 +132,9 @@ async function updateCLIConfigFile(missingConfigQuestions, currentConfigFile) {
|
|
|
132
132
|
);
|
|
133
133
|
|
|
134
134
|
blank();
|
|
135
|
-
success('
|
|
135
|
+
success('Updated the generate-react-cli.json config file');
|
|
136
136
|
blank();
|
|
137
|
-
outro('You can always update
|
|
137
|
+
outro('You can always update the config file manually. Happy Hacking!');
|
|
138
138
|
|
|
139
139
|
return updatedAnswers;
|
|
140
140
|
} catch (e) {
|
|
@@ -186,13 +186,12 @@ export async function getCLIConfigFile() {
|
|
|
186
186
|
return await createCLIConfigFile();
|
|
187
187
|
}
|
|
188
188
|
} catch {
|
|
189
|
-
|
|
189
|
+
exitWithError('Not in project root', {
|
|
190
190
|
details: 'Could not find package.json in current directory',
|
|
191
191
|
suggestions: [
|
|
192
192
|
'Run this command from your project root directory',
|
|
193
193
|
'Make sure package.json exists in the current directory',
|
|
194
194
|
],
|
|
195
195
|
});
|
|
196
|
-
return process.exit(1);
|
|
197
196
|
}
|
|
198
197
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
|
|
3
|
+
const DEFAULT_TERMINAL_WIDTH = 80;
|
|
4
|
+
|
|
3
5
|
// Symbols for consistent visual feedback
|
|
4
6
|
const symbols = {
|
|
5
7
|
success: chalk.green('✓'),
|
|
@@ -12,10 +14,14 @@ const symbols = {
|
|
|
12
14
|
|
|
13
15
|
// Create a responsive divider that adapts to terminal width
|
|
14
16
|
function divider(color = 'cyan') {
|
|
15
|
-
const width = Math.min(process.stdout.columns ||
|
|
17
|
+
const width = Math.min(process.stdout.columns || DEFAULT_TERMINAL_WIDTH, DEFAULT_TERMINAL_WIDTH);
|
|
16
18
|
return chalk[color]('─'.repeat(width));
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
function pluralize(count, word) {
|
|
22
|
+
return count === 1 ? word : `${word}s`;
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
// Success message with optional file path
|
|
20
26
|
export function success(message, filePath) {
|
|
21
27
|
if (filePath) {
|
|
@@ -45,6 +51,12 @@ export function error(message, { details, suggestions } = {}) {
|
|
|
45
51
|
console.log();
|
|
46
52
|
}
|
|
47
53
|
|
|
54
|
+
// Error message with exit
|
|
55
|
+
export function exitWithError(message, options = {}, exitCode = 1) {
|
|
56
|
+
error(message, options);
|
|
57
|
+
process.exit(exitCode);
|
|
58
|
+
}
|
|
59
|
+
|
|
48
60
|
// Warning message
|
|
49
61
|
export function warning(message) {
|
|
50
62
|
console.log(`${symbols.warning} ${chalk.yellow(message)}`);
|
|
@@ -102,12 +114,11 @@ export function fileSummary(files, basePath, { dryRun = false } = {}) {
|
|
|
102
114
|
} else {
|
|
103
115
|
// Actual run: show what happened
|
|
104
116
|
if (createdFiles.length > 0) {
|
|
105
|
-
console.log(`${symbols.success} ${chalk.green(`Created ${createdFiles.length}
|
|
117
|
+
console.log(`${symbols.success} ${chalk.green(`Created ${createdFiles.length} ${pluralize(createdFiles.length, 'file')} in ${basePath}`)}`);
|
|
106
118
|
}
|
|
107
119
|
if (skippedFiles.length > 0) {
|
|
108
|
-
console.log(`${symbols.warning} ${chalk.yellow(`Skipped ${skippedFiles.length}
|
|
120
|
+
console.log(`${symbols.warning} ${chalk.yellow(`Skipped ${skippedFiles.length} ${pluralize(skippedFiles.length, 'file')} (already exist)`)}`);
|
|
109
121
|
}
|
|
110
|
-
console.log();
|
|
111
122
|
|
|
112
123
|
// Show file tree with status icons
|
|
113
124
|
files.forEach((file, index) => {
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export default `import React from 'react';
|
|
2
|
-
import { shallow } from 'enzyme';
|
|
3
|
-
import templatename from './templatename';
|
|
4
|
-
|
|
5
|
-
describe('<templatename />', () => {
|
|
6
|
-
let component;
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
component = shallow(<templatename />);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test('It should mount', () => {
|
|
13
|
-
expect(component.length).toBe(1);
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
`;
|