@gqlkit-ts/cli 0.5.0 → 0.6.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/README.md +3 -1
- package/dist/auto-type-generator/auto-type-generator.d.ts.map +1 -1
- package/dist/auto-type-generator/auto-type-generator.js +4 -1
- package/dist/auto-type-generator/auto-type-generator.js.map +1 -1
- package/dist/auto-type-generator/naming-convention.d.ts +2 -2
- package/dist/auto-type-generator/naming-convention.d.ts.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/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/main.d.ts +1 -0
- package/dist/commands/main.d.ts.map +1 -1
- package/dist/gen-orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/gen-orchestrator/orchestrator.js +32 -1
- 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 +31 -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/schema-generator/emitter/code-emitter.d.ts.map +1 -1
- package/dist/schema-generator/emitter/code-emitter.js +11 -3
- package/dist/schema-generator/emitter/code-emitter.js.map +1 -1
- package/dist/schema-generator/integrator/result-integrator.d.ts +1 -0
- package/dist/schema-generator/integrator/result-integrator.d.ts.map +1 -1
- package/dist/schema-generator/integrator/result-integrator.js +26 -1
- package/dist/schema-generator/integrator/result-integrator.js.map +1 -1
- package/dist/type-extractor/extractor/type-name-collector.d.ts +6 -0
- package/dist/type-extractor/extractor/type-name-collector.d.ts.map +1 -1
- package/dist/type-extractor/extractor/type-name-collector.js +54 -18
- package/dist/type-extractor/extractor/type-name-collector.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/docs/getting-started.md +2 -1
- package/docs/index.md +1 -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/package.json +3 -3
- package/src/auto-type-generator/auto-type-generator.ts +12 -4
- package/src/auto-type-generator/naming-convention.ts +2 -2
- package/src/auto-type-generator/resolver-field-iterator.ts +5 -1
- package/src/gen-orchestrator/orchestrator.ts +38 -1
- package/src/resolver-extractor/extract-resolvers.ts +5 -0
- package/src/resolver-extractor/extractor/define-api-extractor.ts +43 -7
- package/src/resolver-extractor/index.ts +1 -0
- package/src/schema-generator/emitter/code-emitter.ts +17 -4
- package/src/schema-generator/integrator/result-integrator.ts +30 -1
- package/src/type-extractor/extractor/type-name-collector.ts +80 -18
- package/src/type-extractor/types/diagnostics.ts +2 -1
|
@@ -35,7 +35,11 @@ import type {
|
|
|
35
35
|
TSTypeReference,
|
|
36
36
|
} from "../../type-extractor/types/index.js";
|
|
37
37
|
|
|
38
|
-
export type DefineApiResolverType =
|
|
38
|
+
export type DefineApiResolverType =
|
|
39
|
+
| "query"
|
|
40
|
+
| "mutation"
|
|
41
|
+
| "field"
|
|
42
|
+
| "subscription";
|
|
39
43
|
|
|
40
44
|
export type AbstractResolverKind = "resolveType" | "isTypeOf";
|
|
41
45
|
|
|
@@ -56,6 +60,7 @@ export interface ArgumentDefinition {
|
|
|
56
60
|
|
|
57
61
|
export interface DefineApiResolverInfo {
|
|
58
62
|
readonly fieldName: string;
|
|
63
|
+
readonly resolverExportName: string;
|
|
59
64
|
readonly resolverType: DefineApiResolverType;
|
|
60
65
|
readonly parentTypeName: string | null;
|
|
61
66
|
readonly argsType: TSTypeReference | null;
|
|
@@ -222,7 +227,12 @@ function detectResolverFromMetadataType(
|
|
|
222
227
|
const kindType = checker.getTypeOfSymbol(kindProp);
|
|
223
228
|
if (kindType.isStringLiteral()) {
|
|
224
229
|
const kind = kindType.value;
|
|
225
|
-
if (
|
|
230
|
+
if (
|
|
231
|
+
kind === "query" ||
|
|
232
|
+
kind === "mutation" ||
|
|
233
|
+
kind === "field" ||
|
|
234
|
+
kind === "subscription"
|
|
235
|
+
) {
|
|
226
236
|
return kind;
|
|
227
237
|
}
|
|
228
238
|
}
|
|
@@ -230,6 +240,20 @@ function detectResolverFromMetadataType(
|
|
|
230
240
|
return null;
|
|
231
241
|
}
|
|
232
242
|
|
|
243
|
+
function resolveFieldNameFromExportName(exportName: string): string | null {
|
|
244
|
+
const delimiterIndex = exportName.lastIndexOf("$");
|
|
245
|
+
if (delimiterIndex === -1) {
|
|
246
|
+
return exportName;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const fieldName = exportName.slice(delimiterIndex + 1);
|
|
250
|
+
if (fieldName.length === 0) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return fieldName;
|
|
255
|
+
}
|
|
256
|
+
|
|
233
257
|
function isInlineTypeLiteralDeclaration(declaration: ts.Declaration): boolean {
|
|
234
258
|
if (!ts.isPropertySignature(declaration)) {
|
|
235
259
|
return false;
|
|
@@ -633,7 +657,7 @@ export function extractDefineApiResolvers(
|
|
|
633
657
|
continue;
|
|
634
658
|
}
|
|
635
659
|
|
|
636
|
-
const
|
|
660
|
+
const exportName = declaration.name.getText(sourceFile);
|
|
637
661
|
const initializer = declaration.initializer;
|
|
638
662
|
|
|
639
663
|
if (!initializer) {
|
|
@@ -647,11 +671,11 @@ export function extractDefineApiResolvers(
|
|
|
647
671
|
) {
|
|
648
672
|
const hasDefineCall = initializer
|
|
649
673
|
.getText(sourceFile)
|
|
650
|
-
.match(/define(Query|Mutation|Field)/);
|
|
674
|
+
.match(/define(Query|Mutation|Field|Subscription)/);
|
|
651
675
|
if (hasDefineCall) {
|
|
652
676
|
diagnostics.push({
|
|
653
677
|
code: "INVALID_DEFINE_CALL",
|
|
654
|
-
message: `Complex expressions with define* functions are not supported. Use a simple 'export const ${
|
|
678
|
+
message: `Complex expressions with define* functions are not supported. Use a simple 'export const ${exportName} = defineXxx(...)' pattern.`,
|
|
655
679
|
severity: "error",
|
|
656
680
|
location: getSourceLocationFromNode(declaration.name),
|
|
657
681
|
});
|
|
@@ -671,7 +695,7 @@ export function extractDefineApiResolvers(
|
|
|
671
695
|
abstractTypeResolvers.push({
|
|
672
696
|
kind: abstractResolverInfo.kind,
|
|
673
697
|
targetTypeName: abstractResolverInfo.targetTypeName,
|
|
674
|
-
exportName
|
|
698
|
+
exportName,
|
|
675
699
|
sourceFile: filePath,
|
|
676
700
|
sourceLocation,
|
|
677
701
|
});
|
|
@@ -688,6 +712,17 @@ export function extractDefineApiResolvers(
|
|
|
688
712
|
continue;
|
|
689
713
|
}
|
|
690
714
|
|
|
715
|
+
const fieldName = resolveFieldNameFromExportName(exportName);
|
|
716
|
+
if (fieldName === null) {
|
|
717
|
+
diagnostics.push({
|
|
718
|
+
code: "INVALID_DEFINE_CALL",
|
|
719
|
+
message: `Resolver export '${exportName}' must have a non-empty field name after '$'.`,
|
|
720
|
+
severity: "error",
|
|
721
|
+
location: getSourceLocationFromNode(declaration.name),
|
|
722
|
+
});
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
|
|
691
726
|
const funcName = ts.isIdentifier(initializer.expression)
|
|
692
727
|
? initializer.expression.text
|
|
693
728
|
: undefined;
|
|
@@ -702,7 +737,7 @@ export function extractDefineApiResolvers(
|
|
|
702
737
|
if (!typeInfo) {
|
|
703
738
|
diagnostics.push({
|
|
704
739
|
code: "INVALID_DEFINE_CALL",
|
|
705
|
-
message: `Failed to extract type arguments from ${funcName ?? "define*"} call for '${
|
|
740
|
+
message: `Failed to extract type arguments from ${funcName ?? "define*"} call for '${exportName}'`,
|
|
706
741
|
severity: "error",
|
|
707
742
|
location: getSourceLocationFromNode(declaration.name),
|
|
708
743
|
});
|
|
@@ -720,6 +755,7 @@ export function extractDefineApiResolvers(
|
|
|
720
755
|
|
|
721
756
|
resolvers.push({
|
|
722
757
|
fieldName,
|
|
758
|
+
resolverExportName: exportName,
|
|
723
759
|
resolverType,
|
|
724
760
|
parentTypeName: typeInfo.parentTypeName,
|
|
725
761
|
argsType: typeInfo.argsType,
|
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
} from "../integrator/result-integrator.js";
|
|
16
16
|
import type {
|
|
17
17
|
AbstractTypeResolverInfo,
|
|
18
|
+
FieldResolver,
|
|
18
19
|
ResolverInfo,
|
|
19
20
|
TypeResolvers,
|
|
20
21
|
} from "../resolver-collector/resolver-collector.js";
|
|
@@ -315,21 +316,33 @@ function buildStringEnumResolvers(
|
|
|
315
316
|
return stringEnumMappings.map(buildStringEnumResolver);
|
|
316
317
|
}
|
|
317
318
|
|
|
319
|
+
function buildFieldResolverValue(
|
|
320
|
+
localName: string,
|
|
321
|
+
field: FieldResolver,
|
|
322
|
+
): string {
|
|
323
|
+
if (field.isDirectExport) {
|
|
324
|
+
return localName;
|
|
325
|
+
}
|
|
326
|
+
return `${localName}.${field.fieldName}`;
|
|
327
|
+
}
|
|
328
|
+
|
|
318
329
|
function buildTypeResolverEntry(
|
|
319
330
|
type: TypeResolvers,
|
|
320
331
|
abstractResolverForType: AbstractTypeResolverInfo | null,
|
|
321
332
|
): string {
|
|
322
333
|
const entries: string[] = [];
|
|
334
|
+
const isSubscription = type.typeName === "Subscription";
|
|
323
335
|
|
|
324
336
|
for (const field of type.fields) {
|
|
325
337
|
const localName = makeResolverLocalName(type.typeName, field.fieldName);
|
|
338
|
+
const resolverValue = buildFieldResolverValue(localName, field);
|
|
326
339
|
|
|
327
|
-
if (
|
|
328
|
-
entries.push(` ${field.fieldName}: ${localName},`);
|
|
329
|
-
} else {
|
|
340
|
+
if (isSubscription) {
|
|
330
341
|
entries.push(
|
|
331
|
-
` ${field.fieldName}: ${
|
|
342
|
+
` ${field.fieldName}: { subscribe: ${resolverValue}, resolve: (event: unknown) => event },`,
|
|
332
343
|
);
|
|
344
|
+
} else {
|
|
345
|
+
entries.push(` ${field.fieldName}: ${resolverValue},`);
|
|
333
346
|
}
|
|
334
347
|
}
|
|
335
348
|
|
|
@@ -145,6 +145,7 @@ export interface IntegratedResult {
|
|
|
145
145
|
readonly stringEnumMappings: ReadonlyArray<StringEnumMappingInfo>;
|
|
146
146
|
readonly hasQuery: boolean;
|
|
147
147
|
readonly hasMutation: boolean;
|
|
148
|
+
readonly hasSubscription: boolean;
|
|
148
149
|
readonly hasErrors: boolean;
|
|
149
150
|
readonly diagnostics: ReadonlyArray<Diagnostic>;
|
|
150
151
|
}
|
|
@@ -469,8 +470,10 @@ export function integrate(params: IntegrateParams): IntegratedResult {
|
|
|
469
470
|
|
|
470
471
|
const hasQuery = resolversResult.queryFields.fields.length > 0;
|
|
471
472
|
const hasMutation = resolversResult.mutationFields.fields.length > 0;
|
|
473
|
+
const hasSubscription = resolversResult.subscriptionFields.fields.length > 0;
|
|
472
474
|
|
|
473
|
-
|
|
475
|
+
// GraphQL spec requires Query root type even when only Subscription/Mutation are defined
|
|
476
|
+
if (hasQuery || hasMutation || hasSubscription) {
|
|
474
477
|
baseTypes.push({
|
|
475
478
|
name: "Query",
|
|
476
479
|
kind: "Object",
|
|
@@ -502,6 +505,22 @@ export function integrate(params: IntegrateParams): IntegratedResult {
|
|
|
502
505
|
directives: null,
|
|
503
506
|
});
|
|
504
507
|
}
|
|
508
|
+
if (hasSubscription) {
|
|
509
|
+
baseTypes.push({
|
|
510
|
+
name: "Subscription",
|
|
511
|
+
kind: "Object",
|
|
512
|
+
fields: [],
|
|
513
|
+
unionMembers: null,
|
|
514
|
+
enumValues: null,
|
|
515
|
+
isNumericEnum: false,
|
|
516
|
+
needsStringEnumMapping: false,
|
|
517
|
+
implementedInterfaces: null,
|
|
518
|
+
description: null,
|
|
519
|
+
deprecated: null,
|
|
520
|
+
sourceFile: null,
|
|
521
|
+
directives: null,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
505
524
|
|
|
506
525
|
const typenameAutoResolveTypeNames = new Set([
|
|
507
526
|
...(typenameAutoResolveTypes?.map((t) => t.abstractTypeName) ?? []),
|
|
@@ -540,6 +559,15 @@ export function integrate(params: IntegrateParams): IntegratedResult {
|
|
|
540
559
|
});
|
|
541
560
|
}
|
|
542
561
|
|
|
562
|
+
if (hasSubscription) {
|
|
563
|
+
typeExtensions.push({
|
|
564
|
+
targetTypeName: "Subscription",
|
|
565
|
+
fields: resolversResult.subscriptionFields.fields.map(
|
|
566
|
+
convertToExtensionField,
|
|
567
|
+
),
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
|
|
543
571
|
for (const ext of resolversResult.typeExtensions) {
|
|
544
572
|
if (!knownTypeNames.has(ext.targetTypeName)) {
|
|
545
573
|
const firstField = ext.fields[0];
|
|
@@ -712,6 +740,7 @@ export function integrate(params: IntegrateParams): IntegratedResult {
|
|
|
712
740
|
stringEnumMappings,
|
|
713
741
|
hasQuery,
|
|
714
742
|
hasMutation,
|
|
743
|
+
hasSubscription,
|
|
715
744
|
hasErrors,
|
|
716
745
|
diagnostics,
|
|
717
746
|
};
|
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
+
import {
|
|
3
|
+
getSourceLocationFromNode,
|
|
4
|
+
type SourceLocation,
|
|
5
|
+
} from "../../shared/source-location.js";
|
|
2
6
|
import {
|
|
3
7
|
isExported,
|
|
4
8
|
resolveOriginalSymbol,
|
|
5
9
|
} from "../../shared/typescript-utils.js";
|
|
10
|
+
import type { Diagnostic } from "../types/index.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Tracks location and symbol of a type declaration for duplicate detection.
|
|
14
|
+
*/
|
|
15
|
+
interface TypeDeclarationLocation {
|
|
16
|
+
readonly location: SourceLocation;
|
|
17
|
+
readonly symbol: ts.Symbol | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Formats a source location as a human-readable string.
|
|
22
|
+
*/
|
|
23
|
+
function formatLocation(location: SourceLocation): string {
|
|
24
|
+
return `${location.file}:${location.line}`;
|
|
25
|
+
}
|
|
6
26
|
|
|
7
27
|
export interface TypeNameCollectionResult {
|
|
8
28
|
readonly typeNames: ReadonlySet<string>;
|
|
@@ -13,6 +33,11 @@ export interface TypeNameCollectionResult {
|
|
|
13
33
|
* This allows recognizing `ExternalUser` as `User` in field types.
|
|
14
34
|
*/
|
|
15
35
|
readonly underlyingSymbolToTypeName: ReadonlyMap<ts.Symbol, string>;
|
|
36
|
+
/**
|
|
37
|
+
* Diagnostics collected during type name collection.
|
|
38
|
+
* Contains errors for duplicate type exports.
|
|
39
|
+
*/
|
|
40
|
+
readonly diagnostics: ReadonlyArray<Diagnostic>;
|
|
16
41
|
}
|
|
17
42
|
|
|
18
43
|
/**
|
|
@@ -33,8 +58,51 @@ export function collectDeclaredTypeNames(
|
|
|
33
58
|
const typeNames = new Set<string>();
|
|
34
59
|
const typeSymbols = new Map<string, ts.Symbol>();
|
|
35
60
|
const underlyingSymbolToTypeName = new Map<ts.Symbol, string>();
|
|
61
|
+
const diagnostics: Diagnostic[] = [];
|
|
36
62
|
const checker = program.getTypeChecker();
|
|
37
63
|
|
|
64
|
+
const typeLocations = new Map<string, TypeDeclarationLocation>();
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Registers a type name and checks for duplicates.
|
|
68
|
+
* Returns true if this is a new type, false if it's a duplicate.
|
|
69
|
+
*/
|
|
70
|
+
function registerTypeName(
|
|
71
|
+
name: string,
|
|
72
|
+
location: SourceLocation,
|
|
73
|
+
symbol: ts.Symbol | undefined,
|
|
74
|
+
): boolean {
|
|
75
|
+
const resolvedSymbol = symbol
|
|
76
|
+
? resolveOriginalSymbol(symbol, checker)
|
|
77
|
+
: null;
|
|
78
|
+
const existing = typeLocations.get(name);
|
|
79
|
+
if (existing) {
|
|
80
|
+
// Check if both symbols resolve to the same underlying type
|
|
81
|
+
// (e.g., re-exports of the same type from different files)
|
|
82
|
+
if (
|
|
83
|
+
resolvedSymbol &&
|
|
84
|
+
existing.symbol &&
|
|
85
|
+
resolvedSymbol === existing.symbol
|
|
86
|
+
) {
|
|
87
|
+
// Same underlying type - not a true duplicate, skip silently
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
diagnostics.push({
|
|
91
|
+
code: "DUPLICATE_TYPE_EXPORT",
|
|
92
|
+
message: `Type '${name}' is exported from multiple files. First defined at ${formatLocation(existing.location)}.`,
|
|
93
|
+
severity: "error",
|
|
94
|
+
location,
|
|
95
|
+
});
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
typeLocations.set(name, { location, symbol: resolvedSymbol });
|
|
99
|
+
typeNames.add(name);
|
|
100
|
+
if (resolvedSymbol) {
|
|
101
|
+
typeSymbols.set(name, resolvedSymbol);
|
|
102
|
+
}
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
38
106
|
for (const filePath of sourceFiles) {
|
|
39
107
|
const sourceFile = program.getSourceFile(filePath);
|
|
40
108
|
if (!sourceFile) continue;
|
|
@@ -43,11 +111,11 @@ export function collectDeclaredTypeNames(
|
|
|
43
111
|
// Direct type declarations
|
|
44
112
|
if (ts.isTypeAliasDeclaration(node) && isExported(node)) {
|
|
45
113
|
const name = node.name.getText(sourceFile);
|
|
46
|
-
|
|
114
|
+
const location = getSourceLocationFromNode(node)!;
|
|
47
115
|
const symbol = checker.getSymbolAtLocation(node.name);
|
|
48
|
-
|
|
49
|
-
typeSymbols.set(name, resolveOriginalSymbol(symbol, checker));
|
|
116
|
+
const isNew = registerTypeName(name, location, symbol);
|
|
50
117
|
|
|
118
|
+
if (isNew && symbol) {
|
|
51
119
|
// For type aliases like `type User = ExternalUser;`,
|
|
52
120
|
// also track the underlying type's symbol
|
|
53
121
|
const type = checker.getTypeAtLocation(node.type);
|
|
@@ -62,19 +130,15 @@ export function collectDeclaredTypeNames(
|
|
|
62
130
|
}
|
|
63
131
|
if (ts.isInterfaceDeclaration(node) && isExported(node)) {
|
|
64
132
|
const name = node.name.getText(sourceFile);
|
|
65
|
-
|
|
133
|
+
const location = getSourceLocationFromNode(node)!;
|
|
66
134
|
const symbol = checker.getSymbolAtLocation(node.name);
|
|
67
|
-
|
|
68
|
-
typeSymbols.set(name, resolveOriginalSymbol(symbol, checker));
|
|
69
|
-
}
|
|
135
|
+
registerTypeName(name, location, symbol);
|
|
70
136
|
}
|
|
71
137
|
if (ts.isEnumDeclaration(node) && isExported(node)) {
|
|
72
138
|
const name = node.name.getText(sourceFile);
|
|
73
|
-
|
|
139
|
+
const location = getSourceLocationFromNode(node)!;
|
|
74
140
|
const symbol = checker.getSymbolAtLocation(node.name);
|
|
75
|
-
|
|
76
|
-
typeSymbols.set(name, resolveOriginalSymbol(symbol, checker));
|
|
77
|
-
}
|
|
141
|
+
registerTypeName(name, location, symbol);
|
|
78
142
|
}
|
|
79
143
|
|
|
80
144
|
// Re-exports: `export type { ... } from "..."` or `export type * from "..."`
|
|
@@ -85,11 +149,9 @@ export function collectDeclaredTypeNames(
|
|
|
85
149
|
for (const element of node.exportClause.elements) {
|
|
86
150
|
// Use the exported name (element.name), not the property name
|
|
87
151
|
const name = element.name.getText(sourceFile);
|
|
88
|
-
|
|
152
|
+
const location = getSourceLocationFromNode(element)!;
|
|
89
153
|
const symbol = checker.getSymbolAtLocation(element.name);
|
|
90
|
-
|
|
91
|
-
typeSymbols.set(name, resolveOriginalSymbol(symbol, checker));
|
|
92
|
-
}
|
|
154
|
+
registerTypeName(name, location, symbol);
|
|
93
155
|
}
|
|
94
156
|
}
|
|
95
157
|
} else if (node.moduleSpecifier) {
|
|
@@ -103,8 +165,8 @@ export function collectDeclaredTypeNames(
|
|
|
103
165
|
const name = exp.getName();
|
|
104
166
|
// Check if the export is a type (not a value)
|
|
105
167
|
if (isTypeExport(exp)) {
|
|
106
|
-
|
|
107
|
-
|
|
168
|
+
const location = getSourceLocationFromNode(node)!;
|
|
169
|
+
registerTypeName(name, location, exp);
|
|
108
170
|
}
|
|
109
171
|
}
|
|
110
172
|
}
|
|
@@ -113,7 +175,7 @@ export function collectDeclaredTypeNames(
|
|
|
113
175
|
});
|
|
114
176
|
}
|
|
115
177
|
|
|
116
|
-
return { typeNames, typeSymbols, underlyingSymbolToTypeName };
|
|
178
|
+
return { typeNames, typeSymbols, underlyingSymbolToTypeName, diagnostics };
|
|
117
179
|
}
|
|
118
180
|
|
|
119
181
|
/**
|
|
@@ -96,7 +96,8 @@ export type DiagnosticCode =
|
|
|
96
96
|
| "TYPENAME_FIELD_STRUCTURE_MISMATCH"
|
|
97
97
|
| "DUPLICATE_TYPENAME_VALUE"
|
|
98
98
|
| "IGNORE_FIELD_NOT_FOUND"
|
|
99
|
-
| "IGNORE_ALL_FIELDS"
|
|
99
|
+
| "IGNORE_ALL_FIELDS"
|
|
100
|
+
| "DUPLICATE_TYPE_EXPORT";
|
|
100
101
|
|
|
101
102
|
export interface Diagnostic {
|
|
102
103
|
readonly code: DiagnosticCode;
|