zod-codegen 1.4.0 → 1.5.0

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.
Files changed (31) hide show
  1. package/.github/workflows/ci.yml +18 -18
  2. package/.github/workflows/release.yml +8 -8
  3. package/CHANGELOG.md +26 -0
  4. package/dist/src/services/code-generator.service.d.ts +15 -0
  5. package/dist/src/services/code-generator.service.d.ts.map +1 -1
  6. package/dist/src/services/code-generator.service.js +124 -6
  7. package/package.json +14 -19
  8. package/src/services/code-generator.service.ts +160 -11
  9. package/tests/unit/code-generator.test.ts +199 -3
  10. package/tests/unit/generator.test.ts +2 -2
  11. package/tsconfig.json +1 -1
  12. package/.claude/settings.local.json +0 -43
  13. package/dist/scripts/update-manifest.d.ts +0 -14
  14. package/dist/scripts/update-manifest.d.ts.map +0 -1
  15. package/dist/scripts/update-manifest.js +0 -31
  16. package/dist/tests/integration/cli.test.d.ts +0 -2
  17. package/dist/tests/integration/cli.test.d.ts.map +0 -1
  18. package/dist/tests/integration/cli.test.js +0 -25
  19. package/dist/tests/unit/code-generator.test.d.ts +0 -2
  20. package/dist/tests/unit/code-generator.test.d.ts.map +0 -1
  21. package/dist/tests/unit/code-generator.test.js +0 -459
  22. package/dist/tests/unit/file-reader.test.d.ts +0 -2
  23. package/dist/tests/unit/file-reader.test.d.ts.map +0 -1
  24. package/dist/tests/unit/file-reader.test.js +0 -110
  25. package/dist/tests/unit/generator.test.d.ts +0 -2
  26. package/dist/tests/unit/generator.test.d.ts.map +0 -1
  27. package/dist/tests/unit/generator.test.js +0 -100
  28. package/dist/tests/unit/naming-convention.test.d.ts +0 -2
  29. package/dist/tests/unit/naming-convention.test.d.ts.map +0 -1
  30. package/dist/tests/unit/naming-convention.test.js +0 -231
  31. package/scripts/republish-versions.sh +0 -94
@@ -21,6 +21,10 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
21
21
  private readonly namingConvention: NamingConvention | undefined;
22
22
  private readonly operationNameTransformer: OperationNameTransformer | undefined;
23
23
 
