graphql-jerky 1.0.2 → 1.0.4

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/build/lib.cjs CHANGED
@@ -39,23 +39,26 @@ async function findExports(source, resolveModuleFn = resolveModule) {
39
39
  if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) {
40
40
  if (node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
41
41
  if (node.modifiers.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword)) {
42
- exports.default = { declaration: node, source, name: "default" };
42
+ (exports.default || (exports.default = [])).push({ declaration: node, source, name: "default" });
43
43
  }
44
44
  else if (node.name) {
45
- exports[node.name.getText(source)] = { declaration: node, source, name: node.name.getText(source) };
45
+ const name = node.name.getText(source);
46
+ (exports[name] || (exports[name] = [])).push({ declaration: node, source, name: name });
46
47
  }
47
48
  }
48
49
  }
49
50
  else if (ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node) || ts.isModuleDeclaration(node)) {
50
51
  if (node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
51
- exports[node.name.getText(source)] = { declaration: node, source, name: node.name.getText(source) };
52
+ const name = node.name.getText(source);
53
+ (exports[name] || (exports[name] = [])).push({ declaration: node, source, name: name });
52
54
  }
53
55
  }
54
56
  else if (ts.isVariableStatement(node)) {
55
57
  if (node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
56
58
  for (const decl of node.declarationList.declarations) {
57
59
  if (decl.name) {
58
- exports[decl.name.getText(source)] = { declaration: decl, source, name: decl.name.getText(source) };
60
+ const name = decl.name.getText(source);
61
+ (exports[name] || (exports[name] = [])).push({ declaration: decl, source, name: name });
59
62
  }
60
63
  }
61
64
  }
@@ -73,7 +76,7 @@ async function findExports(source, resolveModuleFn = resolveModule) {
73
76
  if (!(name in moduleExports)) {
74
77
  throw new Error(`Exported name ${name} not found in ${JSON.stringify(specifier)}`);
75
78
  }
76
- exports[name] = moduleExports[name];
79
+ (exports[name] || (exports[name] = [])).push(...moduleExports[name]);
77
80
  }
78
81
  }
79
82
  else {
@@ -82,7 +85,6 @@ async function findExports(source, resolveModuleFn = resolveModule) {
82
85
  name: element.name.text,
83
86
  property: element.propertyName?.text,
84
87
  });
85
- // exports[element.name.getText(source)] = { declaration: element, source };
86
88
  }
87
89
  }
88
90
  }
@@ -98,57 +100,63 @@ async function findExports(source, resolveModuleFn = resolveModule) {
98
100
  if (ts.isVariableStatement(statement)) {
99
101
  for (const decl of statement.declarationList.declarations) {
100
102
  if (ts.isIdentifier(decl.name) && decl.name.getText(source) in names) {
101
- exports[names[decl.name.getText(source)]] = {
103
+ const name = names[decl.name.getText(source)];
104
+ (exports[name] || (exports[name] = [])).push({
102
105
  declaration: decl,
103
106
  source,
104
107
  name: names[decl.name.getText(source)],
105
- };
108
+ });
106
109
  }
107
110
  }
108
111
  }
109
112
  if (ts.isFunctionDeclaration(statement)) {
110
113
  if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
111
- exports[names[statement.name.getText(source)]] = {
114
+ const name = names[statement.name.getText(source)];
115
+ (exports[name] || (exports[name] = [])).push({
112
116
  declaration: statement,
113
117
  source,
114
118
  name: names[statement.name.getText(source)],
115
- };
119
+ });
116
120
  }
117
121
  }
118
122
  if (ts.isClassDeclaration(statement)) {
119
123
  if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
120
- exports[names[statement.name.getText(source)]] = {
124
+ const name = names[statement.name.getText(source)];
125
+ (exports[name] || (exports[name] = [])).push({
121
126
  declaration: statement,
122
127
  source,
123
128
  name: names[statement.name.getText(source)],
124
- };
129
+ });
125
130
  }
126
131
  }
127
132
  if (ts.isTypeAliasDeclaration(statement)) {
128
133
  if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
129
- exports[names[statement.name.getText(source)]] = {
134
+ const name = names[statement.name.getText(source)];
135
+ (exports[name] || (exports[name] = [])).push({
130
136
  declaration: statement,
131
137
  source,
132
138
  name: names[statement.name.getText(source)],
133
- };
139
+ });
134
140
  }
135
141
  }
136
142
  if (ts.isInterfaceDeclaration(statement)) {
137
143
  if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
138
- exports[names[statement.name.getText(source)]] = {
144
+ const name = names[statement.name.getText(source)];
145
+ (exports[name] || (exports[name] = [])).push({
139
146
  declaration: statement,
140
147
  source,
141
148
  name: names[statement.name.getText(source)],
142
- };
149
+ });
143
150
  }
144
151
  }
145
152
  if (ts.isEnumDeclaration(statement)) {
146
153
  if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
147
- exports[names[statement.name.getText(source)]] = {
154
+ const name = names[statement.name.getText(source)];
155
+ (exports[name] || (exports[name] = [])).push({
148
156
  declaration: statement,
149
157
  source,
150
158
  name: names[statement.name.getText(source)],
151
- };
159
+ });
152
160
  }
153
161
  }
154
162
  if (ts.isImportDeclaration(statement)) {
@@ -168,11 +176,12 @@ async function findExports(source, resolveModuleFn = resolveModule) {
168
176
  if (!importExports[element.name.getText(source)]) {
169
177
  throw new Error(`Exported name ${element.name.getText(source)} not found in ${JSON.stringify(statement.moduleSpecifier.getText(source))}`);
170
178
  }
171
- exports[names[element.propertyName.getText(source)]] = {
179
+ const name = names[element.propertyName.getText(source)];
180
+ (exports[name] || (exports[name] = [])).push({
172
181
  declaration: element,
173
182
  source,
174
183
  name: names[element.propertyName.getText(source)],
175
- };
184
+ });
176
185
  }
177
186
  }
