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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "generate-react-cli",
3
- "version": "8.3.0-alpha.1",
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
  [![License](https://img.shields.io/npm/l/express.svg)](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 insted of creating new folder for it
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
- <tr>
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
- }
@@ -0,0 +1,5 @@
1
+ export default `import styled from 'styled-components';
2
+
3
+ export const TemplateNameWrapper = styled.div\`
4
+ \`;
5
+ `;
@@ -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, aiComponentTestGenerator } from '../services/openAiService.js';
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 componentTemplateGenerator({ cmd, componentName, cliConfigFile }) {
84
- const { cssPreprocessor, testLibrary, usesCssModule, usesTypeScript } = cliConfigFile;
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
- const module = usesCssModule ? '.module' : '';
117
- const cssPath = `${componentName}${module}.${cssPreprocessor}`;
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
- // --- If the css module is true make sure to update the template accordingly
177
+ // --- If the css module is true make sure to update the template accordingly
120
178
 
121
- if (module.length) {
122
- template = template.replace(`'./TemplateName.module.css'`, `'./${cssPath}'`);
123
- } else {
124
- template = template.replace(`{styles.TemplateName}`, `"${componentName}"`);
125
- template = template.replace(`styles from './TemplateName.module.css'`, `'./${cssPath}'`);
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: `${cmd.path}${cmd.flat ? '' : `/${componentName}`}/${filename}`,
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
- const module = usesCssModule ? '.module' : '';
162
- const cssFilename = `${componentName}${module}.${cssPreprocessor}`;
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
- // --- Else use GRC built-in style template
227
+ // --- Else use GRC built-in style template
165
228
 
166
- template = componentCssTemplate;
167
- filename = cssFilename;
229
+ template = componentCssTemplate;
230
+ filename = cssFilename;
231
+ }
168
232
  }
169
233
 
170
234
  return {
171
- componentPath: `${cmd.path}${cmd.flat ? '' : `/${componentName}`}/${filename}`,
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: `${cmd.path}${cmd.flat ? '' : `/${componentName}`}/${filename}`,
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: `${cmd.path}${cmd.flat ? '' : `/${componentName}`}/${filename}`,
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: `${cmd.path}${cmd.flat ? '' : `/${componentName}`}/${filename}`,
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: `${cmd.path}${cmd.flat ? '' : `/${componentName}`}/${filename}`,
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: componentName,
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: startCase(camelCase(componentName)).replace(/ /g, ''),
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: camelCase(componentName),
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: kebabCase(componentName),
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: snakeCase(componentName),
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: snakeCase(componentName).toUpperCase(),
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) => !deepKeys(currentConfigFile).includes(question.name)
194
+ (question) =>
195
+ !deepKeys(currentConfigFile).includes(question.name) &&
196
+ (question.when ? question.when(currentConfigFile) : true)
188
197
  );
189
198
 
190
199
  if (missingConfigQuestions.length) {