24
+ // Track circular dependencies for z.lazy() wrapping
25
+ private circularSchemas = new Set<string>();
26
+ private currentSchemaName: string | null = null;
27
+
24
28
  constructor(options: GeneratorOptions = {}) {
25
29
  this.namingConvention = options.namingConvention;
26
30
  this.operationNameTransformer = options.operationNameTransformer;
@@ -85,12 +89,25 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
85
89
 
86
90
  private buildSchemas(openapi: OpenApiSpecType): Record<string, ts.VariableStatement> {
87
91
  const schemasEntries = Object.entries(openapi.components?.schemas ?? {});
88
- const sortedSchemaNames = this.topologicalSort(Object.fromEntries(schemasEntries));
92
+ const schemasMap = Object.fromEntries(schemasEntries);
93
+
94
+ // Detect circular dependencies before building schemas
95
+ this.circularSchemas = this.detectCircularDependencies(schemasMap);
96
+
97
+ const sortedSchemaNames = this.topologicalSort(schemasMap);
89
98
 
90
99
  return sortedSchemaNames.reduce<Record<string, ts.VariableStatement>>((schemaRegistered, name) => {
91
100
  const schema = openapi.components?.schemas?.[name];
92
101
  if (!schema) return schemaRegistered;
93
102
 
103
+ // Set context for current schema being built
104
+ this.currentSchemaName = name;
105
+
106
+ const schemaExpression = this.buildSchema(schema);
107
+
108
+ // Clear context
109
+ this.currentSchemaName = null;
110
+
94
111
  const variableStatement = ts.factory.createVariableStatement(
95
112
  [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
96
113
  ts.factory.createVariableDeclarationList(
@@ -99,7 +116,7 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
99
116
  ts.factory.createIdentifier(this.typeBuilder.sanitizeIdentifier(name)),
100
117
  undefined,
101
118
  undefined,
102
- this.buildSchema(schema),
119
+ schemaExpression,
103
120
  ),
104
121
  ],
105
122
  ts.NodeFlags.Const,
@@ -136,7 +153,7 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
136
153
  const methods = this.buildClientMethods(openapi, schemas);
137
154
 
138
155
  return ts.factory.createClassDeclaration(
139
- [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
156
+ [ts.factory.createToken(ts.SyntaxKind.ExportKeyword), ts.factory.createToken(ts.SyntaxKind.DefaultKeyword)],
140
157
  ts.factory.createIdentifier(clientName),
141
158
  undefined,
142
159
  undefined,
@@ -275,9 +292,9 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
275
292
 
276
293
  private buildHttpRequestMethod(): ts.MethodDeclaration {
277
294
  return ts.factory.createMethodDeclaration(
278
- [ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)],
295
+ [ts.factory.createToken(ts.SyntaxKind.ProtectedKeyword), ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)],
279
296
  undefined,
280
- ts.factory.createPrivateIdentifier('#makeRequest'),
297
+ ts.factory.createIdentifier('makeRequest'),
281
298
  undefined,
282
299
  [this.typeBuilder.createGenericType('T')],
283
300
  [
@@ -1002,10 +1019,7 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
1002
1019
 
1003
1020
  // Call makeRequest
1004
1021
  const makeRequestCall = ts.factory.createCallExpression(
1005
- ts.factory.createPropertyAccessExpression(
1006
- ts.factory.createThis(),
1007
- ts.factory.createPrivateIdentifier('#makeRequest'),
1008
- ),
1022
+ ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createIdentifier('makeRequest')),
1009
1023
  undefined,
1010
1024
  [ts.factory.createStringLiteral(method.toUpperCase(), true), pathExpression, optionsExpression],
1011
1025
  );
@@ -2691,10 +2705,145 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
2691
2705
  return false;
2692
2706
  }
2693
2707
 
2694
- private buildFromReference(reference: ReferenceType): ts.Identifier {
2708
+ private buildFromReference(reference: ReferenceType): ts.CallExpression | ts.Identifier {
2695
2709
  const {$ref = ''} = Reference.parse(reference);
2696
2710
  const refName = $ref.split('/').pop() ?? 'never';
2697
- return ts.factory.createIdentifier(this.typeBuilder.sanitizeIdentifier(refName));
2711
+ const sanitizedRefName = this.typeBuilder.sanitizeIdentifier(refName);
2712
+
2713
+ // Check if this reference creates a circular dependency
2714
+ if (this.isCircularReference(refName)) {
2715
+ // Generate: z.lazy(() => RefSchema)
2716
+ return ts.factory.createCallExpression(
2717
+ ts.factory.createPropertyAccessExpression(
2718
+ ts.factory.createIdentifier('z'),
2719
+ ts.factory.createIdentifier('lazy'),
2720
+ ),
2721
+ undefined,
2722
+ [
2723
+ ts.factory.createArrowFunction(
2724
+ undefined,
2725
+ undefined,
2726
+ [],
2727
+ undefined,
2728
+ ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
2729
+ ts.factory.createIdentifier(sanitizedRefName),
2730
+ ),
2731
+ ],
2732
+ );
2733
+ }
2734
+
2735
+ return ts.factory.createIdentifier(sanitizedRefName);
2736
+ }
2737
+
2738
+ /**
2739
+ * Determines if a reference creates a circular dependency that needs z.lazy().
2740
+ * A reference is circular if:
2741
+ * 1. It's a direct self-reference (schema references itself)
2742
+ * 2. It's part of a circular dependency chain (A -> B -> A)
2743
+ */
2744
+ private isCircularReference(refName: string): boolean {
2745
+ // Case 1: Direct self-reference
2746
+ if (refName === this.currentSchemaName) {
2747
+ return true;
2748
+ }
2749
+
2750
+ // Case 2: Reference to a schema that's part of a circular dependency chain
2751
+ // and we're currently building a schema that's also in that chain
2752
+ if (
2753
+ this.circularSchemas.has(refName) &&
2754
+ this.currentSchemaName !== null &&
2755
+ this.circularSchemas.has(this.currentSchemaName)
2756
+ ) {
2757
+ return true;
2758
+ }
2759
+
2760
+ return false;
2761
+ }
2762
+
2763
+ /**
2764
+ * Detects schemas that are part of circular dependency chains.
2765
+ * Uses a modified Tarjan's algorithm to find strongly connected components (SCCs).
2766
+ * Schemas in SCCs with more than one node, or self-referencing schemas, are circular.
2767
+ */
2768
+ private detectCircularDependencies(schemas: Record<string, unknown>): Set<string> {
2769
+ const circularSchemas = new Set<string>();
2770
+
2771
+ // Build dependency graph
2772
+ const graph = new Map<string, string[]>();
2773
+ for (const [name, schema] of Object.entries(schemas)) {
2774
+ const dependencies = jp
2775
+ .query(schema, '$..["$ref"]')
2776
+ .filter((ref: string) => ref.startsWith('#/components/schemas/'))
2777
+ .map((ref: string) => ref.replace('#/components/schemas/', ''))
2778
+ .filter((dep: string) => dep in schemas);
2779
+ graph.set(name, dependencies);
2780
+ }
2781
+
2782
+ // Tarjan's algorithm for finding SCCs
2783
+ let index = 0;
2784
+ const indices = new Map<string, number>();
2785
+ const lowlinks = new Map<string, number>();
2786
+ const onStack = new Set<string>();
2787
+ const stack: string[] = [];
2788
+
2789
+ const strongConnect = (node: string): void => {
2790
+ indices.set(node, index);
2791
+ lowlinks.set(node, index);
2792
+ index++;
2793
+ stack.push(node);
2794
+ onStack.add(node);
2795
+
2796
+ const neighbors = graph.get(node) ?? [];
2797
+ for (const neighbor of neighbors) {
2798
+ if (!indices.has(neighbor)) {
2799
+ strongConnect(neighbor);
2800
+ const currentLowlink = lowlinks.get(node) ?? 0;
2801
+ const neighborLowlink = lowlinks.get(neighbor) ?? 0;
2802
+ lowlinks.set(node, Math.min(currentLowlink, neighborLowlink));
2803
+ } else if (onStack.has(neighbor)) {
2804
+ const currentLowlink = lowlinks.get(node) ?? 0;
2805
+ const neighborIndex = indices.get(neighbor) ?? 0;
2806
+ lowlinks.set(node, Math.min(currentLowlink, neighborIndex));
2807
+ }
2808
+ }
2809
+
2810
+ // If node is a root of an SCC
2811
+ if (lowlinks.get(node) === indices.get(node)) {
2812
+ const scc: string[] = [];
2813
+ let w: string | undefined;
2814
+ do {
2815
+ w = stack.pop();
2816
+ if (w !== undefined) {
2817
+ onStack.delete(w);
2818
+ scc.push(w);
2819
+ }
2820
+ } while (w !== undefined && w !== node);
2821
+
2822
+ // An SCC is circular if it has more than one node
2823
+ // or if it has one node that references itself
2824
+ if (scc.length > 1) {
2825
+ for (const schemaName of scc) {
2826
+ circularSchemas.add(schemaName);
2827
+ }
2828
+ } else if (scc.length === 1) {
2829
+ const schemaName = scc[0];
2830
+ if (schemaName !== undefined) {
2831
+ const deps = graph.get(schemaName) ?? [];
2832
+ if (deps.includes(schemaName)) {
2833
+ circularSchemas.add(schemaName);
2834
+ }
2835
+ }
2836
+ }
2837
+ }
2838
+ };
2839
+
2840
+ for (const node of graph.keys()) {
2841
+ if (!indices.has(node)) {
2842
+ strongConnect(node);
2843
+ }
2844
+ }
2845
+
2846
+ return circularSchemas;
2698
2847
  }
2699
2848
 
2700
2849
  private topologicalSort(schemas: Record<string, unknown>): string[] {
@@ -212,7 +212,7 @@ describe('TypeScriptCodeGeneratorService', () => {
212
212
  expect(code).toBeTruthy();
213
213
  expect(typeof code).toBe('string');
214
214
  expect(code).toMatch(/import\s*{\s*z\s*}\s*from\s*['"]zod['"]/);
215
- expect(code).toContain('export class');
215
+ expect(code).toContain('export default class');
216
216
  });
217
217
 
218
218
  it('should generate schemas for component schemas', () => {
@@ -275,7 +275,7 @@ describe('TypeScriptCodeGeneratorService', () => {
275
275
 
276
276
  const code = generator.generate(spec);
277
277
  expect(code).toContain('async getUsers');
278
- expect(code).toContain('#makeRequest');
278
+ expect(code).toContain('makeRequest');
279
279
  });
280
280
 
281
281
  it('should generate getBaseRequestOptions method', () => {
@@ -408,7 +408,7 @@ describe('TypeScriptCodeGeneratorService', () => {
408
408
  expect(code).not.toContain('ExecutionMode: z.enum');
409
409
  });
410
410
 
411
- it('should merge baseOptions with request-specific options in #makeRequest', () => {
411
+ it('should merge baseOptions with request-specific options in makeRequest', () => {
412
412
  const spec: OpenApiSpecType = {
413
413
  openapi: '3.0.0',
414
414
  info: {
@@ -506,4 +506,200 @@ describe('TypeScriptCodeGeneratorService', () => {
506
506
  expect(result).toBeDefined();
507
507
  });
508
508
  });
509
+
510
+ describe('circular dependencies', () => {
511
+ it('should use z.lazy() for direct self-referencing schemas', () => {
512
+ const spec: OpenApiSpecType = {
513
+ openapi: '3.0.0',
514
+ info: {
515
+ title: 'Test API',
516
+ version: '1.0.0',
517
+ },
518
+ paths: {},
519
+ components: {
520
+ schemas: {
521
+ TreeNode: {
522
+ type: 'object',
523
+ properties: {
524
+ value: {type: 'string'},
525
+ children: {
526
+ type: 'array',
527
+ items: {$ref: '#/components/schemas/TreeNode'},
528
+ },
529
+ },
530
+ required: ['value'],
531
+ },
532
+ },
533
+ },
534
+ };
535
+
536
+ const code = generator.generate(spec);
537
+ expect(code).toContain('z.lazy(() => TreeNode)');
538
+ });
539
+
540
+ it('should use z.lazy() for indirect circular dependencies (A -> B -> A)', () => {
541
+ const spec: OpenApiSpecType = {
542
+ openapi: '3.0.0',
543
+ info: {
544
+ title: 'Test API',
545
+ version: '1.0.0',
546
+ },
547
+ paths: {},
548
+ components: {
549
+ schemas: {
550
+ Person: {
551
+ type: 'object',
552
+ properties: {
553
+ name: {type: 'string'},
554
+ bestFriend: {$ref: '#/components/schemas/Friend'},
555
+ },
556
+ required: ['name'],
557
+ },
558
+ Friend: {
559
+ type: 'object',
560
+ properties: {
561
+ nickname: {type: 'string'},
562
+ person: {$ref: '#/components/schemas/Person'},
563
+ },
564
+ required: ['nickname'],
565
+ },
566
+ },
567
+ },
568
+ };
569
+
570
+ const code = generator.generate(spec);
571
+ // Both references should use z.lazy()
572
+ expect(code).toContain('z.lazy(() => Friend)');
573
+ expect(code).toContain('z.lazy(() => Person)');
574
+ });
575
+
576
+ it('should not use z.lazy() for non-circular references', () => {
577
+ const spec: OpenApiSpecType = {
578
+ openapi: '3.0.0',
579
+ info: {
580
+ title: 'Test API',
581
+ version: '1.0.0',
582
+ },
583
+ paths: {},
584
+ components: {
585
+ schemas: {
586
+ User: {
587
+ type: 'object',
588
+ properties: {
589
+ id: {type: 'integer'},
590
+ profile: {$ref: '#/components/schemas/Profile'},
591
+ },
592
+ required: ['id'],
593
+ },
594
+ Profile: {
595
+ type: 'object',
596
+ properties: {
597
+ name: {type: 'string'},
598
+ },
599
+ required: ['name'],
600
+ },
601
+ },
602
+ },
603
+ };
604
+
605
+ const code = generator.generate(spec);
606
+ // Profile should be referenced directly, not with z.lazy()
607
+ expect(code).not.toContain('z.lazy(() => Profile)');
608
+ expect(code).toContain('profile: Profile.optional()');
609
+ });
610
+
611
+ it('should handle complex circular chains (A -> B -> C -> A)', () => {
612
+ const spec: OpenApiSpecType = {
613
+ openapi: '3.0.0',
614
+ info: {
615
+ title: 'Test API',
616
+ version: '1.0.0',
617
+ },
618
+ paths: {},
619
+ components: {
620
+ schemas: {
621
+ Alpha: {
622
+ type: 'object',
623
+ properties: {
624
+ beta: {$ref: '#/components/schemas/Beta'},
625
+ },
626
+ },
627
+ Beta: {
628
+ type: 'object',
629
+ properties: {
630
+ gamma: {$ref: '#/components/schemas/Gamma'},
631
+ },
632
+ },
633
+ Gamma: {
634
+ type: 'object',
635
+ properties: {
636
+ alpha: {$ref: '#/components/schemas/Alpha'},
637
+ },
638
+ },
639
+ },
640
+ },
641
+ };
642
+
643
+ const code = generator.generate(spec);
644
+ // All references in the cycle should use z.lazy()
645
+ expect(code).toContain('z.lazy(() => Beta)');
646
+ expect(code).toContain('z.lazy(() => Gamma)');
647
+ expect(code).toContain('z.lazy(() => Alpha)');
648
+ });
649
+
650
+ it('should handle self-referencing schemas in arrays', () => {
651
+ const spec: OpenApiSpecType = {
652
+ openapi: '3.0.0',
653
+ info: {
654
+ title: 'Test API',
655
+ version: '1.0.0',
656
+ },
657
+ paths: {},
658
+ components: {
659
+ schemas: {
660
+ Category: {
661
+ type: 'object',
662
+ properties: {
663
+ name: {type: 'string'},
664
+ subcategories: {
665
+ type: 'array',
666
+ items: {$ref: '#/components/schemas/Category'},
667
+ },
668
+ },
669
+ required: ['name'],
670
+ },
671
+ },
672
+ },
673
+ };
674
+
675
+ const code = generator.generate(spec);
676
+ expect(code).toContain('z.array(z.lazy(() => Category))');
677
+ });
678
+
679
+ it('should handle self-referencing schemas in anyOf', () => {
680
+ const spec: OpenApiSpecType = {
681
+ openapi: '3.0.0',
682
+ info: {
683
+ title: 'Test API',
684
+ version: '1.0.0',
685
+ },
686
+ paths: {},
687
+ components: {
688
+ schemas: {
689
+ Expression: {
690
+ type: 'object',
691
+ properties: {
692
+ value: {
693
+ anyOf: [{type: 'string'}, {$ref: '#/components/schemas/Expression'}],
694
+ },
695
+ },
696
+ },
697
+ },
698
+ },
699
+ };
700
+
701
+ const code = generator.generate(spec);
702
+ expect(code).toContain('z.lazy(() => Expression)');
703
+ });
704
+ });
509
705
  });
@@ -68,7 +68,7 @@ describe('Generator', () => {
68
68
  // Verify file contains expected content
69
69
  const content = readFileSync(outputFile, 'utf-8');
70
70
  expect(content).toMatch(/import\s*{\s*z\s*}\s*from\s*['"]zod['"]/);
71
- expect(content).toContain('export class');
71
+ expect(content).toContain('export default class');
72
72
  expect(content).toContain('getBaseRequestOptions');
73
73
  });
74
74
 
@@ -109,7 +109,7 @@ describe('Generator', () => {
109
109
  expect(content).toMatch(/import\s*{\s*z\s*}\s*from\s*['"]zod['"]/);
110
110
  expect(content).toContain('export const');
111
111
  expect(content).toContain('protected getBaseRequestOptions');
112
- expect(content).toContain('async #makeRequest');
112
+ expect(content).toContain('protected async makeRequest');
113
113
  });
114
114
 
115
115
  it('should include header comments in generated file', async () => {
package/tsconfig.json CHANGED
@@ -41,6 +41,6 @@
41
41
  "useUnknownInCatchVariables": true,
42
42
  "verbatimModuleSyntax": true
43
43
  },
44
- "exclude": ["node_modules", "dist", "build", "examples/**/*.ts"],
44
+ "exclude": ["node_modules", "dist", "build", "examples/**/*.ts", "tests/**/*.ts", "scripts/**/*.ts"],
45
45
  "include": ["src/**/*.ts", "scripts/**/*.ts", "tests/**/*.ts", "vitest.config.ts"]
46
46
  }
@@ -1,43 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(npm install:*)",
5
- "Bash(npm uninstall:*)",
6
- "Bash(npm run type-check:*)",
7
- "Bash(npm run build:*)",
8
- "Bash(./dist/src/cli.js --help)",
9
- "Bash(./dist/src/cli.js --version)",
10
- "Bash(npm run dev:*)",
11
- "Bash(node:*)",
12
- "Bash(npm run lint)",
13
- "Bash(npx husky init:*)",
14
- "Bash(chmod:*)",
15
- "Bash(npm run validate:*)",
16
- "Bash(npm run format:*)",
17
- "Bash(npm run test:*)",
18
- "Bash(npm test)",
19
- "Bash(npm audit:*)",
20
- "Bash(npx depcheck:*)",
21
- "Bash(npm outdated)",
22
- "Bash(npm update:*)",
23
- "WebSearch",
24
- "Bash(find:*)",
25
- "Bash(npx zod-codegen:*)",
26
- "Bash(DEBUG=1 node ./dist/src/cli.js --input ./samples/openapi.json --output ./test-generated)",
27
- "Bash(rm:*)",
28
- "Bash(npm outdated:*)",
29
- "Bash(npm view:*)",
30
- "Bash(git tag:*)",
31
- "Bash(npm run release:*)",
32
- "Bash(git log:*)",
33
- "Bash(npx commitlint:*)",
34
- "Bash(git rev-list:*)",
35
- "Bash(./fix-commit-messages.sh:*)",
36
- "Bash(git add:*)",
37
- "Bash(git commit:*)",
38
- "Bash(FILTER_BRANCH_SQUELCH_WARNING=1 ./fix-commit-messages.sh)"
39
- ],
40
- "deny": [],
41
- "ask": []
42
- }
43
- }
@@ -1,14 +0,0 @@
1
- interface PackageJson {
2
- name: string;
3
- version: string;
4
- description: string;
5
- }
6
- /**
7
- * Type guard for the package.json object
8
- *
9
- * @param input Unknown input
10
- * @returns true if the input is an event object
11
- */
12
- export declare function isPackageJson(input: unknown): input is PackageJson;
13
- export {};
14
- //# sourceMappingURL=update-manifest.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"update-manifest.d.ts","sourceRoot":"","sources":["../../scripts/update-manifest.ts"],"names":[],"mappings":"AAIA,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAclE"}
@@ -1,31 +0,0 @@
1
- import { readFileSync, writeFileSync } from 'fs';
2
- import { resolve } from 'path';
3
- import { z } from 'zod';
4
- /**
5
- * Type guard for the package.json object
6
- *
7
- * @param input Unknown input
8
- * @returns true if the input is an event object
9
- */
10
- export function isPackageJson(input) {
11
- const event = z
12
- .object({
13
- name: z.string(),
14
- version: z.string(),
15
- description: z.string(),
16
- })
17
- .strict()
18
- .catchall(z.any())
19
- .required();
20
- const { success } = event.safeParse(input);
21
- return success;
22
- }
23
- const sourcePath = resolve(__dirname, '..', 'package.json');
24
- const data = JSON.parse(readFileSync(sourcePath, 'utf8'));
25
- if (!isPackageJson(data)) {
26
- process.exit(1);
27
- }
28
- const { name, version, description } = data;
29
- const targetPath = resolve(__dirname, '..', 'src', 'assets', 'manifest.json');
30
- writeFileSync(targetPath, JSON.stringify({ name, version, description }, null, 2));
31
- process.exit(0);
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=cli.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/cli.test.ts"],"names":[],"mappings":""}
@@ -1,25 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { execSync } from 'node:child_process';
3
- import { resolve } from 'node:path';
4
- describe('CLI Integration', () => {
5
- describe('--help', () => {
6
- it('should display help information', () => {
7
- const result = execSync('npm run build && node ./dist/src/cli.js --help', {
8
- encoding: 'utf-8',
9
- cwd: resolve(__dirname, '../..'),
10
- });
11
- expect(result).toContain('Usage:');
12
- expect(result).toContain('--input');
13
- expect(result).toContain('--output');
14
- });
15
- });
16
- describe('--version', () => {
17
- it('should display version information', () => {
18
- const result = execSync('npm run build && node ./dist/src/cli.js --version', {
19
- encoding: 'utf-8',
20
- cwd: resolve(__dirname, '../..'),
21
- });
22
- expect(result).toMatch(/\d+\.\d+\.\d+/);
23
- });
24
- });
25
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=code-generator.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"code-generator.test.d.ts","sourceRoot":"","sources":["../../../tests/unit/code-generator.test.ts"],"names":[],"mappings":""}