From e0449d4693567ed21be2207b1578da1081c72bad Mon Sep 17 00:00:00 2001 From: Lubos Date: Sun, 28 Jul 2024 17:35:57 +0100 Subject: [PATCH] feat: allow clients to throw on error --- .../openapi-ts-axios/openapi-ts.config.ts | 1 - .../openapi-ts-fetch/openapi-ts.config.ts | 1 - examples/openapi-ts-fetch/src/App.tsx | 10 ++-- .../src/client/services.gen.ts | 8 ++- .../openapi-ts-fetch/src/client/types.gen.ts | 2 +- packages/client-fetch/src/index.ts | 49 ++++++------------ packages/client-fetch/src/node/index.ts | 3 +- packages/client-fetch/src/types.ts | 9 +--- packages/client-fetch/src/utils.ts | 13 ++++- packages/openapi-ts/src/compiler/classes.ts | 11 ++-- packages/openapi-ts/src/compiler/function.ts | 22 -------- packages/openapi-ts/src/compiler/index.ts | 19 ++++--- packages/openapi-ts/src/compiler/module.ts | 41 +++++++++++++-- packages/openapi-ts/src/compiler/return.ts | 12 ++--- packages/openapi-ts/src/compiler/transform.ts | 51 ++++++++----------- packages/openapi-ts/src/compiler/types.ts | 26 ++++++---- packages/openapi-ts/src/compiler/utils.ts | 8 +++ packages/openapi-ts/src/generate/plugins.ts | 3 +- packages/openapi-ts/src/generate/schemas.ts | 3 +- packages/openapi-ts/src/generate/services.ts | 50 +++++++++++++----- .../openapi-ts/src/generate/transformers.ts | 5 +- packages/openapi-ts/src/generate/types.ts | 3 +- packages/openapi-ts/src/index.ts | 1 + packages/openapi-ts/src/types/config.ts | 5 ++ packages/openapi-ts/test/sample.cjs | 9 ++-- 25 files changed, 202 insertions(+), 163 deletions(-) delete mode 100644 packages/openapi-ts/src/compiler/function.ts diff --git a/examples/openapi-ts-axios/openapi-ts.config.ts b/examples/openapi-ts-axios/openapi-ts.config.ts index 932cdd118..d1a90d1a4 100644 --- a/examples/openapi-ts-axios/openapi-ts.config.ts +++ b/examples/openapi-ts-axios/openapi-ts.config.ts @@ -1,7 +1,6 @@ import { defineConfig } from '@hey-api/openapi-ts'; export default defineConfig({ - base: 'https://petstore3.swagger.io/api/v3', client: '@hey-api/client-axios', input: 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml', diff --git a/examples/openapi-ts-fetch/openapi-ts.config.ts b/examples/openapi-ts-fetch/openapi-ts.config.ts index 3ae7f0c7c..5fff3d094 100644 --- a/examples/openapi-ts-fetch/openapi-ts.config.ts +++ b/examples/openapi-ts-fetch/openapi-ts.config.ts @@ -1,7 +1,6 @@ import { defineConfig } from '@hey-api/openapi-ts'; export default defineConfig({ - base: 'https://petstore3.swagger.io/api/v3', client: '@hey-api/client-fetch', input: 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml', diff --git a/examples/openapi-ts-fetch/src/App.tsx b/examples/openapi-ts-fetch/src/App.tsx index c0349dbb8..d6217617d 100644 --- a/examples/openapi-ts-fetch/src/App.tsx +++ b/examples/openapi-ts-fetch/src/App.tsx @@ -18,26 +18,26 @@ import { import { useState } from 'react'; import { $Pet } from './client/schemas.gen'; -import { addPet, getPetById, updatePet } from './client/services.gen'; +import { addPet, client, getPetById, updatePet } from './client/services.gen'; import type { Pet } from './client/types.gen'; -createClient({ +// configure internal service client +client.setConfig({ // set default base url for requests baseUrl: 'https://petstore3.swagger.io/api/v3', // set default headers for requests headers: { - Authorization: 'Bearer ', + Authorization: 'Bearer ', }, }); const localClient = createClient({ // set default base url for requests made by this client baseUrl: 'https://petstore3.swagger.io/api/v3', - global: false, /** * Set default headers only for requests made by this client. This is to * demonstrate local clients and their configuration taking precedence over - * global configuration. + * internal service client. */ headers: { Authorization: 'Bearer ', diff --git a/examples/openapi-ts-fetch/src/client/services.gen.ts b/examples/openapi-ts-fetch/src/client/services.gen.ts index 18a87829a..dcc30114d 100644 --- a/examples/openapi-ts-fetch/src/client/services.gen.ts +++ b/examples/openapi-ts-fetch/src/client/services.gen.ts @@ -1,6 +1,10 @@ // This file is auto-generated by @hey-api/openapi-ts -import { client, type Options } from '@hey-api/client-fetch'; +import { + createClient, + createConfig, + type Options, +} from '@hey-api/client-fetch'; import type { AddPetData, @@ -52,6 +56,8 @@ import type { UploadFileResponse, } from './types.gen'; +export const client = createClient(createConfig()); + /** * Add a new pet to the store * Add a new pet to the store diff --git a/examples/openapi-ts-fetch/src/client/types.gen.ts b/examples/openapi-ts-fetch/src/client/types.gen.ts index 12c8e1391..19df2b8b3 100644 --- a/examples/openapi-ts-fetch/src/client/types.gen.ts +++ b/examples/openapi-ts-fetch/src/client/types.gen.ts @@ -258,7 +258,7 @@ export type CreateUsersWithListInputData = { body?: Array; }; -export type CreateUsersWithListInputResponse = User; +export type CreateUsersWithListInputResponse = User | unknown; export type CreateUsersWithListInputError = unknown; diff --git a/packages/client-fetch/src/index.ts b/packages/client-fetch/src/index.ts index c2ec84d8d..336f73c84 100644 --- a/packages/client-fetch/src/index.ts +++ b/packages/client-fetch/src/index.ts @@ -1,10 +1,11 @@ import type { Client, Config, RequestOptions } from './types'; import { - createDefaultConfig, + createConfig, createInterceptors, createQuerySerializer, getParseAs, getUrl, + mergeConfigs, mergeHeaders, } from './utils'; @@ -13,42 +14,24 @@ type ReqInit = Omit & { headers: ReturnType; }; -let globalConfig = createDefaultConfig(); +export const createClient = (config: Config = {}): Client => { + let _config = mergeConfigs(createConfig(), config); -const globalInterceptors = createInterceptors< - Request, - Response, - RequestOptions ->(); + const getConfig = (): Config => ({ ..._config }); -export const createClient = (config: Config): Client => { - const defaultConfig = createDefaultConfig(); - const _config = { ...defaultConfig, ...config }; - - if (_config.baseUrl?.endsWith('/')) { - _config.baseUrl = _config.baseUrl.substring(0, _config.baseUrl.length - 1); - } - _config.headers = mergeHeaders(defaultConfig.headers, _config.headers); - - if (_config.global) { - globalConfig = { ..._config }; - } - - // @ts-ignore - const getConfig = () => (_config.root ? globalConfig : _config); + const setConfig = (config: Config): Config => { + _config = mergeConfigs(_config, config); + return getConfig(); + }; - const interceptors = _config.global - ? globalInterceptors - : createInterceptors(); + const interceptors = createInterceptors(); // @ts-ignore const request: Client['request'] = async (options) => { - const config = getConfig(); - const opts: RequestOptions = { - ...config, + ..._config, ...options, - headers: mergeHeaders(config.headers, options.headers), + headers: mergeHeaders(_config.headers, options.headers), }; if (opts.body && opts.bodySerializer) { opts.body = opts.bodySerializer(opts.body); @@ -157,12 +140,12 @@ export const createClient = (config: Config): Client => { post: (options) => request({ ...options, method: 'POST' }), put: (options) => request({ ...options, method: 'PUT' }), request, + setConfig, trace: (options) => request({ ...options, method: 'TRACE' }), }; }; -export const client = createClient({ - ...globalConfig, - // @ts-ignore - root: true, +const c = createClient(); +c.setConfig({ + baseUrl: 'aaaa', }); diff --git a/packages/client-fetch/src/node/index.ts b/packages/client-fetch/src/node/index.ts index 1a4585eeb..2f4392697 100644 --- a/packages/client-fetch/src/node/index.ts +++ b/packages/client-fetch/src/node/index.ts @@ -1,6 +1,7 @@ -export { client, createClient } from '../'; +export { createClient } from '../'; export type { Client, Options, RequestResult } from '../types'; export { + createConfig, formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer, diff --git a/packages/client-fetch/src/types.ts b/packages/client-fetch/src/types.ts index 13955fb64..406a9f0a4 100644 --- a/packages/client-fetch/src/types.ts +++ b/packages/client-fetch/src/types.ts @@ -36,12 +36,6 @@ export interface Config * @default globalThis.fetch */ fetch?: (request: Request) => ReturnType; - /** - * By default, options passed to this call will be applied to the global - * client instance. Set to false to create a local client instance. - * @default true - */ - global?: boolean; /** * An object containing any HTTP headers that you want to pre-populate your * `Headers` object with. @@ -99,7 +93,7 @@ export interface Config responseTransformer?: (data: unknown) => Promise; } -interface RequestOptionsBase extends Omit { +interface RequestOptionsBase extends Config { path?: Record; query?: Record; url: string; @@ -131,6 +125,7 @@ interface ClientBase { post: MethodFn; put: MethodFn; request: RequestFn; + setConfig: (config: Config) => Config; trace: MethodFn; } diff --git a/packages/client-fetch/src/utils.ts b/packages/client-fetch/src/utils.ts index b7f026ce0..43d12b0de 100644 --- a/packages/client-fetch/src/utils.ts +++ b/packages/client-fetch/src/utils.ts @@ -382,6 +382,15 @@ export const getUrl = ({ return url; }; +export const mergeConfigs = (a: Config, b: Config): Config => { + const config = { ...a, ...b }; + if (config.baseUrl?.endsWith('/')) { + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); + } + config.headers = mergeHeaders(a.headers, b.headers); + return config; +}; + export const mergeHeaders = ( ...headers: Array['headers'] | undefined> ) => { @@ -542,11 +551,11 @@ const defaultHeaders = { 'Content-Type': 'application/json', }; -export const createDefaultConfig = (): Config => ({ +export const createConfig = (override: Config = {}): Config => ({ ...jsonBodySerializer, baseUrl: '', fetch: globalThis.fetch, - global: true, headers: defaultHeaders, querySerializer: defaultQuerySerializer, + ...override, }); diff --git a/packages/openapi-ts/src/compiler/classes.ts b/packages/openapi-ts/src/compiler/classes.ts index 5d3aee14d..c9ac6d8ae 100644 --- a/packages/openapi-ts/src/compiler/classes.ts +++ b/packages/openapi-ts/src/compiler/classes.ts @@ -1,5 +1,6 @@ import ts from 'typescript'; +import { createCallExpression } from './module'; import { createTypeNode } from './typedef'; import { type AccessLevel, @@ -124,14 +125,12 @@ export const createClassDeclaration = ({ if (decorator) { modifiers = [ ts.factory.createDecorator( - // TODO: refactor to call `createCallExpression()` from 'compiler/function' - ts.factory.createCallExpression( - ts.factory.createIdentifier(decorator.name), - undefined, - decorator.args + createCallExpression({ + functionName: decorator.name, + parameters: decorator.args .map((arg) => toExpression({ value: arg })) .filter(isType), - ), + }), ), ...modifiers, ]; diff --git a/packages/openapi-ts/src/compiler/function.ts b/packages/openapi-ts/src/compiler/function.ts deleted file mode 100644 index df3e94d48..000000000 --- a/packages/openapi-ts/src/compiler/function.ts +++ /dev/null @@ -1,22 +0,0 @@ -import ts from 'typescript'; - -export const createCallExpression = ({ - parameters, - functionName, -}: { - functionName: string; - parameters: Array; -}) => { - const functionIdentifier = ts.factory.createIdentifier(functionName); - const argumentsArray = parameters.map((parameter) => - ts.factory.createIdentifier(parameter), - ); - - const callExpression = ts.factory.createCallExpression( - functionIdentifier, - undefined, - argumentsArray, - ); - - return callExpression; -}; diff --git a/packages/openapi-ts/src/compiler/index.ts b/packages/openapi-ts/src/compiler/index.ts index d6a336f09..ea447b95f 100644 --- a/packages/openapi-ts/src/compiler/index.ts +++ b/packages/openapi-ts/src/compiler/index.ts @@ -5,13 +5,12 @@ import ts from 'typescript'; import * as classes from './classes'; import * as convert from './convert'; -import * as functions from './function'; import * as module from './module'; import * as _return from './return'; import * as transform from './transform'; import * as typedef from './typedef'; import * as types from './types'; -import { stringToTsNodes, tsNodeToString } from './utils'; +import * as utils from './utils'; export type { Property } from './typedef'; export type { FunctionParameter } from './types'; @@ -104,7 +103,7 @@ export class TypeScriptFile { if (this._imports.length) { output = [ ...output, - this._imports.map((node) => tsNodeToString({ node })).join('\n'), + this._imports.map((node) => utils.tsNodeToString({ node })).join('\n'), ]; } output = [ @@ -112,7 +111,7 @@ export class TypeScriptFile { ...this._items.map((node) => typeof node === 'string' ? node - : tsNodeToString({ node, unescape: true }), + : utils.tsNodeToString({ node, unescape: true }), ), ]; return output.join(seperator); @@ -138,12 +137,8 @@ export const compiler = { }, export: { all: module.createExportAllDeclaration, - const: module.createExportConstVariable, named: module.createNamedExportDeclarations, }, - function: { - call: functions.createCallExpression, - }, import: { named: module.createNamedImportDeclarations, }, @@ -177,12 +172,16 @@ export const compiler = { }, types: { array: types.createArrayType, + call: module.createCallExpression, + const: module.createConstVariable, enum: types.createEnumDeclaration, function: types.createFunction, object: types.createObjectType, }, utils: { - toNode: stringToTsNodes, - toString: tsNodeToString, + isTsNode: utils.isTsNode, + ots: utils.ots, + toNode: utils.stringToTsNodes, + toString: utils.tsNodeToString, }, }; diff --git a/packages/openapi-ts/src/compiler/module.ts b/packages/openapi-ts/src/compiler/module.ts index caf6f8ff3..9b47ad224 100644 --- a/packages/openapi-ts/src/compiler/module.ts +++ b/packages/openapi-ts/src/compiler/module.ts @@ -28,6 +28,32 @@ export const createExportAllDeclaration = ({ type ImportExportItem = ImportExportItemObject | string; +export const createCallExpression = ({ + parameters, + functionName, + types, +}: { + functionName: string | ts.PropertyAccessExpression; + parameters: Array; + types?: ReadonlyArray; +}) => { + const expression = + typeof functionName === 'string' + ? ts.factory.createIdentifier(functionName) + : functionName; + const argumentsArray = parameters.map((parameter) => + typeof parameter === 'string' + ? ts.factory.createIdentifier(parameter) + : parameter, + ); + const callExpression = ts.factory.createCallExpression( + expression, + types, + argumentsArray, + ); + return callExpression; +}; + /** * Create a named export declaration. Example: `export { X } from './y'`. * @param exports - named imports to export @@ -65,22 +91,25 @@ export const createNamedExportDeclarations = ({ }; /** - * Create a const variable export. Optionally, it can use const assertion. - * Example: `export x = {} as const`. + * Create a const variable. Optionally, it can use const assertion or export + * statement. Example: `export x = {} as const`. * @param constAssertion use const assertion? + * @param exportConst export created variable? * @param expression expression for the variable. * @param name name of the variable. * @returns ts.VariableStatement */ -export const createExportConstVariable = ({ +export const createConstVariable = ({ comment, - constAssertion = false, + constAssertion, expression, + exportConst, name, typeName, }: { comment?: Comments; constAssertion?: boolean; + exportConst?: boolean; expression: ts.Expression; name: string; typeName?: string; @@ -98,7 +127,9 @@ export const createExportConstVariable = ({ initializer, ); const statement = ts.factory.createVariableStatement( - [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + exportConst + ? [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)] + : undefined, ts.factory.createVariableDeclarationList([declaration], ts.NodeFlags.Const), ); if (comment) { diff --git a/packages/openapi-ts/src/compiler/return.ts b/packages/openapi-ts/src/compiler/return.ts index 93f9b6a30..afbf81832 100644 --- a/packages/openapi-ts/src/compiler/return.ts +++ b/packages/openapi-ts/src/compiler/return.ts @@ -1,5 +1,6 @@ import ts from 'typescript'; +import { createCallExpression } from './module'; import { isType } from './utils'; /** @@ -27,12 +28,11 @@ export const createReturnFunctionCall = ({ ts.isExpression(arg) ? arg : ts.factory.createIdentifier(arg), ) .filter(isType); - // TODO: refactor to call `createCallExpression()` from 'compiler/function' - const expression = ts.factory.createCallExpression( - ts.factory.createIdentifier(name), - typeArguments, - argumentsArray, - ); + const expression = createCallExpression({ + functionName: name, + parameters: argumentsArray, + types: typeArguments, + }); const statement = createReturnStatement({ expression }); return statement; }; diff --git a/packages/openapi-ts/src/compiler/transform.ts b/packages/openapi-ts/src/compiler/transform.ts index 88e1dbb47..5fd51e5c7 100644 --- a/packages/openapi-ts/src/compiler/transform.ts +++ b/packages/openapi-ts/src/compiler/transform.ts @@ -1,6 +1,7 @@ import ts from 'typescript'; import { convertExpressionToStatement } from './convert'; +import { createCallExpression } from './module'; import { createReturnStatement } from './return'; export const createSafeAccessExpression = (path: string[]) => @@ -81,12 +82,10 @@ export const createFunctionTransformMutation = ({ const thenStatement = ts.factory.createBlock( [ convertExpressionToStatement({ - // TODO: refactor to call `createCallExpression()` from 'compiler/function' - expression: ts.factory.createCallExpression( - ts.factory.createIdentifier(transformerName), - undefined, - [accessExpression], - ), + expression: createCallExpression({ + functionName: transformerName, + parameters: [accessExpression], + }), }), ], true, @@ -113,15 +112,13 @@ export const createArrayTransformMutation = ({ const accessExpression = createAccessExpression(path); const statement = createIfStatement({ - // TODO: refactor to call `createCallExpression()` from 'compiler/function' - expression: ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( + expression: createCallExpression({ + functionName: ts.factory.createPropertyAccessExpression( ts.factory.createIdentifier('Array'), ts.factory.createIdentifier('isArray'), ), - undefined, - [safeAccessExpression], - ), + parameters: [safeAccessExpression], + }), thenStatement: ts.factory.createBlock( [ convertExpressionToStatement({ @@ -165,15 +162,13 @@ export const createArrayMapTransform = ({ const accessExpression = createAccessExpression(path); const statement = createIfStatement({ - // TODO: refactor to call `createCallExpression()` from 'compiler/function' - expression: ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( + expression: createCallExpression({ + functionName: ts.factory.createPropertyAccessExpression( ts.factory.createIdentifier('Array'), ts.factory.createIdentifier('isArray'), ), - undefined, - [safeAccessExpression], - ), + parameters: [safeAccessExpression], + }), thenStatement: ts.factory.createBlock( [ convertExpressionToStatement({ @@ -264,27 +259,23 @@ export const createResponseArrayTransform = ({ ts.factory.createBlock( [ createIfStatement({ - // TODO: refactor to call `createCallExpression()` from 'compiler/function' - expression: ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( + expression: createCallExpression({ + functionName: ts.factory.createPropertyAccessExpression( ts.factory.createIdentifier('Array'), ts.factory.createIdentifier('isArray'), ), - undefined, - [ts.factory.createIdentifier('data')], - ), + parameters: ['data'], + }), thenStatement: ts.factory.createBlock( [ convertExpressionToStatement({ - // TODO: refactor to call `createCallExpression()` from 'compiler/function' - expression: ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( + expression: createCallExpression({ + functionName: ts.factory.createPropertyAccessExpression( ts.factory.createIdentifier('data'), ts.factory.createIdentifier('forEach'), ), - undefined, - [ts.factory.createIdentifier(transform)], - ), + parameters: [transform], + }), }), ], true, diff --git a/packages/openapi-ts/src/compiler/types.ts b/packages/openapi-ts/src/compiler/types.ts index f460d45f4..bc0c2801b 100644 --- a/packages/openapi-ts/src/compiler/types.ts +++ b/packages/openapi-ts/src/compiler/types.ts @@ -1,7 +1,13 @@ import ts from 'typescript'; import { createTypeNode } from './typedef'; -import { addLeadingJSDocComment, type Comments, isType, ots } from './utils'; +import { + addLeadingJSDocComment, + type Comments, + isTsNode, + isType, + ots, +} from './utils'; export type AccessLevel = 'public' | 'protected' | 'private'; @@ -230,14 +236,16 @@ export const createObjectType = < value.value, ); } else { - let initializer: ts.Expression | undefined = toExpression({ - identifiers: identifiers.includes(value.key) - ? Object.keys(value.value) - : [], - shorthand, - unescape, - value: value.value, - }); + let initializer: ts.Expression | undefined = isTsNode(value.value) + ? value.value + : toExpression({ + identifiers: identifiers.includes(value.key) + ? Object.keys(value.value) + : [], + shorthand, + unescape, + value: value.value, + }); if (!initializer) { return undefined; } diff --git a/packages/openapi-ts/src/compiler/utils.ts b/packages/openapi-ts/src/compiler/utils.ts index 4e2aaa92f..3abd83308 100644 --- a/packages/openapi-ts/src/compiler/utils.ts +++ b/packages/openapi-ts/src/compiler/utils.ts @@ -147,6 +147,14 @@ export const ots = { }, }; +export const isTsNode = (node: any): node is ts.Expression => + node !== null && + typeof node === 'object' && + typeof node.kind === 'number' && + typeof node.flags === 'number' && + typeof node.pos === 'number' && + typeof node.end === 'number'; + export const isType = (value: T | undefined): value is T => value !== undefined; diff --git a/packages/openapi-ts/src/generate/plugins.ts b/packages/openapi-ts/src/generate/plugins.ts index 17ba5d5db..639491b13 100644 --- a/packages/openapi-ts/src/generate/plugins.ts +++ b/packages/openapi-ts/src/generate/plugins.ts @@ -66,7 +66,8 @@ export const generatePlugins = async ({ }), ], }); - const statement = compiler.export.const({ + const statement = compiler.types.const({ + exportConst: true, expression, name: toQueryOptionsName(operation), }); diff --git a/packages/openapi-ts/src/generate/schemas.ts b/packages/openapi-ts/src/generate/schemas.ts index 4d287ef69..3376fa78b 100644 --- a/packages/openapi-ts/src/generate/schemas.ts +++ b/packages/openapi-ts/src/generate/schemas.ts @@ -70,8 +70,9 @@ export const generateSchemas = async ({ const validName = `$${ensureValidTypeScriptJavaScriptIdentifier(name)}`; const obj = ensureValidSchemaOutput(schema); const expression = compiler.types.object({ obj }); - const statement = compiler.export.const({ + const statement = compiler.types.const({ constAssertion: true, + exportConst: true, expression, name: validName, }); diff --git a/packages/openapi-ts/src/generate/services.ts b/packages/openapi-ts/src/generate/services.ts index 147ac1044..08bce4015 100644 --- a/packages/openapi-ts/src/generate/services.ts +++ b/packages/openapi-ts/src/generate/services.ts @@ -575,8 +575,9 @@ const processService = ({ onClientImport, ), }); - const statement = compiler.export.const({ + const statement = compiler.types.const({ comment: toOperationComment(operation), + exportConst: true, expression, name: toOperationName(operation, true), }); @@ -666,11 +667,31 @@ export const generateServices = async ({ return; } + const isStandalone = isStandaloneClient(config); + files.services = new TypeScriptFile({ dir: config.output.path, name: 'services.ts', }); + // define client first + if (isStandalone) { + const innerFunction = compiler.types.call({ + functionName: 'createConfig', + parameters: [], + }); + const outerFunction = compiler.types.call({ + functionName: 'createClient', + parameters: [innerFunction], + }); + const statement = compiler.types.const({ + exportConst: true, + expression: outerFunction, + name: 'client', + }); + files.services.add(statement); + } + let imports: string[] = []; let clientImports: string[] = []; @@ -684,17 +705,18 @@ export const generateServices = async ({ imports = [...imports, imported]; }, onNode: (node) => { - files.services?.add(node); + files.services.add(node); }, service, }); } // Import required packages and core files. - if (isStandaloneClient(config)) { - files.services?.addImport({ + if (isStandalone) { + files.services.addImport({ imports: [ - 'client', + 'createClient', + 'createConfig', { asType: true, name: 'Options', @@ -705,38 +727,38 @@ export const generateServices = async ({ }); } else { if (config.client.name === 'angular') { - files.services?.addImport({ + files.services.addImport({ imports: 'Injectable', module: '@angular/core', }); if (!config.name) { - files.services?.addImport({ + files.services.addImport({ imports: 'HttpClient', module: '@angular/common/http', }); } - files.services?.addImport({ + files.services.addImport({ imports: { asType: true, name: 'Observable' }, module: 'rxjs', }); } else { - files.services?.addImport({ + files.services.addImport({ imports: { asType: true, name: 'CancelablePromise' }, module: './core/CancelablePromise', }); } if (config.services.response === 'response') { - files.services?.addImport({ + files.services.addImport({ imports: { asType: true, name: 'ApiResult' }, module: './core/ApiResult', }); } if (config.name) { - files.services?.addImport({ + files.services.addImport({ imports: { asType: config.client.name !== 'angular', name: 'BaseHttpRequest', @@ -744,11 +766,11 @@ export const generateServices = async ({ module: './core/BaseHttpRequest', }); } else { - files.services?.addImport({ + files.services.addImport({ imports: 'OpenAPI', module: './core/OpenAPI', }); - files.services?.addImport({ + files.services.addImport({ imports: { alias: '__request', name: 'request' }, module: './core/request', }); @@ -763,7 +785,7 @@ export const generateServices = async ({ name, })); if (importedTypes.length) { - files.services?.addImport({ + files.services.addImport({ imports: importedTypes, module: `./${files.types.getName(false)}`, }); diff --git a/packages/openapi-ts/src/generate/transformers.ts b/packages/openapi-ts/src/generate/transformers.ts index 0d66a502d..657fde1f8 100644 --- a/packages/openapi-ts/src/generate/transformers.ts +++ b/packages/openapi-ts/src/generate/transformers.ts @@ -161,7 +161,7 @@ const processModel = (props: ModelProps): ts.Statement[] => { return model.in === 'response' ? [ compiler.convert.expressionToStatement({ - expression: compiler.function.call({ + expression: compiler.types.call({ functionName: nameModelResponseTransformer, parameters: [dataVariableName], }), @@ -221,7 +221,8 @@ const generateResponseTransformer = ({ }), ], }); - const statement = compiler.export.const({ + const statement = compiler.types.const({ + exportConst: true, expression, name, typeName: name, diff --git a/packages/openapi-ts/src/generate/types.ts b/packages/openapi-ts/src/generate/types.ts index ef30e323e..7614eaf77 100644 --- a/packages/openapi-ts/src/generate/types.ts +++ b/packages/openapi-ts/src/generate/types.ts @@ -171,9 +171,10 @@ const processEnum = ({ client, model, onNode }: TypesProps) => { obj: properties, unescape: true, }); - const node = compiler.export.const({ + const node = compiler.types.const({ comment, constAssertion: true, + exportConst: true, expression, name, }); diff --git a/packages/openapi-ts/src/index.ts b/packages/openapi-ts/src/index.ts index bd7344dd4..99cbeb585 100644 --- a/packages/openapi-ts/src/index.ts +++ b/packages/openapi-ts/src/index.ts @@ -102,6 +102,7 @@ const getClient = (userConfig: ClientConfig): Config['client'] => { bundle: false, // @ts-ignore name: '', + throwOnError: false, }; if (typeof userConfig.client === 'string') { client.name = userConfig.client; diff --git a/packages/openapi-ts/src/types/config.ts b/packages/openapi-ts/src/types/config.ts index 4d993d915..fea2e66e3 100644 --- a/packages/openapi-ts/src/types/config.ts +++ b/packages/openapi-ts/src/types/config.ts @@ -39,6 +39,11 @@ export interface ClientConfig { * @default 'fetch' */ name: Client; + /** + * Throw an error instead of returning it in the response? + * @default false + */ + throwOnError?: boolean; }; /** * Path to the config file. Set this value if you don't use the default diff --git a/packages/openapi-ts/test/sample.cjs b/packages/openapi-ts/test/sample.cjs index c61374d96..1ecd69694 100644 --- a/packages/openapi-ts/test/sample.cjs +++ b/packages/openapi-ts/test/sample.cjs @@ -3,10 +3,11 @@ const path = require('node:path'); const main = async () => { /** @type {import('../src/node/index').UserConfig} */ const config = { - // client: { - // bundle: true, - // name: '@hey-api/client-fetch', - // }, + client: { + // bundle: true, + name: '@hey-api/client-fetch', + throwOnError: true, + }, debug: true, // input: './test/spec/v3-transforms.json', input: './test/spec/v3.json',