178
187
  }
@@ -239,16 +248,9 @@ function compareEntryKey([a], [b]) {
239
248
  function compareName({ name: a }, { name: b }) {
240
249
  return a.localeCompare(b);
241
250
  }
251
+ const PRIMITIVE_TYPES = new Set(["ID", "Int", "Float", "String", "Boolean"]);
242
252
  function isPrimitiveType(type) {
243
- switch (type.name) {
244
- case "ID":
245
- case "Int":
246
- case "Float":
247
- case "String":
248
- case "Boolean":
249
- return true;
250
- }
251
- return false;
253
+ return PRIMITIVE_TYPES.has(type.name);
252
254
  }
253
255
  function importAlias(line) {
254
256
  const pos = line.indexOf(" as ");
@@ -283,6 +285,9 @@ function renderImports(base, imports, aliases, extMode = "omit") {
283
285
  .map((symbol) => symbol in aliases ? jen.id(aliases[symbol]).as.id(symbol) : jen.id(symbol))).from.lit(mod);
284
286
  }));
285
287
  }
288
+ function renderAstNode(astNode) {
289
+ return jen.id("JSON").dot("parse").call(jen.literal(JSON.stringify(astNode, (key, value) => key === "loc" ? undefined : value)));
290
+ }
286
291
  function isBuiltInDirective(name) {
287
292
  // https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives
288
293
  switch (name) {
@@ -364,7 +369,7 @@ class GeneratorContext {
364
369
  _renderArgumentNodes(nodes) {
365
370
  return jen.obj(...Array.from(nodes ?? [], (arg) => jen.prop(arg.name.value, this._renderValueNode(arg.value))));
366
371
  }
367
- _renderFieldObject(type, f, inputFieldDirectives, noresolver) {
372
+ _renderFieldObject(type, f, inputFieldDirectives, astNodeExpr, noresolver) {
368
373
  let resolveFn;
369
374
  if (!noresolver) {
370
375
  let resolver = this._resolversInfo[type.name]?.[f.name];
@@ -421,12 +426,18 @@ class GeneratorContext {
421
426
  // executor implementation, but nonetheless accessible by a potential alternate implementation.
422
427
  resolveFn && jen.prop("resolve", jen.parens(resolveFn).as.any),
423
428
  subscriber && jen.prop("subscribe", jen.parens(renderSymbolReference(this._pkgs, subscriber)).as.any),
429
+ astNodeExpr && jen.prop("astNode", astNodeExpr),
424
430
  ]));
425
431
  }
426
- _renderFieldsThunk(type, inputFieldDirectives, noresolver) {
432
+ _renderFieldsThunk(type, inputFieldDirectives, includeAstNodes, noresolver) {
433
+ let astNodeExpr;
434
+ if (includeAstNodes) {
435
+ astNodeExpr = jen.id(type.name + "Type").dot("astNode").op("?").dot("fields").op("?.");
436
+ }
427
437
  return jen.arrow().parens(jen.obj(...Object.values(type.getFields())
428
- .sort(compareName)
429
- .map((field) => jen.prop(field.name, this._renderFieldObject(type, field, inputFieldDirectives, noresolver)))));
438
+ .map((field, i) => [field, i])
439
+ .sort(([a], [b]) => compareName(a, b))
440
+ .map(([field, i]) => jen.prop(field.name, this._renderFieldObject(type, field, inputFieldDirectives, astNodeExpr?.index(i), noresolver)))));
430
441
  }
431
442
  _renderOptionalGraphQLTypeType(type) {
432
443
  if (type instanceof graphql.GraphQLNonNull) {
@@ -500,13 +511,14 @@ class GeneratorContext {
500
511
  .sort(compareName)
501
512
  .map((field) => jen.prop(jen.id(field.name).op("?"), this._renderGraphQLTypeType(field.type))));
502
513
  }
503
- _renderGraphQLObjectTypeDefinition(type, { inputFieldDirectives }) {
514
+ _renderGraphQLObjectTypeDefinition(type, { inputFieldDirectives, includeAstNodes }) {
504
515
  const typeInterfaces = type.getInterfaces();
505
- return jen.new.add(this._gql("GraphQLObjectType")).call(jen.obj(jen.prop("name", jen.lit(type.name)), jen.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives, type.name === "Subscription")), ...truthify([
516
+ return jen.new.add(this._gql("GraphQLObjectType")).call(jen.obj(jen.prop("name", jen.lit(type.name)), jen.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives, includeAstNodes, type.name === "Subscription")), ...truthify([
506
517
  type.description && jen.prop("description", jen.lit(type.description)),
507
518
  typeInterfaces.length && jen.prop("interfaces", jen.arrow().arr(
508
519
  // do not sort interfaces, the declaration order may be important
509
520
  ...typeInterfaces.map(this._renderGraphQLTypeIdentifier, this))),
521
+ includeAstNodes && type.astNode && jen.prop("astNode", renderAstNode(type.astNode)),
510
522
  ]))).as.add(this._gql("GraphQLObjectType"));
511
523
  }
512
524
  _renderGraphQLObjectTypeTypes(type) {
@@ -545,9 +557,10 @@ class GeneratorContext {
545
557
  ],
546
558
  ];
547
559
  }
548
- _renderGraphQLInterfaceTypeDefinition(type, { implementors: { implementors }, inputFieldDirectives }) {
549
- return jen.new.add(this._gql("GraphQLInterfaceType")).call(jen.obj(jen.prop("name", jen.lit(type.name)), jen.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives)), ...truthify([
560
+ _renderGraphQLInterfaceTypeDefinition(type, { inputFieldDirectives, includeAstNodes }) {
561
+ return jen.new.add(this._gql("GraphQLInterfaceType")).call(jen.obj(jen.prop("name", jen.lit(type.name)), jen.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives, includeAstNodes)), ...truthify([
550
562
  type.description && jen.prop("description", jen.lit(type.description)),
563
+ includeAstNodes && type.astNode && jen.prop("astNode", renderAstNode(type.astNode)),
551
564
  ]))).as.add(this._gql("GraphQLInterfaceType"));
552
565
  }
553
566
  _renderGraphQLInterfaceTypeTypes(type, { implementors: { implementors } }) {
@@ -626,9 +639,10 @@ class GeneratorContext {
626
639
  return jen.prop(field.name, transformedExpr);
627
640
  }));
628
641
  }
629
- _renderGraphQLInputObjectTypeDefinition(type, { inputFieldDirectives }) {
630
- return jen.new.add(this._gql("GraphQLInputObjectType")).call(jen.obj(jen.prop("name", jen.lit(type.name)), jen.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives, true)), ...truthify([
642
+ _renderGraphQLInputObjectTypeDefinition(type, { inputFieldDirectives, includeAstNodes }) {
643
+ return jen.new.add(this._gql("GraphQLInputObjectType")).call(jen.obj(jen.prop("name", jen.lit(type.name)), jen.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives, includeAstNodes, true)), ...truthify([
631
644
  type.description && jen.prop("description", jen.lit(type.description)),
645
+ includeAstNodes && type.astNode && jen.prop("astNode", renderAstNode(type.astNode)),
632
646
  ])));
633
647
  }
634
648
  _renderGraphQLInputObjectTypeTypes(type, { inputFieldDirectives }) {
@@ -703,10 +717,11 @@ class GeneratorContext {
703
717
  }
704
718
  throw new Error("unhandled type: " + type.toString());
705
719
  }
706
- _renderGraphQLTypeDefinitions(type, interfaces, inputFieldDirectives) {
720
+ _renderGraphQLTypeDefinitions(type, interfaces, inputFieldDirectives, includeAstNodes) {
707
721
  return jen.const.add(this._renderGraphQLTypeIdentifier(type)).op("=").add(this._getRenderDefintionFnForType(type)({
708
722
  implementors: interfaces[type.name],
709
723
  inputFieldDirectives,
724
+ includeAstNodes,
710
725
  }));
711
726
  }
712
727
  _renderGraphQLTypeTypes(type, interfaces, inputFieldDirectives) {
@@ -715,11 +730,12 @@ class GeneratorContext {
715
730
  inputFieldDirectives,
716
731
  });
717
732
  }
718
- _renderGraphQLDirectiveDefinition(directive) {
733
+ _renderGraphQLDirectiveDefinition(directive, includeAstNodes) {
719
734
  return jen.const.add(this._renderGraphQLTypeIdentifier(directive)).op("=").new.add(this._gql("GraphQLDirective"))
720
735
  .call(jen.obj(jen.prop("name", jen.lit(directive.name)), jen.prop("locations", jen.array(...Array.from(directive.locations).sort().map((location) => jen.add(this._gql("DirectiveLocation")).dot(location)))), jen.prop("args", this._renderArgumentsObject(directive.args)), ...truthify([
721
736
  directive.description && jen.prop("description", jen.lit(directive.description)),
722
737
  directive.isRepeatable && jen.prop("isRepeatable", jen.true),
738
+ includeAstNodes && directive.astNode && jen.prop("astNode", renderAstNode(directive.astNode)),
723
739
  ])));
724
740
  }
725
741
  _renderGraphQLDirectiveTypes(directive) {
@@ -729,12 +745,12 @@ class GeneratorContext {
729
745
  ])), this._renderGraphQLTypeType(arg.type)))),
