@nmarks/graphql-codegen-per-operation-file-preset 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.
@@ -145,5 +145,5 @@ function buildFragmentResolver(collectorOptions, presetOptions, schemaObject, de
145
145
  })),
146
146
  };
147
147
  }
148
- return resolveFragments;
148
+ return { resolveFragments, fragmentRegistry };
149
149
  }
package/cjs/index.js CHANGED
@@ -55,13 +55,19 @@ function createLoadedFragments(fragments) {
55
55
  * After splitting, these fragments will be in separate files, so we need to generate imports.
56
56
  *
57
57
  * Example: query references RetailerServicesData → import from './RetailerServicesData.ts'
58
+ *
59
+ * NOTE: We use the fragment registry to get the proper import identifiers (with correct naming/suffix)
60
+ * instead of hardcoding them, since naming depends on config (dedupeOperationSuffix, etc.)
58
61
  */
59
- function createLocalFragmentImports(localFragments, sourceLocation, currentFilename, folder, extension, options) {
62
+ function createLocalFragmentImports(localFragments, fragmentRegistry, sourceLocation, currentFilename, folder, extension, options) {
60
63
  return localFragments.map((frag) => {
61
64
  var _a;
62
65
  const fragmentFilePath = (0, utils_js_1.generateOperationFilePath)(sourceLocation, frag.name.value, folder, extension);
63
- // Generate import identifiers: both the document (for runtime) and type (for TypeScript)
64
- const identifiers = [
66
+ // Get the proper import identifiers from the fragment registry
67
+ // (calculated by fragment-resolver using BaseVisitor with correct naming conventions)
68
+ const registryEntry = fragmentRegistry[frag.name.value];
69
+ const identifiers = (registryEntry === null || registryEntry === void 0 ? void 0 : registryEntry.imports) || [
70
+ // Fallback if not in registry (shouldn't happen, but be safe)
65
71
  {
66
72
  name: `${frag.name.value}FragmentDoc`,
67
73
  kind: 'document',
@@ -97,7 +103,8 @@ function createLocalFragmentImports(localFragments, sourceLocation, currentFilen
97
103
  * We ONLY need the Types import when generated code actually references the Types namespace:
98
104
  *
99
105
  * ✅ NEEDS IMPORT:
100
- * - Operations (query/mutation/subscription) - generate `Types.Exact<{...}>` for Variables type
106
+ * - Operations (query/mutation/subscription) - ALWAYS generate `Types.Exact<{...}>` for Variables type
107
+ * (even operations without variables: `Types.Exact<{ [key: string]: never; }>`)
101
108
  * - Enums - generate `role: Types.Role`
102
109
  * - Custom scalars (unless mapped to primitives) - generate `date: Types.DateTime`
103
110
  * - Input types - used in variables as `Types.MyInput`
@@ -116,8 +123,11 @@ function needsSchemaTypesImport(document, schema, config) {
116
123
  const scalarMappings = config.scalars || {};
117
124
  /**
118
125
  * STEP 1: Check for operations (queries/mutations/subscriptions)
119
- * All operations generate a Variables type that uses Types.Exact, even without variables:
126
+ * ALL operations generate a Variables type that uses Types.Exact, even without variables:
120
127
  * `export type MyQueryVariables = Types.Exact<{ [key: string]: never; }>;`
128
+ *
129
+ * This is true for typescript-operations plugin regardless of whether the operation
130
+ * has any variable definitions or not.
121
131
  */
122
132
  let hasOperation = false;
123
133
  const referencedTypeNames = new Set();
@@ -133,6 +143,7 @@ function needsSchemaTypesImport(document, schema, config) {
133
143
  referencedTypeNames.add(node.typeCondition.name.value);
134
144
  },
135
145
  });
146
+ // All operations need Types import for Variables type
136
147
  if (hasOperation) {
137
148
  return true;
138
149
  }
@@ -195,9 +206,17 @@ function needsSchemaTypesImport(document, schema, config) {
195
206
  * STEP 4: Check field return types
196
207
  * Use TypeInfo to properly resolve field types through the schema.
197
208
  * This catches cases like: user { role } where 'role' returns an enum.
209
+ *
210
+ * IMPORTANT: Only check the FIRST definition (the actual operation/fragment being generated).
211
+ * Don't check external fragments - those will have their own imports.
198
212
  */
213
+ // Create a document with ONLY the first definition for type checking
214
+ const singleDefOnly = {
215
+ kind: graphql_1.Kind.DOCUMENT,
216
+ definitions: [document.definitions[0]],
217
+ };
199
218
  const typeInfo = new graphql_1.TypeInfo(schema);
200
- (0, graphql_1.visit)(document, (0, graphql_1.visitWithTypeInfo)(typeInfo, {
219
+ (0, graphql_1.visit)(singleDefOnly, (0, graphql_1.visitWithTypeInfo)(typeInfo, {
201
220
  Field: () => {
202
221
  const fieldType = typeInfo.getType();
203
222
  if (checkType(fieldType)) {
@@ -261,7 +280,7 @@ exports.preset = {
261
280
  * This uses our adapted fragment-resolver that generates paths based on fragment NAMES
262
281
  * (not source file names), enabling proper imports after splitting.
263
282
  */
264
- const sources = (0, resolve_document_imports_js_1.resolveDocumentImports)(options, schemaObject, {
283
+ const { results: sources, fragmentRegistry } = (0, resolve_document_imports_js_1.resolveDocumentImports)(options, schemaObject, {
265
284
  baseDir,
266
285
  folder,
267
286
  extension,
@@ -326,7 +345,7 @@ exports.preset = {
326
345
  ...fragmentImport,
327
346
  outputPath: filename,
328
347
  }));
329
- const localFragmentImports = createLocalFragmentImports(localFragments, source.documents[0].location, filename, folder, extension, {
348
+ const localFragmentImports = createLocalFragmentImports(localFragments, fragmentRegistry, source.documents[0].location, filename, folder, extension, {
330
349
  baseDir,
331
350
  baseOutputDir: options.baseOutputDir,
332
351
  emitLegacyCommonJSImports: options.config.emitLegacyCommonJSImports,
@@ -12,10 +12,10 @@ const fragment_resolver_js_1 = tslib_1.__importDefault(require("./fragment-resol
12
12
  * Does not define specific plugins, but rather returns a string[] of `importStatements` for the calling plugin to make use of
13
13
  */
14
14
  function resolveDocumentImports(presetOptions, schemaObject, importResolverOptions, dedupeFragments = false) {
15
- const resolveFragments = (0, fragment_resolver_js_1.default)(importResolverOptions, presetOptions, schemaObject, dedupeFragments);
15
+ const { resolveFragments, fragmentRegistry } = (0, fragment_resolver_js_1.default)(importResolverOptions, presetOptions, schemaObject, dedupeFragments);
16
16
  const { baseOutputDir, documents } = presetOptions;
17
17
  const { schemaTypesSource, baseDir, typesImport } = importResolverOptions;
18
- return documents.map(documentFile => {
18
+ const results = documents.map(documentFile => {
19
19
  try {
20
20
  // NOTE: We pass a placeholder filename here since we'll generate proper filenames per-operation later
21
21
  // The important part is that fragment resolution will use the correct paths from the registry
@@ -55,4 +55,5 @@ function resolveDocumentImports(presetOptions, schemaObject, importResolverOptio
55
55
  ${e.message || e.toString()}`);
56
56
  }
57
57
  });
58
+ return { results, fragmentRegistry };
58
59
  }
@@ -142,5 +142,5 @@ export default function buildFragmentResolver(collectorOptions, presetOptions, s
142
142
  })),
143
143
  };
144
144
  }
145
- return resolveFragments;
145
+ return { resolveFragments, fragmentRegistry };
146
146
  }
package/esm/index.js CHANGED
@@ -51,13 +51,19 @@ function createLoadedFragments(fragments) {
51
51
  * After splitting, these fragments will be in separate files, so we need to generate imports.
52
52
  *
53
53
  * Example: query references RetailerServicesData → import from './RetailerServicesData.ts'
54
+ *
55
+ * NOTE: We use the fragment registry to get the proper import identifiers (with correct naming/suffix)
56
+ * instead of hardcoding them, since naming depends on config (dedupeOperationSuffix, etc.)
54
57
  */
55
- function createLocalFragmentImports(localFragments, sourceLocation, currentFilename, folder, extension, options) {
58
+ function createLocalFragmentImports(localFragments, fragmentRegistry, sourceLocation, currentFilename, folder, extension, options) {
56
59
  return localFragments.map((frag) => {
57
60
  var _a;
58
61
  const fragmentFilePath = generateOperationFilePath(sourceLocation, frag.name.value, folder, extension);
59
- // Generate import identifiers: both the document (for runtime) and type (for TypeScript)
60
- const identifiers = [
62
+ // Get the proper import identifiers from the fragment registry
63
+ // (calculated by fragment-resolver using BaseVisitor with correct naming conventions)
64
+ const registryEntry = fragmentRegistry[frag.name.value];
65
+ const identifiers = (registryEntry === null || registryEntry === void 0 ? void 0 : registryEntry.imports) || [
66
+ // Fallback if not in registry (shouldn't happen, but be safe)
61
67
  {
62
68
  name: `${frag.name.value}FragmentDoc`,
63
69
  kind: 'document',
@@ -93,7 +99,8 @@ function createLocalFragmentImports(localFragments, sourceLocation, currentFilen
93
99
  * We ONLY need the Types import when generated code actually references the Types namespace:
94
100
  *
95
101
  * ✅ NEEDS IMPORT:
96
- * - Operations (query/mutation/subscription) - generate `Types.Exact<{...}>` for Variables type
102
+ * - Operations (query/mutation/subscription) - ALWAYS generate `Types.Exact<{...}>` for Variables type
103
+ * (even operations without variables: `Types.Exact<{ [key: string]: never; }>`)
97
104
  * - Enums - generate `role: Types.Role`
98
105
  * - Custom scalars (unless mapped to primitives) - generate `date: Types.DateTime`
99
106
  * - Input types - used in variables as `Types.MyInput`
@@ -112,8 +119,11 @@ function needsSchemaTypesImport(document, schema, config) {
112
119
  const scalarMappings = config.scalars || {};
113
120
  /**
114
121
  * STEP 1: Check for operations (queries/mutations/subscriptions)
115
- * All operations generate a Variables type that uses Types.Exact, even without variables:
122
+ * ALL operations generate a Variables type that uses Types.Exact, even without variables:
116
123
  * `export type MyQueryVariables = Types.Exact<{ [key: string]: never; }>;`
124
+ *
125
+ * This is true for typescript-operations plugin regardless of whether the operation
126
+ * has any variable definitions or not.
117
127
  */
118
128
  let hasOperation = false;
119
129
  const referencedTypeNames = new Set();
@@ -129,6 +139,7 @@ function needsSchemaTypesImport(document, schema, config) {
129
139
  referencedTypeNames.add(node.typeCondition.name.value);
130
140
  },
131
141
  });
142
+ // All operations need Types import for Variables type
132
143
  if (hasOperation) {
133
144
  return true;
134
145
  }
@@ -191,9 +202,17 @@ function needsSchemaTypesImport(document, schema, config) {
191
202
  * STEP 4: Check field return types
192
203
  * Use TypeInfo to properly resolve field types through the schema.
193
204
  * This catches cases like: user { role } where 'role' returns an enum.
205
+ *
206
+ * IMPORTANT: Only check the FIRST definition (the actual operation/fragment being generated).
207
+ * Don't check external fragments - those will have their own imports.
194
208
  */
209
+ // Create a document with ONLY the first definition for type checking
210
+ const singleDefOnly = {
211
+ kind: Kind.DOCUMENT,
212
+ definitions: [document.definitions[0]],
213
+ };
195
214
  const typeInfo = new TypeInfo(schema);
196
- visit(document, visitWithTypeInfo(typeInfo, {
215
+ visit(singleDefOnly, visitWithTypeInfo(typeInfo, {
197
216
  Field: () => {
198
217
  const fieldType = typeInfo.getType();
199
218
  if (checkType(fieldType)) {
@@ -257,7 +276,7 @@ export const preset = {
257
276
  * This uses our adapted fragment-resolver that generates paths based on fragment NAMES
258
277
  * (not source file names), enabling proper imports after splitting.
259
278
  */
260
- const sources = resolveDocumentImports(options, schemaObject, {
279
+ const { results: sources, fragmentRegistry } = resolveDocumentImports(options, schemaObject, {
261
280
  baseDir,
262
281
  folder,
263
282
  extension,
@@ -322,7 +341,7 @@ export const preset = {
322
341
  ...fragmentImport,
323
342
  outputPath: filename,
324
343
  }));
325
- const localFragmentImports = createLocalFragmentImports(localFragments, source.documents[0].location, filename, folder, extension, {
344
+ const localFragmentImports = createLocalFragmentImports(localFragments, fragmentRegistry, source.documents[0].location, filename, folder, extension, {
326
345
  baseDir,
327
346
  baseOutputDir: options.baseOutputDir,
328
347
  emitLegacyCommonJSImports: options.config.emitLegacyCommonJSImports,
@@ -8,10 +8,10 @@ import buildFragmentResolver from './fragment-resolver.js';
8
8
  * Does not define specific plugins, but rather returns a string[] of `importStatements` for the calling plugin to make use of
9
9
  */
10
10
  export function resolveDocumentImports(presetOptions, schemaObject, importResolverOptions, dedupeFragments = false) {
11
- const resolveFragments = buildFragmentResolver(importResolverOptions, presetOptions, schemaObject, dedupeFragments);
11
+ const { resolveFragments, fragmentRegistry } = buildFragmentResolver(importResolverOptions, presetOptions, schemaObject, dedupeFragments);
12
12
  const { baseOutputDir, documents } = presetOptions;
13
13
  const { schemaTypesSource, baseDir, typesImport } = importResolverOptions;
14
- return documents.map(documentFile => {
14
+ const results = documents.map(documentFile => {
15
15
  try {
16
16
  // NOTE: We pass a placeholder filename here since we'll generate proper filenames per-operation later
17
17
  // The important part is that fragment resolution will use the correct paths from the registry
@@ -51,4 +51,5 @@ export function resolveDocumentImports(presetOptions, schemaObject, importResolv
51
51
  ${e.message || e.toString()}`);
52
52
  }
53
53
  });
54
+ return { results, fragmentRegistry };
54
55
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nmarks/graphql-codegen-per-operation-file-preset",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "GraphQL Code Generator preset for generating one file per operation/fragment",
5
5
  "peerDependencies": {
6
6
  "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
@@ -21,9 +21,12 @@ export type FragmentRegistry = {
21
21
  /**
22
22
  * Builds a fragment "resolver" that collects `externalFragments` definitions and `fragmentImportStatements`
23
23
  */
24
- export default function buildFragmentResolver<T>(collectorOptions: DocumentImportResolverOptions, presetOptions: Types.PresetFnArgs<T>, schemaObject: GraphQLSchema, dedupeFragments?: boolean): (generatedFilePath: string, documentFileContent: DocumentNode) => {
25
- externalFragments: LoadedFragment<{
26
- level: number;
27
- }>[];
28
- fragmentImports: ImportDeclaration<FragmentImport>[];
24
+ export default function buildFragmentResolver<T>(collectorOptions: DocumentImportResolverOptions, presetOptions: Types.PresetFnArgs<T>, schemaObject: GraphQLSchema, dedupeFragments?: boolean): {
25
+ resolveFragments: (generatedFilePath: string, documentFileContent: DocumentNode) => {
26
+ externalFragments: LoadedFragment<{
27
+ level: number;
28
+ }>[];
29
+ fragmentImports: ImportDeclaration<FragmentImport>[];
30
+ };
31
+ fragmentRegistry: FragmentRegistry;
29
32
  };
@@ -21,9 +21,12 @@ export type FragmentRegistry = {
21
21
  /**
22
22
  * Builds a fragment "resolver" that collects `externalFragments` definitions and `fragmentImportStatements`
23
23
  */
24
- export default function buildFragmentResolver<T>(collectorOptions: DocumentImportResolverOptions, presetOptions: Types.PresetFnArgs<T>, schemaObject: GraphQLSchema, dedupeFragments?: boolean): (generatedFilePath: string, documentFileContent: DocumentNode) => {
25
- externalFragments: LoadedFragment<{
26
- level: number;
27
- }>[];
28
- fragmentImports: ImportDeclaration<FragmentImport>[];
24
+ export default function buildFragmentResolver<T>(collectorOptions: DocumentImportResolverOptions, presetOptions: Types.PresetFnArgs<T>, schemaObject: GraphQLSchema, dedupeFragments?: boolean): {
25
+ resolveFragments: (generatedFilePath: string, documentFileContent: DocumentNode) => {
26
+ externalFragments: LoadedFragment<{
27
+ level: number;
28
+ }>[];
29
+ fragmentImports: ImportDeclaration<FragmentImport>[];
30
+ };
31
+ fragmentRegistry: FragmentRegistry;
29
32
  };
@@ -44,5 +44,8 @@ interface ResolveDocumentImportResult {
44
44
  * Resolves user provided imports and fragment imports using the `DocumentImportResolverOptions`.
45
45
  * Does not define specific plugins, but rather returns a string[] of `importStatements` for the calling plugin to make use of
46
46
  */
47
- export declare function resolveDocumentImports<T>(presetOptions: Types.PresetFnArgs<T>, schemaObject: GraphQLSchema, importResolverOptions: DocumentImportResolverOptions, dedupeFragments?: boolean): Array<ResolveDocumentImportResult>;
47
+ export declare function resolveDocumentImports<T>(presetOptions: Types.PresetFnArgs<T>, schemaObject: GraphQLSchema, importResolverOptions: DocumentImportResolverOptions, dedupeFragments?: boolean): {
48
+ results: ResolveDocumentImportResult[];
49
+ fragmentRegistry: any;
50
+ };
48
51
  export {};
@@ -44,5 +44,8 @@ interface ResolveDocumentImportResult {
44
44
  * Resolves user provided imports and fragment imports using the `DocumentImportResolverOptions`.
45
45
  * Does not define specific plugins, but rather returns a string[] of `importStatements` for the calling plugin to make use of
46
46
  */
47
- export declare function resolveDocumentImports<T>(presetOptions: Types.PresetFnArgs<T>, schemaObject: GraphQLSchema, importResolverOptions: DocumentImportResolverOptions, dedupeFragments?: boolean): Array<ResolveDocumentImportResult>;
47
+ export declare function resolveDocumentImports<T>(presetOptions: Types.PresetFnArgs<T>, schemaObject: GraphQLSchema, importResolverOptions: DocumentImportResolverOptions, dedupeFragments?: boolean): {
48
+ results: ResolveDocumentImportResult[];
49
+ fragmentRegistry: any;
50
+ };
48
51
  export {};