zod-codegen 1.1.1 → 1.2.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.
@@ -29,25 +29,25 @@ jobs:
29
29
  uses: actions/setup-node@v5
30
30
  with:
31
31
  node-version: ${{ matrix.node-version }}
32
- cache: 'npm'
32
+ cache: 'yarn'
33
33
 
34
34
  - name: Install dependencies
35
- run: npm ci
35
+ run: yarn install --frozen-lockfile
36
36
 
37
37
  - name: Run type check
38
- run: npm run type-check
38
+ run: yarn type-check
39
39
 
40
40
  - name: Run linter
41
- run: npm run lint:check
41
+ run: yarn lint:check
42
42
 
43
43
  - name: Run formatter check
44
- run: npm run format:check
44
+ run: yarn format:check
45
45
 
46
46
  - name: Build project
47
- run: npm run build
47
+ run: yarn build
48
48
 
49
49
  - name: Run tests
50
- run: npm run test:coverage
50
+ run: yarn test:coverage
51
51
 
52
52
  - name: Upload coverage to Codecov
53
53
  if: matrix.node-version == '24.11.1' && matrix.os == 'ubuntu-latest'
@@ -70,13 +70,13 @@ jobs:
70
70
  uses: actions/setup-node@v5
71
71
  with:
72
72
  node-version: '24.11.1'
73
- cache: 'npm'
73
+ cache: 'yarn'
74
74
 
75
75
  - name: Install dependencies
76
- run: npm ci
76
+ run: yarn install --frozen-lockfile
77
77
 
78
78
  - name: Run security audit
79
- run: npm audit --audit-level high
79
+ run: yarn audit --level high
80
80
 
81
81
  build:
82
82
  name: Build & Package
@@ -91,13 +91,13 @@ jobs:
91
91
  uses: actions/setup-node@v5
92
92
  with:
93
93
  node-version: '24.11.1'
94
- cache: 'npm'
94
+ cache: 'yarn'
95
95
 
96
96
  - name: Install dependencies
97
- run: npm ci
97
+ run: yarn install --frozen-lockfile
98
98
 
99
99
  - name: Build project
100
- run: npm run build
100
+ run: yarn build
101
101
 
102
102
  - name: Test CLI
103
103
  run: |
@@ -129,15 +129,15 @@ jobs:
129
129
  uses: actions/setup-node@v5
130
130
  with:
131
131
  node-version: '24.11.1'
132
- cache: 'npm'
132
+ cache: 'yarn'
133
133
 
134
134
  - name: Install dependencies
135
- run: npm ci
135
+ run: yarn install --frozen-lockfile
136
136
 
137
137
  - name: Build project
138
- run: npm run build
138
+ run: yarn build
139
139
 
140
140
  - name: Preview semantic-release
141
141
  env:
142
142
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
143
- run: npm run release:dry
143
+ run: yarn release:dry
@@ -29,26 +29,26 @@ jobs:
29
29
  uses: actions/setup-node@v5
30
30
  with:
31
31
  node-version: '24.11.1'
32
- cache: 'npm'
32
+ cache: 'yarn'
33
33
  registry-url: 'https://registry.npmjs.org'
34
34
 
35
35
  - name: Install dependencies
36
- run: npm ci
36
+ run: yarn install --frozen-lockfile
37
37
 
38
38
  - name: Run type check
39
- run: npm run type-check
39
+ run: yarn type-check
40
40
 
41
41
  - name: Run linter check
42
- run: npm run lint:check
42
+ run: yarn lint:check
43
43
 
44
44
  - name: Run formatter check
45
- run: npm run format:check
45
+ run: yarn format:check
46
46
 
47
47
  - name: Run tests with coverage
48
- run: npm run test:coverage
48
+ run: yarn test:coverage
49
49
 
50
50
  - name: Build project
51
- run: npm run build
51
+ run: yarn build
52
52
 
53
53
  - name: Test CLI functionality
54
54
  run: |
