generate-react-cli 8.3.0 → 8.4.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 +2 -2
- package/package.json +27 -21
- package/readme.md +106 -113
- package/src/commands/generateComponent.js +11 -1
- package/src/utils/generateComponentUtils.js +77 -40
- package/src/services/openAiService.js +0 -18
package/bin/generate-react.js
CHANGED
|
@@ -6,12 +6,12 @@ const isNotValidNodeVersion = () => {
|
|
|
6
6
|
const semver = currentNodeVersion.split('.');
|
|
7
7
|
const major = semver[0];
|
|
8
8
|
|
|
9
|
-
if (major <
|
|
9
|
+
if (major < 18) {
|
|
10
10
|
console.error(
|
|
11
11
|
// eslint-disable-next-line
|
|
12
12
|
'You are running Node ' +
|
|
13
13
|
currentNodeVersion +
|
|
14
|
-
' Generate React CLI requires Node
|
|
14
|
+
' Generate React CLI requires Node 18 or higher. Please update your version of Node.'
|
|
15
15
|
);
|
|
16
16
|
|
|
17
17
|
return true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "generate-react-cli",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.4.1",
|
|
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",
|
|
@@ -38,32 +38,31 @@
|
|
|
38
38
|
"prepare": "husky install"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"chalk": "5.
|
|
42
|
-
"commander": "
|
|
41
|
+
"chalk": "5.3.0",
|
|
42
|
+
"commander": "12.0.0",
|
|
43
43
|
"deep-keys": "0.5.0",
|
|
44
|
-
"dotenv": "
|
|
45
|
-
"fs-extra": "11.
|
|
46
|
-
"inquirer": "9.
|
|
44
|
+
"dotenv": "16.4.5",
|
|
45
|
+
"fs-extra": "11.2.0",
|
|
46
|
+
"inquirer": "9.2.15",
|
|
47
47
|
"lodash": "4.17.21",
|
|
48
|
-
"openai": "^3.1.0",
|
|
49
48
|
"replace": "1.2.2"
|
|
50
49
|
},
|
|
51
50
|
"devDependencies": {
|
|
52
|
-
"@commitlint/cli": "
|
|
53
|
-
"@commitlint/config-conventional": "
|
|
54
|
-
"@semantic-release/commit-analyzer": "
|
|
51
|
+
"@commitlint/cli": "18.6.1",
|
|
52
|
+
"@commitlint/config-conventional": "18.6.2",
|
|
53
|
+
"@semantic-release/commit-analyzer": "11.1.0",
|
|
55
54
|
"@semantic-release/git": "10.0.1",
|
|
56
|
-
"@semantic-release/github": "
|
|
57
|
-
"@semantic-release/npm": "
|
|
58
|
-
"@semantic-release/release-notes-generator": "
|
|
59
|
-
"eslint": "8.
|
|
55
|
+
"@semantic-release/github": "9.2.6",
|
|
56
|
+
"@semantic-release/npm": "11.0.2",
|
|
57
|
+
"@semantic-release/release-notes-generator": "12.1.0",
|
|
58
|
+
"eslint": "8.57.0",
|
|
60
59
|
"eslint-config-airbnb-base": "15.0.0",
|
|
61
|
-
"eslint-config-prettier": "
|
|
62
|
-
"eslint-plugin-prettier": "
|
|
63
|
-
"husky": "
|
|
64
|
-
"prettier": "2.
|
|
65
|
-
"pretty-quick": "
|
|
66
|
-
"semantic-release": "
|
|
60
|
+
"eslint-config-prettier": "9.1.0",
|
|
61
|
+
"eslint-plugin-prettier": "5.1.3",
|
|
62
|
+
"husky": "9.0.11",
|
|
63
|
+
"prettier": "3.2.5",
|
|
64
|
+
"pretty-quick": "4.0.0",
|
|
65
|
+
"semantic-release": "23.0.2"
|
|
67
66
|
},
|
|
68
67
|
"prettier": {
|
|
69
68
|
"singleQuote": true,
|
|
@@ -90,7 +89,14 @@
|
|
|
90
89
|
"commitlint": {
|
|
91
90
|
"extends": [
|
|
92
91
|
"@commitlint/config-conventional"
|
|
93
|
-
]
|
|
92
|
+
],
|
|
93
|
+
"rules": {
|
|
94
|
+
"body-max-line-length": [
|
|
95
|
+
0,
|
|
96
|
+
"always",
|
|
97
|
+
200
|
|
98
|
+
]
|
|
99
|
+
}
|
|
94
100
|
},
|
|
95
101
|
"eslintConfig": {
|
|
96
102
|
"extends": [
|
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,8 +20,9 @@ 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
|
-
- [OpenAi integration (Alpha release)](#openai-integration-alpha-release)
|
|
25
|
+
- [OpenAi integration (Alpha release)](https://github.com/arminbro/generate-react-cli/tree/alpha?tab=readme-ov-file#openai-integration-alpha-release)
|
|
25
26
|
|
|
26
27
|
## You can run it using npx like this:
|
|
27
28
|
|
|
@@ -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,25 +161,28 @@ 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
|
-
|
|
170
|
-
|
|
173
|
+
|
|
174
|
+
<tr>
|
|
175
|
+
<td width="20%"><b>--customDirectory</b></td>
|
|
171
176
|
<td width="60%">
|
|
172
|
-
|
|
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>.
|
|
173
179
|
</td>
|
|
174
180
|
<td width="20%">String</td>
|
|
175
|
-
<td width="20%"><code>null
|
|
181
|
+
<td width="20%"><code>null</code></td>
|
|
176
182
|
</tr>
|
|
177
183
|
</table>
|
|
178
184
|
|
|
179
|
-
### Custom component types
|
|
185
|
+
### Custom component types
|
|
180
186
|
|
|
181
187
|
By default, GRC will use the `component.default` configuration rules when running the component command out of the box.
|
|
182
188
|
|
|
@@ -329,6 +335,93 @@ it('It should mount', () => {
|
|
|
329
335
|
});
|
|
330
336
|
```
|
|
331
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
|
+
|
|
332
425
|
### Custom component files
|
|
333
426
|
|
|
334
427
|
GRC comes with corresponding built-in files for a given component if you need them (i.e., `withStyle`, `withTest`, `withStory`, and `withLazy`).
|
|
@@ -384,106 +477,6 @@ You should also see that we added `index` and `storyStyle` to our `customTemplat
|
|
|
384
477
|
|
|
385
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.
|
|
386
479
|
|
|
387
|
-
## OpenAI integration (Alpha release)
|
|
388
|
-
|
|
389
|
-
Well, the time has come to incorporate OpenAI with GRC.
|
|
390
|
-
|
|
391
|
-
I had a chance to experiment with OpenAI's latest GPT-3 natural language model, and I was super impressed by its capabilities. You can read more about OpenAI by visiting their site: https://openai.com/.
|
|
392
|
-
|
|
393
|
-
If you've been using GRC, you already know about its component generation capabilities using its internal templates or the custom ones you provide.
|
|
394
|
-
|
|
395
|
-
With the help of OpenAI, we can now generate our components intelligently by describing them using the new `--describe` flag, or `-d` for short.
|
|
396
|
-
|
|
397
|
-
The plan for this alpha integration will start simple, but I have a few good ideas coming in the near future (and I'm hoping to hear some of yours) on how we can use OpenAI with GRC to improve the developer experience.
|
|
398
|
-
|
|
399
|
-
Please remember that this release is still early, and you will run into bugs. So, please report any bugs or issues [here](https://github.com/arminbro/generate-react-cli/issues).
|
|
400
|
-
|
|
401
|
-
### Okay, let's get started.
|
|
402
|
-
|
|
403
|
-
1. If you don't have one, you must create an OpenAI account: https://openai.com/api/.
|
|
404
|
-
2. You'll need to obtain a secret API key from OpenAI. You can do so by visiting https://beta.openai.com/account/api-keys.
|
|
405
|
-
3. Once you have your API key, you'll need to create a `.env.local` file in your react project and store it as a variable as `OPENAI_API_KEY`. Please be sure not to share the key or push it to version control, as it is private.
|
|
406
|
-
|
|
407
|
-
GRC will pass the key to OpenAI to communicate with the API on your behalf. You will see the usage reflected on your OpenAI account here: https://beta.openai.com/account/usage.
|
|
408
|
-
|
|
409
|
-
GRC uses the DaVinci language model, so you can check out their pricing here: https://openai.com/api/pricing/.
|
|
410
|
-
|
|
411
|
-
Let's generate our first component using OpenAI:
|
|
412
|
-
|
|
413
|
-
```sh
|
|
414
|
-
npx generate-react-cli@alpha c Counter -d "Create a counter component that increments by one when I click on the increment button"
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
GRC should have created a Counter component that looks something like this 🤯:
|
|
418
|
-
|
|
419
|
-
```jsx
|
|
420
|
-
import React, { useState } from 'react';
|
|
421
|
-
import PropTypes from 'prop-types';
|
|
422
|
-
import './Counter.css';
|
|
423
|
-
|
|
424
|
-
const Counter = () => {
|
|
425
|
-
const [count, setCount] = useState(0);
|
|
426
|
-
|
|
427
|
-
return (
|
|
428
|
-
<div className="Counter" data-testid="Counter">
|
|
429
|
-
<h2> The count is: {count} </h2>
|
|
430
|
-
<button onClick={() => setCount(count + 1)}>Increment</button>
|
|
431
|
-
</div>
|
|
432
|
-
);
|
|
433
|
-
};
|
|
434
|
-
|
|
435
|
-
Counter.propTypes = {};
|
|
436
|
-
|
|
437
|
-
Counter.defaultProps = {};
|
|
438
|
-
|
|
439
|
-
export default Counter;
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
OpenAI will do its best to generate the component following the instructions provided in the `--describe` flag while using the patterns supplied from the internal or custom component templates.
|
|
443
|
-
|
|
444
|
-
Okay, let's try another one.
|
|
445
|
-
|
|
446
|
-
```sh
|
|
447
|
-
npx generate-react-cli@alpha c GlobalNav -d "Create a navbar component with 1 logo named 'GRC' and 3 links: 'Home', 'About', 'Contact'"
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
and here's the output in src/components/GlobalNav/GlobalNav.js:
|
|
451
|
-
|
|
452
|
-
```jsx
|
|
453
|
-
import React from 'react';
|
|
454
|
-
import PropTypes from 'prop-types';
|
|
455
|
-
import './GlobalNav.css';
|
|
456
|
-
|
|
457
|
-
const GlobalNav = () => (
|
|
458
|
-
<div className="GlobalNav" data-testid="GlobalNav">
|
|
459
|
-
<a href="#">GRC</a>
|
|
460
|
-
<ul>
|
|
461
|
-
<li>
|
|
462
|
-
<a href="#">Home</a>
|
|
463
|
-
</li>
|
|
464
|
-
<li>
|
|
465
|
-
<a href="#">About</a>
|
|
466
|
-
</li>
|
|
467
|
-
<li>
|
|
468
|
-
<a href="#">Contact</a>
|
|
469
|
-
</li>
|
|
470
|
-
</ul>
|
|
471
|
-
</div>
|
|
472
|
-
);
|
|
473
|
-
|
|
474
|
-
GlobalNav.propTypes = {};
|
|
475
|
-
|
|
476
|
-
GlobalNav.defaultProps = {};
|
|
477
|
-
|
|
478
|
-
export default GlobalNav;
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
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.
|
|
482
|
-
|
|
483
|
-
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.
|
|
484
|
-
|
|
485
|
-
Please share them with me on Twitter [@arminbro](https://twitter.com/arminbro).
|
|
486
|
-
|
|
487
480
|
## License
|
|
488
481
|
|
|
489
482
|
Generate React CLI is an open source software [licensed as MIT](https://github.com/arminbro/generate-react-cli/blob/master/LICENSE).
|
|
@@ -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
|
|
|
@@ -7,7 +7,6 @@ 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 } from '../services/openAiService.js';
|
|
11
10
|
import componentJsTemplate from '../templates/component/componentJsTemplate.js';
|
|
12
11
|
import componentTsTemplate from '../templates/component/componentTsTemplate.js';
|
|
13
12
|
import componentCssTemplate from '../templates/component/componentCssTemplate.js';
|
|
@@ -19,6 +18,8 @@ import componentTestEnzymeTemplate from '../templates/component/componentTestEnz
|
|
|
19
18
|
import componentTestDefaultTemplate from '../templates/component/componentTestDefaultTemplate.js';
|
|
20
19
|
import componentTestTestingLibraryTemplate from '../templates/component/componentTestTestingLibraryTemplate.js';
|
|
21
20
|
|
|
21
|
+
const templateNameRE = /.*(template[|_-]?name).*/i;
|
|
22
|
+
|
|
22
23
|
const { existsSync, outputFileSync, readFileSync } = fsExtra;
|
|
23
24
|
|
|
24
25
|
export function getComponentByType(args, cliConfigFile) {
|
|
@@ -71,7 +72,7 @@ function getCustomTemplate(componentName, templatePath) {
|
|
|
71
72
|
console.error(
|
|
72
73
|
chalk.red(
|
|
73
74
|
`
|
|
74
|
-
ERROR: The custom template path of "${templatePath}" does not exist.
|
|
75
|
+
ERROR: The custom template path of "${templatePath}" does not exist.
|
|
75
76
|
Please make sure you're pointing to the right custom template path in your generate-react-cli.json config file.
|
|
76
77
|
`
|
|
77
78
|
)
|
|
@@ -81,7 +82,52 @@ Please make sure you're pointing to the right custom template path in your gener
|
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
function
|
|
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
|
|
85
131
|
const { usesStyledComponents, cssPreprocessor, testLibrary, usesCssModule, usesTypeScript } = cliConfigFile;
|
|
86
132
|
const { customTemplates } = cliConfigFile.component[cmd.type];
|
|
87
133
|
let template = null;
|
|
@@ -145,13 +191,13 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile }) {
|
|
|
145
191
|
}
|
|
146
192
|
|
|
147
193
|
return {
|
|
148
|
-
componentPath:
|
|
194
|
+
componentPath: componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, filename, convertors }),
|
|
149
195
|
filename,
|
|
150
196
|
template,
|
|
151
197
|
};
|
|
152
198
|
}
|
|
153
199
|
|
|
154
|
-
function componentStyleTemplateGenerator({ cliConfigFile, cmd, componentName }) {
|
|
200
|
+
function componentStyleTemplateGenerator({ cliConfigFile, cmd, componentName, convertors }) {
|
|
155
201
|
const { customTemplates } = cliConfigFile.component[cmd.type];
|
|
156
202
|
let template = null;
|
|
157
203
|
let filename = null;
|
|
@@ -185,13 +231,13 @@ function componentStyleTemplateGenerator({ cliConfigFile, cmd, componentName })
|
|
|
185
231
|
}
|
|
186
232
|
|
|
187
233
|
return {
|
|
188
|
-
componentPath:
|
|
234
|
+
componentPath: componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, filename, convertors }),
|
|
189
235
|
filename,
|
|
190
236
|
template,
|
|
191
237
|
};
|
|
192
238
|
}
|
|
193
239
|
|
|
194
|
-
function componentTestTemplateGenerator({ cliConfigFile, cmd, componentName }) {
|
|
240
|
+
function componentTestTemplateGenerator({ cliConfigFile, cmd, componentName, convertors }) {
|
|
195
241
|
const { customTemplates } = cliConfigFile.component[cmd.type];
|
|
196
242
|
const { testLibrary, usesTypeScript } = cliConfigFile;
|
|
197
243
|
let template = null;
|
|
@@ -224,13 +270,13 @@ function componentTestTemplateGenerator({ cliConfigFile, cmd, componentName }) {
|
|
|
224
270
|
}
|
|
225
271
|
|
|
226
272
|
return {
|
|
227
|
-
componentPath:
|
|
273
|
+
componentPath: componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, filename, convertors }),
|
|
228
274
|
filename,
|
|
229
275
|
template,
|
|
230
276
|
};
|
|
231
277
|
}
|
|
232
278
|
|
|
233
|
-
function componentStoryTemplateGenerator({ cliConfigFile, cmd, componentName }) {
|
|
279
|
+
function componentStoryTemplateGenerator({ cliConfigFile, cmd, componentName, convertors }) {
|
|
234
280
|
const { usesTypeScript } = cliConfigFile;
|
|
235
281
|
const { customTemplates } = cliConfigFile.component[cmd.type];
|
|
236
282
|
let template = null;
|
|
@@ -256,13 +302,13 @@ function componentStoryTemplateGenerator({ cliConfigFile, cmd, componentName })
|
|
|
256
302
|
}
|
|
257
303
|
|
|
258
304
|
return {
|
|
259
|
-
componentPath:
|
|
305
|
+
componentPath: componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, filename, convertors }),
|
|
260
306
|
filename,
|
|
261
307
|
template,
|
|
262
308
|
};
|
|
263
309
|
}
|
|
264
310
|
|
|
265
|
-
function componentLazyTemplateGenerator({ cmd, componentName, cliConfigFile }) {
|
|
311
|
+
function componentLazyTemplateGenerator({ cmd, componentName, cliConfigFile, convertors }) {
|
|
266
312
|
const { usesTypeScript } = cliConfigFile;
|
|
267
313
|
const { customTemplates } = cliConfigFile.component[cmd.type];
|
|
268
314
|
let template = null;
|
|
@@ -288,13 +334,13 @@ function componentLazyTemplateGenerator({ cmd, componentName, cliConfigFile }) {
|
|
|
288
334
|
}
|
|
289
335
|
|
|
290
336
|
return {
|
|
291
|
-
componentPath:
|
|
337
|
+
componentPath: componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, filename, convertors }),
|
|
292
338
|
filename,
|
|
293
339
|
template,
|
|
294
340
|
};
|
|
295
341
|
}
|
|
296
342
|
|
|
297
|
-
function customFileTemplateGenerator({ componentName, cmd, cliConfigFile, componentFileType }) {
|
|
343
|
+
function customFileTemplateGenerator({ componentName, cmd, cliConfigFile, componentFileType, convertors }) {
|
|
298
344
|
const { customTemplates } = cliConfigFile.component[cmd.type];
|
|
299
345
|
const fileType = camelCase(componentFileType.split('with')[1]);
|
|
300
346
|
let filename = null;
|
|
@@ -306,7 +352,7 @@ function customFileTemplateGenerator({ componentName, cmd, cliConfigFile, compon
|
|
|
306
352
|
console.error(
|
|
307
353
|
chalk.red(
|
|
308
354
|
`
|
|
309
|
-
ERROR: Custom component files require a valid custom template.
|
|
355
|
+
ERROR: Custom component files require a valid custom template.
|
|
310
356
|
Please make sure you're pointing to the right custom template path in your generate-react-cli.json config file.
|
|
311
357
|
`
|
|
312
358
|
)
|
|
@@ -326,7 +372,7 @@ Please make sure you're pointing to the right custom template path in your gener
|
|
|
326
372
|
filename = customTemplateFilename;
|
|
327
373
|
|
|
328
374
|
return {
|
|
329
|
-
componentPath:
|
|
375
|
+
componentPath: componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, filename, convertors }),
|
|
330
376
|
filename,
|
|
331
377
|
template,
|
|
332
378
|
};
|
|
@@ -365,11 +411,21 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
365
411
|
) {
|
|
366
412
|
const generateTemplate = componentTemplateGeneratorMap[componentFileType] || customFileTemplateGenerator;
|
|
367
413
|
|
|
414
|
+
const convertors = {
|
|
415
|
+
templatename: componentName,
|
|
416
|
+
TemplateName: startCase(camelCase(componentName)).replace(/ /g, ''),
|
|
417
|
+
templateName: camelCase(componentName),
|
|
418
|
+
'template-name': kebabCase(componentName),
|
|
419
|
+
template_name: snakeCase(componentName),
|
|
420
|
+
TEMPLATE_NAME: snakeCase(componentName).toUpperCase(),
|
|
421
|
+
};
|
|
422
|
+
|
|
368
423
|
const { componentPath, filename, template } = generateTemplate({
|
|
369
424
|
cmd,
|
|
370
425
|
componentName,
|
|
371
426
|
cliConfigFile,
|
|
372
427
|
componentFileType,
|
|
428
|
+
convertors,
|
|
373
429
|
});
|
|
374
430
|
|
|
375
431
|
// --- Make sure the component does not already exist in the path directory.
|
|
@@ -384,7 +440,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
384
440
|
// Will replace the templatename in whichever format the user typed the component name in the command.
|
|
385
441
|
replace({
|
|
386
442
|
regex: 'templatename',
|
|
387
|
-
replacement:
|
|
443
|
+
replacement: convertors['templatename'],
|
|
388
444
|
paths: [componentPath],
|
|
389
445
|
recursive: false,
|
|
390
446
|
silent: true,
|
|
@@ -393,7 +449,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
393
449
|
// Will replace the TemplateName in PascalCase
|
|
394
450
|
replace({
|
|
395
451
|
regex: 'TemplateName',
|
|
396
|
-
replacement:
|
|
452
|
+
replacement: convertors['TemplateName'],
|
|
397
453
|
paths: [componentPath],
|
|
398
454
|
recursive: false,
|
|
399
455
|
silent: true,
|
|
@@ -402,7 +458,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
402
458
|
// Will replace the templateName in camelCase
|
|
403
459
|
replace({
|
|
404
460
|
regex: 'templateName',
|
|
405
|
-
replacement:
|
|
461
|
+
replacement: convertors['templateName'],
|
|
406
462
|
paths: [componentPath],
|
|
407
463
|
recursive: false,
|
|
408
464
|
silent: true,
|
|
@@ -411,7 +467,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
411
467
|
// Will replace the template-name in kebab-case
|
|
412
468
|
replace({
|
|
413
469
|
regex: 'template-name',
|
|
414
|
-
replacement:
|
|
470
|
+
replacement: convertors['template-name'],
|
|
415
471
|
paths: [componentPath],
|
|
416
472
|
recursive: false,
|
|
417
473
|
silent: true,
|
|
@@ -420,7 +476,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
420
476
|
// Will replace the template_name in snake_case
|
|
421
477
|
replace({
|
|
422
478
|
regex: 'template_name',
|
|
423
|
-
replacement:
|
|
479
|
+
replacement: convertors['template_name'],
|
|
424
480
|
paths: [componentPath],
|
|
425
481
|
recursive: false,
|
|
426
482
|
silent: true,
|
|
@@ -429,32 +485,13 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
429
485
|
// Will replace the TEMPLATE_NAME in uppercase SNAKE_CASE
|
|
430
486
|
replace({
|
|
431
487
|
regex: 'TEMPLATE_NAME',
|
|
432
|
-
replacement:
|
|
488
|
+
replacement: convertors['TEMPLATE_NAME'],
|
|
433
489
|
paths: [componentPath],
|
|
434
490
|
recursive: false,
|
|
435
491
|
silent: true,
|
|
436
492
|
});
|
|
437
493
|
}
|
|
438
494
|
|
|
439
|
-
// Generate component with openAi, if component description is provided
|
|
440
|
-
|
|
441
|
-
if (cmd.describe && componentFileType === buildInComponentFileTypes.COMPONENT) {
|
|
442
|
-
aiComponentGenerator(template, cmd.describe)
|
|
443
|
-
.then((aiGeneratedComponent) => {
|
|
444
|
-
outputFileSync(componentPath, aiGeneratedComponent.trim());
|
|
445
|
-
console.log(
|
|
446
|
-
chalk.green(`OpenAI Successfully created the ${filename} component with the provided description.`)
|
|
447
|
-
);
|
|
448
|
-
})
|
|
449
|
-
.catch((error) =>
|
|
450
|
-
console.log(
|
|
451
|
-
chalk.red(`OpenAI failed to create the ${filename} component with the provided description.`, error)
|
|
452
|
-
)
|
|
453
|
-
);
|
|
454
|
-
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
495
|
console.log(chalk.green(`${filename} was successfully created at ${componentPath}`));
|
|
459
496
|
} catch (error) {
|
|
460
497
|
console.error(chalk.red(`${filename} failed and was not created.`));
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Configuration, OpenAIApi } from 'openai';
|
|
2
|
-
|
|
3
|
-
export async function aiComponentGenerator(componentTemplate, prompt) {
|
|
4
|
-
const configuration = new Configuration({ apiKey: process.env.OPENAI_API_KEY });
|
|
5
|
-
const openAiApi = new OpenAIApi(configuration);
|
|
6
|
-
|
|
7
|
-
const generatedComponent = await openAiApi.createCompletion({
|
|
8
|
-
model: 'text-davinci-003',
|
|
9
|
-
prompt: `Create a React component using this template "${componentTemplate}", but make the adjustments needed with these instructions as follows "${prompt}"`,
|
|
10
|
-
temperature: 0.7,
|
|
11
|
-
max_tokens: 2000,
|
|
12
|
-
top_p: 1.0,
|
|
13
|
-
frequency_penalty: 0.0,
|
|
14
|
-
presence_penalty: 1,
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
return generatedComponent.data.choices[0].text;
|
|
18
|
-
}
|