generate-react-cli 10.1.0 → 11.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,6 @@ function isNotValidNodeVersion() {
12
12
  details: 'Generate React CLI requires Node 22 or higher',
13
13
  suggestions: [
14
14
  'Update your Node.js version to 22 or higher',
15
- 'Use nvm to manage multiple Node versions: nvm install 22',
16
15
  ],
17
16
  });
18
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "generate-react-cli",
3
- "version": "10.1.0",
3
+ "version": "11.0.1",
4
4
  "description": "A simple React CLI to generate components instantly and more.",
5
5
  "author": "Armin Broubakarian",
6
6
  "license": "MIT",
@@ -43,12 +43,12 @@
43
43
  "chalk": "5.6.2",
44
44
  "commander": "14.0.2",
45
45
  "fs-extra": "11.3.3",
46
- "inquirer": "13.2.0",
47
- "lodash": "4.17.21",
46
+ "inquirer": "13.2.2",
47
+ "lodash": "4.17.23",
48
48
  "replace": "1.2.2"
49
49
  },
50
50
  "devDependencies": {
51
- "@antfu/eslint-config": "7.0.1",
51
+ "@antfu/eslint-config": "7.2.0",
52
52
  "@commitlint/cli": "20.3.1",
53
53
  "@commitlint/config-conventional": "20.3.1",
54
54
  "@semantic-release/commit-analyzer": "13.0.1",
package/readme.md CHANGED
@@ -6,36 +6,41 @@
6
6
  <img src="https://raw.githubusercontent.com/arminbro/generate-react-cli/master/docs/assets/generate-react-cli.svg?raw=true"/>
7
7
  </p>
8
8
 
9
- ## Why?
10
-
11
- To help speed up productivity in React projects and stop copying, pasting, and renaming files each time you want to create a new component.
12
-
13
- A short [article](https://dev.to/arminbro/generate-react-cli-1ooh) goes deeper into why we created GRC if you have the time.
14
-
15
- You can also watch an excellent [video](https://www.youtube.com/watch?v=NEvnt3MWttY) tutorial on how to use GRC by [Eric Murphy](https://www.youtube.com/channel/UC5KDiSAFxrDWhmysBcNqtMA).
16
-
17
- ## Table of Contents:
18
-
19
- - [Config file](#config-file)
20
- - [Generate components](#generate-components)
21
- - [Custom component types](#custom-component-types)
22
- - [Custom component templates](#custom-component-templates)
23
- - [Custom component directory](#custom-component-directory)
24
- - [Custom component files](#custom-component-files)
25
-
26
- ## You can run it using npx like this:
27
-
28
- ```
29
- npx generate-react-cli component Box
9
+ A CLI tool to speed up productivity in React projects by generating components instantly with configurable templates.
10
+
11
+ ## Table of Contents
12
+
13
+ - [Quick Start](#quick-start)
14
+ - [Requirements](#requirements)
15
+ - [Config File](#config-file)
16
+ - [Generate Components](#generate-components)
17
+ - [Options](#options)
18
+ - [Custom Component Types](#custom-component-types)
19
+ - [Custom Component Templates](#custom-component-templates)
20
+ - [Template Keywords](#template-keywords)
21
+ - [Custom Component Files](#custom-component-files)
22
+ - [Advanced: Custom Directory](#advanced-custom-directory)
23
+ - [License](#license)
24
+
25
+ ## Quick Start
26
+
27
+ ```bash
28
+ # Generate your first component (creates config on first run)
29
+ npx generate-react-cli component Box
30
+
31
+ # Or install globally
32
+ npm i -g generate-react-cli
33
+ generate-react component Button
30
34
  ```
31
35
 
32
- _([npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) is a package runner tool that comes with npm 5.2+)_
36
+ ## Requirements
33
37
 
34
- ## Config File
38
+ - Node.js 22 or higher
39
+ - npm 10 or higher
35
40
 
36
- When you run GRC within your project the first time, it will ask you a series of questions to customize the cli for your project needs (this will create a "generate-react-cli.json" config file).
41
+ ## Config File
37
42
 
38
- #### Example of the **generate-react-cli.json** config file:
43
+ When you run GRC within your project the first time, it will ask you a series of questions to customize the CLI for your project needs (this will create a `generate-react-cli.json` config file).
39
44
 
40
45
  ```json
41
46
  {
@@ -55,15 +60,21 @@ When you run GRC within your project the first time, it will ask you a series of
55
60
  }
56
61
  ```
57
62
 
63
+ **Test library options:**
64
+
65
+ | Option | Description |
66
+ |--------|-------------|
67
+ | `Testing Library` | Uses [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) |
68
+ | `Vitest` | Uses [Vitest](https://vitest.dev/) with React Testing Library |
69
+ | `None` | Basic tests using React's createRoot API |
70
+
58
71
  ## Generate Components
59
72
 
60
73
  ```sh
61
- npx generate-react-cli component Box
74
+ npx generate-react-cli component Box
62
75
  ```
63
76
 
64
- This command will create a folder with your component name within your default (e.g. **src/components**) directory, and its corresponding files.
65
-
66
- #### Example of the component files structure:
77
+ This command will create a folder with your component name within your default (e.g., `src/components`) directory, and its corresponding files.
67
78
 
68
79
  ```
69
80
  |-- /src
@@ -74,120 +85,29 @@ This command will create a folder with your component name within your default (
74
85
  |-- Box.test.js
75
86
  ```
76
87
 
77
- ### Options
88
+ ## Options
78
89
 
79
- You can also override some of the GRC component config rules using one-off commands. So for example, let's say you have set **withTest** to be `true` in the `component.default` property. You can override it like this:
90
+ You can override config rules using command-line options:
80
91
 
81
92
  ```sh
82
- npx generate-react-cli component Box --withTest=false
93
+ npx generate-react-cli component Box --withTest=false
83
94
  ```
84
95
 
85
- Or vice versa, if you have set **withTest** to be `false` you can do this:
96
+ | Option | Description | Default |
97
+ |--------|-------------|---------|
98
+ | `--path` | Output directory for the component | Config value |
99
+ | `--type` | [Custom component type](#custom-component-types) to use | `default` |
100
+ | `--withLazy` | Generate a [lazy-loading](https://react.dev/reference/react/lazy) wrapper file | Config value |
101
+ | `--withStory` | Generate a [Storybook](https://storybook.js.org) story file | Config value |
102
+ | `--withStyle` | Generate a stylesheet file | Config value |
103
+ | `--withTest` | Generate a test file | Config value |
104
+ | `--dry-run` | Preview what will be generated without writing files | `false` |
105
+ | `--flat` | Generate files directly in path without creating a folder | `false` |
106
+ | `--customDirectory` | Override the component's folder name ([see below](#advanced-custom-directory)) | Component name |
86
107
 
87
- ```sh
88
- npx generate-react-cli component Box --withTest=true
89
- ```
108
+ ## Custom Component Types
90
109
 
91
- Otherwise, if you don't pass any options, it will just use the default values that you have set in the GRC config file under `component.default`.
92
-
93
- <table>
94
- <tr align="left">
95
- <th>Options</th>
96
- <th>Description</th>
97
- <th>Value Type</th>
98
- <th>Default Value</th>
99
- </tr>
100
-
101
- <tr>
102
- <td width="20%"><b>--path</b></td>
103
- <td width="60%">
104
- Value of the path where you want the component to be generated in (e.g. <b>src/components</b>).
105
- </td>
106
- <td width="20%">String</td>
107
- <td width="20%"><code>component.default.path<code></td>
108
- </tr>
109
-
110
- <tr>
111
- <td width="20%"><b>--type</b></td>
112
- <td width="60%">
113
- You can pass a custom component type that you have configured in the GRC config file that has its own set of component config rules. Read more about <a href="#custom-component-types">custom component types</a>.
114
- </td>
115
- <td width="20%">String</td>
116
- <td width="20%"><code>component.default<code></td>
117
- </tr>
118
-
119
- <tr>
120
- <td width="20%"><b>--withLazy</b></td>
121
- <td width="60%">
122
- Creates a corresponding lazy file (a file that lazy-loads your component out of the box and enables <a href="https://reactjs.org/docs/code-splitting.html#code-splitting">code splitting</a>) with this component.
123
- </td>
124
- <td width="20%">Boolean</td>
125
- <td width="20%"><code>component.default.withLazy<code></td>
126
- </tr>
127
-
128
- <tr>
129
- <td width="20%"><b>--withStory</b></td>
130
- <td width="60%">
131
- Creates a corresponding (<a href="https://storybook.js.org">storybook</a>) story file with this component.
132
- </td>
133
- <td width="20%">Boolean</td>
134
- <td width="20%"><code>component.default.withStory<code></td>
135
- </tr>
136
-
137
- <tr>
138
- <td width="20%"><b>--withStyle</b></td>
139
- <td width="60%">
140
- Creates a corresponding stylesheet file with this component.
141
- </td>
142
- <td width="20%">Boolean</td>
143
- <td width="20%"><code>component.default.withStyle<code></td>
144
- </tr>
145
-
146
- <tr>
147
- <td width="20%"><b>--withTest</b></td>
148
- <td width="60%">
149
- Creates a corresponding test file with this component.
150
- </td>
151
- <td width="20%">Boolean</td>
152
- <td width="20%"><code>component.default.withTest<code></td>
153
- </tr>
154
-
155
- <tr>
156
- <td width="20%"><b>--dry-run</b></td>
157
- <td width="60%">
158
- Show what will be generated without writing to disk
159
- </td>
160
- <td width="20%">Boolean</td>
161
- <td width="20%"><code>false<code></td>
162
- </tr>
163
-
164
- <tr>
165
- <td width="20%"><b>--flat</b></td>
166
- <td width="60%">
167
- Generate the files in the mentioned path instead of creating new folder for it
168
- </td>
169
- <td width="20%">Boolean</td>
170
- <td width="20%"><code>false<code></td>
171
- </tr>
172
-
173
- <tr>
174
- <td width="20%"><b>--customDirectory</b></td>
175
- <td width="60%">
176
- Template value that overrides the name of the directory of the component to be generated in.<br />
177
- See more under <a href="#custom-component-directory">custom component directory</a>.
178
- </td>
179
- <td width="20%">String</td>
180
- <td width="20%"><code>null</code></td>
181
- </tr>
182
- </table>
183
-
184
- ### Custom component types
185
-
186
- By default, GRC will use the `component.default` configuration rules when running the component command out of the box.
187
-
188
- What if you wanted to generate other types of components that have their own set of config rules (e.g., **page** or **layout**)?
189
-
190
- You can do so by extending the **generate-react-cli.json** config file like this.
110
+ By default, GRC uses the `component.default` configuration. You can define additional component types with their own rules:
191
111
 
192
112
  ```json
193
113
  {
@@ -221,81 +141,39 @@ You can do so by extending the **generate-react-cli.json** config file like this
221
141
  }
222
142
  ```
223
143
 
224
- Now you can generate a component with your custom component types like this:
144
+ Generate components with custom types:
225
145
 
226
146
  ```sh
227
- npx generate-react-cli component HomePage --type=page
228
- ```
229
-
230
- ```sh
231
- npx generate-react-cli component BoxLayout --type=layout
232
- ```
233
-
234
- You can also pass the same [options](#options) to your custom component types as you would for the default component type.
235
-
236
- ### Custom component templates
237
-
238
- You can also create your own custom templates that GRC can use instead of the built-in templates that come with it. We hope this will provide more flexibility for your components that you want to generate.
239
-
240
- There is an optional `customTemplates` object that you can pass to the `component.default` or any of your custom component types within your **generate-react-cli.json** config file.
241
-
242
- #### Example of the `customTemplates` object:
243
-
244
- ```json
245
- "customTemplates": {
246
- "component": "templates/TemplateName.js",
247
- "lazy": "templates/TemplateName.lazy.js",
248
- "story": "templates/TemplateName.story.js",
249
- "style": "templates/TemplateName.style.scss",
250
- "test": "templates/TemplateName.test.js"
251
- },
147
+ npx generate-react-cli component HomePage --type=page
148
+ npx generate-react-cli component Sidebar --type=layout
252
149
  ```
253
150
 
254
- The keys represent the type of file, and the values are the paths that point to where your custom template lives in your project/system. Please note the `TemplateName` keyword in the template filename. GRC will use this keyword and replace it with your component name (in whichever format you typed the component name in the command) as the filename.
151
+ ## Custom Component Templates
255
152
 
256
- #### Example of using the `customTemplates` object within your generate-react-cli.json config file:
153
+ Create your own templates that GRC uses instead of the built-in ones. Add a `customTemplates` object to any component type:
257
154
 
258
155
  ```json
259
156
  {
260
- "usesTypeScript": false,
261
- "usesCssModule": true,
262
- "cssPreprocessor": "scss",
263
- "testLibrary": "Testing Library",
264
157
  "component": {
265
158
  "default": {
266
- "customTemplates": {
267
- "component": "templates/component/TemplateName.js",
268
- "style": "templates/component/TemplateName.style.scss",
269
- "test": "templates/component/TemplateName.test.js"
270
- },
271
159
  "path": "src/components",
272
160
  "withStyle": true,
273
161
  "withTest": true,
274
- "withStory": true,
275
- "withLazy": false
276
- },
277
- "page": {
278
162
  "customTemplates": {
279
- "test": "templates/page/TemplateName.test.js"
280
- },
281
- "path": "src/pages",
282
- "withLazy": true,
283
- "withStory": false,
284
- "withStyle": true,
285
- "withTest": true
163
+ "component": "templates/TemplateName.js",
164
+ "style": "templates/TemplateName.module.css",
165
+ "test": "templates/TemplateName.test.js"
166
+ }
286
167
  }
287
168
  }
288
169
  }
289
170
  ```
290
171
 
291
- Notice in the `page.customTemplates` that we only specified the `test` custom template type. That's because all the custom template types are optional. If you don't set the other types, GRC will default to using the built-in templates it comes with.
292
-
293
- #### Example of a custom component template file:
172
+ Example custom component template:
294
173
 
295
174
  ```jsx
296
- // templates/component/TemplateName.js
175
+ // templates/TemplateName.js
297
176
 
298
- import React from 'react';
299
177
  import styles from './TemplateName.module.css';
300
178
 
301
179
  const TemplateName = () => (
@@ -307,176 +185,99 @@ const TemplateName = () => (
307
185
  export default TemplateName;
308
186
  ```
309
187
 
310
- **Important:** You can use the following keywords within your custom templates to format the component name. Note that the built-in GRC templates use `templatename` casing by default:
311
-
312
- | Keyword | Replacement |
313
- | --------------- | ---------------------------------------------------------------------------------------------- |
314
- | `templatename` | component name in raw case (whichever format the user typed the component name in the command) |
315
- | `TemplateName` | component name in PascalCase |
316
- | `templateName` | component name in camelCase |
317
- | `template-name` | component name in kebab-case |
318
- | `template_name` | component name in snake_case |
319
- | `TEMPLATE_NAME` | component name in uppercase SNAKE_CASE |
320
- | `TEMPLATENAME` | component name in full UPPERCASE |
321
-
322
- #### Example of a custom test template file:
188
+ Example custom test template:
323
189
 
324
190
  ```jsx
325
- // templates/component/TemplateName.test.js
191
+ // templates/TemplateName.test.js
326
192
 
327
- import React from 'react';
328
- import ReactDOM from 'react-dom';
193
+ import { createRoot } from 'react-dom/client';
329
194
  import TemplateName from './TemplateName';
330
195
 
331
- it('It should mount', () => {
332
- const div = document.createElement('div');
333
- ReactDOM.render(<TemplateName />, div);
334
- ReactDOM.unmountComponentAtNode(div);
196
+ it('should mount', () => {
197
+ const container = document.createElement('div');
198
+ const root = createRoot(container);
199
+ root.render(<TemplateName />);
200
+ root.unmount();
335
201
  });
336
202
  ```
337
203
 
338
- ### 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:
204
+ All template types are optional. If you don't specify a custom template for a file type, GRC uses its built-in template.
343
205
 
344
- - top
345
- - component.default
346
- - component._type_
347
- - CLI
206
+ ## Template Keywords
348
207
 
349
- #### Example:
208
+ Use these keywords in your custom templates and filenames. GRC replaces them with the component name in various formats:
350
209
 
351
- For React Context Providers in a project, the decision has been made to separate Context generation from the visual components.
210
+ | Keyword | Output Format | Example (`Box`) |
211
+ |---------|--------------|-----------------|
212
+ | `templatename` | raw (as typed) | `Box` |
213
+ | `TemplateName` | PascalCase | `Box` |
214
+ | `templateName` | camelCase | `box` |
215
+ | `template-name` | kebab-case | `box` |
216
+ | `template_name` | snake_case | `box` |
217
+ | `TEMPLATE_NAME` | UPPER_SNAKE | `BOX` |
218
+ | `TEMPLATENAME` | UPPERCASE | `BOX` |
352
219
 
353
- In a typical configuration the configuration would look as following:
220
+ ## Custom Component Files
354
221
 
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
- ```
222
+ Add custom files beyond the built-in options (`withStyle`, `withTest`, `withStory`, `withLazy`).
375
223
 
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.
224
+ Example: Adding an `index.js` barrel file for cleaner imports:
380
225
 
381
226
  ```json
382
227
  {
383
- ...
384
- "provider": {
385
- "path": "src/components/providers",
386
- "withLazy": false,
387
- "withStory": true,
388
- "withStyle": false,
228
+ "component": {
229
+ "default": {
230
+ "path": "src/components",
231
+ "withStyle": true,
389
232
  "withTest": true,
390
- "withTypes": true,
391
- "withContext": true,
392
- "customDirectory": "TemplateNameProvider",
233
+ "withIndex": true,
393
234
  "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"
235
+ "index": "templates/index.js"
399
236
  }
237
+ }
400
238
  }
401
- ...
402
239
  }
403
240
  ```
404
241
 
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
242
+ ```jsx
243
+ // templates/index.js
244
+ export { default } from './TemplateName';
421
245
  ```
422
246
 
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
-
425
- ### Custom component files
247
+ Custom files require corresponding custom templates in `customTemplates`.
426
248
 
427
- GRC comes with corresponding built-in files for a given component if you need them (i.e., `withStyle`, `withTest`, `withStory`, and `withLazy`).
249
+ ## Advanced: Custom Directory
428
250
 
429
- What if you wanted to add custom files of your own?
251
+ Override the generated component's folder name using `customDirectory`. This is useful when you need naming conventions that differ from the component name.
430
252
 
431
- For example, let's say you wanted to add an `index.js` file for each component, so you don't have to add the additional component name with each import (i.e., `import Box from './components/Box'` instead of `import Box from './components/Box/Box'`).
432
-
433
- Or maybe you need an additional style file for your component stories.
434
-
435
- You can do so by editing your **generate-react-cli.json** config file like so.
253
+ Example: Generate a `Theme` provider where files live in a `ThemeProvider` folder:
436
254
 
437
255
  ```json
438
256
  {
439
- "usesTypeScript": false,
440
- "usesCssModule": false,
441
- "cssPreprocessor": "css",
442
- "testLibrary": "Testing Library",
443
257
  "component": {
444
- "default": {
445
- "path": "src/components",
446
- "withStyle": true,
258
+ "provider": {
259
+ "path": "src/providers",
447
260
  "withTest": true,
448
- "withStory": true,
449
- "withLazy": false,
450
- "withIndex": true,
451
- "withStoryStyle": true,
261
+ "customDirectory": "TemplateNameProvider",
452
262
  "customTemplates": {
453
- "index": "templates/default/index.js",
454
- "storyStyle": "templates/default/TemplateName.stories.css"
263
+ "component": "templates/TemplateNameProvider.tsx"
455
264
  }
456
265
  }
457
266
  }
458
267
  }
459
268
  ```
460
269
 
461
- ```jsx
462
- // templates/default/index.js
463
-
464
- export { default } from './TemplateName';
270
+ ```sh
271
+ npx generate-react-cli component Theme --type=provider
272
+ # Creates: src/providers/ThemeProvider/ThemeProvider.tsx
465
273
  ```
466
274
 
467
- ```css
468
- /* templates/default/TemplateName.stories.css */
275
+ You can also pass it as a CLI option:
469
276
 
470
- .TemplateName {
471
- }
277
+ ```sh
278
+ npx generate-react-cli component Box --customDirectory=TemplateNameLayout
472
279
  ```
473
280
 
474
- In this case, we added a `withIndex` & `withStoryStyle` to the `component.default`. Note: You can add custom files to any of your custom component types.
475
-
476
- You should also see that we added `index` and `storyStyle` to our `customTemplates` object. That's because custom files require custom templates. Otherwise, you will get an error when you generate a component.
477
-
478
- Also, we used the `TemplateName` keyword for the `storyStyle` custom file. GRC will generate this corresponding file and replace `TemplateName` with the component name.
479
-
480
281
  ## License
481
282
 
482
- Generate React CLI is an open source software [licensed as MIT](https://github.com/arminbro/generate-react-cli/blob/master/LICENSE).
283
+ Generate React CLI is open source software [licensed as MIT](https://github.com/arminbro/generate-react-cli/blob/master/LICENSE).
@@ -1,6 +1,4 @@
1
- export default `import React from 'react';
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 React, { lazy, Suspense } from 'react';
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 `/* eslint-disable */
2
- import templatename from './templatename';
1
+ export default `import templatename from './templatename';
3
2
 
4
3
  export default {
5
- title: "templatename",
4
+ title: 'templatename',
5
+ component: templatename,
6
6
  };
7
7
 
8
- export const Default = () => <templatename />;
9
-
10
- Default.story = {
11
- name: 'default',
12
- };
8
+ export const Default = {};
13
9
  `;
@@ -1,9 +1,9 @@
1
- export default `import React from 'react';
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('It should mount', () => {
6
- const div = document.createElement('div');
7
- ReactDOM.render(<templatename />, div);
8
- ReactDOM.unmountComponentAtNode(div);
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 React from 'react';
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('it should mount', () => {
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 React, { lazy, Suspense } from 'react';
1
+ export default `import { lazy, Suspense, ComponentProps } from 'react';
2
2
 
3
3
  const Lazytemplatename = lazy(() => import('./templatename'));
4
4
 
5
- const templatename = (props: JSX.IntrinsicAttributes & { children?: React.ReactNode; }) => (
5
+ const templatename = (props: ComponentProps<typeof Lazytemplatename>) => (
6
6
  <Suspense fallback={null}>
7
7
  <Lazytemplatename {...props} />
8
8
  </Suspense>
@@ -1,4 +1,4 @@
1
- export default `import React, { FC } from 'react';
1
+ export default `import type { FC } from 'react';
2
2
  import styles from './templatename.module.css';
3
3
 
4
4
  interface templatenameProps {}
@@ -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 templateNameRE = /.*(template[|_-]?name).*/i;
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
- error(`Unknown component type "${componentType}"`, {
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
- // --- Try loading custom template
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
- error(`Custom template not found: "${templatePath}"`, {
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
- // Double check if the customDirectory is templatable
101
- if (templateNameRE.exec(customDirectory) == null) {
102
- error(`Invalid customDirectory: "${customDirectory}"`, {
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
- // --- Load and use the custom component template
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
- // --- Else use GRC built-in component template
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
- // --- If test library is not Testing Library or if withTest is false. Remove data-testid from template
149
+ // If test library doesn't use data-testid or if withTest is false. Remove data-testid from template
156
150
 
157
- if (testLibrary !== 'Testing Library' || !cmd.withTest) {
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
- // --- If it has a corresponding stylesheet
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
- // --- If the css module is true make sure to update the template accordingly
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
- // --- If no stylesheet, remove className attribute and style import from jsTemplate
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
- // --- Load and use the custom style template
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
- // --- Else use GRC built-in style template
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
- // --- Load and use the custom test template
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 === 'Enzyme') {
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
- // --- Load and use the custom story template
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
- // --- Else use GRC built-in story template
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
- // --- Load and use the custom lazy template
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
- // --- Else use GRC built-in lazy template
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
- error(`Missing custom template for "${fileType}"`, {
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
- // --- Load and use the custom component template.
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
- // --- Built in component file types
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
- // --- Generate component template map
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
- // --- Generate templates only if the component options (withStyle, withTest, etc..) are true,
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
- // --- Make sure the component does not already exist in the path directory.
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
- // Will replace the templatename in whichever format the user typed the component name in the command.
449
- replace({
450
- regex: 'templatename',
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', 'Enzyme', 'None'],
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://reactjs.org/docs/code-splitting.html#code-splitting) with each component you generate?',
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('Config file created successfully');
102
+ success('Created the generate-react-cli.json config file');
103
103
  blank();
104
- outro('You can always update it manually. Happy Hacking!');
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('Config file updated successfully');
135
+ success('Updated the generate-react-cli.json config file');
136
136
  blank();
137
- outro('You can always update it manually. Happy Hacking!');
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
- error('Not in project root', {
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 || 80, 80);
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} file${createdFiles.length === 1 ? '' : 's'} in ${basePath}`)}`);
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} file${skippedFiles.length === 1 ? '' : 's'} (already exist)`)}`);
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
- `;