generate-react-cli 8.3.0-alpha.1 → 8.4.0
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/package.json +1 -1
- package/readme.md +111 -52
- package/src/commands/generateComponent.js +11 -1
- package/src/services/openAiService.js +0 -17
- package/src/templates/component/componentStyledTemplate.js +5 -0
- package/src/utils/generateComponentUtils.js +111 -60
- package/src/utils/grcConfigUtils.js +10 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "generate-react-cli",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.4.0",
|
|
4
4
|
"description": "A simple React CLI to generate components instantly and more.",
|
|
5
5
|
"repository": "https://github.com/arminbro/generate-react-cli",
|
|
6
6
|
"bugs": "https://github.com/arminbro/generate-react-cli/issues",
|
package/readme.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/arminbro/generate-react-cli/blob/master/LICENSE)
|
|
4
4
|
|
|
5
|
-
<p align="center">
|
|
5
|
+
<p align="center">
|
|
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
|
|
|
@@ -20,6 +20,7 @@ You can also watch an excellent [video](https://www.youtube.com/watch?v=NEvnt3MW
|
|
|
20
20
|
- [Generate components](#generate-components)
|
|
21
21
|
- [Custom component types](#custom-component-types)
|
|
22
22
|
- [Custom component templates](#custom-component-templates)
|
|
23
|
+
- [Custom component directory](#custom-component-directory)
|
|
23
24
|
- [Custom component files](#custom-component-files)
|
|
24
25
|
- [OpenAi integration (Alpha release)](#openai-integration-alpha-release)
|
|
25
26
|
|
|
@@ -97,10 +98,11 @@ Otherwise, if you don't pass any options, it will just use the default values th
|
|
|
97
98
|
<th>Value Type</th>
|
|
98
99
|
<th>Default Value</th>
|
|
99
100
|
</tr>
|
|
101
|
+
|
|
100
102
|
<tr>
|
|
101
103
|
<td width="20%"><b>--path</b></td>
|
|
102
104
|
<td width="60%">
|
|
103
|
-
Value of the path where you want the component to be generated in (e.g. <b>src/components</b>).
|
|
105
|
+
Value of the path where you want the component to be generated in (e.g. <b>src/components</b>).
|
|
104
106
|
</td>
|
|
105
107
|
<td width="20%">String</td>
|
|
106
108
|
<td width="20%"><code>component.default.path<code></td>
|
|
@@ -118,7 +120,7 @@ Otherwise, if you don't pass any options, it will just use the default values th
|
|
|
118
120
|
<tr>
|
|
119
121
|
<td width="20%"><b>--withLazy</b></td>
|
|
120
122
|
<td width="60%">
|
|
121
|
-
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
|
+
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.
|
|
122
124
|
</td>
|
|
123
125
|
<td width="20%">Boolean</td>
|
|
124
126
|
<td width="20%"><code>component.default.withLazy<code></td>
|
|
@@ -127,7 +129,7 @@ Otherwise, if you don't pass any options, it will just use the default values th
|
|
|
127
129
|
<tr>
|
|
128
130
|
<td width="20%"><b>--withStory</b></td>
|
|
129
131
|
<td width="60%">
|
|
130
|
-
Creates a corresponding (<a href="https://storybook.js.org">storybook</a>) story file with this component.
|
|
132
|
+
Creates a corresponding (<a href="https://storybook.js.org">storybook</a>) story file with this component.
|
|
131
133
|
</td>
|
|
132
134
|
<td width="20%">Boolean</td>
|
|
133
135
|
<td width="20%"><code>component.default.withStory<code></td>
|
|
@@ -136,7 +138,7 @@ Otherwise, if you don't pass any options, it will just use the default values th
|
|
|
136
138
|
<tr>
|
|
137
139
|
<td width="20%"><b>--withStyle</b></td>
|
|
138
140
|
<td width="60%">
|
|
139
|
-
Creates a corresponding stylesheet file with this component.
|
|
141
|
+
Creates a corresponding stylesheet file with this component.
|
|
140
142
|
</td>
|
|
141
143
|
<td width="20%">Boolean</td>
|
|
142
144
|
<td width="20%"><code>component.default.withStyle<code></td>
|
|
@@ -145,11 +147,12 @@ Otherwise, if you don't pass any options, it will just use the default values th
|
|
|
145
147
|
<tr>
|
|
146
148
|
<td width="20%"><b>--withTest</b></td>
|
|
147
149
|
<td width="60%">
|
|
148
|
-
Creates a corresponding test file with this component.
|
|
150
|
+
Creates a corresponding test file with this component.
|
|
149
151
|
</td>
|
|
150
152
|
<td width="20%">Boolean</td>
|
|
151
153
|
<td width="20%"><code>component.default.withTest<code></td>
|
|
152
154
|
</tr>
|
|
155
|
+
|
|
153
156
|
<tr>
|
|
154
157
|
<td width="20%"><b>--dry-run</b></td>
|
|
155
158
|
<td width="60%">
|
|
@@ -158,15 +161,27 @@ Otherwise, if you don't pass any options, it will just use the default values th
|
|
|
158
161
|
<td width="20%">Boolean</td>
|
|
159
162
|
<td width="20%"><code>false<code></td>
|
|
160
163
|
</tr>
|
|
164
|
+
|
|
161
165
|
<tr>
|
|
162
166
|
<td width="20%"><b>--flat</b></td>
|
|
163
167
|
<td width="60%">
|
|
164
|
-
Generate the files in the mentioned path
|
|
168
|
+
Generate the files in the mentioned path instead of creating new folder for it
|
|
165
169
|
</td>
|
|
166
170
|
<td width="20%">Boolean</td>
|
|
167
171
|
<td width="20%"><code>false<code></td>
|
|
168
172
|
</tr>
|
|
169
|
-
|
|
173
|
+
|
|
174
|
+
<tr>
|
|
175
|
+
<td width="20%"><b>--customDirectory</b></td>
|
|
176
|
+
<td width="60%">
|
|
177
|
+
Template value that overrides the name of the directory of the component to be generated in.<br />
|
|
178
|
+
See more under <a href="#custom-component-directory">custom component directory</a>.
|
|
179
|
+
</td>
|
|
180
|
+
<td width="20%">String</td>
|
|
181
|
+
<td width="20%"><code>null</code></td>
|
|
182
|
+
</tr>
|
|
183
|
+
|
|
184
|
+
<tr>
|
|
170
185
|
<td width="20%"><b>--describe</b></td>
|
|
171
186
|
<td width="60%">
|
|
172
187
|
Describe the component you're trying to generate, and OpenAI will do its best to render it following your instructions.
|
|
@@ -176,7 +191,7 @@ Otherwise, if you don't pass any options, it will just use the default values th
|
|
|
176
191
|
</tr>
|
|
177
192
|
</table>
|
|
178
193
|
|
|
179
|
-
### Custom component types
|
|
194
|
+
### Custom component types
|
|
180
195
|
|
|
181
196
|
By default, GRC will use the `component.default` configuration rules when running the component command out of the box.
|
|
182
197
|
|
|
@@ -329,6 +344,93 @@ it('It should mount', () => {
|
|
|
329
344
|
});
|
|
330
345
|
```
|
|
331
346
|
|
|
347
|
+
### Custom component directory
|
|
348
|
+
|
|
349
|
+
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.
|
|
350
|
+
|
|
351
|
+
The `customDirectory` directive allows all supported casings (see previous section) and can be overridden at the following levels in ascending specific of priority:
|
|
352
|
+
|
|
353
|
+
- top
|
|
354
|
+
- component.default
|
|
355
|
+
- component._type_
|
|
356
|
+
- CLI
|
|
357
|
+
|
|
358
|
+
#### Example:
|
|
359
|
+
|
|
360
|
+
For React Context Providers in a project, the decision has been made to separate Context generation from the visual components.
|
|
361
|
+
|
|
362
|
+
In a typical configuration the configuration would look as following:
|
|
363
|
+
|
|
364
|
+
```json
|
|
365
|
+
{
|
|
366
|
+
"provider": {
|
|
367
|
+
"path": "src/components/providers",
|
|
368
|
+
"withLazy": false,
|
|
369
|
+
"withStory": true,
|
|
370
|
+
"withStyle": false,
|
|
371
|
+
"withTest": true,
|
|
372
|
+
"withTypes": true,
|
|
373
|
+
"withContext": true,
|
|
374
|
+
"customTemplates": {
|
|
375
|
+
"component": "src/components/templates/provider/TemplateName.tsx",
|
|
376
|
+
"context": "src/components/templates/provider/TemplateName.context.ts",
|
|
377
|
+
"story": "src/components/templates/provider/TemplateName.stories.tsx",
|
|
378
|
+
"test": "src/components/templates/provider/TemplateName.test.tsx",
|
|
379
|
+
"types": "src/components/templates/provider/TemplateName.types.ts"
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
With the configuration above, the component would be required to either follow a full or a minimalistic naming convention.
|
|
386
|
+
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.
|
|
387
|
+
|
|
388
|
+
To work around this, the `customDirectory` option can be used to enforce a particular style.
|
|
389
|
+
|
|
390
|
+
```json
|
|
391
|
+
{
|
|
392
|
+
...
|
|
393
|
+
"provider": {
|
|
394
|
+
"path": "src/components/providers",
|
|
395
|
+
"withLazy": false,
|
|
396
|
+
"withStory": true,
|
|
397
|
+
"withStyle": false,
|
|
398
|
+
"withTest": true,
|
|
399
|
+
"withTypes": true,
|
|
400
|
+
"withContext": true,
|
|
401
|
+
"customDirectory": "TemplateNameProvider",
|
|
402
|
+
"customTemplates": {
|
|
403
|
+
"component": "src/components/templates/provider/TemplateNameProvider.tsx",
|
|
404
|
+
"context": "src/components/templates/provider/TemplateName.context.ts",
|
|
405
|
+
"story": "src/components/templates/provider/TemplateNameProvider.stories.tsx",
|
|
406
|
+
"test": "src/components/templates/provider/TemplateNameProvider.test.tsx",
|
|
407
|
+
"types": "src/components/templates/provider/TemplateNameProvider.types.ts"
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
...
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
The above configuration would allow you to mix and match different template names and keep naming consistent.
|
|
415
|
+
|
|
416
|
+
If we executed GRC with the above configuration (`npx generate-react-cli component Theme --type=provider`), the result would look like this:
|
|
417
|
+
|
|
418
|
+
```fs
|
|
419
|
+
src/components/providers/ThemeProvider/Theme.context.ts
|
|
420
|
+
src/components/providers/ThemeProvider/ThemeProvider.tsx
|
|
421
|
+
src/components/providers/ThemeProvider/ThemeProvider.stories.tsx
|
|
422
|
+
src/components/providers/ThemeProvider/ThemeProvider.test.tsx
|
|
423
|
+
src/components/providers/ThemeProvider/ThemeProvider.types.ts
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
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:
|
|
427
|
+
|
|
428
|
+
```sh
|
|
429
|
+
npx generate-react-cli component Box --type=layout --customDir=TemplateNameLayout
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
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
|
|
433
|
+
|
|
332
434
|
### Custom component files
|
|
333
435
|
|
|
334
436
|
GRC comes with corresponding built-in files for a given component if you need them (i.e., `withStyle`, `withTest`, `withStory`, and `withLazy`).
|
|
@@ -478,49 +580,6 @@ GlobalNav.defaultProps = {};
|
|
|
478
580
|
export default GlobalNav;
|
|
479
581
|
```
|
|
480
582
|
|
|
481
|
-
If you have unit testing enabled in your project and have the `withTest` flag enabled, it will generate the unit test file and initialize with some test cases for the generated component right out of the box!
|
|
482
|
-
|
|
483
|
-
Here are the unit tests it generated for the GlobalNav component using the default testing library template (`@testing-library/react`) that comes with GRC.
|
|
484
|
-
|
|
485
|
-
```tsx
|
|
486
|
-
import React from 'react';
|
|
487
|
-
import { render, screen } from '@testing-library/react';
|
|
488
|
-
import '@testing-library/jest-dom/extend-expect';
|
|
489
|
-
import GlobalNav from './GlobalNav';
|
|
490
|
-
|
|
491
|
-
describe('<GlobalNav />', () => {
|
|
492
|
-
test('it should mount', () => {
|
|
493
|
-
render(<GlobalNav />);
|
|
494
|
-
|
|
495
|
-
const globalNav = screen.getByTestId('GlobalNav');
|
|
496
|
-
|
|
497
|
-
expect(globalNav).toBeInTheDocument();
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
test('it should display the logo "GRC"', () => {
|
|
501
|
-
render(<GlobalNav />);
|
|
502
|
-
|
|
503
|
-
const logo = screen.getByText('GRC');
|
|
504
|
-
|
|
505
|
-
expect(logo).toBeInTheDocument();
|
|
506
|
-
});
|
|
507
|
-
|
|
508
|
-
test('it should display three links: Home, About and Contact', () => {
|
|
509
|
-
render(<GlobalNav />);
|
|
510
|
-
|
|
511
|
-
const homeLink = screen.getByText('Home');
|
|
512
|
-
const aboutLink = screen.getByText('About');
|
|
513
|
-
const contactLink = screen.getByText('Contact');
|
|
514
|
-
|
|
515
|
-
expect(homeLink).toBeInTheDocument();
|
|
516
|
-
expect(aboutLink).toBeInTheDocument();
|
|
517
|
-
expect(contactLink).toBeInTheDocument();
|
|
518
|
-
});
|
|
519
|
-
});
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
Please note, If you're using a different testing library, you need to provide it as a [custom testing component template](#custom-component-templates), and GRC will instruct openAi to use that to write your unit tests.
|
|
523
|
-
|
|
524
583
|
That's a wrap. I hope this integration will allow us to generate React components more efficiently, and we can still go in and make the necessary adjustments.
|
|
525
584
|
|
|
526
585
|
Again, please provide any feedback if you have any, and I would love to see some of the components that you generate with GRC+OpenAI.
|
|
@@ -29,7 +29,17 @@ export default function initGenerateComponentCommand(args, cliConfigFile, progra
|
|
|
29
29
|
'Generate the files in the mentioned path instead of creating new folder for it',
|
|
30
30
|
selectedComponentType.flat || false
|
|
31
31
|
)
|
|
32
|
-
.option('-dr, --dry-run', 'Show what will be generated without writing to disk')
|
|
32
|
+
.option('-dr, --dry-run', 'Show what will be generated without writing to disk')
|
|
33
|
+
.option(
|
|
34
|
+
'--customDirectory <customDirectory>',
|
|
35
|
+
'You can pass a cased path template that will be used as the component path for the component being generated.\n' +
|
|
36
|
+
'E.g. this allows you to add a prefix or suffix to the component path, ' +
|
|
37
|
+
'or change the case of the name of the directory holding the components to kebab-case.\n' +
|
|
38
|
+
'Examples:\n' +
|
|
39
|
+
'- TemplateName\n' +
|
|
40
|
+
'- template-name\n' +
|
|
41
|
+
'- TemplateNameSuffix'
|
|
42
|
+
);
|
|
33
43
|
|
|
34
44
|
// Dynamic component command option defaults.
|
|
35
45
|
|
|
@@ -16,20 +16,3 @@ export async function aiComponentGenerator(componentTemplate, prompt) {
|
|
|
16
16
|
|
|
17
17
|
return generatedComponent.data.choices[0].text;
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
export async function aiComponentTestGenerator(componentTemplate, prompt) {
|
|
21
|
-
const configuration = new Configuration({ apiKey: process.env.OPENAI_API_KEY });
|
|
22
|
-
const openAiApi = new OpenAIApi(configuration);
|
|
23
|
-
|
|
24
|
-
const generatedComponent = await openAiApi.createCompletion({
|
|
25
|
-
model: 'text-davinci-003',
|
|
26
|
-
prompt: `Create a React component unit tests using this template "${componentTemplate}", but make the adjustments needed with these instructions as follows "${prompt}"`,
|
|
27
|
-
temperature: 0.7,
|
|
28
|
-
max_tokens: 2000,
|
|
29
|
-
top_p: 1.0,
|
|
30
|
-
frequency_penalty: 0.0,
|
|
31
|
-
presence_penalty: 1,
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
return generatedComponent.data.choices[0].text;
|
|
35
|
-
}
|
|
@@ -7,10 +7,11 @@ import snakeCase from 'lodash/snakeCase.js';
|
|
|
7
7
|
import startCase from 'lodash/startCase.js';
|
|
8
8
|
import fsExtra from 'fs-extra';
|
|
9
9
|
|
|
10
|
-
import { aiComponentGenerator
|
|
10
|
+
import { aiComponentGenerator } from '../services/openAiService.js';
|
|
11
11
|
import componentJsTemplate from '../templates/component/componentJsTemplate.js';
|
|
12
12
|
import componentTsTemplate from '../templates/component/componentTsTemplate.js';
|
|
13
13
|
import componentCssTemplate from '../templates/component/componentCssTemplate.js';
|
|
14
|
+
import componentStyledTemplate from '../templates/component/componentStyledTemplate.js';
|
|
14
15
|
import componentLazyTemplate from '../templates/component/componentLazyTemplate.js';
|
|
15
16
|
import componentTsLazyTemplate from '../templates/component/componentTsLazyTemplate.js';
|
|
16
17
|
import componentStoryTemplate from '../templates/component/componentStoryTemplate.js';
|
|
@@ -18,6 +19,8 @@ import componentTestEnzymeTemplate from '../templates/component/componentTestEnz
|
|
|
18
19
|
import componentTestDefaultTemplate from '../templates/component/componentTestDefaultTemplate.js';
|
|
19
20
|
import componentTestTestingLibraryTemplate from '../templates/component/componentTestTestingLibraryTemplate.js';
|
|
20
21
|
|
|
22
|
+
const templateNameRE = /.*(template[|_-]?name).*/i;
|
|
23
|
+
|
|
21
24
|
const { existsSync, outputFileSync, readFileSync } = fsExtra;
|
|
22
25
|
|
|
23
26
|
export function getComponentByType(args, cliConfigFile) {
|
|
@@ -70,7 +73,7 @@ function getCustomTemplate(componentName, templatePath) {
|
|
|
70
73
|
console.error(
|
|
71
74
|
chalk.red(
|
|
72
75
|
`
|
|
73
|
-
ERROR: The custom template path of "${templatePath}" does not exist.
|
|
76
|
+
ERROR: The custom template path of "${templatePath}" does not exist.
|
|
74
77
|
Please make sure you're pointing to the right custom template path in your generate-react-cli.json config file.
|
|
75
78
|
`
|
|
76
79
|
)
|
|
@@ -80,8 +83,53 @@ Please make sure you're pointing to the right custom template path in your gener
|
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
function
|
|
84
|
-
|
|
86
|
+
function componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, filename, convertors }) {
|
|
87
|
+
let componentPath = cmd.path;
|
|
88
|
+
|
|
89
|
+
if (cmd.flat !== true) {
|
|
90
|
+
let componentDirectory = componentName;
|
|
91
|
+
|
|
92
|
+
const customDirectoryConfigs = [
|
|
93
|
+
cliConfigFile.customDirectory,
|
|
94
|
+
cliConfigFile.component.default.customDirectory,
|
|
95
|
+
cliConfigFile.component[cmd.type].customDirectory,
|
|
96
|
+
cmd.customDirectory,
|
|
97
|
+
].filter((e) => Boolean(e) && typeof e === 'string');
|
|
98
|
+
|
|
99
|
+
if (customDirectoryConfigs.length > 0) {
|
|
100
|
+
const customDirectory = customDirectoryConfigs.slice(-1).toString();
|
|
101
|
+
|
|
102
|
+
// Double check if the customDirectory is templatable
|
|
103
|
+
if (templateNameRE.exec(customDirectory) == null) {
|
|
104
|
+
console.error(
|
|
105
|
+
chalk.red(
|
|
106
|
+
`customDirectory [${customDirectory}] for ${componentName} does not contain a templatable value.\nPlease check your configuration!`
|
|
107
|
+
)
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
process.exit(-2);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
for (const convertor in convertors) {
|
|
114
|
+
const re = new RegExp(`.*${convertor}.*`);
|
|
115
|
+
|
|
116
|
+
if (re.exec(customDirectory) !== null) {
|
|
117
|
+
componentDirectory = customDirectory.replace(convertor, convertors[convertor]);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
componentPath += `/${componentDirectory}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
componentPath += `/${filename}`;
|
|
126
|
+
|
|
127
|
+
return componentPath;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function componentTemplateGenerator({ cmd, componentName, cliConfigFile, convertors }) {
|
|
131
|
+
// @ts-ignore
|
|
132
|
+
const { usesStyledComponents, cssPreprocessor, testLibrary, usesCssModule, usesTypeScript } = cliConfigFile;
|
|
85
133
|
const { customTemplates } = cliConfigFile.component[cmd.type];
|
|
86
134
|
let template = null;
|
|
87
135
|
let filename = null;
|
|
@@ -113,16 +161,27 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile }) {
|
|
|
113
161
|
// --- If it has a corresponding stylesheet
|
|
114
162
|
|
|
115
163
|
if (cmd.withStyle) {
|
|
116
|
-
|
|
117
|
-
|
|
164
|
+
if (cliConfigFile.usesStyledComponents) {
|
|
165
|
+
const cssPath = `${componentName}.styled`;
|
|
166
|
+
template = template.replace(
|
|
167
|
+
`import styles from './TemplateName.module.css'`,
|
|
168
|
+
`import { TemplateNameWrapper } from './${cssPath}'`
|
|
169
|
+
);
|
|
170
|
+
template = template.replace(` className={styles.TemplateName}`, '');
|
|
171
|
+
template = template.replace(` <div`, '<TemplateNameWrapper');
|
|
172
|
+
template = template.replace(` </div>`, '</TemplateNameWrapper>');
|
|
173
|
+
} else {
|
|
174
|
+
const module = usesCssModule ? '.module' : '';
|
|
175
|
+
const cssPath = `${componentName}${module}.${cssPreprocessor}`;
|
|
118
176
|
|
|
119
|
-
|
|
177
|
+
// --- If the css module is true make sure to update the template accordingly
|
|
120
178
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
179
|
+
if (module.length) {
|
|
180
|
+
template = template.replace(`'./TemplateName.module.css'`, `'./${cssPath}'`);
|
|
181
|
+
} else {
|
|
182
|
+
template = template.replace(`{styles.TemplateName}`, `"${componentName}"`);
|
|
183
|
+
template = template.replace(`styles from './TemplateName.module.css'`, `'./${cssPath}'`);
|
|
184
|
+
}
|
|
126
185
|
}
|
|
127
186
|
} else {
|
|
128
187
|
// --- If no stylesheet, remove className attribute and style import from jsTemplate
|
|
@@ -133,13 +192,13 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile }) {
|
|
|
133
192
|
}
|
|
134
193
|
|
|
135
194
|
return {
|
|
136
|
-
componentPath:
|
|
195
|
+
componentPath: componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, filename, convertors }),
|
|
137
196
|
filename,
|
|
138
197
|
template,
|
|
139
198
|
};
|
|
140
199
|
}
|
|
141
200
|
|
|
142
|
-
function componentStyleTemplateGenerator({ cliConfigFile, cmd, componentName }) {
|
|
201
|
+
function componentStyleTemplateGenerator({ cliConfigFile, cmd, componentName, convertors }) {
|
|
143
202
|
const { customTemplates } = cliConfigFile.component[cmd.type];
|
|
144
203
|
let template = null;
|
|
145
204
|
let filename = null;
|
|
@@ -157,24 +216,29 @@ function componentStyleTemplateGenerator({ cliConfigFile, cmd, componentName })
|
|
|
157
216
|
template = customTemplate;
|
|
158
217
|
filename = customTemplateFilename;
|
|
159
218
|
} else {
|
|
160
|
-
const { cssPreprocessor, usesCssModule } = cliConfigFile;
|
|
161
|
-
|
|
162
|
-
|
|
219
|
+
const { usesTypeScript, usesStyledComponents, cssPreprocessor, usesCssModule } = cliConfigFile;
|
|
220
|
+
if (usesStyledComponents) {
|
|
221
|
+
filename = usesTypeScript ? `${componentName}.styled.ts` : `${componentName}.styled.js`;
|
|
222
|
+
template = componentStyledTemplate;
|
|
223
|
+
} else {
|
|
224
|
+
const module = usesCssModule ? '.module' : '';
|
|
225
|
+
const cssFilename = `${componentName}${module}.${cssPreprocessor}`;
|
|
163
226
|
|
|
164
|
-
|
|
227
|
+
// --- Else use GRC built-in style template
|
|
165
228
|
|
|
166
|
-
|
|
167
|
-
|
|
229
|
+
template = componentCssTemplate;
|
|
230
|
+
filename = cssFilename;
|
|
231
|
+
}
|
|
168
232
|
}
|
|
169
233
|
|
|
170
234
|
return {
|
|
171
|
-
componentPath:
|
|
235
|
+
componentPath: componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, filename, convertors }),
|
|
172
236
|
filename,
|
|
173
237
|
template,
|
|
174
238
|
};
|
|
175
239
|
}
|
|
176
240
|
|
|
177
|
-
function componentTestTemplateGenerator({ cliConfigFile, cmd, componentName }) {
|
|
241
|
+
function componentTestTemplateGenerator({ cliConfigFile, cmd, componentName, convertors }) {
|
|
178
242
|
const { customTemplates } = cliConfigFile.component[cmd.type];
|
|
179
243
|
const { testLibrary, usesTypeScript } = cliConfigFile;
|
|
180
244
|
let template = null;
|
|
@@ -207,13 +271,13 @@ function componentTestTemplateGenerator({ cliConfigFile, cmd, componentName }) {
|
|
|
207
271
|
}
|
|
208
272
|
|
|
209
273
|
return {
|
|
210
|
-
componentPath:
|
|
274
|
+
componentPath: componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, filename, convertors }),
|
|
211
275
|
filename,
|
|
212
276
|
template,
|
|
213
277
|
};
|
|
214
278
|
}
|
|
215
279
|
|
|
216
|
-
function componentStoryTemplateGenerator({ cliConfigFile, cmd, componentName }) {
|
|
280
|
+
function componentStoryTemplateGenerator({ cliConfigFile, cmd, componentName, convertors }) {
|
|
217
281
|
const { usesTypeScript } = cliConfigFile;
|
|
218
282
|
const { customTemplates } = cliConfigFile.component[cmd.type];
|
|
219
283
|
let template = null;
|
|
@@ -239,13 +303,13 @@ function componentStoryTemplateGenerator({ cliConfigFile, cmd, componentName })
|
|
|
239
303
|
}
|
|
240
304
|
|
|
241
305
|
return {
|
|
242
|
-
componentPath:
|
|
306
|
+
componentPath: componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, filename, convertors }),
|
|
243
307
|
filename,
|
|
244
308
|
template,
|
|
245
309
|
};
|
|
246
310
|
}
|
|
247
311
|
|
|
248
|
-
function componentLazyTemplateGenerator({ cmd, componentName, cliConfigFile }) {
|
|
312
|
+
function componentLazyTemplateGenerator({ cmd, componentName, cliConfigFile, convertors }) {
|
|
249
313
|
const { usesTypeScript } = cliConfigFile;
|
|
250
314
|
const { customTemplates } = cliConfigFile.component[cmd.type];
|
|
251
315
|
let template = null;
|
|
@@ -271,13 +335,13 @@ function componentLazyTemplateGenerator({ cmd, componentName, cliConfigFile }) {
|
|
|
271
335
|
}
|
|
272
336
|
|
|
273
337
|
return {
|
|
274
|
-
componentPath:
|
|
338
|
+
componentPath: componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, filename, convertors }),
|
|
275
339
|
filename,
|
|
276
340
|
template,
|
|
277
341
|
};
|
|
278
342
|
}
|
|
279
343
|
|
|
280
|
-
function customFileTemplateGenerator({ componentName, cmd, cliConfigFile, componentFileType }) {
|
|
344
|
+
function customFileTemplateGenerator({ componentName, cmd, cliConfigFile, componentFileType, convertors }) {
|
|
281
345
|
const { customTemplates } = cliConfigFile.component[cmd.type];
|
|
282
346
|
const fileType = camelCase(componentFileType.split('with')[1]);
|
|
283
347
|
let filename = null;
|
|
@@ -289,7 +353,7 @@ function customFileTemplateGenerator({ componentName, cmd, cliConfigFile, compon
|
|
|
289
353
|
console.error(
|
|
290
354
|
chalk.red(
|
|
291
355
|
`
|
|
292
|
-
ERROR: Custom component files require a valid custom template.
|
|
356
|
+
ERROR: Custom component files require a valid custom template.
|
|
293
357
|
Please make sure you're pointing to the right custom template path in your generate-react-cli.json config file.
|
|
294
358
|
`
|
|
295
359
|
)
|
|
@@ -309,7 +373,7 @@ Please make sure you're pointing to the right custom template path in your gener
|
|
|
309
373
|
filename = customTemplateFilename;
|
|
310
374
|
|
|
311
375
|
return {
|
|
312
|
-
componentPath:
|
|
376
|
+
componentPath: componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, filename, convertors }),
|
|
313
377
|
filename,
|
|
314
378
|
template,
|
|
315
379
|
};
|
|
@@ -348,11 +412,21 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
348
412
|
) {
|
|
349
413
|
const generateTemplate = componentTemplateGeneratorMap[componentFileType] || customFileTemplateGenerator;
|
|
350
414
|
|
|
415
|
+
const convertors = {
|
|
416
|
+
templatename: componentName,
|
|
417
|
+
TemplateName: startCase(camelCase(componentName)).replace(/ /g, ''),
|
|
418
|
+
templateName: camelCase(componentName),
|
|
419
|
+
'template-name': kebabCase(componentName),
|
|
420
|
+
template_name: snakeCase(componentName),
|
|
421
|
+
TEMPLATE_NAME: snakeCase(componentName).toUpperCase(),
|
|
422
|
+
};
|
|
423
|
+
|
|
351
424
|
const { componentPath, filename, template } = generateTemplate({
|
|
352
425
|
cmd,
|
|
353
426
|
componentName,
|
|
354
427
|
cliConfigFile,
|
|
355
428
|
componentFileType,
|
|
429
|
+
convertors,
|
|
356
430
|
});
|
|
357
431
|
|
|
358
432
|
// --- Make sure the component does not already exist in the path directory.
|
|
@@ -367,7 +441,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
367
441
|
// Will replace the templatename in whichever format the user typed the component name in the command.
|
|
368
442
|
replace({
|
|
369
443
|
regex: 'templatename',
|
|
370
|
-
replacement:
|
|
444
|
+
replacement: convertors['templatename'],
|
|
371
445
|
paths: [componentPath],
|
|
372
446
|
recursive: false,
|
|
373
447
|
silent: true,
|
|
@@ -376,7 +450,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
376
450
|
// Will replace the TemplateName in PascalCase
|
|
377
451
|
replace({
|
|
378
452
|
regex: 'TemplateName',
|
|
379
|
-
replacement:
|
|
453
|
+
replacement: convertors['TemplateName'],
|
|
380
454
|
paths: [componentPath],
|
|
381
455
|
recursive: false,
|
|
382
456
|
silent: true,
|
|
@@ -385,7 +459,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
385
459
|
// Will replace the templateName in camelCase
|
|
386
460
|
replace({
|
|
387
461
|
regex: 'templateName',
|
|
388
|
-
replacement:
|
|
462
|
+
replacement: convertors['templateName'],
|
|
389
463
|
paths: [componentPath],
|
|
390
464
|
recursive: false,
|
|
391
465
|
silent: true,
|
|
@@ -394,7 +468,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
394
468
|
// Will replace the template-name in kebab-case
|
|
395
469
|
replace({
|
|
396
470
|
regex: 'template-name',
|
|
397
|
-
replacement:
|
|
471
|
+
replacement: convertors['template-name'],
|
|
398
472
|
paths: [componentPath],
|
|
399
473
|
recursive: false,
|
|
400
474
|
silent: true,
|
|
@@ -403,7 +477,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
403
477
|
// Will replace the template_name in snake_case
|
|
404
478
|
replace({
|
|
405
479
|
regex: 'template_name',
|
|
406
|
-
replacement:
|
|
480
|
+
replacement: convertors['template_name'],
|
|
407
481
|
paths: [componentPath],
|
|
408
482
|
recursive: false,
|
|
409
483
|
silent: true,
|
|
@@ -412,7 +486,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
412
486
|
// Will replace the TEMPLATE_NAME in uppercase SNAKE_CASE
|
|
413
487
|
replace({
|
|
414
488
|
regex: 'TEMPLATE_NAME',
|
|
415
|
-
replacement:
|
|
489
|
+
replacement: convertors['TEMPLATE_NAME'],
|
|
416
490
|
paths: [componentPath],
|
|
417
491
|
recursive: false,
|
|
418
492
|
silent: true,
|
|
@@ -424,6 +498,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
424
498
|
if (cmd.describe && componentFileType === buildInComponentFileTypes.COMPONENT) {
|
|
425
499
|
aiComponentGenerator(template, cmd.describe)
|
|
426
500
|
.then((aiGeneratedComponent) => {
|
|
501
|
+
// @ts-ignore
|
|
427
502
|
outputFileSync(componentPath, aiGeneratedComponent.trim());
|
|
428
503
|
console.log(
|
|
429
504
|
chalk.green(`OpenAI Successfully created the ${filename} component with the provided description.`)
|
|
@@ -438,30 +513,6 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
438
513
|
return;
|
|
439
514
|
}
|
|
440
515
|
|
|
441
|
-
// Generate component test with openAi, if component description is provided
|
|
442
|
-
|
|
443
|
-
if (cmd.describe && componentFileType === buildInComponentFileTypes.TEST) {
|
|
444
|
-
aiComponentTestGenerator(template, cmd.describe)
|
|
445
|
-
.then((aiGeneratedComponentTest) => {
|
|
446
|
-
outputFileSync(componentPath, aiGeneratedComponentTest.trim());
|
|
447
|
-
console.log(
|
|
448
|
-
chalk.green(
|
|
449
|
-
`OpenAI Successfully created the ${filename} component test with the provided description.`
|
|
450
|
-
)
|
|
451
|
-
);
|
|
452
|
-
})
|
|
453
|
-
.catch((error) =>
|
|
454
|
-
console.log(
|
|
455
|
-
chalk.red(
|
|
456
|
-
`OpenAI failed to create the ${filename} component test with the provided description.`,
|
|
457
|
-
error
|
|
458
|
-
)
|
|
459
|
-
)
|
|
460
|
-
);
|
|
461
|
-
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
516
|
console.log(chalk.green(`${filename} was successfully created at ${componentPath}`));
|
|
466
517
|
} catch (error) {
|
|
467
518
|
console.error(chalk.red(`${filename} failed and was not created.`));
|
|
@@ -20,12 +20,19 @@ const projectLevelQuestions = [
|
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
type: 'confirm',
|
|
23
|
+
name: 'usesStyledComponents',
|
|
24
|
+
message: 'Does this project use styled-components?',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: 'confirm',
|
|
28
|
+
when: (answers) => !answers['usesStyledComponents'],
|
|
23
29
|
name: 'usesCssModule',
|
|
24
30
|
message: 'Does this project use CSS modules?',
|
|
25
31
|
},
|
|
26
32
|
{
|
|
27
33
|
type: 'list',
|
|
28
34
|
name: 'cssPreprocessor',
|
|
35
|
+
when: (answers) => !answers['usesStyledComponents'],
|
|
29
36
|
message: 'Does this project use a CSS Preprocessor?',
|
|
30
37
|
choices: ['css', 'scss', 'less', 'styl'],
|
|
31
38
|
},
|
|
@@ -184,7 +191,9 @@ export async function getCLIConfigFile() {
|
|
|
184
191
|
*/
|
|
185
192
|
|
|
186
193
|
const missingConfigQuestions = grcConfigQuestions.filter(
|
|
187
|
-
(question) =>
|
|
194
|
+
(question) =>
|
|
195
|
+
!deepKeys(currentConfigFile).includes(question.name) &&
|
|
196
|
+
(question.when ? question.when(currentConfigFile) : true)
|
|
188
197
|
);
|
|
189
198
|
|
|
190
199
|
if (missingConfigQuestions.length) {
|