graphql-jerky 1.0.8 → 1.0.10
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 +24 -0
- package/build/lib.cjs +164 -96
- package/build/lib.mjs +168 -100
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -31,9 +31,33 @@ By default, the working directory is searched recursively for `*.graphql` files.
|
|
|
31
31
|
All of which are combined into a single schema and rendered to `schema_gen.ts`
|
|
32
32
|
in the same directory.
|
|
33
33
|
|
|
34
|
+
Jerky reads the schema files and the resolver files in the order of their
|
|
35
|
+
paths. Thus, the output is the same on all platforms and file systems.
|
|
36
|
+
|
|
34
37
|
A path to a directory or file can be passed as the first parameter to limit to
|
|
35
38
|
scope of the search.
|
|
36
39
|
|
|
40
|
+
### Type extensions
|
|
41
|
+
|
|
42
|
+
Type extensions (`extend type`, `extend interface`, `extend union`,
|
|
43
|
+
`extend enum`, `extend input` and `extend scalar`) are merged into the type
|
|
44
|
+
that they extend. The extension and the definition can be in different files,
|
|
45
|
+
in any order.
|
|
46
|
+
|
|
47
|
+
```gql
|
|
48
|
+
# user.graphql
|
|
49
|
+
type Query {
|
|
50
|
+
me: User
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# post.graphql
|
|
54
|
+
extend type Query {
|
|
55
|
+
posts: [Post!]!
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
An extension of a type that is not defined causes an error.
|
|
60
|
+
|
|
37
61
|
### Options
|
|
38
62
|
|
|
39
63
|
#### --graphql
|
package/build/lib.cjs
CHANGED
|
@@ -277,6 +277,30 @@ class RequiredPackages {
|
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
+
/**
|
|
281
|
+
* Glob options that make Windows paths work as patterns. On Windows, a
|
|
282
|
+
* backslash is a path separator and not an escape character.
|
|
283
|
+
*/
|
|
284
|
+
const globOptions = {
|
|
285
|
+
windowsPathsNoEscape: path.sep === "\\",
|
|
286
|
+
};
|
|
287
|
+
/**
|
|
288
|
+
* Converts native path separators to forward slashes.
|
|
289
|
+
*
|
|
290
|
+
* Glob patterns and module specifiers must use forward slashes, even on
|
|
291
|
+
* Windows.
|
|
292
|
+
*/
|
|
293
|
+
function toPosixPath(p) {
|
|
294
|
+
return path.sep === "\\" ? p.replaceAll("\\", "/") : p;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Compares paths by code unit. The result does not change with the locale
|
|
298
|
+
* or with the order in which the file system returns entries.
|
|
299
|
+
*/
|
|
300
|
+
function comparePaths(a, b) {
|
|
301
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
302
|
+
}
|
|
303
|
+
|
|
280
304
|
function renderSymbolReference(pkgs, ref, alias) {
|
|
281
305
|
const ns = ref.module
|
|
282
306
|
? jen.jen.id(pkgs.addRequires(ref.module, ref.symbol, alias ?? ref.alias))
|
|
@@ -285,10 +309,10 @@ function renderSymbolReference(pkgs, ref, alias) {
|
|
|
285
309
|
}
|
|
286
310
|
|
|
287
311
|
function compareEntryKey([a], [b]) {
|
|
288
|
-
return a.localeCompare(b);
|
|
312
|
+
return a.localeCompare(b, "en");
|
|
289
313
|
}
|
|
290
314
|
function compareName({ name: a }, { name: b }) {
|
|
291
|
-
return a.localeCompare(b);
|
|
315
|
+
return a.localeCompare(b, "en");
|
|
292
316
|
}
|
|
293
317
|
const PRIMITIVE_TYPES = new Set(["ID", "Int", "Float", "String", "Boolean"]);
|
|
294
318
|
function isPrimitiveType(type) {
|
|
@@ -307,8 +331,9 @@ function renderImports(base, imports, aliases, extMode = "omit") {
|
|
|
307
331
|
if (mod.startsWith("file:///")) {
|
|
308
332
|
mod = node_url.fileURLToPath(mod);
|
|
309
333
|
}
|
|
310
|
-
if (
|
|
311
|
-
|
|
334
|
+
if (path.isAbsolute(mod)) {
|
|
335
|
+
// NOTE: module specifiers always use forward slashes, even on Windows.
|
|
336
|
+
mod = toPosixPath(path.relative(base, mod));
|
|
312
337
|
if (!mod.startsWith(".")) {
|
|
313
338
|
mod = `./${mod}`;
|
|
314
339
|
}
|
|
@@ -324,8 +349,8 @@ function renderImports(base, imports, aliases, extMode = "omit") {
|
|
|
324
349
|
}
|
|
325
350
|
return jen.import
|
|
326
351
|
.obj(...Array.from(symbols)
|
|
327
|
-
.sort((a, b) => a.localeCompare(b) ||
|
|
328
|
-
importAlias(a).localeCompare(importAlias(b)))
|
|
352
|
+
.sort((a, b) => a.localeCompare(b, "en") ||
|
|
353
|
+
importAlias(a).localeCompare(importAlias(b), "en"))
|
|
329
354
|
.map((symbol) => symbol in aliases
|
|
330
355
|
? jen.id(aliases[symbol]).as.id(symbol)
|
|
331
356
|
: jen.id(symbol)))
|
|
@@ -340,14 +365,8 @@ function renderAstNode(astNode) {
|
|
|
340
365
|
}
|
|
341
366
|
function isBuiltInDirective(name) {
|
|
342
367
|
// https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
case "skip":
|
|
346
|
-
case "include":
|
|
347
|
-
case "specifiedBy":
|
|
348
|
-
return true;
|
|
349
|
-
}
|
|
350
|
-
return false;
|
|
368
|
+
// Use the list from graphql, because new versions add directives (for example, @oneOf).
|
|
369
|
+
return graphql.specifiedDirectives.some((d) => d.name === name);
|
|
351
370
|
}
|
|
352
371
|
function truthify(arr) {
|
|
353
372
|
return arr.filter(Boolean);
|
|
@@ -1098,9 +1117,9 @@ class GeneratorContext {
|
|
|
1098
1117
|
];
|
|
1099
1118
|
}
|
|
1100
1119
|
renderFile(outfile, renderStatements) {
|
|
1101
|
-
const base =
|
|
1120
|
+
const base = path.resolve(outfile);
|
|
1102
1121
|
const statements = renderStatements.call(this);
|
|
1103
|
-
const [localImports, externalImports] = partition(Object.entries(this._pkgs.requires), ([pkg]) =>
|
|
1122
|
+
const [localImports, externalImports] = partition(Object.entries(this._pkgs.requires), ([pkg]) => path.isAbsolute(pkg));
|
|
1104
1123
|
return format(jen
|
|
1105
1124
|
.statements(renderImports(path.dirname(base), externalImports, this._pkgs.aliases, this._extMode), renderImports(path.dirname(base), localImports, this._pkgs.aliases, this._extMode), ...statements)
|
|
1106
1125
|
.toString());
|
|
@@ -1281,23 +1300,25 @@ async function parseResolverFile(path) {
|
|
|
1281
1300
|
async function parseTypeResolvers(typeName, glob$1, ignore) {
|
|
1282
1301
|
const shouldIgnore = createMatcher(ignore);
|
|
1283
1302
|
const result = {};
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1303
|
+
// NOTE: sort the files so that the result (and the warnings) do not change
|
|
1304
|
+
// with the order in which the file system returns entries.
|
|
1305
|
+
const fullpaths = (await glob.glob(glob$1, { ...globOptions, withFileTypes: true }))
|
|
1306
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".ts"))
|
|
1307
|
+
.map((entry) => toPosixPath(entry.fullpath()))
|
|
1308
|
+
.filter((fullpath) => !shouldIgnore(fullpath))
|
|
1309
|
+
.sort(comparePaths);
|
|
1310
|
+
for (const fullpath of fullpaths) {
|
|
1311
|
+
for (const [name, reference] of Object.entries(await parseResolverFile(fullpath))) {
|
|
1312
|
+
let realName = name;
|
|
1313
|
+
if (realName === "default" &&
|
|
1314
|
+
typeof glob$1 === "string" &&
|
|
1315
|
+
toPosixPath(glob$1).endsWith("/*.ts")) {
|
|
1316
|
+
realName = path.basename(fullpath, path.extname(fullpath));
|
|
1317
|
+
}
|
|
1318
|
+
if (realName in result) {
|
|
1319
|
+
console.warn(`WARNING: Duplicate resolver name: ${typeName ? `${typeName}.` : ""}${realName} in ${fullpath} and ${result[realName].module}`);
|
|
1320
|
+
}
|
|
1321
|
+
result[realName] = reference;
|
|
1301
1322
|
}
|
|
1302
1323
|
}
|
|
1303
1324
|
return result;
|
|
@@ -1309,8 +1330,9 @@ async function parse$2(dirpath, ignore) {
|
|
|
1309
1330
|
}
|
|
1310
1331
|
const out = {};
|
|
1311
1332
|
const shouldIgnore = createMatcher(ignore);
|
|
1312
|
-
|
|
1313
|
-
|
|
1333
|
+
const names = (await fs.readdir(dirpath)).sort(comparePaths);
|
|
1334
|
+
for (const name of names) {
|
|
1335
|
+
const file = path.posix.join(toPosixPath(dirpath), name);
|
|
1314
1336
|
const entry = await fs.stat(file);
|
|
1315
1337
|
if (entry.isDirectory()) {
|
|
1316
1338
|
out[name] = await parseTypeResolvers(name, `${file}/*.ts`, shouldIgnore);
|
|
@@ -1613,11 +1635,14 @@ function setTraceToSchema(err, name, loc) {
|
|
|
1613
1635
|
}
|
|
1614
1636
|
return err;
|
|
1615
1637
|
}
|
|
1638
|
+
function isMergableDefinitionNode(node) {
|
|
1639
|
+
return graphql.isTypeDefinitionNode(node) || node.kind === graphql.Kind.DIRECTIVE_DEFINITION;
|
|
1640
|
+
}
|
|
1616
1641
|
function namedNodesDict(nodes = []) {
|
|
1617
1642
|
return Object.fromEntries(nodes.map((node) => [node.name.value, node]));
|
|
1618
1643
|
}
|
|
1619
1644
|
function sortNamedNodes(nodes) {
|
|
1620
|
-
return Array.from(nodes).sort(({ name: { value: a } }, { name: { value: b } }) => a.localeCompare(b));
|
|
1645
|
+
return Array.from(nodes).sort(({ name: { value: a } }, { name: { value: b } }) => a.localeCompare(b, "en"));
|
|
1621
1646
|
}
|
|
1622
1647
|
function isIntValueNodeEqual(a, b) {
|
|
1623
1648
|
return a.value === b.value;
|
|
@@ -1726,7 +1751,7 @@ function mergeFieldDefinitionNodes(name, a, b) {
|
|
|
1726
1751
|
console.warn(`WARNING: there is more than one declaration for ${name}.${f.name.value}${traceLine(f.loc)}${traceLine(prev.loc, "previously ")}\n`);
|
|
1727
1752
|
fields[f.name.value] = {
|
|
1728
1753
|
...prev,
|
|
1729
|
-
directives: mergeDirectiveNodes(
|
|
1754
|
+
directives: mergeDirectiveNodes(prev.directives, f.directives),
|
|
1730
1755
|
};
|
|
1731
1756
|
}
|
|
1732
1757
|
else {
|
|
@@ -1751,7 +1776,7 @@ function mergeInputValueDefinitionNodes(name, a, b) {
|
|
|
1751
1776
|
console.warn(`WARNING: there is more than one declaration for ${name}.${f.name.value}${traceLine(f.loc)}${traceLine(prev.loc, "previously ")}\n`);
|
|
1752
1777
|
fields[f.name.value] = {
|
|
1753
1778
|
...prev,
|
|
1754
|
-
directives: mergeDirectiveNodes(
|
|
1779
|
+
directives: mergeDirectiveNodes(prev.directives, f.directives),
|
|
1755
1780
|
};
|
|
1756
1781
|
}
|
|
1757
1782
|
else {
|
|
@@ -1770,18 +1795,19 @@ function mergeNamedNodes(a, b) {
|
|
|
1770
1795
|
}
|
|
1771
1796
|
return Object.values(nodes);
|
|
1772
1797
|
}
|
|
1773
|
-
function mergeDirectiveNodes(
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1798
|
+
function mergeDirectiveNodes(a, b) {
|
|
1799
|
+
// Keep each occurrence, because a repeatable directive can occur more than
|
|
1800
|
+
// once. Skip only an occurrence that is identical to one that is already
|
|
1801
|
+
// applied. buildASTSchema rejects a non-repeatable directive that occurs
|
|
1802
|
+
// more than once.
|
|
1803
|
+
const directives = Array.from(a ?? []);
|
|
1804
|
+
for (const d of b ?? []) {
|
|
1805
|
+
if (!directives.some((prev) => prev.name.value === d.name.value &&
|
|
1806
|
+
isArgumentNodesEqual(prev.arguments, d.arguments))) {
|
|
1807
|
+
directives.push(d);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
return directives;
|
|
1785
1811
|
}
|
|
1786
1812
|
function mergeScalarTypeDefinitionNodes(a, b) {
|
|
1787
1813
|
if (a.name.value !== b.name.value) {
|
|
@@ -1789,8 +1815,10 @@ function mergeScalarTypeDefinitionNodes(a, b) {
|
|
|
1789
1815
|
}
|
|
1790
1816
|
return {
|
|
1791
1817
|
kind: graphql.Kind.SCALAR_TYPE_DEFINITION,
|
|
1818
|
+
loc: a.loc,
|
|
1792
1819
|
name: a.name,
|
|
1793
|
-
|
|
1820
|
+
description: a.description || b.description,
|
|
1821
|
+
directives: mergeDirectiveNodes(a.directives, b.directives),
|
|
1794
1822
|
};
|
|
1795
1823
|
}
|
|
1796
1824
|
function mergeObjectTypeDefinitionNodes(a, b) {
|
|
@@ -1800,7 +1828,7 @@ function mergeObjectTypeDefinitionNodes(a, b) {
|
|
|
1800
1828
|
name: a.name,
|
|
1801
1829
|
description: a.description || b.description,
|
|
1802
1830
|
interfaces: mergeNamedNodes(a.interfaces, b.interfaces),
|
|
1803
|
-
directives: mergeDirectiveNodes(a.
|
|
1831
|
+
directives: mergeDirectiveNodes(a.directives, b.directives),
|
|
1804
1832
|
fields: mergeFieldDefinitionNodes(a.name.value, a.fields, b.fields),
|
|
1805
1833
|
};
|
|
1806
1834
|
}
|
|
@@ -1811,7 +1839,7 @@ function mergeInterfaceTypeDefinitionNodes(a, b) {
|
|
|
1811
1839
|
name: a.name,
|
|
1812
1840
|
description: a.description || b.description,
|
|
1813
1841
|
interfaces: mergeNamedNodes(a.interfaces, b.interfaces),
|
|
1814
|
-
directives: mergeDirectiveNodes(a.
|
|
1842
|
+
directives: mergeDirectiveNodes(a.directives, b.directives),
|
|
1815
1843
|
fields: mergeFieldDefinitionNodes(a.name.value, a.fields, b.fields),
|
|
1816
1844
|
};
|
|
1817
1845
|
}
|
|
@@ -1821,7 +1849,7 @@ function mergeUnionTypeDefinitionNodes(a, b) {
|
|
|
1821
1849
|
loc: a.loc,
|
|
1822
1850
|
name: a.name,
|
|
1823
1851
|
description: a.description || b.description,
|
|
1824
|
-
directives: mergeDirectiveNodes(a.
|
|
1852
|
+
directives: mergeDirectiveNodes(a.directives, b.directives),
|
|
1825
1853
|
types: mergeNamedNodes(a.types, b.types),
|
|
1826
1854
|
};
|
|
1827
1855
|
}
|
|
@@ -1830,7 +1858,7 @@ function mergeEnumTypeDefinitionNodes(a, b) {
|
|
|
1830
1858
|
if (b) {
|
|
1831
1859
|
for (const v of b.values || []) {
|
|
1832
1860
|
if (v.name.value in values) {
|
|
1833
|
-
throw setTraceToSchema(new graphql.GraphQLError(`Cannot merge duplicate enum value: ${
|
|
1861
|
+
throw setTraceToSchema(new graphql.GraphQLError(`Cannot merge duplicate enum value: ${a.name.value}.${v.name.value}`), `${a.name.value}.${v.name.value}`, v.loc);
|
|
1834
1862
|
}
|
|
1835
1863
|
values[v.name.value] = v;
|
|
1836
1864
|
}
|
|
@@ -1840,7 +1868,7 @@ function mergeEnumTypeDefinitionNodes(a, b) {
|
|
|
1840
1868
|
loc: a.loc,
|
|
1841
1869
|
name: a.name,
|
|
1842
1870
|
description: a.description || b.description,
|
|
1843
|
-
directives: mergeDirectiveNodes(a.
|
|
1871
|
+
directives: mergeDirectiveNodes(a.directives, b.directives),
|
|
1844
1872
|
values: Object.values(values),
|
|
1845
1873
|
};
|
|
1846
1874
|
}
|
|
@@ -1850,26 +1878,74 @@ function mergeInputObjectTypeDefinitionNodes(a, b) {
|
|
|
1850
1878
|
loc: a.loc,
|
|
1851
1879
|
name: a.name,
|
|
1852
1880
|
description: a.description || b.description,
|
|
1853
|
-
directives: mergeDirectiveNodes(a.
|
|
1881
|
+
directives: mergeDirectiveNodes(a.directives, b.directives),
|
|
1854
1882
|
fields: mergeInputValueDefinitionNodes(a.name.value, a.fields, b.fields),
|
|
1855
1883
|
};
|
|
1856
1884
|
}
|
|
1885
|
+
const EXTENSION_DEFINITION_KINDS = {
|
|
1886
|
+
[graphql.Kind.SCALAR_TYPE_EXTENSION]: graphql.Kind.SCALAR_TYPE_DEFINITION,
|
|
1887
|
+
[graphql.Kind.OBJECT_TYPE_EXTENSION]: graphql.Kind.OBJECT_TYPE_DEFINITION,
|
|
1888
|
+
[graphql.Kind.INTERFACE_TYPE_EXTENSION]: graphql.Kind.INTERFACE_TYPE_DEFINITION,
|
|
1889
|
+
[graphql.Kind.UNION_TYPE_EXTENSION]: graphql.Kind.UNION_TYPE_DEFINITION,
|
|
1890
|
+
[graphql.Kind.ENUM_TYPE_EXTENSION]: graphql.Kind.ENUM_TYPE_DEFINITION,
|
|
1891
|
+
[graphql.Kind.INPUT_OBJECT_TYPE_EXTENSION]: graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
|
1892
|
+
};
|
|
1893
|
+
function mergeTypeDefinitionNodes(a, b) {
|
|
1894
|
+
switch (b.kind) {
|
|
1895
|
+
case graphql.Kind.SCALAR_TYPE_DEFINITION:
|
|
1896
|
+
return mergeScalarTypeDefinitionNodes(a, b);
|
|
1897
|
+
case graphql.Kind.OBJECT_TYPE_DEFINITION:
|
|
1898
|
+
return mergeObjectTypeDefinitionNodes(a, b);
|
|
1899
|
+
case graphql.Kind.INTERFACE_TYPE_DEFINITION:
|
|
1900
|
+
return mergeInterfaceTypeDefinitionNodes(a, b);
|
|
1901
|
+
case graphql.Kind.UNION_TYPE_DEFINITION:
|
|
1902
|
+
return mergeUnionTypeDefinitionNodes(a, b);
|
|
1903
|
+
case graphql.Kind.ENUM_TYPE_DEFINITION:
|
|
1904
|
+
return mergeEnumTypeDefinitionNodes(a, b);
|
|
1905
|
+
case graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION:
|
|
1906
|
+
return mergeInputObjectTypeDefinitionNodes(a, b);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
function applyTypeExtensionNode(definitions, extension) {
|
|
1910
|
+
const name = extension.name.value;
|
|
1911
|
+
const definition = definitions[name];
|
|
1912
|
+
if (!definition) {
|
|
1913
|
+
throw setTraceToSchema(new graphql.GraphQLError(`Cannot extend type that is not defined: ${name}`), name, extension.loc);
|
|
1914
|
+
}
|
|
1915
|
+
const kind = EXTENSION_DEFINITION_KINDS[extension.kind];
|
|
1916
|
+
if (definition.kind !== kind) {
|
|
1917
|
+
throw setTraceToSchema(new graphql.GraphQLError(`Cannot extend ${definition.kind} with ${extension.kind}: ${name}`), name, extension.loc);
|
|
1918
|
+
}
|
|
1919
|
+
// An extension has the same shape as its definition, without a description.
|
|
1920
|
+
const merged = mergeTypeDefinitionNodes(definition, {
|
|
1921
|
+
...extension,
|
|
1922
|
+
kind,
|
|
1923
|
+
directives: undefined,
|
|
1924
|
+
});
|
|
1925
|
+
// The directives of an extension are added to the directives of the type.
|
|
1926
|
+
// buildASTSchema rejects a non-repeatable directive that occurs more than
|
|
1927
|
+
// once.
|
|
1928
|
+
definitions[name] = {
|
|
1929
|
+
...merged,
|
|
1930
|
+
directives: [
|
|
1931
|
+
...(definition.directives ?? []),
|
|
1932
|
+
...(extension.directives ?? []),
|
|
1933
|
+
],
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1857
1936
|
function mergeDocumentNodes(documentNodes) {
|
|
1858
1937
|
const definitions = {};
|
|
1938
|
+
const extensions = [];
|
|
1859
1939
|
for (const documentNode of documentNodes) {
|
|
1860
1940
|
for (const definition of documentNode.definitions) {
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
case graphql.Kind.UNION_TYPE_EXTENSION:
|
|
1870
|
-
case graphql.Kind.ENUM_TYPE_EXTENSION:
|
|
1871
|
-
case graphql.Kind.INPUT_OBJECT_TYPE_EXTENSION:
|
|
1872
|
-
throw new Error(`unexpected definition kind: ${definition.kind}`);
|
|
1941
|
+
if (graphql.isTypeExtensionNode(definition)) {
|
|
1942
|
+
// Apply extensions after all definitions are known, because an
|
|
1943
|
+
// extension can occur before the definition that it extends.
|
|
1944
|
+
extensions.push(definition);
|
|
1945
|
+
continue;
|
|
1946
|
+
}
|
|
1947
|
+
if (!isMergableDefinitionNode(definition)) {
|
|
1948
|
+
throw new Error(`unexpected definition kind: ${definition.kind}`);
|
|
1873
1949
|
}
|
|
1874
1950
|
const prevDefinition = definitions[definition.name.value];
|
|
1875
1951
|
if (!prevDefinition) {
|
|
@@ -1887,30 +1963,12 @@ function mergeDocumentNodes(documentNodes) {
|
|
|
1887
1963
|
prevDefinition.description.value !== definition.description.value) {
|
|
1888
1964
|
console.warn(`WARNING: different descriptions for type ${prevDefinition.name.value}: ${JSON.stringify(prevDefinition.description.value)} and ${JSON.stringify(definition.description.value)}${traceLine(definition.description.loc)}${traceLine(prevDefinition.description.loc, "previously ")}\n`);
|
|
1889
1965
|
}
|
|
1890
|
-
|
|
1891
|
-
case graphql.Kind.SCALAR_TYPE_DEFINITION:
|
|
1892
|
-
definitions[definition.name.value] = mergeScalarTypeDefinitionNodes(prevDefinition, definition);
|
|
1893
|
-
break;
|
|
1894
|
-
case graphql.Kind.OBJECT_TYPE_DEFINITION:
|
|
1895
|
-
definitions[definition.name.value] = mergeObjectTypeDefinitionNodes(prevDefinition, definition);
|
|
1896
|
-
break;
|
|
1897
|
-
case graphql.Kind.INTERFACE_TYPE_DEFINITION:
|
|
1898
|
-
definitions[definition.name.value] =
|
|
1899
|
-
mergeInterfaceTypeDefinitionNodes(prevDefinition, definition);
|
|
1900
|
-
break;
|
|
1901
|
-
case graphql.Kind.UNION_TYPE_DEFINITION:
|
|
1902
|
-
definitions[definition.name.value] = mergeUnionTypeDefinitionNodes(prevDefinition, definition);
|
|
1903
|
-
break;
|
|
1904
|
-
case graphql.Kind.ENUM_TYPE_DEFINITION:
|
|
1905
|
-
definitions[definition.name.value] = mergeEnumTypeDefinitionNodes(prevDefinition, definition);
|
|
1906
|
-
break;
|
|
1907
|
-
case graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION:
|
|
1908
|
-
definitions[definition.name.value] =
|
|
1909
|
-
mergeInputObjectTypeDefinitionNodes(prevDefinition, definition);
|
|
1910
|
-
break;
|
|
1911
|
-
}
|
|
1966
|
+
definitions[definition.name.value] = mergeTypeDefinitionNodes(prevDefinition, definition);
|
|
1912
1967
|
}
|
|
1913
1968
|
}
|
|
1969
|
+
for (const extension of extensions) {
|
|
1970
|
+
applyTypeExtensionNode(definitions, extension);
|
|
1971
|
+
}
|
|
1914
1972
|
return {
|
|
1915
1973
|
kind: graphql.Kind.DOCUMENT,
|
|
1916
1974
|
loc: documentNodes[0].loc,
|
|
@@ -1918,21 +1976,31 @@ function mergeDocumentNodes(documentNodes) {
|
|
|
1918
1976
|
};
|
|
1919
1977
|
}
|
|
1920
1978
|
|
|
1921
|
-
async function
|
|
1922
|
-
for
|
|
1979
|
+
async function collectSchemaFiles(glob$1, files) {
|
|
1980
|
+
for (const path$1 of await glob.glob(glob$1, globOptions)) {
|
|
1923
1981
|
const entry = await fs.stat(path$1);
|
|
1924
1982
|
if (entry.isFile()) {
|
|
1925
|
-
|
|
1983
|
+
files.add(toPosixPath(path$1));
|
|
1926
1984
|
}
|
|
1927
1985
|
else if (entry.isDirectory()) {
|
|
1928
|
-
|
|
1986
|
+
await collectSchemaFiles(path.posix.join(toPosixPath(path$1), "**/*.graphql"), files);
|
|
1929
1987
|
}
|
|
1930
1988
|
}
|
|
1931
1989
|
}
|
|
1990
|
+
/**
|
|
1991
|
+
* Finds the schema files and sorts them by path. The order of the files sets
|
|
1992
|
+
* the order of the merged definitions and fields, so it must not change with
|
|
1993
|
+
* the file system or the platform.
|
|
1994
|
+
*/
|
|
1995
|
+
async function findSchemaFiles(glob) {
|
|
1996
|
+
const files = new Set();
|
|
1997
|
+
await collectSchemaFiles(glob, files);
|
|
1998
|
+
return Array.from(files).sort(comparePaths);
|
|
1999
|
+
}
|
|
1932
2000
|
async function parse(glob, ignore) {
|
|
1933
2001
|
const shouldIgnore = createMatcher(ignore);
|
|
1934
2002
|
const documentNodes = [];
|
|
1935
|
-
for
|
|
2003
|
+
for (const entry of await findSchemaFiles(glob)) {
|
|
1936
2004
|
if (shouldIgnore(entry)) {
|
|
1937
2005
|
continue;
|
|
1938
2006
|
}
|
package/build/lib.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import path, { resolve, basename, extname,
|
|
1
|
+
import path, { resolve, basename, extname, posix } from 'node:path';
|
|
2
2
|
import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
3
|
-
import { GraphQLNonNull, GraphQLList, GraphQLScalarType, GraphQLEnumType, GraphQLDirective, Kind, GraphQLObjectType, getNamedType, getNullableType, GraphQLInterfaceType, GraphQLUnionType, GraphQLInputObjectType, DirectiveLocation, GraphQLError, parse as parse$3, Source, buildASTSchema, validateSchema } from 'graphql';
|
|
3
|
+
import { GraphQLNonNull, GraphQLList, GraphQLScalarType, GraphQLEnumType, GraphQLDirective, Kind, GraphQLObjectType, getNamedType, getNullableType, GraphQLInterfaceType, GraphQLUnionType, GraphQLInputObjectType, DirectiveLocation, specifiedDirectives, isTypeExtensionNode, GraphQLError, isTypeDefinitionNode, parse as parse$3, Source, buildASTSchema, validateSchema } from 'graphql';
|
|
4
4
|
import jen$1, { jen } from 'jennifer-js';
|
|
5
5
|
import format from 'jennifer-js/format';
|
|
6
6
|
import partition from 'just-partition';
|
|
7
7
|
import pascalCase from 'just-pascal-case';
|
|
8
8
|
import fs from 'node:fs/promises';
|
|
9
9
|
import ts, { getLineAndCharacterOfPosition } from 'typescript';
|
|
10
|
-
import {
|
|
10
|
+
import { glob } from 'glob';
|
|
11
11
|
import pm from 'picomatch';
|
|
12
12
|
import zip from 'just-zip-it';
|
|
13
13
|
|
|
@@ -275,6 +275,30 @@ class RequiredPackages {
|
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Glob options that make Windows paths work as patterns. On Windows, a
|
|
280
|
+
* backslash is a path separator and not an escape character.
|
|
281
|
+
*/
|
|
282
|
+
const globOptions = {
|
|
283
|
+
windowsPathsNoEscape: path.sep === "\\",
|
|
284
|
+
};
|
|
285
|
+
/**
|
|
286
|
+
* Converts native path separators to forward slashes.
|
|
287
|
+
*
|
|
288
|
+
* Glob patterns and module specifiers must use forward slashes, even on
|
|
289
|
+
* Windows.
|
|
290
|
+
*/
|
|
291
|
+
function toPosixPath(p) {
|
|
292
|
+
return path.sep === "\\" ? p.replaceAll("\\", "/") : p;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Compares paths by code unit. The result does not change with the locale
|
|
296
|
+
* or with the order in which the file system returns entries.
|
|
297
|
+
*/
|
|
298
|
+
function comparePaths(a, b) {
|
|
299
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
300
|
+
}
|
|
301
|
+
|
|
278
302
|
function renderSymbolReference(pkgs, ref, alias) {
|
|
279
303
|
const ns = ref.module
|
|
280
304
|
? jen.id(pkgs.addRequires(ref.module, ref.symbol, alias ?? ref.alias))
|
|
@@ -283,10 +307,10 @@ function renderSymbolReference(pkgs, ref, alias) {
|
|
|
283
307
|
}
|
|
284
308
|
|
|
285
309
|
function compareEntryKey([a], [b]) {
|
|
286
|
-
return a.localeCompare(b);
|
|
310
|
+
return a.localeCompare(b, "en");
|
|
287
311
|
}
|
|
288
312
|
function compareName({ name: a }, { name: b }) {
|
|
289
|
-
return a.localeCompare(b);
|
|
313
|
+
return a.localeCompare(b, "en");
|
|
290
314
|
}
|
|
291
315
|
const PRIMITIVE_TYPES = new Set(["ID", "Int", "Float", "String", "Boolean"]);
|
|
292
316
|
function isPrimitiveType(type) {
|
|
@@ -305,8 +329,9 @@ function renderImports(base, imports, aliases, extMode = "omit") {
|
|
|
305
329
|
if (mod.startsWith("file:///")) {
|
|
306
330
|
mod = fileURLToPath(mod);
|
|
307
331
|
}
|
|
308
|
-
if (
|
|
309
|
-
|
|
332
|
+
if (path.isAbsolute(mod)) {
|
|
333
|
+
// NOTE: module specifiers always use forward slashes, even on Windows.
|
|
334
|
+
mod = toPosixPath(path.relative(base, mod));
|
|
310
335
|
if (!mod.startsWith(".")) {
|
|
311
336
|
mod = `./${mod}`;
|
|
312
337
|
}
|
|
@@ -322,8 +347,8 @@ function renderImports(base, imports, aliases, extMode = "omit") {
|
|
|
322
347
|
}
|
|
323
348
|
return jen$1.import
|
|
324
349
|
.obj(...Array.from(symbols)
|
|
325
|
-
.sort((a, b) => a.localeCompare(b) ||
|
|
326
|
-
importAlias(a).localeCompare(importAlias(b)))
|
|
350
|
+
.sort((a, b) => a.localeCompare(b, "en") ||
|
|
351
|
+
importAlias(a).localeCompare(importAlias(b), "en"))
|
|
327
352
|
.map((symbol) => symbol in aliases
|
|
328
353
|
? jen$1.id(aliases[symbol]).as.id(symbol)
|
|
329
354
|
: jen$1.id(symbol)))
|
|
@@ -338,14 +363,8 @@ function renderAstNode(astNode) {
|
|
|
338
363
|
}
|
|
339
364
|
function isBuiltInDirective(name) {
|
|
340
365
|
// https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
case "skip":
|
|
344
|
-
case "include":
|
|
345
|
-
case "specifiedBy":
|
|
346
|
-
return true;
|
|
347
|
-
}
|
|
348
|
-
return false;
|
|
366
|
+
// Use the list from graphql, because new versions add directives (for example, @oneOf).
|
|
367
|
+
return specifiedDirectives.some((d) => d.name === name);
|
|
349
368
|
}
|
|
350
369
|
function truthify(arr) {
|
|
351
370
|
return arr.filter(Boolean);
|
|
@@ -1096,9 +1115,9 @@ class GeneratorContext {
|
|
|
1096
1115
|
];
|
|
1097
1116
|
}
|
|
1098
1117
|
renderFile(outfile, renderStatements) {
|
|
1099
|
-
const base =
|
|
1118
|
+
const base = path.resolve(outfile);
|
|
1100
1119
|
const statements = renderStatements.call(this);
|
|
1101
|
-
const [localImports, externalImports] = partition(Object.entries(this._pkgs.requires), ([pkg]) =>
|
|
1120
|
+
const [localImports, externalImports] = partition(Object.entries(this._pkgs.requires), ([pkg]) => path.isAbsolute(pkg));
|
|
1102
1121
|
return format(jen$1
|
|
1103
1122
|
.statements(renderImports(path.dirname(base), externalImports, this._pkgs.aliases, this._extMode), renderImports(path.dirname(base), localImports, this._pkgs.aliases, this._extMode), ...statements)
|
|
1104
1123
|
.toString());
|
|
@@ -1276,26 +1295,28 @@ async function parseResolverFile(path) {
|
|
|
1276
1295
|
return [];
|
|
1277
1296
|
})));
|
|
1278
1297
|
}
|
|
1279
|
-
async function parseTypeResolvers(typeName, glob, ignore) {
|
|
1298
|
+
async function parseTypeResolvers(typeName, glob$1, ignore) {
|
|
1280
1299
|
const shouldIgnore = createMatcher(ignore);
|
|
1281
1300
|
const result = {};
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1301
|
+
// NOTE: sort the files so that the result (and the warnings) do not change
|
|
1302
|
+
// with the order in which the file system returns entries.
|
|
1303
|
+
const fullpaths = (await glob(glob$1, { ...globOptions, withFileTypes: true }))
|
|
1304
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".ts"))
|
|
1305
|
+
.map((entry) => toPosixPath(entry.fullpath()))
|
|
1306
|
+
.filter((fullpath) => !shouldIgnore(fullpath))
|
|
1307
|
+
.sort(comparePaths);
|
|
1308
|
+
for (const fullpath of fullpaths) {
|
|
1309
|
+
for (const [name, reference] of Object.entries(await parseResolverFile(fullpath))) {
|
|
1310
|
+
let realName = name;
|
|
1311
|
+
if (realName === "default" &&
|
|
1312
|
+
typeof glob$1 === "string" &&
|
|
1313
|
+
toPosixPath(glob$1).endsWith("/*.ts")) {
|
|
1314
|
+
realName = basename(fullpath, extname(fullpath));
|
|
1315
|
+
}
|
|
1316
|
+
if (realName in result) {
|
|
1317
|
+
console.warn(`WARNING: Duplicate resolver name: ${typeName ? `${typeName}.` : ""}${realName} in ${fullpath} and ${result[realName].module}`);
|
|
1318
|
+
}
|
|
1319
|
+
result[realName] = reference;
|
|
1299
1320
|
}
|
|
1300
1321
|
}
|
|
1301
1322
|
return result;
|
|
@@ -1307,8 +1328,9 @@ async function parse$2(dirpath, ignore) {
|
|
|
1307
1328
|
}
|
|
1308
1329
|
const out = {};
|
|
1309
1330
|
const shouldIgnore = createMatcher(ignore);
|
|
1310
|
-
|
|
1311
|
-
|
|
1331
|
+
const names = (await fs.readdir(dirpath)).sort(comparePaths);
|
|
1332
|
+
for (const name of names) {
|
|
1333
|
+
const file = posix.join(toPosixPath(dirpath), name);
|
|
1312
1334
|
const entry = await fs.stat(file);
|
|
1313
1335
|
if (entry.isDirectory()) {
|
|
1314
1336
|
out[name] = await parseTypeResolvers(name, `${file}/*.ts`, shouldIgnore);
|
|
@@ -1611,11 +1633,14 @@ function setTraceToSchema(err, name, loc) {
|
|
|
1611
1633
|
}
|
|
1612
1634
|
return err;
|
|
1613
1635
|
}
|
|
1636
|
+
function isMergableDefinitionNode(node) {
|
|
1637
|
+
return isTypeDefinitionNode(node) || node.kind === Kind.DIRECTIVE_DEFINITION;
|
|
1638
|
+
}
|
|
1614
1639
|
function namedNodesDict(nodes = []) {
|
|
1615
1640
|
return Object.fromEntries(nodes.map((node) => [node.name.value, node]));
|
|
1616
1641
|
}
|
|
1617
1642
|
function sortNamedNodes(nodes) {
|
|
1618
|
-
return Array.from(nodes).sort(({ name: { value: a } }, { name: { value: b } }) => a.localeCompare(b));
|
|
1643
|
+
return Array.from(nodes).sort(({ name: { value: a } }, { name: { value: b } }) => a.localeCompare(b, "en"));
|
|
1619
1644
|
}
|
|
1620
1645
|
function isIntValueNodeEqual(a, b) {
|
|
1621
1646
|
return a.value === b.value;
|
|
@@ -1724,7 +1749,7 @@ function mergeFieldDefinitionNodes(name, a, b) {
|
|
|
1724
1749
|
console.warn(`WARNING: there is more than one declaration for ${name}.${f.name.value}${traceLine(f.loc)}${traceLine(prev.loc, "previously ")}\n`);
|
|
1725
1750
|
fields[f.name.value] = {
|
|
1726
1751
|
...prev,
|
|
1727
|
-
directives: mergeDirectiveNodes(
|
|
1752
|
+
directives: mergeDirectiveNodes(prev.directives, f.directives),
|
|
1728
1753
|
};
|
|
1729
1754
|
}
|
|
1730
1755
|
else {
|
|
@@ -1749,7 +1774,7 @@ function mergeInputValueDefinitionNodes(name, a, b) {
|
|
|
1749
1774
|
console.warn(`WARNING: there is more than one declaration for ${name}.${f.name.value}${traceLine(f.loc)}${traceLine(prev.loc, "previously ")}\n`);
|
|
1750
1775
|
fields[f.name.value] = {
|
|
1751
1776
|
...prev,
|
|
1752
|
-
directives: mergeDirectiveNodes(
|
|
1777
|
+
directives: mergeDirectiveNodes(prev.directives, f.directives),
|
|
1753
1778
|
};
|
|
1754
1779
|
}
|
|
1755
1780
|
else {
|
|
@@ -1768,18 +1793,19 @@ function mergeNamedNodes(a, b) {
|
|
|
1768
1793
|
}
|
|
1769
1794
|
return Object.values(nodes);
|
|
1770
1795
|
}
|
|
1771
|
-
function mergeDirectiveNodes(
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1796
|
+
function mergeDirectiveNodes(a, b) {
|
|
1797
|
+
// Keep each occurrence, because a repeatable directive can occur more than
|
|
1798
|
+
// once. Skip only an occurrence that is identical to one that is already
|
|
1799
|
+
// applied. buildASTSchema rejects a non-repeatable directive that occurs
|
|
1800
|
+
// more than once.
|
|
1801
|
+
const directives = Array.from(a ?? []);
|
|
1802
|
+
for (const d of b ?? []) {
|
|
1803
|
+
if (!directives.some((prev) => prev.name.value === d.name.value &&
|
|
1804
|
+
isArgumentNodesEqual(prev.arguments, d.arguments))) {
|
|
1805
|
+
directives.push(d);
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
return directives;
|
|
1783
1809
|
}
|
|
1784
1810
|
function mergeScalarTypeDefinitionNodes(a, b) {
|
|
1785
1811
|
if (a.name.value !== b.name.value) {
|
|
@@ -1787,8 +1813,10 @@ function mergeScalarTypeDefinitionNodes(a, b) {
|
|
|
1787
1813
|
}
|
|
1788
1814
|
return {
|
|
1789
1815
|
kind: Kind.SCALAR_TYPE_DEFINITION,
|
|
1816
|
+
loc: a.loc,
|
|
1790
1817
|
name: a.name,
|
|
1791
|
-
|
|
1818
|
+
description: a.description || b.description,
|
|
1819
|
+
directives: mergeDirectiveNodes(a.directives, b.directives),
|
|
1792
1820
|
};
|
|
1793
1821
|
}
|
|
1794
1822
|
function mergeObjectTypeDefinitionNodes(a, b) {
|
|
@@ -1798,7 +1826,7 @@ function mergeObjectTypeDefinitionNodes(a, b) {
|
|
|
1798
1826
|
name: a.name,
|
|
1799
1827
|
description: a.description || b.description,
|
|
1800
1828
|
interfaces: mergeNamedNodes(a.interfaces, b.interfaces),
|
|
1801
|
-
directives: mergeDirectiveNodes(a.
|
|
1829
|
+
directives: mergeDirectiveNodes(a.directives, b.directives),
|
|
1802
1830
|
fields: mergeFieldDefinitionNodes(a.name.value, a.fields, b.fields),
|
|
1803
1831
|
};
|
|
1804
1832
|
}
|
|
@@ -1809,7 +1837,7 @@ function mergeInterfaceTypeDefinitionNodes(a, b) {
|
|
|
1809
1837
|
name: a.name,
|
|
1810
1838
|
description: a.description || b.description,
|
|
1811
1839
|
interfaces: mergeNamedNodes(a.interfaces, b.interfaces),
|
|
1812
|
-
directives: mergeDirectiveNodes(a.
|
|
1840
|
+
directives: mergeDirectiveNodes(a.directives, b.directives),
|
|
1813
1841
|
fields: mergeFieldDefinitionNodes(a.name.value, a.fields, b.fields),
|
|
1814
1842
|
};
|
|
1815
1843
|
}
|
|
@@ -1819,7 +1847,7 @@ function mergeUnionTypeDefinitionNodes(a, b) {
|
|
|
1819
1847
|
loc: a.loc,
|
|
1820
1848
|
name: a.name,
|
|
1821
1849
|
description: a.description || b.description,
|
|
1822
|
-
directives: mergeDirectiveNodes(a.
|
|
1850
|
+
directives: mergeDirectiveNodes(a.directives, b.directives),
|
|
1823
1851
|
types: mergeNamedNodes(a.types, b.types),
|
|
1824
1852
|
};
|
|
1825
1853
|
}
|
|
@@ -1828,7 +1856,7 @@ function mergeEnumTypeDefinitionNodes(a, b) {
|
|
|
1828
1856
|
if (b) {
|
|
1829
1857
|
for (const v of b.values || []) {
|
|
1830
1858
|
if (v.name.value in values) {
|
|
1831
|
-
throw setTraceToSchema(new GraphQLError(`Cannot merge duplicate enum value: ${
|
|
1859
|
+
throw setTraceToSchema(new GraphQLError(`Cannot merge duplicate enum value: ${a.name.value}.${v.name.value}`), `${a.name.value}.${v.name.value}`, v.loc);
|
|
1832
1860
|
}
|
|
1833
1861
|
values[v.name.value] = v;
|
|
1834
1862
|
}
|
|
@@ -1838,7 +1866,7 @@ function mergeEnumTypeDefinitionNodes(a, b) {
|
|
|
1838
1866
|
loc: a.loc,
|
|
1839
1867
|
name: a.name,
|
|
1840
1868
|
description: a.description || b.description,
|
|
1841
|
-
directives: mergeDirectiveNodes(a.
|
|
1869
|
+
directives: mergeDirectiveNodes(a.directives, b.directives),
|
|
1842
1870
|
values: Object.values(values),
|
|
1843
1871
|
};
|
|
1844
1872
|
}
|
|
@@ -1848,26 +1876,74 @@ function mergeInputObjectTypeDefinitionNodes(a, b) {
|
|
|
1848
1876
|
loc: a.loc,
|
|
1849
1877
|
name: a.name,
|
|
1850
1878
|
description: a.description || b.description,
|
|
1851
|
-
directives: mergeDirectiveNodes(a.
|
|
1879
|
+
directives: mergeDirectiveNodes(a.directives, b.directives),
|
|
1852
1880
|
fields: mergeInputValueDefinitionNodes(a.name.value, a.fields, b.fields),
|
|
1853
1881
|
};
|
|
1854
1882
|
}
|
|
1883
|
+
const EXTENSION_DEFINITION_KINDS = {
|
|
1884
|
+
[Kind.SCALAR_TYPE_EXTENSION]: Kind.SCALAR_TYPE_DEFINITION,
|
|
1885
|
+
[Kind.OBJECT_TYPE_EXTENSION]: Kind.OBJECT_TYPE_DEFINITION,
|
|
1886
|
+
[Kind.INTERFACE_TYPE_EXTENSION]: Kind.INTERFACE_TYPE_DEFINITION,
|
|
1887
|
+
[Kind.UNION_TYPE_EXTENSION]: Kind.UNION_TYPE_DEFINITION,
|
|
1888
|
+
[Kind.ENUM_TYPE_EXTENSION]: Kind.ENUM_TYPE_DEFINITION,
|
|
1889
|
+
[Kind.INPUT_OBJECT_TYPE_EXTENSION]: Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
|
1890
|
+
};
|
|
1891
|
+
function mergeTypeDefinitionNodes(a, b) {
|
|
1892
|
+
switch (b.kind) {
|
|
1893
|
+
case Kind.SCALAR_TYPE_DEFINITION:
|
|
1894
|
+
return mergeScalarTypeDefinitionNodes(a, b);
|
|
1895
|
+
case Kind.OBJECT_TYPE_DEFINITION:
|
|
1896
|
+
return mergeObjectTypeDefinitionNodes(a, b);
|
|
1897
|
+
case Kind.INTERFACE_TYPE_DEFINITION:
|
|
1898
|
+
return mergeInterfaceTypeDefinitionNodes(a, b);
|
|
1899
|
+
case Kind.UNION_TYPE_DEFINITION:
|
|
1900
|
+
return mergeUnionTypeDefinitionNodes(a, b);
|
|
1901
|
+
case Kind.ENUM_TYPE_DEFINITION:
|
|
1902
|
+
return mergeEnumTypeDefinitionNodes(a, b);
|
|
1903
|
+
case Kind.INPUT_OBJECT_TYPE_DEFINITION:
|
|
1904
|
+
return mergeInputObjectTypeDefinitionNodes(a, b);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
function applyTypeExtensionNode(definitions, extension) {
|
|
1908
|
+
const name = extension.name.value;
|
|
1909
|
+
const definition = definitions[name];
|
|
1910
|
+
if (!definition) {
|
|
1911
|
+
throw setTraceToSchema(new GraphQLError(`Cannot extend type that is not defined: ${name}`), name, extension.loc);
|
|
1912
|
+
}
|
|
1913
|
+
const kind = EXTENSION_DEFINITION_KINDS[extension.kind];
|
|
1914
|
+
if (definition.kind !== kind) {
|
|
1915
|
+
throw setTraceToSchema(new GraphQLError(`Cannot extend ${definition.kind} with ${extension.kind}: ${name}`), name, extension.loc);
|
|
1916
|
+
}
|
|
1917
|
+
// An extension has the same shape as its definition, without a description.
|
|
1918
|
+
const merged = mergeTypeDefinitionNodes(definition, {
|
|
1919
|
+
...extension,
|
|
1920
|
+
kind,
|
|
1921
|
+
directives: undefined,
|
|
1922
|
+
});
|
|
1923
|
+
// The directives of an extension are added to the directives of the type.
|
|
1924
|
+
// buildASTSchema rejects a non-repeatable directive that occurs more than
|
|
1925
|
+
// once.
|
|
1926
|
+
definitions[name] = {
|
|
1927
|
+
...merged,
|
|
1928
|
+
directives: [
|
|
1929
|
+
...(definition.directives ?? []),
|
|
1930
|
+
...(extension.directives ?? []),
|
|
1931
|
+
],
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1855
1934
|
function mergeDocumentNodes(documentNodes) {
|
|
1856
1935
|
const definitions = {};
|
|
1936
|
+
const extensions = [];
|
|
1857
1937
|
for (const documentNode of documentNodes) {
|
|
1858
1938
|
for (const definition of documentNode.definitions) {
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
case Kind.UNION_TYPE_EXTENSION:
|
|
1868
|
-
case Kind.ENUM_TYPE_EXTENSION:
|
|
1869
|
-
case Kind.INPUT_OBJECT_TYPE_EXTENSION:
|
|
1870
|
-
throw new Error(`unexpected definition kind: ${definition.kind}`);
|
|
1939
|
+
if (isTypeExtensionNode(definition)) {
|
|
1940
|
+
// Apply extensions after all definitions are known, because an
|
|
1941
|
+
// extension can occur before the definition that it extends.
|
|
1942
|
+
extensions.push(definition);
|
|
1943
|
+
continue;
|
|
1944
|
+
}
|
|
1945
|
+
if (!isMergableDefinitionNode(definition)) {
|
|
1946
|
+
throw new Error(`unexpected definition kind: ${definition.kind}`);
|
|
1871
1947
|
}
|
|
1872
1948
|
const prevDefinition = definitions[definition.name.value];
|
|
1873
1949
|
if (!prevDefinition) {
|
|
@@ -1885,30 +1961,12 @@ function mergeDocumentNodes(documentNodes) {
|
|
|
1885
1961
|
prevDefinition.description.value !== definition.description.value) {
|
|
1886
1962
|
console.warn(`WARNING: different descriptions for type ${prevDefinition.name.value}: ${JSON.stringify(prevDefinition.description.value)} and ${JSON.stringify(definition.description.value)}${traceLine(definition.description.loc)}${traceLine(prevDefinition.description.loc, "previously ")}\n`);
|
|
1887
1963
|
}
|
|
1888
|
-
|
|
1889
|
-
case Kind.SCALAR_TYPE_DEFINITION:
|
|
1890
|
-
definitions[definition.name.value] = mergeScalarTypeDefinitionNodes(prevDefinition, definition);
|
|
1891
|
-
break;
|
|
1892
|
-
case Kind.OBJECT_TYPE_DEFINITION:
|
|
1893
|
-
definitions[definition.name.value] = mergeObjectTypeDefinitionNodes(prevDefinition, definition);
|
|
1894
|
-
break;
|
|
1895
|
-
case Kind.INTERFACE_TYPE_DEFINITION:
|
|
1896
|
-
definitions[definition.name.value] =
|
|
1897
|
-
mergeInterfaceTypeDefinitionNodes(prevDefinition, definition);
|
|
1898
|
-
break;
|
|
1899
|
-
case Kind.UNION_TYPE_DEFINITION:
|
|
1900
|
-
definitions[definition.name.value] = mergeUnionTypeDefinitionNodes(prevDefinition, definition);
|
|
1901
|
-
break;
|
|
1902
|
-
case Kind.ENUM_TYPE_DEFINITION:
|
|
1903
|
-
definitions[definition.name.value] = mergeEnumTypeDefinitionNodes(prevDefinition, definition);
|
|
1904
|
-
break;
|
|
1905
|
-
case Kind.INPUT_OBJECT_TYPE_DEFINITION:
|
|
1906
|
-
definitions[definition.name.value] =
|
|
1907
|
-
mergeInputObjectTypeDefinitionNodes(prevDefinition, definition);
|
|
1908
|
-
break;
|
|
1909
|
-
}
|
|
1964
|
+
definitions[definition.name.value] = mergeTypeDefinitionNodes(prevDefinition, definition);
|
|
1910
1965
|
}
|
|
1911
1966
|
}
|
|
1967
|
+
for (const extension of extensions) {
|
|
1968
|
+
applyTypeExtensionNode(definitions, extension);
|
|
1969
|
+
}
|
|
1912
1970
|
return {
|
|
1913
1971
|
kind: Kind.DOCUMENT,
|
|
1914
1972
|
loc: documentNodes[0].loc,
|
|
@@ -1916,21 +1974,31 @@ function mergeDocumentNodes(documentNodes) {
|
|
|
1916
1974
|
};
|
|
1917
1975
|
}
|
|
1918
1976
|
|
|
1919
|
-
async function
|
|
1920
|
-
for
|
|
1977
|
+
async function collectSchemaFiles(glob$1, files) {
|
|
1978
|
+
for (const path of await glob(glob$1, globOptions)) {
|
|
1921
1979
|
const entry = await fs.stat(path);
|
|
1922
1980
|
if (entry.isFile()) {
|
|
1923
|
-
|
|
1981
|
+
files.add(toPosixPath(path));
|
|
1924
1982
|
}
|
|
1925
1983
|
else if (entry.isDirectory()) {
|
|
1926
|
-
|
|
1984
|
+
await collectSchemaFiles(posix.join(toPosixPath(path), "**/*.graphql"), files);
|
|
1927
1985
|
}
|
|
1928
1986
|
}
|
|
1929
1987
|
}
|
|
1988
|
+
/**
|
|
1989
|
+
* Finds the schema files and sorts them by path. The order of the files sets
|
|
1990
|
+
* the order of the merged definitions and fields, so it must not change with
|
|
1991
|
+
* the file system or the platform.
|
|
1992
|
+
*/
|
|
1993
|
+
async function findSchemaFiles(glob) {
|
|
1994
|
+
const files = new Set();
|
|
1995
|
+
await collectSchemaFiles(glob, files);
|
|
1996
|
+
return Array.from(files).sort(comparePaths);
|
|
1997
|
+
}
|
|
1930
1998
|
async function parse(glob, ignore) {
|
|
1931
1999
|
const shouldIgnore = createMatcher(ignore);
|
|
1932
2000
|
const documentNodes = [];
|
|
1933
|
-
for
|
|
2001
|
+
for (const entry of await findSchemaFiles(glob)) {
|
|
1934
2002
|
if (shouldIgnore(entry)) {
|
|
1935
2003
|
continue;
|
|
1936
2004
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphql-jerky",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "Jerky is a schema-first static graphql schema lightweight code generator.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"bin":
|
|
6
|
+
"bin": {
|
|
7
|
+
"graphql-jerky": "build/bin/jerky.cjs"
|
|
8
|
+
},
|
|
7
9
|
"types": "./build/lib.d.ts",
|
|
8
10
|
"module": "./build/lib.mjs",
|
|
9
11
|
"main": "./build/lib.cjs",
|