graphql-jerky 1.0.8 → 1.0.9

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 CHANGED
@@ -34,6 +34,27 @@ in the same directory.
34
34
  A path to a directory or file can be passed as the first parameter to limit to
35
35
  scope of the search.
36
36
 
37
+ ### Type extensions
38
+
39
+ Type extensions (`extend type`, `extend interface`, `extend union`,
40
+ `extend enum`, `extend input` and `extend scalar`) are merged into the type
41
+ that they extend. The extension and the definition can be in different files,
42
+ in any order.
43
+
44
+ ```gql
45
+ # user.graphql
46
+ type Query {
47
+ me: User
48
+ }
49
+
50
+ # post.graphql
51
+ extend type Query {
52
+ posts: [Post!]!
53
+ }
54
+ ```
55
+
56
+ An extension of a type that is not defined causes an error.
57
+
37
58
  ### Options
38
59
 
39
60
  #### --graphql
package/build/lib.cjs CHANGED
@@ -340,14 +340,8 @@ function renderAstNode(astNode) {
340
340
  }
341
341
  function isBuiltInDirective(name) {
342
342
  // https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives
343
- switch (name) {
344
- case "deprecated":
345
- case "skip":
346
- case "include":
347
- case "specifiedBy":
348
- return true;
349
- }
350
- return false;
343
+ // Use the list from graphql, because new versions add directives (for example, @oneOf).
344
+ return graphql.specifiedDirectives.some((d) => d.name === name);
351
345
  }
352
346
  function truthify(arr) {
353
347
  return arr.filter(Boolean);
@@ -1613,6 +1607,9 @@ function setTraceToSchema(err, name, loc) {
1613
1607
  }
1614
1608
  return err;
1615
1609
  }
1610
+ function isMergableDefinitionNode(node) {
1611
+ return graphql.isTypeDefinitionNode(node) || node.kind === graphql.Kind.DIRECTIVE_DEFINITION;
1612
+ }
1616
1613
  function namedNodesDict(nodes = []) {
1617
1614
  return Object.fromEntries(nodes.map((node) => [node.name.value, node]));
1618
1615
  }
@@ -1726,7 +1723,7 @@ function mergeFieldDefinitionNodes(name, a, b) {
1726
1723
  console.warn(`WARNING: there is more than one declaration for ${name}.${f.name.value}${traceLine(f.loc)}${traceLine(prev.loc, "previously ")}\n`);
1727
1724
  fields[f.name.value] = {
1728
1725
  ...prev,
1729
- directives: mergeDirectiveNodes(`${name}.${f.name.value}`, prev.directives, f.directives),
1726
+ directives: mergeDirectiveNodes(prev.directives, f.directives),
1730
1727
  };
1731
1728
  }