@@ -62,4 +62,4 @@ jobs:
62
62
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
63
63
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
64
64
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
65
- run: npx semantic-release
65
+ run: yarn release
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 1.2.0 (2025-11-19)
2
+
3
+ - Merge pull request #31 from julienandreu/dependabot/npm_and_yarn/dev-dependencies-1dd8918b9f ([97ebb65](https://github.com/julienandreu/zod-codegen/commit/97ebb65)), closes [#31](https://github.com/julienandreu/zod-codegen/issues/31)
4
+ - Merge pull request #32 from julienandreu/feat/add-jsdoc-comments ([9e0d589](https://github.com/julienandreu/zod-codegen/commit/9e0d589)), closes [#32](https://github.com/julienandreu/zod-codegen/issues/32)
5
+ - feat: add JSDoc comments to generated API client methods ([4f42f97](https://github.com/julienandreu/zod-codegen/commit/4f42f97))
6
+ - chore(deps-dev): bump the dev-dependencies group with 3 updates ([43be1ab](https://github.com/julienandreu/zod-codegen/commit/43be1ab))
7
+
8
+ ## <small>1.1.2 (2025-11-13)</small>
9
+
10
+ - Merge pull request #30 from julienandreu/fix/cli-strict-mode ([d098ac7](https://github.com/julienandreu/zod-codegen/commit/d098ac7)), closes [#30](https://github.com/julienandreu/zod-codegen/issues/30)
11
+ - fix: add strict mode to CLI to catch typos in arguments ([b5ad8aa](https://github.com/julienandreu/zod-codegen/commit/b5ad8aa))
12
+
1
13
  ## <small>1.1.1 (2025-11-13)</small>
2
14
 
3
15
  - Merge pull request #29 from julienandreu/feat/server-configuration-and-examples ([c598b5a](https://github.com/julienandreu/zod-codegen/commit/c598b5a)), closes [#29](https://github.com/julienandreu/zod-codegen/issues/29)
package/CONTRIBUTING.md CHANGED
@@ -22,7 +22,7 @@ This project adheres to a code of conduct. By participating, you are expected to
22
22
  ### Prerequisites
23
23
 
24
24
  - Node.js >= 24.11.1
25
- - npm or yarn
25
+ - yarn
26
26
  - Git
27
27
 
28
28
  ### Development Setup
@@ -37,24 +37,24 @@ This project adheres to a code of conduct. By participating, you are expected to
37
37
  2. **Install Dependencies**
38
38
 
39
39
  ```bash
40
- npm install
40
+ yarn install
41
41
  ```
42
42
 
43
43
  3. **Build the Project**
44
44
 
45
45
  ```bash
46
- npm run build
46
+ yarn build
47
47
  ```
48
48
 
49
49
  4. **Run Tests**
50
50
 
51
51
  ```bash
52
- npm test
52
+ yarn test
53
53
  ```
54
54
 
55
55
  5. **Run Development Mode**
56
56
  ```bash
57
- npm run dev
57
+ yarn dev
58
58
  ```
59
59
 
60
60
  ## Making Changes
@@ -119,7 +119,7 @@ test: add integration tests for CLI
119
119
  3. **Validate Your Changes**
120
120
 
121
121
  ```bash
122
- npm run validate
122
+ yarn validate
123
123
  ```
124
124
 
125
125
  This runs:
@@ -171,10 +171,10 @@ We use ESLint and Prettier for consistent code style:
171
171
 
172
172
  ```bash
173
173
  # Auto-fix linting issues
174
- npm run lint
174
+ yarn lint
175
175
 
176
176
  # Format code
177
- npm run format
177
+ yarn format
178
178
  ```
179
179
 
180
180
  ### File Structure
@@ -218,16 +218,16 @@ tests/
218
218
 
219
219
  ```bash
220
220
  # Run all tests
221
- npm test
221
+ yarn test
222
222
 
223
223
  # Run tests in watch mode
224
- npm run test:watch
224
+ yarn test:watch
225
225
 
226
226
  # Run with coverage
227
- npm run test:coverage
227
+ yarn test:coverage
228
228
 
229
229
  # Run specific test file
230
- npx vitest run generator.test.ts
230
+ yarn vitest run generator.test.ts
231
231
  ```
232
232
 
233
233
  ## Documentation
package/PERFORMANCE.md CHANGED
@@ -12,14 +12,14 @@ For even faster builds, you can optionally use the TypeScript Native Preview (TS
12
12
  ### Installation
13
13
 
14
14
  ```bash
15
- npm install -D @typescript/native-preview
15
+ yarn add -D @typescript/native-preview
16
16
  ```
17
17
 
18
18
  ### Usage
19
19
 
20
20
  ```bash
21
21
  # Use native compiler for builds
22
- npm run build:native
22
+ yarn build:native
23
23
 
24
24
  # Or use directly
25
25
  npx tsgo --project tsconfig.json
@@ -52,8 +52,8 @@ To compare performance:
52
52
 
53
53
  ```bash
54
54
  # Standard TypeScript
55
- time npm run build
55
+ time yarn build
56
56
 
57
57
  # Native TypeScript
58
- time npm run build:native
58
+ time yarn build:native
59
59
  ```
package/README.md CHANGED
@@ -28,13 +28,13 @@ A powerful TypeScript code generator that creates **Zod schemas** and **type-saf
28
28
  ### Global Installation (CLI)
29
29
 
30
30
  ```bash
31
- npm install -g zod-codegen
31
+ yarn global add zod-codegen
32
32
  ```
33
33
 
34
34
  ### Project Installation
35
35
 
36
36
  ```bash
37
- npm install --save-dev zod-codegen
37
+ yarn add --dev zod-codegen
38
38
  ```
39
39
 
40
40
  ## 🔧 Usage
@@ -378,7 +378,7 @@ Each example includes:
378
378
  ### Prerequisites
379
379
 
380
380
  - Node.js ≥ 24.11.1
381
- - npm or yarn
381
+ - yarn
382
382
 
383
383
  ### Setup
384
384
 
@@ -388,52 +388,52 @@ git clone https://github.com/julienandreu/zod-codegen.git
388
388
  cd zod-codegen
389
389
 
390
390
  # Install dependencies
391
- npm install
391
+ yarn install
392
392
 
393
393
  # Build the project
394
- npm run build
394
+ yarn build
395
395
 
396
396
  # Run tests
397
- npm test
397
+ yarn test
398
398
 
399
399
  # Run linting
400
- npm run lint
400
+ yarn lint
401
401
 
402
402
  # Format code
403
- npm run format
403
+ yarn format
404
404
  ```
405
405
 
406
406
  ### Testing
407
407
 
408
408
  ```bash
409
409
  # Run all tests
410
- npm test
410
+ yarn test
411
411
 
412
412
  # Run tests in watch mode
413
- npm run test:watch
413
+ yarn test:watch
414
414
 
415
415
  # Run tests with coverage
416
- npm run test:coverage
416
+ yarn test:coverage
417
417
 
418
418
  # Run tests with UI
419
- npm run test:ui
419
+ yarn test:ui
420
420
  ```
421
421
 
422
422
  ### Available Scripts
423
423
 
424
- | Script | Description |
425
- | ----------------------- | ----------------------------------------------- |
426
- | `npm run build` | Build the project |
427
- | `npm run build:watch` | Build in watch mode |
428
- | `npm run dev` | Development mode with example |
429
- | `npm test` | Run tests |
430
- | `npm run test:watch` | Run tests in watch mode |
431
- | `npm run test:coverage` | Run tests with coverage |
432
- | `npm run lint` | Lint and fix code |
433
- | `npm run format` | Format code with Prettier |
434
- | `npm run type-check` | Type check without emitting |
435
- | `npm run validate` | Run all checks (lint, format, type-check, test) |
436
- | `npm run clean` | Clean build artifacts |
424
+ | Script | Description |
425
+ | -------------------- | ----------------------------------------------- |
426
+ | `yarn build` | Build the project |
427
+ | `yarn build:watch` | Build in watch mode |
428
+ | `yarn dev` | Development mode with example |
429
+ | `yarn test` | Run tests |
430
+ | `yarn test:watch` | Run tests in watch mode |
431
+ | `yarn test:coverage` | Run tests with coverage |
432
+ | `yarn lint` | Lint and fix code |
433
+ | `yarn format` | Format code with Prettier |
434
+ | `yarn type-check` | Type check without emitting |
435
+ | `yarn validate` | Run all checks (lint, format, type-check, test) |
436
+ | `yarn clean` | Clean build artifacts |
437
437
 
438
438
  ## 📋 Requirements
439
439
 
@@ -450,8 +450,8 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
450
450
  1. Fork the repository
451
451
  2. Create your feature branch: `git checkout -b feature/amazing-feature`
452
452
  3. Make your changes
453
- 4. Run tests: `npm test`
454
- 5. Run validation: `npm run validate`
453
+ 4. Run tests: `yarn test`
454
+ 5. Run validation: `yarn validate`
455
455
  6. Commit your changes: `git commit -m 'feat: add amazing feature'`
456
456
  7. Push to the branch: `git push origin feature/amazing-feature`
457
457
  8. Open a Pull Request
package/SECURITY.md CHANGED
@@ -75,7 +75,7 @@ When using zod-codegen, please follow these security best practices:
75
75
 
76
76
  - Keep zod-codegen and its dependencies up to date
77
77
  - Regularly audit your dependency tree for known vulnerabilities
78
- - Use tools like `npm audit` to check for security issues
78
+ - Use tools like `yarn audit` or `npm audit` to check for security issues
79
79
 
80
80
  ## Known Security Considerations
81
81
 
package/dist/src/cli.js CHANGED
@@ -59,6 +59,7 @@ const argv = yargs(hideBin(process.argv))
59
59
  description: 'Directory to output the generated files',
60
60
  default: 'generated',
61
61
  })
62
+ .strict()
62
63
  .help()
63
64
  .parseSync();
64
65
  const { input, output } = argv;
@@ -296,7 +296,15 @@ export class TypeScriptCodeGeneratorService {
296
296
  else {
297
297
  statements.push(ts.factory.createReturnStatement(ts.factory.createAwaitExpression(makeRequestCall)));
298
298
  }
299
- return ts.factory.createMethodDeclaration([ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)], undefined, ts.factory.createIdentifier(String(schema.operationId)), undefined, undefined, parameters, responseType, ts.factory.createBlock(statements, true));
299
+ const methodDeclaration = ts.factory.createMethodDeclaration([ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)], undefined, ts.factory.createIdentifier(String(schema.operationId)), undefined, undefined, parameters, responseType, ts.factory.createBlock(statements, true));
300
+ // Add JSDoc comment if summary or description exists
301
+ const jsdocComment = this.buildJSDocComment(schema.summary, schema.description, schema, responseType);
302
+ if (jsdocComment) {
303
+ // addSyntheticLeadingComment expects the comment content without delimiters
304
+ // and will wrap it in /** */ for JSDoc-style comments
305
+ ts.addSyntheticLeadingComment(methodDeclaration, ts.SyntaxKind.MultiLineCommentTrivia, `*\n${jsdocComment}\n `, true);
306
+ }
307
+ return methodDeclaration;
300
308
  }
301
309
  buildPathExpression(path, pathParams) {
302
310
  // Replace {param} with ${param} for template literal
@@ -669,6 +677,75 @@ export class TypeScriptCodeGeneratorService {
669
677
  ts.addSyntheticTrailingComment(commentNode, ts.SyntaxKind.SingleLineCommentTrivia, ` ${text}`, true);
670
678
  return ts.factory.createExpressionStatement(commentNode);
671
679
  }
680
+ /**
681
+ * Builds a JSDoc comment string from operation metadata
682
+ */
683
+ buildJSDocComment(summary, description, schema, responseType) {
684
+ const lines = [];
685
+ // Add summary or description as the main comment
686
+ if (summary) {
687
+ lines.push(` * ${summary}`);
688
+ }
689
+ else if (description) {
690
+ // Use first line of description as summary if no summary exists
691
+ const firstLine = description.split('\n')[0]?.trim();
692
+ if (firstLine) {
693
+ lines.push(` * ${firstLine}`);
694
+ }
695
+ }
696
+ // Add full description if it exists and is different from summary
697
+ if (description && description !== summary) {
698
+ const descLines = description.split('\n');
699
+ if (descLines.length > 1 || descLines[0] !== summary) {
700
+ if (lines.length > 0) {
701
+ lines.push(' *');
702
+ }
703
+ descLines.forEach((line) => {
704
+ lines.push(` * ${line.trim() || ''}`);
705
+ });
706
+ }
707
+ }
708
+ // Add @param tags for each parameter
709
+ if (schema.parameters && schema.parameters.length > 0) {
710
+ if (lines.length > 0) {
711
+ lines.push(' *');
712
+ }
713
+ for (const param of schema.parameters) {
714
+ const paramName = this.typeBuilder.sanitizeIdentifier(param.name);
715
+ const paramDesc = param.description ? ` ${param.description}` : '';
716
+ lines.push(` * @param ${paramName}${paramDesc}`);
717
+ }
718
+ }
719
+ // Add @param tag for request body if present
720
+ if (schema.requestBody) {
721
+ const bodyDesc = schema.requestBody.description ? ` ${schema.requestBody.description}` : '';
722
+ lines.push(` * @param body${bodyDesc}`);
723
+ }
724
+ // Add @returns tag if we have a response type
725
+ if (responseType) {
726
+ // Extract the inner type from Promise<T> for JSDoc
727
+ let returnTypeText;
728
+ if (ts.isTypeReferenceNode(responseType) &&
729
+ ts.isIdentifier(responseType.typeName) &&
730
+ responseType.typeName.text === 'Promise' &&
731
+ responseType.typeArguments &&
732
+ responseType.typeArguments.length > 0 &&
733
+ responseType.typeArguments[0]) {
734
+ // Extract the inner type from Promise<T>
735
+ const innerType = responseType.typeArguments[0];
736
+ returnTypeText = this.printer.printNode(ts.EmitHint.Unspecified, innerType, ts.createSourceFile('', '', ts.ScriptTarget.Latest));
737
+ }
738
+ else {
739
+ returnTypeText = this.printer.printNode(ts.EmitHint.Unspecified, responseType, ts.createSourceFile('', '', ts.ScriptTarget.Latest));
740
+ }
741
+ lines.push(` * @returns {${returnTypeText}}`);
742
+ }
743
+ // Build the complete JSDoc comment (without delimiters, as addSyntheticLeadingComment adds them)
744
+ if (lines.length === 0) {
745
+ return '';
746
+ }
747
+ return lines.join('\n');
748
+ }
672
749
  buildZodAST(input) {
673
750
  const [initial, ...rest] = input;
674
751
  const safeInitial = this.ZodAST.safeParse(initial);
@@ -4,7 +4,7 @@ import { resolve } from 'node:path';
4
4
  describe('CLI Integration', () => {
5
5
  describe('--help', () => {
6
6
  it('should display help information', () => {
7
- const result = execSync('npm run build && node ./dist/src/cli.js --help', {
7
+ const result = execSync('yarn build && node ./dist/src/cli.js --help', {
8
8
  encoding: 'utf-8',
9
9
  cwd: resolve(__dirname, '../..'),
10
10
  });
@@ -15,7 +15,7 @@ describe('CLI Integration', () => {
15
15
  });
16
16
  describe('--version', () => {
17
17
  it('should display version information', () => {
18
- const result = execSync('npm run build && node ./dist/src/cli.js --version', {
18
+ const result = execSync('yarn build && node ./dist/src/cli.js --version', {
19
19
  encoding: 'utf-8',
20
20
  cwd: resolve(__dirname, '../..'),
21
21
  });
package/package.json CHANGED
@@ -8,13 +8,13 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "@apidevtools/swagger-parser": "^12.1.0",
11
- "debug": "^4.3.7",
11
+ "debug": "^4.4.3",
12
12
  "js-yaml": "^4.1.1",
13
13
  "jsonpath": "^1.1.1",
14
14
  "loud-rejection": "^2.2.0",
15
15
  "openapi-types": "^12.1.3",
16
16
  "openapi-typescript": "^7.10.1",
17
- "path-to-regexp": "^8.2.0",
17
+ "path-to-regexp": "^8.3.0",
18
18
  "prettier": "^3.6.2",
19
19
  "typescript": "^5.9.3",
20
20
  "url-pattern": "^1.0.3",
@@ -44,8 +44,8 @@
44
44
  "@types/js-yaml": "^4.0.9",
45
45
  "@types/jsonpath": "^0.2.4",
46
46
  "@types/node": "^24.10.1",
47
- "@types/yargs": "^17.0.34",
48
- "@vitest/coverage-v8": "^4.0.8",
47
+ "@types/yargs": "^17.0.35",
48
+ "@vitest/coverage-v8": "^4.0.9",
49
49
  "eslint": "^9.39.1",
50
50
  "eslint-config-prettier": "^10.1.8",
51
51
  "husky": "^9.1.7",
@@ -54,7 +54,8 @@
54
54
  "ts-node": "^10.9.2",
55
55
  "typescript-eslint": "^8.46.4",
56
56
  "undici": "^7.16.0",
57
- "vitest": "^4.0.8"
57
+ "vitest": "^4.0.8",
58
+ "yarn-audit-fix": "^10.1.1"
58
59
  },
59
60
  "optionalDependencies": {
60
61
  "undici": "^7.16.0"
@@ -77,11 +78,20 @@
77
78
  "engines": {
78
79
  "node": ">=24.11.1"
79
80
  },
81
+ "overrides": {
82
+ "npm": {
83
+ "glob": "^11.1.0",
84
+ "tar": "^7.5.2"
85
+ },
86
+ "glob": "^11.1.0",
87
+ "tar": "^7.5.2"
88
+ },
80
89
  "scripts": {
90
+ "audit:fix": "yarn-audit-fix",
81
91
  "build": "rm -rf dist && tsc --project tsconfig.json && cp -r src/assets dist/src/ && chmod +x ./dist/src/cli.js",
82
92
  "build:native": "rm -rf dist && npx tsgo --project tsconfig.json && cp -r src/assets dist/src/ && chmod +x ./dist/src/cli.js",
83
93
  "build:watch": "tsc --project tsconfig.json --watch",
84
- "dev": "npm run build && node ./dist/src/cli.js --input ./samples/swagger-petstore.yaml --output ./examples/petstore && npm run format && npm run lint",
94
+ "dev": "yarn build && node ./dist/src/cli.js --input ./samples/swagger-petstore.yaml --output ./examples/petstore && yarn format && yarn lint",
85
95
  "lint": "eslint src --fix",
86
96
  "lint:check": "eslint src",
87
97
  "format": "prettier src --write --log-level error",
@@ -93,12 +103,12 @@
93
103
  "test:ui": "vitest --ui",
94
104
  "manifest:update": "ts-node scripts/update-manifest.ts",
95
105
  "prepare": "husky",
96
- "prepublishOnly": "npm run build && npm run test && npm run lint:check && npm run type-check",
106
+ "prepublishOnly": "yarn build && yarn test && yarn lint:check && yarn type-check",
97
107
  "clean": "rm -rf dist coverage node_modules/.cache",
98
- "validate": "npm run type-check && npm run lint:check && npm run format:check && npm run test",
108
+ "validate": "yarn type-check && yarn lint:check && yarn format:check && yarn test",
99
109
  "validate:examples": "tsc -p ./tsconfig.examples.json --noEmit",
100
110
  "release": "semantic-release",
101
111
  "release:dry": "semantic-release --dry-run"
102
112
  },
103
- "version": "1.1.1"
113
+ "version": "1.2.0"
104
114
  }
package/src/cli.ts CHANGED
@@ -72,6 +72,7 @@ const argv = yargs(hideBin(process.argv))
72
72
  description: 'Directory to output the generated files',
73
73
  default: 'generated',
74
74
  })
75
+ .strict()
75
76
  .help()
76
77
  .parseSync();
77
78
 
@@ -962,7 +962,7 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
962
962
  statements.push(ts.factory.createReturnStatement(ts.factory.createAwaitExpression(makeRequestCall)));
963
963
  }
964
964
 
965
- return ts.factory.createMethodDeclaration(
965
+ const methodDeclaration = ts.factory.createMethodDeclaration(
966
966
  [ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)],
967
967
  undefined,
968
968
  ts.factory.createIdentifier(String(schema.operationId)),
@@ -972,6 +972,22 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
972
972
  responseType,
973
973
  ts.factory.createBlock(statements, true),
974
974
  );
975
+
976
+ // Add JSDoc comment if summary or description exists
977
+ const jsdocComment = this.buildJSDocComment(schema.summary, schema.description, schema, responseType);
978
+
979
+ if (jsdocComment) {
980
+ // addSyntheticLeadingComment expects the comment content without delimiters
981
+ // and will wrap it in /** */ for JSDoc-style comments
982
+ ts.addSyntheticLeadingComment(
983
+ methodDeclaration,
984
+ ts.SyntaxKind.MultiLineCommentTrivia,
985
+ `*\n${jsdocComment}\n `,
986
+ true,
987
+ );
988
+ }
989
+
990
+ return methodDeclaration;
975
991
  }
976
992
 
977
993
  private buildPathExpression(path: string, pathParams: {name: string; type: string}[]): ts.Expression {
@@ -1670,6 +1686,96 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
1670
1686
  return ts.factory.createExpressionStatement(commentNode);
1671
1687
  }
1672
1688
 
1689
+ /**
1690
+ * Builds a JSDoc comment string from operation metadata
1691
+ */
1692
+ private buildJSDocComment(
1693
+ summary: string | undefined,
1694
+ description: string | undefined,
1695
+ schema: MethodSchemaType,
1696
+ responseType: ts.TypeNode | undefined,
1697
+ ): string {
1698
+ const lines: string[] = [];
1699
+
1700
+ // Add summary or description as the main comment
1701
+ if (summary) {
1702
+ lines.push(` * ${summary}`);
1703
+ } else if (description) {
1704
+ // Use first line of description as summary if no summary exists
1705
+ const firstLine = description.split('\n')[0]?.trim();
1706
+ if (firstLine) {
1707
+ lines.push(` * ${firstLine}`);
1708
+ }
1709
+ }
1710
+
1711
+ // Add full description if it exists and is different from summary
1712
+ if (description && description !== summary) {
1713
+ const descLines = description.split('\n');
1714
+ if (descLines.length > 1 || descLines[0] !== summary) {
1715
+ if (lines.length > 0) {
1716
+ lines.push(' *');
1717
+ }
1718
+ descLines.forEach((line) => {
1719
+ lines.push(` * ${line.trim() || ''}`);
1720
+ });
1721
+ }
1722
+ }
1723
+
1724
+ // Add @param tags for each parameter
1725
+ if (schema.parameters && schema.parameters.length > 0) {
1726
+ if (lines.length > 0) {
1727
+ lines.push(' *');
1728
+ }
1729
+ for (const param of schema.parameters) {
1730
+ const paramName = this.typeBuilder.sanitizeIdentifier(param.name);
1731
+ const paramDesc = param.description ? ` ${param.description}` : '';
1732
+ lines.push(` * @param ${paramName}${paramDesc}`);
1733
+ }
1734
+ }
1735
+
1736
+ // Add @param tag for request body if present
1737
+ if (schema.requestBody) {
1738
+ const bodyDesc = schema.requestBody.description ? ` ${schema.requestBody.description}` : '';
1739
+ lines.push(` * @param body${bodyDesc}`);
1740
+ }
1741
+
1742
+ // Add @returns tag if we have a response type
1743
+ if (responseType) {
1744
+ // Extract the inner type from Promise<T> for JSDoc
1745
+ let returnTypeText: string;
1746
+ if (
1747
+ ts.isTypeReferenceNode(responseType) &&
1748
+ ts.isIdentifier(responseType.typeName) &&
1749
+ responseType.typeName.text === 'Promise' &&
1750
+ responseType.typeArguments &&
1751
+ responseType.typeArguments.length > 0 &&
1752
+ responseType.typeArguments[0]
1753
+ ) {
1754
+ // Extract the inner type from Promise<T>
1755
+ const innerType = responseType.typeArguments[0];
1756
+ returnTypeText = this.printer.printNode(
1757
+ ts.EmitHint.Unspecified,
1758
+ innerType,
1759
+ ts.createSourceFile('', '', ts.ScriptTarget.Latest),
1760
+ );
1761
+ } else {
1762
+ returnTypeText = this.printer.printNode(
1763
+ ts.EmitHint.Unspecified,
1764
+ responseType,
1765
+ ts.createSourceFile('', '', ts.ScriptTarget.Latest),
1766
+ );
1767
+ }
1768
+ lines.push(` * @returns {${returnTypeText}}`);
1769
+ }
1770
+
1771
+ // Build the complete JSDoc comment (without delimiters, as addSyntheticLeadingComment adds them)
1772
+ if (lines.length === 0) {
1773
+ return '';
1774
+ }
1775
+
1776
+ return lines.join('\n');
1777
+ }
1778
+
1673
1779
  private buildZodAST(input: (string | z.infer<typeof this.ZodAST>)[]): ts.CallExpression {
1674
1780
  const [initial, ...rest] = input;
1675
1781
 
@@ -5,7 +5,7 @@ import {resolve} from 'node:path';
5
5
  describe('CLI Integration', () => {
6
6
  describe('--help', () => {
7
7
  it('should display help information', () => {
8
- const result = execSync('npm run build && node ./dist/src/cli.js --help', {
8
+ const result = execSync('yarn build && node ./dist/src/cli.js --help', {
9
9
  encoding: 'utf-8',
10
10
  cwd: resolve(__dirname, '../..'),
11
11
  });
@@ -18,7 +18,7 @@ describe('CLI Integration', () => {
18
18
 
19
19
  describe('--version', () => {
20
20
  it('should display version information', () => {
21
- const result = execSync('npm run build && node ./dist/src/cli.js --version', {
21
+ const result = execSync('yarn build && node ./dist/src/cli.js --version', {
22
22
  encoding: 'utf-8',
23
23
  cwd: resolve(__dirname, '../..'),
24
24
  });