@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.
- package/cjs/fragment-resolver.js +1 -1
- package/cjs/index.js +27 -8
- package/cjs/resolve-document-imports.js +3 -2
- package/esm/fragment-resolver.js +1 -1
- package/esm/index.js +27 -8
- package/esm/resolve-document-imports.js +3 -2
- package/package.json +1 -1
- package/typings/fragment-resolver.d.cts +8 -5
- package/typings/fragment-resolver.d.ts +8 -5
- package/typings/resolve-document-imports.d.cts +4 -1
- package/typings/resolve-document-imports.d.ts +4 -1
package/cjs/fragment-resolver.js
CHANGED
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
|
-
//
|
|
64
|
-
|
|
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
|
-
*
|
|
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)(
|
|
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
|
-
|
|
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
|
}
|
package/esm/fragment-resolver.js
CHANGED
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
|
-
//
|
|
60
|
-
|
|
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
|
-
*
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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):
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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):
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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):
|
|
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):
|
|
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 {};
|