graphql-data-generator 0.1.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 +211 -0
- package/esm/_dnt.shims.d.ts +5 -0
- package/esm/_dnt.shims.js +61 -0
- package/esm/cli.d.ts +2 -0
- package/esm/cli.js +80 -0
- package/esm/codegen.d.ts +12 -0
- package/esm/codegen.js +474 -0
- package/esm/extendedTypes.d.ts +65 -0
- package/esm/extendedTypes.js +1 -0
- package/esm/index.d.ts +5 -0
- package/esm/index.js +4 -0
- package/esm/init.d.ts +16 -0
- package/esm/init.js +133 -0
- package/esm/package.json +3 -0
- package/esm/proxy.d.ts +8 -0
- package/esm/proxy.js +589 -0
- package/esm/types.d.ts +37 -0
- package/esm/types.js +1 -0
- package/esm/util.d.ts +11 -0
- package/esm/util.js +47 -0
- package/package.json +30 -0
package/esm/codegen.js
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { Kind, parse } from "graphql";
|
|
3
|
+
import fg from "fast-glob";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { raise } from "./util.js";
|
|
6
|
+
const getType = ({ type, ...props }) => {
|
|
7
|
+
if (type.kind === "NamedType") {
|
|
8
|
+
if (props.selections) {
|
|
9
|
+
const def = props.definitions[type.name.value];
|
|
10
|
+
const implementations = def?.[0].kind === "InterfaceTypeDefinition"
|
|
11
|
+
? Object.values(props.definitions)
|
|
12
|
+
.filter((v) => v?.[0].kind === "ObjectTypeDefinition" &&
|
|
13
|
+
v[0].interfaces?.some((i) => i.name.value === def[0].name.value) ||
|
|
14
|
+
false)
|
|
15
|
+
: def?.[0].kind === "UnionTypeDefinition"
|
|
16
|
+
? Object.values(props.definitions)
|
|
17
|
+
.filter((v) => "types" in def[0] &&
|
|
18
|
+
def[0].types?.some((t) => v && t.name.value === v[0].name.value) ||
|
|
19
|
+
false)
|
|
20
|
+
: [];
|
|
21
|
+
const groupedValues = getSelectionsType(type.name.value, props.selections, props.definitions, props.fragments, props.references, props.includeTypenames).reduce((union, [name, value, group]) => {
|
|
22
|
+
group ??= type.name.value;
|
|
23
|
+
if (!union[group]) {
|
|
24
|
+
union[group] = {};
|
|
25
|
+
Object.defineProperty(union[group], "__typename", {
|
|
26
|
+
enumerable: props.includeTypenames &&
|
|
27
|
+
props.definitions[group]?.[0].kind === "ObjectTypeDefinition",
|
|
28
|
+
value: { kind: "StringLiteral", value: group, optional: false },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
union[group][name] = value;
|
|
32
|
+
const def = props.definitions[group];
|
|
33
|
+
if (def) {
|
|
34
|
+
if (implementations.length) {
|
|
35
|
+
for (const implementation of implementations) {
|
|
36
|
+
implementation[1].add(name);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else
|
|
40
|
+
def[1].add(name);
|
|
41
|
+
}
|
|
42
|
+
return union;
|
|
43
|
+
}, {});
|
|
44
|
+
const value = groupedValues[type.name.value] ?? {};
|
|
45
|
+
delete groupedValues[type.name.value];
|
|
46
|
+
const nonExhaustive = implementations
|
|
47
|
+
.filter((o) => !(o[0].name.value in groupedValues)).map((o) => o[0].name.value);
|
|
48
|
+
return {
|
|
49
|
+
kind: "Object",
|
|
50
|
+
value,
|
|
51
|
+
conditionals: Object.values(groupedValues).map((value) => ({
|
|
52
|
+
kind: "Object",
|
|
53
|
+
value,
|
|
54
|
+
optional: false,
|
|
55
|
+
})),
|
|
56
|
+
nonExhaustive,
|
|
57
|
+
optional: props.optional ?? true,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const reference = props.references[type.name.value];
|
|
61
|
+
if (reference)
|
|
62
|
+
reference[1] = true;
|
|
63
|
+
return {
|
|
64
|
+
kind: "Name",
|
|
65
|
+
value: type.name.value,
|
|
66
|
+
optional: props.optional ?? true,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
else if (type.kind === "NonNullType") {
|
|
70
|
+
return getType({ ...props, type: type.type, optional: false });
|
|
71
|
+
}
|
|
72
|
+
else if (type.kind === "ListType") {
|
|
73
|
+
return {
|
|
74
|
+
kind: "List",
|
|
75
|
+
value: getType({ ...props, type: type.type, optional: true }),
|
|
76
|
+
optional: props.optional ?? true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
else
|
|
80
|
+
throw new Error(`Unhandled type '${type}'`);
|
|
81
|
+
};
|
|
82
|
+
const getSelectionsType = (name, selections, definitions, fragments, references, includeTypenames) => {
|
|
83
|
+
const selectionTypes = [];
|
|
84
|
+
for (const selection of selections) {
|
|
85
|
+
switch (selection.kind) {
|
|
86
|
+
case "Field": {
|
|
87
|
+
const selectionType = definitions[name];
|
|
88
|
+
if (!selectionType) {
|
|
89
|
+
throw new Error(`Could not find type '${selection.name.value}'`);
|
|
90
|
+
}
|
|
91
|
+
switch (selectionType[0].kind) {
|
|
92
|
+
case "ObjectTypeDefinition":
|
|
93
|
+
case "InterfaceTypeDefinition": {
|
|
94
|
+
const fieldType = selectionType[0].fields?.find((f) => f.name.value === selection.name.value);
|
|
95
|
+
if (!fieldType) {
|
|
96
|
+
throw new Error(`Could not find field '${selection.name.value}' on type '${name}'`);
|
|
97
|
+
}
|
|
98
|
+
selectionTypes.push([
|
|
99
|
+
selection.alias?.value ?? selection.name.value,
|
|
100
|
+
getType({
|
|
101
|
+
type: fieldType.type,
|
|
102
|
+
selections: selection.selectionSet?.selections,
|
|
103
|
+
definitions,
|
|
104
|
+
fragments,
|
|
105
|
+
references,
|
|
106
|
+
includeTypenames,
|
|
107
|
+
}),
|
|
108
|
+
]);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
case "UnionTypeDefinition": {
|
|
112
|
+
const types = selectionType[0].types;
|
|
113
|
+
if (!types) {
|
|
114
|
+
throw new Error(`Expected types to be present on union '${selectionType[0].name.value}'`);
|
|
115
|
+
}
|
|
116
|
+
selectionTypes.push(...getSelectionsType(types[0].name.value, // Assuming the selection is in all types
|
|
117
|
+
[selection], definitions, fragments, references, includeTypenames));
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
default:
|
|
121
|
+
throw new Error(`Unhandled selection type '${selectionType[0].kind}'`);
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
case "InlineFragment": {
|
|
126
|
+
const subSelection = getSelectionsType(selection.typeCondition?.name.value ?? name, selection.selectionSet.selections, definitions, fragments, references, includeTypenames);
|
|
127
|
+
const group = selection.typeCondition?.name.value ??
|
|
128
|
+
selection.directives?.map((d) => d.name).join(",");
|
|
129
|
+
selectionTypes.push(...(group
|
|
130
|
+
? subSelection.map(([n, t, g]) => [n, t, g ? `${group}:${g}` : group])
|
|
131
|
+
: subSelection));
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
case "FragmentSpread": {
|
|
135
|
+
const fragment = fragments[selection.name.value];
|
|
136
|
+
if (!fragment) {
|
|
137
|
+
throw new Error(`Could not find fragment '${selection.name.value}' in '${name}'`);
|
|
138
|
+
}
|
|
139
|
+
selectionTypes.push(...getSelectionsType(fragment.typeCondition.name.value, fragment.selectionSet.selections, definitions, fragments, references, includeTypenames));
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
default:
|
|
143
|
+
throw new Error(`Unhandled selection kind '${selection.kind}'`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return selectionTypes;
|
|
147
|
+
};
|
|
148
|
+
const getOperationType = (operation, definitions, fragments, root, references, includeTypenames) => ({
|
|
149
|
+
kind: "Object",
|
|
150
|
+
value: Object.fromEntries(operation.selectionSet.selections.map((selection) => {
|
|
151
|
+
if (selection.kind !== "Field") {
|
|
152
|
+
throw new Error(`Expected top-level selection on operation to be field, got '${selection.kind}'`);
|
|
153
|
+
}
|
|
154
|
+
const definition = root[selection.name.value];
|
|
155
|
+
if (!definition) {
|
|
156
|
+
throw new Error(`Could not find definition '${selection.name.value}'`);
|
|
157
|
+
}
|
|
158
|
+
return [
|
|
159
|
+
selection.alias?.value ?? selection.name.value,
|
|
160
|
+
getType({
|
|
161
|
+
type: definition.type,
|
|
162
|
+
selections: selection.selectionSet?.selections,
|
|
163
|
+
definitions,
|
|
164
|
+
fragments,
|
|
165
|
+
references,
|
|
166
|
+
includeTypenames,
|
|
167
|
+
}),
|
|
168
|
+
];
|
|
169
|
+
})),
|
|
170
|
+
optional: false,
|
|
171
|
+
});
|
|
172
|
+
const getOperationVariables = (operation, references) => ({
|
|
173
|
+
kind: "Object",
|
|
174
|
+
value: Object.fromEntries(operation.variableDefinitions?.map((v) => [
|
|
175
|
+
v.variable.name.value,
|
|
176
|
+
getType({
|
|
177
|
+
type: v.type,
|
|
178
|
+
references,
|
|
179
|
+
definitions: {},
|
|
180
|
+
fragments: {},
|
|
181
|
+
includeTypenames: false,
|
|
182
|
+
}),
|
|
183
|
+
]) ?? []),
|
|
184
|
+
optional: false,
|
|
185
|
+
});
|
|
186
|
+
const serializeType = (type, variables = false, depth = 0) => {
|
|
187
|
+
switch (type.kind) {
|
|
188
|
+
case "Name":
|
|
189
|
+
return `${type.value}${type.optional ? " | null" : ""}`;
|
|
190
|
+
case "List":
|
|
191
|
+
if (type.value.optional ||
|
|
192
|
+
(type.value.kind === "Object" &&
|
|
193
|
+
((type.value.conditionals?.length ?? 0) -
|
|
194
|
+
(Object.keys(type.value.value).length ? 0 : 1)))) {
|
|
195
|
+
return `(${serializeType(type.value, variables, depth)})[]${type.optional ? " | null" : ""}`;
|
|
196
|
+
}
|
|
197
|
+
return `${serializeType(type.value, variables, depth)}[]${type.optional ? " | null" : ""}`;
|
|
198
|
+
case "Object": {
|
|
199
|
+
const content = Object.entries(type.value).map(([key, value]) => `${" ".repeat(depth + 1)}${key}${value.optional && variables ? "?" : ""}: ${serializeType(value, variables, depth + 1)};`).join("\n");
|
|
200
|
+
const ands = [
|
|
201
|
+
`{${content ? `\n${content}\n${" ".repeat(depth)}` : ""}}`,
|
|
202
|
+
...(type.conditionals?.filter((c) => c.kind !== "Object" || !c.value.__typename)?.map((c) => `(${serializeType(c, variables, depth + 1)} | {})`) ??
|
|
203
|
+
[]),
|
|
204
|
+
];
|
|
205
|
+
const ors = type.conditionals
|
|
206
|
+
?.filter((c) => c.kind === "Object" && c.value.__typename)
|
|
207
|
+
.map((c) => serializeType(c, variables, depth)) ?? [];
|
|
208
|
+
if (type.nonExhaustive?.length && ors.length) {
|
|
209
|
+
ors.push(`{ __typename: ${type.nonExhaustive.map((v) => `"${v}"`).join(" | ")} }`);
|
|
210
|
+
}
|
|
211
|
+
// TODO: Ideally this would be better formatted, but then I need to track
|
|
212
|
+
// depth
|
|
213
|
+
return `${[
|
|
214
|
+
ands[0] === "{}" ? undefined : ands[0],
|
|
215
|
+
ors.length > 1 && (ands.length !== 1 || ands[0] !== "{}")
|
|
216
|
+
? `(${ors.join(" | ")})`
|
|
217
|
+
: ors.join(" | "),
|
|
218
|
+
...ands.slice(1),
|
|
219
|
+
].filter(Boolean).join(" & ")}${type.optional ? " | null" : ""}`;
|
|
220
|
+
}
|
|
221
|
+
case "StringLiteral":
|
|
222
|
+
return `"${type.value}"`;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
const serializeInput = (fields, optional, inputs, references) => ({
|
|
226
|
+
kind: "Object",
|
|
227
|
+
value: Object.fromEntries(fields.map((f) => [
|
|
228
|
+
f.name.value,
|
|
229
|
+
fillOutInput(getType({
|
|
230
|
+
type: f.type,
|
|
231
|
+
references,
|
|
232
|
+
definitions: {},
|
|
233
|
+
fragments: {},
|
|
234
|
+
includeTypenames: false,
|
|
235
|
+
}), inputs, references),
|
|
236
|
+
])),
|
|
237
|
+
optional,
|
|
238
|
+
});
|
|
239
|
+
const fillOutInput = (input, inputs, references) => {
|
|
240
|
+
switch (input.kind) {
|
|
241
|
+
case "List":
|
|
242
|
+
return { ...input, value: fillOutInput(input.value, inputs, references) };
|
|
243
|
+
case "Object":
|
|
244
|
+
return {
|
|
245
|
+
...input,
|
|
246
|
+
value: Object.fromEntries(Object.entries(input.value).map(([k, v]) => [k, fillOutInput(v, inputs, references)])),
|
|
247
|
+
};
|
|
248
|
+
case "Name": {
|
|
249
|
+
const def = inputs[input.value];
|
|
250
|
+
if (!def)
|
|
251
|
+
return input;
|
|
252
|
+
return serializeInput(def[0].fields ?? [], input.optional, inputs, references);
|
|
253
|
+
}
|
|
254
|
+
case "StringLiteral":
|
|
255
|
+
return input;
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
const operationNames = {
|
|
259
|
+
query: { types: "Query", list: "queries" },
|
|
260
|
+
mutation: { types: "Mutation", list: "mutations" },
|
|
261
|
+
subscription: { types: "Subscription", list: "subscriptions" },
|
|
262
|
+
};
|
|
263
|
+
const simpleType = (type, types, optional = true) => {
|
|
264
|
+
switch (type.kind) {
|
|
265
|
+
case Kind.NON_NULL_TYPE:
|
|
266
|
+
return simpleType(type.type, types, false);
|
|
267
|
+
case Kind.LIST_TYPE:
|
|
268
|
+
return {
|
|
269
|
+
kind: "List",
|
|
270
|
+
value: simpleType(type.type, types),
|
|
271
|
+
optional,
|
|
272
|
+
};
|
|
273
|
+
case Kind.NAMED_TYPE:
|
|
274
|
+
return {
|
|
275
|
+
kind: "Name",
|
|
276
|
+
value: type.name.value,
|
|
277
|
+
optional,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
export const codegen = (schema, files, { useEnums = true, scalars = {}, includeTypenames = true } = {}) => {
|
|
282
|
+
const schemaDoc = parse(schema);
|
|
283
|
+
const types = {};
|
|
284
|
+
const inputs = {};
|
|
285
|
+
const fragments = {};
|
|
286
|
+
const references = Object.fromEntries(["Int", "Float", "String", "Boolean", "ID"].map((name) => [name, [{
|
|
287
|
+
kind: Kind.SCALAR_TYPE_DEFINITION,
|
|
288
|
+
name: { kind: Kind.NAME, value: name },
|
|
289
|
+
}, false]]));
|
|
290
|
+
let query = {};
|
|
291
|
+
let mutation = {};
|
|
292
|
+
let subscription = {};
|
|
293
|
+
for (const definition of schemaDoc.definitions) {
|
|
294
|
+
switch (definition.kind) {
|
|
295
|
+
case "ObjectTypeDefinition":
|
|
296
|
+
if (definition.name.value === "Query") {
|
|
297
|
+
query = Object.fromEntries(definition.fields?.map((f) => [f.name.value, f]) ?? []);
|
|
298
|
+
}
|
|
299
|
+
else if (definition.name.value === "Mutation") {
|
|
300
|
+
mutation = Object.fromEntries(definition.fields?.map((f) => [f.name.value, f]) ?? []);
|
|
301
|
+
}
|
|
302
|
+
else if (definition.name.value === "Subscription") {
|
|
303
|
+
subscription = Object.fromEntries(definition.fields?.map((f) => [f.name.value, f]) ?? []);
|
|
304
|
+
}
|
|
305
|
+
else
|
|
306
|
+
types[definition.name.value] = [definition, new Set()];
|
|
307
|
+
break;
|
|
308
|
+
case "InputObjectTypeDefinition":
|
|
309
|
+
inputs[definition.name.value] = [definition, new Set()];
|
|
310
|
+
break;
|
|
311
|
+
case "InterfaceTypeDefinition":
|
|
312
|
+
types[definition.name.value] = [definition, new Set()];
|
|
313
|
+
break;
|
|
314
|
+
case "UnionTypeDefinition": {
|
|
315
|
+
const prev = types[definition.name.value];
|
|
316
|
+
if (prev?.[0].kind === "UnionTypeDefinition") {
|
|
317
|
+
types[definition.name.value] = [{
|
|
318
|
+
...prev[0],
|
|
319
|
+
types: [...(prev[0].types ?? []), ...(definition.types ?? [])],
|
|
320
|
+
}, prev[1]];
|
|
321
|
+
}
|
|
322
|
+
else
|
|
323
|
+
types[definition.name.value] = [definition, new Set()];
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
case "EnumTypeDefinition":
|
|
327
|
+
if (!references[definition.name.value]) {
|
|
328
|
+
references[definition.name.value] = [definition, false];
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
const prev = references[definition.name.value][0];
|
|
332
|
+
references[definition.name.value][0] = {
|
|
333
|
+
...prev,
|
|
334
|
+
values: [
|
|
335
|
+
...(prev.values ?? []),
|
|
336
|
+
...(definition.values ?? []),
|
|
337
|
+
],
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
break;
|
|
341
|
+
case "ScalarTypeDefinition":
|
|
342
|
+
references[definition.name.value] = [definition, false];
|
|
343
|
+
break;
|
|
344
|
+
default:
|
|
345
|
+
throw new Error(`Unhandled definition type '${definition.kind}'`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const roots = { query, mutation, subscription };
|
|
349
|
+
const operations = {
|
|
350
|
+
query: [],
|
|
351
|
+
mutation: [],
|
|
352
|
+
subscription: [],
|
|
353
|
+
};
|
|
354
|
+
for (const { path, content } of files) {
|
|
355
|
+
const dom = parse(content);
|
|
356
|
+
for (const definition of dom.definitions) {
|
|
357
|
+
switch (definition.kind) {
|
|
358
|
+
case "OperationDefinition": {
|
|
359
|
+
const name = definition.name?.value;
|
|
360
|
+
if (!name) {
|
|
361
|
+
console.warn(`Skipping unnamed operation in '${path}'`);
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
operations[definition.operation].push({
|
|
365
|
+
name,
|
|
366
|
+
path,
|
|
367
|
+
definition: definition,
|
|
368
|
+
});
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
case "FragmentDefinition":
|
|
372
|
+
fragments[definition.name.value] = definition;
|
|
373
|
+
break;
|
|
374
|
+
default:
|
|
375
|
+
throw new Error(`Unhandled definition kind '${definition.kind}' in operation file '${path}'`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
const handledInputs = new Set();
|
|
380
|
+
const serializedTypes = Object.entries(operations).filter(([, v]) => v.length)
|
|
381
|
+
.flatMap(([name, collection]) => [
|
|
382
|
+
...collection.flatMap((c) => c.definition.variableDefinitions?.map((v) => {
|
|
383
|
+
let type = getType({
|
|
384
|
+
type: v.type,
|
|
385
|
+
definitions: inputs,
|
|
386
|
+
references,
|
|
387
|
+
fragments: {},
|
|
388
|
+
includeTypenames: false,
|
|
389
|
+
});
|
|
390
|
+
let inputType = fillOutInput(type, inputs, references);
|
|
391
|
+
if (JSON.stringify(type) === JSON.stringify(inputType))
|
|
392
|
+
return;
|
|
393
|
+
if (type.kind === "List")
|
|
394
|
+
type = type.value;
|
|
395
|
+
if (inputType.kind === "List")
|
|
396
|
+
inputType = inputType.value;
|
|
397
|
+
if (type.kind !== "Name") {
|
|
398
|
+
throw new Error(`Could not find type for variable '${v.variable.name.value}'`);
|
|
399
|
+
}
|
|
400
|
+
if (handledInputs.has(type.value))
|
|
401
|
+
return;
|
|
402
|
+
handledInputs.add(type.value);
|
|
403
|
+
// return `type ${type.value} = ${serializeType(inputType)};`;
|
|
404
|
+
}).filter(Boolean)),
|
|
405
|
+
`export type ${operationNames[name].types} = {
|
|
406
|
+
${collection.map((o) => {
|
|
407
|
+
const data = serializeType(getOperationType(o.definition, types, fragments, roots[o.definition.operation], references, includeTypenames), false, 2);
|
|
408
|
+
const variables = getOperationVariables(o.definition, references);
|
|
409
|
+
return ` ${o.name}: {
|
|
410
|
+
data: ${data};${variables.kind === "Object" && Object.keys(variables.value).length
|
|
411
|
+
? `
|
|
412
|
+
variables: ${serializeType(variables, true, 2)};`
|
|
413
|
+
: ""}
|
|
414
|
+
};`;
|
|
415
|
+
}).join("\n")}
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
export const ${operationNames[name].list} = {
|
|
419
|
+
${collection.map((o) => ` ${o.name}: "${o.path}",`).join("\n")}
|
|
420
|
+
};`,
|
|
421
|
+
]);
|
|
422
|
+
if (handledInputs.size) {
|
|
423
|
+
serializedTypes.unshift(...Array.from(handledInputs).map((i) => {
|
|
424
|
+
const def = inputs[i]?.[0];
|
|
425
|
+
if (!def)
|
|
426
|
+
throw new Error(`Could not find input '${i}'`);
|
|
427
|
+
return `type ${i} = ${serializeType(serializeInput(def.fields ?? [], false, {}, references))};`;
|
|
428
|
+
}), `export type Inputs = {
|
|
429
|
+
${Array.from(handledInputs).map((i) => ` ${i}: ${i};`).join("\n")}
|
|
430
|
+
};`, `export const inputs = [${Array.from(handledInputs).map((i) => `"${i}"`).join(", ")}] as const;`);
|
|
431
|
+
}
|
|
432
|
+
const usedTypes = Object.entries(types)
|
|
433
|
+
.map(([name, info]) => [name, ...info])
|
|
434
|
+
.filter((data) => data[1].kind === "ObjectTypeDefinition" && data[2].size > 0);
|
|
435
|
+
if (usedTypes.length) {
|
|
436
|
+
serializedTypes.unshift(...usedTypes.map(([name, type, usage]) => `type ${name} = {
|
|
437
|
+
${includeTypenames ? ` __typename: "${name}";\n` : ""}${type.fields?.filter((f) => usage.has(f.name.value)).map((v) => ` ${v.name.value}: ${serializeType(simpleType(v.type, types))};`).join("\n")}
|
|
438
|
+
};`), `export type Types = {
|
|
439
|
+
${usedTypes.map(([name]) => ` ${name}: ${name};`).join("\n")}
|
|
440
|
+
};`, `export const types = [${usedTypes.map(([name]) => `"${name}"`).join(", ")}] as const;`);
|
|
441
|
+
}
|
|
442
|
+
const usedReferences = Object.values(references).filter((r) => r[1]).map((r) => r[0]);
|
|
443
|
+
serializedTypes.unshift(...usedReferences.map((r) => {
|
|
444
|
+
// TODO: warn if missing and use unknown instead
|
|
445
|
+
if (r.kind === "ScalarTypeDefinition") {
|
|
446
|
+
return `type ${r.name.value} = ${scalars[r.name.value] ??
|
|
447
|
+
raise(`Could not find scalar '${r.name.value}'`)};`;
|
|
448
|
+
}
|
|
449
|
+
if (useEnums) {
|
|
450
|
+
return `enum ${r.name.value} {
|
|
451
|
+
${r.values?.map((r) => r.name.value).join(",\n ")}
|
|
452
|
+
}`;
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
return `type ${r.name.value} = ${r.values?.map((r) => `"${r.name.value}"`).join(" | ")};`;
|
|
456
|
+
}
|
|
457
|
+
}));
|
|
458
|
+
return serializedTypes.join("\n\n") + "\n";
|
|
459
|
+
};
|
|
460
|
+
export const loadFiles = async (schemaPath, operationDirs) => {
|
|
461
|
+
const operationPromises = [];
|
|
462
|
+
for (const dir of operationDirs) {
|
|
463
|
+
for await (const path of fg.stream(join(dir, "**/*.gql"))) {
|
|
464
|
+
operationPromises.push(readFile(path.toString(), "utf-8").then((content) => ({
|
|
465
|
+
path: path.toString(),
|
|
466
|
+
content,
|
|
467
|
+
})));
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return [
|
|
471
|
+
await readFile(schemaPath, "utf-8"),
|
|
472
|
+
await Promise.all(operationPromises),
|
|
473
|
+
];
|
|
474
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ContravariantEmpty, OperationMock, Patch, SimpleOperationMock } from "./types.js";
|
|
2
|
+
type DefaultObjectTransformSwitch<T, U> = T extends (infer G)[] ? DefaultObjectTransformSwitch<G, U>[] : DefaultObjectTransform<T, U>;
|
|
3
|
+
type DefaultObjectTransform<T, U = T> = {
|
|
4
|
+
[K in keyof T]?: T[K] extends object ? DefaultObjectTransformSwitch<T[K], U> | ((host: U) => DefaultObjectTransformSwitch<T[K], U>) : T[K] | ((host: U) => T[K]);
|
|
5
|
+
};
|
|
6
|
+
export type Shift<T extends unknown[]> = T extends [infer _First, ...infer Rest] ? Rest : [];
|
|
7
|
+
type OperationBuilder<Data extends Record<string, unknown> = Record<string, unknown>, Variables = unknown, Transforms = unknown, Extra = ContravariantEmpty> = ((...patches: ((Patch<SimpleOperationMock<Data, Variables>> & Partial<Extra>) | ((prev: SimpleOperationMock<Data, Variables>) => Patch<SimpleOperationMock<Data, Variables>>))[]) => OperationBuilderWithMock<Data, Variables, Transforms, Extra>) & {
|
|
8
|
+
variables: (variables: Patch<Variables> | ((data: Data, variables: Variables) => Patch<Variables>)) => OperationBuilderWithMock<Data, Variables, Transforms, Extra>;
|
|
9
|
+
data: (data: Patch<Data> | ((variables: Variables, data: Data) => Patch<Data>)) => OperationBuilderWithMock<Data, Variables, Transforms, Extra>;
|
|
10
|
+
patch: (patch: Patch<{
|
|
11
|
+
data: Data;
|
|
12
|
+
variables: Variables;
|
|
13
|
+
}>) => OperationBuilderWithMock<Data, Variables, Transforms, Extra>;
|
|
14
|
+
} & {
|
|
15
|
+
[Transform in keyof Transforms]: Transforms[Transform] extends (...args: any[]) => unknown ? (...params: Shift<Parameters<Transforms[Transform]>>) => OperationBuilderWithMock<Data, Variables, Transforms, Extra> : () => OperationBuilderWithMock<Data, Variables, Transforms, Extra>;
|
|
16
|
+
};
|
|
17
|
+
type OperationBuilderWithMock<Data extends Record<string, unknown>, Variables, Transforms, Extra> = OperationBuilder<Data, Variables, Transforms> & OperationMock<Data, Variables> & Partial<Extra>;
|
|
18
|
+
type ObjectTransforms<T, Transforms> = {
|
|
19
|
+
[Transform in keyof Transforms]: Transforms[Transform] extends (...args: any[]) => any ? (...args: Shift<Parameters<Transforms[Transform]>>) => T & ObjectTransforms<T, Transforms> : () => T & ObjectTransforms<T, Transforms>;
|
|
20
|
+
} & {
|
|
21
|
+
patch: (patch: Patch<T> | ((prev: T) => Patch<T>)) => T & ObjectTransforms<T, Transforms>;
|
|
22
|
+
};
|
|
23
|
+
type ObjectBuilder<T, Transforms> = ((...patches: (Patch<T> | ((previous: T) => Patch<T>))[]) => T & ObjectTransforms<T, Transforms>) & ObjectTransforms<T, Transforms>;
|
|
24
|
+
type CapitalizeFirst<S extends string> = S extends `${infer F}${infer R}` ? `${Uppercase<F>}${R}` : S;
|
|
25
|
+
type PrefixKeys<T, Prefix extends string> = {
|
|
26
|
+
[K in keyof T as `${Prefix}${CapitalizeFirst<string & K>}`]: T[K];
|
|
27
|
+
};
|
|
28
|
+
type MapObjectsToBuilders<T, Transforms> = {
|
|
29
|
+
[K in keyof T]: ObjectBuilder<T[K], K extends keyof Transforms ? Transforms[K] : ContravariantEmpty>;
|
|
30
|
+
};
|
|
31
|
+
export type MapObjectsToTransforms<Objects extends Record<string, Record<string, unknown>>> = {
|
|
32
|
+
[Object in keyof Objects]?: Record<string, DefaultObjectTransform<Objects[Object], Objects[Object]> | ((prev: Objects[Object], ...args: any[]) => Patch<Objects[Object]>)>;
|
|
33
|
+
};
|
|
34
|
+
type InnerMapOperationsToTransforms<Operations> = {
|
|
35
|
+
[Operation in keyof Operations]?: Record<string, InferOperationTransforms<Operations[Operation]>>;
|
|
36
|
+
};
|
|
37
|
+
export type MapOperationsToTransforms<Queries, Mutations, Subscriptions, Types, Inputs> = InnerMapOperationsToTransforms<ResolveConflicts<Queries, Mutations, Subscriptions, Types, Inputs>>;
|
|
38
|
+
type MapOperationsToBuilders<T, Transforms, Extra> = {
|
|
39
|
+
[K in keyof T]: OperationBuilder<T[K] extends {
|
|
40
|
+
data: infer U;
|
|
41
|
+
} ? U extends Record<string, unknown> ? U : Record<string, unknown> : Record<string, unknown>, T[K] extends {
|
|
42
|
+
variables: infer U;
|
|
43
|
+
} ? U : unknown, K extends keyof Transforms ? Transforms[K] : ContravariantEmpty, Extra>;
|
|
44
|
+
};
|
|
45
|
+
type ResolveOperationConflicts<T, Name extends string, A, B, C, D> = Omit<T, keyof A | keyof B | keyof C | keyof D> & PrefixKeys<Pick<T, keyof T & (keyof A | keyof B | keyof C | keyof D)>, Name>;
|
|
46
|
+
type ResolveConflicts<Queries, Mutations, Subscriptions, Types, Inputs> = ResolveOperationConflicts<Queries, "queries", Mutations, Subscriptions, Types, Inputs> & ResolveOperationConflicts<Mutations, "mutations", Queries, Subscriptions, Types, Inputs> & ResolveOperationConflicts<Subscriptions, "subscriptions", Queries, Mutations, Types, Inputs>;
|
|
47
|
+
type DefaultOperationTransform<T> = {
|
|
48
|
+
data?: T extends {
|
|
49
|
+
data: infer D;
|
|
50
|
+
} ? Patch<D> : never;
|
|
51
|
+
variables?: T extends {
|
|
52
|
+
variables: infer V;
|
|
53
|
+
} ? Patch<V> : never;
|
|
54
|
+
};
|
|
55
|
+
type OperationTransform<T> = (b: {
|
|
56
|
+
data: T extends {
|
|
57
|
+
data: infer U;
|
|
58
|
+
} ? U : never;
|
|
59
|
+
variables: T extends {
|
|
60
|
+
variables: infer U;
|
|
61
|
+
} ? U : never;
|
|
62
|
+
}, ...args: any[]) => Patch<T>;
|
|
63
|
+
type InferOperationTransforms<T> = DefaultOperationTransform<T> | OperationTransform<T>;
|
|
64
|
+
export type Build<Queries, Mutations, Subscriptions, Types, Inputs, Transforms, Extra> = MapObjectsToBuilders<Types & Inputs, Transforms> & MapOperationsToBuilders<ResolveConflicts<Queries, Mutations, Subscriptions, Types, Inputs>, Transforms, Extra>;
|
|
65
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/esm/index.d.ts
ADDED
package/esm/index.js
ADDED
package/esm/init.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Build, MapObjectsToTransforms, MapOperationsToTransforms } from "./extendedTypes.js";
|
|
2
|
+
import type { ContravariantEmpty, OperationMock } from "./types.js";
|
|
3
|
+
export declare const init: <Query extends Record<string, {
|
|
4
|
+
data: Record<string, unknown>;
|
|
5
|
+
variables?: Record<string, unknown>;
|
|
6
|
+
}>, Mutation extends Record<string, {
|
|
7
|
+
data: Record<string, unknown>;
|
|
8
|
+
variables?: Record<string, unknown>;
|
|
9
|
+
}>, Subscription extends Record<string, {
|
|
10
|
+
data: Record<string, unknown>;
|
|
11
|
+
variables?: Record<string, unknown>;
|
|
12
|
+
}>, Types extends Record<string, Record<string, unknown>>, Inputs extends Record<string, Record<string, unknown>>, Extra = object>(schema: string, queries: { [operation in keyof Query]: string; }, mutations: { [operation in keyof Mutation]: string; }, subscriptions: { [operation in keyof Subscription]: string; }, types: readonly (keyof Types & string)[], inputs: readonly (keyof Inputs & string)[], scalars: {
|
|
13
|
+
[name: string]: ((typeName: string, fieldName: string) => unknown) | string | number | boolean | null;
|
|
14
|
+
}, options?: {
|
|
15
|
+
finalizeOperation: <T extends OperationMock & Partial<Extra>>(operation: T) => T;
|
|
16
|
+
}) => <Transforms extends MapObjectsToTransforms<Types & Inputs> & MapOperationsToTransforms<Query, Mutation, Subscription, Types, Inputs>>(fn: (b: Build<Query, Mutation, Subscription, Types, Inputs, ContravariantEmpty, Extra>) => Transforms) => Build<Query, Mutation, Subscription, Types, Inputs, Transforms, Extra>;
|