fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #3612 from hey-api/feat/ts-compiler-1

feat: scaffold ts-compiler folder

authored by

Lubos and committed by
GitHub
8a0bdaa6 f2ca37ee

+573 -47
+1
packages/openapi-python/src/py-compiler/__snapshots__/nodes/expressions/literal/primitive.py
··· 1 1 s = "hello" 2 2 n = 123 3 3 b = True 4 + c = False 4 5 none = None
+5
packages/openapi-python/src/py-compiler/__tests__/nodes/expressions/literal.test.ts
··· 20 20 py.factory.createLiteral(true), 21 21 ), 22 22 py.factory.createAssignment( 23 + py.factory.createIdentifier('c'), 24 + undefined, 25 + py.factory.createLiteral(false), 26 + ), 27 + py.factory.createAssignment( 23 28 py.factory.createIdentifier('none'), 24 29 undefined, 25 30 py.factory.createLiteral(null),
+7 -1
packages/openapi-python/src/py-compiler/index.ts
··· 24 24 import type { PyKeywordArgument as _PyKeywordArgument } from './nodes/expressions/keywordArg'; 25 25 import type { PyLambdaExpression as _PyLambdaExpression } from './nodes/expressions/lambda'; 26 26 import type { PyListExpression as _PyListExpression } from './nodes/expressions/list'; 27 - import type { PyLiteral as _PyLiteral } from './nodes/expressions/literal'; 27 + import type { 28 + PyLiteral as _PyLiteral, 29 + PyLiteralValue as _PyLiteralValue, 30 + } from './nodes/expressions/literal'; 28 31 import type { PyMemberExpression as _PyMemberExpression } from './nodes/expressions/member'; 29 32 import type { PySetExpression as _PySetExpression } from './nodes/expressions/set'; 30 33 import type { PySubscriptExpression as _PySubscriptExpression } from './nodes/expressions/subscript'; ··· 129 132 130 133 // Printer 131 134 export type PrinterOptions = _PyPrinterOptions; 135 + 136 + // Miscellaneous 137 + export type LiteralValue = _PyLiteralValue; 132 138 } 133 139 134 140 export const py = {
+3 -3
packages/openapi-python/src/py-compiler/nodes/declarations/functionParameter.ts
··· 3 3 import { PyNodeKind } from '../kinds'; 4 4 5 5 export interface PyFunctionParameter extends PyNodeBase { 6 - annotation?: PyExpression; 7 6 defaultValue?: PyExpression; 8 7 kind: PyNodeKind.FunctionParameter; 9 8 name: string; 9 + type?: PyExpression; 10 10 } 11 11 12 12 export function createFunctionParameter( 13 13 name: string, 14 - annotation?: PyExpression, 14 + type?: PyExpression, 15 15 defaultValue?: PyExpression, 16 16 leadingComments?: ReadonlyArray<string>, 17 17 trailingComments?: ReadonlyArray<string>, 18 18 ): PyFunctionParameter { 19 19 return { 20 - annotation, 21 20 defaultValue, 22 21 kind: PyNodeKind.FunctionParameter, 23 22 leadingComments, 24 23 name, 25 24 trailingComments, 25 + type, 26 26 }; 27 27 }
+3 -3
packages/openapi-python/src/py-compiler/nodes/expressions/literal.ts
··· 1 1 import type { PyNodeBase } from '../base'; 2 2 import { PyNodeKind } from '../kinds'; 3 3 4 - export type LiteralValue = string | number | boolean | null; 4 + export type PyLiteralValue = string | number | boolean | null; 5 5 6 6 export interface PyLiteral extends PyNodeBase { 7 7 kind: PyNodeKind.Literal; 8 - value: LiteralValue; 8 + value: PyLiteralValue; 9 9 } 10 10 11 11 export function createLiteral( 12 - value: LiteralValue, 12 + value: PyLiteralValue, 13 13 leadingComments?: ReadonlyArray<string>, 14 14 trailingComments?: ReadonlyArray<string>, 15 15 ): PyLiteral {
+5 -5
packages/openapi-python/src/py-compiler/nodes/statements/assignment.ts
··· 3 3 import { PyNodeKind } from '../kinds'; 4 4 5 5 export interface PyAssignment extends PyNodeBase { 6 - annotation?: PyExpression; 7 6 kind: PyNodeKind.Assignment; 8 7 target: PyExpression; 8 + type?: PyExpression; 9 9 value?: PyExpression; 10 10 } 11 11 12 12 export function createAssignment( 13 13 target: PyExpression, 14 - annotation?: PyExpression, 14 + type?: PyExpression, 15 15 value?: PyExpression, 16 16 leadingComments?: ReadonlyArray<string>, 17 17 trailingComments?: ReadonlyArray<string>, 18 18 ): PyAssignment { 19 - if (!annotation && !value) { 20 - throw new Error('Assignment requires at least annotation or value'); 19 + if (!type && !value) { 20 + throw new Error('Assignment requires at least type or value'); 21 21 } 22 22 23 23 return { 24 - annotation, 25 24 kind: PyNodeKind.Assignment, 26 25 leadingComments, 27 26 target, 28 27 trailingComments, 28 + type, 29 29 value, 30 30 }; 31 31 }
+8 -7
packages/openapi-python/src/py-compiler/printer.ts
··· 5 5 indentSize?: number; 6 6 } 7 7 8 + const DEFAULT_INDENT_SIZE = 4; 8 9 const PARAMS_MULTILINE_THRESHOLD = 3; 9 10 10 11 export function createPrinter(options?: PyPrinterOptions) { 11 - const indentSize = options?.indentSize ?? 4; 12 + const indentSize = options?.indentSize ?? DEFAULT_INDENT_SIZE; 12 13 13 14 let indentLevel = 0; 14 15 ··· 52 53 switch (node.kind) { 53 54 case PyNodeKind.Assignment: { 54 55 const target = printNode(node.target); 55 - if (node.annotation) { 56 - const annotation = printNode(node.annotation); 56 + if (node.type) { 57 + const type = printNode(node.type); 57 58 if (node.value) { 58 - parts.push(printLine(`${target}: ${annotation} = ${printNode(node.value)}`)); 59 + parts.push(printLine(`${target}: ${type} = ${printNode(node.value)}`)); 59 60 } else { 60 - parts.push(printLine(`${target}: ${annotation}`)); 61 + parts.push(printLine(`${target}: ${type}`)); 61 62 } 62 63 } else { 63 64 parts.push(printLine(`${target} = ${printNode(node.value!)}`)); ··· 182 183 const defPrefix = modifiers ? `${modifiers} def` : 'def'; 183 184 const formatParameter = (parameter: (typeof node.parameters)[number]): string => { 184 185 const children: Array<string> = [parameter.name]; 185 - if (parameter.annotation) children.push(`: ${printNode(parameter.annotation)}`); 186 + if (parameter.type) children.push(`: ${printNode(parameter.type)}`); 186 187 if (parameter.defaultValue) children.push(` = ${printNode(parameter.defaultValue)}`); 187 188 return children.join(''); 188 189 }; ··· 272 273 case PyNodeKind.LambdaExpression: { 273 274 const parameters = node.parameters.map((parameter) => { 274 275 const children: Array<string> = [parameter.name]; 275 - if (parameter.annotation) children.push(`: ${printNode(parameter.annotation)}`); 276 + if (parameter.type) children.push(`: ${printNode(parameter.type)}`); 276 277 if (parameter.defaultValue) children.push(` = ${printNode(parameter.defaultValue)}`); 277 278 return children.join(''); 278 279 });
+2 -4
packages/openapi-python/src/py-dsl/expr/literal.ts
··· 3 3 import { py } from '../../py-compiler'; 4 4 import { PyDsl } from '../base'; 5 5 6 - export type LiteralValue = string | number | boolean | null; 7 - 8 6 const Mixed = PyDsl<py.Literal>; 9 7 10 8 export class LiteralPyDsl extends Mixed { 11 9 readonly '~dsl' = 'LiteralPyDsl'; 12 10 13 - protected value: LiteralValue; 11 + protected value: py.LiteralValue; 14 12 15 - constructor(value: LiteralValue) { 13 + constructor(value: py.LiteralValue) { 16 14 super(); 17 15 this.value = value; 18 16 }
+2 -2
packages/openapi-python/src/py-dsl/stmt/var.ts
··· 44 44 override toAst() { 45 45 this.$validate(); 46 46 const target = this.$node(this.name)!; 47 - const annotation = this.$type(); 47 + const type = this.$type(); 48 48 const value = this.$value(); 49 49 50 - return py.factory.createAssignment(target, annotation, value); 50 + return py.factory.createAssignment(target, type, value); 51 51 } 52 52 53 53 $validate(): asserts this {
+4
packages/openapi-ts/src/ts-compiler/__snapshots__/nodes/expressions/identifier/identifier.ts
··· 1 + let x; 2 + let y; 3 + y = 42; 4 + x = y;
+10
packages/openapi-ts/src/ts-compiler/__snapshots__/nodes/expressions/literal/primitive.ts
··· 1 + let s; 2 + let n; 3 + let b; 4 + let c; 5 + let none; 6 + s = "hello"; 7 + n = 123; 8 + b = true; 9 + c = false; 10 + none = null;
+1
packages/openapi-ts/src/ts-compiler/__snapshots__/nodes/statements/var/const.ts
··· 1 + const answer = 42;
+1
packages/openapi-ts/src/ts-compiler/__snapshots__/nodes/statements/var/let.ts
··· 1 + let message = "hello";
+1
packages/openapi-ts/src/ts-compiler/__snapshots__/nodes/statements/var/var.ts
··· 1 + var count = 0;
+4
packages/openapi-ts/src/ts-compiler/__tests__/constants.ts
··· 1 + import path from 'node:path'; 2 + 3 + export const snapshotsDir = path.join(__dirname, '..', '__snapshots__'); 4 + export const tmpDir = path.join(__dirname, '..', '.tmp');
+7
packages/openapi-ts/src/ts-compiler/__tests__/globalTeardown.ts
··· 1 + import fs from 'node:fs'; 2 + 3 + import { tmpDir } from './constants'; 4 + 5 + export function teardown() { 6 + fs.rmSync(tmpDir, { force: true, recursive: true }); 7 + }
+22
packages/openapi-ts/src/ts-compiler/__tests__/nodes/expressions/identifier.test.ts
··· 1 + import { ts } from '../../../index'; 2 + import { assertPrintedMatchesSnapshot } from '../utils'; 3 + 4 + describe('identifier expression', () => { 5 + it('assignment', async () => { 6 + const file = ts.factory.createSourceFile([ 7 + ts.factory.createVariableStatement('let', 'x'), 8 + ts.factory.createVariableStatement('let', 'y'), 9 + ts.factory.createAssignment( 10 + ts.factory.createIdentifier('y'), 11 + undefined, 12 + ts.factory.createLiteral(42), 13 + ), 14 + ts.factory.createAssignment( 15 + ts.factory.createIdentifier('x'), 16 + undefined, 17 + ts.factory.createIdentifier('y'), 18 + ), 19 + ]); 20 + await assertPrintedMatchesSnapshot(file, 'identifier.ts'); 21 + }); 22 + });
+40
packages/openapi-ts/src/ts-compiler/__tests__/nodes/expressions/literal.test.ts
··· 1 + import { ts } from '../../../index'; 2 + import { assertPrintedMatchesSnapshot } from '../utils'; 3 + 4 + describe('literal expression', () => { 5 + it('primitive variables', async () => { 6 + const file = ts.factory.createSourceFile([ 7 + ts.factory.createVariableStatement('let', 's'), 8 + ts.factory.createVariableStatement('let', 'n'), 9 + ts.factory.createVariableStatement('let', 'b'), 10 + ts.factory.createVariableStatement('let', 'c'), 11 + ts.factory.createVariableStatement('let', 'none'), 12 + ts.factory.createAssignment( 13 + ts.factory.createIdentifier('s'), 14 + undefined, 15 + ts.factory.createLiteral('hello'), 16 + ), 17 + ts.factory.createAssignment( 18 + ts.factory.createIdentifier('n'), 19 + undefined, 20 + ts.factory.createLiteral(123), 21 + ), 22 + ts.factory.createAssignment( 23 + ts.factory.createIdentifier('b'), 24 + undefined, 25 + ts.factory.createLiteral(true), 26 + ), 27 + ts.factory.createAssignment( 28 + ts.factory.createIdentifier('c'), 29 + undefined, 30 + ts.factory.createLiteral(false), 31 + ), 32 + ts.factory.createAssignment( 33 + ts.factory.createIdentifier('none'), 34 + undefined, 35 + ts.factory.createLiteral(null), 36 + ), 37 + ]); 38 + await assertPrintedMatchesSnapshot(file, 'primitive.ts'); 39 + }); 40 + });
+25
packages/openapi-ts/src/ts-compiler/__tests__/nodes/statements/var.test.ts
··· 1 + import { ts } from '../../../index'; 2 + import { assertPrintedMatchesSnapshot } from '../utils'; 3 + 4 + describe('variable statement', () => { 5 + it('const', async () => { 6 + const file = ts.factory.createSourceFile([ 7 + ts.factory.createVariableStatement('const', 'answer', ts.factory.createLiteral(42)), 8 + ]); 9 + await assertPrintedMatchesSnapshot(file, 'const.ts'); 10 + }); 11 + 12 + it('let', async () => { 13 + const file = ts.factory.createSourceFile([ 14 + ts.factory.createVariableStatement('let', 'message', ts.factory.createLiteral('hello')), 15 + ]); 16 + await assertPrintedMatchesSnapshot(file, 'let.ts'); 17 + }); 18 + 19 + it('var', async () => { 20 + const file = ts.factory.createSourceFile([ 21 + ts.factory.createVariableStatement('var', 'count', ts.factory.createLiteral(0)), 22 + ]); 23 + await assertPrintedMatchesSnapshot(file, 'var.ts'); 24 + }); 25 + });
+43
packages/openapi-ts/src/ts-compiler/__tests__/nodes/utils.ts
··· 1 + import fs from 'node:fs'; 2 + import path from 'node:path'; 3 + 4 + import { ts } from '../../index'; 5 + import { snapshotsDir, tmpDir } from '../constants'; 6 + 7 + function getCallerFile(): string { 8 + const error = new Error(); 9 + const stack = (error.stack ?? '').split('\n'); 10 + const callerLine = stack.find((line) => line.includes('.test.ts')); 11 + if (!callerLine) { 12 + throw new Error('Could not find test file in stack trace'); 13 + } 14 + const match = callerLine.match(/\(([^)]+)\)/) || callerLine.match(/at (.+):\d+:\d+/); 15 + if (!match?.[1]) { 16 + throw new Error('Could not extract file path'); 17 + } 18 + return match[1]; 19 + } 20 + 21 + export async function assertPrintedMatchesSnapshot( 22 + file: ts.SourceFile, 23 + filename: string, 24 + ): Promise<void> { 25 + const result = ts.createPrinter().printFile(file); 26 + 27 + const caller = getCallerFile(); 28 + const relPath = path 29 + .relative(path.join(process.cwd(), 'src', 'ts-compiler', '__tests__'), caller) 30 + .replace(/\.test\.ts$/, ''); 31 + const outputPath = path.join(tmpDir, relPath, filename); 32 + const outputDir = path.dirname(outputPath); 33 + 34 + fs.mkdirSync(outputDir, { recursive: true }); 35 + fs.writeFileSync(outputPath, result); 36 + 37 + const snapshotPath = path.join(snapshotsDir, relPath, filename); 38 + 39 + const snapshotDir = path.dirname(snapshotPath); 40 + fs.mkdirSync(snapshotDir, { recursive: true }); 41 + 42 + await expect(result).toMatchFileSnapshot(snapshotPath); 43 + }
+56
packages/openapi-ts/src/ts-compiler/index.ts
··· 1 + import type { TsNode as _TsNode, TsNodeBase as _TsNodeBase } from './nodes/base'; 2 + import type { TsExpression as _TsExpression } from './nodes/expression'; 3 + import type { TsIdentifier as _TsIdentifier } from './nodes/expressions/identifier'; 4 + import type { 5 + TsLiteral as _TsLiteral, 6 + TsLiteralValue as _TsLiteralValue, 7 + } from './nodes/expressions/literal'; 8 + import { factory } from './nodes/factory'; 9 + import { TsNodeKind } from './nodes/kinds'; 10 + import type { TsStatement as _TsStatement } from './nodes/statement'; 11 + import type { TsAssignment as _TsAssignment } from './nodes/statements/assignment'; 12 + import type { TsVariableStatement as _TsVariableStatement } from './nodes/statements/var'; 13 + import type { TsSourceFile } from './nodes/structure/sourceFile'; 14 + import type { TsType as _TsType } from './nodes/type'; 15 + import type { TsPrinterOptions as _TsPrinterOptions } from './printer'; 16 + import { createPrinter, printAst } from './printer'; 17 + 18 + // eslint-disable-next-line @typescript-eslint/no-namespace 19 + export namespace ts { 20 + // Base / Core 21 + export type Node = _TsNode; 22 + export type NodeBase = _TsNodeBase; 23 + export type NodeKind = TsNodeKind; 24 + export type Expression = _TsExpression; 25 + export type Statement = _TsStatement; 26 + export type Type = _TsType; 27 + 28 + // Structure 29 + export type SourceFile = TsSourceFile; 30 + 31 + // Declarations 32 + // ... 33 + 34 + // Statements 35 + export type Assignment = _TsAssignment; 36 + export type VariableStatement = _TsVariableStatement; 37 + 38 + // Expressions 39 + export type Identifier = _TsIdentifier; 40 + export type Literal = _TsLiteral; 41 + 42 + // Printer 43 + export type PrinterOptions = _TsPrinterOptions; 44 + 45 + // Miscellaneous 46 + export type LiteralValue = _TsLiteralValue; 47 + } 48 + 49 + export const ts = { 50 + TsNodeKind, 51 + createPrinter, 52 + factory, 53 + printAst, 54 + } as const; 55 + 56 + export { factory };
+14
packages/openapi-ts/src/ts-compiler/nodes/base.ts
··· 1 + import type { TsExpression } from './expression'; 2 + import type { TsNodeKind } from './kinds'; 3 + import type { TsStatement } from './statement'; 4 + // import type { TsBlock } from './statements/block'; 5 + import type { TsSourceFile } from './structure/sourceFile'; 6 + 7 + export interface TsNodeBase { 8 + kind: TsNodeKind; 9 + leadingComments?: ReadonlyArray<string>; 10 + trailingComments?: ReadonlyArray<string>; 11 + } 12 + 13 + // TsBlock | 14 + export type TsNode = TsExpression | TsSourceFile | TsStatement;
+4
packages/openapi-ts/src/ts-compiler/nodes/expression.ts
··· 1 + import type { TsIdentifier } from './expressions/identifier'; 2 + import type { TsLiteral } from './expressions/literal'; 3 + 4 + export type TsExpression = TsIdentifier | TsLiteral;
+20
packages/openapi-ts/src/ts-compiler/nodes/expressions/identifier.ts
··· 1 + import type { TsNodeBase } from '../base'; 2 + import { TsNodeKind } from '../kinds'; 3 + 4 + export interface TsIdentifier extends TsNodeBase { 5 + kind: TsNodeKind.Identifier; 6 + text: string; 7 + } 8 + 9 + export function createIdentifier( 10 + text: string, 11 + leadingComments?: ReadonlyArray<string>, 12 + trailingComments?: ReadonlyArray<string>, 13 + ): TsIdentifier { 14 + return { 15 + kind: TsNodeKind.Identifier, 16 + leadingComments, 17 + text, 18 + trailingComments, 19 + }; 20 + }
+22
packages/openapi-ts/src/ts-compiler/nodes/expressions/literal.ts
··· 1 + import type { TsNodeBase } from '../base'; 2 + import { TsNodeKind } from '../kinds'; 3 + 4 + export type TsLiteralValue = string | number | boolean | bigint | null; 5 + 6 + export interface TsLiteral extends TsNodeBase { 7 + kind: TsNodeKind.Literal; 8 + value: TsLiteralValue; 9 + } 10 + 11 + export function createLiteral( 12 + value: TsLiteralValue, 13 + leadingComments?: ReadonlyArray<string>, 14 + trailingComments?: ReadonlyArray<string>, 15 + ): TsLiteral { 16 + return { 17 + kind: TsNodeKind.Literal, 18 + leadingComments, 19 + trailingComments, 20 + value, 21 + }; 22 + }
+13
packages/openapi-ts/src/ts-compiler/nodes/factory.ts
··· 1 + import { createIdentifier } from './expressions/identifier'; 2 + import { createLiteral } from './expressions/literal'; 3 + import { createAssignment } from './statements/assignment'; 4 + import { createVariableStatement } from './statements/var'; 5 + import { createSourceFile } from './structure/sourceFile'; 6 + 7 + export const factory = { 8 + createAssignment, 9 + createIdentifier, 10 + createLiteral, 11 + createSourceFile, 12 + createVariableStatement, 13 + };
+7
packages/openapi-ts/src/ts-compiler/nodes/kinds.ts
··· 1 + export enum TsNodeKind { 2 + Assignment = 'Assignment', 3 + Identifier = 'Identifier', 4 + Literal = 'Literal', 5 + SourceFile = 'SourceFile', 6 + VariableStatement = 'VariableStatement', 7 + }
+4
packages/openapi-ts/src/ts-compiler/nodes/statement.ts
··· 1 + import type { TsAssignment } from './statements/assignment'; 2 + import type { TsVariableStatement } from './statements/var'; 3 + 4 + export type TsStatement = TsAssignment | TsVariableStatement;
+31
packages/openapi-ts/src/ts-compiler/nodes/statements/assignment.ts
··· 1 + import type { TsNodeBase } from '../base'; 2 + import type { TsExpression } from '../expression'; 3 + import { TsNodeKind } from '../kinds'; 4 + 5 + export interface TsAssignment extends TsNodeBase { 6 + kind: TsNodeKind.Assignment; 7 + target: TsExpression; 8 + type?: TsExpression; 9 + value?: TsExpression; 10 + } 11 + 12 + export function createAssignment( 13 + target: TsExpression, 14 + type?: TsExpression, 15 + value?: TsExpression, 16 + leadingComments?: ReadonlyArray<string>, 17 + trailingComments?: ReadonlyArray<string>, 18 + ): TsAssignment { 19 + if (!type && !value) { 20 + throw new Error('Assignment requires at least type or value'); 21 + } 22 + 23 + return { 24 + kind: TsNodeKind.Assignment, 25 + leadingComments, 26 + target, 27 + trailingComments, 28 + type, 29 + value, 30 + }; 31 + }
+33
packages/openapi-ts/src/ts-compiler/nodes/statements/var.ts
··· 1 + import type { TsNodeBase } from '../base'; 2 + import type { TsExpression } from '../expression'; 3 + import { TsNodeKind } from '../kinds'; 4 + import type { TsType } from '../type'; 5 + 6 + export type TsVariableKeyword = 'var' | 'let' | 'const'; 7 + 8 + export interface TsVariableStatement extends TsNodeBase { 9 + initializer?: TsExpression; 10 + keyword: TsVariableKeyword; 11 + kind: TsNodeKind.VariableStatement; 12 + name: string; 13 + typeAnnotation?: TsType; 14 + } 15 + 16 + export function createVariableStatement( 17 + keyword: TsVariableKeyword, 18 + name: string, 19 + initializer?: TsExpression, 20 + typeAnnotation?: TsType, 21 + leadingComments?: ReadonlyArray<string>, 22 + trailingComments?: ReadonlyArray<string>, 23 + ): TsVariableStatement { 24 + return { 25 + initializer, 26 + keyword, 27 + kind: TsNodeKind.VariableStatement, 28 + leadingComments, 29 + name, 30 + trailingComments, 31 + typeAnnotation, 32 + }; 33 + }
+20
packages/openapi-ts/src/ts-compiler/nodes/structure/sourceFile.ts
··· 1 + import type { TsNode, TsNodeBase } from '../base'; 2 + import { TsNodeKind } from '../kinds'; 3 + 4 + export interface TsSourceFile extends TsNodeBase { 5 + kind: TsNodeKind.SourceFile; 6 + statements: ReadonlyArray<TsNode>; 7 + } 8 + 9 + export function createSourceFile( 10 + statements: ReadonlyArray<TsNode>, 11 + leadingComments?: ReadonlyArray<string>, 12 + trailingComments?: ReadonlyArray<string>, 13 + ): TsSourceFile { 14 + return { 15 + kind: TsNodeKind.SourceFile, 16 + leadingComments, 17 + statements, 18 + trailingComments, 19 + }; 20 + }
+1
packages/openapi-ts/src/ts-compiler/nodes/type.ts
··· 1 + export type TsType = never;
+130
packages/openapi-ts/src/ts-compiler/printer.ts
··· 1 + import type { TsNode } from './nodes/base'; 2 + import { TsNodeKind } from './nodes/kinds'; 3 + 4 + export interface TsPrinterOptions { 5 + /** 6 + * Number of spaces per indentation level. 7 + * 8 + * @default 2 9 + */ 10 + indentSize?: number; 11 + /** 12 + * Whether to add trailing semicolons to statements. 13 + * 14 + * @default true 15 + */ 16 + semicolons?: boolean; 17 + } 18 + 19 + const DEFAULT_INDENT_SIZE = 2; 20 + const DEFAULT_SEMICOLONS = true; 21 + 22 + export function createPrinter(options?: TsPrinterOptions) { 23 + const indentSize = options?.indentSize ?? DEFAULT_INDENT_SIZE; 24 + const semicolons = options?.semicolons ?? DEFAULT_SEMICOLONS; 25 + 26 + let indentLevel = 0; 27 + 28 + function printComments( 29 + parts: Array<string>, 30 + lines: ReadonlyArray<string>, 31 + indent?: boolean, 32 + ): void { 33 + if (indent) indentLevel += 1; 34 + parts.push(...lines.map((line) => printLine(`// ${line}`))); 35 + if (indent) indentLevel -= 1; 36 + } 37 + 38 + function printLine(line: string): string { 39 + if (line === '') return ''; 40 + return ' '.repeat(indentLevel * indentSize) + line; 41 + } 42 + 43 + function printNode(node: TsNode): string { 44 + const parts: Array<string> = []; 45 + 46 + if (node.leadingComments) { 47 + printComments(parts, node.leadingComments); 48 + } 49 + 50 + switch (node.kind) { 51 + case TsNodeKind.Assignment: { 52 + const target = printNode(node.target); 53 + if (node.type) { 54 + const type = printNode(node.type); 55 + if (node.value) { 56 + parts.push(printLine(`${target}: ${type} = ${printNode(node.value)}`)); 57 + } else { 58 + parts.push(printLine(`${target}: ${type}`)); 59 + } 60 + } else { 61 + parts.push(printLine(`${target} = ${printNode(node.value!)}`)); 62 + } 63 + if (semicolons) { 64 + const lastIndex = parts.length - 1; 65 + parts[lastIndex] += ';'; 66 + } 67 + break; 68 + } 69 + 70 + case TsNodeKind.Identifier: 71 + parts.push(node.text); 72 + break; 73 + 74 + case TsNodeKind.Literal: 75 + if (typeof node.value === 'string') { 76 + parts.push(`"${node.value}"`); 77 + } else if (typeof node.value === 'boolean') { 78 + parts.push(node.value ? 'true' : 'false'); 79 + } else if (node.value === null) { 80 + parts.push('null'); 81 + } else { 82 + parts.push(String(node.value)); 83 + } 84 + break; 85 + 86 + case TsNodeKind.SourceFile: 87 + parts.push(...node.statements.map(printNode)); 88 + break; 89 + 90 + case TsNodeKind.VariableStatement: { 91 + const keyword = node.keyword; 92 + const name = node.name; 93 + let line = `${keyword} ${name}`; 94 + if (node.typeAnnotation) { 95 + line += `: ${printNode(node.typeAnnotation)}`; 96 + } 97 + if (node.initializer) { 98 + line += ` = ${printNode(node.initializer)}`; 99 + } 100 + if (semicolons) { 101 + line += ';'; 102 + } 103 + parts.push(printLine(line)); 104 + break; 105 + } 106 + 107 + default: 108 + throw new Error(`Unsupported node kind: ${(node as { kind: string }).kind}`); 109 + } 110 + 111 + if (node.trailingComments) { 112 + printComments(parts, node.trailingComments); 113 + } 114 + 115 + return parts.join('\n'); 116 + } 117 + 118 + function printFile(node: TsNode): string { 119 + const parts: Array<string> = [printNode(node), '']; 120 + return parts.join('\n'); 121 + } 122 + 123 + return { 124 + printFile, 125 + }; 126 + } 127 + 128 + export function printAst(node: TsNode): string { 129 + return JSON.stringify(node, null, 2); 130 + }
+15 -16
packages/openapi-ts/src/ts-dsl/expr/literal.ts
··· 1 1 import type { AnalysisContext } from '@hey-api/codegen-core'; 2 - import ts from 'typescript'; 2 + import tsOld from 'typescript'; 3 3 4 + import type { ts } from '../../ts-compiler'; 4 5 import { TsDsl } from '../base'; 5 6 import { PrefixTsDsl } from '../expr/prefix'; 6 7 import { AsMixin } from '../mixins/as'; 7 8 8 - export type LiteralValue = string | number | boolean | bigint | null; 9 - 10 9 const Mixed = AsMixin( 11 10 TsDsl< 12 - | ts.BigIntLiteral 13 - | ts.BooleanLiteral 14 - | ts.NullLiteral 15 - | ts.NumericLiteral 16 - | ts.PrefixUnaryExpression 17 - | ts.StringLiteral 11 + | tsOld.BigIntLiteral 12 + | tsOld.BooleanLiteral 13 + | tsOld.NullLiteral 14 + | tsOld.NumericLiteral 15 + | tsOld.PrefixUnaryExpression 16 + | tsOld.StringLiteral 18 17 >, 19 18 ); 20 19 21 20 export class LiteralTsDsl extends Mixed { 22 21 readonly '~dsl' = 'LiteralTsDsl'; 23 22 24 - protected value: LiteralValue; 23 + protected value: ts.LiteralValue; 25 24 26 - constructor(value: LiteralValue) { 25 + constructor(value: ts.LiteralValue) { 27 26 super(); 28 27 this.value = value; 29 28 } ··· 34 33 35 34 override toAst() { 36 35 if (typeof this.value === 'boolean') { 37 - return this.value ? ts.factory.createTrue() : ts.factory.createFalse(); 36 + return this.value ? tsOld.factory.createTrue() : tsOld.factory.createFalse(); 38 37 } 39 38 if (typeof this.value === 'number') { 40 - const expr = ts.factory.createNumericLiteral(Math.abs(this.value)); 39 + const expr = tsOld.factory.createNumericLiteral(Math.abs(this.value)); 41 40 return this.value < 0 ? this.$node(new PrefixTsDsl(expr).neg()) : expr; 42 41 } 43 42 if (typeof this.value === 'string') { 44 - return ts.factory.createStringLiteral(this.value, true); 43 + return tsOld.factory.createStringLiteral(this.value, true); 45 44 } 46 45 if (typeof this.value === 'bigint') { 47 - return ts.factory.createBigIntLiteral(this.value.toString()); 46 + return tsOld.factory.createBigIntLiteral(this.value.toString()); 48 47 } 49 48 if (this.value === null) { 50 - return ts.factory.createNull(); 49 + return tsOld.factory.createNull(); 51 50 } 52 51 throw new Error(`Unsupported literal: ${String(this.value)}`); 53 52 }
+7 -6
packages/openapi-ts/src/ts-dsl/type/literal.ts
··· 1 1 import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 2 - import ts from 'typescript'; 2 + import tsOld from 'typescript'; 3 3 4 + import type { ts } from '../../ts-compiler'; 4 5 import { TsDsl } from '../base'; 5 - import { LiteralTsDsl, type LiteralValue } from '../expr/literal'; 6 + import { LiteralTsDsl } from '../expr/literal'; 6 7 7 - const Mixed = TsDsl<ts.LiteralTypeNode>; 8 + const Mixed = TsDsl<tsOld.LiteralTypeNode>; 8 9 9 10 export class TypeLiteralTsDsl extends Mixed { 10 11 readonly '~dsl' = 'TypeLiteralTsDsl'; 11 12 override scope: NodeScope = 'type'; 12 13 13 - protected value: LiteralValue; 14 + protected value: ts.LiteralValue; 14 15 15 - constructor(value: LiteralValue) { 16 + constructor(value: ts.LiteralValue) { 16 17 super(); 17 18 this.value = value; 18 19 } ··· 22 23 } 23 24 24 25 override toAst() { 25 - return ts.factory.createLiteralTypeNode(this.$node(new LiteralTsDsl(this.value))); 26 + return tsOld.factory.createLiteralTypeNode(this.$node(new LiteralTsDsl(this.value))); 26 27 } 27 28 }
+2
vitest.config.ts
··· 22 22 { 23 23 extends: true, 24 24 test: { 25 + globalSetup: ['./src/py-compiler/__tests__/globalTeardown.ts'], 25 26 name: '@hey-api/openapi-python', 26 27 root: 'packages/openapi-python', 27 28 setupFiles: ['./vitest.setup.ts'], ··· 30 31 { 31 32 extends: true, 32 33 test: { 34 + globalSetup: ['./src/ts-compiler/__tests__/globalTeardown.ts'], 33 35 name: '@hey-api/openapi-ts', 34 36 root: 'packages/openapi-ts', 35 37 setupFiles: ['./vitest.setup.ts'],