@gqlkit-ts/cli 0.2.0 → 0.4.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 +143 -0
- package/dist/auto-type-generator/auto-type-generator.d.ts +10 -4
- package/dist/auto-type-generator/auto-type-generator.d.ts.map +1 -1
- package/dist/auto-type-generator/auto-type-generator.js +656 -146
- package/dist/auto-type-generator/auto-type-generator.js.map +1 -1
- package/dist/auto-type-generator/index.d.ts +8 -1
- 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 +13 -5
- package/dist/auto-type-generator/inline-enum-collector.d.ts.map +1 -1
- package/dist/auto-type-generator/inline-enum-collector.js +107 -71
- package/dist/auto-type-generator/inline-enum-collector.js.map +1 -1
- package/dist/auto-type-generator/inline-object-traverser.d.ts +20 -0
- package/dist/auto-type-generator/inline-object-traverser.d.ts.map +1 -0
- package/dist/auto-type-generator/inline-object-traverser.js +22 -0
- package/dist/auto-type-generator/inline-object-traverser.js.map +1 -0
- package/dist/auto-type-generator/inline-union-collector.d.ts +29 -0
- package/dist/auto-type-generator/inline-union-collector.d.ts.map +1 -0
- package/dist/auto-type-generator/inline-union-collector.js +216 -0
- package/dist/auto-type-generator/inline-union-collector.js.map +1 -0
- package/dist/auto-type-generator/inline-union-types.d.ts +29 -0
- package/dist/auto-type-generator/inline-union-types.d.ts.map +1 -0
- package/dist/auto-type-generator/inline-union-types.js +2 -0
- package/dist/auto-type-generator/inline-union-types.js.map +1 -0
- package/dist/auto-type-generator/inline-union-validator.d.ts +76 -0
- package/dist/auto-type-generator/inline-union-validator.d.ts.map +1 -0
- package/dist/auto-type-generator/inline-union-validator.js +329 -0
- package/dist/auto-type-generator/inline-union-validator.js.map +1 -0
- package/dist/auto-type-generator/naming-convention.d.ts +18 -1
- package/dist/auto-type-generator/naming-convention.d.ts.map +1 -1
- package/dist/auto-type-generator/naming-convention.js +16 -0
- package/dist/auto-type-generator/naming-convention.js.map +1 -1
- package/dist/auto-type-generator/resolve-type-generator.d.ts +20 -0
- package/dist/auto-type-generator/resolve-type-generator.d.ts.map +1 -0
- package/dist/auto-type-generator/resolve-type-generator.js +2 -0
- package/dist/auto-type-generator/resolve-type-generator.js.map +1 -0
- package/dist/auto-type-generator/resolver-field-iterator.d.ts +13 -0
- package/dist/auto-type-generator/resolver-field-iterator.d.ts.map +1 -0
- package/dist/auto-type-generator/resolver-field-iterator.js +22 -0
- package/dist/auto-type-generator/resolver-field-iterator.js.map +1 -0
- package/dist/auto-type-generator/typename-extractor.d.ts +26 -0
- package/dist/auto-type-generator/typename-extractor.d.ts.map +1 -0
- package/dist/auto-type-generator/typename-extractor.js +142 -0
- package/dist/auto-type-generator/typename-extractor.js.map +1 -0
- package/dist/auto-type-generator/typename-resolve-type-generator.d.ts +35 -0
- package/dist/auto-type-generator/typename-resolve-type-generator.d.ts.map +1 -0
- package/dist/auto-type-generator/typename-resolve-type-generator.js +177 -0
- package/dist/auto-type-generator/typename-resolve-type-generator.js.map +1 -0
- package/dist/auto-type-generator/typename-types.d.ts +43 -0
- package/dist/auto-type-generator/typename-types.d.ts.map +1 -0
- package/dist/auto-type-generator/typename-types.js +37 -0
- package/dist/auto-type-generator/typename-types.js.map +1 -0
- package/dist/auto-type-generator/typename-validator.d.ts +37 -0
- package/dist/auto-type-generator/typename-validator.d.ts.map +1 -0
- package/dist/auto-type-generator/typename-validator.js +206 -0
- package/dist/auto-type-generator/typename-validator.js.map +1 -0
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/docs.d.ts +51 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +154 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/config/types.d.ts +13 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config-loader/loader.d.ts +3 -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 +23 -0
- package/dist/config-loader/validator.js.map +1 -1
- package/dist/gen-orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/gen-orchestrator/orchestrator.js +32 -12
- package/dist/gen-orchestrator/orchestrator.js.map +1 -1
- package/dist/resolver-extractor/extract-resolvers.d.ts +19 -1
- package/dist/resolver-extractor/extract-resolvers.d.ts.map +1 -1
- package/dist/resolver-extractor/extractor/define-api-extractor.d.ts +5 -0
- package/dist/resolver-extractor/extractor/define-api-extractor.d.ts.map +1 -1
- package/dist/resolver-extractor/extractor/define-api-extractor.js +18 -65
- package/dist/resolver-extractor/extractor/define-api-extractor.js.map +1 -1
- package/dist/resolver-extractor/index.d.ts +0 -1
- package/dist/resolver-extractor/index.d.ts.map +1 -1
- package/dist/resolver-extractor/validator/abstract-resolver-validator.d.ts +1 -0
- package/dist/resolver-extractor/validator/abstract-resolver-validator.d.ts.map +1 -1
- package/dist/resolver-extractor/validator/abstract-resolver-validator.js +9 -5
- package/dist/resolver-extractor/validator/abstract-resolver-validator.js.map +1 -1
- package/dist/schema-generator/builder/ast-builder.d.ts +2 -2
- package/dist/schema-generator/builder/ast-builder.d.ts.map +1 -1
- package/dist/schema-generator/builder/ast-builder.js +12 -34
- package/dist/schema-generator/builder/ast-builder.js.map +1 -1
- package/dist/schema-generator/emitter/code-emitter.d.ts +3 -1
- package/dist/schema-generator/emitter/code-emitter.d.ts.map +1 -1
- package/dist/schema-generator/emitter/code-emitter.js +42 -12
- package/dist/schema-generator/emitter/code-emitter.js.map +1 -1
- package/dist/schema-generator/emitter/sdl-emitter.d.ts +0 -4
- package/dist/schema-generator/emitter/sdl-emitter.d.ts.map +1 -1
- package/dist/schema-generator/emitter/sdl-emitter.js +0 -4
- package/dist/schema-generator/emitter/sdl-emitter.js.map +1 -1
- package/dist/schema-generator/generate-schema.d.ts +3 -0
- package/dist/schema-generator/generate-schema.d.ts.map +1 -1
- package/dist/schema-generator/generate-schema.js +83 -5
- package/dist/schema-generator/generate-schema.js.map +1 -1
- package/dist/schema-generator/integrator/result-integrator.d.ts +19 -9
- package/dist/schema-generator/integrator/result-integrator.d.ts.map +1 -1
- package/dist/schema-generator/integrator/result-integrator.js +60 -44
- 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 +22 -0
- package/dist/schema-generator/resolver-collector/resolver-collector.js.map +1 -1
- package/dist/shared/branded-type-detector.d.ts +43 -0
- package/dist/shared/branded-type-detector.d.ts.map +1 -0
- package/dist/shared/branded-type-detector.js +146 -0
- package/dist/shared/branded-type-detector.js.map +1 -0
- package/dist/shared/enum-prefix-detector.d.ts +63 -0
- package/dist/shared/enum-prefix-detector.d.ts.map +1 -0
- package/dist/shared/enum-prefix-detector.js +80 -0
- package/dist/shared/enum-prefix-detector.js.map +1 -0
- package/dist/shared/ignore-fields-detector.d.ts +26 -0
- package/dist/shared/ignore-fields-detector.d.ts.map +1 -0
- package/dist/shared/ignore-fields-detector.js +83 -0
- package/dist/shared/ignore-fields-detector.js.map +1 -0
- package/dist/shared/ignore-fields-validator.d.ts +29 -0
- package/dist/shared/ignore-fields-validator.d.ts.map +1 -0
- package/dist/shared/ignore-fields-validator.js +43 -0
- package/dist/shared/ignore-fields-validator.js.map +1 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/source-location.d.ts +5 -0
- package/dist/shared/source-location.d.ts.map +1 -1
- package/dist/shared/source-location.js +7 -0
- package/dist/shared/source-location.js.map +1 -1
- package/dist/shared/string-utils.d.ts +2 -0
- package/dist/shared/string-utils.d.ts.map +1 -0
- package/dist/shared/string-utils.js +8 -0
- package/dist/shared/string-utils.js.map +1 -0
- package/dist/type-extractor/converter/field-eligibility.d.ts +8 -6
- package/dist/type-extractor/converter/field-eligibility.d.ts.map +1 -1
- package/dist/type-extractor/converter/field-eligibility.js +7 -28
- package/dist/type-extractor/converter/field-eligibility.js.map +1 -1
- package/dist/type-extractor/converter/graphql-converter.d.ts.map +1 -1
- package/dist/type-extractor/converter/graphql-converter.js +27 -18
- package/dist/type-extractor/converter/graphql-converter.js.map +1 -1
- package/dist/type-extractor/extractor/field-type-resolver.d.ts.map +1 -1
- package/dist/type-extractor/extractor/field-type-resolver.js +81 -7
- package/dist/type-extractor/extractor/field-type-resolver.js.map +1 -1
- package/dist/type-extractor/extractor/type-extractor.d.ts.map +1 -1
- package/dist/type-extractor/extractor/type-extractor.js +88 -23
- 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/ts-type-reference-factory.d.ts +10 -2
- package/dist/type-extractor/types/ts-type-reference-factory.d.ts.map +1 -1
- package/dist/type-extractor/types/ts-type-reference-factory.js +8 -2
- package/dist/type-extractor/types/ts-type-reference-factory.js.map +1 -1
- package/dist/type-extractor/types/typescript.d.ts +4 -0
- package/dist/type-extractor/types/typescript.d.ts.map +1 -1
- package/dist/type-extractor/validator/type-validator.d.ts +1 -1
- package/dist/type-extractor/validator/type-validator.d.ts.map +1 -1
- package/dist/type-extractor/validator/type-validator.js +2 -10
- package/dist/type-extractor/validator/type-validator.js.map +1 -1
- package/docs/coding-agents.md +64 -0
- package/docs/configuration.md +15 -20
- package/docs/getting-started.md +15 -12
- package/docs/index.md +36 -22
- package/docs/integration/apollo.md +8 -40
- package/docs/integration/drizzle.md +6 -10
- package/docs/integration/prisma.md +196 -0
- package/docs/integration/yoga.md +8 -40
- package/docs/schema/abstract-resolvers.md +117 -0
- package/docs/schema/directives.md +5 -0
- package/docs/schema/documentation.md +5 -0
- package/docs/schema/enums.md +99 -0
- package/docs/schema/fields.md +64 -0
- package/docs/schema/index.md +21 -0
- package/docs/schema/inputs.md +115 -15
- package/docs/schema/interfaces.md +31 -1
- package/docs/schema/objects.md +40 -0
- package/docs/schema/queries-mutations.md +136 -22
- package/docs/schema/scalars.md +5 -0
- package/docs/schema/unions.md +208 -1
- package/docs/what-is-gqlkit.md +13 -8
- package/package.json +6 -4
- package/src/auto-type-generator/auto-type-generator.ts +969 -227
- package/src/auto-type-generator/index.ts +42 -0
- package/src/auto-type-generator/inline-enum-collector.ts +187 -139
- package/src/auto-type-generator/inline-object-traverser.ts +49 -0
- package/src/auto-type-generator/inline-union-collector.ts +402 -0
- package/src/auto-type-generator/inline-union-types.ts +33 -0
- package/src/auto-type-generator/inline-union-validator.ts +482 -0
- package/src/auto-type-generator/naming-convention.ts +38 -1
- package/src/auto-type-generator/resolve-type-generator.ts +21 -0
- package/src/auto-type-generator/resolver-field-iterator.ts +39 -0
- package/src/auto-type-generator/typename-extractor.ts +230 -0
- package/src/auto-type-generator/typename-resolve-type-generator.ts +281 -0
- package/src/auto-type-generator/typename-types.ts +66 -0
- package/src/auto-type-generator/typename-validator.ts +326 -0
- package/src/cli.ts +2 -0
- package/src/commands/docs.ts +211 -0
- package/src/config/types.ts +15 -0
- package/src/config-loader/loader.ts +4 -0
- package/src/config-loader/validator.ts +33 -0
- package/src/gen-orchestrator/orchestrator.ts +50 -17
- package/src/resolver-extractor/extract-resolvers.ts +19 -0
- package/src/resolver-extractor/extractor/define-api-extractor.ts +28 -94
- package/src/resolver-extractor/index.ts +0 -6
- package/src/resolver-extractor/validator/abstract-resolver-validator.ts +16 -8
- package/src/schema-generator/builder/ast-builder.ts +52 -81
- package/src/schema-generator/emitter/code-emitter.ts +82 -11
- package/src/schema-generator/emitter/sdl-emitter.ts +0 -4
- package/src/schema-generator/generate-schema.ts +109 -14
- package/src/schema-generator/integrator/result-integrator.ts +91 -63
- package/src/schema-generator/resolver-collector/resolver-collector.ts +34 -0
- package/src/shared/branded-type-detector.ts +182 -0
- package/src/shared/enum-prefix-detector.ts +99 -0
- package/src/shared/ignore-fields-detector.ts +109 -0
- package/src/shared/ignore-fields-validator.ts +66 -0
- package/src/shared/index.ts +2 -0
- package/src/shared/source-location.ts +11 -0
- package/src/shared/string-utils.ts +7 -0
- package/src/type-extractor/converter/field-eligibility.ts +13 -29
- package/src/type-extractor/converter/graphql-converter.ts +37 -23
- package/src/type-extractor/extractor/field-type-resolver.ts +97 -7
- package/src/type-extractor/extractor/type-extractor.ts +103 -26
- package/src/type-extractor/types/diagnostics.ts +13 -2
- package/src/type-extractor/types/ts-type-reference-factory.ts +18 -5
- package/src/type-extractor/types/typescript.ts +4 -0
- package/src/type-extractor/validator/type-validator.ts +2 -15
- package/dist/resolver-extractor/validator/only-validator.d.ts +0 -61
- package/dist/resolver-extractor/validator/only-validator.d.ts.map +0 -1
- package/dist/resolver-extractor/validator/only-validator.js +0 -76
- package/dist/resolver-extractor/validator/only-validator.js.map +0 -1
- package/src/resolver-extractor/validator/only-validator.ts +0 -158
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Diagnostic,
|
|
3
|
+
ExtractedTypeInfo,
|
|
4
|
+
InlineObjectMember,
|
|
5
|
+
SourceLocation,
|
|
6
|
+
} from "../type-extractor/types/index.js";
|
|
7
|
+
import type { TypenameExtractionResult } from "./typename-extractor.js";
|
|
8
|
+
import {
|
|
9
|
+
findTypenameProperty,
|
|
10
|
+
type TypenameFieldName,
|
|
11
|
+
} from "./typename-types.js";
|
|
12
|
+
|
|
13
|
+
export interface ValidateTypenamesParams {
|
|
14
|
+
readonly extractionResult: TypenameExtractionResult;
|
|
15
|
+
readonly sourceLocation: SourceLocation;
|
|
16
|
+
readonly inlineObjectMembers: ReadonlyArray<InlineObjectMember> | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ValidateTypenamesResult {
|
|
20
|
+
readonly valid: boolean;
|
|
21
|
+
readonly diagnostics: ReadonlyArray<Diagnostic>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ValidateSchemaTypenamesParams {
|
|
25
|
+
readonly objectTypes: ReadonlyArray<ExtractedTypeInfo>;
|
|
26
|
+
readonly typeMap: ReadonlyMap<string, ExtractedTypeInfo>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ValidateSchemaTypenamesResult {
|
|
30
|
+
readonly valid: boolean;
|
|
31
|
+
readonly diagnostics: ReadonlyArray<Diagnostic>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface TypenameValueInfo {
|
|
35
|
+
readonly memberTypeName: string;
|
|
36
|
+
readonly fieldName: TypenameFieldName;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getAbstractTypeLabel(kind: "union" | "interface"): string {
|
|
40
|
+
return kind === "union" ? "Union" : "Interface";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getMemberLabel(kind: "union" | "interface"): string {
|
|
44
|
+
return kind === "union" ? "members" : "implementers";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface InlineObjectTypenameAnalysis {
|
|
48
|
+
readonly exists: boolean;
|
|
49
|
+
readonly fieldName: TypenameFieldName | null;
|
|
50
|
+
readonly isStringLiteral: boolean;
|
|
51
|
+
readonly isNullable: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function analyzeInlineObjectTypename(
|
|
55
|
+
inlineObjectMember: InlineObjectMember,
|
|
56
|
+
): InlineObjectTypenameAnalysis {
|
|
57
|
+
const found = findTypenameProperty(
|
|
58
|
+
inlineObjectMember.properties,
|
|
59
|
+
(p) => p.propertyName,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (!found) {
|
|
63
|
+
return {
|
|
64
|
+
exists: false,
|
|
65
|
+
fieldName: null,
|
|
66
|
+
isStringLiteral: false,
|
|
67
|
+
isNullable: false,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const { property, fieldName } = found;
|
|
72
|
+
const { propertyType: tsType } = property;
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
exists: true,
|
|
76
|
+
fieldName,
|
|
77
|
+
isStringLiteral: tsType.kind === "literal" && tsType.name !== null,
|
|
78
|
+
isNullable: tsType.nullable,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function validateInlineObjectsTypenames(
|
|
83
|
+
extractionResult: TypenameExtractionResult,
|
|
84
|
+
inlineObjectMembers: ReadonlyArray<InlineObjectMember>,
|
|
85
|
+
sourceLocation: SourceLocation,
|
|
86
|
+
): Diagnostic[] {
|
|
87
|
+
if (!extractionResult.hasInlineObjects) {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (extractionResult.abstractTypeName.endsWith("Input")) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const diagnostics: Diagnostic[] = [];
|
|
96
|
+
const abstractTypeName = extractionResult.abstractTypeName;
|
|
97
|
+
const abstractTypeLabel = getAbstractTypeLabel(
|
|
98
|
+
extractionResult.abstractTypeKind,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const namedMemberCount = extractionResult.members.filter(
|
|
102
|
+
(m) => !m.isInlineObject,
|
|
103
|
+
).length;
|
|
104
|
+
|
|
105
|
+
for (const member of extractionResult.members) {
|
|
106
|
+
if (member.isInlineObject) {
|
|
107
|
+
const inlineIndex = member.memberIndex - namedMemberCount;
|
|
108
|
+
const inlineObjectMember = inlineObjectMembers[inlineIndex];
|
|
109
|
+
|
|
110
|
+
if (inlineObjectMember) {
|
|
111
|
+
const analysis = analyzeInlineObjectTypename(inlineObjectMember);
|
|
112
|
+
|
|
113
|
+
if (!analysis.exists) {
|
|
114
|
+
diagnostics.push({
|
|
115
|
+
code: "MISSING_TYPENAME_PROPERTY",
|
|
116
|
+
message: `${abstractTypeLabel} '${abstractTypeName}' member at index ${member.memberIndex} is missing '__typename' or '$typeName' property. When a union contains inline objects, all members must have a typename property.`,
|
|
117
|
+
severity: "error",
|
|
118
|
+
location: sourceLocation,
|
|
119
|
+
});
|
|
120
|
+
} else if (!analysis.isStringLiteral) {
|
|
121
|
+
diagnostics.push({
|
|
122
|
+
code: "INVALID_TYPENAME_TYPE",
|
|
123
|
+
message: `${abstractTypeLabel} '${abstractTypeName}' member at index ${member.memberIndex} has '${analysis.fieldName}' that is not a string literal type. Expected a string literal like '__typename: "TypeName"'.`,
|
|
124
|
+
severity: "error",
|
|
125
|
+
location: sourceLocation,
|
|
126
|
+
});
|
|
127
|
+
} else if (analysis.isNullable) {
|
|
128
|
+
diagnostics.push({
|
|
129
|
+
code: "NULLABLE_TYPENAME_PROPERTY",
|
|
130
|
+
message: `${abstractTypeLabel} '${abstractTypeName}' member at index ${member.memberIndex} has nullable '${analysis.fieldName}' property. The typename property must not be nullable.`,
|
|
131
|
+
severity: "error",
|
|
132
|
+
location: sourceLocation,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return diagnostics;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Validates typename fields within an abstract type.
|
|
144
|
+
* Reports errors for:
|
|
145
|
+
* - DUPLICATE_TYPENAME_VALUE: Multiple members have the same typename value
|
|
146
|
+
* - MISSING_TYPENAME_PROPERTY: Inline objects without typename (when union has inline objects)
|
|
147
|
+
* - INVALID_TYPENAME_TYPE: Typename is not a string literal type
|
|
148
|
+
* - NULLABLE_TYPENAME_PROPERTY: Typename is nullable
|
|
149
|
+
*/
|
|
150
|
+
export function validateTypenames(
|
|
151
|
+
params: ValidateTypenamesParams,
|
|
152
|
+
): ValidateTypenamesResult {
|
|
153
|
+
const { extractionResult, sourceLocation, inlineObjectMembers } = params;
|
|
154
|
+
const diagnostics: Diagnostic[] = [];
|
|
155
|
+
|
|
156
|
+
if (inlineObjectMembers) {
|
|
157
|
+
const inlineValidationDiagnostics = validateInlineObjectsTypenames(
|
|
158
|
+
extractionResult,
|
|
159
|
+
inlineObjectMembers,
|
|
160
|
+
sourceLocation,
|
|
161
|
+
);
|
|
162
|
+
diagnostics.push(...inlineValidationDiagnostics);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const typenameValueToMembers = new Map<string, TypenameValueInfo[]>();
|
|
166
|
+
|
|
167
|
+
for (const member of extractionResult.members) {
|
|
168
|
+
if (member.typenameInfo === null) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const { typeName, fieldName } = member.typenameInfo;
|
|
173
|
+
const memberTypeName =
|
|
174
|
+
member.memberTypeName ?? `member${member.memberIndex}`;
|
|
175
|
+
|
|
176
|
+
const existing = typenameValueToMembers.get(typeName) ?? [];
|
|
177
|
+
existing.push({ memberTypeName, fieldName });
|
|
178
|
+
typenameValueToMembers.set(typeName, existing);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const abstractTypeLabel = getAbstractTypeLabel(
|
|
182
|
+
extractionResult.abstractTypeKind,
|
|
183
|
+
);
|
|
184
|
+
const memberLabel = getMemberLabel(extractionResult.abstractTypeKind);
|
|
185
|
+
|
|
186
|
+
for (const [typenameValue, members] of typenameValueToMembers) {
|
|
187
|
+
if (members.length > 1) {
|
|
188
|
+
const allSameFieldName = members.every(
|
|
189
|
+
(m) => m.fieldName === members[0]!.fieldName,
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
let message: string;
|
|
193
|
+
if (allSameFieldName) {
|
|
194
|
+
const memberNames = members
|
|
195
|
+
.map((m) => `'${m.memberTypeName}'`)
|
|
196
|
+
.join(" and ");
|
|
197
|
+
message = `Duplicate typename value '${typenameValue}' in ${abstractTypeLabel} '${extractionResult.abstractTypeName}': used by ${memberLabel} ${memberNames}.`;
|
|
198
|
+
} else {
|
|
199
|
+
const memberDescriptions = members
|
|
200
|
+
.map((m) => `member '${m.memberTypeName}' (${m.fieldName})`)
|
|
201
|
+
.join(" and ");
|
|
202
|
+
message = `Duplicate typename value '${typenameValue}' in ${abstractTypeLabel} '${extractionResult.abstractTypeName}': ${memberDescriptions} have the same value.`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
diagnostics.push({
|
|
206
|
+
code: "DUPLICATE_TYPENAME_VALUE",
|
|
207
|
+
message,
|
|
208
|
+
severity: "error",
|
|
209
|
+
location: sourceLocation,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
valid: diagnostics.length === 0,
|
|
216
|
+
diagnostics,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
interface ObjectTypeTypenameInfo {
|
|
221
|
+
readonly typeName: string;
|
|
222
|
+
readonly typenameValue: string;
|
|
223
|
+
readonly fieldName: TypenameFieldName;
|
|
224
|
+
readonly sourceLocation: SourceLocation;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function extractTypenameFromObjectType(
|
|
228
|
+
typeInfo: ExtractedTypeInfo,
|
|
229
|
+
): ObjectTypeTypenameInfo | null {
|
|
230
|
+
const found = findTypenameProperty(typeInfo.fields, (f) => f.name);
|
|
231
|
+
if (!found) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const { property: field, fieldName } = found;
|
|
236
|
+
const { tsType } = field;
|
|
237
|
+
|
|
238
|
+
if (
|
|
239
|
+
field.optional ||
|
|
240
|
+
tsType.nullable ||
|
|
241
|
+
tsType.kind !== "literal" ||
|
|
242
|
+
tsType.name === null
|
|
243
|
+
) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
typeName: typeInfo.metadata.name,
|
|
249
|
+
typenameValue: tsType.name,
|
|
250
|
+
fieldName,
|
|
251
|
+
sourceLocation: typeInfo.metadata.sourceLocation,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Validates that there are no duplicate typename values across the entire schema.
|
|
257
|
+
* Reports DUPLICATE_TYPENAME_VALUE error when:
|
|
258
|
+
* - Different object types have the same __typename value (Requirement 4.7)
|
|
259
|
+
* - Different object types have the same $typeName value (Requirement 4.8)
|
|
260
|
+
* - An object's __typename equals another object's $typeName (Requirement 4.9)
|
|
261
|
+
*/
|
|
262
|
+
export function validateSchemaTypenames(
|
|
263
|
+
params: ValidateSchemaTypenamesParams,
|
|
264
|
+
): ValidateSchemaTypenamesResult {
|
|
265
|
+
const { objectTypes } = params;
|
|
266
|
+
const diagnostics: Diagnostic[] = [];
|
|
267
|
+
|
|
268
|
+
const typenameValueToTypes = new Map<string, ObjectTypeTypenameInfo[]>();
|
|
269
|
+
|
|
270
|
+
for (const typeInfo of objectTypes) {
|
|
271
|
+
if (
|
|
272
|
+
typeInfo.metadata.kind !== "object" &&
|
|
273
|
+
typeInfo.metadata.kind !== "interface" &&
|
|
274
|
+
typeInfo.metadata.kind !== "graphqlInterface"
|
|
275
|
+
) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const typenameInfo = extractTypenameFromObjectType(typeInfo);
|
|
280
|
+
if (typenameInfo === null) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const existing = typenameValueToTypes.get(typenameInfo.typenameValue) ?? [];
|
|
285
|
+
existing.push(typenameInfo);
|
|
286
|
+
typenameValueToTypes.set(typenameInfo.typenameValue, existing);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
for (const [typenameValue, types] of typenameValueToTypes) {
|
|
290
|
+
if (types.length > 1) {
|
|
291
|
+
const allSameFieldName = types.every(
|
|
292
|
+
(t) => t.fieldName === types[0]!.fieldName,
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
const sortedTypes = [...types].sort((a, b) =>
|
|
296
|
+
a.typeName.localeCompare(b.typeName),
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
let message: string;
|
|
300
|
+
if (allSameFieldName) {
|
|
301
|
+
const typeNames = sortedTypes
|
|
302
|
+
.map((t) => `'${t.typeName}'`)
|
|
303
|
+
.join(" and ");
|
|
304
|
+
message = `Duplicate typename value '${typenameValue}' in schema: types ${typeNames} have the same ${sortedTypes[0]!.fieldName} value.`;
|
|
305
|
+
} else {
|
|
306
|
+
const typeDescriptions = sortedTypes
|
|
307
|
+
.map((t) => `'${t.typeName}' (${t.fieldName})`)
|
|
308
|
+
.join(" and ");
|
|
309
|
+
message = `Duplicate typename value '${typenameValue}' in schema: types ${typeDescriptions} have the same value.`;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const firstType = sortedTypes[1]!;
|
|
313
|
+
diagnostics.push({
|
|
314
|
+
code: "DUPLICATE_TYPENAME_VALUE",
|
|
315
|
+
message,
|
|
316
|
+
severity: "error",
|
|
317
|
+
location: firstType.sourceLocation,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
valid: diagnostics.length === 0,
|
|
324
|
+
diagnostics,
|
|
325
|
+
};
|
|
326
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cli } from "gunshi";
|
|
2
|
+
import { docsCommand } from "./commands/docs.js";
|
|
2
3
|
import { genCommand } from "./commands/gen.js";
|
|
3
4
|
import { mainCommand } from "./commands/main.js";
|
|
4
5
|
|
|
@@ -6,6 +7,7 @@ await cli(process.argv.slice(2), mainCommand, {
|
|
|
6
7
|
name: "gqlkit",
|
|
7
8
|
version: "0.0.0",
|
|
8
9
|
subCommands: {
|
|
10
|
+
docs: docsCommand,
|
|
9
11
|
gen: genCommand,
|
|
10
12
|
},
|
|
11
13
|
});
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { access, mkdir, readFile, symlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join, relative } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { define } from "gunshi";
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const CLI_DOCS_DIR = join(__dirname, "../../docs");
|
|
8
|
+
const SKILL_NAME = "gqlkit-guide";
|
|
9
|
+
|
|
10
|
+
export interface RunDocsCommandOptions {
|
|
11
|
+
readonly output: string;
|
|
12
|
+
readonly claude: boolean;
|
|
13
|
+
readonly codex: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface RunDocsCommandResult {
|
|
17
|
+
readonly exitCode: number;
|
|
18
|
+
readonly filesWritten: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function exists(path: string): Promise<boolean> {
|
|
22
|
+
try {
|
|
23
|
+
await access(path);
|
|
24
|
+
return true;
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function detectClaudeEnvironment(dir: string): Promise<boolean> {
|
|
31
|
+
const claudeMdExists = await exists(join(dir, "CLAUDE.md"));
|
|
32
|
+
const claudeDirExists = await exists(join(dir, ".claude"));
|
|
33
|
+
return claudeMdExists || claudeDirExists;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function detectCodexEnvironment(dir: string): Promise<boolean> {
|
|
37
|
+
const agentsMdExists = await exists(join(dir, "AGENTS.md"));
|
|
38
|
+
const codexDirExists = await exists(join(dir, ".codex"));
|
|
39
|
+
return agentsMdExists || codexDirExists;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function generateSkillMd(): string {
|
|
43
|
+
return `---
|
|
44
|
+
name: ${SKILL_NAME}
|
|
45
|
+
description: Use when the user asks about "gqlkit", "gqlkit usage", "gqlkit schema definition", "gqlkit configuration", "gqlkit resolvers", "GraphQL code generation with gqlkit", or needs guidance on gqlkit conventions, type definitions, or integration with GraphQL servers or ORMs.
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
# gqlkit Guide
|
|
49
|
+
|
|
50
|
+
gqlkit generates GraphQL schema and resolver maps from TypeScript types and functions.
|
|
51
|
+
|
|
52
|
+
## How it works
|
|
53
|
+
|
|
54
|
+
1. Write TypeScript types in \`src/gqlkit/schema/\` → become GraphQL types
|
|
55
|
+
2. Write resolver functions using \`defineQuery\`, \`defineMutation\`, \`defineField\` → become GraphQL resolvers
|
|
56
|
+
3. Run \`gqlkit gen\` → outputs \`typeDefs\` and \`resolvers\` to \`src/gqlkit/__generated__/\`
|
|
57
|
+
|
|
58
|
+
## Design principles
|
|
59
|
+
|
|
60
|
+
- **Implement first**: Write types and resolvers, generate schema when ready. No edit-regenerate-implement loops.
|
|
61
|
+
- **Just types and functions**: Plain TypeScript with a thin API. No decorators, no complex generics.
|
|
62
|
+
- **Type-safe**: TypeScript types become GraphQL types. Resolver signatures checked at compile time.
|
|
63
|
+
|
|
64
|
+
## How to Use This Skill
|
|
65
|
+
|
|
66
|
+
Read [references/index.md](references/index.md) first. It contains the complete documentation index with all available topics.
|
|
67
|
+
|
|
68
|
+
Navigate to specific documentation files based on user needs as indicated in the index.
|
|
69
|
+
`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function generateRules(): string {
|
|
73
|
+
return `## gqlkit
|
|
74
|
+
|
|
75
|
+
When working with GraphQL schema, types, or resolvers using gqlkit, use the \`${SKILL_NAME}\` skill.
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function createSymlinkIfNotExists(
|
|
80
|
+
linkPath: string,
|
|
81
|
+
target: string,
|
|
82
|
+
): Promise<void> {
|
|
83
|
+
try {
|
|
84
|
+
await symlink(target, linkPath, "dir");
|
|
85
|
+
} catch (error) {
|
|
86
|
+
if ((error as NodeJS.ErrnoException).code !== "EEXIST") {
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function appendOrCreateFile(
|
|
93
|
+
filePath: string,
|
|
94
|
+
content: string,
|
|
95
|
+
): Promise<void> {
|
|
96
|
+
try {
|
|
97
|
+
const existing = await readFile(filePath, "utf-8");
|
|
98
|
+
if (!existing.includes("## gqlkit")) {
|
|
99
|
+
await writeFile(filePath, `${existing}\n${content}`);
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
103
|
+
await writeFile(filePath, content);
|
|
104
|
+
} else {
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface AiToolConfig {
|
|
111
|
+
readonly configDir: string;
|
|
112
|
+
readonly rulesFile: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function generateToolFiles(
|
|
116
|
+
outputDir: string,
|
|
117
|
+
config: AiToolConfig,
|
|
118
|
+
filesWritten: string[],
|
|
119
|
+
): Promise<void> {
|
|
120
|
+
const skillDir = join(outputDir, `${config.configDir}/skills/${SKILL_NAME}`);
|
|
121
|
+
await mkdir(skillDir, { recursive: true });
|
|
122
|
+
|
|
123
|
+
const skillMdPath = join(skillDir, "SKILL.md");
|
|
124
|
+
await writeFile(skillMdPath, generateSkillMd());
|
|
125
|
+
filesWritten.push(skillMdPath);
|
|
126
|
+
|
|
127
|
+
const referencesPath = join(skillDir, "references");
|
|
128
|
+
const relativePath = relative(skillDir, CLI_DOCS_DIR);
|
|
129
|
+
await createSymlinkIfNotExists(referencesPath, relativePath);
|
|
130
|
+
filesWritten.push(referencesPath);
|
|
131
|
+
|
|
132
|
+
const rulesPath = join(outputDir, config.rulesFile);
|
|
133
|
+
await appendOrCreateFile(rulesPath, generateRules());
|
|
134
|
+
filesWritten.push(rulesPath);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function runDocsCommand(
|
|
138
|
+
options: RunDocsCommandOptions,
|
|
139
|
+
): Promise<RunDocsCommandResult> {
|
|
140
|
+
if (!(await exists(CLI_DOCS_DIR))) {
|
|
141
|
+
console.error(
|
|
142
|
+
`Documentation directory not found: ${CLI_DOCS_DIR}\nRun "pnpm build" to generate documentation files.`,
|
|
143
|
+
);
|
|
144
|
+
return { exitCode: 1, filesWritten: [] };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const filesWritten: string[] = [];
|
|
148
|
+
|
|
149
|
+
const autoDetect = !options.claude && !options.codex;
|
|
150
|
+
const generateClaude =
|
|
151
|
+
options.claude ||
|
|
152
|
+
(autoDetect && (await detectClaudeEnvironment(options.output)));
|
|
153
|
+
const generateCodex =
|
|
154
|
+
options.codex ||
|
|
155
|
+
(autoDetect && (await detectCodexEnvironment(options.output)));
|
|
156
|
+
|
|
157
|
+
if (!generateClaude && !generateCodex) {
|
|
158
|
+
console.log(
|
|
159
|
+
"No AI tool environment detected. Use --claude or --codex to generate explicitly.",
|
|
160
|
+
);
|
|
161
|
+
return { exitCode: 0, filesWritten: [] };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (generateClaude) {
|
|
165
|
+
await generateToolFiles(
|
|
166
|
+
options.output,
|
|
167
|
+
{ configDir: ".claude", rulesFile: "CLAUDE.md" },
|
|
168
|
+
filesWritten,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (generateCodex) {
|
|
173
|
+
await generateToolFiles(
|
|
174
|
+
options.output,
|
|
175
|
+
{ configDir: ".codex", rulesFile: "AGENTS.md" },
|
|
176
|
+
filesWritten,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return { exitCode: 0, filesWritten };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export const docsCommand = define({
|
|
184
|
+
name: "docs",
|
|
185
|
+
args: {
|
|
186
|
+
output: {
|
|
187
|
+
type: "string",
|
|
188
|
+
description: "Output directory for generated files",
|
|
189
|
+
},
|
|
190
|
+
claude: {
|
|
191
|
+
type: "boolean",
|
|
192
|
+
description: `Generate Claude Code files (.claude/skills/${SKILL_NAME}/, CLAUDE.md)`,
|
|
193
|
+
},
|
|
194
|
+
codex: {
|
|
195
|
+
type: "boolean",
|
|
196
|
+
description: `Generate Codex files (.codex/skills/${SKILL_NAME}/, AGENTS.md)`,
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
run: async (ctx) => {
|
|
200
|
+
const output = ctx.values.output ?? process.cwd();
|
|
201
|
+
const claude = ctx.values.claude ?? false;
|
|
202
|
+
const codex = ctx.values.codex ?? false;
|
|
203
|
+
const result = await runDocsCommand({ output, claude, codex });
|
|
204
|
+
for (const file of result.filesWritten) {
|
|
205
|
+
console.log(`Generated: ${file}`);
|
|
206
|
+
}
|
|
207
|
+
if (result.exitCode !== 0) {
|
|
208
|
+
process.exitCode = result.exitCode;
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
});
|
package/src/config/types.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File extension mode for generated import statements.
|
|
3
|
+
* - "js": Convert .ts to .js (default, compatible with ESM + Node16)
|
|
4
|
+
* - "none": No extension (for bundlers)
|
|
5
|
+
* - "ts": Keep .ts extension (for Deno)
|
|
6
|
+
*/
|
|
7
|
+
export type ImportExtension = "js" | "none" | "ts";
|
|
8
|
+
|
|
1
9
|
/**
|
|
2
10
|
* Output configuration for generated files.
|
|
3
11
|
* All paths are relative to project root.
|
|
@@ -29,6 +37,13 @@ export interface OutputConfig {
|
|
|
29
37
|
* @default "src/gqlkit/__generated__/schema.graphql"
|
|
30
38
|
*/
|
|
31
39
|
readonly schemaPath?: string | null;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* File extension to use in generated import statements.
|
|
43
|
+
* @see ImportExtension
|
|
44
|
+
* @default "js"
|
|
45
|
+
*/
|
|
46
|
+
readonly importExtension?: ImportExtension;
|
|
32
47
|
}
|
|
33
48
|
|
|
34
49
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { createJiti } from "jiti";
|
|
4
|
+
import type { ImportExtension } from "../config/types.js";
|
|
4
5
|
import type { Diagnostic } from "../type-extractor/types/index.js";
|
|
5
6
|
import { validateConfig } from "./validator.js";
|
|
6
7
|
|
|
@@ -27,6 +28,8 @@ export interface ResolvedOutputConfig {
|
|
|
27
28
|
readonly typeDefsPath: string | null;
|
|
28
29
|
/** Schema SDL output path. Null suppresses output */
|
|
29
30
|
readonly schemaPath: string | null;
|
|
31
|
+
/** File extension for imports. Default: "js" */
|
|
32
|
+
readonly importExtension: ImportExtension;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
/**
|
|
@@ -64,6 +67,7 @@ const DEFAULT_OUTPUT_CONFIG: ResolvedOutputConfig = {
|
|
|
64
67
|
resolversPath: DEFAULT_RESOLVERS_PATH,
|
|
65
68
|
typeDefsPath: DEFAULT_TYPEDEFS_PATH,
|
|
66
69
|
schemaPath: DEFAULT_SCHEMA_PATH,
|
|
70
|
+
importExtension: "js",
|
|
67
71
|
};
|
|
68
72
|
|
|
69
73
|
const DEFAULT_HOOKS_CONFIG: ResolvedHooksConfig = {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ImportExtension } from "../config/types.js";
|
|
1
2
|
import type { Diagnostic } from "../type-extractor/types/index.js";
|
|
2
3
|
import {
|
|
3
4
|
DEFAULT_RESOLVERS_PATH,
|
|
@@ -186,6 +187,31 @@ function validateTsconfigPath(
|
|
|
186
187
|
return { resolved: value, diagnostics: [] };
|
|
187
188
|
}
|
|
188
189
|
|
|
190
|
+
function validateImportExtension(
|
|
191
|
+
value: unknown,
|
|
192
|
+
configPath: string,
|
|
193
|
+
): { resolved: ImportExtension; diagnostics: Diagnostic[] } {
|
|
194
|
+
if (value === undefined) {
|
|
195
|
+
return { resolved: "js", diagnostics: [] };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (value !== "js" && value !== "none" && value !== "ts") {
|
|
199
|
+
return {
|
|
200
|
+
resolved: "js",
|
|
201
|
+
diagnostics: [
|
|
202
|
+
{
|
|
203
|
+
code: "CONFIG_INVALID_IMPORT_EXTENSION",
|
|
204
|
+
message: 'output.importExtension must be "js", "none", or "ts"',
|
|
205
|
+
severity: "error",
|
|
206
|
+
location: { file: configPath, line: 1, column: 1 },
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return { resolved: value, diagnostics: [] };
|
|
213
|
+
}
|
|
214
|
+
|
|
189
215
|
function validateOutputConfig(
|
|
190
216
|
output: unknown,
|
|
191
217
|
configPath: string,
|
|
@@ -198,6 +224,7 @@ function validateOutputConfig(
|
|
|
198
224
|
resolversPath: DEFAULT_RESOLVERS_PATH,
|
|
199
225
|
typeDefsPath: DEFAULT_TYPEDEFS_PATH,
|
|
200
226
|
schemaPath: DEFAULT_SCHEMA_PATH,
|
|
227
|
+
importExtension: "js",
|
|
201
228
|
},
|
|
202
229
|
diagnostics: [],
|
|
203
230
|
};
|
|
@@ -228,10 +255,15 @@ function validateOutputConfig(
|
|
|
228
255
|
"output.schemaPath",
|
|
229
256
|
configPath,
|
|
230
257
|
);
|
|
258
|
+
const importExtensionResult = validateImportExtension(
|
|
259
|
+
output["importExtension"],
|
|
260
|
+
configPath,
|
|
261
|
+
);
|
|
231
262
|
|
|
232
263
|
diagnostics.push(...resolversPathResult.diagnostics);
|
|
233
264
|
diagnostics.push(...typeDefsPathResult.diagnostics);
|
|
234
265
|
diagnostics.push(...schemaPathResult.diagnostics);
|
|
266
|
+
diagnostics.push(...importExtensionResult.diagnostics);
|
|
235
267
|
|
|
236
268
|
if (diagnostics.length > 0) {
|
|
237
269
|
return { resolved: undefined, diagnostics };
|
|
@@ -242,6 +274,7 @@ function validateOutputConfig(
|
|
|
242
274
|
resolversPath: resolversPathResult.resolved,
|
|
243
275
|
typeDefsPath: typeDefsPathResult.resolved,
|
|
244
276
|
schemaPath: schemaPathResult.resolved,
|
|
277
|
+
importExtension: importExtensionResult.resolved,
|
|
245
278
|
},
|
|
246
279
|
diagnostics: [],
|
|
247
280
|
};
|