Skip to content

Commit 8008d18

Browse files
authored
Allow node VFS based programmatic usage (#2392)
1 parent 28edcdc commit 8008d18

File tree

5 files changed

+189
-5
lines changed

5 files changed

+189
-5
lines changed

factory/generator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { createProgram } from "./program.js";
77

88
export function createGenerator(config: Config): SchemaGenerator {
99
const completedConfig = { ...DEFAULT_CONFIG, ...config };
10-
const program = createProgram(completedConfig);
10+
const program = config.tsProgram || createProgram(completedConfig);
1111
const parser = createParser(program, completedConfig);
1212
const formatter = createFormatter(completedConfig);
1313

package-lock.json

Lines changed: 13 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@
6363
"normalize-path": "^3.0.0",
6464
"safe-stable-stringify": "^2.5.0",
6565
"tslib": "^2.8.1",
66-
"typescript": "^5.8.3"
66+
"typescript": "^5.8.3",
67+
"@typescript/vfs":"1.6.2"
6768
},
6869
"devDependencies": {
6970
"@auto-it/conventional-commits": "^11.3.0",

src/Config.ts

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,149 @@
1+
import type ts from "typescript";
2+
13
export interface Config {
4+
/**
5+
* Glob pattern(s) for source TypeScript files to process.
6+
* If not provided, falls back to files from tsconfig.
7+
*/
28
path?: string;
9+
10+
/**
11+
* Name of the type/interface to generate schema for.
12+
* Use "*" to generate schemas for all exported types.
13+
*/
314
type?: string;
15+
16+
/**
17+
* Minify the output JSON schema (no whitespace).
18+
* When false, the schema is pretty-printed with 2-space indentation.
19+
* @default false
20+
*/
421
minify?: boolean;
22+
23+
/**
24+
* Sets the `$id` property in the root of the generated schema.
25+
* Used for schema identification and referencing.
26+
*/
527
schemaId?: string;
28+
29+
/**
30+
* Path to a custom tsconfig.json file for TypeScript compilation.
31+
* If not provided, uses default TypeScript configuration.
32+
*/
633
tsconfig?: string;
34+
35+
/**
36+
* Controls which types are exposed as definitions in the schema.
37+
* - "all": Exposes all types except type literals
38+
* - "none": Exposes no types automatically
39+
* - "export": Only exposes exported types (respects @internal JSDoc tag)
40+
* @default "export"
41+
*/
742
expose?: "all" | "none" | "export";
43+
44+
/**
45+
* Wraps the root type in a `$ref` definition.
46+
* When false, inlines the root type definition directly.
47+
* @default true
48+
*/
849
topRef?: boolean;
50+
51+
/**
52+
* Controls how JSDoc comments are parsed and included in the schema.
53+
* - "none": Ignores all JSDoc annotations
54+
* - "basic": Parses standard JSON Schema JSDoc tags
55+
* - "extended": Parses all tags plus descriptions, examples, and type overrides
56+
* @default "extended"
57+
*/
958
jsDoc?: "none" | "extended" | "basic";
59+
60+
/**
61+
* Adds a `markdownDescription` field alongside `description` in the schema.
62+
* Preserves markdown formatting including newlines.
63+
* Only works with `jsDoc: "extended"`.
64+
* @default false
65+
*/
1066
markdownDescription?: boolean;
67+
68+
/**
69+
* Includes the complete raw JSDoc comment as `fullDescription` in the schema.
70+
* Only works with `jsDoc: "extended"`.
71+
* @default false
72+
*/
1173
fullDescription?: boolean;
74+
75+
/**
76+
* Sorts object properties alphabetically in the output.
77+
* @default true
78+
*/
1279
sortProps?: boolean;
80+
81+
/**
82+
* Controls whether tuples allow additional items beyond their defined length.
83+
* @default false
84+
*/
1385
strictTuples?: boolean;
86+
87+
/**
88+
* Skips TypeScript type checking to improve performance.
89+
* Speeds up generation but may miss type errors.
90+
* @default false
91+
*/
1492
skipTypeCheck?: boolean;
93+
94+
/**
95+
* URI-encodes `$ref` values (e.g., `#/definitions/Foo%3CBar%3E`).
96+
* When false, uses raw names in reference paths.
97+
* @default true
98+
*/
1599
encodeRefs?: boolean;
100+
101+
/**
102+
* Array of additional JSDoc tag names to include in the schema.
103+
* Custom tags (e.g., `@customProperty`) are parsed and included in output.
104+
* Values are parsed as JSON5.
105+
* @default []
106+
*/
16107
extraTags?: string[];
108+
109+
/**
110+
* Sets default value for `additionalProperties` on objects without index signatures.
111+
* When false, objects get `additionalProperties: false` by default.
112+
* When true, allows additional properties on all objects.
113+
* @default false
114+
*/
17115
additionalProperties?: boolean;
116+
117+
/**
118+
* Controls discriminator style for discriminated unions.
119+
* - "json-schema": Uses `if`/`then`/`allOf` with properties containing discriminator enum
120+
* - "open-api": Uses OpenAPI 3.x style with `discriminator: { propertyName }` and `oneOf`
121+
* @default "json-schema"
122+
*/
18123
discriminatorType?: "json-schema" | "open-api";
124+
125+
/**
126+
* Controls how function types are handled in the schema.
127+
* - "fail": Throws error when encountering function types
128+
* - "comment": Generates schema with `$comment` describing the function signature
129+
* - "hide": Treats functions as NeverType (excluded from schema)
130+
* @default "comment"
131+
*/
19132
functions?: FunctionOptions;
133+
134+
/**
135+
* Pre-compiled TypeScript Program instance to use.
136+
* Bypasses the default setup of a TypeScript program, and so some configuration options may not be applied.
137+
* Useful for programmatic usage with existing TypeScript compilation, or for vfs scenarios where you do not want file-system representation.
138+
*/
139+
tsProgram?: ts.Program;
20140
}
21141

22142
export type CompletedConfig = Config & typeof DEFAULT_CONFIG;
23143

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

26-
export const DEFAULT_CONFIG: Omit<Required<Config>, "path" | "type" | "schemaId" | "tsconfig"> = {
146+
export const DEFAULT_CONFIG: Omit<Required<Config>, "path" | "type" | "schemaId" | "tsconfig" | "tsProgram"> = {
27147
expose: "export",
28148
topRef: true,
29149
jsDoc: "extended",
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import ts from "typescript";
2+
import { createFSBackedSystem, createVirtualTypeScriptEnvironment } from "@typescript/vfs";
3+
import type { Config } from "../../src/Config";
4+
import { createGenerator } from "../../factory/generator";
5+
6+
it("Can generate a schema from a vfs", () => {
7+
const tsInterface = `
8+
/**
9+
* This is a sample interface
10+
*/
11+
export interface SampleInterface {
12+
/** This is a name */
13+
name: string;
14+
}
15+
`;
16+
17+
const fsMap = new Map<string, string>();
18+
fsMap.set("/schema.ts", tsInterface);
19+
20+
// The FS backed API means that it will use the node_modules for lib.d.ts lookups
21+
const system = createFSBackedSystem(fsMap, __dirname, ts);
22+
const env = createVirtualTypeScriptEnvironment(system, ["/schema.ts"], ts);
23+
const program = env.languageService.getProgram();
24+
if (!program) throw new Error("No program");
25+
26+
const schemaConfig: Config = { path: "/schema.ts", tsProgram: program };
27+
const generator = createGenerator(schemaConfig);
28+
29+
const result = generator.createSchema();
30+
expect(result).toMatchInlineSnapshot(`
31+
{
32+
"$ref": "#/definitions/SampleInterface",
33+
"$schema": "http://json-schema.org/draft-07/schema#",
34+
"definitions": {
35+
"SampleInterface": {
36+
"additionalProperties": false,
37+
"description": "This is a sample interface",
38+
"properties": {
39+
"name": {
40+
"description": "This is a name",
41+
"type": "string",
42+
},
43+
},
44+
"required": [
45+
"name",
46+
],
47+
"type": "object",
48+
},
49+
},
50+
}
51+
`);
52+
});

0 commit comments

Comments
 (0)