From ced4219164c53940f48695df78586fd098151fc3 Mon Sep 17 00:00:00 2001 From: Jordan Shatford Date: Mon, 8 Apr 2024 11:26:17 +1000 Subject: [PATCH 1/6] chore(services): move function to services file --- packages/openapi-ts/src/utils/handlebars.ts | 56 ---------------- .../openapi-ts/src/utils/write/services.ts | 64 ++++++++++++++++++- 2 files changed, 63 insertions(+), 57 deletions(-) diff --git a/packages/openapi-ts/src/utils/handlebars.ts b/packages/openapi-ts/src/utils/handlebars.ts index 5a253715f..5a35494e3 100644 --- a/packages/openapi-ts/src/utils/handlebars.ts +++ b/packages/openapi-ts/src/utils/handlebars.ts @@ -59,7 +59,6 @@ import type { Config } from '../types/config'; import { enumKey, enumName, enumUnionType, enumValue } from './enum'; import { escapeComment, escapeDescription, escapeName } from './escape'; import { getDefaultPrintable, modelIsRequired } from './required'; -import { sortByName } from './sort'; import { toType } from './write/type'; const dataDestructure = (config: Config, operation: Operation) => { @@ -140,61 +139,6 @@ const nameOperationDataType = (service: Service, operation: Service['operations' return `${namespace}['${key}']`; }; -export const operationDataType = (config: Config, service: Service) => { - const operationsWithParameters = service.operations.filter(operation => operation.parameters.length); - if (!config.useOptions || !operationsWithParameters.length) { - return ''; - } - const namespace = `${camelCase(service.name, { pascalCase: true })}Data`; - const output = `export type ${namespace} = { - ${operationsWithParameters - .map( - operation => `${camelCase(operation.name, { pascalCase: true })}: { - ${sortByName(operation.parameters) - .filter(parameter => { - if (!config.experimental) { - return true; - } - return parameter.in !== 'query'; - }) - .map(parameter => { - let comment: string[] = []; - if (parameter.description) { - comment = ['/**', ` * ${escapeComment(parameter.description)}`, ' */']; - } - return [ - ...comment, - `${parameter.name + modelIsRequired(config, parameter)}: ${toType(parameter, config)}`, - ].join('\n'); - }) - .join('\n')} - ${ - config.experimental - ? ` - query${operation.parametersQuery.every(parameter => !parameter.isRequired) ? '?' : ''}: { - ${sortByName(operation.parametersQuery) - .map(parameter => { - let comment: string[] = []; - if (parameter.description) { - comment = ['/**', ` * ${escapeComment(parameter.description)}`, ' */']; - } - return [ - ...comment, - `${parameter.name + modelIsRequired(config, parameter)}: ${toType(parameter, config)}`, - ].join('\n'); - }) - .join('\n')} - } - ` - : '' - } - };` - ) - .join('\n')} - }`; - return output; -}; - export const registerHandlebarHelpers = (config: Config, client: Client): void => { Handlebars.registerHelper('camelCase', camelCase); diff --git a/packages/openapi-ts/src/utils/write/services.ts b/packages/openapi-ts/src/utils/write/services.ts index 1b1a032eb..c870d4230 100644 --- a/packages/openapi-ts/src/utils/write/services.ts +++ b/packages/openapi-ts/src/utils/write/services.ts @@ -1,11 +1,73 @@ import { writeFileSync } from 'node:fs'; import path from 'node:path'; +import camelCase from 'camelcase'; + import { TypeScriptFile } from '../../compiler'; +import { Service } from '../../openApi'; import type { Client } from '../../types/client'; import type { Config } from '../../types/config'; -import { operationDataType, type Templates } from '../handlebars'; +import { escapeComment } from '../escape'; +import type { Templates } from '../handlebars'; +import { modelIsRequired } from '../required'; +import { sortByName } from '../sort'; import { unique } from '../unique'; +import { toType } from './type'; + +export const operationDataType = (config: Config, service: Service) => { + const operationsWithParameters = service.operations.filter(operation => operation.parameters.length); + if (!config.useOptions || !operationsWithParameters.length) { + return ''; + } + const namespace = `${camelCase(service.name, { pascalCase: true })}Data`; + const output = `export type ${namespace} = { + ${operationsWithParameters + .map( + operation => `${camelCase(operation.name, { pascalCase: true })}: { + ${sortByName(operation.parameters) + .filter(parameter => { + if (!config.experimental) { + return true; + } + return parameter.in !== 'query'; + }) + .map(parameter => { + let comment: string[] = []; + if (parameter.description) { + comment = ['/**', ` * ${escapeComment(parameter.description)}`, ' */']; + } + return [ + ...comment, + `${parameter.name + modelIsRequired(config, parameter)}: ${toType(parameter, config)}`, + ].join('\n'); + }) + .join('\n')} + ${ + config.experimental + ? ` + query${operation.parametersQuery.every(parameter => !parameter.isRequired) ? '?' : ''}: { + ${sortByName(operation.parametersQuery) + .map(parameter => { + let comment: string[] = []; + if (parameter.description) { + comment = ['/**', ` * ${escapeComment(parameter.description)}`, ' */']; + } + return [ + ...comment, + `${parameter.name + modelIsRequired(config, parameter)}: ${toType(parameter, config)}`, + ].join('\n'); + }) + .join('\n')} + } + ` + : '' + } + };` + ) + .join('\n')} + }`; + return output; +}; /** * Generate Services using the Handlebar template and write to disk. From 94e638cd18cb6b15f0cd2958462c8cd597b8082c Mon Sep 17 00:00:00 2001 From: Jordan Shatford Date: Mon, 8 Apr 2024 11:28:51 +1000 Subject: [PATCH 2/6] chore(templates): remove unused templates --- packages/openapi-ts/src/templates/partials/isNullable.hbs | 1 - packages/openapi-ts/src/templates/partials/isReadOnly.hbs | 1 - packages/openapi-ts/src/utils/handlebars.ts | 4 ---- 3 files changed, 6 deletions(-) delete mode 100644 packages/openapi-ts/src/templates/partials/isNullable.hbs delete mode 100644 packages/openapi-ts/src/templates/partials/isReadOnly.hbs diff --git a/packages/openapi-ts/src/templates/partials/isNullable.hbs b/packages/openapi-ts/src/templates/partials/isNullable.hbs deleted file mode 100644 index 7ef73e3d0..000000000 --- a/packages/openapi-ts/src/templates/partials/isNullable.hbs +++ /dev/null @@ -1 +0,0 @@ -{{#if isNullable}} | null{{/if}} diff --git a/packages/openapi-ts/src/templates/partials/isReadOnly.hbs b/packages/openapi-ts/src/templates/partials/isReadOnly.hbs deleted file mode 100644 index 7ab3bdbf6..000000000 --- a/packages/openapi-ts/src/templates/partials/isReadOnly.hbs +++ /dev/null @@ -1 +0,0 @@ -{{#if isReadOnly}}readonly {{/if}} diff --git a/packages/openapi-ts/src/utils/handlebars.ts b/packages/openapi-ts/src/utils/handlebars.ts index 5a35494e3..61704c567 100644 --- a/packages/openapi-ts/src/utils/handlebars.ts +++ b/packages/openapi-ts/src/utils/handlebars.ts @@ -48,8 +48,6 @@ import xhrGetResponseHeader from '../templates/core/xhr/getResponseHeader.hbs'; import xhrRequest from '../templates/core/xhr/request.hbs'; import xhrSendRequest from '../templates/core/xhr/sendRequest.hbs'; import templateExportService from '../templates/exportService.hbs'; -import partialIsNullable from '../templates/partials/isNullable.hbs'; -import partialIsReadOnly from '../templates/partials/isReadOnly.hbs'; import partialOperationParameters from '../templates/partials/operationParameters.hbs'; import partialOperationResult from '../templates/partials/operationResult.hbs'; import partialOperationTypes from '../templates/partials/operationTypes.hbs'; @@ -262,8 +260,6 @@ export const registerHandlebarTemplates = (config: Config, client: Client): Temp }; // Partials for the generations of the models, services, etc. - Handlebars.registerPartial('isNullable', Handlebars.template(partialIsNullable)); - Handlebars.registerPartial('isReadOnly', Handlebars.template(partialIsReadOnly)); Handlebars.registerPartial('operationParameters', Handlebars.template(partialOperationParameters)); Handlebars.registerPartial('operationResult', Handlebars.template(partialOperationResult)); Handlebars.registerPartial('operationTypes', Handlebars.template(partialOperationTypes)); From d864bc5f7a465d06a8e56e9a15ec81dfbf2ba9e0 Mon Sep 17 00:00:00 2001 From: Jordan Shatford Date: Mon, 8 Apr 2024 11:34:20 +1000 Subject: [PATCH 3/6] chore(compiler): move addLeadingJSDocComment to compiler utils --- packages/openapi-ts/src/compiler/utils.ts | 20 ++++++++++++++++ packages/openapi-ts/src/utils/write/models.ts | 3 ++- packages/openapi-ts/src/utils/write/type.ts | 23 +------------------ 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/openapi-ts/src/compiler/utils.ts b/packages/openapi-ts/src/compiler/utils.ts index 86e0b6219..de4005015 100644 --- a/packages/openapi-ts/src/compiler/utils.ts +++ b/packages/openapi-ts/src/compiler/utils.ts @@ -70,3 +70,23 @@ export const ots = { }; export const isType = (value: T | undefined): value is T => value !== undefined; + +export const addLeadingJSDocComment = ( + node: ts.Node | undefined, + text: Array, + hasTrailingNewLine: boolean = true +): string => { + // if node is falsy, assume string mode + if (node) { + ts.addSyntheticLeadingComment( + node, + ts.SyntaxKind.MultiLineCommentTrivia, + ['*', ...text, ' '].filter(Boolean).join('\n'), + hasTrailingNewLine + ); + return ''; + } + + const result = ['/**', ...text, ' */'].filter(Boolean).join('\n'); + return hasTrailingNewLine ? `${result}\n` : result; +}; diff --git a/packages/openapi-ts/src/utils/write/models.ts b/packages/openapi-ts/src/utils/write/models.ts index 20ec76a48..1cdf7e170 100644 --- a/packages/openapi-ts/src/utils/write/models.ts +++ b/packages/openapi-ts/src/utils/write/models.ts @@ -4,6 +4,7 @@ import ts from 'typescript'; import compiler, { TypeScriptFile } from '../../compiler'; import { toExpression } from '../../compiler/types'; +import { addLeadingJSDocComment } from '../../compiler/utils'; import { isType } from '../../compiler/utils'; import type { Model } from '../../openApi'; import type { Client } from '../../types/client'; @@ -11,7 +12,7 @@ import type { Config } from '../../types/config'; import { enumKey, enumName, enumUnionType, enumValue } from '../enum'; import { escapeComment } from '../escape'; import type { Templates } from '../handlebars'; -import { addLeadingJSDocComment, toType } from './type'; +import { toType } from './type'; type Nodes = Array; diff --git a/packages/openapi-ts/src/utils/write/type.ts b/packages/openapi-ts/src/utils/write/type.ts index 427afcb50..df316aa31 100644 --- a/packages/openapi-ts/src/utils/write/type.ts +++ b/packages/openapi-ts/src/utils/write/type.ts @@ -1,5 +1,4 @@ -import ts from 'typescript'; - +import { addLeadingJSDocComment } from '../../compiler/utils'; import { Model } from '../../openApi'; import { Config } from '../../types/config'; import { enumUnionType } from '../enum'; @@ -7,26 +6,6 @@ import { escapeComment } from '../escape'; import { modelIsRequired } from '../required'; import { unique } from '../unique'; -export const addLeadingJSDocComment = ( - node: any | undefined, - text: Array, - hasTrailingNewLine: boolean = true -): string => { - // if node is falsy, assume string mode - if (node) { - ts.addSyntheticLeadingComment( - node, - ts.SyntaxKind.MultiLineCommentTrivia, - ['*', ...text, ' '].filter(Boolean).join('\n'), - hasTrailingNewLine - ); - return ''; - } - - const result = ['/**', ...text, ' */'].filter(Boolean).join('\n'); - return hasTrailingNewLine ? `${result}\n` : result; -}; - const base = (model: Model, config: Config) => { if (model.base === 'binary') { return 'Blob | File'; From f93c655ed7cd42191cff69659afc65ef5664d963 Mon Sep 17 00:00:00 2001 From: Jordan Shatford Date: Mon, 8 Apr 2024 11:36:31 +1000 Subject: [PATCH 4/6] chore(models): simply node array typing --- packages/openapi-ts/src/utils/write/models.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/openapi-ts/src/utils/write/models.ts b/packages/openapi-ts/src/utils/write/models.ts index 1cdf7e170..8903f9fd5 100644 --- a/packages/openapi-ts/src/utils/write/models.ts +++ b/packages/openapi-ts/src/utils/write/models.ts @@ -14,10 +14,8 @@ import { escapeComment } from '../escape'; import type { Templates } from '../handlebars'; import { toType } from './type'; -type Nodes = Array; - const processComposition = (config: Config, client: Client, model: Model) => { - let nodes: Nodes = [ + let nodes: Array = [ ts.factory.createTypeAliasDeclaration( [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(model.name), @@ -42,7 +40,7 @@ const processComposition = (config: Config, client: Client, model: Model) => { }; const processEnum = (config: Config, client: Client, model: Model, exportType: boolean) => { - let nodes: Nodes = []; + let nodes: Array = []; if (exportType) { if (config.enums === 'typescript') { @@ -105,7 +103,7 @@ const processEnum = (config: Config, client: Client, model: Model, exportType: b }; const processInterface = (config: Config, client: Client, model: Model) => { - let nodes: Nodes = [ + let nodes: Array = [ ts.factory.createTypeAliasDeclaration( [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(model.name), @@ -130,7 +128,7 @@ const processInterface = (config: Config, client: Client, model: Model) => { }; const processType = (config: Config, client: Client, model: Model) => { - const nodes: Nodes = [ + const nodes: Array = [ ts.factory.createTypeAliasDeclaration( [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(model.name), From e7a62738d7d7547f4fa526b0ae1bc990534d41f8 Mon Sep 17 00:00:00 2001 From: Jordan Shatford Date: Mon, 8 Apr 2024 12:13:43 +1000 Subject: [PATCH 5/6] chore(handlebars): remove more unused helpers --- packages/openapi-ts/rollup.config.ts | 4 - packages/openapi-ts/src/index.ts | 2 +- .../src/utils/__tests__/handlebars.spec.ts | 102 +++++++----------- packages/openapi-ts/src/utils/handlebars.ts | 17 +-- 4 files changed, 44 insertions(+), 81 deletions(-) diff --git a/packages/openapi-ts/rollup.config.ts b/packages/openapi-ts/rollup.config.ts index 4b8952a29..186a72fdd 100644 --- a/packages/openapi-ts/rollup.config.ts +++ b/packages/openapi-ts/rollup.config.ts @@ -26,10 +26,6 @@ export function handlebarsPlugin(): Plugin { camelCase: true, dataDestructure: true, dataParameters: true, - enumKey: true, - enumName: true, - enumUnionType: true, - enumValue: true, equals: true, escapeComment: true, escapeDescription: true, diff --git a/packages/openapi-ts/src/index.ts b/packages/openapi-ts/src/index.ts index 533e1e5d5..0abd7fe43 100644 --- a/packages/openapi-ts/src/index.ts +++ b/packages/openapi-ts/src/index.ts @@ -193,7 +193,7 @@ export async function createClient(userConfig: UserConfig): Promise { : (config.input as unknown as Awaited>); const client = postProcessClient(parse(openApi, config)); - const templates = registerHandlebarTemplates(config, client); + const templates = registerHandlebarTemplates(config); if (config.write) { logClientMessage(config.client); diff --git a/packages/openapi-ts/src/utils/__tests__/handlebars.spec.ts b/packages/openapi-ts/src/utils/__tests__/handlebars.spec.ts index 3baa4819a..48e56e5ed 100644 --- a/packages/openapi-ts/src/utils/__tests__/handlebars.spec.ts +++ b/packages/openapi-ts/src/utils/__tests__/handlebars.spec.ts @@ -5,43 +5,30 @@ import { registerHandlebarHelpers, registerHandlebarTemplates } from '../handleb describe('registerHandlebarHelpers', () => { it('should register the helpers', () => { - registerHandlebarHelpers( - { - client: 'fetch', - debug: false, - enums: 'javascript', - experimental: false, - exportCore: true, - exportModels: true, - exportSchemas: true, - exportServices: true, - format: true, - input: '', - lint: false, - operationId: true, - output: '', - postfixServices: '', - serviceResponse: 'body', - useDateType: false, - useOptions: false, - write: true, - }, - { - enumNames: [], - models: [], - server: '', - services: [], - version: '', - } - ); + registerHandlebarHelpers({ + client: 'fetch', + debug: false, + enums: 'javascript', + experimental: false, + exportCore: true, + exportModels: true, + exportSchemas: true, + exportServices: true, + format: true, + input: '', + lint: false, + operationId: true, + output: '', + postfixServices: '', + serviceResponse: 'body', + useDateType: false, + useOptions: false, + write: true, + }); const helpers = Object.keys(Handlebars.helpers); expect(helpers).toContain('camelCase'); expect(helpers).toContain('dataDestructure'); expect(helpers).toContain('dataParameters'); - expect(helpers).toContain('enumKey'); - expect(helpers).toContain('enumName'); - expect(helpers).toContain('enumUnionType'); - expect(helpers).toContain('enumValue'); expect(helpers).toContain('equals'); expect(helpers).toContain('escapeComment'); expect(helpers).toContain('escapeDescription'); @@ -55,35 +42,26 @@ describe('registerHandlebarHelpers', () => { describe('registerHandlebarTemplates', () => { it('should return correct templates', () => { - const templates = registerHandlebarTemplates( - { - client: 'fetch', - debug: false, - enums: 'javascript', - experimental: false, - exportCore: true, - exportModels: true, - exportSchemas: true, - exportServices: true, - format: true, - input: '', - lint: false, - operationId: true, - output: '', - postfixServices: '', - serviceResponse: 'body', - useDateType: false, - useOptions: false, - write: true, - }, - { - enumNames: [], - models: [], - server: '', - services: [], - version: '', - } - ); + const templates = registerHandlebarTemplates({ + client: 'fetch', + debug: false, + enums: 'javascript', + experimental: false, + exportCore: true, + exportModels: true, + exportSchemas: true, + exportServices: true, + format: true, + input: '', + lint: false, + operationId: true, + output: '', + postfixServices: '', + serviceResponse: 'body', + useDateType: false, + useOptions: false, + write: true, + }); expect(templates.exports.service).toBeDefined(); expect(templates.core.settings).toBeDefined(); expect(templates.core.apiError).toBeDefined(); diff --git a/packages/openapi-ts/src/utils/handlebars.ts b/packages/openapi-ts/src/utils/handlebars.ts index 61704c567..1e92275e1 100644 --- a/packages/openapi-ts/src/utils/handlebars.ts +++ b/packages/openapi-ts/src/utils/handlebars.ts @@ -52,9 +52,7 @@ import partialOperationParameters from '../templates/partials/operationParameter import partialOperationResult from '../templates/partials/operationResult.hbs'; import partialOperationTypes from '../templates/partials/operationTypes.hbs'; import partialRequestConfig from '../templates/partials/requestConfig.hbs'; -import type { Client } from '../types/client'; import type { Config } from '../types/config'; -import { enumKey, enumName, enumUnionType, enumValue } from './enum'; import { escapeComment, escapeDescription, escapeName } from './escape'; import { getDefaultPrintable, modelIsRequired } from './required'; import { toType } from './write/type'; @@ -137,7 +135,7 @@ const nameOperationDataType = (service: Service, operation: Service['operations' return `${namespace}['${key}']`; }; -export const registerHandlebarHelpers = (config: Config, client: Client): void => { +export const registerHandlebarHelpers = (config: Config): void => { Handlebars.registerHelper('camelCase', camelCase); Handlebars.registerHelper('dataDestructure', function (operation: Operation) { @@ -148,15 +146,6 @@ export const registerHandlebarHelpers = (config: Config, client: Client): void = return dataParameters(config, parameters); }); - Handlebars.registerHelper('enumKey', enumKey); - - Handlebars.registerHelper('enumName', function (name: string | undefined) { - return enumName(config, client, name); - }); - - Handlebars.registerHelper('enumUnionType', enumUnionType); - Handlebars.registerHelper('enumValue', enumValue); - Handlebars.registerHelper( 'equals', function (this: unknown, a: string, b: string, options: Handlebars.HelperOptions) { @@ -237,8 +226,8 @@ export interface Templates { * Read all the Handlebar templates that we need and return a wrapper object * so we can easily access the templates in our generator/write functions. */ -export const registerHandlebarTemplates = (config: Config, client: Client): Templates => { - registerHandlebarHelpers(config, client); +export const registerHandlebarTemplates = (config: Config): Templates => { + registerHandlebarHelpers(config); // Main templates (entry points for the files we write to disk) const templates: Templates = { From b03f57ac8b86dfba8e456fcb64ade65a5aa97631 Mon Sep 17 00:00:00 2001 From: Jordan Shatford Date: Mon, 8 Apr 2024 12:19:27 +1000 Subject: [PATCH 6/6] feat(compiler): add function to create type alias and simplify model generation --- packages/openapi-ts/src/compiler/index.ts | 3 + packages/openapi-ts/src/compiler/types.ts | 20 ++++- packages/openapi-ts/src/utils/write/models.ts | 73 +++---------------- 3 files changed, 33 insertions(+), 63 deletions(-) diff --git a/packages/openapi-ts/src/compiler/index.ts b/packages/openapi-ts/src/compiler/index.ts index 73c2133ab..992103f6d 100644 --- a/packages/openapi-ts/src/compiler/index.ts +++ b/packages/openapi-ts/src/compiler/index.ts @@ -37,6 +37,9 @@ export const compiler = { import: { named: module.createNamedImportDeclarations, }, + typedef: { + alias: types.createTypeAliasDeclaration, + }, types: { array: types.createArrayType, object: types.createObjectType, diff --git a/packages/openapi-ts/src/compiler/types.ts b/packages/openapi-ts/src/compiler/types.ts index 6545ccf41..25712cae6 100644 --- a/packages/openapi-ts/src/compiler/types.ts +++ b/packages/openapi-ts/src/compiler/types.ts @@ -1,6 +1,6 @@ import ts from 'typescript'; -import { isType, ots } from './utils'; +import { addLeadingJSDocComment, isType, ots } from './utils'; /** * Convert an unknown value to an expression. @@ -62,3 +62,21 @@ export const createObjectType = (obj: T, multiLine: boolean = .filter(isType), multiLine ); + +export const createTypeAliasDeclaration = ( + name: string, + type: string, + typeParameters: string[] = [], + comments?: Parameters[1] +) => { + const node = ts.factory.createTypeAliasDeclaration( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + ts.factory.createIdentifier(name), + typeParameters.map(p => ts.factory.createTypeParameterDeclaration(undefined, p, undefined, undefined)), + ts.factory.createTypeReferenceNode(type) + ); + if (comments?.length) { + addLeadingJSDocComment(node, comments); + } + return node; +}; diff --git a/packages/openapi-ts/src/utils/write/models.ts b/packages/openapi-ts/src/utils/write/models.ts index 8903f9fd5..2c466b9f7 100644 --- a/packages/openapi-ts/src/utils/write/models.ts +++ b/packages/openapi-ts/src/utils/write/models.ts @@ -14,30 +14,10 @@ import { escapeComment } from '../escape'; import type { Templates } from '../handlebars'; import { toType } from './type'; -const processComposition = (config: Config, client: Client, model: Model) => { - let nodes: Array = [ - ts.factory.createTypeAliasDeclaration( - [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], - ts.factory.createIdentifier(model.name), - undefined, - ts.factory.createTypeReferenceNode(toType(model, config)!) - ), - ]; - - if (model.description || model.deprecated) { - addLeadingJSDocComment(nodes[0], [ - model.description && ` * ${escapeComment(model.description)}`, - model.deprecated && ' * @deprecated', - ]); - } - - model.enums.forEach(enumerator => { - const result = processEnum(config, client, enumerator, false); - nodes = [...nodes, ...result]; - }); - - return nodes; -}; +const processComposition = (config: Config, client: Client, model: Model) => [ + processType(config, client, model), + ...model.enums.flatMap(enumerator => processEnum(config, client, enumerator, false)), +]; const processEnum = (config: Config, client: Client, model: Model, exportType: boolean) => { let nodes: Array = []; @@ -102,49 +82,17 @@ const processEnum = (config: Config, client: Client, model: Model, exportType: b return nodes; }; -const processInterface = (config: Config, client: Client, model: Model) => { - let nodes: Array = [ - ts.factory.createTypeAliasDeclaration( - [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], - ts.factory.createIdentifier(model.name), - undefined, - ts.factory.createTypeReferenceNode(toType(model, config)!) - ), - ]; - - if (model.description || model.deprecated) { - addLeadingJSDocComment(nodes[0], [ - model.description && ` * ${escapeComment(model.description)}`, - model.deprecated && ' * @deprecated', - ]); - } - - model.enums.forEach(enumerator => { - const result = processEnum(config, client, enumerator, false); - nodes = [...nodes, ...result]; - }); - - return nodes; -}; +const processInterface = processComposition; const processType = (config: Config, client: Client, model: Model) => { - const nodes: Array = [ - ts.factory.createTypeAliasDeclaration( - [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], - ts.factory.createIdentifier(model.name), - undefined, - ts.factory.createTypeReferenceNode(toType(model, config)!) - ), - ]; - + let comments: Parameters[3] = []; if (model.description || model.deprecated) { - addLeadingJSDocComment(nodes[0], [ + comments = [ model.description && ` * ${escapeComment(model.description)}`, model.deprecated && ' * @deprecated', - ]); + ]; } - - return nodes; + return compiler.typedef.alias(model.name, toType(model, config)!, [], comments); }; const processModel = (config: Config, client: Client, model: Model) => { @@ -182,7 +130,8 @@ export const writeClientModels = async ( const file = new TypeScriptFile(); for (const model of client.models) { const nodes = processModel(config, client, model); - file.add(...nodes); + const n = Array.isArray(nodes) ? nodes : [nodes]; + file.add(...n); } file.write(path.resolve(outputPath, 'models.ts'), '\n\n'); };