generate-react-cli 10.1.0 → 11.0.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/bin/generate-react.js +0 -1
- package/package.json +1 -1
- package/readme.md +13 -8
- package/src/templates/component/componentJsTemplate.js +1 -7
- package/src/templates/component/componentLazyTemplate.js +2 -2
- package/src/templates/component/componentStoryTemplate.js +4 -8
- package/src/templates/component/componentTestDefaultTemplate.js +6 -6
- package/src/templates/component/componentTestTestingLibraryTemplate.js +2 -3
- package/src/templates/component/componentTestVitestTemplate.js +15 -0
- package/src/templates/component/componentTsLazyTemplate.js +2 -2
- package/src/templates/component/componentTsTemplate.js +1 -1
- package/src/utils/generateComponentUtils.js +36 -103
- package/src/utils/grcConfigUtils.js +8 -9
- package/src/utils/messagesUtils.js +15 -4
- package/src/templates/component/componentTestEnzymeTemplate.js +0 -16
package/bin/generate-react.js
CHANGED
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -55,6 +55,12 @@ When you run GRC within your project the first time, it will ask you a series of
|
|
|
55
55
|
}
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
#### Test library options:
|
|
59
|
+
|
|
60
|
+
- `Testing Library` - Generates tests using [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)
|
|
61
|
+
- `Vitest` - Generates tests using [Vitest](https://vitest.dev/) with React Testing Library
|
|
62
|
+
- `None` - Generates basic tests using React's createRoot API
|
|
63
|
+
|
|
58
64
|
## Generate Components
|
|
59
65
|
|
|
60
66
|
```sh
|
|
@@ -119,7 +125,7 @@ Otherwise, if you don't pass any options, it will just use the default values th
|
|
|
119
125
|
<tr>
|
|
120
126
|
<td width="20%"><b>--withLazy</b></td>
|
|
121
127
|
<td width="60%">
|
|
122
|
-
Creates a corresponding lazy file (a file that lazy-loads your component out of the box and enables <a href="https://
|
|
128
|
+
Creates a corresponding lazy file (a file that lazy-loads your component out of the box and enables <a href="https://react.dev/reference/react/lazy">code splitting</a>) with this component.
|
|
123
129
|
</td>
|
|
124
130
|
<td width="20%">Boolean</td>
|
|
125
131
|
<td width="20%"><code>component.default.withLazy<code></td>
|
|
@@ -295,7 +301,6 @@ Notice in the `page.customTemplates` that we only specified the `test` custom te
|
|
|
295
301
|
```jsx
|
|
296
302
|
// templates/component/TemplateName.js
|
|
297
303
|
|
|
298
|
-
import React from 'react';
|
|
299
304
|
import styles from './TemplateName.module.css';
|
|
300
305
|
|
|
301
306
|
const TemplateName = () => (
|
|
@@ -324,14 +329,14 @@ export default TemplateName;
|
|
|
324
329
|
```jsx
|
|
325
330
|
// templates/component/TemplateName.test.js
|
|
326
331
|
|
|
327
|
-
import
|
|
328
|
-
import ReactDOM from 'react-dom';
|
|
332
|
+
import { createRoot } from 'react-dom/client';
|
|
329
333
|
import TemplateName from './TemplateName';
|
|
330
334
|
|
|
331
|
-
it('
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
+
it('should mount', () => {
|
|
336
|
+
const container = document.createElement('div');
|
|
337
|
+
const root = createRoot(container);
|
|
338
|
+
root.render(<TemplateName />);
|
|
339
|
+
root.unmount();
|
|
335
340
|
});
|
|
336
341
|
```
|
|
337
342
|
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
export default `import
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import styles from './templatename.module.css';
|
|
1
|
+
export default `import styles from './templatename.module.css';
|
|
4
2
|
|
|
5
3
|
const templatename = () => (
|
|
6
4
|
<div className={styles.templatename} data-testid="templatename">
|
|
@@ -8,9 +6,5 @@ const templatename = () => (
|
|
|
8
6
|
</div>
|
|
9
7
|
);
|
|
10
8
|
|
|
11
|
-
templatename.propTypes = {};
|
|
12
|
-
|
|
13
|
-
templatename.defaultProps = {};
|
|
14
|
-
|
|
15
9
|
export default templatename;
|
|
16
10
|
`;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export default `import
|
|
1
|
+
export default `import { lazy, Suspense } from 'react';
|
|
2
2
|
|
|
3
3
|
const Lazytemplatename = lazy(() => import('./templatename'));
|
|
4
4
|
|
|
5
|
-
const templatename = props => (
|
|
5
|
+
const templatename = (props) => (
|
|
6
6
|
<Suspense fallback={null}>
|
|
7
7
|
<Lazytemplatename {...props} />
|
|
8
8
|
</Suspense>
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
export default
|
|
2
|
-
import templatename from './templatename';
|
|
1
|
+
export default `import templatename from './templatename';
|
|
3
2
|
|
|
4
3
|
export default {
|
|
5
|
-
title:
|
|
4
|
+
title: 'templatename',
|
|
5
|
+
component: templatename,
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
export const Default =
|
|
9
|
-
|
|
10
|
-
Default.story = {
|
|
11
|
-
name: 'default',
|
|
12
|
-
};
|
|
8
|
+
export const Default = {};
|
|
13
9
|
`;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export default `import
|
|
2
|
-
import ReactDOM from 'react-dom';
|
|
1
|
+
export default `import { createRoot } from 'react-dom/client';
|
|
3
2
|
import templatename from './templatename';
|
|
4
3
|
|
|
5
|
-
it('
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
it('should mount', () => {
|
|
5
|
+
const container = document.createElement('div');
|
|
6
|
+
const root = createRoot(container);
|
|
7
|
+
root.render(<templatename />);
|
|
8
|
+
root.unmount();
|
|
9
9
|
});`;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
export default `import
|
|
2
|
-
import { render, screen } from '@testing-library/react';
|
|
1
|
+
export default `import { render, screen } from '@testing-library/react';
|
|
3
2
|
import '@testing-library/jest-dom';
|
|
4
3
|
import templatename from './templatename';
|
|
5
4
|
|
|
6
5
|
describe('<templatename />', () => {
|
|
7
|
-
test('
|
|
6
|
+
test('should mount', () => {
|
|
8
7
|
render(<templatename />);
|
|
9
8
|
|
|
10
9
|
const templateName = screen.getByTestId('templatename');
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default `import { describe, test, expect } from 'vitest';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom/vitest';
|
|
4
|
+
import templatename from './templatename';
|
|
5
|
+
|
|
6
|
+
describe('<templatename />', () => {
|
|
7
|
+
test('should mount', () => {
|
|
8
|
+
render(<templatename />);
|
|
9
|
+
|
|
10
|
+
const templateName = screen.getByTestId('templatename');
|
|
11
|
+
|
|
12
|
+
expect(templateName).toBeInTheDocument();
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
`;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export default `import
|
|
1
|
+
export default `import { lazy, Suspense, ComponentProps } from 'react';
|
|
2
2
|
|
|
3
3
|
const Lazytemplatename = lazy(() => import('./templatename'));
|
|
4
4
|
|
|
5
|
-
const templatename = (props:
|
|
5
|
+
const templatename = (props: ComponentProps<typeof Lazytemplatename>) => (
|
|
6
6
|
<Suspense fallback={null}>
|
|
7
7
|
<Lazytemplatename {...props} />
|
|
8
8
|
</Suspense>
|
|
@@ -12,13 +12,13 @@ import componentLazyTemplate from '../templates/component/componentLazyTemplate.
|
|
|
12
12
|
import componentStoryTemplate from '../templates/component/componentStoryTemplate.js';
|
|
13
13
|
import componentStyledTemplate from '../templates/component/componentStyledTemplate.js';
|
|
14
14
|
import componentTestDefaultTemplate from '../templates/component/componentTestDefaultTemplate.js';
|
|
15
|
-
import componentTestEnzymeTemplate from '../templates/component/componentTestEnzymeTemplate.js';
|
|
16
15
|
import componentTestTestingLibraryTemplate from '../templates/component/componentTestTestingLibraryTemplate.js';
|
|
16
|
+
import componentTestVitestTemplate from '../templates/component/componentTestVitestTemplate.js';
|
|
17
17
|
import componentTsLazyTemplate from '../templates/component/componentTsLazyTemplate.js';
|
|
18
18
|
import componentTsTemplate from '../templates/component/componentTsTemplate.js';
|
|
19
|
-
import { error, fileSummary } from './messagesUtils.js';
|
|
19
|
+
import { error, exitWithError, fileSummary } from './messagesUtils.js';
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const TEMPLATE_NAME_REGEX = /template[-_]?name/i;
|
|
22
22
|
|
|
23
23
|
const { existsSync, outputFileSync, readFileSync } = fsExtra;
|
|
24
24
|
|
|
@@ -35,15 +35,13 @@ export function getComponentByType(args, cliConfigFile) {
|
|
|
35
35
|
|
|
36
36
|
if (!selectedComponentType) {
|
|
37
37
|
const availableTypes = Object.keys(cliConfigFile.component).join(', ');
|
|
38
|
-
|
|
38
|
+
exitWithError(`Unknown component type "${componentType}"`, {
|
|
39
39
|
details: `Available types: ${availableTypes}`,
|
|
40
40
|
suggestions: [
|
|
41
41
|
`Use one of the available types: ${availableTypes}`,
|
|
42
42
|
'Add this component type to your generate-react-cli.json config',
|
|
43
43
|
],
|
|
44
44
|
});
|
|
45
|
-
|
|
46
|
-
process.exit(1);
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
// Otherwise return it.
|
|
@@ -61,7 +59,7 @@ export function getCorrespondingComponentFileTypes(component) {
|
|
|
61
59
|
}
|
|
62
60
|
|
|
63
61
|
function getCustomTemplate(componentName, templatePath) {
|
|
64
|
-
//
|
|
62
|
+
// Try loading custom template
|
|
65
63
|
|
|
66
64
|
try {
|
|
67
65
|
const template = readFileSync(templatePath, 'utf8');
|
|
@@ -69,15 +67,13 @@ function getCustomTemplate(componentName, templatePath) {
|
|
|
69
67
|
|
|
70
68
|
return { template, filename };
|
|
71
69
|
} catch {
|
|
72
|
-
|
|
70
|
+
exitWithError(`Custom template not found: "${templatePath}"`, {
|
|
73
71
|
suggestions: [
|
|
74
72
|
'Verify the template path in your generate-react-cli.json config',
|
|
75
73
|
'Check that the file exists and is readable',
|
|
76
74
|
'Use an absolute path or a path relative to project root',
|
|
77
75
|
],
|
|
78
76
|
});
|
|
79
|
-
|
|
80
|
-
return process.exit(1);
|
|
81
77
|
}
|
|
82
78
|
}
|
|
83
79
|
|
|
@@ -97,17 +93,15 @@ function componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, fi
|
|
|
97
93
|
if (customDirectoryConfigs.length > 0) {
|
|
98
94
|
const customDirectory = customDirectoryConfigs.slice(-1).toString();
|
|
99
95
|
|
|
100
|
-
//
|
|
101
|
-
if (
|
|
102
|
-
|
|
96
|
+
// Check if the customDirectory contains a template placeholder
|
|
97
|
+
if (!TEMPLATE_NAME_REGEX.test(customDirectory)) {
|
|
98
|
+
exitWithError(`Invalid customDirectory: "${customDirectory}"`, {
|
|
103
99
|
details: 'customDirectory must contain a template placeholder',
|
|
104
100
|
suggestions: [
|
|
105
101
|
'Use templatename, TemplateName, template-name, or template_name',
|
|
106
102
|
'Example: "{{templatename}}" or "TemplateName"',
|
|
107
103
|
],
|
|
108
104
|
});
|
|
109
|
-
|
|
110
|
-
process.exit(-2);
|
|
111
105
|
}
|
|
112
106
|
|
|
113
107
|
for (const convertor in convertors) {
|
|
@@ -137,7 +131,7 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile, convert
|
|
|
137
131
|
// Check for a custom component template.
|
|
138
132
|
|
|
139
133
|
if (customTemplates && customTemplates.component) {
|
|
140
|
-
//
|
|
134
|
+
// Load and use the custom component template
|
|
141
135
|
|
|
142
136
|
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
|
|
143
137
|
componentName,
|
|
@@ -147,18 +141,19 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile, convert
|
|
|
147
141
|
template = customTemplate;
|
|
148
142
|
filename = customTemplateFilename;
|
|
149
143
|
} else {
|
|
150
|
-
//
|
|
144
|
+
// Else use GRC built-in component template
|
|
151
145
|
|
|
152
146
|
template = usesTypeScript ? componentTsTemplate : componentJsTemplate;
|
|
153
147
|
filename = usesTypeScript ? `${componentName}.tsx` : `${componentName}.js`;
|
|
154
148
|
|
|
155
|
-
//
|
|
149
|
+
// If test library doesn't use data-testid or if withTest is false. Remove data-testid from template
|
|
156
150
|
|
|
157
|
-
|
|
151
|
+
const usesTestId = testLibrary === 'Testing Library' || testLibrary === 'Vitest';
|
|
152
|
+
if (!usesTestId || !cmd.withTest) {
|
|
158
153
|
template = template.replace(` data-testid="templatename"`, '');
|
|
159
154
|
}
|
|
160
155
|
|
|
161
|
-
//
|
|
156
|
+
// If it has a corresponding stylesheet
|
|
162
157
|
|
|
163
158
|
if (cmd.withStyle) {
|
|
164
159
|
if (cliConfigFile.usesStyledComponents) {
|
|
@@ -174,7 +169,7 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile, convert
|
|
|
174
169
|
const module = usesCssModule ? '.module' : '';
|
|
175
170
|
const cssPath = `${componentName}${module}.${cssPreprocessor}`;
|
|
176
171
|
|
|
177
|
-
//
|
|
172
|
+
// If the css module is true make sure to update the template accordingly
|
|
178
173
|
|
|
179
174
|
if (module.length) {
|
|
180
175
|
template = template.replace(`'./templatename.module.css'`, `'./${cssPath}'`);
|
|
@@ -184,7 +179,7 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile, convert
|
|
|
184
179
|
}
|
|
185
180
|
}
|
|
186
181
|
} else {
|
|
187
|
-
//
|
|
182
|
+
// If no stylesheet, remove className attribute and style import from jsTemplate
|
|
188
183
|
|
|
189
184
|
template = template.replace(` className={styles.templatename}`, '');
|
|
190
185
|
template = template.replace(`import styles from './templatename.module.css';`, '');
|
|
@@ -206,7 +201,7 @@ function componentStyleTemplateGenerator({ cliConfigFile, cmd, componentName, co
|
|
|
206
201
|
// Check for a custom style template.
|
|
207
202
|
|
|
208
203
|
if (customTemplates && customTemplates.style) {
|
|
209
|
-
//
|
|
204
|
+
// Load and use the custom style template
|
|
210
205
|
|
|
211
206
|
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
|
|
212
207
|
componentName,
|
|
@@ -224,7 +219,7 @@ function componentStyleTemplateGenerator({ cliConfigFile, cmd, componentName, co
|
|
|
224
219
|
const module = usesCssModule ? '.module' : '';
|
|
225
220
|
const cssFilename = `${componentName}${module}.${cssPreprocessor}`;
|
|
226
221
|
|
|
227
|
-
//
|
|
222
|
+
// Else use GRC built-in style template
|
|
228
223
|
|
|
229
224
|
template = componentCssTemplate;
|
|
230
225
|
filename = cssFilename;
|
|
@@ -247,7 +242,7 @@ function componentTestTemplateGenerator({ cliConfigFile, cmd, componentName, con
|
|
|
247
242
|
// Check for a custom test template.
|
|
248
243
|
|
|
249
244
|
if (customTemplates && customTemplates.test) {
|
|
250
|
-
//
|
|
245
|
+
// Load and use the custom test template
|
|
251
246
|
|
|
252
247
|
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
|
|
253
248
|
componentName,
|
|
@@ -259,12 +254,10 @@ function componentTestTemplateGenerator({ cliConfigFile, cmd, componentName, con
|
|
|
259
254
|
} else {
|
|
260
255
|
filename = usesTypeScript ? `${componentName}.test.tsx` : `${componentName}.test.js`;
|
|
261
256
|
|
|
262
|
-
if (testLibrary === '
|
|
263
|
-
// --- Else use GRC built-in test template based on test library type
|
|
264
|
-
|
|
265
|
-
template = componentTestEnzymeTemplate;
|
|
266
|
-
} else if (testLibrary === 'Testing Library') {
|
|
257
|
+
if (testLibrary === 'Testing Library') {
|
|
267
258
|
template = componentTestTestingLibraryTemplate;
|
|
259
|
+
} else if (testLibrary === 'Vitest') {
|
|
260
|
+
template = componentTestVitestTemplate;
|
|
268
261
|
} else {
|
|
269
262
|
template = componentTestDefaultTemplate;
|
|
270
263
|
}
|
|
@@ -286,7 +279,7 @@ function componentStoryTemplateGenerator({ cliConfigFile, cmd, componentName, co
|
|
|
286
279
|
// Check for a custom story template.
|
|
287
280
|
|
|
288
281
|
if (customTemplates && customTemplates.story) {
|
|
289
|
-
//
|
|
282
|
+
// Load and use the custom story template
|
|
290
283
|
|
|
291
284
|
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
|
|
292
285
|
componentName,
|
|
@@ -296,7 +289,7 @@ function componentStoryTemplateGenerator({ cliConfigFile, cmd, componentName, co
|
|
|
296
289
|
template = customTemplate;
|
|
297
290
|
filename = customTemplateFilename;
|
|
298
291
|
} else {
|
|
299
|
-
//
|
|
292
|
+
// Else use GRC built-in story template
|
|
300
293
|
|
|
301
294
|
template = componentStoryTemplate;
|
|
302
295
|
filename = usesTypeScript ? `${componentName}.stories.tsx` : `${componentName}.stories.js`;
|
|
@@ -318,7 +311,7 @@ function componentLazyTemplateGenerator({ cmd, componentName, cliConfigFile, con
|
|
|
318
311
|
// Check for a custom lazy template.
|
|
319
312
|
|
|
320
313
|
if (customTemplates && customTemplates.lazy) {
|
|
321
|
-
//
|
|
314
|
+
// Load and use the custom lazy template
|
|
322
315
|
|
|
323
316
|
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
|
|
324
317
|
componentName,
|
|
@@ -328,7 +321,7 @@ function componentLazyTemplateGenerator({ cmd, componentName, cliConfigFile, con
|
|
|
328
321
|
template = customTemplate;
|
|
329
322
|
filename = customTemplateFilename;
|
|
330
323
|
} else {
|
|
331
|
-
//
|
|
324
|
+
// Else use GRC built-in lazy template
|
|
332
325
|
|
|
333
326
|
template = usesTypeScript ? componentTsLazyTemplate : componentLazyTemplate;
|
|
334
327
|
filename = usesTypeScript ? `${componentName}.lazy.tsx` : `${componentName}.lazy.js`;
|
|
@@ -350,18 +343,16 @@ function customFileTemplateGenerator({ componentName, cmd, cliConfigFile, compon
|
|
|
350
343
|
// Check for a valid custom template for the corresponding custom component file.
|
|
351
344
|
|
|
352
345
|
if (!customTemplates || !customTemplates[fileType]) {
|
|
353
|
-
|
|
346
|
+
exitWithError(`Missing custom template for "${fileType}"`, {
|
|
354
347
|
details: 'Custom component files require a matching custom template',
|
|
355
348
|
suggestions: [
|
|
356
349
|
`Add a "${fileType}" template path to customTemplates in your config`,
|
|
357
350
|
'Check that the template file exists at the specified path',
|
|
358
351
|
],
|
|
359
352
|
});
|
|
360
|
-
|
|
361
|
-
return process.exit(1);
|
|
362
353
|
}
|
|
363
354
|
|
|
364
|
-
//
|
|
355
|
+
// Load and use the custom component template.
|
|
365
356
|
|
|
366
357
|
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
|
|
367
358
|
componentName,
|
|
@@ -378,7 +369,7 @@ function customFileTemplateGenerator({ componentName, cmd, cliConfigFile, compon
|
|
|
378
369
|
};
|
|
379
370
|
}
|
|
380
371
|
|
|
381
|
-
//
|
|
372
|
+
// Built in component file types
|
|
382
373
|
|
|
383
374
|
const buildInComponentFileTypes = {
|
|
384
375
|
COMPONENT: 'component',
|
|
@@ -388,7 +379,7 @@ const buildInComponentFileTypes = {
|
|
|
388
379
|
LAZY: 'withLazy',
|
|
389
380
|
};
|
|
390
381
|
|
|
391
|
-
//
|
|
382
|
+
// Generate component template map
|
|
392
383
|
|
|
393
384
|
const componentTemplateGeneratorMap = {
|
|
394
385
|
[buildInComponentFileTypes.COMPONENT]: componentTemplateGenerator,
|
|
@@ -404,7 +395,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
404
395
|
let basePath = '';
|
|
405
396
|
|
|
406
397
|
componentFileTypes.forEach((componentFileType) => {
|
|
407
|
-
//
|
|
398
|
+
// Generate templates only if the component options (withStyle, withTest, etc..) are true,
|
|
408
399
|
// or if the template type is "component"
|
|
409
400
|
|
|
410
401
|
if (
|
|
@@ -436,7 +427,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
436
427
|
basePath = path.dirname(componentPath);
|
|
437
428
|
}
|
|
438
429
|
|
|
439
|
-
//
|
|
430
|
+
// Make sure the component does not already exist in the path directory.
|
|
440
431
|
|
|
441
432
|
if (existsSync(componentPath)) {
|
|
442
433
|
generatedFiles.push({ filename, status: 'skipped', path: componentPath });
|
|
@@ -445,67 +436,9 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
|
|
|
445
436
|
if (!cmd.dryRun) {
|
|
446
437
|
outputFileSync(componentPath, template);
|
|
447
438
|
|
|
448
|
-
//
|
|
449
|
-
|
|
450
|
-
regex:
|
|
451
|
-
replacement: convertors.templatename,
|
|
452
|
-
paths: [componentPath],
|
|
453
|
-
recursive: false,
|
|
454
|
-
silent: true,
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
// Will replace the TemplateName in PascalCase
|
|
458
|
-
replace({
|
|
459
|
-
regex: 'TemplateName',
|
|
460
|
-
replacement: convertors.TemplateName,
|
|
461
|
-
paths: [componentPath],
|
|
462
|
-
recursive: false,
|
|
463
|
-
silent: true,
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
// Will replace the templateName in camelCase
|
|
467
|
-
replace({
|
|
468
|
-
regex: 'templateName',
|
|
469
|
-
replacement: convertors.templateName,
|
|
470
|
-
paths: [componentPath],
|
|
471
|
-
recursive: false,
|
|
472
|
-
silent: true,
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
// Will replace the template-name in kebab-case
|
|
476
|
-
replace({
|
|
477
|
-
regex: 'template-name',
|
|
478
|
-
replacement: convertors['template-name'],
|
|
479
|
-
paths: [componentPath],
|
|
480
|
-
recursive: false,
|
|
481
|
-
silent: true,
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
// Will replace the template_name in snake_case
|
|
485
|
-
replace({
|
|
486
|
-
regex: 'template_name',
|
|
487
|
-
replacement: convertors.template_name,
|
|
488
|
-
paths: [componentPath],
|
|
489
|
-
recursive: false,
|
|
490
|
-
silent: true,
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
// Will replace the TEMPLATE_NAME in uppercase SNAKE_CASE
|
|
494
|
-
replace({
|
|
495
|
-
regex: 'TEMPLATE_NAME',
|
|
496
|
-
replacement: convertors.TEMPLATE_NAME,
|
|
497
|
-
paths: [componentPath],
|
|
498
|
-
recursive: false,
|
|
499
|
-
silent: true,
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
// Will replace the TEMPLATENAME in uppercase SNAKE_CASE
|
|
503
|
-
replace({
|
|
504
|
-
regex: 'TEMPLATENAME',
|
|
505
|
-
replacement: convertors.TEMPLATENAME,
|
|
506
|
-
paths: [componentPath],
|
|
507
|
-
recursive: false,
|
|
508
|
-
silent: true,
|
|
439
|
+
// Replace all template placeholders with their corresponding component name formats
|
|
440
|
+
Object.entries(convertors).forEach(([pattern, replacement]) => {
|
|
441
|
+
replace({ regex: pattern, replacement, paths: [componentPath], recursive: false, silent: true });
|
|
509
442
|
});
|
|
510
443
|
}
|
|
511
444
|
|
|
@@ -3,7 +3,7 @@ import inquirer from 'inquirer';
|
|
|
3
3
|
|
|
4
4
|
import merge from 'lodash/merge.js';
|
|
5
5
|
import deepKeys from './deepKeysUtils.js';
|
|
6
|
-
import { blank, error, header, outro, success } from './messagesUtils.js';
|
|
6
|
+
import { blank, error, exitWithError, header, outro, success } from './messagesUtils.js';
|
|
7
7
|
|
|
8
8
|
const { accessSync, constants, outputFileSync, readFileSync } = fsExtra;
|
|
9
9
|
const { prompt } = inquirer;
|
|
@@ -40,7 +40,7 @@ const projectLevelQuestions = [
|
|
|
40
40
|
type: 'select',
|
|
41
41
|
name: 'testLibrary',
|
|
42
42
|
message: 'What testing library does your project use?',
|
|
43
|
-
choices: ['Testing Library', '
|
|
43
|
+
choices: ['Testing Library', 'Vitest', 'None'],
|
|
44
44
|
},
|
|
45
45
|
];
|
|
46
46
|
|
|
@@ -76,7 +76,7 @@ export const componentLevelQuestions = [
|
|
|
76
76
|
type: 'confirm',
|
|
77
77
|
name: 'component.default.withLazy',
|
|
78
78
|
message:
|
|
79
|
-
'Would you like to create a corresponding lazy file (a file that lazy-loads your component out of the box and enables code splitting: https://
|
|
79
|
+
'Would you like to create a corresponding lazy file (a file that lazy-loads your component out of the box and enables code splitting: https://react.dev/reference/react/lazy) with each component you generate?',
|
|
80
80
|
},
|
|
81
81
|
];
|
|
82
82
|
|
|
@@ -99,9 +99,9 @@ async function createCLIConfigFile() {
|
|
|
99
99
|
outputFileSync('generate-react-cli.json', JSON.stringify(answers, null, 2));
|
|
100
100
|
|
|
101
101
|
blank();
|
|
102
|
-
success('
|
|
102
|
+
success('Created the generate-react-cli.json config file');
|
|
103
103
|
blank();
|
|
104
|
-
outro('You can always update
|
|
104
|
+
outro('You can always update the config file manually. Happy Hacking!');
|
|
105
105
|
|
|
106
106
|
return answers;
|
|
107
107
|
} catch (e) {
|
|
@@ -132,9 +132,9 @@ async function updateCLIConfigFile(missingConfigQuestions, currentConfigFile) {
|
|
|
132
132
|
);
|
|
133
133
|
|
|
134
134
|
blank();
|
|
135
|
-
success('
|
|
135
|
+
success('Updated the generate-react-cli.json config file');
|
|
136
136
|
blank();
|
|
137
|
-
outro('You can always update
|
|
137
|
+
outro('You can always update the config file manually. Happy Hacking!');
|
|
138
138
|
|
|
139
139
|
return updatedAnswers;
|
|
140
140
|
} catch (e) {
|
|
@@ -186,13 +186,12 @@ export async function getCLIConfigFile() {
|
|
|
186
186
|
return await createCLIConfigFile();
|
|
187
187
|
}
|
|
188
188
|
} catch {
|
|
189
|
-
|
|
189
|
+
exitWithError('Not in project root', {
|
|
190
190
|
details: 'Could not find package.json in current directory',
|
|
191
191
|
suggestions: [
|
|
192
192
|
'Run this command from your project root directory',
|
|
193
193
|
'Make sure package.json exists in the current directory',
|
|
194
194
|
],
|
|
195
195
|
});
|
|
196
|
-
return process.exit(1);
|
|
197
196
|
}
|
|
198
197
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
|
|
3
|
+
const DEFAULT_TERMINAL_WIDTH = 80;
|
|
4
|
+
|
|
3
5
|
// Symbols for consistent visual feedback
|
|
4
6
|
const symbols = {
|
|
5
7
|
success: chalk.green('✓'),
|
|
@@ -12,10 +14,14 @@ const symbols = {
|
|
|
12
14
|
|
|
13
15
|
// Create a responsive divider that adapts to terminal width
|
|
14
16
|
function divider(color = 'cyan') {
|
|
15
|
-
const width = Math.min(process.stdout.columns ||
|
|
17
|
+
const width = Math.min(process.stdout.columns || DEFAULT_TERMINAL_WIDTH, DEFAULT_TERMINAL_WIDTH);
|
|
16
18
|
return chalk[color]('─'.repeat(width));
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
function pluralize(count, word) {
|
|
22
|
+
return count === 1 ? word : `${word}s`;
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
// Success message with optional file path
|
|
20
26
|
export function success(message, filePath) {
|
|
21
27
|
if (filePath) {
|
|
@@ -45,6 +51,12 @@ export function error(message, { details, suggestions } = {}) {
|
|
|
45
51
|
console.log();
|
|
46
52
|
}
|
|
47
53
|
|
|
54
|
+
// Error message with exit
|
|
55
|
+
export function exitWithError(message, options = {}, exitCode = 1) {
|
|
56
|
+
error(message, options);
|
|
57
|
+
process.exit(exitCode);
|
|
58
|
+
}
|
|
59
|
+
|
|
48
60
|
// Warning message
|
|
49
61
|
export function warning(message) {
|
|
50
62
|
console.log(`${symbols.warning} ${chalk.yellow(message)}`);
|
|
@@ -102,12 +114,11 @@ export function fileSummary(files, basePath, { dryRun = false } = {}) {
|
|
|
102
114
|
} else {
|
|
103
115
|
// Actual run: show what happened
|
|
104
116
|
if (createdFiles.length > 0) {
|
|
105
|
-
console.log(`${symbols.success} ${chalk.green(`Created ${createdFiles.length}
|
|
117
|
+
console.log(`${symbols.success} ${chalk.green(`Created ${createdFiles.length} ${pluralize(createdFiles.length, 'file')} in ${basePath}`)}`);
|
|
106
118
|
}
|
|
107
119
|
if (skippedFiles.length > 0) {
|
|
108
|
-
console.log(`${symbols.warning} ${chalk.yellow(`Skipped ${skippedFiles.length}
|
|
120
|
+
console.log(`${symbols.warning} ${chalk.yellow(`Skipped ${skippedFiles.length} ${pluralize(skippedFiles.length, 'file')} (already exist)`)}`);
|
|
109
121
|
}
|
|
110
|
-
console.log();
|
|
111
122
|
|
|
112
123
|
// Show file tree with status icons
|
|
113
124
|
files.forEach((file, index) => {
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export default `import React from 'react';
|
|
2
|
-
import { shallow } from 'enzyme';
|
|
3
|
-
import templatename from './templatename';
|
|
4
|
-
|
|
5
|
-
describe('<templatename />', () => {
|
|
6
|
-
let component;
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
component = shallow(<templatename />);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test('It should mount', () => {
|
|
13
|
-
expect(component.length).toBe(1);
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
`;
|