730
746
  ]);
731
747
  }
732
- schemaExprs(schema, [types, interfaces, inputFieldDirectives]) {
748
+ schemaExprs(schema, [types, interfaces, inputFieldDirectives, includeAstNodes]) {
733
749
  return [
734
750
  ...[
735
751
  // render type definitions
736
752
  ...types
737
- .map((type) => [type.name, this._renderGraphQLTypeDefinitions(type, interfaces, inputFieldDirectives)])
753
+ .map((type) => [type.name, this._renderGraphQLTypeDefinitions(type, interfaces, inputFieldDirectives, includeAstNodes)])
738
754
  .sort(compareEntryKey),
739
755
  // render directive definitions
740
756
  ...schema.getDirectives()
@@ -742,6 +758,20 @@ class GeneratorContext {
742
758
  .map((directive) => [directive.name, this._renderGraphQLDirectiveDefinition(directive)])
743
759
  .sort(compareEntryKey),
744
760
  ].map(([, expr]) => expr),
761
+ ...Array.from(PRIMITIVE_TYPES)
762
+ .flatMap((type) => {
763
+ const info = this._scalarsInfo[type];
764
+ if (!info) {
765
+ return [];
766
+ }
767
+ return [
768
+ jen.statements(...[
769
+ info.serializeFunc && this._gql("GraphQL" + type).dot("serialize").op("=").parens(renderSymbolReference(this._pkgs, info.serializeFunc).as.any),
770
+ info.parseValueFunc && this._gql("GraphQL" + type).dot("parseValue").op("=").parens(renderSymbolReference(this._pkgs, info.parseValueFunc).as.any),
771
+ info.parseLiteralFunc && this._gql("GraphQL" + type).dot("parseLiteral").op("=").parens(renderSymbolReference(this._pkgs, info.parseLiteralFunc).as.any),
772
+ ].filter(Boolean))
773
+ ];
774
+ }),
745
775
  // render schema
746
776
  jen.const.id("schema").op("=").new.add(this._gql("GraphQLSchema")).call(jen.obj(...(schema.getQueryType()
747
777
  ? [jen.prop("query", this._renderGraphQLTypeIdentifier(schema.getQueryType()))]
@@ -769,10 +799,10 @@ class GeneratorContext {
769
799
  .sort(compareEntryKey),
770
800
  ].map(([, expr]) => expr);
771
801
  }
772
- schemaAndTypesExprs(schema, [types, interfaces, inputFieldDirectives]) {
802
+ schemaAndTypesExprs(schema, args) {
773
803
  return [
774
- ...this.schemaExprs(schema, [types, interfaces, inputFieldDirectives]),
775
- ...this.typesExprs(schema, [types, interfaces, inputFieldDirectives]),
804
+ ...this.schemaExprs(schema, args),
805
+ ...this.typesExprs(schema, args),
776
806
  ];
777
807
  }
778
808
  renderFile(outfile, renderStatements) {
@@ -854,7 +884,7 @@ class GeneratorContext {
854
884
  interfaces[iface.name].implementors.push(type);
855
885
  }
856
886
  }
857
- return [types, interfaces, inputFieldDirectives];
887
+ return [types, interfaces, inputFieldDirectives, options.includeAstNodes];
858
888
  }
859
889
  }
860
890
  function createGenerator(schema, options) {
@@ -918,7 +948,7 @@ function createMatcher(pattern) {
918
948
 
919
949
  async function parseResolverFile(path) {
920
950
  return Object.fromEntries(Object.entries((await parseModule(path)).exports)
921
- .flatMap(([name, { declaration }]) => {
951
+ .flatMap(([name, decls]) => decls.flatMap(({ declaration }) => {
922
952
  if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
923
953
  if (ts.isFunctionExpression(declaration.initializer)) {
924
954
  return [[name, { module: path, symbol: name }]];
@@ -931,7 +961,7 @@ async function parseResolverFile(path) {
931
961
  return [[name, { module: path, symbol: name }]];
932
962
  }
933
963
  return [];
934
- }));
964
+ })));
935
965
  }
936
966
  async function parseTypeResolvers(typeName, glob$1, ignore) {
937
967
  const shouldIgnore = createMatcher(ignore);
@@ -1006,6 +1036,12 @@ function refFromTypeReferenceNode(typeNode, source) {
1006
1036
  return;
1007
1037
  }
1008
1038
  const typeName = typeNode.typeName.text;
1039
+ if (typeNode.typeArguments?.length) {
1040
+ if (typeName !== 'Promise') {
1041
+ throw new Error('TODO: handle type arguments');
1042
+ }
1043
+ return refFromTypeNode(typeNode.typeArguments[0], source);
1044
+ }
1009
1045
  for (const statement of source.statements) {
1010
1046
  if (ts.isImportDeclaration(statement)) {
1011
1047
  // search imports
@@ -1145,21 +1181,23 @@ async function parse$1(path) {
1145
1181
  const scalarFnsInfos = {};
1146
1182
  const scalarTypeNodes = {};
1147
1183
  const sources = {};
1148
- for (const [name, { declaration, source }] of Object.entries((await parseModule(path)).exports)) {
1149
- sources[name] = source;
1150
- if (ts.isVariableDeclaration(declaration)) {
1151
- if (declaration.initializer && ts.isObjectLiteralExpression(declaration.initializer)) {
1152
- scalarFnsInfos[name] = scalarInfoFromObjectLiteralExpression(name, new URL(source.fileName).toString(), declaration.initializer.properties);
1153
- }
1154
- }
1155
- else if (ts.isTypeAliasDeclaration(declaration)) {
1156
- if (declaration.typeParameters?.length) {
1157
- console.log(`WARNING: skipping scalar type ${name}: type parameters are not supported`);
1158
- continue;
1184
+ for (const [name, decls] of Object.entries((await parseModule(path)).exports)) {
1185
+ for (const { declaration, source } of decls) {
1186
+ sources[name] = source;
1187
+ if (ts.isVariableDeclaration(declaration)) {
1188
+ if (declaration.initializer && ts.isObjectLiteralExpression(declaration.initializer)) {
1189
+ scalarFnsInfos[name] = scalarInfoFromObjectLiteralExpression(name, new URL(source.fileName).toString(), declaration.initializer.properties);
1190
+ }
1159
1191
  }
1160
- const ref = refFromTypeNode(declaration.type, source);
1161
- if (ref) {
1162
- scalarTypeNodes[name] = declaration.type;
1192
+ else if (ts.isTypeAliasDeclaration(declaration)) {
1193
+ if (declaration.typeParameters?.length) {
1194
+ console.log(`WARNING: skipping scalar type ${name}: type parameters are not supported`);
1195
+ continue;
1196
+ }
1197
+ const ref = refFromTypeNode(declaration.type, source);
1198
+ if (ref) {
1199
+ scalarTypeNodes[name] = declaration.type;
1200
+ }
1163
1201
  }
1164
1202
  }
1165
1203
  }
@@ -1172,7 +1210,7 @@ async function parse$1(path) {
1172
1210
  module: source.fileName,
1173
1211
  symbol: name,
1174
1212
  } : reconcileScalarType(name, [
1175
- fnsInfo?.serializeFunc?.declaration?.parameters?.[0]?.type,
1213
+ // NOTE: omit serialize input type because it is supposed to be `unknown`
1176
1214
  fnsInfo?.parseValueFunc?.declaration?.type,
1177
1215
  fnsInfo?.parseLiteralFunc?.declaration?.type,
1178
1216
  ], source);
package/build/lib.d.ts CHANGED
@@ -38,6 +38,7 @@ interface GenerateOptions {
38
38
  subscribersInfo?: Awaited<ReturnType<typeof parseTypeResolvers>>;
39
39
  graphqlModuleSpecifier?: string;
40
40
  extMode?: "omit" | "replace" | "keep";
41
+ includeAstNodes?: boolean;
41
42
  }
42
43
  declare function createGenerator(schema: GraphQLSchema, options: GenerateOptions): {
43
44
  generateSchema: (outfile: string) => string;
package/build/lib.mjs CHANGED
@@ -37,23 +37,26 @@ async function findExports(source, resolveModuleFn = resolveModule) {
37
37
  if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) {
38
38
  if (node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
39
39
  if (node.modifiers.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword)) {
40
- exports.default = { declaration: node, source, name: "default" };
40
+ (exports.default || (exports.default = [])).push({ declaration: node, source, name: "default" });
41
41
  }
42
42
  else if (node.name) {
43
- exports[node.name.getText(source)] = { declaration: node, source, name: node.name.getText(source) };
43
+ const name = node.name.getText(source);
44
+ (exports[name] || (exports[name] = [])).push({ declaration: node, source, name: name });
44
45
  }
45
46
  }
46
47
  }
47
48
  else if (ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node) || ts.isModuleDeclaration(node)) {
48
49
  if (node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
49
- exports[node.name.getText(source)] = { declaration: node, source, name: node.name.getText(source) };
50
+ const name = node.name.getText(source);
51
+ (exports[name] || (exports[name] = [])).push({ declaration: node, source, name: name });
50
52
  }
51
53
  }
52
54
  else if (ts.isVariableStatement(node)) {
53
55
  if (node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
54
56
  for (const decl of node.declarationList.declarations) {
55
57
  if (decl.name) {
56
- exports[decl.name.getText(source)] = { declaration: decl, source, name: decl.name.getText(source) };
58
+ const name = decl.name.getText(source);
59
+ (exports[name] || (exports[name] = [])).push({ declaration: decl, source, name: name });
57
60
  }
58
61
  }
59
62
  }
@@ -71,7 +74,7 @@ async function findExports(source, resolveModuleFn = resolveModule) {
71
74
  if (!(name in moduleExports)) {
72
75
  throw new Error(`Exported name ${name} not found in ${JSON.stringify(specifier)}`);
73
76
  }
74
- exports[name] = moduleExports[name];
77
+ (exports[name] || (exports[name] = [])).push(...moduleExports[name]);
75
78
  }
76
79
  }
77
80
  else {
@@ -80,7 +83,6 @@ async function findExports(source, resolveModuleFn = resolveModule) {
80
83
  name: element.name.text,
81
84
  property: element.propertyName?.text,
82
85
  });
83
- // exports[element.name.getText(source)] = { declaration: element, source };
84
86
  }
85
87
  }
86
88
  }
@@ -96,57 +98,63 @@ async function findExports(source, resolveModuleFn = resolveModule) {
96
98
  if (ts.isVariableStatement(statement)) {
97
99
  for (const decl of statement.declarationList.declarations) {
98
100
  if (ts.isIdentifier(decl.name) && decl.name.getText(source) in names) {
99
- exports[names[decl.name.getText(source)]] = {
101
+ const name = names[decl.name.getText(source)];
102
+ (exports[name] || (exports[name] = [])).push({
100
103
  declaration: decl,
101
104
  source,
102
105
  name: names[decl.name.getText(source)],
103
- };
106
+ });
104
107
  }
105
108
  }
106
109
  }
107
110
  if (ts.isFunctionDeclaration(statement)) {
108
111
  if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
109
- exports[names[statement.name.getText(source)]] = {
112
+ const name = names[statement.name.getText(source)];
113
+ (exports[name] || (exports[name] = [])).push({
110
114
  declaration: statement,
111
115
  source,
112
116
  name: names[statement.name.getText(source)],
113
- };
117
+ });
114
118
  }
115
119
  }
116
120
  if (ts.isClassDeclaration(statement)) {
117
121
  if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
118
- exports[names[statement.name.getText(source)]] = {
122
+ const name = names[statement.name.getText(source)];
123
+ (exports[name] || (exports[name] = [])).push({
119
124
  declaration: statement,
120
125
  source,
121
126
  name: names[statement.name.getText(source)],
122
- };
127
+ });
123
128
  }
124
129
  }
125
130
  if (ts.isTypeAliasDeclaration(statement)) {
126
131
  if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
127
- exports[names[statement.name.getText(source)]] = {
132
+ const name = names[statement.name.getText(source)];
133
+ (exports[name] || (exports[name] = [])).push({
128
134
  declaration: statement,
129
135
  source,
130
136
  name: names[statement.name.getText(source)],
131
- };
137
+ });
132
138
  }
133
139
  }
134
140
  if (ts.isInterfaceDeclaration(statement)) {
135
141
  if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
136
- exports[names[statement.name.getText(source)]] = {
142
+ const name = names[statement.name.getText(source)];
143
+ (exports[name] || (exports[name] = [])).push({
137
144
  declaration: statement,
138
145
  source,
139
146
  name: names[statement.name.getText(source)],
140
- };
147
+ });
141
148
  }
142
149
  }
143
150
  if (ts.isEnumDeclaration(statement)) {
144
151
  if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
145
- exports[names[statement.name.getText(source)]] = {
152
+ const name = names[statement.name.getText(source)];
153
+ (exports[name] || (exports[name] = [])).push({
146
154
  declaration: statement,
147
155
  source,
148
156
  name: names[statement.name.getText(source)],
149
- };
157
+ });
150
158
  }
151
159
  }
152
160
  if (ts.isImportDeclaration(statement)) {
@@ -166,11 +174,12 @@ async function findExports(source, resolveModuleFn = resolveModule) {
166
174
  if (!importExports[element.name.getText(source)]) {
167
175
  throw new Error(`Exported name ${element.name.getText(source)} not found in ${JSON.stringify(statement.moduleSpecifier.getText(source))}`);
168
176
  }
169
- exports[names[element.propertyName.getText(source)]] = {
177
+ const name = names[element.propertyName.getText(source)];
178
+ (exports[name] || (exports[name] = [])).push({
170
179
  declaration: element,
171
180
  source,
172
181
  name: names[element.propertyName.getText(source)],
173
- };
182
+ });
174
183
  }
175
184
  }
176
185
  }
@@ -237,16 +246,9 @@ function compareEntryKey([a], [b]) {
237
246
  function compareName({ name: a }, { name: b }) {
238
247
  return a.localeCompare(b);
239
248
  }
249
+ const PRIMITIVE_TYPES = new Set(["ID", "Int", "Float", "String", "Boolean"]);
240
250
  function isPrimitiveType(type) {
241
- switch (type.name) {
242
- case "ID":
243
- case "Int":
244
- case "Float":
245
- case "String":
246
- case "Boolean":
247
- return true;
248
- }
249
- return false;
251
+ return PRIMITIVE_TYPES.has(type.name);
250
252
  }
251
253
  function importAlias(line) {
252
254
  const pos = line.indexOf(" as ");
@@ -281,6 +283,9 @@ function renderImports(base, imports, aliases, extMode = "omit") {
281
283
  .map((symbol) => symbol in aliases ? jen$1.id(aliases[symbol]).as.id(symbol) : jen$1.id(symbol))).from.lit(mod);
282
284
  }));
283
285
  }
286
+ function renderAstNode(astNode) {
287
+ return jen$1.id("JSON").dot("parse").call(jen$1.literal(JSON.stringify(astNode, (key, value) => key === "loc" ? undefined : value)));
288
+ }
284
289
  function isBuiltInDirective(name) {
285
290
  // https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives
286
291
  switch (name) {
@@ -362,7 +367,7 @@ class GeneratorContext {
362
367
  _renderArgumentNodes(nodes) {
363
368
  return jen$1.obj(...Array.from(nodes ?? [], (arg) => jen$1.prop(arg.name.value, this._renderValueNode(arg.value))));
364
369
  }
365
- _renderFieldObject(type, f, inputFieldDirectives, noresolver) {
370
+ _renderFieldObject(type, f, inputFieldDirectives, astNodeExpr, noresolver) {
366
371
  let resolveFn;
367
372
  if (!noresolver) {
368
373
  let resolver = this._resolversInfo[type.name]?.[f.name];
@@ -419,12 +424,18 @@ class GeneratorContext {
419
424
  // executor implementation, but nonetheless accessible by a potential alternate implementation.
420
425
  resolveFn && jen$1.prop("resolve", jen$1.parens(resolveFn).as.any),
421
426
  subscriber && jen$1.prop("subscribe", jen$1.parens(renderSymbolReference(this._pkgs, subscriber)).as.any),
427
+ astNodeExpr && jen$1.prop("astNode", astNodeExpr),
422
428
  ]));
423
429
  }
424
- _renderFieldsThunk(type, inputFieldDirectives, noresolver) {
430
+ _renderFieldsThunk(type, inputFieldDirectives, includeAstNodes, noresolver) {
431
+ let astNodeExpr;
432
+ if (includeAstNodes) {
433
+ astNodeExpr = jen$1.id(type.name + "Type").dot("astNode").op("?").dot("fields").op("?.");
434
+ }
425
435
  return jen$1.arrow().parens(jen$1.obj(...Object.values(type.getFields())
426
- .sort(compareName)
427
- .map((field) => jen$1.prop(field.name, this._renderFieldObject(type, field, inputFieldDirectives, noresolver)))));
436
+ .map((field, i) => [field, i])
437
+ .sort(([a], [b]) => compareName(a, b))
438
+ .map(([field, i]) => jen$1.prop(field.name, this._renderFieldObject(type, field, inputFieldDirectives, astNodeExpr?.index(i), noresolver)))));
428
439
  }
429
440
  _renderOptionalGraphQLTypeType(type) {
430
441
  if (type instanceof GraphQLNonNull) {
@@ -498,13 +509,14 @@ class GeneratorContext {
498
509
  .sort(compareName)
499
510
  .map((field) => jen$1.prop(jen$1.id(field.name).op("?"), this._renderGraphQLTypeType(field.type))));
500
511
  }
501
- _renderGraphQLObjectTypeDefinition(type, { inputFieldDirectives }) {
512
+ _renderGraphQLObjectTypeDefinition(type, { inputFieldDirectives, includeAstNodes }) {
502
513
  const typeInterfaces = type.getInterfaces();
503
- return jen$1.new.add(this._gql("GraphQLObjectType")).call(jen$1.obj(jen$1.prop("name", jen$1.lit(type.name)), jen$1.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives, type.name === "Subscription")), ...truthify([
514
+ return jen$1.new.add(this._gql("GraphQLObjectType")).call(jen$1.obj(jen$1.prop("name", jen$1.lit(type.name)), jen$1.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives, includeAstNodes, type.name === "Subscription")), ...truthify([
504
515
  type.description && jen$1.prop("description", jen$1.lit(type.description)),
505
516
  typeInterfaces.length && jen$1.prop("interfaces", jen$1.arrow().arr(
506
517
  // do not sort interfaces, the declaration order may be important
507
518
  ...typeInterfaces.map(this._renderGraphQLTypeIdentifier, this))),
519
+ includeAstNodes && type.astNode && jen$1.prop("astNode", renderAstNode(type.astNode)),
508
520
  ]))).as.add(this._gql("GraphQLObjectType"));
509
521
  }
510
522
  _renderGraphQLObjectTypeTypes(type) {
@@ -543,9 +555,10 @@ class GeneratorContext {
543
555
  ],
544
556
  ];
545
557
  }
546
- _renderGraphQLInterfaceTypeDefinition(type, { implementors: { implementors }, inputFieldDirectives }) {
547
- return jen$1.new.add(this._gql("GraphQLInterfaceType")).call(jen$1.obj(jen$1.prop("name", jen$1.lit(type.name)), jen$1.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives)), ...truthify([
558
+ _renderGraphQLInterfaceTypeDefinition(type, { inputFieldDirectives, includeAstNodes }) {
559
+ return jen$1.new.add(this._gql("GraphQLInterfaceType")).call(jen$1.obj(jen$1.prop("name", jen$1.lit(type.name)), jen$1.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives, includeAstNodes)), ...truthify([
548
560
  type.description && jen$1.prop("description", jen$1.lit(type.description)),
561
+ includeAstNodes && type.astNode && jen$1.prop("astNode", renderAstNode(type.astNode)),
549
562
  ]))).as.add(this._gql("GraphQLInterfaceType"));
550
563
  }
551
564
  _renderGraphQLInterfaceTypeTypes(type, { implementors: { implementors } }) {
@@ -624,9 +637,10 @@ class GeneratorContext {
624
637
  return jen$1.prop(field.name, transformedExpr);
625
638
  }));
626
639
  }
627
- _renderGraphQLInputObjectTypeDefinition(type, { inputFieldDirectives }) {
628
- return jen$1.new.add(this._gql("GraphQLInputObjectType")).call(jen$1.obj(jen$1.prop("name", jen$1.lit(type.name)), jen$1.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives, true)), ...truthify([
640
+ _renderGraphQLInputObjectTypeDefinition(type, { inputFieldDirectives, includeAstNodes }) {
641
+ return jen$1.new.add(this._gql("GraphQLInputObjectType")).call(jen$1.obj(jen$1.prop("name", jen$1.lit(type.name)), jen$1.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives, includeAstNodes, true)), ...truthify([
629
642
  type.description && jen$1.prop("description", jen$1.lit(type.description)),
643
+ includeAstNodes && type.astNode && jen$1.prop("astNode", renderAstNode(type.astNode)),
630
644
  ])));
631
645
  }
632
646
  _renderGraphQLInputObjectTypeTypes(type, { inputFieldDirectives }) {
@@ -701,10 +715,11 @@ class GeneratorContext {
701
715
  }
702
716
  throw new Error("unhandled type: " + type.toString());
703
717
  }
704
- _renderGraphQLTypeDefinitions(type, interfaces, inputFieldDirectives) {
718
+ _renderGraphQLTypeDefinitions(type, interfaces, inputFieldDirectives, includeAstNodes) {
705
719
  return jen$1.const.add(this._renderGraphQLTypeIdentifier(type)).op("=").add(this._getRenderDefintionFnForType(type)({
706
720
  implementors: interfaces[type.name],
707
721
  inputFieldDirectives,
722
+ includeAstNodes,
708
723
  }));
709
724
  }
710
725
  _renderGraphQLTypeTypes(type, interfaces, inputFieldDirectives) {
@@ -713,11 +728,12 @@ class GeneratorContext {
713
728
  inputFieldDirectives,
714
729
  });
715
730
  }
716
- _renderGraphQLDirectiveDefinition(directive) {
731
+ _renderGraphQLDirectiveDefinition(directive, includeAstNodes) {
717
732
  return jen$1.const.add(this._renderGraphQLTypeIdentifier(directive)).op("=").new.add(this._gql("GraphQLDirective"))
718
733
  .call(jen$1.obj(jen$1.prop("name", jen$1.lit(directive.name)), jen$1.prop("locations", jen$1.array(...Array.from(directive.locations).sort().map((location) => jen$1.add(this._gql("DirectiveLocation")).dot(location)))), jen$1.prop("args", this._renderArgumentsObject(directive.args)), ...truthify([
719
734
  directive.description && jen$1.prop("description", jen$1.lit(directive.description)),
720
735
  directive.isRepeatable && jen$1.prop("isRepeatable", jen$1.true),
736
+ includeAstNodes && directive.astNode && jen$1.prop("astNode", renderAstNode(directive.astNode)),
721
737
  ])));
722
738
  }
723
739
  _renderGraphQLDirectiveTypes(directive) {
@@ -727,12 +743,12 @@ class GeneratorContext {
727
743
  ])), this._renderGraphQLTypeType(arg.type)))),
728
744
  ]);
729
745
  }
730
- schemaExprs(schema, [types, interfaces, inputFieldDirectives]) {
746
+ schemaExprs(schema, [types, interfaces, inputFieldDirectives, includeAstNodes]) {
731
747
  return [
732
748
  ...[
733
749
  // render type definitions
734
750
  ...types
735
- .map((type) => [type.name, this._renderGraphQLTypeDefinitions(type, interfaces, inputFieldDirectives)])
751
+ .map((type) => [type.name, this._renderGraphQLTypeDefinitions(type, interfaces, inputFieldDirectives, includeAstNodes)])
736
752
  .sort(compareEntryKey),
737
753
  // render directive definitions
738
754
  ...schema.getDirectives()
@@ -740,6 +756,20 @@ class GeneratorContext {
740
756
  .map((directive) => [directive.name, this._renderGraphQLDirectiveDefinition(directive)])
741
757
  .sort(compareEntryKey),
742
758
  ].map(([, expr]) => expr),
759
+ ...Array.from(PRIMITIVE_TYPES)
760
+ .flatMap((type) => {
761
+ const info = this._scalarsInfo[type];
762
+ if (!info) {
763
+ return [];
764
+ }
765
+ return [
766
+ jen$1.statements(...[
767
+ info.serializeFunc && this._gql("GraphQL" + type).dot("serialize").op("=").parens(renderSymbolReference(this._pkgs, info.serializeFunc).as.any),
768
+ info.parseValueFunc && this._gql("GraphQL" + type).dot("parseValue").op("=").parens(renderSymbolReference(this._pkgs, info.parseValueFunc).as.any),
769
+ info.parseLiteralFunc && this._gql("GraphQL" + type).dot("parseLiteral").op("=").parens(renderSymbolReference(this._pkgs, info.parseLiteralFunc).as.any),
770
+ ].filter(Boolean))
771
+ ];
772
+ }),
743
773
  // render schema
744
774
  jen$1.const.id("schema").op("=").new.add(this._gql("GraphQLSchema")).call(jen$1.obj(...(schema.getQueryType()
745
775
  ? [jen$1.prop("query", this._renderGraphQLTypeIdentifier(schema.getQueryType()))]
@@ -767,10 +797,10 @@ class GeneratorContext {
767
797
  .sort(compareEntryKey),
768
798
  ].map(([, expr]) => expr);
769
799
  }
770
- schemaAndTypesExprs(schema, [types, interfaces, inputFieldDirectives]) {
800
+ schemaAndTypesExprs(schema, args) {
771
801
  return [
772
- ...this.schemaExprs(schema, [types, interfaces, inputFieldDirectives]),
773
- ...this.typesExprs(schema, [types, interfaces, inputFieldDirectives]),
802
+ ...this.schemaExprs(schema, args),
803
+ ...this.typesExprs(schema, args),
774
804
  ];
775
805
  }
776
806
  renderFile(outfile, renderStatements) {
@@ -852,7 +882,7 @@ class GeneratorContext {
852
882
  interfaces[iface.name].implementors.push(type);
853
883
  }
854
884
  }
855
- return [types, interfaces, inputFieldDirectives];
885
+ return [types, interfaces, inputFieldDirectives, options.includeAstNodes];
856
886
  }
857
887
  }
858
888
  function createGenerator(schema, options) {
@@ -916,7 +946,7 @@ function createMatcher(pattern) {
916
946
 
917
947
  async function parseResolverFile(path) {
918
948
  return Object.fromEntries(Object.entries((await parseModule(path)).exports)
919
- .flatMap(([name, { declaration }]) => {
949
+ .flatMap(([name, decls]) => decls.flatMap(({ declaration }) => {
920
950
  if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
921
951
  if (ts.isFunctionExpression(declaration.initializer)) {
922
952
  return [[name, { module: path, symbol: name }]];
@@ -929,7 +959,7 @@ async function parseResolverFile(path) {
929
959
  return [[name, { module: path, symbol: name }]];
930
960
  }
931
961
  return [];
932
- }));
962
+ })));
933
963
  }
934
964
  async function parseTypeResolvers(typeName, glob, ignore) {
935
965
  const shouldIgnore = createMatcher(ignore);
@@ -1004,6 +1034,12 @@ function refFromTypeReferenceNode(typeNode, source) {
1004
1034
  return;
1005
1035
  }
1006
1036
  const typeName = typeNode.typeName.text;
1037
+ if (typeNode.typeArguments?.length) {
1038
+ if (typeName !== 'Promise') {
1039
+ throw new Error('TODO: handle type arguments');
1040
+ }
1041
+ return refFromTypeNode(typeNode.typeArguments[0], source);
1042
+ }
1007
1043
  for (const statement of source.statements) {
1008
1044
  if (ts.isImportDeclaration(statement)) {
1009
1045
  // search imports
@@ -1143,21 +1179,23 @@ async function parse$1(path) {
1143
1179
  const scalarFnsInfos = {};
1144
1180
  const scalarTypeNodes = {};
1145
1181
  const sources = {};
1146
- for (const [name, { declaration, source }] of Object.entries((await parseModule(path)).exports)) {
1147
- sources[name] = source;
1148
- if (ts.isVariableDeclaration(declaration)) {
1149
- if (declaration.initializer && ts.isObjectLiteralExpression(declaration.initializer)) {
1150
- scalarFnsInfos[name] = scalarInfoFromObjectLiteralExpression(name, new URL(source.fileName).toString(), declaration.initializer.properties);
1151
- }
1152
- }
1153
- else if (ts.isTypeAliasDeclaration(declaration)) {
1154
- if (declaration.typeParameters?.length) {
1155
- console.log(`WARNING: skipping scalar type ${name}: type parameters are not supported`);
1156
- continue;
1182
+ for (const [name, decls] of Object.entries((await parseModule(path)).exports)) {
1183
+ for (const { declaration, source } of decls) {
1184
+ sources[name] = source;
1185
+ if (ts.isVariableDeclaration(declaration)) {
1186
+ if (declaration.initializer && ts.isObjectLiteralExpression(declaration.initializer)) {
1187
+ scalarFnsInfos[name] = scalarInfoFromObjectLiteralExpression(name, new URL(source.fileName).toString(), declaration.initializer.properties);
1188
+ }
1157
1189
  }
1158
- const ref = refFromTypeNode(declaration.type, source);
1159
- if (ref) {
1160
- scalarTypeNodes[name] = declaration.type;
1190
+ else if (ts.isTypeAliasDeclaration(declaration)) {
1191
+ if (declaration.typeParameters?.length) {
1192
+ console.log(`WARNING: skipping scalar type ${name}: type parameters are not supported`);
1193
+ continue;
1194
+ }
1195
+ const ref = refFromTypeNode(declaration.type, source);
1196
+ if (ref) {
1197
+ scalarTypeNodes[name] = declaration.type;
1198
+ }
1161
1199
  }
1162
1200
  }
1163
1201
  }
@@ -1170,7 +1208,7 @@ async function parse$1(path) {
1170
1208
  module: source.fileName,
1171
1209
  symbol: name,
1172
1210
  } : reconcileScalarType(name, [
1173
- fnsInfo?.serializeFunc?.declaration?.parameters?.[0]?.type,
1211
+ // NOTE: omit serialize input type because it is supposed to be `unknown`
1174
1212
  fnsInfo?.parseValueFunc?.declaration?.type,
1175
1213
  fnsInfo?.parseLiteralFunc?.declaration?.type,
1176
1214
  ], source);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphql-jerky",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
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",