algolia-codegen 0.1.3 → 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/README.md CHANGED
@@ -149,7 +149,15 @@ After installation, you can use the CLI command:
149
149
  algolia-codegen
150
150
  ```
151
151
 
152
- 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:
153
161
 
154
162
  ```bash
155
163
  algolia-codegen --config path/to/config.ts
@@ -157,6 +165,26 @@ algolia-codegen --config path/to/config.ts
157
165
  algolia-codegen -c path/to/config.ts
158
166
  ```
159
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
+
160
188
  Or if installed locally:
161
189
 
162
190
  ```bash
@@ -222,6 +250,7 @@ Each generated file contains all types found in the index, including nested type
222
250
  ## Examples
223
251
 
224
252
  See the [examples directory](./examples/) for comprehensive examples including:
253
+
225
254
  - Framework integrations (Next.js, React, Vue)
226
255
  - CI/CD integration (GitHub Actions)
227
256
  - Custom prefixes/postfixes
@@ -450,27 +450,34 @@ function generateTypeScriptTypes(sampleHit, config) {
450
450
  }
451
451
 
452
452
  // src/utils/fetch-algolia-data.ts
453
- async function fetchAlgoliaData(filePath, generatorConfig, overwrite) {
454
- console.log(`
455
- Processing file: ${filePath}`);
453
+ async function fetchAlgoliaData(filePath, generatorConfig, overwrite, logger) {
454
+ logger.info(`Processing file: ${filePath}`);
456
455
  const resolvedPath = resolve2(process.cwd(), filePath);
456
+ logger.verbose(`Resolved path: ${resolvedPath}`);
457
457
  if (existsSync2(resolvedPath) && !overwrite) {
458
458
  throw new Error(
459
459
  `File already exists: ${resolvedPath}
460
460
  Set overwrite: true in config to allow overwriting existing files.`
461
461
  );
462
462
  }
463
- console.log(`Connecting to Algolia...`);
464
- console.log(`App ID: ${generatorConfig.appId}`);
463
+ const connectSpinner = logger.spinner("Connecting to Algolia...");
464
+ connectSpinner.start();
465
465
  let client;
466
466
  try {
467
+ logger.verbose(`App ID: ${generatorConfig.appId}`);
468
+ logger.verbose(`Index: ${generatorConfig.indexName}`);
467
469
  client = algoliasearch(generatorConfig.appId, generatorConfig.searchKey);
470
+ connectSpinner.succeed("Connected to Algolia");
468
471
  } catch (error) {
472
+ connectSpinner.fail("Failed to connect to Algolia");
469
473
  throw new Error(
470
474
  `Failed to initialize Algolia client: ${error instanceof Error ? error.message : String(error)}`
471
475
  );
472
476
  }
473
- 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();
474
481
  let results;
475
482
  try {
476
483
  results = await client.search([
@@ -482,7 +489,9 @@ Set overwrite: true in config to allow overwriting existing files.`
482
489
  }
483
490
  }
484
491
  ]);
492
+ fetchSpinner.succeed("Sample record fetched successfully");
485
493
  } catch (error) {
494
+ fetchSpinner.fail("Failed to fetch data from Algolia");
486
495
  let errorMessage;
487
496
  if (error instanceof Error) {
488
497
  errorMessage = error.message;
@@ -514,52 +523,183 @@ Set overwrite: true in config to allow overwriting existing files.`
514
523
  throw new Error(`No hits found in Algolia index: ${generatorConfig.indexName}`);
515
524
  }
516
525
  const sampleHit = result.hits[0];
517
- console.log("Sample record fetched successfully");
518
- 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();
519
529
  const fileContent = generateTypeScriptTypes(sampleHit, generatorConfig);
530
+ generateSpinner.succeed("TypeScript types generated");
520
531
  const dir = dirname(resolvedPath);
521
532
  if (!existsSync2(dir)) {
522
- 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}`);
523
546
  }
524
- writeFileSync(resolvedPath, fileContent, "utf-8");
525
- console.log(`Generated file: ${filePath}`);
526
547
  }
527
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
+
528
667
  // src/index.ts
529
- 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
+ });
530
674
  try {
531
- const config = await loadConfig(configPath);
532
- 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"}`);
533
681
  const generatesArray = Array.isArray(config.generates) ? config.generates : [config.generates];
682
+ logger.verbose(`Found ${generatesArray.length} generate configuration(s)`);
534
683
  for (const urlSchema of generatesArray) {
535
684
  for (const [filePath, generatorConfig] of Object.entries(urlSchema)) {
536
685
  try {
537
- await fetchAlgoliaData(filePath, generatorConfig, config.overwrite);
686
+ await fetchAlgoliaData(filePath, generatorConfig, config.overwrite, logger);
538
687
  } catch (error) {
539
- console.error(`
540
- Error processing file: ${filePath}`);
541
- if (error instanceof Error) {
542
- console.error(error.message);
543
- if (error.stack) {
544
- console.error(error.stack);
545
- }
546
- } else {
547
- try {
548
- console.error(JSON.stringify(error, null, 2));
549
- } catch {
550
- console.error(String(error));
551
- }
552
- }
688
+ logger.error(`Error processing file: ${filePath}`);
689
+ const errorMessage = logger.formatError(error, "Processing failed");
690
+ logger.error(errorMessage);
553
691
  }
554
692
  }
555
693
  }
556
- } catch (error) {
557
- console.error("Error loading config:");
558
- if (error instanceof Error) {
559
- console.error(error.message);
694
+ if (opts.dryRun) {
695
+ logger.info("Dry-run completed. No files were written.");
560
696
  } else {
561
- console.error(String(error));
697
+ logger.success("All files generated successfully!");
562
698
  }
699
+ } catch (error) {
700
+ logger.error("Failed to load configuration");
701
+ const errorMessage = logger.formatError(error, "Configuration error");
702
+ logger.error(errorMessage);
563
703
  process.exit(1);
564
704
  }
565
705
  };
package/dist/cli.cjs CHANGED
@@ -481,27 +481,34 @@ function generateTypeScriptTypes(sampleHit, config) {
481
481
  }
482
482
 
483
483
  // src/utils/fetch-algolia-data.ts
484
- async function fetchAlgoliaData(filePath, generatorConfig, overwrite) {
485
- console.log(`
486
- Processing file: ${filePath}`);
484
+ async function fetchAlgoliaData(filePath, generatorConfig, overwrite, logger) {
485
+ logger.info(`Processing file: ${filePath}`);
487
486
  const resolvedPath = (0, import_path2.resolve)(process.cwd(), filePath);
487
+ logger.verbose(`Resolved path: ${resolvedPath}`);
488
488
  if ((0, import_fs3.existsSync)(resolvedPath) && !overwrite) {
489
489
  throw new Error(
490
490
  `File already exists: ${resolvedPath}
491
491
  Set overwrite: true in config to allow overwriting existing files.`
492
492
  );
493
493
  }
494
- console.log(`Connecting to Algolia...`);
495
- console.log(`App ID: ${generatorConfig.appId}`);
494
+ const connectSpinner = logger.spinner("Connecting to Algolia...");
495
+ connectSpinner.start();
496
496
  let client;
497
497
  try {
498
+ logger.verbose(`App ID: ${generatorConfig.appId}`);
499
+ logger.verbose(`Index: ${generatorConfig.indexName}`);
498
500
  client = (0, import_algoliasearch.algoliasearch)(generatorConfig.appId, generatorConfig.searchKey);
501
+ connectSpinner.succeed("Connected to Algolia");
499
502
  } catch (error) {
503
+ connectSpinner.fail("Failed to connect to Algolia");
500
504
  throw new Error(
501
505
  `Failed to initialize Algolia client: ${error instanceof Error ? error.message : String(error)}`
502
506
  );
503
507
  }
504
- console.log(`Fetching sample record from index: ${generatorConfig.indexName}`);
508
+ const fetchSpinner = logger.spinner(
509
+ `Fetching sample record from index: ${generatorConfig.indexName}`
510
+ );
511
+ fetchSpinner.start();
505
512
  let results;
506
513
  try {
507
514
  results = await client.search([
@@ -513,7 +520,9 @@ Set overwrite: true in config to allow overwriting existing files.`
513
520
  }
514
521
  }
515
522
  ]);
523
+ fetchSpinner.succeed("Sample record fetched successfully");
516
524
  } catch (error) {
525
+ fetchSpinner.fail("Failed to fetch data from Algolia");
517
526
  let errorMessage;
518
527
  if (error instanceof Error) {
519
528
  errorMessage = error.message;
@@ -545,52 +554,183 @@ Set overwrite: true in config to allow overwriting existing files.`
545
554
  throw new Error(`No hits found in Algolia index: ${generatorConfig.indexName}`);
546
555
  }
547
556
  const sampleHit = result.hits[0];
548
- console.log("Sample record fetched successfully");
549
- console.log(`ObjectID: ${sampleHit.objectID || "N/A"}`);
557
+ logger.verbose(`ObjectID: ${sampleHit.objectID || "N/A"}`);
558
+ const generateSpinner = logger.spinner("Generating TypeScript types...");
559
+ generateSpinner.start();
550
560
  const fileContent = generateTypeScriptTypes(sampleHit, generatorConfig);
561
+ generateSpinner.succeed("TypeScript types generated");
551
562
  const dir = (0, import_path2.dirname)(resolvedPath);
552
563
  if (!(0, import_fs3.existsSync)(dir)) {
553
- (0, import_fs3.mkdirSync)(dir, { recursive: true });
564
+ if (!logger.isDryRun) {
565
+ (0, import_fs3.mkdirSync)(dir, { recursive: true });
566
+ logger.verbose(`Created directory: ${dir}`);
567
+ } else {
568
+ logger.dryRun(`Would create directory: ${dir}`);
569
+ }
570
+ }
571
+ if (logger.isDryRun) {
572
+ logger.dryRun(`Would write file: ${filePath}`);
573
+ logger.verbose(`File content length: ${fileContent.length} characters`);
574
+ } else {
575
+ (0, import_fs3.writeFileSync)(resolvedPath, fileContent, "utf-8");
576
+ logger.success(`Generated file: ${filePath}`);
554
577
  }
555
- (0, import_fs3.writeFileSync)(resolvedPath, fileContent, "utf-8");
556
- console.log(`Generated file: ${filePath}`);
557
578
  }
558
579
 
580
+ // src/utils/logger.ts
581
+ var import_chalk = __toESM(require("chalk"), 1);
582
+ var import_ora = __toESM(require("ora"), 1);
583
+ var Logger = class {
584
+ _verbose;
585
+ _dryRun;
586
+ constructor(options = {}) {
587
+ this._verbose = options.verbose ?? false;
588
+ this._dryRun = options.dryRun ?? false;
589
+ }
590
+ /**
591
+ * Check if verbose mode is enabled
592
+ */
593
+ get isVerbose() {
594
+ return this._verbose;
595
+ }
596
+ /**
597
+ * Check if dry-run mode is enabled
598
+ */
599
+ get isDryRun() {
600
+ return this._dryRun;
601
+ }
602
+ /**
603
+ * Log info message (always shown)
604
+ */
605
+ info(message) {
606
+ console.log(import_chalk.default.blue("\u2139"), message);
607
+ }
608
+ /**
609
+ * Log success message (always shown)
610
+ */
611
+ success(message) {
612
+ console.log(import_chalk.default.green("\u2713"), message);
613
+ }
614
+ /**
615
+ * Log warning message (always shown)
616
+ */
617
+ warn(message) {
618
+ console.log(import_chalk.default.yellow("\u26A0"), message);
619
+ }
620
+ /**
621
+ * Log error message (always shown)
622
+ */
623
+ error(message) {
624
+ console.error(import_chalk.default.red("\u2717"), message);
625
+ }
626
+ /**
627
+ * Log verbose message (only shown in verbose mode)
628
+ */
629
+ verbose(message) {
630
+ if (this._verbose) {
631
+ console.log(import_chalk.default.gray("\u2192"), message);
632
+ }
633
+ }
634
+ /**
635
+ * Log dry-run message (only shown in dry-run mode)
636
+ */
637
+ dryRun(message) {
638
+ if (this._dryRun) {
639
+ console.log(import_chalk.default.cyan("[DRY-RUN]"), message);
640
+ }
641
+ }
642
+ /**
643
+ * Create a spinner for long-running operations
644
+ */
645
+ spinner(text) {
646
+ return (0, import_ora.default)({
647
+ text,
648
+ color: "cyan",
649
+ spinner: "dots"
650
+ });
651
+ }
652
+ /**
653
+ * Log file operation (respects dry-run)
654
+ */
655
+ fileOperation(action, filePath) {
656
+ if (this._dryRun) {
657
+ this.dryRun(`Would ${action}: ${import_chalk.default.underline(filePath)}`);
658
+ } else {
659
+ this.verbose(`${action}: ${filePath}`);
660
+ }
661
+ }
662
+ /**
663
+ * Format error with context
664
+ */
665
+ formatError(error, context) {
666
+ let errorMessage = "";
667
+ if (error instanceof Error) {
668
+ errorMessage = error.message;
669
+ if (this._verbose && error.stack) {
670
+ errorMessage += `
671
+ ${import_chalk.default.gray(error.stack)}`;
672
+ }
673
+ } else if (error && typeof error === "object") {
674
+ const errorObj = error;
675
+ if (errorObj.message) {
676
+ errorMessage = String(errorObj.message);
677
+ } else if (errorObj.status) {
678
+ errorMessage = `HTTP ${errorObj.status}: ${errorObj.statusText || "Unknown error"}`;
679
+ } else {
680
+ try {
681
+ errorMessage = JSON.stringify(error, null, 2);
682
+ } catch {
683
+ errorMessage = String(error);
684
+ }
685
+ }
686
+ } else {
687
+ errorMessage = String(error);
688
+ }
689
+ if (context) {
690
+ return `${import_chalk.default.red(context)}
691
+ ${errorMessage}`;
692
+ }
693
+ return errorMessage;
694
+ }
695
+ };
696
+ var logger_default = Logger;
697
+
559
698
  // src/index.ts
560
- var main = async (configPath) => {
699
+ var main = async (options) => {
700
+ const opts = typeof options === "string" ? { configPath: options } : options ?? {};
701
+ const logger = new logger_default({
702
+ verbose: opts.verbose ?? false,
703
+ dryRun: opts.dryRun ?? false
704
+ });
561
705
  try {
562
- const config = await loadConfig(configPath);
563
- console.log("Config loaded successfully");
706
+ const configSpinner = logger.spinner("Loading configuration...");
707
+ configSpinner.start();
708
+ const config = await loadConfig(opts.configPath);
709
+ configSpinner.succeed("Configuration loaded successfully");
710
+ logger.verbose(`Config path: ${opts.configPath || "default"}`);
711
+ logger.verbose(`Overwrite mode: ${config.overwrite ? "enabled" : "disabled"}`);
564
712
  const generatesArray = Array.isArray(config.generates) ? config.generates : [config.generates];
713
+ logger.verbose(`Found ${generatesArray.length} generate configuration(s)`);
565
714
  for (const urlSchema of generatesArray) {
566
715
  for (const [filePath, generatorConfig] of Object.entries(urlSchema)) {
567
716
  try {
568
- await fetchAlgoliaData(filePath, generatorConfig, config.overwrite);
717
+ await fetchAlgoliaData(filePath, generatorConfig, config.overwrite, logger);
569
718
  } catch (error) {
570
- console.error(`
571
- Error processing file: ${filePath}`);
572
- if (error instanceof Error) {
573
- console.error(error.message);
574
- if (error.stack) {
575
- console.error(error.stack);
576
- }
577
- } else {
578
- try {
579
- console.error(JSON.stringify(error, null, 2));
580
- } catch {
581
- console.error(String(error));
582
- }
583
- }
719
+ logger.error(`Error processing file: ${filePath}`);
720
+ const errorMessage = logger.formatError(error, "Processing failed");
721
+ logger.error(errorMessage);
584
722
  }
585
723
  }
586
724
  }
587
- } catch (error) {
588
- console.error("Error loading config:");
589
- if (error instanceof Error) {
590
- console.error(error.message);
725
+ if (opts.dryRun) {
726
+ logger.info("Dry-run completed. No files were written.");
591
727
  } else {
592
- console.error(String(error));
728
+ logger.success("All files generated successfully!");
593
729
  }
730
+ } catch (error) {
731
+ logger.error("Failed to load configuration");
732
+ const errorMessage = logger.formatError(error, "Configuration error");
733
+ logger.error(errorMessage);
594
734
  process.exit(1);
595
735
  }
596
736
  };
@@ -601,6 +741,10 @@ if ((0, import_fs4.existsSync)(envPath)) {
601
741
  (0, import_dotenv.config)({ path: envPath });
602
742
  }
603
743
  var program = new import_commander.Command();
604
- program.name("algolia-codegen").description("Generate TypeScript types from Algolia index").option("-c, --config <path>", "Config file path").action(async (options) => {
605
- await main(options.config);
744
+ program.name("algolia-codegen").description("Generate TypeScript types from Algolia index").option("-c, --config <path>", "Config file path").option("-v, --verbose", "Enable verbose output").option("--dry-run", "Simulate execution without writing files").action(async (options) => {
745
+ await main({
746
+ configPath: options.config,
747
+ verbose: options.verbose ?? false,
748
+ dryRun: options.dryRun ?? false
749
+ });
606
750
  }).parse();
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  main
4
- } from "./chunk-4T5DZK75.js";
4
+ } from "./chunk-USMYY2DN.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { config as loadDotenv } from "dotenv";
@@ -13,6 +13,10 @@ if (existsSync(envPath)) {
13
13
  loadDotenv({ path: envPath });
14
14
  }
15
15
  var program = new Command();
16
- program.name("algolia-codegen").description("Generate TypeScript types from Algolia index").option("-c, --config <path>", "Config file path").action(async (options) => {
17
- await main(options.config);
16
+ program.name("algolia-codegen").description("Generate TypeScript types from Algolia index").option("-c, --config <path>", "Config file path").option("-v, --verbose", "Enable verbose output").option("--dry-run", "Simulate execution without writing files").action(async (options) => {
17
+ await main({
18
+ configPath: options.config,
19
+ verbose: options.verbose ?? false,
20
+ dryRun: options.dryRun ?? false
21
+ });
18
22
  }).parse();
package/dist/index.cjs CHANGED
@@ -489,27 +489,34 @@ function generateTypeScriptTypes(sampleHit, config) {
489
489
  }
490
490
 
491
491
  // src/utils/fetch-algolia-data.ts
492
- async function fetchAlgoliaData(filePath, generatorConfig, overwrite) {
493
- console.log(`
494
- Processing file: ${filePath}`);
492
+ async function fetchAlgoliaData(filePath, generatorConfig, overwrite, logger) {
493
+ logger.info(`Processing file: ${filePath}`);
495
494
  const resolvedPath = (0, import_path2.resolve)(process.cwd(), filePath);
495
+ logger.verbose(`Resolved path: ${resolvedPath}`);
496
496
  if ((0, import_fs3.existsSync)(resolvedPath) && !overwrite) {
497
497
  throw new Error(
498
498
  `File already exists: ${resolvedPath}
499
499
  Set overwrite: true in config to allow overwriting existing files.`
500
500
  );
501
501
  }
502
- console.log(`Connecting to Algolia...`);
503
- console.log(`App ID: ${generatorConfig.appId}`);
502
+ const connectSpinner = logger.spinner("Connecting to Algolia...");
503
+ connectSpinner.start();
504
504
  let client;
505
505
  try {
506
+ logger.verbose(`App ID: ${generatorConfig.appId}`);
507
+ logger.verbose(`Index: ${generatorConfig.indexName}`);
506
508
  client = (0, import_algoliasearch.algoliasearch)(generatorConfig.appId, generatorConfig.searchKey);
509
+ connectSpinner.succeed("Connected to Algolia");
507
510
  } catch (error) {
511
+ connectSpinner.fail("Failed to connect to Algolia");
508
512
  throw new Error(
509
513
  `Failed to initialize Algolia client: ${error instanceof Error ? error.message : String(error)}`
510
514
  );
511
515
  }
512
- console.log(`Fetching sample record from index: ${generatorConfig.indexName}`);
516
+ const fetchSpinner = logger.spinner(
517
+ `Fetching sample record from index: ${generatorConfig.indexName}`
518
+ );
519
+ fetchSpinner.start();
513
520
  let results;
514
521
  try {
515
522
  results = await client.search([
@@ -521,7 +528,9 @@ Set overwrite: true in config to allow overwriting existing files.`
521
528
  }
522
529
  }
523
530
  ]);
531
+ fetchSpinner.succeed("Sample record fetched successfully");
524
532
  } catch (error) {
533
+ fetchSpinner.fail("Failed to fetch data from Algolia");
525
534
  let errorMessage;
526
535
  if (error instanceof Error) {
527
536
  errorMessage = error.message;
@@ -553,52 +562,183 @@ Set overwrite: true in config to allow overwriting existing files.`
553
562
  throw new Error(`No hits found in Algolia index: ${generatorConfig.indexName}`);
554
563
  }
555
564
  const sampleHit = result.hits[0];
556
- console.log("Sample record fetched successfully");
557
- console.log(`ObjectID: ${sampleHit.objectID || "N/A"}`);
565
+ logger.verbose(`ObjectID: ${sampleHit.objectID || "N/A"}`);
566
+ const generateSpinner = logger.spinner("Generating TypeScript types...");
567
+ generateSpinner.start();
558
568
  const fileContent = generateTypeScriptTypes(sampleHit, generatorConfig);
569
+ generateSpinner.succeed("TypeScript types generated");
559
570
  const dir = (0, import_path2.dirname)(resolvedPath);
560
571
  if (!(0, import_fs3.existsSync)(dir)) {
561
- (0, import_fs3.mkdirSync)(dir, { recursive: true });
572
+ if (!logger.isDryRun) {
573
+ (0, import_fs3.mkdirSync)(dir, { recursive: true });
574
+ logger.verbose(`Created directory: ${dir}`);
575
+ } else {
576
+ logger.dryRun(`Would create directory: ${dir}`);
577
+ }
578
+ }
579
+ if (logger.isDryRun) {
580
+ logger.dryRun(`Would write file: ${filePath}`);
581
+ logger.verbose(`File content length: ${fileContent.length} characters`);
582
+ } else {
583
+ (0, import_fs3.writeFileSync)(resolvedPath, fileContent, "utf-8");
584
+ logger.success(`Generated file: ${filePath}`);
562
585
  }
563
- (0, import_fs3.writeFileSync)(resolvedPath, fileContent, "utf-8");
564
- console.log(`Generated file: ${filePath}`);
565
586
  }
566
587
 
588
+ // src/utils/logger.ts
589
+ var import_chalk = __toESM(require("chalk"), 1);
590
+ var import_ora = __toESM(require("ora"), 1);
591
+ var Logger = class {
592
+ _verbose;
593
+ _dryRun;
594
+ constructor(options = {}) {
595
+ this._verbose = options.verbose ?? false;
596
+ this._dryRun = options.dryRun ?? false;
597
+ }
598
+ /**
599
+ * Check if verbose mode is enabled
600
+ */
601
+ get isVerbose() {
602
+ return this._verbose;
603
+ }
604
+ /**
605
+ * Check if dry-run mode is enabled
606
+ */
607
+ get isDryRun() {
608
+ return this._dryRun;
609
+ }
610
+ /**
611
+ * Log info message (always shown)
612
+ */
613
+ info(message) {
614
+ console.log(import_chalk.default.blue("\u2139"), message);
615
+ }
616
+ /**
617
+ * Log success message (always shown)
618
+ */
619
+ success(message) {
620
+ console.log(import_chalk.default.green("\u2713"), message);
621
+ }
622
+ /**
623
+ * Log warning message (always shown)
624
+ */
625
+ warn(message) {
626
+ console.log(import_chalk.default.yellow("\u26A0"), message);
627
+ }
628
+ /**
629
+ * Log error message (always shown)
630
+ */
631
+ error(message) {
632
+ console.error(import_chalk.default.red("\u2717"), message);
633
+ }
634
+ /**
635
+ * Log verbose message (only shown in verbose mode)
636
+ */
637
+ verbose(message) {
638
+ if (this._verbose) {
639
+ console.log(import_chalk.default.gray("\u2192"), message);
640
+ }
641
+ }
642
+ /**
643
+ * Log dry-run message (only shown in dry-run mode)
644
+ */
645
+ dryRun(message) {
646
+ if (this._dryRun) {
647
+ console.log(import_chalk.default.cyan("[DRY-RUN]"), message);
648
+ }
649
+ }
650
+ /**
651
+ * Create a spinner for long-running operations
652
+ */
653
+ spinner(text) {
654
+ return (0, import_ora.default)({
655
+ text,
656
+ color: "cyan",
657
+ spinner: "dots"
658
+ });
659
+ }
660
+ /**
661
+ * Log file operation (respects dry-run)
662
+ */
663
+ fileOperation(action, filePath) {
664
+ if (this._dryRun) {
665
+ this.dryRun(`Would ${action}: ${import_chalk.default.underline(filePath)}`);
666
+ } else {
667
+ this.verbose(`${action}: ${filePath}`);
668
+ }
669
+ }
670
+ /**
671
+ * Format error with context
672
+ */
673
+ formatError(error, context) {
674
+ let errorMessage = "";
675
+ if (error instanceof Error) {
676
+ errorMessage = error.message;
677
+ if (this._verbose && error.stack) {
678
+ errorMessage += `
679
+ ${import_chalk.default.gray(error.stack)}`;
680
+ }
681
+ } else if (error && typeof error === "object") {
682
+ const errorObj = error;
683
+ if (errorObj.message) {
684
+ errorMessage = String(errorObj.message);
685
+ } else if (errorObj.status) {
686
+ errorMessage = `HTTP ${errorObj.status}: ${errorObj.statusText || "Unknown error"}`;
687
+ } else {
688
+ try {
689
+ errorMessage = JSON.stringify(error, null, 2);
690
+ } catch {
691
+ errorMessage = String(error);
692
+ }
693
+ }
694
+ } else {
695
+ errorMessage = String(error);
696
+ }
697
+ if (context) {
698
+ return `${import_chalk.default.red(context)}
699
+ ${errorMessage}`;
700
+ }
701
+ return errorMessage;
702
+ }
703
+ };
704
+ var logger_default = Logger;
705
+
567
706
  // src/index.ts
568
- var main = async (configPath) => {
707
+ var main = async (options) => {
708
+ const opts = typeof options === "string" ? { configPath: options } : options ?? {};
709
+ const logger = new logger_default({
710
+ verbose: opts.verbose ?? false,
711
+ dryRun: opts.dryRun ?? false
712
+ });
569
713
  try {
570
- const config = await loadConfig(configPath);
571
- console.log("Config loaded successfully");
714
+ const configSpinner = logger.spinner("Loading configuration...");
715
+ configSpinner.start();
716
+ const config = await loadConfig(opts.configPath);
717
+ configSpinner.succeed("Configuration loaded successfully");
718
+ logger.verbose(`Config path: ${opts.configPath || "default"}`);
719
+ logger.verbose(`Overwrite mode: ${config.overwrite ? "enabled" : "disabled"}`);
572
720
  const generatesArray = Array.isArray(config.generates) ? config.generates : [config.generates];
721
+ logger.verbose(`Found ${generatesArray.length} generate configuration(s)`);
573
722
  for (const urlSchema of generatesArray) {
574
723
  for (const [filePath, generatorConfig] of Object.entries(urlSchema)) {
575
724
  try {
576
- await fetchAlgoliaData(filePath, generatorConfig, config.overwrite);
725
+ await fetchAlgoliaData(filePath, generatorConfig, config.overwrite, logger);
577
726
  } catch (error) {
578
- console.error(`
579
- Error processing file: ${filePath}`);
580
- if (error instanceof Error) {
581
- console.error(error.message);
582
- if (error.stack) {
583
- console.error(error.stack);
584
- }
585
- } else {
586
- try {
587
- console.error(JSON.stringify(error, null, 2));
588
- } catch {
589
- console.error(String(error));
590
- }
591
- }
727
+ logger.error(`Error processing file: ${filePath}`);
728
+ const errorMessage = logger.formatError(error, "Processing failed");
729
+ logger.error(errorMessage);
592
730
  }
593
731
  }
594
732
  }
595
- } catch (error) {
596
- console.error("Error loading config:");
597
- if (error instanceof Error) {
598
- console.error(error.message);
733
+ if (opts.dryRun) {
734
+ logger.info("Dry-run completed. No files were written.");
599
735
  } else {
600
- console.error(String(error));
736
+ logger.success("All files generated successfully!");
601
737
  }
738
+ } catch (error) {
739
+ logger.error("Failed to load configuration");
740
+ const errorMessage = logger.formatError(error, "Configuration error");
741
+ logger.error(errorMessage);
602
742
  process.exit(1);
603
743
  }
604
744
  };
package/dist/index.d.cts CHANGED
@@ -29,9 +29,14 @@ declare function validateUrlSchema(urlSchema: unknown, path: string): asserts ur
29
29
  */
30
30
  declare function validateGeneratorConfig(config: unknown, path: string): asserts config is AlgoliaCodegenGeneratorConfig;
31
31
 
32
+ interface MainOptions {
33
+ configPath?: string;
34
+ verbose?: boolean;
35
+ dryRun?: boolean;
36
+ }
32
37
  /**
33
38
  * Main function to load and process configuration
34
39
  */
35
- declare const main: (configPath?: string) => Promise<void>;
40
+ declare const main: (options?: MainOptions | string) => Promise<void>;
36
41
 
37
- export { type AlgoliaCodegenConfig, type AlgoliaCodegenGeneratorConfig, type InstanceOrArray, type UrlSchema, main, validateConfig, validateGeneratorConfig, validateUrlSchema };
42
+ export { type AlgoliaCodegenConfig, type AlgoliaCodegenGeneratorConfig, type InstanceOrArray, type MainOptions, type UrlSchema, main, validateConfig, validateGeneratorConfig, validateUrlSchema };
package/dist/index.d.ts CHANGED
@@ -29,9 +29,14 @@ declare function validateUrlSchema(urlSchema: unknown, path: string): asserts ur
29
29
  */
30
30
  declare function validateGeneratorConfig(config: unknown, path: string): asserts config is AlgoliaCodegenGeneratorConfig;
31
31
 
32
+ interface MainOptions {
33
+ configPath?: string;
34
+ verbose?: boolean;
35
+ dryRun?: boolean;
36
+ }
32
37
  /**
33
38
  * Main function to load and process configuration
34
39
  */
35
- declare const main: (configPath?: string) => Promise<void>;
40
+ declare const main: (options?: MainOptions | string) => Promise<void>;
36
41
 
37
- export { type AlgoliaCodegenConfig, type AlgoliaCodegenGeneratorConfig, type InstanceOrArray, type UrlSchema, main, validateConfig, validateGeneratorConfig, validateUrlSchema };
42
+ export { type AlgoliaCodegenConfig, type AlgoliaCodegenGeneratorConfig, type InstanceOrArray, type MainOptions, type UrlSchema, main, validateConfig, validateGeneratorConfig, validateUrlSchema };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  validateConfig,
4
4
  validateGeneratorConfig,
5
5
  validateUrlSchema
6
- } from "./chunk-4T5DZK75.js";
6
+ } from "./chunk-USMYY2DN.js";
7
7
  export {
8
8
  main,
9
9
  validateConfig,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "algolia-codegen",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Generate TypeScript types from Algolia indices",
5
5
  "license": "MIT",
6
6
  "author": "nightlightmare",
@@ -38,9 +38,11 @@
38
38
  ],
39
39
  "dependencies": {
40
40
  "algoliasearch": "^5.46.2",
41
+ "chalk": "^5.6.2",
41
42
  "commander": "^14.0.2",
42
43
  "dotenv": "^17.2.3",
43
44
  "esbuild": "^0.27.0",
45
+ "ora": "^9.0.0",
44
46
  "tsx": "^4.21.0"
45
47
  },
46
48
  "devDependencies": {