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/dist/index.cjs CHANGED
@@ -50,7 +50,7 @@ async function loadTypeScriptConfig(resolvedPath) {
50
50
  const result = esbuild.transformSync(source, {
51
51
  loader: "ts",
52
52
  format: "esm",
53
- target: "node18",
53
+ target: "node20",
54
54
  sourcefile: resolvedPath
55
55
  });
56
56
  if (!result.code) {
@@ -64,10 +64,8 @@ async function loadTypeScriptConfig(resolvedPath) {
64
64
  // src/utils/validations/generator-config.ts
65
65
  function validateGeneratorConfig(config, path) {
66
66
  if (typeof config !== "object" || config === null || Array.isArray(config)) {
67
- throw new Error(
68
- `Invalid generator config: must be an object
69
- Path: ${path}`
70
- );
67
+ throw new Error(`Invalid generator config: must be an object
68
+ Path: ${path}`);
71
69
  }
72
70
  const cfg = config;
73
71
  const requiredFields = [
@@ -109,10 +107,8 @@ Received: ${typeof cfg.postfix}`
109
107
  // src/utils/validations/url-schema.ts
110
108
  function validateUrlSchema(urlSchema, path) {
111
109
  if (typeof urlSchema !== "object" || urlSchema === null || Array.isArray(urlSchema)) {
112
- throw new Error(
113
- `Invalid generates entry: must be an object
114
- Path: ${path}`
115
- );
110
+ throw new Error(`Invalid generates entry: must be an object
111
+ Path: ${path}`);
116
112
  }
117
113
  const schema = urlSchema;
118
114
  for (const [filePath, generatorConfig] of Object.entries(schema)) {
@@ -129,10 +125,8 @@ Path: ${path}[${filePath}]`
129
125
  // src/utils/validations/config.ts
130
126
  function validateConfig(config, configPath) {
131
127
  if (typeof config !== "object" || config === null || Array.isArray(config)) {
132
- throw new Error(
133
- `Invalid config: must be an object
134
- Config file: ${configPath}`
135
- );
128
+ throw new Error(`Invalid config: must be an object
129
+ Config file: ${configPath}`);
136
130
  }
137
131
  const cfg = config;
138
132
  if (!("overwrite" in cfg)) {
@@ -213,10 +207,8 @@ Make sure esbuild is installed as a dependency.`
213
207
  configModule = await import(configUrl);
214
208
  } catch (importError) {
215
209
  const errorMessage = importError instanceof Error ? importError.message : String(importError);
216
- throw new Error(
217
- `Failed to import config file: ${resolvedPath}
218
- Error: ${errorMessage}`
219
- );
210
+ throw new Error(`Failed to import config file: ${resolvedPath}
211
+ Error: ${errorMessage}`);
220
212
  }
221
213
  }
222
214
  if (!configModule.default) {
@@ -345,7 +337,7 @@ var TypeGenerator = class {
345
337
  return `${this.prefix}Hit${this.postfix}`;
346
338
  }
347
339
  const lastPart = path[path.length - 1];
348
- const parts = lastPart.replace(/[\[\]]/g, "").split(/[-_\s]+/).filter(Boolean);
340
+ const parts = lastPart.replace(/[[\]]/g, "").split(/[-_\s]+/).filter(Boolean);
349
341
  const pascalCase = parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join("");
350
342
  if (path.length > 1) {
351
343
  const parent = path[path.length - 2];
@@ -497,30 +489,34 @@ function generateTypeScriptTypes(sampleHit, config) {
497
489
  }
498
490
 
499
491
  // src/utils/fetch-algolia-data.ts
500
- async function fetchAlgoliaData(filePath, generatorConfig, overwrite) {
501
- console.log(`
502
- Processing file: ${filePath}`);
492
+ async function fetchAlgoliaData(filePath, generatorConfig, overwrite, logger) {
493
+ logger.info(`Processing file: ${filePath}`);
503
494
  const resolvedPath = (0, import_path2.resolve)(process.cwd(), filePath);
495
+ logger.verbose(`Resolved path: ${resolvedPath}`);
504
496
  if ((0, import_fs3.existsSync)(resolvedPath) && !overwrite) {
505
497
  throw new Error(
506
498
  `File already exists: ${resolvedPath}
507
499
  Set overwrite: true in config to allow overwriting existing files.`
508
500
  );
509
501
  }
510
- console.log(`Connecting to Algolia...`);
511
- console.log(`App ID: ${generatorConfig.appId}`);
502
+ const connectSpinner = logger.spinner("Connecting to Algolia...");
503
+ connectSpinner.start();
512
504
  let client;
513
505
  try {
514
- client = (0, import_algoliasearch.algoliasearch)(
515
- generatorConfig.appId,
516
- generatorConfig.searchKey
517
- );
506
+ logger.verbose(`App ID: ${generatorConfig.appId}`);
507
+ logger.verbose(`Index: ${generatorConfig.indexName}`);
508
+ client = (0, import_algoliasearch.algoliasearch)(generatorConfig.appId, generatorConfig.searchKey);
509
+ connectSpinner.succeed("Connected to Algolia");
518
510
  } catch (error) {
511
+ connectSpinner.fail("Failed to connect to Algolia");
519
512
  throw new Error(
520
513
  `Failed to initialize Algolia client: ${error instanceof Error ? error.message : String(error)}`
521
514
  );
522
515
  }
523
- 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();
524
520
  let results;
525
521
  try {
526
522
  results = await client.search([
@@ -532,7 +528,9 @@ Set overwrite: true in config to allow overwriting existing files.`
532
528
  }
533
529
  }
534
530
  ]);
531
+ fetchSpinner.succeed("Sample record fetched successfully");
535
532
  } catch (error) {
533
+ fetchSpinner.fail("Failed to fetch data from Algolia");
536
534
  let errorMessage;
537
535
  if (error instanceof Error) {
538
536
  errorMessage = error.message;
@@ -564,52 +562,183 @@ Set overwrite: true in config to allow overwriting existing files.`
564
562
  throw new Error(`No hits found in Algolia index: ${generatorConfig.indexName}`);
565
563
  }
566
564
  const sampleHit = result.hits[0];
567
- console.log("Sample record fetched successfully");
568
- 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();
569
568
  const fileContent = generateTypeScriptTypes(sampleHit, generatorConfig);
569
+ generateSpinner.succeed("TypeScript types generated");
570
570
  const dir = (0, import_path2.dirname)(resolvedPath);
571
571
  if (!(0, import_fs3.existsSync)(dir)) {
572
- (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}`);
573
585
  }
574
- (0, import_fs3.writeFileSync)(resolvedPath, fileContent, "utf-8");
575
- console.log(`Generated file: ${filePath}`);
576
586
  }
577
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
+
578
706
  // src/index.ts
579
- 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
+ });
580
713
  try {
581
- const config = await loadConfig(configPath);
582
- 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"}`);
583
720
  const generatesArray = Array.isArray(config.generates) ? config.generates : [config.generates];
721
+ logger.verbose(`Found ${generatesArray.length} generate configuration(s)`);
584
722
  for (const urlSchema of generatesArray) {
585
723
  for (const [filePath, generatorConfig] of Object.entries(urlSchema)) {
586
724
  try {
587
- await fetchAlgoliaData(filePath, generatorConfig, config.overwrite);
725
+ await fetchAlgoliaData(filePath, generatorConfig, config.overwrite, logger);
588
726
  } catch (error) {
589
- console.error(`
590
- Error processing file: ${filePath}`);
591
- if (error instanceof Error) {
592
- console.error(error.message);
593
- if (error.stack) {
594
- console.error(error.stack);
595
- }
596
- } else {
597
- try {
598
- console.error(JSON.stringify(error, null, 2));
599
- } catch {
600
- console.error(String(error));
601
- }
602
- }
727
+ logger.error(`Error processing file: ${filePath}`);
728
+ const errorMessage = logger.formatError(error, "Processing failed");
729
+ logger.error(errorMessage);
603
730
  }
604
731
  }
605
732
  }
606
- } catch (error) {
607
- console.error("Error loading config:");
608
- if (error instanceof Error) {
609
- console.error(error.message);
733
+ if (opts.dryRun) {
734
+ logger.info("Dry-run completed. No files were written.");
610
735
  } else {
611
- console.error(String(error));
736
+ logger.success("All files generated successfully!");
612
737
  }
738
+ } catch (error) {
739
+ logger.error("Failed to load configuration");
740
+ const errorMessage = logger.formatError(error, "Configuration error");
741
+ logger.error(errorMessage);
613
742
  process.exit(1);
614
743
  }
615
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-R66ECGLW.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.2",
3
+ "version": "0.1.4",
4
4
  "description": "Generate TypeScript types from Algolia indices",
5
5
  "license": "MIT",
6
6
  "author": "nightlightmare",
@@ -38,18 +38,26 @@
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": {
47
49
  "@types/node": "^25.0.3",
50
+ "@vitest/coverage-v8": "^2.1.8",
51
+ "eslint": "^9.39.2",
52
+ "eslint-config-prettier": "^10.1.8",
53
+ "prettier": "^3.7.4",
48
54
  "tsup": "^8.0.0",
49
- "typescript": "^5.4.0"
55
+ "typescript": "^5.4.0",
56
+ "typescript-eslint": "^8.50.1",
57
+ "vitest": "^2.1.8"
50
58
  },
51
59
  "engines": {
52
- "node": ">=18"
60
+ "node": ">=20"
53
61
  },
54
62
  "publishConfig": {
55
63
  "access": "public"
@@ -58,6 +66,13 @@
58
66
  "build": "tsup",
59
67
  "dev": "tsup --watch",
60
68
  "clean": "rm -rf dist",
61
- "type-check": "tsc --noEmit"
69
+ "type-check": "tsc --noEmit",
70
+ "lint": "eslint .",
71
+ "lint:fix": "eslint . --fix",
72
+ "format": "prettier --write .",
73
+ "format:check": "prettier --check .",
74
+ "test": "vitest run",
75
+ "test:watch": "vitest",
76
+ "test:coverage": "vitest run --coverage"
62
77
  }
63
78
  }