1732
1729
  else {
@@ -1751,7 +1748,7 @@ function mergeInputValueDefinitionNodes(name, a, b) {
1751
1748
  console.warn(`WARNING: there is more than one declaration for ${name}.${f.name.value}${traceLine(f.loc)}${traceLine(prev.loc, "previously ")}\n`);
1752
1749
  fields[f.name.value] = {
1753
1750
  ...prev,
1754
- directives: mergeDirectiveNodes(`${name}.${f.name.value}`, prev.directives, f.directives),
1751
+ directives: mergeDirectiveNodes(prev.directives, f.directives),
1755
1752
  };
1756
1753
  }
1757
1754
  else {
@@ -1770,18 +1767,19 @@ function mergeNamedNodes(a, b) {
1770
1767
  }
1771
1768
  return Object.values(nodes);
1772
1769
  }
1773
- function mergeDirectiveNodes(name, a, b) {
1774
- const directives = namedNodesDict(a);
1775
- if (b) {
1776
- for (const d of b) {
1777
- if (d.name.value in directives &&
1778
- !isArgumentNodesEqual(directives[d.name.value].arguments, d.arguments)) {
1779
- throw setTraceToSchema(new graphql.GraphQLError(`Cannot merge duplicate directive with different arguments: ${name} @${d.name.value}`), `${name} @${d.name.value}`, d.loc);
1780
- }
1781
- directives[d.name.value] = d;
1782
- }
1783
- }
1784
- return Object.values(directives);
1770
+ function mergeDirectiveNodes(a, b) {
1771
+ // Keep each occurrence, because a repeatable directive can occur more than
1772
+ // once. Skip only an occurrence that is identical to one that is already
1773
+ // applied. buildASTSchema rejects a non-repeatable directive that occurs
1774
+ // more than once.
1775
+ const directives = Array.from(a ?? []);
1776
+ for (const d of b ?? []) {
1777
+ if (!directives.some((prev) => prev.name.value === d.name.value &&
1778
+ isArgumentNodesEqual(prev.arguments, d.arguments))) {
1779
+ directives.push(d);
1780
+ }
1781
+ }
1782
+ return directives;
1785
1783
  }
1786
1784
  function mergeScalarTypeDefinitionNodes(a, b) {
1787
1785
  if (a.name.value !== b.name.value) {
@@ -1789,8 +1787,10 @@ function mergeScalarTypeDefinitionNodes(a, b) {
1789
1787
  }
1790
1788
  return {
1791
1789
  kind: graphql.Kind.SCALAR_TYPE_DEFINITION,
1790
+ loc: a.loc,
1792
1791
  name: a.name,
1793
- directives: mergeDirectiveNodes(a.name.value, a.directives, b.directives),
1792
+ description: a.description || b.description,
1793
+ directives: mergeDirectiveNodes(a.directives, b.directives),
1794
1794
  };
1795
1795
  }
1796
1796
  function mergeObjectTypeDefinitionNodes(a, b) {
@@ -1800,7 +1800,7 @@ function mergeObjectTypeDefinitionNodes(a, b) {
1800
1800
  name: a.name,
1801
1801
  description: a.description || b.description,
1802
1802
  interfaces: mergeNamedNodes(a.interfaces, b.interfaces),
1803
- directives: mergeDirectiveNodes(a.name.value, a.directives, b.directives),
1803
+ directives: mergeDirectiveNodes(a.directives, b.directives),
1804
1804
  fields: mergeFieldDefinitionNodes(a.name.value, a.fields, b.fields),
1805
1805
  };
1806
1806
  }
@@ -1811,7 +1811,7 @@ function mergeInterfaceTypeDefinitionNodes(a, b) {
1811
1811
  name: a.name,
1812
1812
  description: a.description || b.description,
1813
1813
  interfaces: mergeNamedNodes(a.interfaces, b.interfaces),
1814
- directives: mergeDirectiveNodes(a.name.value, a.directives, b.directives),
1814
+ directives: mergeDirectiveNodes(a.directives, b.directives),
1815
1815
  fields: mergeFieldDefinitionNodes(a.name.value, a.fields, b.fields),
1816
1816
  };
1817
1817
  }
@@ -1821,7 +1821,7 @@ function mergeUnionTypeDefinitionNodes(a, b) {
1821
1821
  loc: a.loc,
1822
1822
  name: a.name,
1823
1823
  description: a.description || b.description,
1824
- directives: mergeDirectiveNodes(a.name.value, a.directives, b.directives),
1824
+ directives: mergeDirectiveNodes(a.directives, b.directives),
1825
1825
  types: mergeNamedNodes(a.types, b.types),
1826
1826
  };
1827
1827
  }
@@ -1830,7 +1830,7 @@ function mergeEnumTypeDefinitionNodes(a, b) {
1830
1830
  if (b) {
1831
1831
  for (const v of b.values || []) {
1832
1832
  if (v.name.value in values) {
1833
- throw setTraceToSchema(new graphql.GraphQLError(`Cannot merge duplicate enum value: ${v.name.value}.${v.name.value}`), `${v.name.value}.${v.name.value}`, v.loc);
1833
+ 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
1834
  }
1835
1835
  values[v.name.value] = v;
1836
1836
  }
@@ -1840,7 +1840,7 @@ function mergeEnumTypeDefinitionNodes(a, b) {
1840
1840
  loc: a.loc,
1841
1841
  name: a.name,
1842
1842
  description: a.description || b.description,
1843
- directives: mergeDirectiveNodes(a.name.value, a.directives, b.directives),
1843
+ directives: mergeDirectiveNodes(a.directives, b.directives),
1844
1844
  values: Object.values(values),
1845
1845
  };
1846
1846
  }
@@ -1850,26 +1850,74 @@ function mergeInputObjectTypeDefinitionNodes(a, b) {
1850
1850
  loc: a.loc,
1851
1851
  name: a.name,
1852
1852
  description: a.description || b.description,
1853
- directives: mergeDirectiveNodes(a.name.value, a.directives, b.directives),
1853
+ directives: mergeDirectiveNodes(a.directives, b.directives),
1854
1854
  fields: mergeInputValueDefinitionNodes(a.name.value, a.fields, b.fields),
1855
1855
  };
1856
1856
  }
1857
+ const EXTENSION_DEFINITION_KINDS = {
1858
+ [graphql.Kind.SCALAR_TYPE_EXTENSION]: graphql.Kind.SCALAR_TYPE_DEFINITION,
1859
+ [graphql.Kind.OBJECT_TYPE_EXTENSION]: graphql.Kind.OBJECT_TYPE_DEFINITION,
1860
+ [graphql.Kind.INTERFACE_TYPE_EXTENSION]: graphql.Kind.INTERFACE_TYPE_DEFINITION,
1861
+ [graphql.Kind.UNION_TYPE_EXTENSION]: graphql.Kind.UNION_TYPE_DEFINITION,
1862
+ [graphql.Kind.ENUM_TYPE_EXTENSION]: graphql.Kind.ENUM_TYPE_DEFINITION,
1863
+ [graphql.Kind.INPUT_OBJECT_TYPE_EXTENSION]: graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION,
1864
+ };
1865
+ function mergeTypeDefinitionNodes(a, b) {
1866
+ switch (b.kind) {
1867
+ case graphql.Kind.SCALAR_TYPE_DEFINITION:
1868
+ return mergeScalarTypeDefinitionNodes(a, b);
1869
+ case graphql.Kind.OBJECT_TYPE_DEFINITION:
1870
+ return mergeObjectTypeDefinitionNodes(a, b);
1871
+ case graphql.Kind.INTERFACE_TYPE_DEFINITION:
1872
+ return mergeInterfaceTypeDefinitionNodes(a, b);
1873
+ case graphql.Kind.UNION_TYPE_DEFINITION:
1874
+ return mergeUnionTypeDefinitionNodes(a, b);
1875
+ case graphql.Kind.ENUM_TYPE_DEFINITION:
1876
+ return mergeEnumTypeDefinitionNodes(a, b);
1877
+ case graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION:
1878
+ return mergeInputObjectTypeDefinitionNodes(a, b);
1879
+ }
1880
+ }
1881
+ function applyTypeExtensionNode(definitions, extension) {
1882
+ const name = extension.name.value;
1883
+ const definition = definitions[name];
1884
+ if (!definition) {
1885
+ throw setTraceToSchema(new graphql.GraphQLError(`Cannot extend type that is not defined: ${name}`), name, extension.loc);
1886
+ }
1887
+ const kind = EXTENSION_DEFINITION_KINDS[extension.kind];
1888
+ if (definition.kind !== kind) {
1889
+ throw setTraceToSchema(new graphql.GraphQLError(`Cannot extend ${definition.kind} with ${extension.kind}: ${name}`), name, extension.loc);
1890
+ }
1891
+ // An extension has the same shape as its definition, without a description.
1892
+ const merged = mergeTypeDefinitionNodes(definition, {
1893
+ ...extension,
1894
+ kind,
1895
+ directives: undefined,
1896
+ });
1897
+ // The directives of an extension are added to the directives of the type.
1898
+ // buildASTSchema rejects a non-repeatable directive that occurs more than
1899
+ // once.
1900
+ definitions[name] = {
1901
+ ...merged,
1902
+ directives: [
1903
+ ...(definition.directives ?? []),
1904
+ ...(extension.directives ?? []),
1905
+ ],
1906
+ };
1907
+ }
1857
1908
  function mergeDocumentNodes(documentNodes) {
1858
1909
  const definitions = {};
1910
+ const extensions = [];
1859
1911
  for (const documentNode of documentNodes) {
1860
1912
  for (const definition of documentNode.definitions) {
1861
- switch (definition.kind) {
1862
- case graphql.Kind.SCHEMA_DEFINITION:
1863
- case graphql.Kind.SCHEMA_EXTENSION:
1864
- case graphql.Kind.OPERATION_DEFINITION:
1865
- case graphql.Kind.FRAGMENT_DEFINITION:
1866
- case graphql.Kind.SCALAR_TYPE_EXTENSION:
1867
- case graphql.Kind.OBJECT_TYPE_EXTENSION:
1868
- case graphql.Kind.INTERFACE_TYPE_EXTENSION:
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}`);
1913
+ if (graphql.isTypeExtensionNode(definition)) {
1914
+ // Apply extensions after all definitions are known, because an
1915
+ // extension can occur before the definition that it extends.
1916
+ extensions.push(definition);
1917
+ continue;
1918
+ }
1919
+ if (!isMergableDefinitionNode(definition)) {
1920
+ throw new Error(`unexpected definition kind: ${definition.kind}`);
1873
1921
  }
1874
1922
  const prevDefinition = definitions[definition.name.value];
1875
1923
  if (!prevDefinition) {
@@ -1887,30 +1935,12 @@ function mergeDocumentNodes(documentNodes) {
1887
1935
  prevDefinition.description.value !== definition.description.value) {
1888
1936
  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
1937
  }
1890
- switch (definition.kind) {
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
- }
1938
+ definitions[definition.name.value] = mergeTypeDefinitionNodes(prevDefinition, definition);
1912
1939
  }
1913
1940
  }
1941
+ for (const extension of extensions) {
1942
+ applyTypeExtensionNode(definitions, extension);
1943
+ }
1914
1944
  return {
1915
1945
  kind: graphql.Kind.DOCUMENT,
1916
1946
  loc: documentNodes[0].loc,
package/build/lib.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import path, { resolve, basename, extname, join } 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';
@@ -338,14 +338,8 @@ function renderAstNode(astNode) {
338
338
  }
339
339
  function isBuiltInDirective(name) {
340
340
  // https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives
341
- switch (name) {
342
- case "deprecated":
343
- case "skip":
344
- case "include":
345
- case "specifiedBy":
346
- return true;
347
- }
348
- return false;
341
+ // Use the list from graphql, because new versions add directives (for example, @oneOf).
342
+ return specifiedDirectives.some((d) => d.name === name);
349
343
  }
350
344
  function truthify(arr) {
351
345
  return arr.filter(Boolean);
@@ -1611,6 +1605,9 @@ function setTraceToSchema(err, name, loc) {
1611
1605
  }
1612
1606
  return err;
1613
1607
  }
1608
+ function isMergableDefinitionNode(node) {
1609
+ return isTypeDefinitionNode(node) || node.kind === Kind.DIRECTIVE_DEFINITION;
1610
+ }
1614
1611
  function namedNodesDict(nodes = []) {
1615
1612
  return Object.fromEntries(nodes.map((node) => [node.name.value, node]));
1616
1613
  }
@@ -1724,7 +1721,7 @@ function mergeFieldDefinitionNodes(name, a, b) {
1724
1721
  console.warn(`WARNING: there is more than one declaration for ${name}.${f.name.value}${traceLine(f.loc)}${traceLine(prev.loc, "previously ")}\n`);
1725
1722
  fields[f.name.value] = {
1726
1723
  ...prev,
1727
- directives: mergeDirectiveNodes(`${name}.${f.name.value}`, prev.directives, f.directives),
1724
+ directives: mergeDirectiveNodes(prev.directives, f.directives),
1728
1725
  };
1729
1726
  }
1730
1727
  else {
@@ -1749,7 +1746,7 @@ function mergeInputValueDefinitionNodes(name, a, b) {
1749
1746
  console.warn(`WARNING: there is more than one declaration for ${name}.${f.name.value}${traceLine(f.loc)}${traceLine(prev.loc, "previously ")}\n`);
1750
1747
  fields[f.name.value] = {
1751
1748
  ...prev,
1752
- directives: mergeDirectiveNodes(`${name}.${f.name.value}`, prev.directives, f.directives),
1749
+ directives: mergeDirectiveNodes(prev.directives, f.directives),
1753
1750
  };
1754
1751
  }
1755
1752
  else {
@@ -1768,18 +1765,19 @@ function mergeNamedNodes(a, b) {
1768
1765
  }
1769
1766
  return Object.values(nodes);
1770
1767
  }
1771
- function mergeDirectiveNodes(name, a, b) {
1772
- const directives = namedNodesDict(a);
1773
- if (b) {
1774
- for (const d of b) {
1775
- if (d.name.value in directives &&
1776
- !isArgumentNodesEqual(directives[d.name.value].arguments, d.arguments)) {
1777
- throw setTraceToSchema(new GraphQLError(`Cannot merge duplicate directive with different arguments: ${name} @${d.name.value}`), `${name} @${d.name.value}`, d.loc);
1778
- }
1779
- directives[d.name.value] = d;
1780
- }
1781
- }
1782
- return Object.values(directives);
1768
+ function mergeDirectiveNodes(a, b) {
1769
+ // Keep each occurrence, because a repeatable directive can occur more than
1770
+ // once. Skip only an occurrence that is identical to one that is already
1771
+ // applied. buildASTSchema rejects a non-repeatable directive that occurs
1772
+ // more than once.
1773
+ const directives = Array.from(a ?? []);
1774
+ for (const d of b ?? []) {
1775
+ if (!directives.some((prev) => prev.name.value === d.name.value &&
1776
+ isArgumentNodesEqual(prev.arguments, d.arguments))) {
1777
+ directives.push(d);
1778
+ }
1779
+ }
1780
+ return directives;
1783
1781
  }
1784
1782
  function mergeScalarTypeDefinitionNodes(a, b) {
1785
1783
  if (a.name.value !== b.name.value) {
@@ -1787,8 +1785,10 @@ function mergeScalarTypeDefinitionNodes(a, b) {
1787
1785
  }
1788
1786
  return {
1789
1787
  kind: Kind.SCALAR_TYPE_DEFINITION,
1788
+ loc: a.loc,
1790
1789
  name: a.name,
1791
- directives: mergeDirectiveNodes(a.name.value, a.directives, b.directives),
1790
+ description: a.description || b.description,
1791
+ directives: mergeDirectiveNodes(a.directives, b.directives),
1792
1792
  };
1793
1793
  }
1794
1794
  function mergeObjectTypeDefinitionNodes(a, b) {
@@ -1798,7 +1798,7 @@ function mergeObjectTypeDefinitionNodes(a, b) {
1798
1798
  name: a.name,
1799
1799
  description: a.description || b.description,
1800
1800
  interfaces: mergeNamedNodes(a.interfaces, b.interfaces),
1801
- directives: mergeDirectiveNodes(a.name.value, a.directives, b.directives),
1801
+ directives: mergeDirectiveNodes(a.directives, b.directives),
1802
1802
  fields: mergeFieldDefinitionNodes(a.name.value, a.fields, b.fields),
1803
1803
  };
1804
1804
  }
@@ -1809,7 +1809,7 @@ function mergeInterfaceTypeDefinitionNodes(a, b) {
1809
1809
  name: a.name,
1810
1810
  description: a.description || b.description,
1811
1811
  interfaces: mergeNamedNodes(a.interfaces, b.interfaces),
1812
- directives: mergeDirectiveNodes(a.name.value, a.directives, b.directives),
1812
+ directives: mergeDirectiveNodes(a.directives, b.directives),
1813
1813
  fields: mergeFieldDefinitionNodes(a.name.value, a.fields, b.fields),
1814
1814
  };
1815
1815
  }
@@ -1819,7 +1819,7 @@ function mergeUnionTypeDefinitionNodes(a, b) {
1819
1819
  loc: a.loc,
1820
1820
  name: a.name,
1821
1821
  description: a.description || b.description,
1822
- directives: mergeDirectiveNodes(a.name.value, a.directives, b.directives),
1822
+ directives: mergeDirectiveNodes(a.directives, b.directives),
1823
1823
  types: mergeNamedNodes(a.types, b.types),
1824
1824
  };
1825
1825
  }
@@ -1828,7 +1828,7 @@ function mergeEnumTypeDefinitionNodes(a, b) {
1828
1828
  if (b) {
1829
1829
  for (const v of b.values || []) {
1830
1830
  if (v.name.value in values) {
1831
- throw setTraceToSchema(new GraphQLError(`Cannot merge duplicate enum value: ${v.name.value}.${v.name.value}`), `${v.name.value}.${v.name.value}`, v.loc);
1831
+ throw setTraceToSchema(new GraphQLError(`Cannot merge duplicate enum value: ${a.name.value}.${v.name.value}`), `${a.name.value}.${v.name.value}`, v.loc);
1832
1832
  }
1833
1833
  values[v.name.value] = v;
1834
1834
  }
@@ -1838,7 +1838,7 @@ function mergeEnumTypeDefinitionNodes(a, b) {
1838
1838
  loc: a.loc,
1839
1839
  name: a.name,
1840
1840
  description: a.description || b.description,
1841
- directives: mergeDirectiveNodes(a.name.value, a.directives, b.directives),
1841
+ directives: mergeDirectiveNodes(a.directives, b.directives),
1842
1842
  values: Object.values(values),
1843
1843
  };
1844
1844
  }
@@ -1848,26 +1848,74 @@ function mergeInputObjectTypeDefinitionNodes(a, b) {
1848
1848
  loc: a.loc,
1849
1849
  name: a.name,
1850
1850
  description: a.description || b.description,
1851
- directives: mergeDirectiveNodes(a.name.value, a.directives, b.directives),
1851
+ directives: mergeDirectiveNodes(a.directives, b.directives),
1852
1852
  fields: mergeInputValueDefinitionNodes(a.name.value, a.fields, b.fields),
1853
1853
  };
1854
1854
  }
1855
+ const EXTENSION_DEFINITION_KINDS = {
1856
+ [Kind.SCALAR_TYPE_EXTENSION]: Kind.SCALAR_TYPE_DEFINITION,
1857
+ [Kind.OBJECT_TYPE_EXTENSION]: Kind.OBJECT_TYPE_DEFINITION,
1858
+ [Kind.INTERFACE_TYPE_EXTENSION]: Kind.INTERFACE_TYPE_DEFINITION,
1859
+ [Kind.UNION_TYPE_EXTENSION]: Kind.UNION_TYPE_DEFINITION,
1860
+ [Kind.ENUM_TYPE_EXTENSION]: Kind.ENUM_TYPE_DEFINITION,
1861
+ [Kind.INPUT_OBJECT_TYPE_EXTENSION]: Kind.INPUT_OBJECT_TYPE_DEFINITION,
1862
+ };
1863
+ function mergeTypeDefinitionNodes(a, b) {
1864
+ switch (b.kind) {
1865
+ case Kind.SCALAR_TYPE_DEFINITION:
1866
+ return mergeScalarTypeDefinitionNodes(a, b);
1867
+ case Kind.OBJECT_TYPE_DEFINITION:
1868
+ return mergeObjectTypeDefinitionNodes(a, b);
1869
+ case Kind.INTERFACE_TYPE_DEFINITION:
1870
+ return mergeInterfaceTypeDefinitionNodes(a, b);
1871
+ case Kind.UNION_TYPE_DEFINITION:
1872
+ return mergeUnionTypeDefinitionNodes(a, b);
1873
+ case Kind.ENUM_TYPE_DEFINITION:
1874
+ return mergeEnumTypeDefinitionNodes(a, b);
1875
+ case Kind.INPUT_OBJECT_TYPE_DEFINITION:
1876
+ return mergeInputObjectTypeDefinitionNodes(a, b);
1877
+ }
1878
+ }
1879
+ function applyTypeExtensionNode(definitions, extension) {
1880
+ const name = extension.name.value;
1881
+ const definition = definitions[name];
1882
+ if (!definition) {
1883
+ throw setTraceToSchema(new GraphQLError(`Cannot extend type that is not defined: ${name}`), name, extension.loc);
1884
+ }
1885
+ const kind = EXTENSION_DEFINITION_KINDS[extension.kind];
1886
+ if (definition.kind !== kind) {
1887
+ throw setTraceToSchema(new GraphQLError(`Cannot extend ${definition.kind} with ${extension.kind}: ${name}`), name, extension.loc);
1888
+ }
1889
+ // An extension has the same shape as its definition, without a description.
1890
+ const merged = mergeTypeDefinitionNodes(definition, {
1891
+ ...extension,
1892
+ kind,
1893
+ directives: undefined,
1894
+ });
1895
+ // The directives of an extension are added to the directives of the type.
1896
+ // buildASTSchema rejects a non-repeatable directive that occurs more than
1897
+ // once.
1898
+ definitions[name] = {
1899
+ ...merged,
1900
+ directives: [
1901
+ ...(definition.directives ?? []),
1902
+ ...(extension.directives ?? []),
1903
+ ],
1904
+ };
1905
+ }
1855
1906
  function mergeDocumentNodes(documentNodes) {
1856
1907
  const definitions = {};
1908
+ const extensions = [];
1857
1909
  for (const documentNode of documentNodes) {
1858
1910
  for (const definition of documentNode.definitions) {
1859
- switch (definition.kind) {
1860
- case Kind.SCHEMA_DEFINITION:
1861
- case Kind.SCHEMA_EXTENSION:
1862
- case Kind.OPERATION_DEFINITION:
1863
- case Kind.FRAGMENT_DEFINITION:
1864
- case Kind.SCALAR_TYPE_EXTENSION:
1865
- case Kind.OBJECT_TYPE_EXTENSION:
1866
- case Kind.INTERFACE_TYPE_EXTENSION:
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}`);
1911
+ if (isTypeExtensionNode(definition)) {
1912
+ // Apply extensions after all definitions are known, because an
1913
+ // extension can occur before the definition that it extends.
1914
+ extensions.push(definition);
1915
+ continue;
1916
+ }
1917
+ if (!isMergableDefinitionNode(definition)) {
1918
+ throw new Error(`unexpected definition kind: ${definition.kind}`);
1871
1919
  }
1872
1920
  const prevDefinition = definitions[definition.name.value];
1873
1921
  if (!prevDefinition) {
@@ -1885,30 +1933,12 @@ function mergeDocumentNodes(documentNodes) {
1885
1933
  prevDefinition.description.value !== definition.description.value) {
1886
1934
  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
1935
  }
1888
- switch (definition.kind) {
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
- }
1936
+ definitions[definition.name.value] = mergeTypeDefinitionNodes(prevDefinition, definition);
1910
1937
  }
1911
1938
  }
1939
+ for (const extension of extensions) {
1940
+ applyTypeExtensionNode(definitions, extension);
1941
+ }
1912
1942
  return {
1913
1943
  kind: Kind.DOCUMENT,
1914
1944
  loc: documentNodes[0].loc,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphql-jerky",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Jerky is a schema-first static graphql schema lightweight code generator.",
5
5
  "type": "module",
6
6
  "bin": "./build/bin/jerky.cjs",