ts-json-schema-generator
Version:
Generate JSON schema from your Typescript sources
62 lines (52 loc) • 2.35 kB
text/typescript
import ts from "typescript";
import type { Context, NodeParser } from "../NodeParser.js";
import type { SubNodeParser } from "../SubNodeParser.js";
import type { BaseType } from "../Type/BaseType.js";
import { LiteralType } from "../Type/LiteralType.js";
import { StringType } from "../Type/StringType.js";
import { UnionType } from "../Type/UnionType.js";
import { extractLiterals } from "../Utils/extractLiterals.js";
import { UnknownTypeError } from "../Error/Errors.js";
export class StringTemplateLiteralNodeParser implements SubNodeParser {
public constructor(protected childNodeParser: NodeParser) {}
public supportsNode(node: ts.NoSubstitutionTemplateLiteral | ts.TemplateLiteralTypeNode): boolean {
return (
node.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral || node.kind === ts.SyntaxKind.TemplateLiteralType
);
}
public createType(node: ts.NoSubstitutionTemplateLiteral | ts.TemplateLiteralTypeNode, context: Context): BaseType {
if (node.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) {
return new LiteralType(node.text);
}
try {
const prefix = node.head.text;
const matrix: string[][] = [[prefix]].concat(
node.templateSpans.map((span) => {
const suffix = span.literal.text;
const type = this.childNodeParser.createType(span.type, context);
return extractLiterals(type).map((value) => value + suffix);
}),
);
const expandedLiterals = expand(matrix);
const expandedTypes = expandedLiterals.map((literal) => new LiteralType(literal));
if (expandedTypes.length === 1) {
return expandedTypes[0];
}
return new UnionType(expandedTypes);
} catch (error) {
if (error instanceof UnknownTypeError) {
return new StringType();
}
throw error;
}
}
}
function expand(matrix: string[][]): string[] {
if (matrix.length === 1) {
return matrix[0];
}
const head = matrix[0];
const nested = expand(matrix.slice(1));
const combined = head.map((prefix) => nested.map((suffix) => prefix + suffix));
return ([] as string[]).concat(...combined);
}