fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #701 from hey-api/feat/prefix-items

Feat/prefix items

authored by

Lubos and committed by
GitHub
3ad6f72d 7415cc18

+408 -66
+5
.changeset/lazy-cheetahs-grow.md
··· 1 + --- 2 + '@hey-api/openapi-ts': patch 3 + --- 4 + 5 + feat: add initial implementation of prefixItems
+30 -42
packages/openapi-ts/src/compiler/typedef.ts
··· 7 7 tsNodeToString, 8 8 } from './utils'; 9 9 10 + const nullNode = ts.factory.createTypeReferenceNode('null'); 11 + 10 12 export const createTypeNode = ( 11 13 base: any | ts.TypeNode, 12 14 args?: (any | ts.TypeNode)[], ··· 63 65 }; 64 66 65 67 /** 68 + * Returns a union of provided node with null if marked as nullable, 69 + * otherwise returns the provided node unmodified. 70 + */ 71 + const maybeNullable = ({ 72 + isNullable, 73 + node, 74 + }: { 75 + node: ts.TypeNode; 76 + isNullable: boolean; 77 + }) => { 78 + if (!isNullable) { 79 + return node; 80 + } 81 + return ts.factory.createUnionTypeNode([node, nullNode]); 82 + }; 83 + 84 + /** 66 85 * Create a interface type node. Example `{ readonly x: string, y?: number }` 67 86 * @param properties - the properties of the interface. 68 87 * @param isNullable - if the whole interface can be nullable ··· 90 109 return signature; 91 110 }), 92 111 ); 93 - if (!isNullable) { 94 - return node; 95 - } 96 - return ts.factory.createUnionTypeNode([ 97 - node, 98 - ts.factory.createTypeReferenceNode('null'), 99 - ]); 112 + return maybeNullable({ isNullable, node }); 100 113 }; 101 114 102 115 /** ··· 110 123 isNullable: boolean = false, 111 124 ) => { 112 125 const nodes = types.map((type) => createTypeNode(type)); 113 - if (isNullable) { 114 - nodes.push(ts.factory.createTypeReferenceNode('null')); 126 + if (!isNullable) { 127 + return ts.factory.createUnionTypeNode(nodes); 115 128 } 116 - return ts.factory.createUnionTypeNode(nodes); 129 + return ts.factory.createUnionTypeNode([...nodes, nullNode]); 117 130 }; 118 131 119 132 /** ··· 126 139 types: (any | ts.TypeNode)[], 127 140 isNullable: boolean = false, 128 141 ) => { 129 - const nodes = types.map((t) => createTypeNode(t)); 130 - const intersect = ts.factory.createIntersectionTypeNode(nodes); 131 - if (isNullable) { 132 - return ts.factory.createUnionTypeNode([ 133 - intersect, 134 - ts.factory.createTypeReferenceNode('null'), 135 - ]); 136 - } 137 - return intersect; 142 + const nodes = types.map((type) => createTypeNode(type)); 143 + const node = ts.factory.createIntersectionTypeNode(nodes); 144 + return maybeNullable({ isNullable, node }); 138 145 }; 139 146 140 147 /** ··· 151 158 types: Array<any | ts.TypeNode>; 152 159 }) => { 153 160 const nodes = types.map((type) => createTypeNode(type)); 154 - const tupleNode = ts.factory.createTupleTypeNode(nodes); 155 - if (isNullable) { 156 - const unionNode = ts.factory.createUnionTypeNode([ 157 - tupleNode, 158 - ts.factory.createTypeReferenceNode('null'), 159 - ]); 160 - return unionNode; 161 - } 162 - return tupleNode; 161 + const node = ts.factory.createTupleTypeNode(nodes); 162 + return maybeNullable({ isNullable, node }); 163 163 }; 164 164 165 165 /** ··· 186 186 type: valueNode, 187 187 }, 188 188 ]); 189 - if (!isNullable) { 190 - return node; 191 - } 192 - return ts.factory.createUnionTypeNode([ 193 - node, 194 - ts.factory.createTypeReferenceNode('null'), 195 - ]); 189 + return maybeNullable({ isNullable, node }); 196 190 }; 197 191 198 192 /** ··· 208 202 const node = ts.factory.createTypeReferenceNode('Array', [ 209 203 createTypeUnionNode(types), 210 204 ]); 211 - if (!isNullable) { 212 - return node; 213 - } 214 - return ts.factory.createUnionTypeNode([ 215 - node, 216 - ts.factory.createTypeReferenceNode('null'), 217 - ]); 205 + return maybeNullable({ isNullable, node }); 218 206 };
+1 -1
packages/openapi-ts/src/openApi/common/interfaces/client.ts
··· 145 145 | OpenApiParameter['in'] 146 146 | OperationResponse['in'] 147 147 | ''; 148 - link: Model | null; 148 + link: Model | Model[] | null; 149 149 meta?: ModelMeta; 150 150 /** 151 151 * @deprecated use `meta.name` instead
+14 -1
packages/openapi-ts/src/openApi/common/parser/operation.ts
··· 8 8 const equal = 9 9 a.type === b.type && a.base === b.base && a.template === b.template; 10 10 if (equal && a.link && b.link) { 11 - return areEqual(a.link, b.link); 11 + if (!Array.isArray(a.link) && !Array.isArray(b.link)) { 12 + return areEqual(a.link, b.link); 13 + } 14 + 15 + if ( 16 + Array.isArray(a.link) && 17 + Array.isArray(b.link) && 18 + a.link.length === b.link.length 19 + ) { 20 + const bLinks = b.link; 21 + return a.link.every((model, index) => areEqual(model, bLinks[index]!)); 22 + } 23 + 24 + return false; 12 25 } 13 26 return equal; 14 27 };
+1
packages/openapi-ts/src/openApi/v3/interfaces/OpenApiSchema.ts
··· 48 48 nullable?: boolean; 49 49 oneOf?: OpenApiSchema[]; 50 50 pattern?: string; 51 + prefixItems?: OpenApiSchema[]; 51 52 properties?: Dictionary<OpenApiSchema>; 52 53 readOnly?: boolean; 53 54 required?: string[];
+38 -1
packages/openapi-ts/src/openApi/v3/parser/getModel.ts
··· 107 107 } 108 108 } 109 109 110 - if (definitionTypes.includes('array') && definition.items) { 110 + if ( 111 + definitionTypes.includes('array') && 112 + (definition.items || definition.prefixItems) 113 + ) { 114 + if (definition.prefixItems) { 115 + const arrayItems = definition.prefixItems.map((item) => 116 + getModel({ 117 + definition: item, 118 + openApi, 119 + parentDefinition: definition, 120 + types, 121 + }), 122 + ); 123 + 124 + model.export = 'array'; 125 + model.$refs = [ 126 + ...model.$refs, 127 + ...arrayItems.reduce( 128 + (acc, m) => [...acc, ...m.$refs], 129 + [] as Model['$refs'], 130 + ), 131 + ]; 132 + model.imports = [ 133 + ...model.imports, 134 + ...arrayItems.reduce( 135 + (acc, m) => [...acc, ...m.imports], 136 + [] as Model['imports'], 137 + ), 138 + ]; 139 + model.link = arrayItems; 140 + model.default = getDefault(definition, model); 141 + return model; 142 + } 143 + 144 + if (!definition.items) { 145 + return model; 146 + } 147 + 111 148 if (definition.items.$ref) { 112 149 const arrayItems = getType({ type: definition.items.$ref }); 113 150 model.$refs = [...model.$refs, definition.items.$ref];
+30 -18
packages/openapi-ts/src/utils/write/type.ts
··· 32 32 }; 33 33 34 34 const typeArray = (model: Model) => { 35 - // Special case where we use tuple to define constant size array. 36 - if ( 37 - model.export === 'array' && 38 - model.link && 39 - model.maxItems && 40 - model.minItems && 41 - model.maxItems === model.minItems && 42 - model.maxItems <= 100 43 - ) { 44 - const types = Array(model.maxItems).fill(toType(model.link)); 45 - const tuple = compiler.typedef.tuple({ 46 - isNullable: model.isNullable, 47 - types, 48 - }); 49 - return tuple; 50 - } 51 - 52 35 if (model.link) { 36 + // We treat an array of `model.link` as constant size array definition. 37 + if (Array.isArray(model.link)) { 38 + const types = model.link.map((m) => toType(m)); 39 + const tuple = compiler.typedef.tuple({ 40 + isNullable: model.isNullable, 41 + types, 42 + }); 43 + return tuple; 44 + } 45 + 46 + // Special case where we use tuple to define constant size array. 47 + if ( 48 + model.export === 'array' && 49 + model.maxItems && 50 + model.minItems && 51 + model.maxItems === model.minItems && 52 + model.maxItems <= 100 53 + ) { 54 + const types = Array(model.maxItems).fill(toType(model.link)); 55 + const tuple = compiler.typedef.tuple({ 56 + isNullable: model.isNullable, 57 + types, 58 + }); 59 + return tuple; 60 + } 61 + 53 62 return compiler.typedef.array([toType(model.link)], model.isNullable); 54 63 } 55 64 ··· 62 71 }; 63 72 64 73 const typeDict = (model: Model) => { 65 - const type = model.link ? toType(model.link) : base(model); 74 + const type = 75 + model.link && !Array.isArray(model.link) ? toType(model.link) : base(model); 66 76 return compiler.typedef.record(['string'], [type], model.isNullable); 67 77 }; 68 78 ··· 137 147 return typeEnum(model); 138 148 case 'interface': 139 149 return typeInterface(model); 150 + case 'const': 151 + case 'generic': 140 152 case 'reference': 141 153 default: 142 154 return typeReference(model);
+22
packages/openapi-ts/test/__snapshots__/test/generated/v3/schemas.gen.ts.snap
··· 1590 1590 maxItems: 3 1591 1591 } as const; 1592 1592 1593 + export const $ModelWithPrefixItemsConstantSizeArray = { 1594 + type: 'array', 1595 + prefixItems: [ 1596 + { 1597 + '$ref': '#/components/schemas/ModelWithInteger' 1598 + }, 1599 + { 1600 + oneOf: [ 1601 + { 1602 + type: 'number' 1603 + }, 1604 + { 1605 + type: 'string' 1606 + } 1607 + ] 1608 + }, 1609 + { 1610 + type: 'string' 1611 + } 1612 + ] 1613 + } as const; 1614 + 1593 1615 export const $ModelWithAnyOfConstantSizeArrayNullable = { 1594 1616 type: ['array'], 1595 1617 items: {
+6
packages/openapi-ts/test/__snapshots__/test/generated/v3/types.gen.ts.snap
··· 875 875 number | string 876 876 ]; 877 877 878 + export type ModelWithPrefixItemsConstantSizeArray = [ 879 + ModelWithInteger, 880 + number | string, 881 + string 882 + ]; 883 + 878 884 export type ModelWithAnyOfConstantSizeArrayNullable = [ 879 885 number | null | string, 880 886 number | null | string,
+22
packages/openapi-ts/test/__snapshots__/test/generated/v3_angular/schemas.gen.ts.snap
··· 1590 1590 maxItems: 3 1591 1591 } as const; 1592 1592 1593 + export const $ModelWithPrefixItemsConstantSizeArray = { 1594 + type: 'array', 1595 + prefixItems: [ 1596 + { 1597 + '$ref': '#/components/schemas/ModelWithInteger' 1598 + }, 1599 + { 1600 + oneOf: [ 1601 + { 1602 + type: 'number' 1603 + }, 1604 + { 1605 + type: 'string' 1606 + } 1607 + ] 1608 + }, 1609 + { 1610 + type: 'string' 1611 + } 1612 + ] 1613 + } as const; 1614 + 1593 1615 export const $ModelWithAnyOfConstantSizeArrayNullable = { 1594 1616 type: ['array'], 1595 1617 items: {
+6
packages/openapi-ts/test/__snapshots__/test/generated/v3_angular/types.gen.ts.snap
··· 765 765 number | string 766 766 ]; 767 767 768 + export type ModelWithPrefixItemsConstantSizeArray = [ 769 + ModelWithInteger, 770 + number | string, 771 + string 772 + ]; 773 + 768 774 export type ModelWithAnyOfConstantSizeArrayNullable = [ 769 775 number | null | string, 770 776 number | null | string,
+22
packages/openapi-ts/test/__snapshots__/test/generated/v3_axios/schemas.gen.ts.snap
··· 1590 1590 maxItems: 3 1591 1591 } as const; 1592 1592 1593 + export const $ModelWithPrefixItemsConstantSizeArray = { 1594 + type: 'array', 1595 + prefixItems: [ 1596 + { 1597 + '$ref': '#/components/schemas/ModelWithInteger' 1598 + }, 1599 + { 1600 + oneOf: [ 1601 + { 1602 + type: 'number' 1603 + }, 1604 + { 1605 + type: 'string' 1606 + } 1607 + ] 1608 + }, 1609 + { 1610 + type: 'string' 1611 + } 1612 + ] 1613 + } as const; 1614 + 1593 1615 export const $ModelWithAnyOfConstantSizeArrayNullable = { 1594 1616 type: ['array'], 1595 1617 items: {
+6
packages/openapi-ts/test/__snapshots__/test/generated/v3_axios/types.gen.ts.snap
··· 875 875 number | string 876 876 ]; 877 877 878 + export type ModelWithPrefixItemsConstantSizeArray = [ 879 + ModelWithInteger, 880 + number | string, 881 + string 882 + ]; 883 + 878 884 export type ModelWithAnyOfConstantSizeArrayNullable = [ 879 885 number | null | string, 880 886 number | null | string,
+6
packages/openapi-ts/test/__snapshots__/test/generated/v3_client/types.gen.ts.snap
··· 765 765 number | string 766 766 ]; 767 767 768 + export type ModelWithPrefixItemsConstantSizeArray = [ 769 + ModelWithInteger, 770 + number | string, 771 + string 772 + ]; 773 + 768 774 export type ModelWithAnyOfConstantSizeArrayNullable = [ 769 775 number | null | string, 770 776 number | null | string,
+6
packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_typescript/types.gen.ts.snap
··· 836 836 number | string 837 837 ]; 838 838 839 + export type ModelWithPrefixItemsConstantSizeArray = [ 840 + ModelWithInteger, 841 + number | string, 842 + string 843 + ]; 844 + 839 845 export type ModelWithAnyOfConstantSizeArrayNullable = [ 840 846 number | null | string, 841 847 number | null | string,
+22
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios/schemas.gen.ts.snap
··· 1590 1590 maxItems: 3 1591 1591 } as const; 1592 1592 1593 + export const $ModelWithPrefixItemsConstantSizeArray = { 1594 + type: 'array', 1595 + prefixItems: [ 1596 + { 1597 + '$ref': '#/components/schemas/ModelWithInteger' 1598 + }, 1599 + { 1600 + oneOf: [ 1601 + { 1602 + type: 'number' 1603 + }, 1604 + { 1605 + type: 'string' 1606 + } 1607 + ] 1608 + }, 1609 + { 1610 + type: 'string' 1611 + } 1612 + ] 1613 + } as const; 1614 + 1593 1615 export const $ModelWithAnyOfConstantSizeArrayNullable = { 1594 1616 type: ['array'], 1595 1617 items: {
+6
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios/types.gen.ts.snap
··· 875 875 number | string 876 876 ]; 877 877 878 + export type ModelWithPrefixItemsConstantSizeArray = [ 879 + ModelWithInteger, 880 + number | string, 881 + string 882 + ]; 883 + 878 884 export type ModelWithAnyOfConstantSizeArrayNullable = [ 879 885 number | null | string, 880 886 number | null | string,
+22
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch/schemas.gen.ts.snap
··· 1590 1590 maxItems: 3 1591 1591 } as const; 1592 1592 1593 + export const $ModelWithPrefixItemsConstantSizeArray = { 1594 + type: 'array', 1595 + prefixItems: [ 1596 + { 1597 + '$ref': '#/components/schemas/ModelWithInteger' 1598 + }, 1599 + { 1600 + oneOf: [ 1601 + { 1602 + type: 'number' 1603 + }, 1604 + { 1605 + type: 'string' 1606 + } 1607 + ] 1608 + }, 1609 + { 1610 + type: 'string' 1611 + } 1612 + ] 1613 + } as const; 1614 + 1593 1615 export const $ModelWithAnyOfConstantSizeArrayNullable = { 1594 1616 type: ['array'], 1595 1617 items: {
+6
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch/types.gen.ts.snap
··· 875 875 number | string 876 876 ]; 877 877 878 + export type ModelWithPrefixItemsConstantSizeArray = [ 879 + ModelWithInteger, 880 + number | string, 881 + string 882 + ]; 883 + 878 884 export type ModelWithAnyOfConstantSizeArrayNullable = [ 879 885 number | null | string, 880 886 number | null | string,
+6
packages/openapi-ts/test/__snapshots__/test/generated/v3_models/types.gen.ts.snap
··· 765 765 number | string 766 766 ]; 767 767 768 + export type ModelWithPrefixItemsConstantSizeArray = [ 769 + ModelWithInteger, 770 + number | string, 771 + string 772 + ]; 773 + 768 774 export type ModelWithAnyOfConstantSizeArrayNullable = [ 769 775 number | null | string, 770 776 number | null | string,
+22
packages/openapi-ts/test/__snapshots__/test/generated/v3_node/schemas.gen.ts.snap
··· 1590 1590 maxItems: 3 1591 1591 } as const; 1592 1592 1593 + export const $ModelWithPrefixItemsConstantSizeArray = { 1594 + type: 'array', 1595 + prefixItems: [ 1596 + { 1597 + '$ref': '#/components/schemas/ModelWithInteger' 1598 + }, 1599 + { 1600 + oneOf: [ 1601 + { 1602 + type: 'number' 1603 + }, 1604 + { 1605 + type: 'string' 1606 + } 1607 + ] 1608 + }, 1609 + { 1610 + type: 'string' 1611 + } 1612 + ] 1613 + } as const; 1614 + 1593 1615 export const $ModelWithAnyOfConstantSizeArrayNullable = { 1594 1616 type: ['array'], 1595 1617 items: {
+6
packages/openapi-ts/test/__snapshots__/test/generated/v3_node/types.gen.ts.snap
··· 875 875 number | string 876 876 ]; 877 877 878 + export type ModelWithPrefixItemsConstantSizeArray = [ 879 + ModelWithInteger, 880 + number | string, 881 + string 882 + ]; 883 + 878 884 export type ModelWithAnyOfConstantSizeArrayNullable = [ 879 885 number | null | string, 880 886 number | null | string,
+22
packages/openapi-ts/test/__snapshots__/test/generated/v3_schemas_form/schemas.gen.ts.snap
··· 1471 1471 maxItems: 3 1472 1472 } as const; 1473 1473 1474 + export const $ModelWithPrefixItemsConstantSizeArray = { 1475 + type: 'array', 1476 + prefixItems: [ 1477 + { 1478 + '$ref': '#/components/schemas/ModelWithInteger' 1479 + }, 1480 + { 1481 + oneOf: [ 1482 + { 1483 + type: 'number' 1484 + }, 1485 + { 1486 + type: 'string' 1487 + } 1488 + ] 1489 + }, 1490 + { 1491 + type: 'string' 1492 + } 1493 + ] 1494 + } as const; 1495 + 1474 1496 export const $ModelWithAnyOfConstantSizeArrayNullable = { 1475 1497 type: ['array'], 1476 1498 items: {
+22
packages/openapi-ts/test/__snapshots__/test/generated/v3_schemas_json/schemas.gen.ts.snap
··· 1590 1590 maxItems: 3 1591 1591 } as const; 1592 1592 1593 + export const $ModelWithPrefixItemsConstantSizeArray = { 1594 + type: 'array', 1595 + prefixItems: [ 1596 + { 1597 + '$ref': '#/components/schemas/ModelWithInteger' 1598 + }, 1599 + { 1600 + oneOf: [ 1601 + { 1602 + type: 'number' 1603 + }, 1604 + { 1605 + type: 'string' 1606 + } 1607 + ] 1608 + }, 1609 + { 1610 + type: 'string' 1611 + } 1612 + ] 1613 + } as const; 1614 + 1593 1615 export const $ModelWithAnyOfConstantSizeArrayNullable = { 1594 1616 type: ['array'], 1595 1617 items: {
+6
packages/openapi-ts/test/__snapshots__/test/generated/v3_tree_shakeable/types.gen.ts.snap
··· 875 875 number | string 876 876 ]; 877 877 878 + export type ModelWithPrefixItemsConstantSizeArray = [ 879 + ModelWithInteger, 880 + number | string, 881 + string 882 + ]; 883 + 878 884 export type ModelWithAnyOfConstantSizeArrayNullable = [ 879 885 number | null | string, 880 886 number | null | string,
+22
packages/openapi-ts/test/__snapshots__/test/generated/v3_xhr/schemas.gen.ts.snap
··· 1590 1590 maxItems: 3 1591 1591 } as const; 1592 1592 1593 + export const $ModelWithPrefixItemsConstantSizeArray = { 1594 + type: 'array', 1595 + prefixItems: [ 1596 + { 1597 + '$ref': '#/components/schemas/ModelWithInteger' 1598 + }, 1599 + { 1600 + oneOf: [ 1601 + { 1602 + type: 'number' 1603 + }, 1604 + { 1605 + type: 'string' 1606 + } 1607 + ] 1608 + }, 1609 + { 1610 + type: 'string' 1611 + } 1612 + ] 1613 + } as const; 1614 + 1593 1615 export const $ModelWithAnyOfConstantSizeArrayNullable = { 1594 1616 type: ['array'], 1595 1617 items: {
+6
packages/openapi-ts/test/__snapshots__/test/generated/v3_xhr/types.gen.ts.snap
··· 875 875 number | string 876 876 ]; 877 877 878 + export type ModelWithPrefixItemsConstantSizeArray = [ 879 + ModelWithInteger, 880 + number | string, 881 + string 882 + ]; 883 + 878 884 export type ModelWithAnyOfConstantSizeArrayNullable = [ 879 885 number | null | string, 880 886 number | null | string,
+4 -3
packages/openapi-ts/test/sample.cjs
··· 7 7 input: './test/spec/v3.json', 8 8 // input: 'https://mongodb-mms-prod-build-server.s3.amazonaws.com/openapi/2caffd88277a4e27c95dcefc7e3b6a63a3b03297-v2-2023-11-15.json', 9 9 output: { 10 - path: './test/generated/v3/', 10 + path: './test/generated/sample/', 11 11 }, 12 12 schemas: { 13 13 export: false, 14 14 }, 15 15 services: { 16 16 asClass: true, 17 - // export: false, 17 + export: false, 18 18 // name: '^Parameters', 19 19 }, 20 20 types: { 21 21 enums: 'typescript', 22 - // include: '^CloudProvider', 22 + include: 23 + '^ModelWithPrefixItemsConstantSizeArray|ModelWithAnyOfConstantSizeArray', 23 24 // name: 'PascalCase', 24 25 }, 25 26 // useOptions: false,
+21
packages/openapi-ts/test/spec/v3.json
··· 3184 3184 "minItems": 3, 3185 3185 "maxItems": 3 3186 3186 }, 3187 + "ModelWithPrefixItemsConstantSizeArray": { 3188 + "type": "array", 3189 + "prefixItems": [ 3190 + { 3191 + "$ref": "#/components/schemas/ModelWithInteger" 3192 + }, 3193 + { 3194 + "oneOf": [ 3195 + { 3196 + "type": "number" 3197 + }, 3198 + { 3199 + "type": "string" 3200 + } 3201 + ] 3202 + }, 3203 + { 3204 + "type": "string" 3205 + } 3206 + ] 3207 + }, 3187 3208 "ModelWithAnyOfConstantSizeArrayNullable": { 3188 3209 "type": ["array"], 3189 3210 "items": {