graphql-data-generator 0.4.2 → 0.4.3-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/codegen.js CHANGED
@@ -20,7 +20,7 @@ const getType = ({ type, ...props }) => {
20
20
  def[0].types?.some((t) => v && t.name.value === v[0].name.value) ||
21
21
  false)
22
22
  : [];
23
- const groupedValues = getSelectionsType(type.name.value, props.selections, props.definitions, props.fragments, props.references, props.includeTypenames).reduce((union, [name, value, group]) => {
23
+ const groupedValues = getSelectionsType(type.name.value, props.selections, props.definitions, props.fragments, props.references, props.includeTypenames).reduce((union, [name, value, group, originalName]) => {
24
24
  group ??= type.name.value;
25
25
  // Extract the actual type name from compound group names (e.g., "User:DelegationSubject" -> "DelegationSubject")
26
26
  const actualTypeName = group.includes(":")
@@ -42,14 +42,16 @@ const getType = ({ type, ...props }) => {
42
42
  }
43
43
  union[group][name] = value;
44
44
  const def = props.definitions[actualTypeName];
45
+ // Use originalName for tracking field usage when field is aliased
46
+ const fieldNameForTracking = originalName ?? name;
45
47
  if (def) {
46
48
  if (implementations.length) {
47
49
  for (const implementation of implementations) {
48
- implementation[1].add(name);
50
+ implementation[1].add(fieldNameForTracking);
49
51
  }
50
52
  }
51
53
  else
52
- def[1].add(name);
54
+ def[1].add(fieldNameForTracking);
53
55
  }
54
56
  return union;
55
57
  }, {});
@@ -144,6 +146,8 @@ const getSelectionsType = (name, selections, definitions, fragments, references,
144
146
  references,
145
147
  includeTypenames,
146
148
  }),
149
+ undefined, // group
150
+ selection.alias ? selection.name.value : undefined, // originalName when aliased
147
151
  ]);
148
152
  break;
149
153
  }
@@ -166,7 +170,7 @@ const getSelectionsType = (name, selections, definitions, fragments, references,
166
170
  const group = selection.typeCondition?.name.value ??
167
171
  selection.directives?.map((d) => d.name).join(",");
168
172
  selectionTypes.push(...(group
169
- ? subSelection.map(([n, t, g]) => [n, t, g ? `${group}:${g}` : group])
173
+ ? subSelection.map(([n, t, g, originalName]) => [n, t, g ? `${group}:${g}` : group, originalName])
170
174
  : subSelection));
171
175
  break;
172
176
  }
@@ -176,10 +180,11 @@ const getSelectionsType = (name, selections, definitions, fragments, references,
176
180
  throw new Error(`Could not find fragment '${selection.name.value}' in '${name}'`);
177
181
  }
178
182
  // Fragment is automatically considered used
