Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion factory/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { createProgram } from "./program.js";

export function createGenerator(config: Config): SchemaGenerator {
const completedConfig = { ...DEFAULT_CONFIG, ...config };
const program = createProgram(completedConfig);
const program = config.tsProgram || createProgram(completedConfig);
const parser = createParser(program, completedConfig);
const formatter = createFormatter(completedConfig);

Expand Down
28 changes: 13 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@
"normalize-path": "^3.0.0",
"safe-stable-stringify": "^2.5.0",
"tslib": "^2.8.1",
"typescript": "^5.8.3"
"typescript": "^5.8.3",
"@typescript/vfs":"1.6.2"
},
"devDependencies": {
"@auto-it/conventional-commits": "^11.3.0",
Expand Down
122 changes: 121 additions & 1 deletion src/Config.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,149 @@
import type ts from "typescript";

export interface Config {
/**
* Glob pattern(s) for source TypeScript files to process.
* If not provided, falls back to files from tsconfig.
*/
path?: string;

/**
* Name of the type/interface to generate schema for.
* Use "*" to generate schemas for all exported types.
*/
type?: string;

/**
* Minify the output JSON schema (no whitespace).
* When false, the schema is pretty-printed with 2-space indentation.
* @default false
*/
minify?: boolean;

/**
* Sets the `$id` property in the root of the generated schema.
* Used for schema identification and referencing.
*/
schemaId?: string;

/**
* Path to a custom tsconfig.json file for TypeScript compilation.
* If not provided, uses default TypeScript configuration.
*/
tsconfig?: string;

/**
* Controls which types are exposed as definitions in the schema.
* - "all": Exposes all types except type literals
* - "none": Exposes no types automatically
* - "export": Only exposes exported types (respects @internal JSDoc tag)
* @default "export"
*/
expose?: "all" | "none" | "export";

/**
* Wraps the root type in a `$ref` definition.
* When false, inlines the root type definition directly.
* @default true
*/
topRef?: boolean;

/**
* Controls how JSDoc comments are parsed and included in the schema.
* - "none": Ignores all JSDoc annotations
* - "basic": Parses standard JSON Schema JSDoc tags
* - "extended": Parses all tags plus descriptions, examples, and type overrides
* @default "extended"
*/
jsDoc?: "none" | "extended" | "basic";

/**
* Adds a `markdownDescription` field alongside `description` in the schema.
* Preserves markdown formatting including newlines.
* Only works with `jsDoc: "extended"`.
* @default false
*/
markdownDescription?: boolean;

/**
* Includes the complete raw JSDoc comment as `fullDescription` in the schema.
* Only works with `jsDoc: "extended"`.
* @default false
*/
fullDescription?: boolean;

/**
* Sorts object properties alphabetically in the output.
* @default true
*/
sortProps?: boolean;

/**
* Controls whether tuples allow additional items beyond their defined length.
* @default false
*/
strictTuples?: boolean;

/**
* Skips TypeScript type checking to improve performance.
* Speeds up generation but may miss type errors.
* @default false
*/
skipTypeCheck?: boolean;

/**
* URI-encodes `$ref` values (e.g., `#/definitions/Foo%3CBar%3E`).
* When false, uses raw names in reference paths.
* @default true
*/
encodeRefs?: boolean;

/**
* Array of additional JSDoc tag names to include in the schema.
* Custom tags (e.g., `@customProperty`) are parsed and included in output.
* Values are parsed as JSON5.
* @default []
*/
extraTags?: string[];

/**
* Sets default value for `additionalProperties` on objects without index signatures.
* When false, objects get `additionalProperties: false` by default.
* When true, allows additional properties on all objects.
* @default false
*/
additionalProperties?: boolean;

/**
* Controls discriminator style for discriminated unions.
* - "json-schema": Uses `if`/`then`/`allOf` with properties containing discriminator enum
* - "open-api": Uses OpenAPI 3.x style with `discriminator: { propertyName }` and `oneOf`
* @default "json-schema"
*/
discriminatorType?: "json-schema" | "open-api";

/**
* Controls how function types are handled in the schema.
* - "fail": Throws error when encountering function types
* - "comment": Generates schema with `$comment` describing the function signature
* - "hide": Treats functions as NeverType (excluded from schema)
* @default "comment"
*/
functions?: FunctionOptions;

/**
* Pre-compiled TypeScript Program instance to use.
* Bypasses the default setup of a TypeScript program, and so some configuration options may not be applied.
* Useful for programmatic usage with existing TypeScript compilation, or for vfs scenarios where you do not want file-system representation.
*/
tsProgram?: ts.Program;
}

export type CompletedConfig = Config & typeof DEFAULT_CONFIG;

export type FunctionOptions = "fail" | "comment" | "hide";

export const DEFAULT_CONFIG: Omit<Required<Config>, "path" | "type" | "schemaId" | "tsconfig"> = {
export const DEFAULT_CONFIG: Omit<Required<Config>, "path" | "type" | "schemaId" | "tsconfig" | "tsProgram"> = {
expose: "export",
topRef: true,
jsDoc: "extended",
Expand Down
52 changes: 52 additions & 0 deletions test/unit/programmaticCustomTSProgram.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import ts from "typescript";
import { createFSBackedSystem, createVirtualTypeScriptEnvironment } from "@typescript/vfs";
import type { Config } from "../../src/Config";
import { createGenerator } from "../../factory/generator";

it("Can generate a schema from a vfs", () => {
const tsInterface = `
/**
* This is a sample interface
*/
export interface SampleInterface {
/** This is a name */
name: string;
}
`;

const fsMap = new Map<string, string>();
fsMap.set("/schema.ts", tsInterface);

// The FS backed API means that it will use the node_modules for lib.d.ts lookups
const system = createFSBackedSystem(fsMap, __dirname, ts);
const env = createVirtualTypeScriptEnvironment(system, ["/schema.ts"], ts);
const program = env.languageService.getProgram();
if (!program) throw new Error("No program");

const schemaConfig: Config = { path: "/schema.ts", tsProgram: program };
const generator = createGenerator(schemaConfig);

const result = generator.createSchema();
expect(result).toMatchInlineSnapshot(`
{
"$ref": "#/definitions/SampleInterface",
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"SampleInterface": {
"additionalProperties": false,
"description": "This is a sample interface",
"properties": {
"name": {
"description": "This is a name",
"type": "string",
},
},
"required": [
"name",
],
"type": "object",
},
},
}
`);
});