From af76a7705c585fcfae0c72872f358936f7d19d56 Mon Sep 17 00:00:00 2001 From: Lubos Date: Thu, 21 Nov 2024 20:13:51 +0800 Subject: [PATCH] fix: Zod plugin handles value constraints and defaults --- .changeset/chilly-hats-cover.md | 5 + eslint.config.js | 3 + .../src/app/pet-store/pet-store.component.ts | 2 +- .../src/routes/sverdle/+page.server.ts | 4 +- package.json | 1 + packages/openapi-ts/src/compiler/index.ts | 1 + packages/openapi-ts/src/compiler/module.ts | 4 +- packages/openapi-ts/src/compiler/transform.ts | 2 +- packages/openapi-ts/src/index.ts | 2 +- .../src/ir/__tests__/mediaType.test.ts | 2 +- packages/openapi-ts/src/ir/ir.d.ts | 16 +- .../src/legacy/handlebars/handlebars.cjs | 6 +- .../src/openApi/3.0.x/parser/schema.ts | 36 +++ .../src/openApi/3.1.x/parser/schema.ts | 36 +++ .../parser/__tests__/getPattern.spec.ts | 2 +- .../common/parser/__tests__/operation.spec.ts | 2 +- .../common/parser/__tests__/sanitize.spec.ts | 6 +- .../common/parser/__tests__/service.spec.ts | 2 +- .../common/parser/__tests__/sort.spec.ts | 2 +- .../parser/__tests__/stripNamespace.spec.ts | 2 +- .../common/parser/__tests__/type.spec.ts | 2 +- .../src/openApi/shared/utils/operation.ts | 2 +- .../src/openApi/v2/parser/operation.ts | 2 +- .../@hey-api/services/plugin-legacy.ts | 2 +- .../src/plugins/@hey-api/services/plugin.ts | 4 +- .../plugins/@hey-api/types/plugin-legacy.ts | 2 +- .../@tanstack/query-core/plugin-legacy.ts | 4 +- .../plugins/@tanstack/query-core/plugin.ts | 4 +- packages/openapi-ts/src/plugins/zod/config.ts | 3 +- .../src/plugins/zod/plugin-legacy.ts | 122 -------- packages/openapi-ts/src/plugins/zod/plugin.ts | 241 +++++++++++---- .../openapi-ts/src/plugins/zod/types.d.ts | 6 + .../src/utils/__tests__/escape.spec.ts | 4 +- .../src/utils/__tests__/parse.spec.ts | 2 +- .../src/utils/__tests__/postprocess.spec.ts | 2 +- .../src/utils/__tests__/type.spec.ts | 2 +- .../src/utils/__tests__/unique.spec.ts | 4 +- .../3.0.x/plugins/zod/default/zod.gen.ts | 286 +++++++++--------- .../3.1.x/plugins/zod/default/zod.gen.ts | 286 +++++++++--------- packages/openapi-ts/test/index.spec.ts | 6 +- pnpm-lock.yaml | 70 +++-- 41 files changed, 653 insertions(+), 539 deletions(-) create mode 100644 .changeset/chilly-hats-cover.md delete mode 100644 packages/openapi-ts/src/plugins/zod/plugin-legacy.ts diff --git a/.changeset/chilly-hats-cover.md b/.changeset/chilly-hats-cover.md new file mode 100644 index 000000000..2c9d983a2 --- /dev/null +++ b/.changeset/chilly-hats-cover.md @@ -0,0 +1,5 @@ +--- +'@hey-api/openapi-ts': patch +--- + +fix: Zod plugin handles value constraints and defaults diff --git a/eslint.config.js b/eslint.config.js index 2a3efd544..d56078d74 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,6 +1,7 @@ import eslint from '@eslint/js'; import configPrettier from 'eslint-config-prettier'; import pluginSimpleImportSort from 'eslint-plugin-simple-import-sort'; +import pluginSortDestructureKeys from 'eslint-plugin-sort-destructure-keys'; import pluginSortKeysFix from 'eslint-plugin-sort-keys-fix'; import pluginTypeScriptSortKeys from 'eslint-plugin-typescript-sort-keys'; // import pluginVue from 'eslint-plugin-vue' @@ -19,6 +20,7 @@ export default tseslint.config( }, plugins: { 'simple-import-sort': pluginSimpleImportSort, + 'sort-destructure-keys': pluginSortDestructureKeys, 'sort-keys-fix': pluginSortKeysFix, 'typescript-sort-keys': pluginTypeScriptSortKeys, }, @@ -38,6 +40,7 @@ export default tseslint.config( 'object-shorthand': 'error', 'simple-import-sort/exports': 'error', 'simple-import-sort/imports': 'error', + 'sort-destructure-keys/sort-destructure-keys': 'warn', 'sort-imports': 'off', 'sort-keys-fix/sort-keys-fix': 'warn', 'typescript-sort-keys/interface': 'warn', diff --git a/examples/openapi-ts-tanstack-angular-query-experimental/src/app/pet-store/pet-store.component.ts b/examples/openapi-ts-tanstack-angular-query-experimental/src/app/pet-store/pet-store.component.ts index 5849492ed..d6ac696ce 100644 --- a/examples/openapi-ts-tanstack-angular-query-experimental/src/app/pet-store/pet-store.component.ts +++ b/examples/openapi-ts-tanstack-angular-query-experimental/src/app/pet-store/pet-store.component.ts @@ -90,7 +90,7 @@ export class PetStoreComponent { return; } - const { name, category } = form.value as { + const { category, name } = form.value as { category: string; name: string; }; diff --git a/examples/openapi-ts-tanstack-svelte-query/src/routes/sverdle/+page.server.ts b/examples/openapi-ts-tanstack-svelte-query/src/routes/sverdle/+page.server.ts index bb0ec26a1..502925e17 100644 --- a/examples/openapi-ts-tanstack-svelte-query/src/routes/sverdle/+page.server.ts +++ b/examples/openapi-ts-tanstack-svelte-query/src/routes/sverdle/+page.server.ts @@ -30,7 +30,7 @@ export const actions = { * Modify game state in reaction to a guessed word. This logic always runs on * the server, so that people can't cheat by peeking at the JavaScript */ - enter: async ({ request, cookies }) => { + enter: async ({ cookies, request }) => { const game = new Game(cookies.get('sverdle')); const data = await request.formData(); @@ -51,7 +51,7 @@ export const actions = { * Modify game state in reaction to a keypress. If client-side JavaScript * is available, this will happen in the browser instead of here */ - update: async ({ request, cookies }) => { + update: async ({ cookies, request }) => { const game = new Game(cookies.get('sverdle')); const data = await request.formData(); diff --git a/package.json b/package.json index ce9d15582..f5331ee96 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "eslint": "9.6.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-simple-import-sort": "12.1.1", + "eslint-plugin-sort-destructure-keys": "2.0.0", "eslint-plugin-sort-keys-fix": "1.1.2", "eslint-plugin-typescript-sort-keys": "3.2.0", "eslint-plugin-vue": "9.23.0", diff --git a/packages/openapi-ts/src/compiler/index.ts b/packages/openapi-ts/src/compiler/index.ts index 205595b8f..845514bcf 100644 --- a/packages/openapi-ts/src/compiler/index.ts +++ b/packages/openapi-ts/src/compiler/index.ts @@ -71,4 +71,5 @@ export const compiler = { typeReferenceNode: types.createTypeReferenceNode, typeTupleNode: typedef.createTypeTupleNode, typeUnionNode: typedef.createTypeUnionNode, + valueToExpression: types.toExpression, }; diff --git a/packages/openapi-ts/src/compiler/module.ts b/packages/openapi-ts/src/compiler/module.ts index 6df3a49e3..eff3bed0c 100644 --- a/packages/openapi-ts/src/compiler/module.ts +++ b/packages/openapi-ts/src/compiler/module.ts @@ -31,8 +31,8 @@ export const createExportAllDeclaration = ({ export type ImportExportItem = ImportExportItemObject | string; export const createCallExpression = ({ - parameters = [], functionName, + parameters = [], types, }: { functionName: @@ -108,8 +108,8 @@ export const createNamedExportDeclarations = ({ * @returns ts.VariableStatement */ export const createConstVariable = ({ - comment, assertion, + comment, destructure, exportConst, expression, diff --git a/packages/openapi-ts/src/compiler/transform.ts b/packages/openapi-ts/src/compiler/transform.ts index 285a4fd5c..d76b99086 100644 --- a/packages/openapi-ts/src/compiler/transform.ts +++ b/packages/openapi-ts/src/compiler/transform.ts @@ -84,9 +84,9 @@ export const createBinaryExpression = ({ }; export const createIfStatement = ({ + elseStatement, expression, thenStatement, - elseStatement, }: { elseStatement?: ts.Statement; expression: ts.Expression; diff --git a/packages/openapi-ts/src/index.ts b/packages/openapi-ts/src/index.ts index c11f5a597..bdcaf042c 100644 --- a/packages/openapi-ts/src/index.ts +++ b/packages/openapi-ts/src/index.ts @@ -314,8 +314,8 @@ const initConfigs = async (userConfig: UserConfig): Promise => { configFile = '', debug = false, dryRun = false, - exportCore = true, experimentalParser = false, + exportCore = true, name, request, useOptions = true, diff --git a/packages/openapi-ts/src/ir/__tests__/mediaType.test.ts b/packages/openapi-ts/src/ir/__tests__/mediaType.test.ts index 0153205df..eabd1d869 100644 --- a/packages/openapi-ts/src/ir/__tests__/mediaType.test.ts +++ b/packages/openapi-ts/src/ir/__tests__/mediaType.test.ts @@ -63,7 +63,7 @@ describe('isMediaTypeFileLike', () => { it.each(scenarios)( 'detects $mediaType as file-like? $fileLike', - async ({ mediaType, fileLike }) => { + async ({ fileLike, mediaType }) => { expect(isMediaTypeFileLike({ mediaType })).toEqual(fileLike); }, ); diff --git a/packages/openapi-ts/src/ir/ir.d.ts b/packages/openapi-ts/src/ir/ir.d.ts index 41dd09760..128e8b90c 100644 --- a/packages/openapi-ts/src/ir/ir.d.ts +++ b/packages/openapi-ts/src/ir/ir.d.ts @@ -113,7 +113,21 @@ export interface IRResponseObject { export interface IRSchemaObject extends Pick< JsonSchemaDraft2020_12, - '$ref' | 'const' | 'deprecated' | 'description' | 'required' | 'title' + | '$ref' + | 'const' + | 'default' + | 'deprecated' + | 'description' + | 'exclusiveMaximum' + | 'exclusiveMinimum' + | 'maximum' + | 'maxItems' + | 'maxLength' + | 'minimum' + | 'minItems' + | 'minLength' + | 'required' + | 'title' > { /** * If the schema is intended to be used as an object property, it can be diff --git a/packages/openapi-ts/src/legacy/handlebars/handlebars.cjs b/packages/openapi-ts/src/legacy/handlebars/handlebars.cjs index 9a69bef56..7f3978575 100644 --- a/packages/openapi-ts/src/legacy/handlebars/handlebars.cjs +++ b/packages/openapi-ts/src/legacy/handlebars/handlebars.cjs @@ -1,12 +1,12 @@ const Handlebars = require('handlebars'); const { + existsSync, + mkdirSync, readFileSync, readdirSync, + rmdirSync, statSync, - existsSync, - mkdirSync, writeFileSync, - rmdirSync, } = require('node:fs'); const path = require('node:path'); diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/schema.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/schema.ts index 634b72e98..d872b26d9 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/schema.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/schema.ts @@ -44,10 +44,46 @@ const parseSchemaMeta = ({ irSchema: IRSchemaObject; schema: SchemaObject; }) => { + if (schema.default !== undefined) { + irSchema.default = schema.default; + } + + if (schema.exclusiveMaximum) { + if (schema.maximum !== undefined) { + irSchema.exclusiveMaximum = schema.maximum; + } + } else if (schema.maximum !== undefined) { + irSchema.maximum = schema.maximum; + } + + if (schema.exclusiveMinimum) { + if (schema.minimum !== undefined) { + irSchema.exclusiveMinimum = schema.minimum; + } + } else if (schema.minimum !== undefined) { + irSchema.minimum = schema.minimum; + } + if (schema.format) { irSchema.format = schema.format; } + if (schema.maxItems !== undefined) { + irSchema.maxItems = schema.maxItems; + } + + if (schema.maxLength !== undefined) { + irSchema.maxLength = schema.maxLength; + } + + if (schema.minItems !== undefined) { + irSchema.minItems = schema.minItems; + } + + if (schema.minLength !== undefined) { + irSchema.minLength = schema.minLength; + } + if (schema.readOnly) { irSchema.accessScope = 'read'; } else if (schema.writeOnly) { diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts index a4f32c8c9..805b7b0f9 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts @@ -74,10 +74,46 @@ const parseSchemaMeta = ({ } } + if (schema.default !== undefined) { + irSchema.default = schema.default; + } + + if (schema.exclusiveMaximum) { + irSchema.exclusiveMaximum = schema.exclusiveMaximum; + } + + if (schema.exclusiveMinimum) { + irSchema.exclusiveMinimum = schema.exclusiveMinimum; + } + if (schema.format) { irSchema.format = schema.format; } + if (schema.maximum !== undefined) { + irSchema.maximum = schema.maximum; + } + + if (schema.maxItems !== undefined) { + irSchema.maxItems = schema.maxItems; + } + + if (schema.maxLength !== undefined) { + irSchema.maxLength = schema.maxLength; + } + + if (schema.minimum !== undefined) { + irSchema.minimum = schema.minimum; + } + + if (schema.minItems !== undefined) { + irSchema.minItems = schema.minItems; + } + + if (schema.minLength !== undefined) { + irSchema.minLength = schema.minLength; + } + if (schema.readOnly) { irSchema.accessScope = 'read'; } else if (schema.writeOnly) { diff --git a/packages/openapi-ts/src/openApi/common/parser/__tests__/getPattern.spec.ts b/packages/openapi-ts/src/openApi/common/parser/__tests__/getPattern.spec.ts index 1fa398143..0116fb07a 100644 --- a/packages/openapi-ts/src/openApi/common/parser/__tests__/getPattern.spec.ts +++ b/packages/openapi-ts/src/openApi/common/parser/__tests__/getPattern.spec.ts @@ -16,7 +16,7 @@ describe('getPattern', () => { { expected: '\\\\/', pattern: '\\/' }, { expected: '\\\\/\\\\/', pattern: '\\/\\/' }, { expected: "\\'", pattern: "'" }, - ])('getPattern($pattern) -> $expected', ({ pattern, expected }) => { + ])('getPattern($pattern) -> $expected', ({ expected, pattern }) => { expect(getPattern(pattern)).toEqual(expected); }); }); diff --git a/packages/openapi-ts/src/openApi/common/parser/__tests__/operation.spec.ts b/packages/openapi-ts/src/openApi/common/parser/__tests__/operation.spec.ts index ca9a7e24a..ea2b43f0c 100644 --- a/packages/openapi-ts/src/openApi/common/parser/__tests__/operation.spec.ts +++ b/packages/openapi-ts/src/openApi/common/parser/__tests__/operation.spec.ts @@ -12,7 +12,7 @@ describe('parseResponseStatusCode', () => { { expected: '4XX', input: '4XX' }, { expected: null, input: 'abc' }, { expected: null, input: '-100' }, - ])('parseResponseStatusCode($input) -> $expected', ({ input, expected }) => { + ])('parseResponseStatusCode($input) -> $expected', ({ expected, input }) => { expect(parseResponseStatusCode(input)).toBe(expected); }); }); diff --git a/packages/openapi-ts/src/openApi/common/parser/__tests__/sanitize.spec.ts b/packages/openapi-ts/src/openApi/common/parser/__tests__/sanitize.spec.ts index 5036a053a..cd389c078 100644 --- a/packages/openapi-ts/src/openApi/common/parser/__tests__/sanitize.spec.ts +++ b/packages/openapi-ts/src/openApi/common/parser/__tests__/sanitize.spec.ts @@ -15,7 +15,7 @@ describe('sanitizeOperationParameterName', () => { { expected: 'unknownArray', input: 'unknown[]' }, ])( 'sanitizeOperationParameterName($input) -> $expected', - ({ input, expected }) => { + ({ expected, input }) => { expect(sanitizeOperationParameterName(input)).toEqual(expected); }, ); @@ -30,7 +30,7 @@ describe('sanitizeNamespaceIdentifier', () => { { expected: 'a-b-c--d--e', input: 'a/b{c}/d/$e' }, ])( 'sanitizeNamespaceIdentifier($input) -> $expected', - ({ input, expected }) => { + ({ expected, input }) => { expect(sanitizeNamespaceIdentifier(input)).toEqual(expected); }, ); @@ -45,7 +45,7 @@ describe('ensureValidTypeScriptJavaScriptIdentifier', () => { { expected: '_400', input: '400' }, ])( 'ensureValidTypeScriptJavaScriptIdentifier($input) -> $expected', - ({ input, expected }) => { + ({ expected, input }) => { expect(ensureValidTypeScriptJavaScriptIdentifier(input)).toEqual( expected, ); diff --git a/packages/openapi-ts/src/openApi/common/parser/__tests__/service.spec.ts b/packages/openapi-ts/src/openApi/common/parser/__tests__/service.spec.ts index c6577298d..2037d7e7b 100644 --- a/packages/openapi-ts/src/openApi/common/parser/__tests__/service.spec.ts +++ b/packages/openapi-ts/src/openApi/common/parser/__tests__/service.spec.ts @@ -7,7 +7,7 @@ describe('getServiceVersion', () => { { expected: '1.0', input: '1.0' }, { expected: '1.2', input: 'v1.2' }, { expected: '2.4', input: 'V2.4' }, - ])('should get $expected when version is $input', ({ input, expected }) => { + ])('should get $expected when version is $input', ({ expected, input }) => { expect(getServiceVersion(input)).toEqual(expected); }); }); diff --git a/packages/openapi-ts/src/openApi/common/parser/__tests__/sort.spec.ts b/packages/openapi-ts/src/openApi/common/parser/__tests__/sort.spec.ts index 7cdac069d..0fe94d472 100644 --- a/packages/openapi-ts/src/openApi/common/parser/__tests__/sort.spec.ts +++ b/packages/openapi-ts/src/openApi/common/parser/__tests__/sort.spec.ts @@ -30,7 +30,7 @@ describe('sort', () => { }, ])( 'should sort $input by required to produce $expected', - ({ input, expected }) => { + ({ expected, input }) => { expect(toSortedByRequired(input)).toEqual(expected); }, ); diff --git a/packages/openapi-ts/src/openApi/common/parser/__tests__/stripNamespace.spec.ts b/packages/openapi-ts/src/openApi/common/parser/__tests__/stripNamespace.spec.ts index 80dbc8dc7..382b5e9e7 100644 --- a/packages/openapi-ts/src/openApi/common/parser/__tests__/stripNamespace.spec.ts +++ b/packages/openapi-ts/src/openApi/common/parser/__tests__/stripNamespace.spec.ts @@ -17,7 +17,7 @@ describe('stripNamespace', () => { { expected: 'Item', input: '#/components/securitySchemes/Item' }, { expected: 'Item', input: '#/components/links/Item' }, { expected: 'Item', input: '#/components/callbacks/Item' }, - ])('stripNamespace($input) -> $expected', ({ input, expected }) => { + ])('stripNamespace($input) -> $expected', ({ expected, input }) => { expect(stripNamespace(input)).toEqual(expected); }); }); diff --git a/packages/openapi-ts/src/openApi/common/parser/__tests__/type.spec.ts b/packages/openapi-ts/src/openApi/common/parser/__tests__/type.spec.ts index 88c174c69..3988a15d8 100644 --- a/packages/openapi-ts/src/openApi/common/parser/__tests__/type.spec.ts +++ b/packages/openapi-ts/src/openApi/common/parser/__tests__/type.spec.ts @@ -43,7 +43,7 @@ describe('getMappedType', () => { { expected: 'unknown[]', type: 'array' }, { expected: 'void', type: 'void' }, { expected: undefined, type: '' }, - ])('should map type $type to $expected', ({ type, expected }) => { + ])('should map type $type to $expected', ({ expected, type }) => { expect(getMappedType(type)).toEqual(expected); }); }); diff --git a/packages/openapi-ts/src/openApi/shared/utils/operation.ts b/packages/openapi-ts/src/openApi/shared/utils/operation.ts index b7eac71dd..7e65c1370 100644 --- a/packages/openapi-ts/src/openApi/shared/utils/operation.ts +++ b/packages/openapi-ts/src/openApi/shared/utils/operation.ts @@ -9,8 +9,8 @@ import { sanitizeNamespaceIdentifier } from '../../common/parser/sanitize'; */ export const operationToId = ({ context, - method, id, + method, path, }: { context: IRContext; diff --git a/packages/openapi-ts/src/openApi/v2/parser/operation.ts b/packages/openapi-ts/src/openApi/v2/parser/operation.ts index 6cdeca181..6897b2cd5 100644 --- a/packages/openapi-ts/src/openApi/v2/parser/operation.ts +++ b/packages/openapi-ts/src/openApi/v2/parser/operation.ts @@ -18,8 +18,8 @@ export const getOperation = ({ method, op, openApi, - types, pathParams, + types, url, }: { method: Lowercase; diff --git a/packages/openapi-ts/src/plugins/@hey-api/services/plugin-legacy.ts b/packages/openapi-ts/src/plugins/@hey-api/services/plugin-legacy.ts index b1694d726..3eea27a6e 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/services/plugin-legacy.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/services/plugin-legacy.ts @@ -487,9 +487,9 @@ const toRequestOptions = ( export const serviceFunctionIdentifier = ({ config, + handleIllegal, id, operation, - handleIllegal, }: { config: Config; handleIllegal?: boolean; diff --git a/packages/openapi-ts/src/plugins/@hey-api/services/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/services/plugin.ts index 9e0bbfda6..39d3a1070 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/services/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/services/plugin.ts @@ -194,7 +194,7 @@ const generateClassServices = ({ context }: { context: IRContext }) => { const services = new Map>(); - context.subscribe('operation', ({ operation, method, path }) => { + context.subscribe('operation', ({ method, operation, path }) => { const identifierData = context.file({ id: 'types' })!.identifier({ $ref: operationIrRef({ id: operation.id, type: 'data' }), namespace: 'type', @@ -314,7 +314,7 @@ const generateFlatServices = ({ context }: { context: IRContext }) => { const file = context.file({ id: servicesId })!; const typesModule = file.relativePathToFile({ context, id: 'types' }); - context.subscribe('operation', ({ operation, method, path }) => { + context.subscribe('operation', ({ method, operation, path }) => { const identifierData = context.file({ id: 'types' })!.identifier({ $ref: operationIrRef({ id: operation.id, type: 'data' }), namespace: 'type', diff --git a/packages/openapi-ts/src/plugins/@hey-api/types/plugin-legacy.ts b/packages/openapi-ts/src/plugins/@hey-api/types/plugin-legacy.ts index 8f774e522..bb6100e91 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/types/plugin-legacy.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/types/plugin-legacy.ts @@ -56,8 +56,8 @@ export const emptyModel: Model = { }; const generateEnum = ({ - leadingComment, comments, + leadingComment, meta, obj, onNode, diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin-legacy.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin-legacy.ts index 8d95ce0ac..7d030aa63 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin-legacy.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin-legacy.ts @@ -70,8 +70,8 @@ const toQueryOptionsName = ({ const toQueryKeyName = ({ config, id, - operation, isInfinite, + operation, }: { config: Config; id: string; @@ -645,8 +645,8 @@ const createTypeResponse = ({ }; const createQueryKeyLiteral = ({ - isInfinite, id, + isInfinite, }: { id: string; isInfinite?: boolean; diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts index a8ff1af8a..320acf45b 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts @@ -74,8 +74,8 @@ const queryOptionsFunctionIdentifier = ({ const queryKeyFunctionIdentifier = ({ context, - operation, isInfinite, + operation, }: { context: IRContext; isInfinite?: boolean; @@ -510,8 +510,8 @@ const createQueryKeyType = ({ file }: { file: Files[keyof Files] }) => { }; const createQueryKeyLiteral = ({ - isInfinite, id, + isInfinite, }: { id: string; isInfinite?: boolean; diff --git a/packages/openapi-ts/src/plugins/zod/config.ts b/packages/openapi-ts/src/plugins/zod/config.ts index ca522d579..efef354e7 100644 --- a/packages/openapi-ts/src/plugins/zod/config.ts +++ b/packages/openapi-ts/src/plugins/zod/config.ts @@ -1,11 +1,10 @@ import type { DefineConfig, PluginConfig } from '../types'; import { handler } from './plugin'; -import { handlerLegacy } from './plugin-legacy'; import type { Config } from './types'; export const defaultConfig: PluginConfig = { _handler: handler, - _handlerLegacy: handlerLegacy, + _handlerLegacy: () => {}, name: 'zod', output: 'zod', }; diff --git a/packages/openapi-ts/src/plugins/zod/plugin-legacy.ts b/packages/openapi-ts/src/plugins/zod/plugin-legacy.ts deleted file mode 100644 index 219edeb7a..000000000 --- a/packages/openapi-ts/src/plugins/zod/plugin-legacy.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { compiler } from '../../compiler'; -import type { TypeScriptFile } from '../../generate/files'; -import type { Model } from '../../types/client'; -import type { PluginLegacyHandler } from '../types'; -import type { Config } from './types'; - -const processArray = ({ - file, - model, -}: { - file: TypeScriptFile; - model: Model; -}) => { - const identifier = file.identifier({ - $ref: model.meta?.$ref || '', - create: true, - namespace: 'value', - }); - - if (!identifier.created) { - return; - } - - const zArrayExpression = compiler.callExpression({ - functionName: compiler.propertyAccessExpression({ - expression: 'z', - name: 'array', - }), - parameters: [ - compiler.callExpression({ - functionName: compiler.propertyAccessExpression({ - expression: 'z', - name: model.base, - }), - }), - ], - }); - - if (model.base === 'number') { - let expression = zArrayExpression; - - if (model.minItems && model.maxItems && model.minItems === model.maxItems) { - expression = compiler.callExpression({ - functionName: compiler.propertyAccessExpression({ - expression, - name: 'length', - }), - parameters: [compiler.ots.number(model.minItems)], - }); - } else { - if (model.minItems) { - expression = compiler.callExpression({ - functionName: compiler.propertyAccessExpression({ - expression, - name: 'min', - }), - parameters: [compiler.ots.number(model.minItems)], - }); - } - - if (model.maxItems) { - expression = compiler.callExpression({ - functionName: compiler.propertyAccessExpression({ - expression, - name: 'max', - }), - parameters: [compiler.ots.number(model.maxItems)], - }); - } - } - - const statement = compiler.constVariable({ - exportConst: true, - expression, - name: identifier.name || '', - }); - file.add(statement); - return; - } - - // console.warn('array!', model.base, model.name) - const statement = compiler.constVariable({ - exportConst: true, - expression: compiler.callExpression({ - functionName: compiler.propertyAccessExpression({ - expression: 'z', - name: 'object', - }), - parameters: [ - compiler.objectExpression({ - multiLine: true, - obj: [], - }), - ], - }), - name: identifier.name || '', - }); - file.add(statement); -}; - -export const handlerLegacy: PluginLegacyHandler = ({ - client, - files, - plugin, -}) => { - const file = files[plugin.name]; - - file.import({ - module: 'zod', - name: 'z', - }); - - for (const model of client.models) { - switch (model.export) { - case 'array': - return processArray({ - file, - model, - }); - } - } -}; diff --git a/packages/openapi-ts/src/plugins/zod/plugin.ts b/packages/openapi-ts/src/plugins/zod/plugin.ts index a908dfd78..4a6d204dc 100644 --- a/packages/openapi-ts/src/plugins/zod/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/plugin.ts @@ -18,7 +18,9 @@ const zodId = 'zod'; const digitsRegExp = /^\d+$/; // frequently used identifiers +const defaultIdentifier = compiler.identifier({ text: 'default' }); const optionalIdentifier = compiler.identifier({ text: 'optional' }); +const readonlyIdentifier = compiler.identifier({ text: 'readonly' }); const zIdentifier = compiler.identifier({ text: 'z' }); const arrayTypeToZodSchema = ({ @@ -29,13 +31,17 @@ const arrayTypeToZodSchema = ({ context: IRContext; namespace: Array; schema: SchemaWithType<'array'>; -}) => { +}): ts.CallExpression => { + const functionName = compiler.propertyAccessExpression({ + expression: zIdentifier, + name: compiler.identifier({ text: schema.type }), + }); + + let arrayExpression: ts.CallExpression | undefined; + if (!schema.items) { - const expression = compiler.callExpression({ - functionName: compiler.propertyAccessExpression({ - expression: zIdentifier, - name: compiler.identifier({ text: schema.type }), - }), + arrayExpression = compiler.callExpression({ + functionName, parameters: [ unknownTypeToZodSchema({ context, @@ -46,57 +52,80 @@ const arrayTypeToZodSchema = ({ }), ], }); - return expression; - } + } else { + schema = deduplicateSchema({ schema }); - schema = deduplicateSchema({ schema }); + // at least one item is guaranteed + const itemExpressions = schema.items!.map((item) => + schemaToZodSchema({ + context, + namespace, + schema: item, + }), + ); - // at least one item is guaranteed - const itemExpressions = schema.items!.map((item) => - schemaToZodSchema({ - context, - namespace, - schema: item, - }), - ); + if (itemExpressions.length === 1) { + arrayExpression = compiler.callExpression({ + functionName, + parameters: itemExpressions, + }); + } else { + if (schema.logicalOperator === 'and') { + // TODO: parser - handle intersection + // return compiler.typeArrayNode( + // compiler.typeIntersectionNode({ types: itemExpressions }), + // ); + } + + // TODO: parser - handle union + // return compiler.typeArrayNode(compiler.typeUnionNode({ types: itemExpressions })); + + arrayExpression = compiler.callExpression({ + functionName, + parameters: [ + unknownTypeToZodSchema({ + context, + namespace, + schema: { + type: 'unknown', + }, + }), + ], + }); + } + } - if (itemExpressions.length === 1) { - const expression = compiler.callExpression({ + if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { + arrayExpression = compiler.callExpression({ functionName: compiler.propertyAccessExpression({ - expression: zIdentifier, - name: compiler.identifier({ text: schema.type }), + expression: arrayExpression, + name: compiler.identifier({ text: 'length' }), }), - parameters: itemExpressions, + parameters: [compiler.valueToExpression({ value: schema.minItems })], }); - return expression; - } + } else { + if (schema.minItems !== undefined) { + arrayExpression = compiler.callExpression({ + functionName: compiler.propertyAccessExpression({ + expression: arrayExpression, + name: compiler.identifier({ text: 'min' }), + }), + parameters: [compiler.valueToExpression({ value: schema.minItems })], + }); + } - if (schema.logicalOperator === 'and') { - // TODO: parser - handle intersection - // return compiler.typeArrayNode( - // compiler.typeIntersectionNode({ types: itemExpressions }), - // ); + if (schema.maxItems !== undefined) { + arrayExpression = compiler.callExpression({ + functionName: compiler.propertyAccessExpression({ + expression: arrayExpression, + name: compiler.identifier({ text: 'max' }), + }), + parameters: [compiler.valueToExpression({ value: schema.maxItems })], + }); + } } - // TODO: parser - handle union - // return compiler.typeArrayNode(compiler.typeUnionNode({ types: itemExpressions })); - - const expression = compiler.callExpression({ - functionName: compiler.propertyAccessExpression({ - expression: zIdentifier, - name: compiler.identifier({ text: schema.type }), - }), - parameters: [ - unknownTypeToZodSchema({ - context, - namespace, - schema: { - type: 'unknown', - }, - }), - ], - }); - return expression; + return arrayExpression; }; const booleanTypeToZodSchema = ({ @@ -130,7 +159,7 @@ const enumTypeToZodSchema = ({ context: IRContext; namespace: Array; schema: SchemaWithType<'enum'>; -}): ts.Expression => { +}): ts.CallExpression => { const enumMembers: Array = []; for (const item of schema.items ?? []) { @@ -209,7 +238,12 @@ const numberTypeToZodSchema = ({ namespace: Array; schema: SchemaWithType<'number'>; }) => { - // TODO: parser - handle min/max length + let numberExpression = compiler.callExpression({ + functionName: compiler.propertyAccessExpression({ + expression: zIdentifier, + name: compiler.identifier({ text: schema.type }), + }), + }); if (schema.const !== undefined) { // TODO: parser - add constant @@ -218,13 +252,47 @@ const numberTypeToZodSchema = ({ // }); } - const expression = compiler.callExpression({ - functionName: compiler.propertyAccessExpression({ - expression: zIdentifier, - name: compiler.identifier({ text: schema.type }), - }), - }); - return expression; + if (schema.exclusiveMinimum !== undefined) { + numberExpression = compiler.callExpression({ + functionName: compiler.propertyAccessExpression({ + expression: numberExpression, + name: compiler.identifier({ text: 'gt' }), + }), + parameters: [ + compiler.valueToExpression({ value: schema.exclusiveMinimum }), + ], + }); + } else if (schema.minimum !== undefined) { + numberExpression = compiler.callExpression({ + functionName: compiler.propertyAccessExpression({ + expression: numberExpression, + name: compiler.identifier({ text: 'gte' }), + }), + parameters: [compiler.valueToExpression({ value: schema.minimum })], + }); + } + + if (schema.exclusiveMaximum !== undefined) { + numberExpression = compiler.callExpression({ + functionName: compiler.propertyAccessExpression({ + expression: numberExpression, + name: compiler.identifier({ text: 'lt' }), + }), + parameters: [ + compiler.valueToExpression({ value: schema.exclusiveMaximum }), + ], + }); + } else if (schema.maximum !== undefined) { + numberExpression = compiler.callExpression({ + functionName: compiler.propertyAccessExpression({ + expression: numberExpression, + name: compiler.identifier({ text: 'lte' }), + }), + parameters: [compiler.valueToExpression({ value: schema.maximum })], + }); + } + + return numberExpression; }; const objectTypeToZodSchema = ({ @@ -258,7 +326,7 @@ const objectTypeToZodSchema = ({ propertyExpression = compiler.callExpression({ functionName: compiler.propertyAccessExpression({ expression: propertyExpression, - name: compiler.identifier({ text: 'readonly' }), + name: readonlyIdentifier, }), }); } @@ -272,6 +340,21 @@ const objectTypeToZodSchema = ({ }); } + if (property.default !== undefined) { + const callParameter = compiler.valueToExpression({ + value: property.default, + }); + if (callParameter) { + propertyExpression = compiler.callExpression({ + functionName: compiler.propertyAccessExpression({ + expression: propertyExpression, + name: defaultIdentifier, + }), + parameters: [callParameter], + }); + } + } + digitsRegExp.lastIndex = 0; let propertyName = digitsRegExp.test(name) ? ts.factory.createNumericLiteral(name) @@ -407,6 +490,36 @@ const stringTypeToZodSchema = ({ } } + if (schema.minLength === schema.maxLength && schema.minLength !== undefined) { + stringExpression = compiler.callExpression({ + functionName: compiler.propertyAccessExpression({ + expression: stringExpression, + name: compiler.identifier({ text: 'length' }), + }), + parameters: [compiler.valueToExpression({ value: schema.minLength })], + }); + } else { + if (schema.minLength !== undefined) { + stringExpression = compiler.callExpression({ + functionName: compiler.propertyAccessExpression({ + expression: stringExpression, + name: compiler.identifier({ text: 'min' }), + }), + parameters: [compiler.valueToExpression({ value: schema.minLength })], + }); + } + + if (schema.maxLength !== undefined) { + stringExpression = compiler.callExpression({ + functionName: compiler.propertyAccessExpression({ + expression: stringExpression, + name: compiler.identifier({ text: 'max' }), + }), + parameters: [compiler.valueToExpression({ value: schema.maxLength })], + }); + } + } + return stringExpression; }; @@ -579,7 +692,7 @@ const schemaToZodSchema = ({ namespace: 'value', }); if (identifier.name) { - expression = compiler.identifier({ text: identifier.name || '' }); + expression = compiler.identifier({ text: `z${identifier.name || ''}` }); } else { const ref = context.resolveIrRef(schema.$ref); expression = schemaToZodSchema({ @@ -646,7 +759,7 @@ const schemaToZodSchema = ({ const statement = compiler.constVariable({ exportConst: true, expression, - name: identifier.name || '', + name: `z${identifier.name || ''}`, }); file.add(statement); } @@ -665,6 +778,14 @@ export const handler: PluginHandler = ({ context, plugin }) => { name: 'z', }); + // context.subscribe('operation', ({ operation }) => { + // schemaToZodSchema({ + // $ref, + // context, + // schema, + // }); + // }); + context.subscribe('schema', ({ $ref, schema }) => { schemaToZodSchema({ $ref, diff --git a/packages/openapi-ts/src/plugins/zod/types.d.ts b/packages/openapi-ts/src/plugins/zod/types.d.ts index 165ed738b..da329d2c8 100644 --- a/packages/openapi-ts/src/plugins/zod/types.d.ts +++ b/packages/openapi-ts/src/plugins/zod/types.d.ts @@ -1,6 +1,12 @@ +// import type { IROperationObject, IRSchemaObject } from '../../ir/ir'; import type { PluginName } from '../types'; export interface Config extends PluginName<'zod'> { + /** + * Customise the Zod schema name. By default, `z{{name}}` is used, + * where `name` is a definition name or an operation name. + */ + // nameBuilder?: (model: IROperationObject | IRSchemaObject) => string; /** * Name of the generated file. * @default 'zod' diff --git a/packages/openapi-ts/src/utils/__tests__/escape.spec.ts b/packages/openapi-ts/src/utils/__tests__/escape.spec.ts index 4c3c57550..c4cf9584a 100644 --- a/packages/openapi-ts/src/utils/__tests__/escape.spec.ts +++ b/packages/openapi-ts/src/utils/__tests__/escape.spec.ts @@ -23,7 +23,7 @@ const toCheck: { escaped: string; unescaped: string }[] = [ describe('escapeName', () => { it.each(toCheck)( 'should escape $unescaped to $escaped', - ({ unescaped, escaped }) => { + ({ escaped, unescaped }) => { expect(escapeName(unescaped)).toBe(escaped); }, ); @@ -32,7 +32,7 @@ describe('escapeName', () => { describe('unescapeName', () => { it.each(toCheck)( 'should unescape $escaped to $unescaped', - ({ unescaped, escaped }) => { + ({ escaped, unescaped }) => { expect(unescapeName(escaped)).toBe(unescaped); }, ); diff --git a/packages/openapi-ts/src/utils/__tests__/parse.spec.ts b/packages/openapi-ts/src/utils/__tests__/parse.spec.ts index 53ef89f1c..6c1b5339f 100644 --- a/packages/openapi-ts/src/utils/__tests__/parse.spec.ts +++ b/packages/openapi-ts/src/utils/__tests__/parse.spec.ts @@ -289,7 +289,7 @@ describe('operationNameFn', () => { }, ])( 'getOperationName($url, $method, { operationId: $useOperationId }, $operationId) -> $expected', - ({ url, method, options, operationId, expected }) => { + ({ expected, method, operationId, options, url }) => { setConfig(options); expect( operationNameFn({ diff --git a/packages/openapi-ts/src/utils/__tests__/postprocess.spec.ts b/packages/openapi-ts/src/utils/__tests__/postprocess.spec.ts index 013df8bd9..86af33c09 100644 --- a/packages/openapi-ts/src/utils/__tests__/postprocess.spec.ts +++ b/packages/openapi-ts/src/utils/__tests__/postprocess.spec.ts @@ -27,7 +27,7 @@ describe('getServiceName', () => { expected: 'NonAsciiÆøåÆøÅöôêÊ字符串', input: 'non-ascii-æøåÆØÅöôêÊ字符串', }, - ])('getServiceName($input) -> $expected', ({ input, expected }) => { + ])('getServiceName($input) -> $expected', ({ expected, input }) => { expect(getServiceName(input)).toEqual(expected); }); }); diff --git a/packages/openapi-ts/src/utils/__tests__/type.spec.ts b/packages/openapi-ts/src/utils/__tests__/type.spec.ts index a5dd90976..5999ff15e 100644 --- a/packages/openapi-ts/src/utils/__tests__/type.spec.ts +++ b/packages/openapi-ts/src/utils/__tests__/type.spec.ts @@ -28,7 +28,7 @@ describe('transformTypeKeyName', () => { { expected: 'fooBar', input: 'FOO-BAR' }, { expected: 'fooBar', input: 'foo[bar]' }, { expected: 'fooBarArray', input: 'foo.bar[]' }, - ])('$input -> $expected', ({ input, expected }) => { + ])('$input -> $expected', ({ expected, input }) => { (isLegacyClient as MockedFunction).mockImplementationOnce( () => true, ); diff --git a/packages/openapi-ts/src/utils/__tests__/unique.spec.ts b/packages/openapi-ts/src/utils/__tests__/unique.spec.ts index 7ed3cd6a1..ef4260578 100644 --- a/packages/openapi-ts/src/utils/__tests__/unique.spec.ts +++ b/packages/openapi-ts/src/utils/__tests__/unique.spec.ts @@ -11,7 +11,7 @@ describe('unique', () => { { arr: ['y', 'z', 'a'], index: 2, result: true, value: 'a' }, ])( 'unique($value, $index, $arr) -> $result', - ({ value, index, arr, result }) => { + ({ arr, index, result, value }) => { expect(unique(value, index, arr)).toEqual(result); }, ); @@ -21,7 +21,7 @@ describe('unique', () => { { expected: [1, 2, 3, 4, 5, 6], input: [1, 2, 3, 4, 4, 5, 6, 3] }, ])( 'should filter: $input to the unique array: $expected', - ({ input, expected }) => { + ({ expected, input }) => { expect(input.filter(unique)).toEqual(expected); }, ); diff --git a/packages/openapi-ts/test/__snapshots__/3.0.x/plugins/zod/default/zod.gen.ts b/packages/openapi-ts/test/__snapshots__/3.0.x/plugins/zod/default/zod.gen.ts index 14c060977..a49f34663 100644 --- a/packages/openapi-ts/test/__snapshots__/3.0.x/plugins/zod/default/zod.gen.ts +++ b/packages/openapi-ts/test/__snapshots__/3.0.x/plugins/zod/default/zod.gen.ts @@ -2,41 +2,41 @@ import { z } from 'zod'; -export const _400 = z.string(); +export const z_400 = z.string(); -export const camelCaseCommentWithBreaks = z.number(); +export const zcamelCaseCommentWithBreaks = z.number(); -export const CommentWithBreaks = z.number(); +export const zCommentWithBreaks = z.number(); -export const CommentWithBackticks = z.number(); +export const zCommentWithBackticks = z.number(); -export const CommentWithBackticksAndQuotes = z.number(); +export const zCommentWithBackticksAndQuotes = z.number(); -export const CommentWithSlashes = z.number(); +export const zCommentWithSlashes = z.number(); -export const CommentWithExpressionPlaceholders = z.number(); +export const zCommentWithExpressionPlaceholders = z.number(); -export const CommentWithQuotes = z.number(); +export const zCommentWithQuotes = z.number(); -export const CommentWithReservedCharacters = z.number(); +export const zCommentWithReservedCharacters = z.number(); -export const SimpleInteger = z.number(); +export const zSimpleInteger = z.number(); -export const SimpleBoolean = z.boolean(); +export const zSimpleBoolean = z.boolean(); -export const SimpleString = z.string(); +export const zSimpleString = z.string(); -export const NonAsciiStringæøåÆØÅöôêÊ字符串 = z.string(); +export const zNonAsciiStringæøåÆØÅöôêÊ字符串 = z.string(); -export const SimpleFile = z.string(); +export const zSimpleFile = z.string(); -export const SimpleReference = z.object({ +export const zSimpleReference = z.object({ prop: z.string().optional() }); -export const SimpleStringWithPattern = z.unknown(); +export const zSimpleStringWithPattern = z.unknown(); -export const EnumWithStrings = z.enum([ +export const zEnumWithStrings = z.enum([ 'Success', 'Warning', 'Error', @@ -45,84 +45,84 @@ export const EnumWithStrings = z.enum([ 'Non-ascii: øæåôöØÆÅÔÖ字符串' ]); -export const EnumWithReplacedCharacters = z.enum([ +export const zEnumWithReplacedCharacters = z.enum([ "'Single Quote'", '"Double Quotes"', 'øæåôöØÆÅÔÖ字符串', '' ]); -export const EnumWithNumbers = z.unknown(); +export const zEnumWithNumbers = z.unknown(); -export const EnumFromDescription = z.number(); +export const zEnumFromDescription = z.number(); -export const EnumWithExtensions = z.unknown(); +export const zEnumWithExtensions = z.unknown(); -export const EnumWithXEnumNames = z.unknown(); +export const zEnumWithXEnumNames = z.unknown(); -export const ArrayWithNumbers = z.array(z.number()); +export const zArrayWithNumbers = z.array(z.number()); -export const ArrayWithBooleans = z.array(z.boolean()); +export const zArrayWithBooleans = z.array(z.boolean()); -export const ArrayWithStrings = z.array(z.string()); +export const zArrayWithStrings = z.array(z.string()); -export const ArrayWithReferences = z.array(z.object({ +export const zArrayWithReferences = z.array(z.object({ prop: z.string().optional() })); -export const ArrayWithArray = z.array(z.array(z.object({ +export const zArrayWithArray = z.array(z.array(z.object({ prop: z.string().optional() }))); -export const ArrayWithProperties = z.array(z.object({ - '16x16': camelCaseCommentWithBreaks.optional(), +export const zArrayWithProperties = z.array(z.object({ + '16x16': zcamelCaseCommentWithBreaks.optional(), bar: z.string().optional() })); -export const ArrayWithAnyOfProperties = z.array(z.unknown()); +export const zArrayWithAnyOfProperties = z.array(z.unknown()); -export const AnyOfAnyAndNull = z.object({ +export const zAnyOfAnyAndNull = z.object({ data: z.unknown().optional() }); -export const AnyOfArrays = z.object({ +export const zAnyOfArrays = z.object({ results: z.array(z.unknown()).optional() }); -export const DictionaryWithString = z.object({}); +export const zDictionaryWithString = z.object({}); -export const DictionaryWithPropertiesAndAdditionalProperties = z.object({ +export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ foo: z.number().optional(), bar: z.boolean().optional() }); -export const DictionaryWithReference = z.object({}); +export const zDictionaryWithReference = z.object({}); -export const DictionaryWithArray = z.object({}); +export const zDictionaryWithArray = z.object({}); -export const DictionaryWithDictionary = z.object({}); +export const zDictionaryWithDictionary = z.object({}); -export const DictionaryWithProperties = z.object({}); +export const zDictionaryWithProperties = z.object({}); -export const ModelWithInteger = z.object({ +export const zModelWithInteger = z.object({ prop: z.number().optional() }); -export const ModelWithBoolean = z.object({ +export const zModelWithBoolean = z.object({ prop: z.boolean().optional() }); -export const ModelWithString = z.object({ +export const zModelWithString = z.object({ prop: z.string().optional() }); -export const ModelWithStringError = z.object({ +export const zModelWithStringError = z.object({ prop: z.string().optional() }); -export const Model_From_Zendesk = z.string(); +export const zModel_From_Zendesk = z.string(); -export const ModelWithNullableString = z.object({ +export const zModelWithNullableString = z.object({ nullableProp1: z.unknown().optional(), nullableRequiredProp1: z.unknown(), nullableProp2: z.unknown().optional(), @@ -135,7 +135,7 @@ export const ModelWithNullableString = z.object({ ]).optional() }); -export const ModelWithEnum = z.object({ +export const zModelWithEnum = z.object({ 'foo_bar-enum': z.enum([ 'Success', 'Warning', @@ -153,17 +153,17 @@ export const ModelWithEnum = z.object({ bool: z.unknown().optional() }); -export const ModelWithEnumWithHyphen = z.object({ +export const zModelWithEnumWithHyphen = z.object({ 'foo-bar-baz-qux': z.enum([ '3.0' ]).optional() }); -export const ModelWithEnumFromDescription = z.object({ +export const zModelWithEnumFromDescription = z.object({ test: z.number().optional() }); -export const ModelWithNestedEnums = z.object({ +export const zModelWithNestedEnums = z.object({ dictionaryWithEnum: z.object({}).optional(), dictionaryWithEnumFromDescription: z.object({}).optional(), arrayWithEnum: z.array(z.enum([ @@ -180,7 +180,7 @@ export const ModelWithNestedEnums = z.object({ ]).optional() }); -export const ModelWithReference = z.object({ +export const zModelWithReference = z.object({ prop: z.object({ required: z.string(), requiredAndReadOnly: z.string().readonly(), @@ -188,7 +188,7 @@ export const ModelWithReference = z.object({ string: z.string().optional(), number: z.number().optional(), boolean: z.boolean().optional(), - reference: ModelWithString.optional(), + reference: zModelWithString.optional(), 'property with space': z.string().optional(), default: z.string().optional(), try: z.string().optional(), @@ -197,7 +197,7 @@ export const ModelWithReference = z.object({ }).optional() }); -export const ModelWithArrayReadOnlyAndWriteOnly = z.object({ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ prop: z.array(z.object({ foo: z.string(), bar: z.string().readonly(), @@ -207,104 +207,104 @@ export const ModelWithArrayReadOnlyAndWriteOnly = z.object({ propWithNumber: z.array(z.number()).optional() }); -export const ModelWithArray = z.object({ - prop: z.array(ModelWithString).optional(), +export const zModelWithArray = z.object({ + prop: z.array(zModelWithString).optional(), propWithFile: z.array(z.string()).optional(), propWithNumber: z.array(z.number()).optional() }); -export const ModelWithDictionary = z.object({ +export const zModelWithDictionary = z.object({ prop: z.object({}).optional() }); -export const DeprecatedModel = z.object({ +export const zDeprecatedModel = z.object({ prop: z.string().optional() }); -export const CompositionWithOneOf = z.object({ +export const zCompositionWithOneOf = z.object({ propA: z.unknown().optional() }); -export const CompositionWithOneOfAnonymous = z.object({ +export const zCompositionWithOneOfAnonymous = z.object({ propA: z.unknown().optional() }); -export const ModelCircle = z.object({ +export const zModelCircle = z.object({ kind: z.string(), radius: z.number().optional() }); -export const ModelSquare = z.object({ +export const zModelSquare = z.object({ kind: z.string(), sideLength: z.number().optional() }); -export const CompositionWithOneOfDiscriminator = z.unknown(); +export const zCompositionWithOneOfDiscriminator = z.unknown(); -export const CompositionWithAnyOf = z.object({ +export const zCompositionWithAnyOf = z.object({ propA: z.unknown().optional() }); -export const CompositionWithAnyOfAnonymous = z.object({ +export const zCompositionWithAnyOfAnonymous = z.object({ propA: z.unknown().optional() }); -export const CompositionWithNestedAnyAndTypeNull = z.object({ +export const zCompositionWithNestedAnyAndTypeNull = z.object({ propA: z.unknown().optional() }); -export const _3e_num_1Период = z.enum([ +export const z_3e_num_1Период = z.enum([ 'Bird', 'Dog' ]); -export const ConstValue = z.enum([ +export const zConstValue = z.enum([ 'ConstValue' ]); -export const CompositionWithNestedAnyOfAndNull = z.object({ +export const zCompositionWithNestedAnyOfAndNull = z.object({ propA: z.unknown().optional() }); -export const CompositionWithOneOfAndNullable = z.object({ +export const zCompositionWithOneOfAndNullable = z.object({ propA: z.unknown().optional() }); -export const CompositionWithOneOfAndSimpleDictionary = z.object({ +export const zCompositionWithOneOfAndSimpleDictionary = z.object({ propA: z.unknown().optional() }); -export const CompositionWithOneOfAndSimpleArrayDictionary = z.object({ +export const zCompositionWithOneOfAndSimpleArrayDictionary = z.object({ propA: z.unknown().optional() }); -export const CompositionWithOneOfAndComplexArrayDictionary = z.object({ +export const zCompositionWithOneOfAndComplexArrayDictionary = z.object({ propA: z.unknown().optional() }); -export const CompositionWithAllOfAndNullable = z.object({ +export const zCompositionWithAllOfAndNullable = z.object({ propA: z.unknown().optional() }); -export const CompositionWithAnyOfAndNullable = z.object({ +export const zCompositionWithAnyOfAndNullable = z.object({ propA: z.unknown().optional() }); -export const CompositionBaseModel = z.object({ +export const zCompositionBaseModel = z.object({ firstName: z.string().optional(), lastname: z.string().optional() }); -export const CompositionExtendedModel = z.unknown(); +export const zCompositionExtendedModel = z.unknown(); -export const ModelWithProperties = z.object({ +export const zModelWithProperties = z.object({ required: z.string(), requiredAndReadOnly: z.string().readonly(), requiredAndNullable: z.unknown(), string: z.string().optional(), number: z.number().optional(), boolean: z.boolean().optional(), - reference: ModelWithString.optional(), + reference: zModelWithString.optional(), 'property with space': z.string().optional(), default: z.string().optional(), try: z.string().optional(), @@ -312,33 +312,33 @@ export const ModelWithProperties = z.object({ '@namespace.integer': z.number().readonly().optional() }); -export const ModelWithNestedProperties = z.object({ +export const zModelWithNestedProperties = z.object({ first: z.unknown().readonly() }); -export const ModelWithDuplicateProperties = z.object({ - prop: ModelWithString.optional() +export const zModelWithDuplicateProperties = z.object({ + prop: zModelWithString.optional() }); -export const ModelWithOrderedProperties = z.object({ +export const zModelWithOrderedProperties = z.object({ zebra: z.string().optional(), apple: z.string().optional(), hawaii: z.string().optional() }); -export const ModelWithDuplicateImports = z.object({ - propA: ModelWithString.optional(), - propB: ModelWithString.optional(), - propC: ModelWithString.optional() +export const zModelWithDuplicateImports = z.object({ + propA: zModelWithString.optional(), + propB: zModelWithString.optional(), + propC: zModelWithString.optional() }); -export const ModelThatExtends = z.unknown(); +export const zModelThatExtends = z.unknown(); -export const ModelThatExtendsExtends = z.unknown(); +export const zModelThatExtendsExtends = z.unknown(); -export const ModelWithPattern = z.object({ - key: z.string(), - name: z.string(), +export const zModelWithPattern = z.object({ + key: z.string().max(64), + name: z.string().max(255), enabled: z.boolean().readonly().optional(), modified: z.string().datetime().readonly().optional(), id: z.string().optional(), @@ -348,31 +348,31 @@ export const ModelWithPattern = z.object({ patternWithBacktick: z.string().optional() }); -export const File = z.object({ - id: z.string().readonly().optional(), +export const zFile = z.object({ + id: z.string().min(1).readonly().optional(), updated_at: z.string().datetime().readonly().optional(), created_at: z.string().datetime().readonly().optional(), - mime: z.string(), + mime: z.string().min(1).max(24), file: z.string().url().readonly().optional() }); -export const _default = z.object({ +export const z_default = z.object({ name: z.string().optional() }); -export const Pageable = z.object({ - page: z.number().optional(), - size: z.number().optional(), +export const zPageable = z.object({ + page: z.number().gte(0).optional().default(0), + size: z.number().gte(1).optional(), sort: z.array(z.string()).optional() }); -export const FreeFormObjectWithoutAdditionalProperties = z.object({}); +export const zFreeFormObjectWithoutAdditionalProperties = z.object({}); -export const FreeFormObjectWithAdditionalPropertiesEqTrue = z.object({}); +export const zFreeFormObjectWithAdditionalPropertiesEqTrue = z.object({}); -export const FreeFormObjectWithAdditionalPropertiesEqEmptyObject = z.object({}); +export const zFreeFormObjectWithAdditionalPropertiesEqEmptyObject = z.object({}); -export const ModelWithConst = z.object({ +export const zModelWithConst = z.object({ String: z.enum([ 'String' ]).optional(), @@ -383,91 +383,91 @@ export const ModelWithConst = z.object({ ]).optional() }); -export const ModelWithAdditionalPropertiesEqTrue = z.object({ +export const zModelWithAdditionalPropertiesEqTrue = z.object({ prop: z.string().optional() }); -export const NestedAnyOfArraysNullable = z.object({ +export const zNestedAnyOfArraysNullable = z.object({ nullableArray: z.unknown().optional() }); -export const CompositionWithOneOfAndProperties = z.unknown(); +export const zCompositionWithOneOfAndProperties = z.unknown(); -export const NullableObject = z.unknown(); +export const zNullableObject = z.unknown(); -export const CharactersInDescription = z.string(); +export const zCharactersInDescription = z.string(); -export const ModelWithNullableObject = z.object({ - data: NullableObject.optional() +export const zModelWithNullableObject = z.object({ + data: zNullableObject.optional() }); -export const ModelWithOneOfEnum = z.unknown(); +export const zModelWithOneOfEnum = z.unknown(); -export const ModelWithNestedArrayEnumsDataFoo = z.enum([ +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ 'foo', 'bar' ]); -export const ModelWithNestedArrayEnumsDataBar = z.enum([ +export const zModelWithNestedArrayEnumsDataBar = z.enum([ 'baz', 'qux' ]); -export const ModelWithNestedArrayEnumsData = z.object({ - foo: z.array(ModelWithNestedArrayEnumsDataFoo).optional(), - bar: z.array(ModelWithNestedArrayEnumsDataBar).optional() +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), + bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() }); -export const ModelWithNestedArrayEnums = z.object({ +export const zModelWithNestedArrayEnums = z.object({ array_strings: z.array(z.string()).optional(), - data: ModelWithNestedArrayEnumsData.optional() + data: zModelWithNestedArrayEnumsData.optional() }); -export const ModelWithNestedCompositionEnums = z.object({ - foo: ModelWithNestedArrayEnumsDataFoo.optional() +export const zModelWithNestedCompositionEnums = z.object({ + foo: zModelWithNestedArrayEnumsDataFoo.optional() }); -export const ModelWithReadOnlyAndWriteOnly = z.object({ +export const zModelWithReadOnlyAndWriteOnly = z.object({ foo: z.string(), bar: z.string().readonly(), baz: z.string() }); -export const ModelWithConstantSizeArray = z.unknown(); +export const zModelWithConstantSizeArray = z.unknown(); -export const ModelWithAnyOfConstantSizeArray = z.unknown(); +export const zModelWithAnyOfConstantSizeArray = z.unknown(); -export const ModelWithPrefixItemsConstantSizeArray = z.array(z.unknown()); +export const zModelWithPrefixItemsConstantSizeArray = z.array(z.unknown()); -export const ModelWithAnyOfConstantSizeArrayNullable = z.unknown(); +export const zModelWithAnyOfConstantSizeArrayNullable = z.unknown(); -export const ModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.unknown(); +export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.unknown(); -export const ModelWithAnyOfConstantSizeArrayAndIntersect = z.unknown(); +export const zModelWithAnyOfConstantSizeArrayAndIntersect = z.unknown(); -export const ModelWithNumericEnumUnion = z.object({ +export const zModelWithNumericEnumUnion = z.object({ value: z.unknown().optional() }); -export const ModelWithBackticksInDescription = z.object({ +export const zModelWithBackticksInDescription = z.object({ template: z.string().optional() }); -export const ModelWithOneOfAndProperties = z.unknown(); +export const zModelWithOneOfAndProperties = z.unknown(); -export const ParameterSimpleParameterUnused = z.string(); +export const zParameterSimpleParameterUnused = z.string(); -export const PostServiceWithEmptyTagResponse = z.string(); +export const zPostServiceWithEmptyTagResponse = z.string(); -export const PostServiceWithEmptyTagResponse2 = z.string(); +export const zPostServiceWithEmptyTagResponse2 = z.string(); -export const DeleteFooData = z.string(); +export const zDeleteFooData = z.string(); -export const DeleteFooData2 = z.string(); +export const zDeleteFooData2 = z.string(); -export const _import = z.string(); +export const z_import = z.string(); -export const SchemaWithFormRestrictedKeys = z.object({ +export const zSchemaWithFormRestrictedKeys = z.object({ description: z.string().optional(), 'x-enum-descriptions': z.string().optional(), 'x-enum-varnames': z.string().optional(), @@ -489,38 +489,38 @@ export const SchemaWithFormRestrictedKeys = z.object({ })).optional() }); -export const io_k8s_apimachinery_pkg_apis_meta_v1_DeleteOptions = z.object({ +export const zio_k8s_apimachinery_pkg_apis_meta_v1_DeleteOptions = z.object({ preconditions: z.object({ resourceVersion: z.string().optional(), uid: z.string().optional() }).optional() }); -export const io_k8s_apimachinery_pkg_apis_meta_v1_Preconditions = z.object({ +export const zio_k8s_apimachinery_pkg_apis_meta_v1_Preconditions = z.object({ resourceVersion: z.string().optional(), uid: z.string().optional() }); -export const AdditionalPropertiesUnknownIssue = z.object({}); +export const zAdditionalPropertiesUnknownIssue = z.object({}); -export const AdditionalPropertiesUnknownIssue2 = z.object({}); +export const zAdditionalPropertiesUnknownIssue2 = z.object({}); -export const AdditionalPropertiesUnknownIssue3 = z.unknown(); +export const zAdditionalPropertiesUnknownIssue3 = z.unknown(); -export const AdditionalPropertiesIntegerIssue = z.object({ +export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.number() }); -export const OneOfAllOfIssue = z.unknown(); +export const zOneOfAllOfIssue = z.unknown(); -export const Generic_Schema_Duplicate_Issue_1_System_Boolean_ = z.object({ +export const zGeneric_Schema_Duplicate_Issue_1_System_Boolean_ = z.object({ item: z.boolean().optional(), error: z.unknown().optional(), hasError: z.boolean().readonly().optional(), data: z.object({}).optional() }); -export const Generic_Schema_Duplicate_Issue_1_System_String_ = z.object({ +export const zGeneric_Schema_Duplicate_Issue_1_System_String_ = z.object({ item: z.unknown().optional(), error: z.unknown().optional(), hasError: z.boolean().readonly().optional() diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/plugins/zod/default/zod.gen.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/plugins/zod/default/zod.gen.ts index 8ce48d09d..845b63ae4 100644 --- a/packages/openapi-ts/test/__snapshots__/3.1.x/plugins/zod/default/zod.gen.ts +++ b/packages/openapi-ts/test/__snapshots__/3.1.x/plugins/zod/default/zod.gen.ts @@ -2,41 +2,41 @@ import { z } from 'zod'; -export const _400 = z.string(); +export const z_400 = z.string(); -export const camelCaseCommentWithBreaks = z.number(); +export const zcamelCaseCommentWithBreaks = z.number(); -export const CommentWithBreaks = z.number(); +export const zCommentWithBreaks = z.number(); -export const CommentWithBackticks = z.number(); +export const zCommentWithBackticks = z.number(); -export const CommentWithBackticksAndQuotes = z.number(); +export const zCommentWithBackticksAndQuotes = z.number(); -export const CommentWithSlashes = z.number(); +export const zCommentWithSlashes = z.number(); -export const CommentWithExpressionPlaceholders = z.number(); +export const zCommentWithExpressionPlaceholders = z.number(); -export const CommentWithQuotes = z.number(); +export const zCommentWithQuotes = z.number(); -export const CommentWithReservedCharacters = z.number(); +export const zCommentWithReservedCharacters = z.number(); -export const SimpleInteger = z.number(); +export const zSimpleInteger = z.number(); -export const SimpleBoolean = z.boolean(); +export const zSimpleBoolean = z.boolean(); -export const SimpleString = z.string(); +export const zSimpleString = z.string(); -export const NonAsciiStringæøåÆØÅöôêÊ字符串 = z.string(); +export const zNonAsciiStringæøåÆØÅöôêÊ字符串 = z.string(); -export const SimpleFile = z.string(); +export const zSimpleFile = z.string(); -export const SimpleReference = z.object({ +export const zSimpleReference = z.object({ prop: z.string().optional() }); -export const SimpleStringWithPattern = z.unknown(); +export const zSimpleStringWithPattern = z.unknown(); -export const EnumWithStrings = z.enum([ +export const zEnumWithStrings = z.enum([ 'Success', 'Warning', 'Error', @@ -45,84 +45,84 @@ export const EnumWithStrings = z.enum([ 'Non-ascii: øæåôöØÆÅÔÖ字符串' ]); -export const EnumWithReplacedCharacters = z.enum([ +export const zEnumWithReplacedCharacters = z.enum([ "'Single Quote'", '"Double Quotes"', 'øæåôöØÆÅÔÖ字符串', '' ]); -export const EnumWithNumbers = z.unknown(); +export const zEnumWithNumbers = z.unknown(); -export const EnumFromDescription = z.number(); +export const zEnumFromDescription = z.number(); -export const EnumWithExtensions = z.unknown(); +export const zEnumWithExtensions = z.unknown(); -export const EnumWithXEnumNames = z.unknown(); +export const zEnumWithXEnumNames = z.unknown(); -export const ArrayWithNumbers = z.array(z.number()); +export const zArrayWithNumbers = z.array(z.number()); -export const ArrayWithBooleans = z.array(z.boolean()); +export const zArrayWithBooleans = z.array(z.boolean()); -export const ArrayWithStrings = z.array(z.string()); +export const zArrayWithStrings = z.array(z.string()); -export const ArrayWithReferences = z.array(z.object({ +export const zArrayWithReferences = z.array(z.object({ prop: z.string().optional() })); -export const ArrayWithArray = z.array(z.array(z.object({ +export const zArrayWithArray = z.array(z.array(z.object({ prop: z.string().optional() }))); -export const ArrayWithProperties = z.array(z.object({ - '16x16': camelCaseCommentWithBreaks.optional(), +export const zArrayWithProperties = z.array(z.object({ + '16x16': zcamelCaseCommentWithBreaks.optional(), bar: z.string().optional() })); -export const ArrayWithAnyOfProperties = z.array(z.unknown()); +export const zArrayWithAnyOfProperties = z.array(z.unknown()); -export const AnyOfAnyAndNull = z.object({ +export const zAnyOfAnyAndNull = z.object({ data: z.unknown().optional() }); -export const AnyOfArrays = z.object({ +export const zAnyOfArrays = z.object({ results: z.array(z.unknown()).optional() }); -export const DictionaryWithString = z.object({}); +export const zDictionaryWithString = z.object({}); -export const DictionaryWithPropertiesAndAdditionalProperties = z.object({ +export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ foo: z.number().optional(), bar: z.boolean().optional() }); -export const DictionaryWithReference = z.object({}); +export const zDictionaryWithReference = z.object({}); -export const DictionaryWithArray = z.object({}); +export const zDictionaryWithArray = z.object({}); -export const DictionaryWithDictionary = z.object({}); +export const zDictionaryWithDictionary = z.object({}); -export const DictionaryWithProperties = z.object({}); +export const zDictionaryWithProperties = z.object({}); -export const ModelWithInteger = z.object({ +export const zModelWithInteger = z.object({ prop: z.number().optional() }); -export const ModelWithBoolean = z.object({ +export const zModelWithBoolean = z.object({ prop: z.boolean().optional() }); -export const ModelWithString = z.object({ +export const zModelWithString = z.object({ prop: z.string().optional() }); -export const ModelWithStringError = z.object({ +export const zModelWithStringError = z.object({ prop: z.string().optional() }); -export const Model_From_Zendesk = z.string(); +export const zModel_From_Zendesk = z.string(); -export const ModelWithNullableString = z.object({ +export const zModelWithNullableString = z.object({ nullableProp1: z.unknown().optional(), nullableRequiredProp1: z.unknown(), nullableProp2: z.unknown().optional(), @@ -135,7 +135,7 @@ export const ModelWithNullableString = z.object({ ]).optional() }); -export const ModelWithEnum = z.object({ +export const zModelWithEnum = z.object({ 'foo_bar-enum': z.enum([ 'Success', 'Warning', @@ -153,17 +153,17 @@ export const ModelWithEnum = z.object({ bool: z.unknown().optional() }); -export const ModelWithEnumWithHyphen = z.object({ +export const zModelWithEnumWithHyphen = z.object({ 'foo-bar-baz-qux': z.enum([ '3.0' ]).optional() }); -export const ModelWithEnumFromDescription = z.object({ +export const zModelWithEnumFromDescription = z.object({ test: z.number().optional() }); -export const ModelWithNestedEnums = z.object({ +export const zModelWithNestedEnums = z.object({ dictionaryWithEnum: z.object({}).optional(), dictionaryWithEnumFromDescription: z.object({}).optional(), arrayWithEnum: z.array(z.enum([ @@ -180,7 +180,7 @@ export const ModelWithNestedEnums = z.object({ ]).optional() }); -export const ModelWithReference = z.object({ +export const zModelWithReference = z.object({ prop: z.object({ required: z.string(), requiredAndReadOnly: z.string().readonly(), @@ -188,7 +188,7 @@ export const ModelWithReference = z.object({ string: z.string().optional(), number: z.number().optional(), boolean: z.boolean().optional(), - reference: ModelWithString.optional(), + reference: zModelWithString.optional(), 'property with space': z.string().optional(), default: z.string().optional(), try: z.string().optional(), @@ -197,7 +197,7 @@ export const ModelWithReference = z.object({ }).optional() }); -export const ModelWithArrayReadOnlyAndWriteOnly = z.object({ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ prop: z.array(z.object({ foo: z.string(), bar: z.string().readonly(), @@ -207,102 +207,102 @@ export const ModelWithArrayReadOnlyAndWriteOnly = z.object({ propWithNumber: z.array(z.number()).optional() }); -export const ModelWithArray = z.object({ - prop: z.array(ModelWithString).optional(), +export const zModelWithArray = z.object({ + prop: z.array(zModelWithString).optional(), propWithFile: z.array(z.string()).optional(), propWithNumber: z.array(z.number()).optional() }); -export const ModelWithDictionary = z.object({ +export const zModelWithDictionary = z.object({ prop: z.object({}).optional() }); -export const DeprecatedModel = z.object({ +export const zDeprecatedModel = z.object({ prop: z.string().optional() }); -export const CompositionWithOneOf = z.object({ +export const zCompositionWithOneOf = z.object({ propA: z.unknown().optional() }); -export const CompositionWithOneOfAnonymous = z.object({ +export const zCompositionWithOneOfAnonymous = z.object({ propA: z.unknown().optional() }); -export const ModelCircle = z.object({ +export const zModelCircle = z.object({ kind: z.string(), radius: z.number().optional() }); -export const ModelSquare = z.object({ +export const zModelSquare = z.object({ kind: z.string(), sideLength: z.number().optional() }); -export const CompositionWithOneOfDiscriminator = z.unknown(); +export const zCompositionWithOneOfDiscriminator = z.unknown(); -export const CompositionWithAnyOf = z.object({ +export const zCompositionWithAnyOf = z.object({ propA: z.unknown().optional() }); -export const CompositionWithAnyOfAnonymous = z.object({ +export const zCompositionWithAnyOfAnonymous = z.object({ propA: z.unknown().optional() }); -export const CompositionWithNestedAnyAndTypeNull = z.object({ +export const zCompositionWithNestedAnyAndTypeNull = z.object({ propA: z.unknown().optional() }); -export const _3e_num_1Период = z.enum([ +export const z_3e_num_1Период = z.enum([ 'Bird', 'Dog' ]); -export const ConstValue = z.string(); +export const zConstValue = z.string(); -export const CompositionWithNestedAnyOfAndNull = z.object({ +export const zCompositionWithNestedAnyOfAndNull = z.object({ propA: z.unknown().optional() }); -export const CompositionWithOneOfAndNullable = z.object({ +export const zCompositionWithOneOfAndNullable = z.object({ propA: z.unknown().optional() }); -export const CompositionWithOneOfAndSimpleDictionary = z.object({ +export const zCompositionWithOneOfAndSimpleDictionary = z.object({ propA: z.unknown().optional() }); -export const CompositionWithOneOfAndSimpleArrayDictionary = z.object({ +export const zCompositionWithOneOfAndSimpleArrayDictionary = z.object({ propA: z.unknown().optional() }); -export const CompositionWithOneOfAndComplexArrayDictionary = z.object({ +export const zCompositionWithOneOfAndComplexArrayDictionary = z.object({ propA: z.unknown().optional() }); -export const CompositionWithAllOfAndNullable = z.object({ +export const zCompositionWithAllOfAndNullable = z.object({ propA: z.unknown().optional() }); -export const CompositionWithAnyOfAndNullable = z.object({ +export const zCompositionWithAnyOfAndNullable = z.object({ propA: z.unknown().optional() }); -export const CompositionBaseModel = z.object({ +export const zCompositionBaseModel = z.object({ firstName: z.string().optional(), lastname: z.string().optional() }); -export const CompositionExtendedModel = z.unknown(); +export const zCompositionExtendedModel = z.unknown(); -export const ModelWithProperties = z.object({ +export const zModelWithProperties = z.object({ required: z.string(), requiredAndReadOnly: z.string().readonly(), requiredAndNullable: z.unknown(), string: z.string().optional(), number: z.number().optional(), boolean: z.boolean().optional(), - reference: ModelWithString.optional(), + reference: zModelWithString.optional(), 'property with space': z.string().optional(), default: z.string().optional(), try: z.string().optional(), @@ -310,33 +310,33 @@ export const ModelWithProperties = z.object({ '@namespace.integer': z.number().readonly().optional() }); -export const ModelWithNestedProperties = z.object({ +export const zModelWithNestedProperties = z.object({ first: z.unknown().readonly() }); -export const ModelWithDuplicateProperties = z.object({ - prop: ModelWithString.optional() +export const zModelWithDuplicateProperties = z.object({ + prop: zModelWithString.optional() }); -export const ModelWithOrderedProperties = z.object({ +export const zModelWithOrderedProperties = z.object({ zebra: z.string().optional(), apple: z.string().optional(), hawaii: z.string().optional() }); -export const ModelWithDuplicateImports = z.object({ - propA: ModelWithString.optional(), - propB: ModelWithString.optional(), - propC: ModelWithString.optional() +export const zModelWithDuplicateImports = z.object({ + propA: zModelWithString.optional(), + propB: zModelWithString.optional(), + propC: zModelWithString.optional() }); -export const ModelThatExtends = z.unknown(); +export const zModelThatExtends = z.unknown(); -export const ModelThatExtendsExtends = z.unknown(); +export const zModelThatExtendsExtends = z.unknown(); -export const ModelWithPattern = z.object({ - key: z.string(), - name: z.string(), +export const zModelWithPattern = z.object({ + key: z.string().max(64), + name: z.string().max(255), enabled: z.boolean().readonly().optional(), modified: z.string().datetime().readonly().optional(), id: z.string().optional(), @@ -346,122 +346,122 @@ export const ModelWithPattern = z.object({ patternWithBacktick: z.string().optional() }); -export const File = z.object({ - id: z.string().readonly().optional(), +export const zFile = z.object({ + id: z.string().min(1).readonly().optional(), updated_at: z.string().datetime().readonly().optional(), created_at: z.string().datetime().readonly().optional(), - mime: z.string(), + mime: z.string().min(1).max(24), file: z.string().url().readonly().optional() }); -export const _default = z.object({ +export const z_default = z.object({ name: z.string().optional() }); -export const Pageable = z.object({ - page: z.number().optional(), - size: z.number().optional(), +export const zPageable = z.object({ + page: z.number().gte(0).optional().default(0), + size: z.number().gte(1).optional(), sort: z.array(z.string()).optional() }); -export const FreeFormObjectWithoutAdditionalProperties = z.object({}); +export const zFreeFormObjectWithoutAdditionalProperties = z.object({}); -export const FreeFormObjectWithAdditionalPropertiesEqTrue = z.object({}); +export const zFreeFormObjectWithAdditionalPropertiesEqTrue = z.object({}); -export const FreeFormObjectWithAdditionalPropertiesEqEmptyObject = z.object({}); +export const zFreeFormObjectWithAdditionalPropertiesEqEmptyObject = z.object({}); -export const ModelWithConst = z.object({ +export const zModelWithConst = z.object({ String: z.string().optional(), number: z.number().optional(), null: z.null().optional(), withType: z.string().optional() }); -export const ModelWithAdditionalPropertiesEqTrue = z.object({ +export const zModelWithAdditionalPropertiesEqTrue = z.object({ prop: z.string().optional() }); -export const NestedAnyOfArraysNullable = z.object({ +export const zNestedAnyOfArraysNullable = z.object({ nullableArray: z.unknown().optional() }); -export const CompositionWithOneOfAndProperties = z.unknown(); +export const zCompositionWithOneOfAndProperties = z.unknown(); -export const NullableObject = z.unknown(); +export const zNullableObject = z.unknown(); -export const CharactersInDescription = z.string(); +export const zCharactersInDescription = z.string(); -export const ModelWithNullableObject = z.object({ - data: NullableObject.optional() +export const zModelWithNullableObject = z.object({ + data: zNullableObject.optional() }); -export const ModelWithOneOfEnum = z.unknown(); +export const zModelWithOneOfEnum = z.unknown(); -export const ModelWithNestedArrayEnumsDataFoo = z.enum([ +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ 'foo', 'bar' ]); -export const ModelWithNestedArrayEnumsDataBar = z.enum([ +export const zModelWithNestedArrayEnumsDataBar = z.enum([ 'baz', 'qux' ]); -export const ModelWithNestedArrayEnumsData = z.object({ - foo: z.array(ModelWithNestedArrayEnumsDataFoo).optional(), - bar: z.array(ModelWithNestedArrayEnumsDataBar).optional() +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), + bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() }); -export const ModelWithNestedArrayEnums = z.object({ +export const zModelWithNestedArrayEnums = z.object({ array_strings: z.array(z.string()).optional(), - data: ModelWithNestedArrayEnumsData.optional() + data: zModelWithNestedArrayEnumsData.optional() }); -export const ModelWithNestedCompositionEnums = z.object({ - foo: ModelWithNestedArrayEnumsDataFoo.optional() +export const zModelWithNestedCompositionEnums = z.object({ + foo: zModelWithNestedArrayEnumsDataFoo.optional() }); -export const ModelWithReadOnlyAndWriteOnly = z.object({ +export const zModelWithReadOnlyAndWriteOnly = z.object({ foo: z.string(), bar: z.string().readonly(), baz: z.string() }); -export const ModelWithConstantSizeArray = z.unknown(); +export const zModelWithConstantSizeArray = z.unknown(); -export const ModelWithAnyOfConstantSizeArray = z.unknown(); +export const zModelWithAnyOfConstantSizeArray = z.unknown(); -export const ModelWithPrefixItemsConstantSizeArray = z.unknown(); +export const zModelWithPrefixItemsConstantSizeArray = z.unknown(); -export const ModelWithAnyOfConstantSizeArrayNullable = z.unknown(); +export const zModelWithAnyOfConstantSizeArrayNullable = z.unknown(); -export const ModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.unknown(); +export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.unknown(); -export const ModelWithAnyOfConstantSizeArrayAndIntersect = z.unknown(); +export const zModelWithAnyOfConstantSizeArrayAndIntersect = z.unknown(); -export const ModelWithNumericEnumUnion = z.object({ +export const zModelWithNumericEnumUnion = z.object({ value: z.unknown().optional() }); -export const ModelWithBackticksInDescription = z.object({ +export const zModelWithBackticksInDescription = z.object({ template: z.string().optional() }); -export const ModelWithOneOfAndProperties = z.unknown(); +export const zModelWithOneOfAndProperties = z.unknown(); -export const ParameterSimpleParameterUnused = z.string(); +export const zParameterSimpleParameterUnused = z.string(); -export const PostServiceWithEmptyTagResponse = z.string(); +export const zPostServiceWithEmptyTagResponse = z.string(); -export const PostServiceWithEmptyTagResponse2 = z.string(); +export const zPostServiceWithEmptyTagResponse2 = z.string(); -export const DeleteFooData = z.string(); +export const zDeleteFooData = z.string(); -export const DeleteFooData2 = z.string(); +export const zDeleteFooData2 = z.string(); -export const _import = z.string(); +export const z_import = z.string(); -export const SchemaWithFormRestrictedKeys = z.object({ +export const zSchemaWithFormRestrictedKeys = z.object({ description: z.string().optional(), 'x-enum-descriptions': z.string().optional(), 'x-enum-varnames': z.string().optional(), @@ -483,38 +483,38 @@ export const SchemaWithFormRestrictedKeys = z.object({ })).optional() }); -export const io_k8s_apimachinery_pkg_apis_meta_v1_DeleteOptions = z.object({ +export const zio_k8s_apimachinery_pkg_apis_meta_v1_DeleteOptions = z.object({ preconditions: z.object({ resourceVersion: z.string().optional(), uid: z.string().optional() }).optional() }); -export const io_k8s_apimachinery_pkg_apis_meta_v1_Preconditions = z.object({ +export const zio_k8s_apimachinery_pkg_apis_meta_v1_Preconditions = z.object({ resourceVersion: z.string().optional(), uid: z.string().optional() }); -export const AdditionalPropertiesUnknownIssue = z.object({}); +export const zAdditionalPropertiesUnknownIssue = z.object({}); -export const AdditionalPropertiesUnknownIssue2 = z.object({}); +export const zAdditionalPropertiesUnknownIssue2 = z.object({}); -export const AdditionalPropertiesUnknownIssue3 = z.unknown(); +export const zAdditionalPropertiesUnknownIssue3 = z.unknown(); -export const AdditionalPropertiesIntegerIssue = z.object({ +export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.number() }); -export const OneOfAllOfIssue = z.unknown(); +export const zOneOfAllOfIssue = z.unknown(); -export const Generic_Schema_Duplicate_Issue_1_System_Boolean_ = z.object({ +export const zGeneric_Schema_Duplicate_Issue_1_System_Boolean_ = z.object({ item: z.boolean().optional(), error: z.unknown().optional(), hasError: z.boolean().readonly().optional(), data: z.object({}).optional() }); -export const Generic_Schema_Duplicate_Issue_1_System_String_ = z.object({ +export const zGeneric_Schema_Duplicate_Issue_1_System_String_ = z.object({ item: z.unknown().optional(), error: z.unknown().optional(), hasError: z.boolean().readonly().optional() diff --git a/packages/openapi-ts/test/index.spec.ts b/packages/openapi-ts/test/index.spec.ts index 5b2485652..00d21449c 100644 --- a/packages/openapi-ts/test/index.spec.ts +++ b/packages/openapi-ts/test/index.spec.ts @@ -40,7 +40,7 @@ describe('OpenAPI v2', () => { description: 'generate fetch client', name: 'v2', }, - ])('$description', async ({ name, config }) => { + ])('$description', async ({ config, name }) => { const output = toOutputPath(name); await createClient({ ...config, @@ -468,7 +468,7 @@ describe('OpenAPI v3', () => { it.each(clientScenarios.concat(allScenarios))( '$description', - async ({ name, config }) => { + async ({ config, name }) => { const output = toOutputPath(name); await createClient({ ...config, @@ -484,7 +484,7 @@ describe('OpenAPI v3', () => { it.each(clientScenarios)( 'transforms $description', - async ({ name, config }) => { + async ({ config, name }) => { const output = toOutputPath(name + '_transform'); await createClient({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7f855bf4f..367c5c616 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: eslint-plugin-simple-import-sort: specifier: 12.1.1 version: 12.1.1(eslint@9.6.0) + eslint-plugin-sort-destructure-keys: + specifier: 2.0.0 + version: 2.0.0(eslint@9.6.0) eslint-plugin-sort-keys-fix: specifier: 1.1.2 version: 1.1.2 @@ -5286,6 +5289,12 @@ packages: peerDependencies: eslint: '>=5.0.0' + eslint-plugin-sort-destructure-keys@2.0.0: + resolution: {integrity: sha512-4w1UQCa3o/YdfWaLr9jY8LfGowwjwjmwClyFLxIsToiyIdZMq3x9Ti44nDn34DtTPP7PWg96tUONKVmATKhYGQ==} + engines: {node: '>=12'} + peerDependencies: + eslint: 5 - 9 + eslint-plugin-sort-keys-fix@1.1.2: resolution: {integrity: sha512-DNPHFGCA0/hZIsfODbeLZqaGY/+q3vgtshF85r+YWDNCQ2apd9PNs/zL6ttKm0nD1IFwvxyg3YOTI7FHl4unrw==} engines: {node: '>=0.10.0'} @@ -9146,7 +9155,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1802.12(chokidar@3.6.0) - '@angular-devkit/build-webpack': 0.1802.12(chokidar@3.6.0)(webpack-dev-server@5.0.4(webpack@5.94.0(esbuild@0.23.0)))(webpack@5.94.0(esbuild@0.23.0)) + '@angular-devkit/build-webpack': 0.1802.12(chokidar@3.6.0)(webpack-dev-server@5.0.4(webpack@5.94.0(esbuild@0.23.1)))(webpack@5.94.0(esbuild@0.23.0)) '@angular-devkit/core': 18.2.12(chokidar@3.6.0) '@angular/build': 18.2.12(@angular/compiler-cli@18.2.12(@angular/compiler@18.2.12(@angular/core@18.2.12(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.5.3))(@types/node@22.8.5)(chokidar@3.6.0)(less@4.2.0)(postcss@8.4.41)(tailwindcss@3.4.9(ts-node@10.9.2(@types/node@22.8.5)(typescript@5.5.3)))(terser@5.31.6)(typescript@5.5.3) '@angular/compiler-cli': 18.2.12(@angular/compiler@18.2.12(@angular/core@18.2.12(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.5.3) @@ -9203,9 +9212,9 @@ snapshots: typescript: 5.5.3 vite: 5.4.6(@types/node@22.8.5)(less@4.2.0)(sass@1.77.6)(terser@5.31.6) watchpack: 2.4.1 - webpack: 5.94.0(esbuild@0.23.1) - webpack-dev-middleware: 7.4.2(webpack@5.94.0(esbuild@0.23.0)) - webpack-dev-server: 5.0.4(webpack@5.94.0(esbuild@0.23.0)) + webpack: 5.94.0(esbuild@0.23.0) + webpack-dev-middleware: 7.4.2(webpack@5.94.0(esbuild@0.23.1)) + webpack-dev-server: 5.0.4(webpack@5.94.0(esbuild@0.23.1)) webpack-merge: 6.0.1 webpack-subresource-integrity: 5.1.0(webpack@5.94.0(esbuild@0.23.0)) optionalDependencies: @@ -9230,12 +9239,12 @@ snapshots: - utf-8-validate - webpack-cli - '@angular-devkit/build-webpack@0.1802.12(chokidar@3.6.0)(webpack-dev-server@5.0.4(webpack@5.94.0(esbuild@0.23.0)))(webpack@5.94.0(esbuild@0.23.0))': + '@angular-devkit/build-webpack@0.1802.12(chokidar@3.6.0)(webpack-dev-server@5.0.4(webpack@5.94.0(esbuild@0.23.1)))(webpack@5.94.0(esbuild@0.23.0))': dependencies: '@angular-devkit/architect': 0.1802.12(chokidar@3.6.0) rxjs: 7.8.1 - webpack: 5.94.0(esbuild@0.23.1) - webpack-dev-server: 5.0.4(webpack@5.94.0(esbuild@0.23.0)) + webpack: 5.94.0(esbuild@0.23.0) + webpack-dev-server: 5.0.4(webpack@5.94.0(esbuild@0.23.1)) transitivePeerDependencies: - chokidar @@ -11226,7 +11235,7 @@ snapshots: dependencies: '@angular/compiler-cli': 18.2.12(@angular/compiler@18.2.12(@angular/core@18.2.12(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.5.3) typescript: 5.5.3 - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(esbuild@0.23.0) '@nodelib/fs.scandir@2.1.5': dependencies: @@ -13472,7 +13481,7 @@ snapshots: '@babel/core': 7.25.2 find-cache-dir: 4.0.0 schema-utils: 4.2.0 - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(esbuild@0.23.0) babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.25.2): dependencies: @@ -13935,7 +13944,7 @@ snapshots: normalize-path: 3.0.0 schema-utils: 4.2.0 serialize-javascript: 6.0.2 - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(esbuild@0.23.0) core-js-compat@3.39.0: dependencies: @@ -13992,7 +14001,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.6.3 optionalDependencies: - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(esbuild@0.23.0) css-select@5.1.0: dependencies: @@ -14399,6 +14408,11 @@ snapshots: dependencies: eslint: 9.6.0 + eslint-plugin-sort-destructure-keys@2.0.0(eslint@9.6.0): + dependencies: + eslint: 9.6.0 + natural-compare-lite: 1.4.0 + eslint-plugin-sort-keys-fix@1.1.2: dependencies: espree: 6.2.1 @@ -15572,7 +15586,7 @@ snapshots: dependencies: less: 4.2.0 optionalDependencies: - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(esbuild@0.23.0) less@4.2.0: dependencies: @@ -15597,7 +15611,7 @@ snapshots: dependencies: webpack-sources: 3.2.3 optionalDependencies: - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(esbuild@0.23.0) light-my-request@6.3.0: dependencies: @@ -15878,7 +15892,7 @@ snapshots: dependencies: schema-utils: 4.2.0 tapable: 2.2.1 - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(esbuild@0.23.0) minimalistic-assert@1.0.1: {} @@ -16517,7 +16531,7 @@ snapshots: postcss: 8.4.41 semver: 7.6.3 optionalDependencies: - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(esbuild@0.23.0) transitivePeerDependencies: - typescript @@ -16991,7 +17005,7 @@ snapshots: neo-async: 2.6.2 optionalDependencies: sass: 1.77.6 - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(esbuild@0.23.0) sass@1.77.6: dependencies: @@ -17263,7 +17277,7 @@ snapshots: dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(esbuild@0.23.0) source-map-support@0.5.21: dependencies: @@ -17632,16 +17646,16 @@ snapshots: term-size@2.2.1: {} - terser-webpack-plugin@5.3.10(esbuild@0.23.1)(webpack@5.94.0(esbuild@0.23.0)): + terser-webpack-plugin@5.3.10(esbuild@0.23.0)(webpack@5.94.0(esbuild@0.23.1)): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.31.6 - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(esbuild@0.23.0) optionalDependencies: - esbuild: 0.23.1 + esbuild: 0.23.0 terser@5.31.6: dependencies: @@ -18441,7 +18455,7 @@ snapshots: webidl-conversions@7.0.0: {} - webpack-dev-middleware@7.4.2(webpack@5.94.0(esbuild@0.23.0)): + webpack-dev-middleware@7.4.2(webpack@5.94.0(esbuild@0.23.1)): dependencies: colorette: 2.0.20 memfs: 4.14.0 @@ -18450,9 +18464,9 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.2.0 optionalDependencies: - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(esbuild@0.23.0) - webpack-dev-server@5.0.4(webpack@5.94.0(esbuild@0.23.0)): + webpack-dev-server@5.0.4(webpack@5.94.0(esbuild@0.23.1)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -18482,10 +18496,10 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.2(webpack@5.94.0(esbuild@0.23.0)) + webpack-dev-middleware: 7.4.2(webpack@5.94.0(esbuild@0.23.1)) ws: 8.17.1 optionalDependencies: - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(esbuild@0.23.0) transitivePeerDependencies: - bufferutil - debug @@ -18503,9 +18517,9 @@ snapshots: webpack-subresource-integrity@5.1.0(webpack@5.94.0(esbuild@0.23.0)): dependencies: typed-assert: 1.0.9 - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(esbuild@0.23.0) - webpack@5.94.0(esbuild@0.23.1): + webpack@5.94.0(esbuild@0.23.0): dependencies: '@types/estree': 1.0.5 '@webassemblyjs/ast': 1.12.1 @@ -18527,7 +18541,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(esbuild@0.23.1)(webpack@5.94.0(esbuild@0.23.0)) + terser-webpack-plugin: 5.3.10(esbuild@0.23.0)(webpack@5.94.0(esbuild@0.23.1)) watchpack: 2.4.1 webpack-sources: 3.2.3 transitivePeerDependencies: