algolia-codegen 0.1.2 → 0.1.4

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 nightlightmare
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md CHANGED
@@ -1,12 +1,20 @@
1
1
  # Algolia Type Generator
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/algolia-codegen.svg?style=flat-square)](https://www.npmjs.com/package/algolia-codegen)
4
+ [![npm downloads](https://img.shields.io/npm/dm/algolia-codegen.svg?style=flat-square)](https://www.npmjs.com/package/algolia-codegen)
5
+ [![npm bundle size](https://img.shields.io/bundlephobia/minzip/algolia-codegen?style=flat-square)](https://bundlephobia.com/package/algolia-codegen)
6
+ [![GitHub stars](https://img.shields.io/github/stars/nightlightmare/algolia-codegen.svg?style=flat-square&logo=github)](https://github.com/nightlightmare/algolia-codegen)
7
+ [![GitHub issues](https://img.shields.io/github/issues/nightlightmare/algolia-codegen.svg?style=flat-square&logo=github)](https://github.com/nightlightmare/algolia-codegen/issues)
8
+ [![GitHub license](https://img.shields.io/github/license/nightlightmare/algolia-codegen.svg?style=flat-square)](https://github.com/nightlightmare/algolia-codegen/blob/main/LICENSE)
9
+ [![Node.js version](https://img.shields.io/node/v/algolia-codegen.svg?style=flat-square)](https://nodejs.org/)
10
+
3
11
  This script automatically generates TypeScript types from your Algolia indices by fetching sample records and analyzing their structure. It supports multiple indices and flexible configuration through a config file.
4
12
 
5
13
  **Repository**: [https://github.com/nightlightmare/algolia-codegen](https://github.com/nightlightmare/algolia-codegen)
6
14
 
7
15
  ## Prerequisites
8
16
 
9
- - Node.js >= 18
17
+ - Node.js >= 20
10
18
  - Algolia account with at least one index
11
19
 
12
20
  ## Installation
@@ -30,17 +38,17 @@ yarn add algolia-codegen
30
38
  1. Create a configuration file named `algolia-codegen.ts` (or `.js`) in your project root:
31
39
 
32
40
  ```typescript
33
- import type { AlgoliaCodegenConfig } from "algolia-codegen";
41
+ import type { AlgoliaCodegenConfig } from 'algolia-codegen';
34
42
 
35
43
  const config: AlgoliaCodegenConfig = {
36
44
  overwrite: true,
37
45
  generates: {
38
- "src/algolia/types.ts": {
39
- appId: "YOUR_APP_ID",
40
- searchKey: "YOUR_SEARCH_API_KEY",
41
- indexName: "products",
42
- prefix: "Algolia", // Optional
43
- postfix: "Type", // Optional
46
+ 'src/algolia/types.ts': {
47
+ appId: 'YOUR_APP_ID',
48
+ searchKey: 'YOUR_SEARCH_API_KEY',
49
+ indexName: 'products',
50
+ prefix: 'Algolia', // Optional
51
+ postfix: 'Type', // Optional
44
52
  },
45
53
  },
46
54
  };
@@ -65,17 +73,17 @@ npx algolia-codegen
65
73
  Create a configuration file named `algolia-codegen.ts` (or `.js`) in your project root. The config file should export a default object with the following structure:
66
74
 
67
75
  ```typescript
68
- import type { AlgoliaCodegenConfig } from "algolia-codegen";
76
+ import type { AlgoliaCodegenConfig } from 'algolia-codegen';
69
77
 
70
78
  const config: AlgoliaCodegenConfig = {
71
79
  overwrite: true,
72
80
  generates: {
73
- "src/algolia/types.ts": {
74
- appId: "YOUR_APP_ID",
75
- searchKey: "YOUR_SEARCH_API_KEY",
76
- indexName: "products",
77
- prefix: "Algolia", // Optional
78
- postfix: "Type", // Optional
81
+ 'src/algolia/types.ts': {
82
+ appId: 'YOUR_APP_ID',
83
+ searchKey: 'YOUR_SEARCH_API_KEY',
84
+ indexName: 'products',
85
+ prefix: 'Algolia', // Optional
86
+ postfix: 'Type', // Optional
79
87
  },
80
88
  },
81
89
  };
@@ -91,15 +99,15 @@ You can generate types for multiple indices:
91
99
  const config: AlgoliaCodegenConfig = {
92
100
  overwrite: true,
93
101
  generates: {
94
- "src/algolia/products.ts": {
95
- appId: "YOUR_APP_ID",
96
- searchKey: "YOUR_SEARCH_API_KEY",
97
- indexName: "products",
102
+ 'src/algolia/products.ts': {
103
+ appId: 'YOUR_APP_ID',
104
+ searchKey: 'YOUR_SEARCH_API_KEY',
105
+ indexName: 'products',
98
106
  },
99
- "src/algolia/users.ts": {
100
- appId: "YOUR_APP_ID",
101
- searchKey: "YOUR_SEARCH_API_KEY",
102
- indexName: "users",
107
+ 'src/algolia/users.ts': {
108
+ appId: 'YOUR_APP_ID',
109
+ searchKey: 'YOUR_SEARCH_API_KEY',
110
+ indexName: 'users',
103
111
  },
104
112
  },
105
113
  };
@@ -114,17 +122,17 @@ const config: AlgoliaCodegenConfig = {
114
122
  overwrite: true,
115
123
  generates: [
116
124
  {
117
- "src/algolia/products.ts": {
118
- appId: "YOUR_APP_ID",
119
- searchKey: "YOUR_SEARCH_API_KEY",
120
- indexName: "products",
125
+ 'src/algolia/products.ts': {
126
+ appId: 'YOUR_APP_ID',
127
+ searchKey: 'YOUR_SEARCH_API_KEY',
128
+ indexName: 'products',
121
129
  },
122
130
  },
123
131
  {
124
- "src/algolia/users.ts": {
125
- appId: "YOUR_APP_ID",
126
- searchKey: "YOUR_SEARCH_API_KEY",
127
- indexName: "users",
132
+ 'src/algolia/users.ts': {
133
+ appId: 'YOUR_APP_ID',
134
+ searchKey: 'YOUR_SEARCH_API_KEY',
135
+ indexName: 'users',
128
136
  },
129
137
  },
130
138
  ],
@@ -141,7 +149,15 @@ After installation, you can use the CLI command:
141
149
  algolia-codegen
142
150
  ```
143
151
 
144
- Or specify a custom config file:
152
+ #### Options
153
+
154
+ - `-c, --config <path>` - Specify a custom config file path
155
+ - `-v, --verbose` - Enable verbose output (shows detailed logging)
156
+ - `--dry-run` - Simulate execution without writing files (useful for testing)
157
+
158
+ #### Examples
159
+
160
+ Specify a custom config file:
145
161
 
146
162
  ```bash
147
163
  algolia-codegen --config path/to/config.ts
@@ -149,6 +165,26 @@ algolia-codegen --config path/to/config.ts
149
165
  algolia-codegen -c path/to/config.ts
150
166
  ```
151
167
 
168
+ Enable verbose output:
169
+
170
+ ```bash
171
+ algolia-codegen --verbose
172
+ # or
173
+ algolia-codegen -v
174
+ ```
175
+
176
+ Run in dry-run mode (no files will be written):
177
+
178
+ ```bash
179
+ algolia-codegen --dry-run
180
+ ```
181
+
182
+ Combine options:
183
+
184
+ ```bash
185
+ algolia-codegen --config custom-config.ts --verbose --dry-run
186
+ ```
187
+
152
188
  Or if installed locally:
153
189
 
154
190
  ```bash
@@ -187,13 +223,13 @@ type AlgoliaCodegenGeneratorConfig = {
187
223
  - Fetches a sample record from the specified index
188
224
  - Analyzes the record structure and generates TypeScript types
189
225
  - Creates a single TypeScript file containing all types found in the index
190
- 3. **Type Generation**: The generator automatically:
226
+ 4. **Type Generation**: The generator automatically:
191
227
  - Infers types from the sample record structure
192
228
  - Handles nested objects, arrays, and complex types
193
229
  - Detects and generates generic `IdValue<T>` types for Algolia's id-value pattern arrays
194
230
  - Generates proper TypeScript interfaces with JSDoc comments
195
231
  - Sorts types by dependencies for correct ordering
196
- 4. **Error Handling**: Continues processing other files even if one fails, with detailed error messages
232
+ 5. **Error Handling**: Continues processing other files even if one fails, with detailed error messages
197
233
 
198
234
  Each generated file contains all types found in the index, including nested types, properly organized and sorted by dependencies.
199
235
 
@@ -213,44 +249,12 @@ Each generated file contains all types found in the index, including nested type
213
249
 
214
250
  ## Examples
215
251
 
216
- ### Generated Type Example
217
-
218
- Given an Algolia record like:
219
-
220
- ```json
221
- {
222
- "objectID": "123",
223
- "name": "Product Name",
224
- "price": 99.99,
225
- "tags": ["tag1", "tag2"],
226
- "metadata": {
227
- "category": "electronics",
228
- "rating": 4.5
229
- }
230
- }
231
- ```
232
-
233
- The generator will create TypeScript types:
252
+ See the [examples directory](./examples/) for comprehensive examples including:
234
253
 
235
- ```typescript
236
- /**
237
- * Generated TypeScript types for Algolia index: products
238
- * This file is auto-generated. Do not edit manually.
239
- */
240
-
241
- export interface AlgoliaHitType {
242
- metadata: AlgoliaMetadataType;
243
- name: string;
244
- objectID: string;
245
- price: number;
246
- tags: string[];
247
- }
248
-
249
- export interface AlgoliaMetadataType {
250
- category: string;
251
- rating: number;
252
- }
253
- ```
254
+ - Framework integrations (Next.js, React, Vue)
255
+ - CI/CD integration (GitHub Actions)
256
+ - Custom prefixes/postfixes
257
+ - Multiple indices configuration
254
258
 
255
259
  ## Repository
256
260
 
@@ -11,7 +11,7 @@ async function loadTypeScriptConfig(resolvedPath) {
11
11
  const result = esbuild.transformSync(source, {
12
12
  loader: "ts",
13
13
  format: "esm",
14
- target: "node18",
14
+ target: "node20",
15
15
  sourcefile: resolvedPath
16
16
  });
17
17
  if (!result.code) {
@@ -25,10 +25,8 @@ async function loadTypeScriptConfig(resolvedPath) {
25
25
  // src/utils/validations/generator-config.ts
26
26
  function validateGeneratorConfig(config, path) {
27
27
  if (typeof config !== "object" || config === null || Array.isArray(config)) {
28
- throw new Error(
29
- `Invalid generator config: must be an object
30
- Path: ${path}`
31
- );
28
+ throw new Error(`Invalid generator config: must be an object
29
+ Path: ${path}`);
32
30
  }
33
31
  const cfg = config;
34
32
  const requiredFields = [
@@ -70,10 +68,8 @@ Received: ${typeof cfg.postfix}`
70
68
  // src/utils/validations/url-schema.ts
71
69
  function validateUrlSchema(urlSchema, path) {
72
70
  if (typeof urlSchema !== "object" || urlSchema === null || Array.isArray(urlSchema)) {
73
- throw new Error(
74
- `Invalid generates entry: must be an object
75
- Path: ${path}`
76
- );
71
+ throw new Error(`Invalid generates entry: must be an object
72
+ Path: ${path}`);
77
73
  }
78
74
  const schema = urlSchema;
79
75
  for (const [filePath, generatorConfig] of Object.entries(schema)) {
@@ -90,10 +86,8 @@ Path: ${path}[${filePath}]`
90
86
  // src/utils/validations/config.ts
91
87
  function validateConfig(config, configPath) {
92
88
  if (typeof config !== "object" || config === null || Array.isArray(config)) {
93
- throw new Error(
94
- `Invalid config: must be an object
95
- Config file: ${configPath}`
96
- );
89
+ throw new Error(`Invalid config: must be an object
90
+ Config file: ${configPath}`);
97
91
  }
98
92
  const cfg = config;
99
93
  if (!("overwrite" in cfg)) {
@@ -174,10 +168,8 @@ Make sure esbuild is installed as a dependency.`
174
168
  configModule = await import(configUrl);
175
169
  } catch (importError) {
176
170
  const errorMessage = importError instanceof Error ? importError.message : String(importError);
177
- throw new Error(
178
- `Failed to import config file: ${resolvedPath}
179
- Error: ${errorMessage}`
180
- );
171
+ throw new Error(`Failed to import config file: ${resolvedPath}
172
+ Error: ${errorMessage}`);
181
173
  }
182
174
  }
183
175
  if (!configModule.default) {
@@ -306,7 +298,7 @@ var TypeGenerator = class {
306
298
  return `${this.prefix}Hit${this.postfix}`;
307
299
  }
308
300
  const lastPart = path[path.length - 1];
309
- const parts = lastPart.replace(/[\[\]]/g, "").split(/[-_\s]+/).filter(Boolean);
301
+ const parts = lastPart.replace(/[[\]]/g, "").split(/[-_\s]+/).filter(Boolean);
310
302
  const pascalCase = parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join("");
311
303
  if (path.length > 1) {
312
304
  const parent = path[path.length - 2];
@@ -458,30 +450,34 @@ function generateTypeScriptTypes(sampleHit, config) {
458
450
  }
459
451
 
460
452
  // src/utils/fetch-algolia-data.ts
461
- async function fetchAlgoliaData(filePath, generatorConfig, overwrite) {
462
- console.log(`
463
- Processing file: ${filePath}`);
453
+ async function fetchAlgoliaData(filePath, generatorConfig, overwrite, logger) {
454
+ logger.info(`Processing file: ${filePath}`);
464
455
  const resolvedPath = resolve2(process.cwd(), filePath);
456
+ logger.verbose(`Resolved path: ${resolvedPath}`);
465
457
  if (existsSync2(resolvedPath) && !overwrite) {
466
458
  throw new Error(
467
459
  `File already exists: ${resolvedPath}
468
460
  Set overwrite: true in config to allow overwriting existing files.`
469
461
  );
470
462
  }
471
- console.log(`Connecting to Algolia...`);
472
- console.log(`App ID: ${generatorConfig.appId}`);
463
+ const connectSpinner = logger.spinner("Connecting to Algolia...");
464
+ connectSpinner.start();
473
465
  let client;
474
466
  try {
475
- client = algoliasearch(
476
- generatorConfig.appId,
477
- generatorConfig.searchKey
478
- );
467
+ logger.verbose(`App ID: ${generatorConfig.appId}`);
468
+ logger.verbose(`Index: ${generatorConfig.indexName}`);
469
+ client = algoliasearch(generatorConfig.appId, generatorConfig.searchKey);
470
+ connectSpinner.succeed("Connected to Algolia");
479
471
  } catch (error) {
472
+ connectSpinner.fail("Failed to connect to Algolia");
480
473
  throw new Error(
481
474
  `Failed to initialize Algolia client: ${error instanceof Error ? error.message : String(error)}`
482
475
  );
483
476
  }
484
- console.log(`Fetching sample record from index: ${generatorConfig.indexName}`);
477
+ const fetchSpinner = logger.spinner(
478
+ `Fetching sample record from index: ${generatorConfig.indexName}`
479
+ );
480
+ fetchSpinner.start();
485
481
  let results;
486
482
  try {
487
483
  results = await client.search([
@@ -493,7 +489,9 @@ Set overwrite: true in config to allow overwriting existing files.`
493
489
  }
494
490
  }
495
491
  ]);
492
+ fetchSpinner.succeed("Sample record fetched successfully");
496
493
  } catch (error) {
494
+ fetchSpinner.fail("Failed to fetch data from Algolia");
497
495
  let errorMessage;
498
496
  if (error instanceof Error) {
499
497
  errorMessage = error.message;
@@ -525,52 +523,183 @@ Set overwrite: true in config to allow overwriting existing files.`
525
523
  throw new Error(`No hits found in Algolia index: ${generatorConfig.indexName}`);
526
524
  }
527
525
  const sampleHit = result.hits[0];
528
- console.log("Sample record fetched successfully");
529
- console.log(`ObjectID: ${sampleHit.objectID || "N/A"}`);
526
+ logger.verbose(`ObjectID: ${sampleHit.objectID || "N/A"}`);
527
+ const generateSpinner = logger.spinner("Generating TypeScript types...");
528
+ generateSpinner.start();
530
529
  const fileContent = generateTypeScriptTypes(sampleHit, generatorConfig);
530
+ generateSpinner.succeed("TypeScript types generated");
531
531
  const dir = dirname(resolvedPath);
532
532
  if (!existsSync2(dir)) {
533
- mkdirSync(dir, { recursive: true });
533
+ if (!logger.isDryRun) {
534
+ mkdirSync(dir, { recursive: true });
535
+ logger.verbose(`Created directory: ${dir}`);
536
+ } else {
537
+ logger.dryRun(`Would create directory: ${dir}`);
538
+ }
539
+ }
540
+ if (logger.isDryRun) {
541
+ logger.dryRun(`Would write file: ${filePath}`);
542
+ logger.verbose(`File content length: ${fileContent.length} characters`);
543
+ } else {
544
+ writeFileSync(resolvedPath, fileContent, "utf-8");
545
+ logger.success(`Generated file: ${filePath}`);
534
546
  }
535
- writeFileSync(resolvedPath, fileContent, "utf-8");
536
- console.log(`Generated file: ${filePath}`);
537
547
  }
538
548
 
549
+ // src/utils/logger.ts
550
+ import chalk from "chalk";
551
+ import ora from "ora";
552
+ var Logger = class {
553
+ _verbose;
554
+ _dryRun;
555
+ constructor(options = {}) {
556
+ this._verbose = options.verbose ?? false;
557
+ this._dryRun = options.dryRun ?? false;
558
+ }
559
+ /**
560
+ * Check if verbose mode is enabled
561
+ */
562
+ get isVerbose() {
563
+ return this._verbose;
564
+ }
565
+ /**
566
+ * Check if dry-run mode is enabled
567
+ */
568
+ get isDryRun() {
569
+ return this._dryRun;
570
+ }
571
+ /**
572
+ * Log info message (always shown)
573
+ */
574
+ info(message) {
575
+ console.log(chalk.blue("\u2139"), message);
576
+ }
577
+ /**
578
+ * Log success message (always shown)
579
+ */
580
+ success(message) {
581
+ console.log(chalk.green("\u2713"), message);
582
+ }
583
+ /**
584
+ * Log warning message (always shown)
585
+ */
586
+ warn(message) {
587
+ console.log(chalk.yellow("\u26A0"), message);
588
+ }
589
+ /**
590
+ * Log error message (always shown)
591
+ */
592
+ error(message) {
593
+ console.error(chalk.red("\u2717"), message);
594
+ }
595
+ /**
596
+ * Log verbose message (only shown in verbose mode)
597
+ */
598
+ verbose(message) {
599
+ if (this._verbose) {
600
+ console.log(chalk.gray("\u2192"), message);
601
+ }
602
+ }
603
+ /**
604
+ * Log dry-run message (only shown in dry-run mode)
605
+ */
606
+ dryRun(message) {
607
+ if (this._dryRun) {
608
+ console.log(chalk.cyan("[DRY-RUN]"), message);
609
+ }
610
+ }
611
+ /**
612
+ * Create a spinner for long-running operations
613
+ */
614
+ spinner(text) {
615
+ return ora({
616
+ text,
617
+ color: "cyan",
618
+ spinner: "dots"
619
+ });
620
+ }
621
+ /**
622
+ * Log file operation (respects dry-run)
623
+ */
624
+ fileOperation(action, filePath) {
625
+ if (this._dryRun) {
626
+ this.dryRun(`Would ${action}: ${chalk.underline(filePath)}`);
627
+ } else {
628
+ this.verbose(`${action}: ${filePath}`);
629
+ }
630
+ }
631
+ /**
632
+ * Format error with context
633
+ */
634
+ formatError(error, context) {
635
+ let errorMessage = "";
636
+ if (error instanceof Error) {
637
+ errorMessage = error.message;
638
+ if (this._verbose && error.stack) {
639
+ errorMessage += `
640
+ ${chalk.gray(error.stack)}`;
641
+ }
642
+ } else if (error && typeof error === "object") {
643
+ const errorObj = error;
644
+ if (errorObj.message) {
645
+ errorMessage = String(errorObj.message);
646
+ } else if (errorObj.status) {
647
+ errorMessage = `HTTP ${errorObj.status}: ${errorObj.statusText || "Unknown error"}`;
648
+ } else {
649
+ try {
650
+ errorMessage = JSON.stringify(error, null, 2);
651
+ } catch {
652
+ errorMessage = String(error);
653
+ }
654
+ }
655
+ } else {
656
+ errorMessage = String(error);
657
+ }
658
+ if (context) {
659
+ return `${chalk.red(context)}
660
+ ${errorMessage}`;
661
+ }
662
+ return errorMessage;
663
+ }
664
+ };
665
+ var logger_default = Logger;
666
+
539
667
  // src/index.ts
540
- var main = async (configPath) => {
668
+ var main = async (options) => {
669
+ const opts = typeof options === "string" ? { configPath: options } : options ?? {};
670
+ const logger = new logger_default({
671
+ verbose: opts.verbose ?? false,
672
+ dryRun: opts.dryRun ?? false
673
+ });
541
674
  try {
542
- const config = await loadConfig(configPath);
543
- console.log("Config loaded successfully");
675
+ const configSpinner = logger.spinner("Loading configuration...");
676
+ configSpinner.start();
677
+ const config = await loadConfig(opts.configPath);
678
+ configSpinner.succeed("Configuration loaded successfully");
679
+ logger.verbose(`Config path: ${opts.configPath || "default"}`);
680
+ logger.verbose(`Overwrite mode: ${config.overwrite ? "enabled" : "disabled"}`);
544
681
  const generatesArray = Array.isArray(config.generates) ? config.generates : [config.generates];
682
+ logger.verbose(`Found ${generatesArray.length} generate configuration(s)`);
545
683
  for (const urlSchema of generatesArray) {
546
684
  for (const [filePath, generatorConfig] of Object.entries(urlSchema)) {
547
685
  try {
548
- await fetchAlgoliaData(filePath, generatorConfig, config.overwrite);
686
+ await fetchAlgoliaData(filePath, generatorConfig, config.overwrite, logger);
549
687
  } catch (error) {
550
- console.error(`
551
- Error processing file: ${filePath}`);
552
- if (error instanceof Error) {
553
- console.error(error.message);
554
- if (error.stack) {
555
- console.error(error.stack);
556
- }
557
- } else {
558
- try {
559
- console.error(JSON.stringify(error, null, 2));
560
- } catch {
561
- console.error(String(error));
562
- }
563
- }
688
+ logger.error(`Error processing file: ${filePath}`);
689
+ const errorMessage = logger.formatError(error, "Processing failed");
690
+ logger.error(errorMessage);
564
691
  }
565
692
  }
566
693
  }
567
- } catch (error) {
568
- console.error("Error loading config:");
569
- if (error instanceof Error) {
570
- console.error(error.message);
694
+ if (opts.dryRun) {
695
+ logger.info("Dry-run completed. No files were written.");
571
696
  } else {
572
- console.error(String(error));
697
+ logger.success("All files generated successfully!");
573
698
  }
699
+ } catch (error) {
700
+ logger.error("Failed to load configuration");
701
+ const errorMessage = logger.formatError(error, "Configuration error");
702
+ logger.error(errorMessage);
574
703
  process.exit(1);
575
704
  }
576
705
  };