diff --git a/.changeset/strong-worms-watch.md b/.changeset/strong-worms-watch.md new file mode 100644 index 0000000..ffeadd1 --- /dev/null +++ b/.changeset/strong-worms-watch.md @@ -0,0 +1,5 @@ +--- +"@alova/wormhole": patch +--- + +handle array default items in union and avoid TypeError diff --git a/packages/wormhole/src/core/loader/astLoader/normalize/rules/convertTypeArray.ts b/packages/wormhole/src/core/loader/astLoader/normalize/rules/convertTypeArray.ts index 079a9fb..a3d3f84 100644 --- a/packages/wormhole/src/core/loader/astLoader/normalize/rules/convertTypeArray.ts +++ b/packages/wormhole/src/core/loader/astLoader/normalize/rules/convertTypeArray.ts @@ -17,6 +17,13 @@ export const TYPE_SPECIFIC_KEYWORDS = { array: ['items', 'minItems', 'maxItems', 'uniqueItems'], object: ['properties', 'additionalProperties', 'required', 'minProperties', 'maxProperties'], } +export const TYPE_REQUIRE_KEYWORDS: Record> = { + string: [], + number: [], + integer: [], + array: [['items', { type: 'any' }]], + object: [], +} export function assignTypeSpecificKeywords( branch: SchemaObject, @@ -24,6 +31,7 @@ export function assignTypeSpecificKeywords( typeSpecificKeywords: Record, ) { const keywords = TYPE_SPECIFIC_KEYWORDS[type as keyof typeof TYPE_SPECIFIC_KEYWORDS] + const requireKeywords = TYPE_REQUIRE_KEYWORDS[type as keyof typeof TYPE_REQUIRE_KEYWORDS] if (!keywords) { return } @@ -32,6 +40,11 @@ export function assignTypeSpecificKeywords( (branch as any)[key] = typeSpecificKeywords[key] } }) + requireKeywords.forEach(([key, value]) => { + if (!(key in typeSpecificKeywords)) { + (branch as any)[key] = value + } + }) } export default function convertTypeArray(schema: SchemaObject): SchemaObject | void { diff --git a/packages/wormhole/test/generates/array.test.ts b/packages/wormhole/test/generates/array.test.ts index 3e42e6a..7df6c31 100644 --- a/packages/wormhole/test/generates/array.test.ts +++ b/packages/wormhole/test/generates/array.test.ts @@ -108,7 +108,7 @@ describe('array Type Generator', () => { comment: ``, type: 'type', code: `Array<{ - value?:string + value?:string }>`, }) expect(result).toEqual(expectResult) @@ -167,4 +167,22 @@ describe('array Type Generator', () => { }) expect(result).toEqual(expectResult) }) + + it('should generate array type with ANY default', async () => { + const ast: TArray = { + type: ASTType.ARRAY, + keyName: 'AnyArray', + params: { + type: ASTType.ANY, + }, + } + const result = await normalizeGeneratorResult(arrayTypeGenerator(ast, defaultCtx)) + const expectResult = await normalizeGeneratorResult({ + name: 'AnyArray', + comment: ``, + type: 'type', + code: `any[]`, + }) + expect(result).toEqual(expectResult) + }) }) diff --git a/packages/wormhole/test/generates/group.test.ts b/packages/wormhole/test/generates/group.test.ts index dd2bcdd..f0f8e73 100644 --- a/packages/wormhole/test/generates/group.test.ts +++ b/packages/wormhole/test/generates/group.test.ts @@ -35,7 +35,7 @@ describe('group Type Generator', () => { name: 'Status', comment: ` /** - * Status type + * Status type */`, type: 'type', code: 'string | number', @@ -83,10 +83,10 @@ describe('group Type Generator', () => { * User with role */`, type: 'type', - code: `{ - name?:string - } & { - role?:string + code: `{ + name?:string + } & { + role?:string }`, }) expect(result).toEqual(expectResult) @@ -145,12 +145,12 @@ describe('group Type Generator', () => { name: 'ComplexType', comment: '', type: 'type', - code: `{ - id?:number + code: `{ + id?:number } & { - name?:string + name?:string } | { - code?:string + code?:string }`, }) expect(result).toEqual(expectResult) @@ -196,12 +196,38 @@ describe('group Type Generator', () => { name: 'DeepTest', comment: '', type: 'type', - code: `{ - value?:string + code: `{ + value?:string } | { - value?:number + value?:number }`, }) expect(result).toEqual(expectResult) }) + + it('should generate union including array(any) and null (fix #140)', async () => { + const ast: TUnion = { + type: ASTType.UNION, + keyName: 'MaybeArray', + params: [ + { + type: ASTType.ARRAY, + params: { + type: ASTType.ANY, + }, + }, + { + type: ASTType.NULL, + }, + ], + } + const result = await normalizeGeneratorResult(groupTypeGenerator(ast, defaultCtx)) + const expectResult = await normalizeGeneratorResult({ + name: 'MaybeArray', + comment: '', + type: 'type', + code: 'any[] | null', + }) + expect(result).toEqual(expectResult) + }) }) diff --git a/packages/wormhole/test/normalize.spec.ts b/packages/wormhole/test/normalize.spec.ts index c80aefd..8e81206 100644 --- a/packages/wormhole/test/normalize.spec.ts +++ b/packages/wormhole/test/normalize.spec.ts @@ -164,6 +164,46 @@ describe('schema Normalizer', () => { expect(stringBranch.minLength).toBe(5) expect((stringBranch as ArraySchemaObject).items).toBeUndefined() }) + + it('should assign default items for array branch when items missing', () => { + const schema: SchemaObject = { + type: ['array', 'string'], + minLength: 5, + title: 'Test', + } + + const result = normalizer.normalize(schema) as SchemaObject + expect(result.anyOf).toBeDefined() + expect(result.anyOf).toHaveLength(2) + + const arrayBranch = result.anyOf![0] as ArraySchemaObject + const stringBranch = result.anyOf![1] as SchemaObject + + expect(arrayBranch.type).toBe('array') + expect(arrayBranch.items).toBeDefined() + expect((arrayBranch.items as SchemaObject).type).toBeDefined() + expect(stringBranch.type).toBe('string') + expect(stringBranch.minLength).toBe(5) + }) + + it('should handle array | null union by injecting default items into array branch (fix #140)', () => { + const schema: SchemaObject = { + type: ['array', 'null'] as any, + title: 'Issue 140', + } + const result = normalizer.normalize(schema) as SchemaObject + expect(result.type).toBeUndefined() + expect(result.anyOf).toBeDefined() + expect(result.anyOf).toHaveLength(2) + + const arrayBranch = result.anyOf![0] as ArraySchemaObject + const nullBranch = result.anyOf![1] as SchemaObject + + expect(arrayBranch.type).toBe('array') + expect(arrayBranch.items).toBeDefined() + expect((arrayBranch.items as SchemaObject).type).toBeDefined() + expect(nullBranch.type).toBe('null') + }) }) describe('mergeAnyOf rule', () => {