Skip to content
Merged
2 changes: 1 addition & 1 deletion packages/models/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"dependsOn": [
"^build",
"generate-docs",
{ "projects": "models-transformers", "target": "build" }
{ "projects": "zod2md-jsdocs", "target": "build" }
]
},
"lint": {},
Expand Down
5 changes: 3 additions & 2 deletions packages/models/tsconfig.lib.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
"types": ["node"],
"plugins": [
{
"transform": "./packages/models/transformers/dist",
"afterDeclarations": true
"transform": "./tools/zod2md-jsdocs/dist",
"afterDeclarations": true,
"baseUrl": "https://github.com/code-pushup/cli/blob/main/packages/models/docs/models-reference.md"
}
]
},
Expand Down
71 changes: 71 additions & 0 deletions tools/zod2md-jsdocs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# @code-pushup/zod2md-jsdocs

TypeScript transformer plugin that automatically enhances type definitions with JSDoc comments and schema metadata.

## Purpose

This package provides a TypeScript compiler transformer that automatically adds JSDoc documentation to type aliases and interfaces during compilation. It's designed to improve developer experience by injecting helpful metadata and documentation links directly into generated type definitions.

## How It Works

The [TS transformer](https://github.com/itsdouges/typescript-transformer-handbook) hooks into the TypeScript compilation process using `ts-patch` and automatically adds JSDoc comments above type definitions. Each comment includes:

- The type name
- A description explaining the type is derived from a Zod schema
- A link to the type reference documentation

## Example

Given a type definition like:

```typescript
export type Report = {
// ... type properties
};
```

The transformer automatically generates:

```typescript
/**
* Type Definition: `Report`
*
* This type is derived from a Zod schema and represents
* the validated structure of `Report` used within the application.
*
* @see {@link https://github.com/code-pushup/cli/blob/main/packages/models/docs/models-reference.md#report}
*/
export type Report = {
// ... type properties
};
```

## Usage

1. `ts-patch install`

2. Add the transformer to your `tsconfig.json`:

```json
{
"compilerOptions": {
"plugins": [
{
"transform": "./path/to/transformer/dist",
"afterDeclarations": true,
"baseUrl": "https://example.com/docs/api-reference.md"
}
]
}
}
```

3. Build your TypeScript project. The transformer will run automatically and add JSDoc comments to your type definitions.

### Options

| Option | Type | Required | Description |
| ------------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `transform` | `string` | Yes | Path to the transformer module |
| `afterDeclarations` | `boolean` | No | Set to `true` to run the transformer after TypeScript generates declaration files (`.d.ts`). This ensures JSDoc comments are added to the emitted type definitions. |
| `baseUrl` | `string` | Yes | Base URL for documentation links (e.g., `https://example.com/docs/api-reference.md`) |
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const baseConfig = require('../../../eslint.config.js').default;
const baseConfig = require('../../eslint.config.js').default;

module.exports = [
...baseConfig,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@code-pushup/models-transformers",
"name": "@code-pushup/zod2md-jsdocs",
"version": "0.0.0",
"description": "TypeScript transformers enhancing models with JSDoc and schema metadata",
"type": "commonjs",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"name": "models-transformers",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/models/transformers/src",
"name": "zod2md-jsdocs",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "tools/zod2md-jsdocs/src",
"projectType": "library",
"targets": {
"build": {
"executor": "@nx/js:tsc",
"outputs": ["{options.outputPath}"],
"dependsOn": ["pre-build"],
"options": {
"outputPath": "packages/models/transformers/dist",
"main": "packages/models/transformers/src/index.ts",
"tsConfig": "packages/models/transformers/tsconfig.lib.json"
"outputPath": "tools/zod2md-jsdocs/dist",
"main": "tools/zod2md-jsdocs/src/index.ts",
"tsConfig": "tools/zod2md-jsdocs/tsconfig.lib.json"
}
},
"pre-build": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,39 @@ import type * as ts from 'typescript';

const tsInstance: typeof ts = require('typescript');

const BASE_URL =
'https://github.com/code-pushup/cli/blob/main/packages/models/docs/models-reference.md';

function generateJSDocComment(typeName: string): string {
const markdownLink = `${BASE_URL}#${typeName.toLowerCase()}`;
function generateJSDocComment(typeName: string, baseUrl: string): string {
const markdownLink = `${baseUrl}#${typeName.toLowerCase()}`;
return `*
* Type Definition: \`${typeName}\`
*
* This type is derived from a Zod schema and represents
*
* This type is derived from a Zod schema and represents
* the validated structure of \`${typeName}\` used within the application.
*
*
* @see {@link ${markdownLink}}
`;
}

function annotateTypeDefinitions(
_program: ts.Program,
_pluginConfig: PluginConfig,
pluginConfig: PluginConfig,
extras?: TransformerExtras,
): ts.TransformerFactory<ts.SourceFile> {
const baseUrl = pluginConfig.baseUrl as string | undefined;

if (!baseUrl) {
throw new Error(
'zod2md-jsdocs: "baseUrl" option is required. ' +
'Please configure it in your tsconfig.json plugins section.',
);
}
const tsLib = extras?.ts ?? tsInstance;
return (context: ts.TransformationContext) => {
const visitor = (node: ts.Node): ts.Node => {
if (
tsLib.isTypeAliasDeclaration(node) ||
tsLib.isInterfaceDeclaration(node)
) {
const jsDocComment = generateJSDocComment(node.name.text);
const jsDocComment = generateJSDocComment(node.name.text, baseUrl);
tsLib.addSyntheticLeadingComment(
node,
tsLib.SyntaxKind.MultiLineCommentTrivia,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "../../../tsconfig.base.json",
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"verbatimModuleSyntax": false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"outDir": "./dist",
"rootDir": "./",
"module": "commonjs",
"types": ["node"]
"types": ["node"],
"esModuleInterop": true
},
"include": ["src/**/*.ts"]
}
29 changes: 29 additions & 0 deletions tools/zod2md-jsdocs/tsconfig.spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": [
"vitest/globals",
"vitest/importMeta",
"vite/client",
"node",
"vitest"
]
},
"include": [
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"vitest.unit.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx",
"src/**/*.d.ts"
]
}
3 changes: 3 additions & 0 deletions tools/zod2md-jsdocs/vitest.unit.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createUnitTestConfig } from '../../testing/test-setup-config/src/index.js';

export default createUnitTestConfig('zod2md-jsdocs');