179
- selectionTypes.push(...getSelectionsType(fragment.typeCondition.name.value, fragment.selectionSet.selections, definitions, fragments, references, includeTypenames).map(([name, type, group]) => [
183
+ selectionTypes.push(...getSelectionsType(fragment.typeCondition.name.value, fragment.selectionSet.selections, definitions, fragments, references, includeTypenames).map(([name, type, group, originalName]) => [
180
184
  name,
181
185
  type,
182
186
  group || fragment.typeCondition.name.value,
187
+ originalName,
183
188
  ]));
184
189
  break;
185
190
  }
package/esm/init.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire } from "node:module";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { Kind, parse, } from "graphql";
5
5
  import { gqlPluckFromCodeStringSync } from "@graphql-tools/graphql-tag-pluck";
6
- import { _proxy, operation, withGetDefaultPatch } from "./proxy.js";
6
+ import { _proxy, operation, withGetDefaultPatch, } from "./proxy.js";
7
7
  import { toObject } from "./util.js";
8
8
  import { dirname, resolve } from "node:path";
9
9
  globalThis.require ??= createRequire(dntShim.Deno.cwd());
@@ -69,8 +69,8 @@ const getTypeDef = (definitions, typeName) => definitions.find((def) => "name" i
69
69
  */
70
70
  export const init = (schema, queries, mutations, subscriptions, types, inputs, scalars, options) => (fn) => {
71
71
  const doc = parse(schema);
72
- // Collect all fragment definitions from operation files
73
- const fragmentDefinitions = [];
72
+ // Collect all fragment definitions from operation files, deduplicating by name
73
+ const fragmentDefinitionsMap = new Map();
74
74
  const collectFragments = (filePath, operationName) => {
75
75
  try {
76
76
  // If we have an operation name, use it; otherwise try to parse the file directly
@@ -83,18 +83,28 @@ export const init = (schema, queries, mutations, subscriptions, types, inputs, s
83
83
  const fileContent = loadFile(filePath);
84
84
  document = parse(fileContent);
85
85
  }
86
- fragmentDefinitions.push(...document.definitions.filter((def) => def.kind === Kind.FRAGMENT_DEFINITION));
86
+ for (const def of document.definitions) {
87
+ if (def.kind === Kind.FRAGMENT_DEFINITION) {
88
+ // Only add if not already seen (dedup by fragment name)
89
+ if (!fragmentDefinitionsMap.has(def.name.value)) {
90
+ fragmentDefinitionsMap.set(def.name.value, def);
91
+ }
92
+ }
93
+ }
87
94
  }
88
95
  catch {
89
96
  // Ignore files that can't be parsed
90
97
  }
91
98
  };
92
99
  // Collect fragments from all operation files (parse entire files to get all fragments)
93
- Object.values(queries).forEach(path => collectFragments(path));
94
- Object.values(mutations).forEach(path => collectFragments(path));
95
- Object.values(subscriptions).forEach(path => collectFragments(path));
96
- // Combine schema definitions with fragment definitions
97
- const allDefinitions = [...doc.definitions, ...fragmentDefinitions];
100
+ Object.values(queries).forEach((path) => collectFragments(path));
101
+ Object.values(mutations).forEach((path) => collectFragments(path));
102
+ Object.values(subscriptions).forEach((path) => collectFragments(path));
103
+ // Combine schema definitions with deduplicated fragment definitions
104
+ const allDefinitions = [
105
+ ...doc.definitions,
106
+ ...fragmentDefinitionsMap.values(),
107
+ ];
98
108
  const build = {};
99
109
  const transforms = fn(build);
100
110
  const addObjectTransforms = (type, obj) => {
package/esm/proxy.js CHANGED
@@ -161,6 +161,10 @@ const resolveConcreteType = (definitions, definition, patch, prev, selectionSet)
161
161
  if (options.length === 0) {
162
162
  throw new Error(`Could not find concrete type for ${definition.name.value}`);
163
163
  }
164
+ // Prefer a type that has a default patch defined
165
+ const typeWithDefault = options.find((o) => getDefaultPatch(o.name.value));
166
+ if (typeWithDefault)
167
+ return typeWithDefault;
164
168
  return options[0];
165
169
  };
166
170
  const resolveValue = (definitions, scalars, type, ctx) => {
@@ -563,34 +567,37 @@ export const _proxy = (definitions, scalars, path, patches, { prev, resolvedType
563
567
  const dataPatch = typeof mockPatch?.data === "function"
564
568
  ? mockPatch.data(mockPrev)
565
569
  : mockPatch?.data;
566
- for (const selection of definition.selectionSet.selections ?? []) {
567
- switch (selection.kind) {
568
- case Kind.FIELD: {
569
- const prop = selection.alias?.value ?? selection.name.value;
570
- const rawValue = dataPatch?.[prop];
571
- const value = typeof rawValue === "function"
572
- ? rawValue(mockPrev?.data)
573
- : rawValue;
574
- if (!mock.data)
575
- mock.data = {};
576
- // Query, Mutation, Subscription
577
- const operationKey = definition.operation[0].toUpperCase() +
578
- definition.operation.slice(1);
579
- if (value === clear) {
580
- mock.data[prop] = _proxy(definitions, scalars, `${operationKey}.${selection.name.value}`, [], { selectionSet: selection.selectionSet });
570
+ // Allow explicitly setting data to null (e.g., for error responses)
571
+ if (dataPatch !== null) {
572
+ for (const selection of definition.selectionSet.selections ?? []) {
573
+ switch (selection.kind) {
574
+ case Kind.FIELD: {
575
+ const prop = selection.alias?.value ?? selection.name.value;
576
+ const rawValue = dataPatch?.[prop];
577
+ const value = typeof rawValue === "function"
578
+ ? rawValue(mockPrev?.data)
579
+ : rawValue;
580
+ if (!mock.data)
581
+ mock.data = {};
582
+ // Query, Mutation, Subscription
583
+ const operationKey = definition.operation[0].toUpperCase() +
584
+ definition.operation.slice(1);
585
+ if (value === clear) {
586
+ mock.data[prop] = _proxy(definitions, scalars, `${operationKey}.${selection.name.value}`, [], { selectionSet: selection.selectionSet });
587
+ continue;
588
+ }
589
+ mock.data[prop] = _proxy(definitions, scalars, `${operationKey}.${selection.name.value}`, value !== undefined ? [value] : [], {
590
+ prev: mockPrev?.data?.[prop],
591
+ selectionSet: selection.selectionSet,
592
+ });
581
593
  continue;
582
594
  }
583
- mock.data[prop] = _proxy(definitions, scalars, `${operationKey}.${selection.name.value}`, value !== undefined ? [value] : [], {
584
- prev: mockPrev?.data?.[prop],
585
- selectionSet: selection.selectionSet,
586
- });
587
- continue;
595
+ case Kind.FRAGMENT_SPREAD:
596
+ case Kind.INLINE_FRAGMENT:
597
+ throw new Error(`Unhandled selection kind ${selection.kind}`);
598
+ default:
599
+ absurd(selection);
588
600
  }
589
- case Kind.FRAGMENT_SPREAD:
590
- case Kind.INLINE_FRAGMENT:
591
- throw new Error(`Unhandled selection kind ${selection.kind}`);
592
- default:
593
- absurd(selection);
594
601
  }
595
602
  }
596
603
  if (patch) {
@@ -691,7 +698,7 @@ export const operation = (definitions, scalars, query, ...patches) => {
691
698
  ? error
692
699
  : Object.assign(new Error(), error);
693
700
  }
694
- else if (data)
701
+ else if (data !== undefined)
695
702
  mock.result.data = data;
696
703
  if (errors)
697
704
  mock.result.errors = errors;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphql-data-generator",
3
- "version": "0.4.2",
3
+ "version": "0.4.3-alpha.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/voces/graphql-data-generator.git"
package/script/codegen.js CHANGED
@@ -26,7 +26,7 @@ const getType = ({ type, ...props }) => {
26
26
  def[0].types?.some((t) => v && t.name.value === v[0].name.value) ||
27
27
  false)
28
28
  : [];
29
- const groupedValues = getSelectionsType(type.name.value, props.selections, props.definitions, props.fragments, props.references, props.includeTypenames).reduce((union, [name, value, group]) => {
29
+ const groupedValues = getSelectionsType(type.name.value, props.selections, props.definitions, props.fragments, props.references, props.includeTypenames).reduce((union, [name, value, group, originalName]) => {
30
30
  group ??= type.name.value;
31
31
  // Extract the actual type name from compound group names (e.g., "User:DelegationSubject" -> "DelegationSubject")
32
32
  const actualTypeName = group.includes(":")
@@ -48,14 +48,16 @@ const getType = ({ type, ...props }) => {
48
48
  }
49
49
  union[group][name] = value;
50
50
  const def = props.definitions[actualTypeName];
51
+ // Use originalName for tracking field usage when field is aliased
52
+ const fieldNameForTracking = originalName ?? name;
51
53
  if (def) {
52
54
  if (implementations.length) {
53
55
  for (const implementation of implementations) {
54
- implementation[1].add(name);
56
+ implementation[1].add(fieldNameForTracking);
55
57
  }
56
58
  }
57
59
  else
58
- def[1].add(name);
60
+ def[1].add(fieldNameForTracking);
59
61
  }
60
62
  return union;
61
63
  }, {});
@@ -150,6 +152,8 @@ const getSelectionsType = (name, selections, definitions, fragments, references,
150
152
  references,
151
153
  includeTypenames,
152
154
  }),
155
+ undefined, // group
156
+ selection.alias ? selection.name.value : undefined, // originalName when aliased
153
157
  ]);
154
158
  break;
155
159
  }
@@ -172,7 +176,7 @@ const getSelectionsType = (name, selections, definitions, fragments, references,
172
176
  const group = selection.typeCondition?.name.value ??
173
177
  selection.directives?.map((d) => d.name).join(",");
174
178
  selectionTypes.push(...(group
175
- ? subSelection.map(([n, t, g]) => [n, t, g ? `${group}:${g}` : group])
179
+ ? subSelection.map(([n, t, g, originalName]) => [n, t, g ? `${group}:${g}` : group, originalName])
176
180
  : subSelection));
177
181
  break;
178
182
  }
@@ -182,10 +186,11 @@ const getSelectionsType = (name, selections, definitions, fragments, references,
182
186
  throw new Error(`Could not find fragment '${selection.name.value}' in '${name}'`);
183
187
  }
184
188
  // Fragment is automatically considered used
185
- selectionTypes.push(...getSelectionsType(fragment.typeCondition.name.value, fragment.selectionSet.selections, definitions, fragments, references, includeTypenames).map(([name, type, group]) => [
189
+ selectionTypes.push(...getSelectionsType(fragment.typeCondition.name.value, fragment.selectionSet.selections, definitions, fragments, references, includeTypenames).map(([name, type, group, originalName]) => [
186
190
  name,
187
191
  type,
188
192
  group || fragment.typeCondition.name.value,
193
+ originalName,
189
194
  ]));
190
195
  break;
191
196
  }
package/script/init.js CHANGED
@@ -95,8 +95,8 @@ const getTypeDef = (definitions, typeName) => definitions.find((def) => "name" i
95
95
  */
96
96
  const init = (schema, queries, mutations, subscriptions, types, inputs, scalars, options) => (fn) => {
97
97
  const doc = (0, graphql_1.parse)(schema);
98
- // Collect all fragment definitions from operation files
99
- const fragmentDefinitions = [];
98
+ // Collect all fragment definitions from operation files, deduplicating by name
99
+ const fragmentDefinitionsMap = new Map();
100
100
  const collectFragments = (filePath, operationName) => {
101
101
  try {
102
102
  // If we have an operation name, use it; otherwise try to parse the file directly
@@ -109,18 +109,28 @@ const init = (schema, queries, mutations, subscriptions, types, inputs, scalars,
109
109
  const fileContent = loadFile(filePath);
110
110
  document = (0, graphql_1.parse)(fileContent);
111
111
  }
112
- fragmentDefinitions.push(...document.definitions.filter((def) => def.kind === graphql_1.Kind.FRAGMENT_DEFINITION));
112
+ for (const def of document.definitions) {
113
+ if (def.kind === graphql_1.Kind.FRAGMENT_DEFINITION) {
114
+ // Only add if not already seen (dedup by fragment name)
115
+ if (!fragmentDefinitionsMap.has(def.name.value)) {
116
+ fragmentDefinitionsMap.set(def.name.value, def);
117
+ }
118
+ }
119
+ }
113
120
  }
114
121
  catch {
115
122
  // Ignore files that can't be parsed
116
123
  }
117
124
  };
118
125
  // Collect fragments from all operation files (parse entire files to get all fragments)
119
- Object.values(queries).forEach(path => collectFragments(path));
120
- Object.values(mutations).forEach(path => collectFragments(path));
121
- Object.values(subscriptions).forEach(path => collectFragments(path));
122
- // Combine schema definitions with fragment definitions
123
- const allDefinitions = [...doc.definitions, ...fragmentDefinitions];
126
+ Object.values(queries).forEach((path) => collectFragments(path));
127
+ Object.values(mutations).forEach((path) => collectFragments(path));
128
+ Object.values(subscriptions).forEach((path) => collectFragments(path));
129
+ // Combine schema definitions with deduplicated fragment definitions
130
+ const allDefinitions = [
131
+ ...doc.definitions,
132
+ ...fragmentDefinitionsMap.values(),
133
+ ];
124
134
  const build = {};
125
135
  const transforms = fn(build);
126
136
  const addObjectTransforms = (type, obj) => {
package/script/proxy.js CHANGED
@@ -165,6 +165,10 @@ const resolveConcreteType = (definitions, definition, patch, prev, selectionSet)
165
165
  if (options.length === 0) {
166
166
  throw new Error(`Could not find concrete type for ${definition.name.value}`);
167
167
  }
168
+ // Prefer a type that has a default patch defined
169
+ const typeWithDefault = options.find((o) => getDefaultPatch(o.name.value));
170
+ if (typeWithDefault)
171
+ return typeWithDefault;
168
172
  return options[0];
169
173
  };
170
174
  const resolveValue = (definitions, scalars, type, ctx) => {
@@ -567,34 +571,37 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
567
571
  const dataPatch = typeof mockPatch?.data === "function"
568
572
  ? mockPatch.data(mockPrev)
569
573
  : mockPatch?.data;
570
- for (const selection of definition.selectionSet.selections ?? []) {
571
- switch (selection.kind) {
572
- case graphql_1.Kind.FIELD: {
573
- const prop = selection.alias?.value ?? selection.name.value;
574
- const rawValue = dataPatch?.[prop];
575
- const value = typeof rawValue === "function"
576
- ? rawValue(mockPrev?.data)
577
- : rawValue;
578
- if (!mock.data)
579
- mock.data = {};
580
- // Query, Mutation, Subscription
581
- const operationKey = definition.operation[0].toUpperCase() +
582
- definition.operation.slice(1);
583
- if (value === exports.clear) {
584
- mock.data[prop] = (0, exports._proxy)(definitions, scalars, `${operationKey}.${selection.name.value}`, [], { selectionSet: selection.selectionSet });
574
+ // Allow explicitly setting data to null (e.g., for error responses)
575
+ if (dataPatch !== null) {
576
+ for (const selection of definition.selectionSet.selections ?? []) {
577
+ switch (selection.kind) {
578
+ case graphql_1.Kind.FIELD: {
579
+ const prop = selection.alias?.value ?? selection.name.value;
580
+ const rawValue = dataPatch?.[prop];
581
+ const value = typeof rawValue === "function"
582
+ ? rawValue(mockPrev?.data)
583
+ : rawValue;
584
+ if (!mock.data)
585
+ mock.data = {};
586
+ // Query, Mutation, Subscription
587
+ const operationKey = definition.operation[0].toUpperCase() +
588
+ definition.operation.slice(1);
589
+ if (value === exports.clear) {
590
+ mock.data[prop] = (0, exports._proxy)(definitions, scalars, `${operationKey}.${selection.name.value}`, [], { selectionSet: selection.selectionSet });
591
+ continue;
592
+ }
593
+ mock.data[prop] = (0, exports._proxy)(definitions, scalars, `${operationKey}.${selection.name.value}`, value !== undefined ? [value] : [], {
594
+ prev: mockPrev?.data?.[prop],
595
+ selectionSet: selection.selectionSet,
596
+ });
585
597
  continue;
586
598
  }
587
- mock.data[prop] = (0, exports._proxy)(definitions, scalars, `${operationKey}.${selection.name.value}`, value !== undefined ? [value] : [], {
588
- prev: mockPrev?.data?.[prop],
589
- selectionSet: selection.selectionSet,
590
- });
591
- continue;
599
+ case graphql_1.Kind.FRAGMENT_SPREAD:
600
+ case graphql_1.Kind.INLINE_FRAGMENT:
601
+ throw new Error(`Unhandled selection kind ${selection.kind}`);
602
+ default:
603
+ (0, util_js_1.absurd)(selection);
592
604
  }
593
- case graphql_1.Kind.FRAGMENT_SPREAD:
594
- case graphql_1.Kind.INLINE_FRAGMENT:
595
- throw new Error(`Unhandled selection kind ${selection.kind}`);
596
- default:
597
- (0, util_js_1.absurd)(selection);
598
605
  }
599
606
  }
600
607
  if (patch) {
@@ -697,7 +704,7 @@ const operation = (definitions, scalars, query, ...patches) => {
697
704
  ? error
698
705
  : Object.assign(new Error(), error);
699
706
  }
700
- else if (data)
707
+ else if (data !== undefined)
701
708
  mock.result.data = data;
702
709
  if (errors)
703
710
  mock.result.errors = errors;
package/types/proxy.d.ts CHANGED
@@ -22,7 +22,8 @@ export declare const _proxy: <T>(definitions: readonly DefinitionNode[], scalars
22
22
  nonNull?: boolean;
23
23
  }) => T;
24
24
  export declare const proxy: <T>(definitions: readonly DefinitionNode[], scalars: Record<string, unknown | ((typename: string) => unknown)>, type: string, ...patches: (Patch<T> | ((prev: T) => Patch<T>))[]) => T;
25
- export declare const operation: <O extends SimpleOperationMock, Extra = object>(definitions: readonly DefinitionNode[], scalars: Record<string, unknown | ((typename: string) => unknown)>, query: string | DocumentNode, ...patches: (Patch<Omit<O, "error" | "errors">> & {
25
+ export declare const operation: <O extends SimpleOperationMock, Extra = object>(definitions: readonly DefinitionNode[], scalars: Record<string, unknown | ((typename: string) => unknown)>, query: string | DocumentNode, ...patches: (Patch<Omit<O, "error" | "errors" | "data">> & {
26
+ data?: Patch<O["data"]> | null;
26
27
  error?: Error;
27
28
  errors?: GraphQLError[];
28
29
  } & Partial<Extra>)[]) => OperationMock<O["data"], O["variables"]> & Partial<Extra>;
package/types/types.d.ts CHANGED
@@ -23,7 +23,7 @@ export type OperationMock<Data extends Record<string, unknown> = Record<string,
23
23
  variables?: Variables;
24
24
  };
25
25
  result: {
26
- data?: Data;
26
+ data?: Data | null;
27
27
  errors?: ReadonlyArray<GraphQLError>;
28
28
  };
29
29
  error?: Error;