Skip to content

Commit 65c1822

Browse files
ochafikclaude
andcommitted
feat: convert all JSDoc comments to @description for .describe() calls
Add convertJsDocToDescription() during pre-processing to transform all JSDoc comments into @description tags. ts-to-zod then generates .describe() calls for each field and type. - Processes all interfaces and their properties - Processes all type aliases - Preserves existing JSDoc tags (@TJS-type, @Format, etc.) - 294 comments converted, resulting in 183 .describe() calls in schemas Example result: progress: z.number().describe('The progress thus far...') 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent d60251b commit 65c1822

File tree

3 files changed

+1748
-2217
lines changed

3 files changed

+1748
-2217
lines changed

scripts/generate-schemas.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ function preProcessTypes(content: string): string {
158158
// Transform 5: Add derived capability types
159159
injectDerivedCapabilityTypes(sourceFile);
160160

161+
// Transform 6: Convert JSDoc comments to @description tags for .describe() generation
162+
convertJsDocToDescription(sourceFile);
163+
161164
return sourceFile.getFullText();
162165
}
163166

@@ -333,6 +336,67 @@ function injectDerivedCapabilityTypes(sourceFile: SourceFile): void {
333336
}
334337
}
335338

339+
/**
340+
* Convert JSDoc comments to @description tags so ts-to-zod generates .describe() calls.
341+
*
342+
* Transforms comments like:
343+
* /** The progress thus far. * /
344+
* To:
345+
* /** @description The progress thus far. * /
346+
*/
347+
function convertJsDocToDescription(sourceFile: SourceFile): void {
348+
let count = 0;
349+
350+
// Process all interfaces
351+
for (const iface of sourceFile.getInterfaces()) {
352+
// Convert interface-level JSDoc
353+
count += convertNodeJsDoc(iface);
354+
355+
// Convert property-level JSDoc
356+
for (const prop of iface.getProperties()) {
357+
count += convertNodeJsDoc(prop);
358+
}
359+
}
360+
361+
// Process all type aliases
362+
for (const typeAlias of sourceFile.getTypeAliases()) {
363+
count += convertNodeJsDoc(typeAlias);
364+
}
365+
366+
console.log(` ✓ Converted ${count} JSDoc comments to @description`);
367+
}
368+
369+
/**
370+
* Convert a node's JSDoc comment to use @description tag.
371+
* Returns 1 if converted, 0 otherwise.
372+
*/
373+
function convertNodeJsDoc(node: { getJsDocs: () => Array<{ getDescription: () => string; getTags: () => Array<{ getTagName: () => string }>; replaceWithText: (text: string) => void }> }): number {
374+
const jsDocs = node.getJsDocs();
375+
if (jsDocs.length === 0) return 0;
376+
377+
const jsDoc = jsDocs[0];
378+
const description = jsDoc.getDescription().trim();
379+
380+
// Skip if no description or already has @description tag
381+
if (!description) return 0;
382+
if (jsDoc.getTags().some(tag => tag.getTagName() === 'description')) return 0;
383+
384+
// Get existing tags to preserve them
385+
const existingTags = jsDoc.getTags().map(tag => {
386+
const tagName = tag.getTagName();
387+
const tagText = tag.getText().replace(new RegExp(`^@${tagName}\\s*`), '').trim();
388+
return `@${tagName}${tagText ? ' ' + tagText : ''}`;
389+
}).join('\n * ');
390+
391+
// Build new JSDoc with @description
392+
const newJsDoc = existingTags
393+
? `/**\n * @description ${description}\n * ${existingTags}\n */`
394+
: `/** @description ${description} */`;
395+
396+
jsDoc.replaceWithText(newJsDoc);
397+
return 1;
398+
}
399+
336400
// =============================================================================
337401
// Post-processing: AST-based transforms using ts-morph
338402
// =============================================================================

0 commit comments

Comments
 (0)