Skip to content
10 changes: 9 additions & 1 deletion src/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ export interface Config {
*/
type?: string;

/**
* Array of type names to generate schemas for. Cannot be used with --type.
*/
types?: string[];

/**
* Minify the output JSON schema (no whitespace).
* When false, the schema is pretty-printed with 2-space indentation.
Expand Down Expand Up @@ -143,7 +148,10 @@ export type CompletedConfig = Config & typeof DEFAULT_CONFIG;

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

export const DEFAULT_CONFIG: Omit<Required<Config>, "path" | "type" | "schemaId" | "tsconfig" | "tsProgram"> = {
export const DEFAULT_CONFIG: Omit<
Required<Config>,
"path" | "type" | "types" | "schemaId" | "tsconfig" | "tsProgram"
> = {
expose: "export",
topRef: true,
jsDoc: "extended",
Expand Down
9 changes: 5 additions & 4 deletions src/SchemaGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export class SchemaGenerator {
protected readonly config?: Config,
) {}

public createSchema(fullName?: string): Schema {
const rootNodes = this.getRootNodes(fullName);
public createSchema(fullNames?: string[]): Schema {
const rootNodes = this.getRootNodes(fullNames);
return this.createSchemaFromNodes(rootNodes);
}

Expand Down Expand Up @@ -60,9 +60,10 @@ export class SchemaGenerator {
};
}

protected getRootNodes(fullName: string | undefined): ts.Node[] {
protected getRootNodes(fullName: "*" | string[] | undefined): ts.Node[] {
if (fullName && fullName !== "*") {
return [this.findNamedNode(fullName)];
const fullNameArr = Array.isArray(fullName) ? fullName : [fullName];
return fullNameArr.map((name) => this.findNamedNode(name));
}

const rootFileNames = this.program.getRootFileNames();
Expand Down
18 changes: 16 additions & 2 deletions test/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const basePath = "test/config";

function assertSchema(
name: string,
userConfig: Config & { type: string },
userConfig: (Config & { type: string }) | (Config & { types: string[] }),
tsconfig?: boolean,
formatterAugmentor?: FormatterAugmentor,
parserAugmentor?: ParserAugmentor,
Expand All @@ -51,7 +51,7 @@ function assertSchema(
config,
);

const schema = generator.createSchema(config.type);
const schema = generator.createSchema(config.type ?? config.types);
const schemaFile = resolve(`${basePath}/${name}/schema.json`);

if (process.env.UPDATE_SCHEMA) {
Expand Down Expand Up @@ -390,6 +390,20 @@ describe("config", () => {
}),
);

it(
"multiple-types",
assertSchema("multiple-types", {
types: ["MyObject1", "MyObject2"],
}),
);

it(
"multiple-types-all",
assertSchema("multiple-types-all", {
types: ["MyObject1", "MyObject2", "Object1Prop", "Object2Prop"],
}),
);

it(
"mapped-intersection",
assertSchema("mapped-intersection", {
Expand Down
30 changes: 30 additions & 0 deletions test/config/multiple-types-all/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
type NonExportedType = {
misc: number;
};

export type ExportedType = {
val: string;
val2: NonExportedType;
};

export interface ExportedInterface {
val: string;
}

export type Object1Prop = {
name: string;
};

export type Object2Prop = {
description: string;
};

export type MyObject1 = {
id: number;
bar: Object1Prop;
};

export type MyObject2 = {
idStr: string;
baz: Object2Prop;
};
61 changes: 61 additions & 0 deletions test/config/multiple-types-all/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"MyObject1": {
"properties": {
"id": {
"type": "number"
},
"bar": {
"$ref": "#/definitions/Object1Prop"
}
},
"required": [
"id",
"bar"
],
"type": "object",
"additionalProperties": false
},
"MyObject2": {
"properties": {
"idStr": {
"type": "string"
},
"baz": {
"$ref": "#/definitions/Object2Prop"
}
},
"required": [
"idStr",
"baz"
],
"type": "object",
"additionalProperties": false
},
"Object1Prop": {
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
],
"type": "object",
"additionalProperties": false
},
"Object2Prop": {
"properties": {
"description": {
"type": "string"
}
},
"required": [
"description"
],
"type": "object",
"additionalProperties": false
}
}
}
31 changes: 31 additions & 0 deletions test/config/multiple-types/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
type NonExportedType = {
misc: number;
};

export type ExportedType = {
val: string;
val2: NonExportedType;
};

export interface ExportedInterface {
val: string;
}

// Exported, so we include it as a root node
export type Object1Prop = {
name: string;
};

type Object2Prop = {
description: string;
};

export type MyObject1 = {
id: number;
bar: Object1Prop;
};

export type MyObject2 = {
idStr: string;
baz: Object2Prop;
};
58 changes: 58 additions & 0 deletions test/config/multiple-types/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"MyObject1": {
"properties": {
"id": {
"type": "number"
},
"bar": {
"$ref": "#/definitions/Object1Prop"
}
},
"required": [
"id",
"bar"
],
"type": "object",
"additionalProperties": false
},
"MyObject2": {
"properties": {
"idStr": {
"type": "string"
},
"baz": {
"properties": {
"description": {
"type": "string"
}
},
"required": [
"description"
],
"type": "object",
"additionalProperties": false
}
},
"required": [
"idStr",
"baz"
],
"type": "object",
"additionalProperties": false
},
"Object1Prop": {
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
],
"type": "object",
"additionalProperties": false
}
}
}
10 changes: 9 additions & 1 deletion ts-json-schema-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import pkg from "./package.json";
const args = new Command()
.option("-p, --path <path>", "Source file path")
.option("-t, --type <name>", "Type name")
.option("-t, --types <array-of-names>", "Type names")
.option("-i, --id <name>", "$id for generated schema")
.option("-f, --tsconfig <path>", "Custom tsconfig.json path")
.addOption(
Expand Down Expand Up @@ -68,6 +69,7 @@ const config: Config = {
tsconfig:
typeof args.tsconfig === "string" ? args.tsconfig : findConfigFile(process.cwd(), (f) => tsSys.fileExists(f)),
type: args.type,
types: args.types,
schemaId: args.id,
expose: args.expose,
topRef: args.topRef,
Expand All @@ -84,7 +86,13 @@ const config: Config = {
};

try {
const schema = createGenerator(config).createSchema(args.type);
if (args.type && args.types) {
throw new Error(`Cannot use both --type and --types options simultaneously.`);
}

const fullNames: string[] | undefined = args.types ? args.types : args.type ? [args.type] : undefined;

const schema = createGenerator(config).createSchema(fullNames);

const stringify = config.sortProps ? stableStringify : JSON.stringify;
// need as string since TS can't figure out that the string | undefined case doesn't happen
Expand Down
Loading