@gqlkit-ts/cli 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auto-type-generator/auto-type-generator.d.ts +7 -0
- package/dist/auto-type-generator/auto-type-generator.d.ts.map +1 -1
- package/dist/auto-type-generator/auto-type-generator.js +379 -56
- package/dist/auto-type-generator/auto-type-generator.js.map +1 -1
- package/dist/auto-type-generator/discriminator-field-validator.d.ts +26 -0
- package/dist/auto-type-generator/discriminator-field-validator.d.ts.map +1 -0
- package/dist/auto-type-generator/discriminator-field-validator.js +242 -0
- package/dist/auto-type-generator/discriminator-field-validator.js.map +1 -0
- package/dist/auto-type-generator/discriminator-naming.d.ts +11 -0
- package/dist/auto-type-generator/discriminator-naming.d.ts.map +1 -0
- package/dist/auto-type-generator/discriminator-naming.js +15 -0
- package/dist/auto-type-generator/discriminator-naming.js.map +1 -0
- package/dist/auto-type-generator/discriminator-resolve-type-generator.d.ts +44 -0
- package/dist/auto-type-generator/discriminator-resolve-type-generator.d.ts.map +1 -0
- package/dist/auto-type-generator/discriminator-resolve-type-generator.js +77 -0
- package/dist/auto-type-generator/discriminator-resolve-type-generator.js.map +1 -0
- package/dist/auto-type-generator/index.d.ts +3 -0
- package/dist/auto-type-generator/index.d.ts.map +1 -1
- package/dist/auto-type-generator/index.js +3 -0
- package/dist/auto-type-generator/index.js.map +1 -1
- package/dist/auto-type-generator/inline-enum-collector.d.ts.map +1 -1
- package/dist/auto-type-generator/inline-enum-collector.js +14 -7
- package/dist/auto-type-generator/inline-enum-collector.js.map +1 -1
- package/dist/auto-type-generator/inline-object-converter.d.ts +12 -0
- package/dist/auto-type-generator/inline-object-converter.d.ts.map +1 -0
- package/dist/auto-type-generator/inline-object-converter.js +72 -0
- package/dist/auto-type-generator/inline-object-converter.js.map +1 -0
- package/dist/auto-type-generator/inline-object-traverser.d.ts +2 -1
- package/dist/auto-type-generator/inline-object-traverser.d.ts.map +1 -1
- package/dist/auto-type-generator/inline-object-traverser.js +22 -4
- package/dist/auto-type-generator/inline-object-traverser.js.map +1 -1
- package/dist/auto-type-generator/inline-union-collector.d.ts.map +1 -1
- package/dist/auto-type-generator/inline-union-collector.js +20 -6
- package/dist/auto-type-generator/inline-union-collector.js.map +1 -1
- package/dist/auto-type-generator/inline-union-types.d.ts +2 -0
- package/dist/auto-type-generator/inline-union-types.d.ts.map +1 -1
- package/dist/auto-type-generator/inline-union-validator.js +3 -3
- package/dist/auto-type-generator/inline-union-validator.js.map +1 -1
- package/dist/auto-type-generator/intersection-flattener.d.ts +44 -0
- package/dist/auto-type-generator/intersection-flattener.d.ts.map +1 -0
- package/dist/auto-type-generator/intersection-flattener.js +398 -0
- package/dist/auto-type-generator/intersection-flattener.js.map +1 -0
- package/dist/auto-type-generator/naming-convention.d.ts +23 -2
- package/dist/auto-type-generator/naming-convention.d.ts.map +1 -1
- package/dist/auto-type-generator/naming-convention.js +145 -1
- package/dist/auto-type-generator/naming-convention.js.map +1 -1
- package/dist/auto-type-generator/resolver-field-iterator.d.ts +1 -1
- package/dist/auto-type-generator/resolver-field-iterator.d.ts.map +1 -1
- package/dist/auto-type-generator/resolver-field-iterator.js +3 -0
- package/dist/auto-type-generator/resolver-field-iterator.js.map +1 -1
- package/dist/auto-type-generator/typename-extractor.d.ts +2 -0
- package/dist/auto-type-generator/typename-extractor.d.ts.map +1 -1
- package/dist/auto-type-generator/typename-extractor.js +11 -3
- package/dist/auto-type-generator/typename-extractor.js.map +1 -1
- package/dist/auto-type-generator/typename-resolve-type-generator.d.ts +2 -0
- package/dist/auto-type-generator/typename-resolve-type-generator.d.ts.map +1 -1
- package/dist/auto-type-generator/typename-resolve-type-generator.js +12 -84
- package/dist/auto-type-generator/typename-resolve-type-generator.js.map +1 -1
- package/dist/auto-type-generator/typename-types.d.ts +4 -0
- package/dist/auto-type-generator/typename-types.d.ts.map +1 -1
- package/dist/auto-type-generator/typename-types.js +6 -0
- package/dist/auto-type-generator/typename-types.js.map +1 -1
- package/dist/auto-type-generator/typename-validator.d.ts.map +1 -1
- package/dist/auto-type-generator/typename-validator.js +4 -3
- package/dist/auto-type-generator/typename-validator.js.map +1 -1
- package/dist/commands/docs.d.ts +1 -0
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/gen.d.ts +1 -0
- package/dist/commands/gen.d.ts.map +1 -1
- package/dist/commands/gen.js +2 -1
- package/dist/commands/gen.js.map +1 -1
- package/dist/commands/main.d.ts +1 -0
- package/dist/commands/main.d.ts.map +1 -1
- package/dist/config/types.d.ts +7 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config-loader/index.d.ts +1 -1
- package/dist/config-loader/index.d.ts.map +1 -1
- package/dist/config-loader/index.js.map +1 -1
- package/dist/config-loader/loader.d.ts +6 -0
- package/dist/config-loader/loader.d.ts.map +1 -1
- package/dist/config-loader/loader.js +1 -0
- package/dist/config-loader/loader.js.map +1 -1
- package/dist/config-loader/validator.d.ts.map +1 -1
- package/dist/config-loader/validator.js +84 -1
- package/dist/config-loader/validator.js.map +1 -1
- package/dist/gen-orchestrator/orchestrator.d.ts +2 -1
- package/dist/gen-orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/gen-orchestrator/orchestrator.js +43 -3
- package/dist/gen-orchestrator/orchestrator.js.map +1 -1
- package/dist/resolver-extractor/extract-resolvers.d.ts +4 -0
- package/dist/resolver-extractor/extract-resolvers.d.ts.map +1 -1
- package/dist/resolver-extractor/extractor/define-api-extractor.d.ts +2 -1
- package/dist/resolver-extractor/extractor/define-api-extractor.d.ts.map +1 -1
- package/dist/resolver-extractor/extractor/define-api-extractor.js +35 -6
- package/dist/resolver-extractor/extractor/define-api-extractor.js.map +1 -1
- package/dist/resolver-extractor/index.d.ts +1 -1
- package/dist/resolver-extractor/index.d.ts.map +1 -1
- package/dist/resolver-extractor/validator/abstract-resolver-validator.d.ts +2 -0
- package/dist/resolver-extractor/validator/abstract-resolver-validator.d.ts.map +1 -1
- package/dist/resolver-extractor/validator/abstract-resolver-validator.js +16 -3
- package/dist/resolver-extractor/validator/abstract-resolver-validator.js.map +1 -1
- package/dist/schema-generator/emitter/code-emitter.d.ts.map +1 -1
- package/dist/schema-generator/emitter/code-emitter.js +24 -4
- package/dist/schema-generator/emitter/code-emitter.js.map +1 -1
- package/dist/schema-generator/emitter/discriminator-resolve-type-emitter.d.ts +18 -0
- package/dist/schema-generator/emitter/discriminator-resolve-type-emitter.d.ts.map +1 -0
- package/dist/schema-generator/emitter/discriminator-resolve-type-emitter.js +89 -0
- package/dist/schema-generator/emitter/discriminator-resolve-type-emitter.js.map +1 -0
- package/dist/schema-generator/generate-schema.d.ts +2 -0
- package/dist/schema-generator/generate-schema.d.ts.map +1 -1
- package/dist/schema-generator/generate-schema.js +69 -10
- package/dist/schema-generator/generate-schema.js.map +1 -1
- package/dist/schema-generator/integrator/result-integrator.d.ts +5 -0
- package/dist/schema-generator/integrator/result-integrator.d.ts.map +1 -1
- package/dist/schema-generator/integrator/result-integrator.js +44 -3
- package/dist/schema-generator/integrator/result-integrator.js.map +1 -1
- package/dist/schema-generator/resolver-collector/resolver-collector.d.ts +2 -0
- package/dist/schema-generator/resolver-collector/resolver-collector.d.ts.map +1 -1
- package/dist/schema-generator/resolver-collector/resolver-collector.js +4 -0
- package/dist/schema-generator/resolver-collector/resolver-collector.js.map +1 -1
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +14 -1
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/enum-prefix-detector.d.ts.map +1 -1
- package/dist/shared/enum-prefix-detector.js +78 -8
- package/dist/shared/enum-prefix-detector.js.map +1 -1
- package/dist/shared/inline-object-utils.js +1 -1
- package/dist/shared/inline-object-utils.js.map +1 -1
- package/dist/shared/type-converter.d.ts.map +1 -1
- package/dist/shared/type-converter.js +55 -0
- package/dist/shared/type-converter.js.map +1 -1
- package/dist/type-extractor/converter/graphql-converter.d.ts.map +1 -1
- package/dist/type-extractor/converter/graphql-converter.js +11 -1
- package/dist/type-extractor/converter/graphql-converter.js.map +1 -1
- package/dist/type-extractor/extractor/field-type-resolver.d.ts +18 -0
- package/dist/type-extractor/extractor/field-type-resolver.d.ts.map +1 -1
- package/dist/type-extractor/extractor/field-type-resolver.js +198 -15
- package/dist/type-extractor/extractor/field-type-resolver.js.map +1 -1
- package/dist/type-extractor/extractor/type-extractor.d.ts +1 -0
- package/dist/type-extractor/extractor/type-extractor.d.ts.map +1 -1
- package/dist/type-extractor/extractor/type-extractor.js +100 -9
- package/dist/type-extractor/extractor/type-extractor.js.map +1 -1
- package/dist/type-extractor/types/diagnostics.d.ts +1 -1
- package/dist/type-extractor/types/diagnostics.d.ts.map +1 -1
- package/dist/type-extractor/types/index.d.ts +1 -1
- package/dist/type-extractor/types/index.d.ts.map +1 -1
- package/dist/type-extractor/types/index.js +1 -1
- package/dist/type-extractor/types/index.js.map +1 -1
- package/dist/type-extractor/types/ts-type-reference-factory.d.ts +7 -1
- package/dist/type-extractor/types/ts-type-reference-factory.d.ts.map +1 -1
- package/dist/type-extractor/types/ts-type-reference-factory.js +18 -3
- package/dist/type-extractor/types/ts-type-reference-factory.js.map +1 -1
- package/dist/type-extractor/types/typescript.d.ts +3 -1
- package/dist/type-extractor/types/typescript.d.ts.map +1 -1
- package/dist/type-extractor/validator/type-validator.d.ts.map +1 -1
- package/dist/type-extractor/validator/type-validator.js +6 -1
- package/dist/type-extractor/validator/type-validator.js.map +1 -1
- package/docs/configuration.md +19 -0
- package/docs/getting-started.md +2 -1
- package/docs/index.md +2 -0
- package/docs/integration/ai-sdk.md +189 -0
- package/docs/schema/conventions.md +7 -0
- package/docs/schema/fields.md +15 -0
- package/docs/schema/queries-mutations.md +21 -2
- package/docs/schema/subscriptions.md +173 -0
- package/docs/schema/unions.md +117 -0
- package/package.json +4 -4
- package/src/auto-type-generator/auto-type-generator.ts +588 -62
- package/src/auto-type-generator/discriminator-field-validator.ts +368 -0
- package/src/auto-type-generator/discriminator-naming.ts +24 -0
- package/src/auto-type-generator/discriminator-resolve-type-generator.ts +136 -0
- package/src/auto-type-generator/index.ts +17 -0
- package/src/auto-type-generator/inline-enum-collector.ts +19 -4
- package/src/auto-type-generator/inline-object-converter.ts +100 -0
- package/src/auto-type-generator/inline-object-traverser.ts +33 -7
- package/src/auto-type-generator/inline-union-collector.ts +26 -4
- package/src/auto-type-generator/inline-union-types.ts +2 -0
- package/src/auto-type-generator/inline-union-validator.ts +3 -3
- package/src/auto-type-generator/intersection-flattener.ts +554 -0
- package/src/auto-type-generator/naming-convention.ts +207 -3
- package/src/auto-type-generator/resolver-field-iterator.ts +5 -1
- package/src/auto-type-generator/typename-extractor.ts +17 -3
- package/src/auto-type-generator/typename-resolve-type-generator.ts +19 -108
- package/src/auto-type-generator/typename-types.ts +7 -0
- package/src/auto-type-generator/typename-validator.ts +4 -3
- package/src/commands/gen.ts +9 -2
- package/src/config/types.ts +10 -0
- package/src/config-loader/index.ts +1 -0
- package/src/config-loader/loader.ts +11 -0
- package/src/config-loader/validator.ts +100 -1
- package/src/gen-orchestrator/orchestrator.ts +50 -3
- package/src/resolver-extractor/extract-resolvers.ts +5 -0
- package/src/resolver-extractor/extractor/define-api-extractor.ts +47 -7
- package/src/resolver-extractor/index.ts +1 -0
- package/src/resolver-extractor/validator/abstract-resolver-validator.ts +20 -6
- package/src/schema-generator/emitter/code-emitter.ts +43 -5
- package/src/schema-generator/emitter/discriminator-resolve-type-emitter.ts +125 -0
- package/src/schema-generator/generate-schema.ts +100 -13
- package/src/schema-generator/integrator/result-integrator.ts +55 -2
- package/src/schema-generator/resolver-collector/resolver-collector.ts +7 -0
- package/src/shared/constants.ts +15 -1
- package/src/shared/enum-prefix-detector.ts +96 -8
- package/src/shared/inline-object-utils.ts +1 -1
- package/src/shared/type-converter.ts +63 -0
- package/src/type-extractor/converter/graphql-converter.ts +17 -1
- package/src/type-extractor/extractor/field-type-resolver.ts +241 -16
- package/src/type-extractor/extractor/type-extractor.ts +119 -5
- package/src/type-extractor/types/diagnostics.ts +10 -1
- package/src/type-extractor/types/index.ts +2 -1
- package/src/type-extractor/types/ts-type-reference-factory.ts +24 -3
- package/src/type-extractor/types/typescript.ts +6 -2
- package/src/type-extractor/validator/type-validator.ts +6 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DiscriminatorResolveTypeInfo,
|
|
3
|
+
DiscriminatorValueMapping,
|
|
4
|
+
} from "../../auto-type-generator/discriminator-resolve-type-generator.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Groups value mappings by their value at a given field index.
|
|
8
|
+
* Null values at the field index mean the member doesn't have that field,
|
|
9
|
+
* so the member should be resolved directly at the current switch level.
|
|
10
|
+
*/
|
|
11
|
+
function groupMappingsByFieldValue(
|
|
12
|
+
mappings: ReadonlyArray<DiscriminatorValueMapping>,
|
|
13
|
+
fieldIndex: number,
|
|
14
|
+
): Map<string | null, DiscriminatorValueMapping[]> {
|
|
15
|
+
const groups = new Map<string | null, DiscriminatorValueMapping[]>();
|
|
16
|
+
for (const mapping of mappings) {
|
|
17
|
+
const value = mapping.values[fieldIndex] ?? null;
|
|
18
|
+
const group = groups.get(value) ?? [];
|
|
19
|
+
group.push(mapping);
|
|
20
|
+
groups.set(value, group);
|
|
21
|
+
}
|
|
22
|
+
return groups;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Builds a switch statement body for a given field level, recursing for nested fields.
|
|
27
|
+
* Returns an array of code lines (without leading indentation for the switch itself).
|
|
28
|
+
*/
|
|
29
|
+
function buildSwitchBody(
|
|
30
|
+
mappings: ReadonlyArray<DiscriminatorValueMapping>,
|
|
31
|
+
fieldNames: ReadonlyArray<string>,
|
|
32
|
+
fieldIndex: number,
|
|
33
|
+
indent: string,
|
|
34
|
+
): string[] {
|
|
35
|
+
const fieldName = fieldNames[fieldIndex]!;
|
|
36
|
+
const groups = groupMappingsByFieldValue(mappings, fieldIndex);
|
|
37
|
+
const lines: string[] = [];
|
|
38
|
+
|
|
39
|
+
lines.push(`${indent}switch (obj.${fieldName}) {`);
|
|
40
|
+
|
|
41
|
+
for (const [value, groupMappings] of groups) {
|
|
42
|
+
// Null value means the member doesn't have this field — resolved at the parent level
|
|
43
|
+
if (value === null) {
|
|
44
|
+
for (const mapping of groupMappings) {
|
|
45
|
+
lines.push(
|
|
46
|
+
`${indent} case "${mapping.values[fieldIndex - 1]!}": return "${mapping.memberGraphQLTypeName}";`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const nextFieldIndex = fieldIndex + 1;
|
|
53
|
+
const hasMoreFields = nextFieldIndex < fieldNames.length;
|
|
54
|
+
|
|
55
|
+
// Check if all mappings in this group can be resolved directly
|
|
56
|
+
// (either no more fields, or only one mapping, or all remaining values are null)
|
|
57
|
+
const canResolveDirectly =
|
|
58
|
+
groupMappings.length === 1 &&
|
|
59
|
+
(!hasMoreFields || groupMappings[0]!.values[nextFieldIndex] === null);
|
|
60
|
+
|
|
61
|
+
if (!hasMoreFields || canResolveDirectly) {
|
|
62
|
+
// Single mapping or no more fields: emit direct return
|
|
63
|
+
if (groupMappings.length === 1) {
|
|
64
|
+
lines.push(
|
|
65
|
+
`${indent} case "${value}": return "${groupMappings[0]!.memberGraphQLTypeName}";`,
|
|
66
|
+
);
|
|
67
|
+
} else {
|
|
68
|
+
// Multiple mappings at the leaf level — shouldn't happen with unique tuples
|
|
69
|
+
for (const mapping of groupMappings) {
|
|
70
|
+
lines.push(
|
|
71
|
+
`${indent} case "${value}": return "${mapping.memberGraphQLTypeName}";`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
// Need nested switch for remaining fields
|
|
77
|
+
lines.push(`${indent} case "${value}":`);
|
|
78
|
+
const nestedLines = buildSwitchBody(
|
|
79
|
+
groupMappings,
|
|
80
|
+
fieldNames,
|
|
81
|
+
nextFieldIndex,
|
|
82
|
+
`${indent} `,
|
|
83
|
+
);
|
|
84
|
+
lines.push(...nestedLines);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
lines.push(`${indent} default: return undefined;`);
|
|
89
|
+
lines.push(`${indent}}`);
|
|
90
|
+
|
|
91
|
+
return lines;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function buildObjTypeAnnotation(fieldNames: ReadonlyArray<string>): string {
|
|
95
|
+
const fields = fieldNames.map((name) => `${name}: string`);
|
|
96
|
+
return `{ ${fields.join("; ")} }`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Builds a resolver map entry for a discriminator-based __resolveType.
|
|
101
|
+
*
|
|
102
|
+
* For single discriminator field:
|
|
103
|
+
* TypeName: {
|
|
104
|
+
* __resolveType: (obj: { field: string }) => {
|
|
105
|
+
* switch (obj.field) {
|
|
106
|
+
* case "val": return "MemberType";
|
|
107
|
+
* default: return undefined;
|
|
108
|
+
* }
|
|
109
|
+
* },
|
|
110
|
+
* },
|
|
111
|
+
*
|
|
112
|
+
* For multiple discriminator fields, generates nested switch statements.
|
|
113
|
+
*/
|
|
114
|
+
export function buildDiscriminatorResolveTypeEntry(
|
|
115
|
+
info: DiscriminatorResolveTypeInfo,
|
|
116
|
+
): string {
|
|
117
|
+
const { unionTypeName, fieldNames, valueMappings } = info;
|
|
118
|
+
const objType = buildObjTypeAnnotation(fieldNames);
|
|
119
|
+
const baseIndent = " ";
|
|
120
|
+
|
|
121
|
+
const switchLines = buildSwitchBody(valueMappings, fieldNames, 0, baseIndent);
|
|
122
|
+
const switchBody = switchLines.join("\n");
|
|
123
|
+
|
|
124
|
+
return ` ${unionTypeName}: {\n __resolveType: (obj: ${objType}) => {\n${switchBody}\n },\n },`;
|
|
125
|
+
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
+
collectDiscriminatorResolveTypes,
|
|
2
3
|
collectTypenameExtractions,
|
|
3
4
|
collectTypenameResolveTypes,
|
|
5
|
+
flattenIntersectionMembers,
|
|
4
6
|
generateAutoTypes,
|
|
7
|
+
validateDiscriminatorFields,
|
|
5
8
|
validateNameCollisions,
|
|
6
9
|
validateSchemaTypenames,
|
|
7
10
|
validateTypenames,
|
|
8
11
|
} from "../auto-type-generator/index.js";
|
|
9
12
|
import type { ImportExtension } from "../config/types.js";
|
|
13
|
+
import type { ResolvedDiscriminatorFieldsMap } from "../config-loader/index.js";
|
|
10
14
|
import type { ExtractResolversResult } from "../resolver-extractor/index.js";
|
|
11
15
|
import type { DirectiveDefinitionInfo } from "../shared/directive-definition-extractor.js";
|
|
12
16
|
import type { CollectedScalarType } from "../type-extractor/collector/scalar-collector.js";
|
|
@@ -35,6 +39,7 @@ export interface GenerateSchemaInput {
|
|
|
35
39
|
readonly sourceRoot: string | null;
|
|
36
40
|
readonly knownTypeNames: ReadonlySet<string> | null;
|
|
37
41
|
readonly importExtension: ImportExtension;
|
|
42
|
+
readonly discriminatorFields: ResolvedDiscriminatorFieldsMap;
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
export interface GenerateSchemaResult {
|
|
@@ -67,6 +72,7 @@ export function generateSchema(
|
|
|
67
72
|
extractedTypes,
|
|
68
73
|
resolversResult,
|
|
69
74
|
knownTypeNames: knownTypeNames ?? new Set(),
|
|
75
|
+
discriminatorFields: input.discriminatorFields,
|
|
70
76
|
});
|
|
71
77
|
|
|
72
78
|
const autoTypeErrors = autoTypeResult.diagnostics.filter(
|
|
@@ -105,11 +111,52 @@ export function generateSchema(
|
|
|
105
111
|
};
|
|
106
112
|
}
|
|
107
113
|
|
|
114
|
+
// Flatten intersection-expanded inline object members for discriminator unions.
|
|
115
|
+
// TypeScript distributes intersections over unions, creating duplicate members
|
|
116
|
+
// that must be merged before discriminator field validation.
|
|
117
|
+
const flattenedExtractedTypes = flattenIntersectionMembers({
|
|
118
|
+
extractedTypes: autoTypeResult.updatedExtractedTypes,
|
|
119
|
+
discriminatorFields: input.discriminatorFields,
|
|
120
|
+
});
|
|
121
|
+
|
|
108
122
|
// Re-convert to GraphQL types using updated extracted types
|
|
109
123
|
// This resolves __INLINE_OBJECT__ references to auto-generated type names
|
|
110
|
-
const updatedConversionResult = convertToGraphQL(
|
|
111
|
-
|
|
124
|
+
const updatedConversionResult = convertToGraphQL(flattenedExtractedTypes);
|
|
125
|
+
const typeMap = new Map(
|
|
126
|
+
flattenedExtractedTypes.map((t) => [t.metadata.name, t]),
|
|
112
127
|
);
|
|
128
|
+
|
|
129
|
+
// Validate discriminator fields against extracted types
|
|
130
|
+
const inlineDiscriminatorUnionNames = new Set(
|
|
131
|
+
autoTypeResult.inlineDiscriminatorResolveTypes.map(
|
|
132
|
+
(rt) => rt.unionTypeName,
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
const discriminatorValidationResult = validateDiscriminatorFields({
|
|
136
|
+
discriminatorFields: input.discriminatorFields,
|
|
137
|
+
extractedTypes: flattenedExtractedTypes,
|
|
138
|
+
typeMap,
|
|
139
|
+
inlineDiscriminatorUnionNames,
|
|
140
|
+
});
|
|
141
|
+
const discriminatorErrors = discriminatorValidationResult.diagnostics.filter(
|
|
142
|
+
(d) => d.severity === "error",
|
|
143
|
+
);
|
|
144
|
+
if (discriminatorErrors.length > 0) {
|
|
145
|
+
return {
|
|
146
|
+
typeDefsCode: "",
|
|
147
|
+
sdlContent: "",
|
|
148
|
+
resolversCode: "",
|
|
149
|
+
diagnostics: discriminatorValidationResult.diagnostics,
|
|
150
|
+
hasErrors: true,
|
|
151
|
+
prunedTypes: null,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const discriminatorWarnings =
|
|
156
|
+
discriminatorValidationResult.diagnostics.filter(
|
|
157
|
+
(d) => d.severity === "warning",
|
|
158
|
+
);
|
|
159
|
+
|
|
113
160
|
const updatedTypesResult: ExtractTypesResult = {
|
|
114
161
|
types: updatedConversionResult.types,
|
|
115
162
|
diagnostics: {
|
|
@@ -126,29 +173,48 @@ export function generateSchema(
|
|
|
126
173
|
(d) => d.severity === "warning",
|
|
127
174
|
),
|
|
128
175
|
...autoTypeResult.diagnostics.filter((d) => d.severity === "warning"),
|
|
176
|
+
...discriminatorWarnings,
|
|
129
177
|
],
|
|
130
178
|
},
|
|
131
179
|
};
|
|
132
180
|
|
|
133
|
-
const typeMap = new Map(
|
|
134
|
-
autoTypeResult.updatedExtractedTypes.map((t) => [t.metadata.name, t]),
|
|
135
|
-
);
|
|
136
|
-
|
|
137
181
|
const manualResolveTypeNames = new Set(
|
|
138
182
|
autoTypeResult.updatedResolversResult.abstractTypeResolvers
|
|
139
183
|
.filter((r) => r.kind === "resolveType")
|
|
140
184
|
.map((r) => r.targetTypeName),
|
|
141
185
|
);
|
|
142
186
|
|
|
187
|
+
// Collect discriminator resolve types from validated entries
|
|
188
|
+
const discriminatorResolveTypesResult = collectDiscriminatorResolveTypes({
|
|
189
|
+
validatedEntries: discriminatorValidationResult.validatedEntries,
|
|
190
|
+
manualResolveTypeNames,
|
|
191
|
+
extractedTypes: flattenedExtractedTypes,
|
|
192
|
+
typeMap,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Merge inline discriminator resolveTypes from auto-type generation
|
|
196
|
+
// (for inline unions flattened by discriminator fields, e.g. UIMessagePart<...>[])
|
|
197
|
+
const mergedDiscriminatorResolveTypes = [
|
|
198
|
+
...discriminatorResolveTypesResult.discriminatorResolveTypes,
|
|
199
|
+
...autoTypeResult.inlineDiscriminatorResolveTypes,
|
|
200
|
+
];
|
|
201
|
+
// discriminatorResolveTypeNames are derived by integrate() from the merged list
|
|
202
|
+
|
|
203
|
+
const discriminatorFieldUnionNames = new Set(
|
|
204
|
+
input.discriminatorFields.keys(),
|
|
205
|
+
);
|
|
206
|
+
|
|
143
207
|
const typenameResolveTypesResult = collectTypenameResolveTypes({
|
|
144
|
-
extractedTypes:
|
|
208
|
+
extractedTypes: flattenedExtractedTypes,
|
|
145
209
|
typeMap,
|
|
146
210
|
manualResolveTypeNames,
|
|
211
|
+
discriminatorFieldUnionNames,
|
|
147
212
|
});
|
|
148
213
|
|
|
149
214
|
const typenameExtractions = collectTypenameExtractions({
|
|
150
|
-
extractedTypes:
|
|
215
|
+
extractedTypes: flattenedExtractedTypes,
|
|
151
216
|
typeMap,
|
|
217
|
+
discriminatorFieldUnionNames,
|
|
152
218
|
});
|
|
153
219
|
|
|
154
220
|
const typenameValidationDiagnostics: Diagnostic[] = [];
|
|
@@ -167,7 +233,7 @@ export function generateSchema(
|
|
|
167
233
|
}
|
|
168
234
|
|
|
169
235
|
const schemaTypenameValidationResult = validateSchemaTypenames({
|
|
170
|
-
objectTypes:
|
|
236
|
+
objectTypes: flattenedExtractedTypes,
|
|
171
237
|
typeMap,
|
|
172
238
|
});
|
|
173
239
|
typenameValidationDiagnostics.push(
|
|
@@ -188,20 +254,38 @@ export function generateSchema(
|
|
|
188
254
|
const generatedInlineObjectTypes =
|
|
189
255
|
typenameResolveTypesResult.generatedInlineObjectTypes;
|
|
190
256
|
|
|
257
|
+
// Collect all generated object types that need to be added as union members
|
|
258
|
+
// (both typename-based and discriminator-based inline objects)
|
|
259
|
+
const discriminatorGeneratedObjectTypes =
|
|
260
|
+
discriminatorResolveTypesResult.generatedObjectTypes;
|
|
261
|
+
|
|
191
262
|
const updatedTypesForIntegration: ExtractTypesResult =
|
|
192
|
-
generatedInlineObjectTypes.length > 0
|
|
263
|
+
generatedInlineObjectTypes.length > 0 ||
|
|
264
|
+
discriminatorGeneratedObjectTypes.length > 0
|
|
193
265
|
? {
|
|
194
266
|
types: updatedTypesResult.types.map((type) => {
|
|
195
267
|
if (type.kind !== "Union") {
|
|
196
268
|
return type;
|
|
197
269
|
}
|
|
198
|
-
const
|
|
270
|
+
const typenameInlineTypes = generatedInlineObjectTypes.filter(
|
|
199
271
|
(g) => g.abstractTypeName === type.name,
|
|
200
272
|
);
|
|
201
|
-
|
|
273
|
+
const discriminatorInlineTypes =
|
|
274
|
+
discriminatorGeneratedObjectTypes.filter(
|
|
275
|
+
(g) =>
|
|
276
|
+
g.generatedFrom !== null &&
|
|
277
|
+
g.generatedFrom.parentTypeName === type.name,
|
|
278
|
+
);
|
|
279
|
+
if (
|
|
280
|
+
typenameInlineTypes.length === 0 &&
|
|
281
|
+
discriminatorInlineTypes.length === 0
|
|
282
|
+
) {
|
|
202
283
|
return type;
|
|
203
284
|
}
|
|
204
|
-
const newMembers =
|
|
285
|
+
const newMembers = [
|
|
286
|
+
...typenameInlineTypes.map((g) => g.typeName),
|
|
287
|
+
...discriminatorInlineTypes.map((g) => g.name),
|
|
288
|
+
];
|
|
205
289
|
return {
|
|
206
290
|
...type,
|
|
207
291
|
unionMembers: [
|
|
@@ -217,6 +301,7 @@ export function generateSchema(
|
|
|
217
301
|
const allAutoGeneratedTypes = [
|
|
218
302
|
...autoTypeResult.autoGeneratedTypes,
|
|
219
303
|
...typenameResolveTypesResult.generatedObjectTypes,
|
|
304
|
+
...discriminatorGeneratedObjectTypes,
|
|
220
305
|
];
|
|
221
306
|
|
|
222
307
|
const integratedResult = integrate({
|
|
@@ -227,6 +312,8 @@ export function generateSchema(
|
|
|
227
312
|
directiveDefinitions,
|
|
228
313
|
autoGeneratedTypes: allAutoGeneratedTypes,
|
|
229
314
|
typenameAutoResolveTypes: typenameResolveTypesResult.autoResolveTypes,
|
|
315
|
+
discriminatorResolveTypes: mergedDiscriminatorResolveTypes,
|
|
316
|
+
tsAliasToGraphQLNameMap: autoTypeResult.tsAliasToGraphQLNameMap,
|
|
230
317
|
});
|
|
231
318
|
|
|
232
319
|
let documentNode = buildDocumentNode(integratedResult, { sourceRoot });
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DiscriminatorResolveTypeInfo } from "../../auto-type-generator/discriminator-resolve-type-generator.js";
|
|
1
2
|
import type {
|
|
2
3
|
AutoGeneratedType,
|
|
3
4
|
ResolveTypeFieldPattern,
|
|
@@ -141,10 +142,12 @@ export interface IntegratedResult {
|
|
|
141
142
|
readonly abstractTypeResolvers: ReadonlyArray<AbstractResolverInfo>;
|
|
142
143
|
readonly autoGeneratedUnions: ReadonlyArray<AutoGeneratedUnionInfo>;
|
|
143
144
|
readonly typenameAutoResolveTypes: ReadonlyArray<TypenameAutoResolveTypeInfo>;
|
|
145
|
+
readonly discriminatorResolveTypes: ReadonlyArray<DiscriminatorResolveTypeInfo>;
|
|
144
146
|
readonly numericEnums: ReadonlyArray<NumericEnumInfo>;
|
|
145
147
|
readonly stringEnumMappings: ReadonlyArray<StringEnumMappingInfo>;
|
|
146
148
|
readonly hasQuery: boolean;
|
|
147
149
|
readonly hasMutation: boolean;
|
|
150
|
+
readonly hasSubscription: boolean;
|
|
148
151
|
readonly hasErrors: boolean;
|
|
149
152
|
readonly diagnostics: ReadonlyArray<Diagnostic>;
|
|
150
153
|
}
|
|
@@ -275,6 +278,8 @@ export interface IntegrateParams {
|
|
|
275
278
|
readonly directiveDefinitions: ReadonlyArray<DirectiveDefinitionInfo> | null;
|
|
276
279
|
readonly autoGeneratedTypes: ReadonlyArray<AutoGeneratedType> | null;
|
|
277
280
|
readonly typenameAutoResolveTypes: ReadonlyArray<TypenameAutoResolveTypeInfo> | null;
|
|
281
|
+
readonly discriminatorResolveTypes: ReadonlyArray<DiscriminatorResolveTypeInfo> | null;
|
|
282
|
+
readonly tsAliasToGraphQLNameMap: ReadonlyMap<string, string> | null;
|
|
278
283
|
}
|
|
279
284
|
|
|
280
285
|
export function integrate(params: IntegrateParams): IntegratedResult {
|
|
@@ -286,6 +291,8 @@ export function integrate(params: IntegrateParams): IntegratedResult {
|
|
|
286
291
|
directiveDefinitions,
|
|
287
292
|
autoGeneratedTypes,
|
|
288
293
|
typenameAutoResolveTypes,
|
|
294
|
+
discriminatorResolveTypes,
|
|
295
|
+
tsAliasToGraphQLNameMap,
|
|
289
296
|
} = params;
|
|
290
297
|
const directiveTypeAliasNames = new Set(
|
|
291
298
|
directiveDefinitions?.map((d) => d.typeAliasName) ?? [],
|
|
@@ -469,8 +476,10 @@ export function integrate(params: IntegrateParams): IntegratedResult {
|
|
|
469
476
|
|
|
470
477
|
const hasQuery = resolversResult.queryFields.fields.length > 0;
|
|
471
478
|
const hasMutation = resolversResult.mutationFields.fields.length > 0;
|
|
479
|
+
const hasSubscription = resolversResult.subscriptionFields.fields.length > 0;
|
|
472
480
|
|
|
473
|
-
|
|
481
|
+
// GraphQL spec requires Query root type even when only Subscription/Mutation are defined
|
|
482
|
+
if (hasQuery || hasMutation || hasSubscription) {
|
|
474
483
|
baseTypes.push({
|
|
475
484
|
name: "Query",
|
|
476
485
|
kind: "Object",
|
|
@@ -502,21 +511,54 @@ export function integrate(params: IntegrateParams): IntegratedResult {
|
|
|
502
511
|
directives: null,
|
|
503
512
|
});
|
|
504
513
|
}
|
|
514
|
+
if (hasSubscription) {
|
|
515
|
+
baseTypes.push({
|
|
516
|
+
name: "Subscription",
|
|
517
|
+
kind: "Object",
|
|
518
|
+
fields: [],
|
|
519
|
+
unionMembers: null,
|
|
520
|
+
enumValues: null,
|
|
521
|
+
isNumericEnum: false,
|
|
522
|
+
needsStringEnumMapping: false,
|
|
523
|
+
implementedInterfaces: null,
|
|
524
|
+
description: null,
|
|
525
|
+
deprecated: null,
|
|
526
|
+
sourceFile: null,
|
|
527
|
+
directives: null,
|
|
528
|
+
});
|
|
529
|
+
}
|
|
505
530
|
|
|
506
531
|
const typenameAutoResolveTypeNames = new Set([
|
|
507
532
|
...(typenameAutoResolveTypes?.map((t) => t.abstractTypeName) ?? []),
|
|
508
533
|
...autoGeneratedUnions
|
|
509
534
|
.filter((u) => u.hasAutoResolveType)
|
|
510
535
|
.map((u) => u.name),
|
|
536
|
+
...(discriminatorResolveTypes?.map((t) => t.unionTypeName) ?? []),
|
|
511
537
|
]);
|
|
512
538
|
|
|
539
|
+
const aliasMap = tsAliasToGraphQLNameMap ?? new Map<string, string>();
|
|
540
|
+
|
|
513
541
|
const abstractResolverValidation = validateAbstractResolvers({
|
|
514
542
|
abstractResolvers: resolversResult.abstractTypeResolvers,
|
|
515
543
|
baseTypes,
|
|
516
544
|
typenameAutoResolveTypeNames,
|
|
545
|
+
tsAliasToGraphQLNameMap: aliasMap,
|
|
517
546
|
});
|
|
518
547
|
diagnostics.push(...abstractResolverValidation.diagnostics);
|
|
519
548
|
|
|
549
|
+
// Remap abstract resolver target type names from TS aliases to GraphQL names
|
|
550
|
+
// Only remap resolveType resolvers — isTypeOf targets object types, not unions
|
|
551
|
+
const remappedAbstractResolvers =
|
|
552
|
+
aliasMap.size > 0
|
|
553
|
+
? resolversResult.abstractTypeResolvers.map((resolver) => {
|
|
554
|
+
if (resolver.kind !== "resolveType") return resolver;
|
|
555
|
+
const mappedName = aliasMap.get(resolver.targetTypeName);
|
|
556
|
+
return mappedName !== undefined
|
|
557
|
+
? { ...resolver, targetTypeName: mappedName }
|
|
558
|
+
: resolver;
|
|
559
|
+
})
|
|
560
|
+
: resolversResult.abstractTypeResolvers;
|
|
561
|
+
|
|
520
562
|
const knownTypeNames = new Set([
|
|
521
563
|
...baseTypes.map((t) => t.name),
|
|
522
564
|
...inputTypes.map((t) => t.name),
|
|
@@ -540,6 +582,15 @@ export function integrate(params: IntegrateParams): IntegratedResult {
|
|
|
540
582
|
});
|
|
541
583
|
}
|
|
542
584
|
|
|
585
|
+
if (hasSubscription) {
|
|
586
|
+
typeExtensions.push({
|
|
587
|
+
targetTypeName: "Subscription",
|
|
588
|
+
fields: resolversResult.subscriptionFields.fields.map(
|
|
589
|
+
convertToExtensionField,
|
|
590
|
+
),
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
|
|
543
594
|
for (const ext of resolversResult.typeExtensions) {
|
|
544
595
|
if (!knownTypeNames.has(ext.targetTypeName)) {
|
|
545
596
|
const firstField = ext.fields[0];
|
|
@@ -705,13 +756,15 @@ export function integrate(params: IntegrateParams): IntegratedResult {
|
|
|
705
756
|
directiveDefinitions && directiveDefinitions.length > 0
|
|
706
757
|
? directiveDefinitions
|
|
707
758
|
: null,
|
|
708
|
-
abstractTypeResolvers:
|
|
759
|
+
abstractTypeResolvers: remappedAbstractResolvers,
|
|
709
760
|
autoGeneratedUnions,
|
|
710
761
|
typenameAutoResolveTypes: typenameAutoResolveTypes ?? [],
|
|
762
|
+
discriminatorResolveTypes: discriminatorResolveTypes ?? [],
|
|
711
763
|
numericEnums,
|
|
712
764
|
stringEnumMappings,
|
|
713
765
|
hasQuery,
|
|
714
766
|
hasMutation,
|
|
767
|
+
hasSubscription,
|
|
715
768
|
hasErrors,
|
|
716
769
|
diagnostics,
|
|
717
770
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DiscriminatorResolveTypeInfo } from "../../auto-type-generator/discriminator-resolve-type-generator.js";
|
|
1
2
|
import type { AutoGeneratedResolveType } from "../../auto-type-generator/resolve-type-generator.js";
|
|
2
3
|
import type { AbstractResolverInfo } from "../../resolver-extractor/extractor/define-api-extractor.js";
|
|
3
4
|
import type {
|
|
@@ -30,6 +31,7 @@ export interface ResolverInfo {
|
|
|
30
31
|
readonly sourceFiles: ReadonlyArray<string>;
|
|
31
32
|
readonly abstractTypeResolvers: ReadonlyArray<AbstractTypeResolverInfo>;
|
|
32
33
|
readonly autoGeneratedResolveTypes: ReadonlyArray<AutoGeneratedResolveType>;
|
|
34
|
+
readonly discriminatorResolveTypes: ReadonlyArray<DiscriminatorResolveTypeInfo>;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
function getResolverValueName(typeName: string): string {
|
|
@@ -148,10 +150,15 @@ export function collectResolverInfo(
|
|
|
148
150
|
|
|
149
151
|
const sourceFiles = [...sourceFilesSet].sort((a, b) => a.localeCompare(b));
|
|
150
152
|
|
|
153
|
+
const discriminatorResolveTypes = [
|
|
154
|
+
...integratedResult.discriminatorResolveTypes,
|
|
155
|
+
].sort((a, b) => a.unionTypeName.localeCompare(b.unionTypeName));
|
|
156
|
+
|
|
151
157
|
return {
|
|
152
158
|
types,
|
|
153
159
|
sourceFiles,
|
|
154
160
|
abstractTypeResolvers,
|
|
155
161
|
autoGeneratedResolveTypes,
|
|
162
|
+
discriminatorResolveTypes,
|
|
156
163
|
};
|
|
157
164
|
}
|
package/src/shared/constants.ts
CHANGED
|
@@ -39,6 +39,19 @@ const TS_ANONYMOUS_TYPE_SYMBOL = "__type";
|
|
|
39
39
|
*/
|
|
40
40
|
const TS_ARRAY_TYPE_SYMBOL = "Array";
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Object literal type symbol name.
|
|
44
|
+
*
|
|
45
|
+
* TypeScript uses `__object` as the internal symbol name for object literal
|
|
46
|
+
* expressions (value-space). This differs from `__type` which is used for
|
|
47
|
+
* anonymous object types in type-space.
|
|
48
|
+
*
|
|
49
|
+
* This commonly occurs when a type is derived via `typeof` from a variable
|
|
50
|
+
* initialized with an object literal, especially when the value comes from
|
|
51
|
+
* an external library.
|
|
52
|
+
*/
|
|
53
|
+
const TS_OBJECT_LITERAL_TYPE_SYMBOL = "__object";
|
|
54
|
+
|
|
42
55
|
/**
|
|
43
56
|
* Checks if a symbol name represents a TypeScript internal/anonymous type
|
|
44
57
|
* that should not be used directly as a GraphQL type name.
|
|
@@ -49,7 +62,8 @@ const TS_ARRAY_TYPE_SYMBOL = "Array";
|
|
|
49
62
|
export function isInternalTypeSymbol(symbolName: string): boolean {
|
|
50
63
|
return (
|
|
51
64
|
symbolName === TS_ANONYMOUS_TYPE_SYMBOL ||
|
|
52
|
-
symbolName === TS_ARRAY_TYPE_SYMBOL
|
|
65
|
+
symbolName === TS_ARRAY_TYPE_SYMBOL ||
|
|
66
|
+
symbolName === TS_OBJECT_LITERAL_TYPE_SYMBOL
|
|
53
67
|
);
|
|
54
68
|
}
|
|
55
69
|
|
|
@@ -32,6 +32,86 @@ export function buildEnumPrefixCandidate(enumName: string): string {
|
|
|
32
32
|
return `${toUpperSnakeCase(enumName)}_`;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
const IRREGULAR_PLURAL_SEGMENTS = new Map([
|
|
36
|
+
["ALIAS", "ALIASES"],
|
|
37
|
+
["ANALYSIS", "ANALYSES"],
|
|
38
|
+
["CHILD", "CHILDREN"],
|
|
39
|
+
["COOKIE", "COOKIES"],
|
|
40
|
+
["CRISIS", "CRISES"],
|
|
41
|
+
["DIAGNOSIS", "DIAGNOSES"],
|
|
42
|
+
["FOOT", "FEET"],
|
|
43
|
+
["GOOSE", "GEESE"],
|
|
44
|
+
["MAN", "MEN"],
|
|
45
|
+
["MOUSE", "MICE"],
|
|
46
|
+
["MOVIE", "MOVIES"],
|
|
47
|
+
["PERSON", "PEOPLE"],
|
|
48
|
+
["THESIS", "THESES"],
|
|
49
|
+
["TOOTH", "TEETH"],
|
|
50
|
+
["WOMAN", "WOMEN"],
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
function isConsonant(char: string): boolean {
|
|
54
|
+
return /^[BCDFGHJKLMNPQRSTVWXYZ]$/.test(char);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function pluralizeUpperSnakeSegment(segment: string): string {
|
|
58
|
+
const irregularPlural = IRREGULAR_PLURAL_SEGMENTS.get(segment);
|
|
59
|
+
if (irregularPlural) {
|
|
60
|
+
return irregularPlural;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
segment.endsWith("Y") &&
|
|
65
|
+
segment.length > 1 &&
|
|
66
|
+
isConsonant(segment.at(-2) ?? "")
|
|
67
|
+
) {
|
|
68
|
+
return `${segment.slice(0, -1)}IES`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (
|
|
72
|
+
segment.endsWith("S") ||
|
|
73
|
+
segment.endsWith("SH") ||
|
|
74
|
+
segment.endsWith("CH") ||
|
|
75
|
+
segment.endsWith("X") ||
|
|
76
|
+
segment.endsWith("Z")
|
|
77
|
+
) {
|
|
78
|
+
return `${segment}ES`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return `${segment}S`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function buildEnumPrefixCandidates(enumName: string): string[] {
|
|
85
|
+
const upperSnakeName = toUpperSnakeCase(enumName);
|
|
86
|
+
const baseCandidate = `${upperSnakeName}_`;
|
|
87
|
+
const segments = upperSnakeName.split("_");
|
|
88
|
+
const candidateSegmentSets: string[][] = [segments];
|
|
89
|
+
|
|
90
|
+
if (segments.length === 0) {
|
|
91
|
+
return [baseCandidate];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (const [index, segment] of segments.entries()) {
|
|
95
|
+
const pluralizedSegment = pluralizeUpperSnakeSegment(segment);
|
|
96
|
+
if (pluralizedSegment === segment) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const existingCandidates = [...candidateSegmentSets];
|
|
101
|
+
for (const candidateSegments of existingCandidates) {
|
|
102
|
+
const nextCandidateSegments = [...candidateSegments];
|
|
103
|
+
nextCandidateSegments[index] = pluralizedSegment;
|
|
104
|
+
candidateSegmentSets.push(nextCandidateSegments);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return [
|
|
109
|
+
...new Set(
|
|
110
|
+
candidateSegmentSets.map((candidate) => `${candidate.join("_")}_`),
|
|
111
|
+
),
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
|
|
35
115
|
export interface DetectEnumPrefixParams {
|
|
36
116
|
readonly enumName: string;
|
|
37
117
|
readonly memberValues: ReadonlyArray<string>;
|
|
@@ -71,20 +151,28 @@ export function detectEnumPrefix(
|
|
|
71
151
|
return { shouldStrip: false, prefix: null };
|
|
72
152
|
}
|
|
73
153
|
|
|
74
|
-
const prefixCandidate
|
|
154
|
+
for (const prefixCandidate of buildEnumPrefixCandidates(enumName)) {
|
|
155
|
+
let matches = true;
|
|
156
|
+
|
|
157
|
+
for (const value of memberValues) {
|
|
158
|
+
if (!value.startsWith(prefixCandidate)) {
|
|
159
|
+
matches = false;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
75
162
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
163
|
+
const stripped = value.slice(prefixCandidate.length);
|
|
164
|
+
if (stripped === "") {
|
|
165
|
+
matches = false;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
79
168
|
}
|
|
80
169
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return { shouldStrip: false, prefix: null };
|
|
170
|
+
if (matches) {
|
|
171
|
+
return { shouldStrip: true, prefix: prefixCandidate };
|
|
84
172
|
}
|
|
85
173
|
}
|
|
86
174
|
|
|
87
|
-
return { shouldStrip:
|
|
175
|
+
return { shouldStrip: false, prefix: null };
|
|
88
176
|
}
|
|
89
177
|
|
|
90
178
|
/**
|
|
@@ -12,7 +12,7 @@ export function isInlineObjectType(type: ts.Type): boolean {
|
|
|
12
12
|
return false;
|
|
13
13
|
}
|
|
14
14
|
const symbolName = type.symbol.getName();
|
|
15
|
-
if (symbolName !== "__type") {
|
|
15
|
+
if (symbolName !== "__type" && symbolName !== "__object") {
|
|
16
16
|
return false;
|
|
17
17
|
}
|
|
18
18
|
if (!(type.flags & ts.TypeFlags.Object)) {
|