Skip to content

Commit 9c44bc6

Browse files
committed
fix: improve error handling and messaging clarity
1 parent d11da7a commit 9c44bc6

3 files changed

Lines changed: 53 additions & 109 deletions

File tree

src/utils/generateComponentUtils.js

Lines changed: 31 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ import componentTestEnzymeTemplate from '../templates/component/componentTestEnz
1616
import componentTestTestingLibraryTemplate from '../templates/component/componentTestTestingLibraryTemplate.js';
1717
import componentTsLazyTemplate from '../templates/component/componentTsLazyTemplate.js';
1818
import componentTsTemplate from '../templates/component/componentTsTemplate.js';
19-
import { error, fileSummary } from './messagesUtils.js';
19+
import { error, exitWithError, fileSummary } from './messagesUtils.js';
2020

21-
const templateNameRE = /.*(template[|_-]?name).*/i;
21+
const TEMPLATE_NAME_REGEX = /template[-_]?name/i;
2222

2323
const { existsSync, outputFileSync, readFileSync } = fsExtra;
2424

@@ -35,15 +35,13 @@ export function getComponentByType(args, cliConfigFile) {
3535

3636
if (!selectedComponentType) {
3737
const availableTypes = Object.keys(cliConfigFile.component).join(', ');
38-
error(`Unknown component type "${componentType}"`, {
38+
exitWithError(`Unknown component type "${componentType}"`, {
3939
details: `Available types: ${availableTypes}`,
4040
suggestions: [
4141
`Use one of the available types: ${availableTypes}`,
4242
'Add this component type to your generate-react-cli.json config',
4343
],
4444
});
45-
46-
process.exit(1);
4745
}
4846

4947
// Otherwise return it.
@@ -61,23 +59,21 @@ export function getCorrespondingComponentFileTypes(component) {
6159
}
6260

6361
function getCustomTemplate(componentName, templatePath) {
64-
// --- Try loading custom template
62+
// Try loading custom template
6563

6664
try {
6765
const template = readFileSync(templatePath, 'utf8');
6866
const filename = path.basename(templatePath).replace(/template[_-]?name/i, componentName);
6967

7068
return { template, filename };
7169
} catch {
72-
error(`Custom template not found: "${templatePath}"`, {
70+
exitWithError(`Custom template not found: "${templatePath}"`, {
7371
suggestions: [
7472
'Verify the template path in your generate-react-cli.json config',
7573
'Check that the file exists and is readable',
7674
'Use an absolute path or a path relative to project root',
7775
],
7876
});
79-
80-
return process.exit(1);
8177
}
8278
}
8379

@@ -97,17 +93,15 @@ function componentDirectoryNameGenerator({ cmd, componentName, cliConfigFile, fi
9793
if (customDirectoryConfigs.length > 0) {
9894
const customDirectory = customDirectoryConfigs.slice(-1).toString();
9995

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}"`, {
10399
details: 'customDirectory must contain a template placeholder',
104100
suggestions: [
105101
'Use templatename, TemplateName, template-name, or template_name',
106102
'Example: "{{templatename}}" or "TemplateName"',
107103
],
108104
});
109-
110-
process.exit(-2);
111105
}
112106

113107
for (const convertor in convertors) {
@@ -137,7 +131,7 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile, convert
137131
// Check for a custom component template.
138132

139133
if (customTemplates && customTemplates.component) {
140-
// --- Load and use the custom component template
134+
// Load and use the custom component template
141135

142136
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
143137
componentName,
@@ -147,18 +141,18 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile, convert
147141
template = customTemplate;
148142
filename = customTemplateFilename;
149143
} else {
150-
// --- Else use GRC built-in component template
144+
// Else use GRC built-in component template
151145

152146
template = usesTypeScript ? componentTsTemplate : componentJsTemplate;
153147
filename = usesTypeScript ? `${componentName}.tsx` : `${componentName}.js`;
154148

155-
// --- If test library is not Testing Library or if withTest is false. Remove data-testid from template
149+
// If test library is not Testing Library or if withTest is false. Remove data-testid from template
156150

157151
if (testLibrary !== 'Testing Library' || !cmd.withTest) {
158152
template = template.replace(` data-testid="templatename"`, '');
159153
}
160154

161-
// --- If it has a corresponding stylesheet
155+
// If it has a corresponding stylesheet
162156

163157
if (cmd.withStyle) {
164158
if (cliConfigFile.usesStyledComponents) {
@@ -174,7 +168,7 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile, convert
174168
const module = usesCssModule ? '.module' : '';
175169
const cssPath = `${componentName}${module}.${cssPreprocessor}`;
176170

177-
// --- If the css module is true make sure to update the template accordingly
171+
// If the css module is true make sure to update the template accordingly
178172

179173
if (module.length) {
180174
template = template.replace(`'./templatename.module.css'`, `'./${cssPath}'`);
@@ -184,7 +178,7 @@ function componentTemplateGenerator({ cmd, componentName, cliConfigFile, convert
184178
}
185179
}
186180
} else {
187-
// --- If no stylesheet, remove className attribute and style import from jsTemplate
181+
// If no stylesheet, remove className attribute and style import from jsTemplate
188182

189183
template = template.replace(` className={styles.templatename}`, '');
190184
template = template.replace(`import styles from './templatename.module.css';`, '');
@@ -206,7 +200,7 @@ function componentStyleTemplateGenerator({ cliConfigFile, cmd, componentName, co
206200
// Check for a custom style template.
207201

208202
if (customTemplates && customTemplates.style) {
209-
// --- Load and use the custom style template
203+
// Load and use the custom style template
210204

211205
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
212206
componentName,
@@ -224,7 +218,7 @@ function componentStyleTemplateGenerator({ cliConfigFile, cmd, componentName, co
224218
const module = usesCssModule ? '.module' : '';
225219
const cssFilename = `${componentName}${module}.${cssPreprocessor}`;
226220

227-
// --- Else use GRC built-in style template
221+
// Else use GRC built-in style template
228222

229223
template = componentCssTemplate;
230224
filename = cssFilename;
@@ -247,7 +241,7 @@ function componentTestTemplateGenerator({ cliConfigFile, cmd, componentName, con
247241
// Check for a custom test template.
248242

249243
if (customTemplates && customTemplates.test) {
250-
// --- Load and use the custom test template
244+
// Load and use the custom test template
251245

252246
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
253247
componentName,
@@ -260,7 +254,7 @@ function componentTestTemplateGenerator({ cliConfigFile, cmd, componentName, con
260254
filename = usesTypeScript ? `${componentName}.test.tsx` : `${componentName}.test.js`;
261255

262256
if (testLibrary === 'Enzyme') {
263-
// --- Else use GRC built-in test template based on test library type
257+
// Else use GRC built-in test template based on test library type
264258

265259
template = componentTestEnzymeTemplate;
266260
} else if (testLibrary === 'Testing Library') {
@@ -286,7 +280,7 @@ function componentStoryTemplateGenerator({ cliConfigFile, cmd, componentName, co
286280
// Check for a custom story template.
287281

288282
if (customTemplates && customTemplates.story) {
289-
// --- Load and use the custom story template
283+
// Load and use the custom story template
290284

291285
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
292286
componentName,
@@ -296,7 +290,7 @@ function componentStoryTemplateGenerator({ cliConfigFile, cmd, componentName, co
296290
template = customTemplate;
297291
filename = customTemplateFilename;
298292
} else {
299-
// --- Else use GRC built-in story template
293+
// Else use GRC built-in story template
300294

301295
template = componentStoryTemplate;
302296
filename = usesTypeScript ? `${componentName}.stories.tsx` : `${componentName}.stories.js`;
@@ -318,7 +312,7 @@ function componentLazyTemplateGenerator({ cmd, componentName, cliConfigFile, con
318312
// Check for a custom lazy template.
319313

320314
if (customTemplates && customTemplates.lazy) {
321-
// --- Load and use the custom lazy template
315+
// Load and use the custom lazy template
322316

323317
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
324318
componentName,
@@ -328,7 +322,7 @@ function componentLazyTemplateGenerator({ cmd, componentName, cliConfigFile, con
328322
template = customTemplate;
329323
filename = customTemplateFilename;
330324
} else {
331-
// --- Else use GRC built-in lazy template
325+
// Else use GRC built-in lazy template
332326

333327
template = usesTypeScript ? componentTsLazyTemplate : componentLazyTemplate;
334328
filename = usesTypeScript ? `${componentName}.lazy.tsx` : `${componentName}.lazy.js`;
@@ -350,18 +344,16 @@ function customFileTemplateGenerator({ componentName, cmd, cliConfigFile, compon
350344
// Check for a valid custom template for the corresponding custom component file.
351345

352346
if (!customTemplates || !customTemplates[fileType]) {
353-
error(`Missing custom template for "${fileType}"`, {
347+
exitWithError(`Missing custom template for "${fileType}"`, {
354348
details: 'Custom component files require a matching custom template',
355349
suggestions: [
356350
`Add a "${fileType}" template path to customTemplates in your config`,
357351
'Check that the template file exists at the specified path',
358352
],
359353
});
360-
361-
return process.exit(1);
362354
}
363355

364-
// --- Load and use the custom component template.
356+
// Load and use the custom component template.
365357

366358
const { template: customTemplate, filename: customTemplateFilename } = getCustomTemplate(
367359
componentName,
@@ -378,7 +370,7 @@ function customFileTemplateGenerator({ componentName, cmd, cliConfigFile, compon
378370
};
379371
}
380372

381-
// --- Built in component file types
373+
// Built in component file types
382374

383375
const buildInComponentFileTypes = {
384376
COMPONENT: 'component',
@@ -388,7 +380,7 @@ const buildInComponentFileTypes = {
388380
LAZY: 'withLazy',
389381
};
390382

391-
// --- Generate component template map
383+
// Generate component template map
392384

393385
const componentTemplateGeneratorMap = {
394386
[buildInComponentFileTypes.COMPONENT]: componentTemplateGenerator,
@@ -404,7 +396,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
404396
let basePath = '';
405397

406398
componentFileTypes.forEach((componentFileType) => {
407-
// --- Generate templates only if the component options (withStyle, withTest, etc..) are true,
399+
// Generate templates only if the component options (withStyle, withTest, etc..) are true,
408400
// or if the template type is "component"
409401

410402
if (
@@ -436,7 +428,7 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
436428
basePath = path.dirname(componentPath);
437429
}
438430

439-
// --- Make sure the component does not already exist in the path directory.
431+
// Make sure the component does not already exist in the path directory.
440432

441433
if (existsSync(componentPath)) {
442434
generatedFiles.push({ filename, status: 'skipped', path: componentPath });
@@ -445,67 +437,9 @@ export function generateComponent(componentName, cmd, cliConfigFile) {
445437
if (!cmd.dryRun) {
446438
outputFileSync(componentPath, template);
447439

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,
440+
// Replace all template placeholders with their corresponding component name formats
441+
Object.entries(convertors).forEach(([pattern, replacement]) => {
442+
replace({ regex: pattern, replacement, paths: [componentPath], recursive: false, silent: true });
509443
});
510444
}
511445

src/utils/grcConfigUtils.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import inquirer from 'inquirer';
33

44
import merge from 'lodash/merge.js';
55
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';
77

88
const { accessSync, constants, outputFileSync, readFileSync } = fsExtra;
99
const { prompt } = inquirer;
@@ -76,7 +76,7 @@ export const componentLevelQuestions = [
7676
type: 'confirm',
7777
name: 'component.default.withLazy',
7878
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?',
8080
},
8181
];
8282

@@ -99,9 +99,9 @@ async function createCLIConfigFile() {
9999
outputFileSync('generate-react-cli.json', JSON.stringify(answers, null, 2));
100100

101101
blank();
102-
success('Config file created successfully');
102+
success('Created the generate-react-cli.json config file');
103103
blank();
104-
outro('You can always update it manually. Happy Hacking!');
104+
outro('You can always update the config file manually. Happy Hacking!');
105105

106106
return answers;
107107
} catch (e) {
@@ -132,9 +132,9 @@ async function updateCLIConfigFile(missingConfigQuestions, currentConfigFile) {
132132
);
133133

134134
blank();
135-
success('Config file updated successfully');
135+
success('Updated the generate-react-cli.json config file');
136136
blank();
137-
outro('You can always update it manually. Happy Hacking!');
137+
outro('You can always update the config file manually. Happy Hacking!');
138138

139139
return updatedAnswers;
140140
} catch (e) {
@@ -186,13 +186,12 @@ export async function getCLIConfigFile() {
186186
return await createCLIConfigFile();
187187
}
188188
} catch {
189-
error('Not in project root', {
189+
exitWithError('Not in project root', {
190190
details: 'Could not find package.json in current directory',
191191
suggestions: [
192192
'Run this command from your project root directory',
193193
'Make sure package.json exists in the current directory',
194194
],
195195
});
196-
return process.exit(1);
197196
}
198197
}

0 commit comments

Comments
 (0)