From c15d81a3a7ac1be63484a50bdf1e077501fd4d61 Mon Sep 17 00:00:00 2001 From: el-j Date: Sat, 31 Oct 2026 10:09:15 +0100 Subject: [PATCH] feat: prepare direct TypeScript 7 AST migration (non-printer parts) - Migrate AST consumers/factories to `typescript/ast` and `typescript/ast/factory` where the TS 7 API is stable - Keep printer/JSDoc-dependent modules on the `typescript.ts` adapter - Make the plugin a native ESM package, bundled with tsup (same as the CLI) so source imports stay extensionless - Add CJS interop wrappers for `case`, `lodash` and `pluralize` - Harden CLI `getText` with an explicit string-literal check Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- cli/examples/openapi-codegen.config.ts | 2 +- cli/src/cli.ts | 4 +- cli/src/commands/GenerateCommand.ts | 4 +- cli/src/commands/InitCommand.ts | 8 ++-- cli/src/core/updateConfig.ts | 11 +----- cli/src/utils/getText.ts | 14 +++---- plugins/typescript/package.json | 6 ++- .../typescript/src/core/analyzeImportUsage.ts | 7 +++- .../src/core/camelizedPathParams.ts | 2 +- .../src/core/createNamedImport.test.ts | 36 ++++++++++------- .../typescript/src/core/createNamedImport.ts | 13 ++++--- .../src/core/createNamespaceImport.ts | 7 ++-- .../src/core/createOperationFetcherFnNodes.ts | 2 +- .../src/core/createOperationQueryFnNodes.ts | 4 +- .../typescript/src/core/createWatermark.ts | 2 +- .../src/core/getDataResponseType.ts | 7 ++-- .../src/core/getErrorResponseType.ts | 4 +- .../typescript/src/core/getOperationTypes.ts | 39 ++++++++++++------- .../src/core/getParamsGroupByType.ts | 2 +- plugins/typescript/src/core/getReference.ts | 2 +- .../typescript/src/core/getRequestBodyType.ts | 5 ++- plugins/typescript/src/core/getUsedImports.ts | 11 ++++-- .../typescript/src/core/getVariablesType.ts | 2 +- .../src/core/isRequestBodyOptional.ts | 2 +- plugins/typescript/src/core/paramsToSchema.ts | 2 +- .../src/core/schemaToEnumDeclaration.ts | 15 +++---- .../src/core/schemaToTypeAliasDeclaration.ts | 14 +++++-- .../src/generators/generateFetchers.ts | 6 +-- .../generateReactQueryComponents.ts | 6 +-- .../src/generators/generateSchemaTypes.ts | 4 +- plugins/typescript/src/generators/types.ts | 2 +- plugins/typescript/src/templates/context.ts | 2 +- plugins/typescript/src/templates/fetcher.ts | 2 +- plugins/typescript/src/utils/addPathParam.ts | 2 +- plugins/typescript/src/utils/case-interop.ts | 6 +++ .../src/utils/forceReactQueryComponent.ts | 2 +- .../typescript/src/utils/getEnumProperties.ts | 2 +- .../typescript/src/utils/isValidIdentifier.ts | 24 +++++------- .../typescript/src/utils/lodash-interop.ts | 15 +++++++ .../typescript/src/utils/pluralize-interop.ts | 3 ++ .../typescript/src/utils/renameComponent.ts | 2 +- plugins/typescript/tsconfig.json | 2 - pnpm-lock.yaml | 3 ++ 43 files changed, 178 insertions(+), 132 deletions(-) create mode 100644 plugins/typescript/src/utils/case-interop.ts create mode 100644 plugins/typescript/src/utils/lodash-interop.ts create mode 100644 plugins/typescript/src/utils/pluralize-interop.ts diff --git a/cli/examples/openapi-codegen.config.ts b/cli/examples/openapi-codegen.config.ts index fb160566..58fb376c 100644 --- a/cli/examples/openapi-codegen.config.ts +++ b/cli/examples/openapi-codegen.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from "../lib/index.js"; +import { defineConfig } from "../lib/index"; export default defineConfig({ withFile: { diff --git a/cli/src/cli.ts b/cli/src/cli.ts index 73d768f5..dc966805 100644 --- a/cli/src/cli.ts +++ b/cli/src/cli.ts @@ -2,8 +2,8 @@ import { Cli } from "clipanion"; -import { GenerateCommand } from "./commands/GenerateCommand.js"; -import { InitCommand } from "./commands/InitCommand.js"; +import { GenerateCommand } from "./commands/GenerateCommand"; +import { InitCommand } from "./commands/InitCommand"; import { readFileSync } from "fs"; import { join, dirname } from "path"; import { fileURLToPath } from "url"; diff --git a/cli/src/commands/GenerateCommand.ts b/cli/src/commands/GenerateCommand.ts index 7fb0c7b0..8726a4e6 100644 --- a/cli/src/commands/GenerateCommand.ts +++ b/cli/src/commands/GenerateCommand.ts @@ -9,8 +9,8 @@ import { fileURLToPath } from "url"; import slash from "slash"; import { Config, FromOptions, Namespace } from "../types"; -import { getOpenAPISourceFile } from "../core/getOpenAPISourceFile.js"; -import { parseOpenAPISourceFile } from "../core/parseOpenAPISourceFile.js"; +import { getOpenAPISourceFile } from "../core/getOpenAPISourceFile"; +import { parseOpenAPISourceFile } from "../core/parseOpenAPISourceFile"; import { getGithubToken } from "../utils/getGithubToken"; import { resolveGithubPullRequest } from "../utils/resolveGithubPullRequest"; diff --git a/cli/src/commands/InitCommand.ts b/cli/src/commands/InitCommand.ts index 7c85035e..a576e408 100644 --- a/cli/src/commands/InitCommand.ts +++ b/cli/src/commands/InitCommand.ts @@ -12,10 +12,10 @@ import type { FileOptions, FromOptions, UrlOptions, Plugin } from "../types"; import { generateConfigProperty, getImports, -} from "../core/generateConfigProperty.js"; -import { getText } from "../utils/getText.js"; -import emptyConfig from "../templates/emptyConfig.js"; -import { updateConfig } from "../core/updateConfig.js"; +} from "../core/generateConfigProperty"; +import { getText } from "../utils/getText"; +import emptyConfig from "../templates/emptyConfig"; +import { updateConfig } from "../core/updateConfig"; import { handlePromptCancel } from "../utils/handlePromptCancel"; export class InitCommand extends Command { diff --git a/cli/src/core/updateConfig.ts b/cli/src/core/updateConfig.ts index d5226dbc..45234c4e 100644 --- a/cli/src/core/updateConfig.ts +++ b/cli/src/core/updateConfig.ts @@ -1,5 +1,6 @@ import ts from "typescript"; import type { Import } from "../types"; +import { getText } from "../utils/getText"; const { factory: f } = ts; @@ -183,13 +184,3 @@ function createImportStatements(imports: Map) { ) .join("\n"); } - -function getText(expression: ts.Expression) { - try { - return ( - ((expression as unknown as Record).text as string) ?? "" - ); - } catch { - return ""; - } -} diff --git a/cli/src/utils/getText.ts b/cli/src/utils/getText.ts index 764361bb..5a7452bb 100644 --- a/cli/src/utils/getText.ts +++ b/cli/src/utils/getText.ts @@ -1,11 +1,9 @@ -import ts from "typescript"; +import { isStringLiteral } from "typescript/ast"; +import type { Expression } from "typescript/ast"; -export function getText(expression: ts.Expression) { - try { - return ( - ((expression as unknown as Record).text as string) ?? "" - ); - } catch { - return ""; +export function getText(expression: Expression) { + if (!isStringLiteral(expression)) { + throw new TypeError("Expected a string literal module specifier"); } + return expression.text; } diff --git a/plugins/typescript/package.json b/plugins/typescript/package.json index bf0f32ae..838237e6 100644 --- a/plugins/typescript/package.json +++ b/plugins/typescript/package.json @@ -2,6 +2,7 @@ "name": "@openapi-codegen/typescript", "version": "11.3.0", "description": "OpenAPI Codegen typescript generators", + "type": "module", "main": "lib/index.js", "types": "lib/index.d.ts", "repository": { @@ -12,7 +13,7 @@ "access": "public" }, "scripts": { - "build": "pnpm --workspace-root tsc -p plugins/typescript/tsconfig.package.json", + "build": "pnpm --workspace-root tsc --emitDeclarationOnly --declaration -p plugins/typescript/tsconfig.package.json && tsup src/index.ts --outDir lib --format esm", "prepublishOnly": "pnpm build" }, "files": [ @@ -38,6 +39,7 @@ "devDependencies": { "@types/lodash": "^4.17.25", "@types/pluralize": "^0.0.33", - "@types/qs": "^6.15.1" + "@types/qs": "^6.15.1", + "tsup": "^8.5.1" } } diff --git a/plugins/typescript/src/core/analyzeImportUsage.ts b/plugins/typescript/src/core/analyzeImportUsage.ts index d8bb443f..17324e6c 100644 --- a/plugins/typescript/src/core/analyzeImportUsage.ts +++ b/plugins/typescript/src/core/analyzeImportUsage.ts @@ -1,4 +1,4 @@ -import ts from "typescript"; +import * as ts from "typescript/ast"; /** * Determines if a node represents a value usage context @@ -68,7 +68,10 @@ export const analyzeImportUsage = ( return !(usages.type && usages.value); } - return !ts.forEachChild(node, (child) => !checkNode(child as ts.Node)); + return !( + typeof node.forEachChild === "function" && + node.forEachChild((child) => !checkNode(child)) + ); }; nodes.every(checkNode); diff --git a/plugins/typescript/src/core/camelizedPathParams.ts b/plugins/typescript/src/core/camelizedPathParams.ts index 5a424352..440dc6be 100644 --- a/plugins/typescript/src/core/camelizedPathParams.ts +++ b/plugins/typescript/src/core/camelizedPathParams.ts @@ -1,4 +1,4 @@ -import { camel } from "case"; +import { camel } from "../utils/case-interop"; /** * Transform url params case to camel. diff --git a/plugins/typescript/src/core/createNamedImport.test.ts b/plugins/typescript/src/core/createNamedImport.test.ts index efbddc58..53343e9f 100644 --- a/plugins/typescript/src/core/createNamedImport.test.ts +++ b/plugins/typescript/src/core/createNamedImport.test.ts @@ -1,8 +1,8 @@ import { describe, it, expect } from "vitest"; import { createNamedImport, shouldUseTypeImport } from "./createNamedImport"; import { analyzeImportUsage } from "./analyzeImportUsage"; -import { factory as f } from "typescript"; -import * as ts from "typescript"; +import * as f from "typescript/ast/factory"; +import * as ts from "typescript/ast"; // Helper function to set parent references in AST nodes const setParentReferences = (node: ts.Node): ts.Node => { @@ -22,23 +22,17 @@ const setParentReferences = (node: ts.Node): ts.Node => { describe("createNamedImport", () => { it("should create a regular import when isTypeOnly is false", () => { const result = createNamedImport("MyType", "./types", false); - expect( - (result.importClause as unknown as Record)?.isTypeOnly - ).toBe(false); + expect(result.importClause?.phaseModifier).toBeUndefined(); }); it("should create a type-only import when isTypeOnly is true", () => { const result = createNamedImport("MyType", "./types", true); - expect( - (result.importClause as unknown as Record)?.isTypeOnly - ).toBe(true); + expect(result.importClause?.phaseModifier).toBe(ts.SyntaxKind.TypeKeyword); }); it("should handle array of imports", () => { const result = createNamedImport(["Type1", "Type2"], "./types", true); - expect( - (result.importClause as unknown as Record)?.isTypeOnly - ).toBe(true); + expect(result.importClause?.phaseModifier).toBe(ts.SyntaxKind.TypeKeyword); expect(result.importClause?.namedBindings).toBeDefined(); }); }); @@ -63,7 +57,7 @@ describe("analyzeImportUsage", () => { undefined, f.createIdentifier("MyType"), undefined, - f.createTypeReferenceNode(f.createIdentifier("User"), undefined) + f.createTypeReferenceNode(f.createIdentifier("User")) ) ), ]; @@ -74,7 +68,13 @@ describe("analyzeImportUsage", () => { it("should detect value usage", () => { const nodes = [ setParentReferences( - f.createCallExpression(f.createIdentifier("fetchData"), undefined, []) + f.createCallExpression( + f.createIdentifier("fetchData"), + undefined, + undefined, + [], + 0 + ) ), ]; @@ -88,11 +88,17 @@ describe("analyzeImportUsage", () => { undefined, f.createIdentifier("MyType"), undefined, - f.createTypeReferenceNode(f.createIdentifier("User"), undefined) + f.createTypeReferenceNode(f.createIdentifier("User")) ) ), setParentReferences( - f.createCallExpression(f.createIdentifier("User"), undefined, []) + f.createCallExpression( + f.createIdentifier("User"), + undefined, + undefined, + [], + 0 + ) ), ]; diff --git a/plugins/typescript/src/core/createNamedImport.ts b/plugins/typescript/src/core/createNamedImport.ts index 545b16a1..25a625bc 100644 --- a/plugins/typescript/src/core/createNamedImport.ts +++ b/plugins/typescript/src/core/createNamedImport.ts @@ -1,5 +1,6 @@ -import { factory as f } from "typescript"; -import type * as ts from "typescript"; +import * as f from "typescript/ast/factory"; +import { SyntaxKind } from "typescript/ast"; +import type * as ts from "typescript/ast"; import { analyzeImportUsage } from "./analyzeImportUsage"; /** @@ -19,7 +20,7 @@ export const createNamedImport = ( return f.createImportDeclaration( undefined, f.createImportClause( - isTypeOnly, + isTypeOnly ? SyntaxKind.TypeKeyword : undefined, undefined, f.createNamedImports( fnNames.map((name) => @@ -27,7 +28,7 @@ export const createNamedImport = ( ) ) ), - f.createStringLiteral(filename), + f.createStringLiteral(filename, 0), undefined ); }; @@ -81,8 +82,8 @@ export const createNamedImportWithTypes = ( return f.createImportDeclaration( undefined, - f.createImportClause(false, undefined, f.createNamedImports(allImports)), - f.createStringLiteral(filename), + f.createImportClause(undefined, undefined, f.createNamedImports(allImports)), + f.createStringLiteral(filename, 0), undefined ); }; diff --git a/plugins/typescript/src/core/createNamespaceImport.ts b/plugins/typescript/src/core/createNamespaceImport.ts index f049e132..a6498833 100644 --- a/plugins/typescript/src/core/createNamespaceImport.ts +++ b/plugins/typescript/src/core/createNamespaceImport.ts @@ -1,4 +1,5 @@ -import { factory as f } from "typescript"; +import * as f from "typescript/ast/factory"; +import { SyntaxKind } from "typescript/ast"; /** * Helper to create namespace import. @@ -11,10 +12,10 @@ export const createNamespaceImport = (namespace: string, filename: string) => f.createImportDeclaration( undefined, f.createImportClause( - true, + SyntaxKind.TypeKeyword, undefined, f.createNamespaceImport(f.createIdentifier(namespace)) ), - f.createStringLiteral(filename), + f.createStringLiteral(filename, 0), undefined ); diff --git a/plugins/typescript/src/core/createOperationFetcherFnNodes.ts b/plugins/typescript/src/core/createOperationFetcherFnNodes.ts index f66e232d..ed24b6ef 100644 --- a/plugins/typescript/src/core/createOperationFetcherFnNodes.ts +++ b/plugins/typescript/src/core/createOperationFetcherFnNodes.ts @@ -1,5 +1,5 @@ import { OperationObject } from "openapi3-ts/oas30"; -import ts, { factory as f } from "typescript"; +import ts, { factory as f } from "../typescript"; import { camelizedPathParams } from "./camelizedPathParams"; /** diff --git a/plugins/typescript/src/core/createOperationQueryFnNodes.ts b/plugins/typescript/src/core/createOperationQueryFnNodes.ts index 7fe5c0b6..9ecb455b 100644 --- a/plugins/typescript/src/core/createOperationQueryFnNodes.ts +++ b/plugins/typescript/src/core/createOperationQueryFnNodes.ts @@ -1,6 +1,6 @@ -import { camelCase } from "lodash"; +import { camelCase } from "../utils/lodash-interop"; import { OperationObject } from "openapi3-ts/oas30"; -import ts, { factory as f } from "typescript"; +import ts, { factory as f } from "../typescript"; import { camelizedPathParams } from "./camelizedPathParams"; /** diff --git a/plugins/typescript/src/core/createWatermark.ts b/plugins/typescript/src/core/createWatermark.ts index b480555f..4807cb90 100644 --- a/plugins/typescript/src/core/createWatermark.ts +++ b/plugins/typescript/src/core/createWatermark.ts @@ -1,5 +1,5 @@ import { InfoObject } from "openapi3-ts/oas30"; -import { factory as f } from "typescript"; +import { factory as f } from "../typescript"; /** * Generate a JSDoc comment with the generated watermark and `info.version` diff --git a/plugins/typescript/src/core/getDataResponseType.ts b/plugins/typescript/src/core/getDataResponseType.ts index 010fed12..177cb24f 100644 --- a/plugins/typescript/src/core/getDataResponseType.ts +++ b/plugins/typescript/src/core/getDataResponseType.ts @@ -1,4 +1,5 @@ -import ts, { factory as f } from "typescript"; +import * as ts from "typescript/ast"; +import * as f from "typescript/ast/factory"; import { ComponentsObject, isReferenceObject, @@ -6,8 +7,8 @@ import { ResponseObject, ResponsesObject, } from "openapi3-ts/oas30"; -import { uniqBy } from "lodash"; -import { pascal } from "case"; +import { uniqBy } from "../utils/lodash-interop"; +import { pascal } from "../utils/case-interop"; import { findCompatibleMediaType } from "./findCompatibleMediaType"; import { getType } from "./schemaToTypeAliasDeclaration"; diff --git a/plugins/typescript/src/core/getErrorResponseType.ts b/plugins/typescript/src/core/getErrorResponseType.ts index eca183b0..3bd9b182 100644 --- a/plugins/typescript/src/core/getErrorResponseType.ts +++ b/plugins/typescript/src/core/getErrorResponseType.ts @@ -1,4 +1,4 @@ -import ts, { factory as f } from "typescript"; +import ts, { factory as f } from "../typescript"; import { ComponentsObject, isReferenceObject, @@ -9,7 +9,7 @@ import { import { findCompatibleMediaType } from "./findCompatibleMediaType"; import { getType } from "./schemaToTypeAliasDeclaration"; -import { pascal } from "case"; +import { pascal } from "../utils/case-interop"; export const clientErrorStatus = "ClientErrorStatus"; export const serverErrorStatus = "ServerErrorStatus"; diff --git a/plugins/typescript/src/core/getOperationTypes.ts b/plugins/typescript/src/core/getOperationTypes.ts index dcd68051..47c79cf2 100644 --- a/plugins/typescript/src/core/getOperationTypes.ts +++ b/plugins/typescript/src/core/getOperationTypes.ts @@ -1,10 +1,11 @@ -import { pascal } from "case"; +import { pascal } from "../utils/case-interop"; import { OpenAPIObject, OperationObject, PathItemObject, } from "openapi3-ts/oas30"; -import ts, { factory as f } from "typescript"; +import * as ts from "typescript/ast"; +import * as f from "typescript/ast/factory"; import { getParamsGroupByType } from "./getParamsGroupByType"; import { getRequestBodyType } from "./getRequestBodyType"; @@ -134,28 +135,30 @@ export const getOperationTypes = ({ const errorTypeIdentifier = pascal(`${operationId}Error`); declarationNodes.push( f.createTypeAliasDeclaration( - [f.createModifier(ts.SyntaxKind.ExportKeyword)], + [f.createToken(ts.SyntaxKind.ExportKeyword)], f.createIdentifier(errorTypeIdentifier), undefined, errorType ) ); - errorType = f.createTypeReferenceNode(errorTypeIdentifier); + errorType = f.createTypeReferenceNode( + f.createIdentifier(errorTypeIdentifier) + ); // Export data type if needed if (shouldExtractNode(dataType)) { const dataTypeIdentifier = pascal(`${operationId}Response`); declarationNodes.push( f.createTypeAliasDeclaration( - [f.createModifier(ts.SyntaxKind.ExportKeyword)], + [f.createToken(ts.SyntaxKind.ExportKeyword)], f.createIdentifier(dataTypeIdentifier), undefined, dataType ) ); - dataType = f.createTypeReferenceNode(dataTypeIdentifier); + dataType = f.createTypeReferenceNode(f.createIdentifier(dataTypeIdentifier)); } // Export requestBody type if needed @@ -163,29 +166,37 @@ export const getOperationTypes = ({ const requestBodyIdentifier = pascal(`${operationId}RequestBody`); declarationNodes.push( f.createTypeAliasDeclaration( - [f.createModifier(ts.SyntaxKind.ExportKeyword)], + [f.createToken(ts.SyntaxKind.ExportKeyword)], f.createIdentifier(requestBodyIdentifier), undefined, requestBodyType ) ); - requestBodyType = f.createTypeReferenceNode(requestBodyIdentifier); + requestBodyType = f.createTypeReferenceNode( + f.createIdentifier(requestBodyIdentifier) + ); } const pathParamsType = pathParams.length > 0 - ? f.createTypeReferenceNode(`${pascal(operationId)}PathParams`) + ? f.createTypeReferenceNode( + f.createIdentifier(`${pascal(operationId)}PathParams`) + ) : f.createTypeLiteralNode([]); const queryParamsType = queryParams.length > 0 - ? f.createTypeReferenceNode(`${pascal(operationId)}QueryParams`) + ? f.createTypeReferenceNode( + f.createIdentifier(`${pascal(operationId)}QueryParams`) + ) : f.createTypeLiteralNode([]); const headersType = headerParams.length > 0 - ? f.createTypeReferenceNode(`${pascal(operationId)}Headers`) + ? f.createTypeReferenceNode( + f.createIdentifier(`${pascal(operationId)}Headers`) + ) : f.createTypeLiteralNode([]); // Generate fetcher variables type @@ -215,14 +226,16 @@ export const getOperationTypes = ({ if (variablesType.kind !== ts.SyntaxKind.VoidKeyword) { declarationNodes.push( f.createTypeAliasDeclaration( - [f.createModifier(ts.SyntaxKind.ExportKeyword)], + [f.createToken(ts.SyntaxKind.ExportKeyword)], f.createIdentifier(variablesIdentifier), undefined, variablesType ) ); - variablesType = f.createTypeReferenceNode(variablesIdentifier); + variablesType = f.createTypeReferenceNode( + f.createIdentifier(variablesIdentifier) + ); } return { diff --git a/plugins/typescript/src/core/getParamsGroupByType.ts b/plugins/typescript/src/core/getParamsGroupByType.ts index df7490db..2e79a63f 100644 --- a/plugins/typescript/src/core/getParamsGroupByType.ts +++ b/plugins/typescript/src/core/getParamsGroupByType.ts @@ -1,4 +1,4 @@ -import { get, groupBy } from "lodash"; +import { get, groupBy } from "../utils/lodash-interop"; import { ComponentsObject, isReferenceObject, diff --git a/plugins/typescript/src/core/getReference.ts b/plugins/typescript/src/core/getReference.ts index 8649bf8d..4099b338 100644 --- a/plugins/typescript/src/core/getReference.ts +++ b/plugins/typescript/src/core/getReference.ts @@ -1,4 +1,4 @@ -import { get } from "lodash"; +import { get } from "../utils/lodash-interop"; import { ComponentsObject, isReferenceObject, diff --git a/plugins/typescript/src/core/getRequestBodyType.ts b/plugins/typescript/src/core/getRequestBodyType.ts index 6a9e8db9..f33fc805 100644 --- a/plugins/typescript/src/core/getRequestBodyType.ts +++ b/plugins/typescript/src/core/getRequestBodyType.ts @@ -1,11 +1,12 @@ -import ts, { factory as f } from "typescript"; +import * as ts from "typescript/ast"; +import * as f from "typescript/ast/factory"; import { ComponentsObject, isReferenceObject, ReferenceObject, RequestBodyObject, } from "openapi3-ts/oas30"; -import { pascal } from "case"; +import { pascal } from "../utils/case-interop"; import { findCompatibleMediaType } from "./findCompatibleMediaType"; import { getType } from "./schemaToTypeAliasDeclaration"; diff --git a/plugins/typescript/src/core/getUsedImports.ts b/plugins/typescript/src/core/getUsedImports.ts index 4347dce3..e1a9af7e 100644 --- a/plugins/typescript/src/core/getUsedImports.ts +++ b/plugins/typescript/src/core/getUsedImports.ts @@ -1,6 +1,7 @@ -import { camel } from "case"; -import { get } from "lodash"; -import ts, { factory as f } from "typescript"; +import { camel } from "../utils/case-interop"; +import { get } from "../utils/lodash-interop"; +import * as ts from "typescript/ast"; +import * as f from "typescript/ast/factory"; import { createNamedImport } from "./createNamedImport"; import { createNamespaceImport } from "./createNamespaceImport"; import { clientErrorStatus, serverErrorStatus } from "./getErrorResponseType"; @@ -81,7 +82,9 @@ export const getUsedImports = ( imports.utils.imports.add(serverErrorStatus); } } - return ts.forEachChild(node, visitor); + return typeof node.forEachChild === "function" + ? node.forEachChild(visitor) + : undefined; }; ts.visitNodes(f.createNodeArray(nodes), visitor); diff --git a/plugins/typescript/src/core/getVariablesType.ts b/plugins/typescript/src/core/getVariablesType.ts index f0294fe6..6cca5be7 100644 --- a/plugins/typescript/src/core/getVariablesType.ts +++ b/plugins/typescript/src/core/getVariablesType.ts @@ -1,4 +1,4 @@ -import ts, { factory as f } from "typescript"; +import ts, { factory as f } from "../typescript"; /** * Get fetcher variables types from the operation types diff --git a/plugins/typescript/src/core/isRequestBodyOptional.ts b/plugins/typescript/src/core/isRequestBodyOptional.ts index a0a10fa5..953dabf0 100644 --- a/plugins/typescript/src/core/isRequestBodyOptional.ts +++ b/plugins/typescript/src/core/isRequestBodyOptional.ts @@ -1,4 +1,4 @@ -import { get } from "lodash"; +import { get } from "../utils/lodash-interop"; import { ComponentsObject, isReferenceObject, diff --git a/plugins/typescript/src/core/paramsToSchema.ts b/plugins/typescript/src/core/paramsToSchema.ts index 79a63d62..6fed0192 100644 --- a/plugins/typescript/src/core/paramsToSchema.ts +++ b/plugins/typescript/src/core/paramsToSchema.ts @@ -1,4 +1,4 @@ -import { camel } from "case"; +import { camel } from "../utils/case-interop"; import { ParameterObject, SchemaObject } from "openapi3-ts/oas30"; /** diff --git a/plugins/typescript/src/core/schemaToEnumDeclaration.ts b/plugins/typescript/src/core/schemaToEnumDeclaration.ts index e1c98e78..e4f72f6d 100644 --- a/plugins/typescript/src/core/schemaToEnumDeclaration.ts +++ b/plugins/typescript/src/core/schemaToEnumDeclaration.ts @@ -1,6 +1,7 @@ -import { pascal } from "case"; +import { pascal } from "../utils/case-interop"; import { SchemaObject } from "openapi3-ts/oas30"; -import ts, { factory as f } from "typescript"; +import * as ts from "typescript/ast"; +import * as f from "typescript/ast/factory"; import { convertNumberToWord } from "../utils/getEnumProperties"; import { isValidIdentifier } from "../utils/isValidIdentifier"; import { Context, getJSDocComment } from "./schemaToTypeAliasDeclaration"; @@ -20,8 +21,8 @@ export const schemaToEnumDeclaration = ( const jsDocNode = getJSDocComment(schema, context); const members = getEnumMembers(schema); const declarationNode = f.createEnumDeclaration( - [f.createModifier(ts.SyntaxKind.ExportKeyword)], - pascal(name), + [f.createToken(ts.SyntaxKind.ExportKeyword)], + f.createIdentifier(pascal(name)), members ); @@ -48,12 +49,12 @@ function getEnumMembers(schema: SchemaObject): ts.EnumMember[] { } else { enumName = enumValue; } - enumValueNode = f.createStringLiteral(enumValue); + enumValueNode = f.createStringLiteral(enumValue, 0); } else if (typeof enumValue === "number") { enumName = convertNumberToWord(enumValue) .toUpperCase() .replace(/[-\s]/g, "_"); - enumValueNode = f.createNumericLiteral(enumValue); + enumValueNode = f.createNumericLiteral(String(enumValue), 0); } else if (typeof enumValue === "boolean") { enumName = enumValue ? "True" : "False"; } else if (enumValue === null) { @@ -67,7 +68,7 @@ function getEnumMembers(schema: SchemaObject): ts.EnumMember[] { return f.createEnumMember( isValidIdentifier(enumName) ? f.createIdentifier(enumName) - : f.createStringLiteral(`${enumValue}`), + : f.createStringLiteral(`${enumValue}`, 0), enumValueNode ); }) diff --git a/plugins/typescript/src/core/schemaToTypeAliasDeclaration.ts b/plugins/typescript/src/core/schemaToTypeAliasDeclaration.ts index 08624a43..533a4077 100644 --- a/plugins/typescript/src/core/schemaToTypeAliasDeclaration.ts +++ b/plugins/typescript/src/core/schemaToTypeAliasDeclaration.ts @@ -1,5 +1,11 @@ -import { pascal } from "case"; -import { keys, pickBy, intersection, merge, omit } from "lodash"; +import { pascal } from "../utils/case-interop"; +import { + keys, + pickBy, + intersection, + merge, + omit, +} from "../utils/lodash-interop"; import { ComponentsObject, DiscriminatorObject, @@ -9,12 +15,12 @@ import { isReferenceObject, isSchemaObject, } from "openapi3-ts/oas30"; -import { singular } from "pluralize"; +import { singular } from "../utils/pluralize-interop"; import ts, { factory as f, isIdentifierPart, isIdentifierStart, -} from "typescript"; +} from "../typescript"; import { convertNumberToWord } from "../utils/getEnumProperties"; import { isValidIdentifier } from "../utils/isValidIdentifier"; import { getReferenceSchema } from "./getReference"; diff --git a/plugins/typescript/src/generators/generateFetchers.ts b/plugins/typescript/src/generators/generateFetchers.ts index 27de2a6e..ded722b6 100644 --- a/plugins/typescript/src/generators/generateFetchers.ts +++ b/plugins/typescript/src/generators/generateFetchers.ts @@ -1,6 +1,6 @@ -import ts, { factory as f } from "typescript"; -import * as c from "case"; -import { get } from "lodash"; +import ts, { factory as f } from "../typescript"; +import * as c from "../utils/case-interop"; +import { get } from "../utils/lodash-interop"; import { ConfigBase, Context } from "./types"; import { PathItemObject } from "openapi3-ts/oas30"; diff --git a/plugins/typescript/src/generators/generateReactQueryComponents.ts b/plugins/typescript/src/generators/generateReactQueryComponents.ts index 7d75b411..1eaa5b60 100644 --- a/plugins/typescript/src/generators/generateReactQueryComponents.ts +++ b/plugins/typescript/src/generators/generateReactQueryComponents.ts @@ -1,6 +1,6 @@ -import ts, { factory as f } from "typescript"; -import * as c from "case"; -import { get, camelCase } from "lodash"; +import ts, { factory as f } from "../typescript"; +import * as c from "../utils/case-interop"; +import { get, camelCase } from "../utils/lodash-interop"; import { ConfigBase, Context } from "./types"; import { OperationObject, PathItemObject } from "openapi3-ts/oas30"; diff --git a/plugins/typescript/src/generators/generateSchemaTypes.ts b/plugins/typescript/src/generators/generateSchemaTypes.ts index c7f88468..754cab52 100644 --- a/plugins/typescript/src/generators/generateSchemaTypes.ts +++ b/plugins/typescript/src/generators/generateSchemaTypes.ts @@ -1,5 +1,5 @@ -import * as c from "case"; -import ts from "typescript"; +import * as c from "../utils/case-interop"; +import ts from "../typescript"; import { ReferenceObject, SchemaObject } from "openapi3-ts/oas30"; import { createWatermark } from "../core/createWatermark"; diff --git a/plugins/typescript/src/generators/types.ts b/plugins/typescript/src/generators/types.ts index 3494b742..f78eaa0c 100644 --- a/plugins/typescript/src/generators/types.ts +++ b/plugins/typescript/src/generators/types.ts @@ -1,5 +1,5 @@ import { OpenAPIObject } from "openapi3-ts/oas30"; -import * as c from "case"; +import * as c from "../utils/case-interop"; /** * Generator context (provided by `@openapi-codegen/cli`) diff --git a/plugins/typescript/src/templates/context.ts b/plugins/typescript/src/templates/context.ts index 816e303f..53c41de6 100644 --- a/plugins/typescript/src/templates/context.ts +++ b/plugins/typescript/src/templates/context.ts @@ -1,4 +1,4 @@ -import { pascal } from "case"; +import { pascal } from "../utils/case-interop"; export const getContext = ( prefix: string, diff --git a/plugins/typescript/src/templates/fetcher.ts b/plugins/typescript/src/templates/fetcher.ts index ff7d20c2..2b6bc001 100644 --- a/plugins/typescript/src/templates/fetcher.ts +++ b/plugins/typescript/src/templates/fetcher.ts @@ -1,4 +1,4 @@ -import { camel, pascal } from "case"; +import { camel, pascal } from "../utils/case-interop"; /** * Get fetcher template diff --git a/plugins/typescript/src/utils/addPathParam.ts b/plugins/typescript/src/utils/addPathParam.ts index 1e202ab4..12738998 100644 --- a/plugins/typescript/src/utils/addPathParam.ts +++ b/plugins/typescript/src/utils/addPathParam.ts @@ -1,4 +1,4 @@ -import { mapValues } from "lodash"; +import { mapValues } from "../utils/lodash-interop"; import { OpenAPIObject, PathItemObject } from "openapi3-ts/oas30"; /** diff --git a/plugins/typescript/src/utils/case-interop.ts b/plugins/typescript/src/utils/case-interop.ts new file mode 100644 index 00000000..957cafd2 --- /dev/null +++ b/plugins/typescript/src/utils/case-interop.ts @@ -0,0 +1,6 @@ +import caseUtils from "case"; + +export const camel = caseUtils.camel; +export const kebab = caseUtils.kebab; +export const pascal = caseUtils.pascal; +export const snake = caseUtils.snake; diff --git a/plugins/typescript/src/utils/forceReactQueryComponent.ts b/plugins/typescript/src/utils/forceReactQueryComponent.ts index 86c8c0c4..b886390b 100644 --- a/plugins/typescript/src/utils/forceReactQueryComponent.ts +++ b/plugins/typescript/src/utils/forceReactQueryComponent.ts @@ -1,4 +1,4 @@ -import { cloneDeep, set } from "lodash"; +import { cloneDeep, set } from "../utils/lodash-interop"; import { OpenAPIObject, PathItemObject } from "openapi3-ts/oas30"; import { isOperationObject } from "../core/isOperationObject"; diff --git a/plugins/typescript/src/utils/getEnumProperties.ts b/plugins/typescript/src/utils/getEnumProperties.ts index 1564f539..3d5e70c7 100644 --- a/plugins/typescript/src/utils/getEnumProperties.ts +++ b/plugins/typescript/src/utils/getEnumProperties.ts @@ -1,4 +1,4 @@ -import { pascal } from "case"; +import { pascal } from "../utils/case-interop"; import { isReferenceObject, ReferenceObject, diff --git a/plugins/typescript/src/utils/isValidIdentifier.ts b/plugins/typescript/src/utils/isValidIdentifier.ts index abe96624..24373a48 100644 --- a/plugins/typescript/src/utils/isValidIdentifier.ts +++ b/plugins/typescript/src/utils/isValidIdentifier.ts @@ -1,30 +1,24 @@ -import ts from "typescript"; +import { + LanguageVariant, + SyntaxKind, + createScanner, +} from "typescript/ast"; export function isValidIdentifier(name: string): boolean { if (!name || name.length === 0) { return false; } - const scanner = ts.createScanner( - ts.ScriptTarget.Latest, - false, - ts.LanguageVariant.Standard, - name - ); + const scanner = createScanner(false, LanguageVariant.Standard, name); const token = scanner.scan(); if ( - token !== ts.SyntaxKind.Identifier && - !( - typeof (scanner as unknown as Record boolean>) - .isIdentifier === "function" && - (scanner as unknown as Record boolean>).isIdentifier() && - !(scanner as unknown as Record boolean>).isReservedWord() - ) + token !== SyntaxKind.Identifier && + !(scanner.isIdentifier() && !scanner.isReservedWord()) ) { return false; } - const end = scanner.getTokenPos() + scanner.getTokenText().length; + const end = scanner.getTokenStart() + scanner.getTokenText().length; return end === name.length; } diff --git a/plugins/typescript/src/utils/lodash-interop.ts b/plugins/typescript/src/utils/lodash-interop.ts new file mode 100644 index 00000000..3bf13172 --- /dev/null +++ b/plugins/typescript/src/utils/lodash-interop.ts @@ -0,0 +1,15 @@ +import lodash from "lodash"; + +export const camelCase = lodash.camelCase; +export const cloneDeep = lodash.cloneDeep; +export const get = lodash.get; +export const groupBy = lodash.groupBy; +export const intersection = lodash.intersection; +export const keys = lodash.keys; +export const mapValues = lodash.mapValues; +export const merge = lodash.merge; +export const omit = lodash.omit; +export const pickBy = lodash.pickBy; +export const set = lodash.set; +export const uniqBy = lodash.uniqBy; +export const unset = lodash.unset; diff --git a/plugins/typescript/src/utils/pluralize-interop.ts b/plugins/typescript/src/utils/pluralize-interop.ts new file mode 100644 index 00000000..314199cc --- /dev/null +++ b/plugins/typescript/src/utils/pluralize-interop.ts @@ -0,0 +1,3 @@ +import pluralize from "pluralize"; + +export const singular = pluralize.singular; diff --git a/plugins/typescript/src/utils/renameComponent.ts b/plugins/typescript/src/utils/renameComponent.ts index fe34b302..fc0f940d 100644 --- a/plugins/typescript/src/utils/renameComponent.ts +++ b/plugins/typescript/src/utils/renameComponent.ts @@ -1,4 +1,4 @@ -import { get, set, unset } from "lodash"; +import { get, set, unset } from "../utils/lodash-interop"; import { OpenAPIObject } from "openapi3-ts/oas30"; /** diff --git a/plugins/typescript/tsconfig.json b/plugins/typescript/tsconfig.json index 372581f7..2494e911 100644 --- a/plugins/typescript/tsconfig.json +++ b/plugins/typescript/tsconfig.json @@ -1,8 +1,6 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "module": "Node16", - "moduleResolution": "Node16", "lib": ["ESNext", "DOM"] }, "include": ["src/**/*"] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c1582817..49449651 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -328,6 +328,9 @@ importers: '@types/qs': specifier: ^6.15.1 version: 6.15.1 + tsup: + specifier: ^8.5.1 + version: 8.5.1(@swc/core@1.16.13)(jiti@2.4.2)(postcss@8.5.29)(supports-color@7.2.0)(typescript@7.1.0-beta)(yaml@2.9.1) packages: