UNPKG

@graphql-codegen/near-operation-file-preset

Version:

GraphQL Code Generator preset for generating operation code near the operation file

151 lines (150 loc) • 7.11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = buildFragmentResolver; const graphql_1 = require("graphql"); const visitor_plugin_common_1 = require("@graphql-codegen/visitor-plugin-common"); const utils_js_1 = require("./utils.js"); /** * Creates a fragment's type imports based on possible types and usage */ function createFragmentTypeImports(baseVisitor, fragmentName, possibleTypes, usedTypes) { const fragmentSuffix = baseVisitor.getFragmentSuffix(fragmentName); if (possibleTypes.length === 1) { return [ { name: baseVisitor.convertName(fragmentName, { useTypesPrefix: true, suffix: fragmentSuffix, }), kind: 'type', }, ]; } if (possibleTypes.length > 0) { const typesToImport = usedTypes && usedTypes.length > 0 ? usedTypes : possibleTypes; return typesToImport.map(typeName => ({ name: baseVisitor.convertName(fragmentName, { useTypesPrefix: true, suffix: `_${typeName}` + (fragmentSuffix.length > 0 ? `_${fragmentSuffix}` : ''), }), kind: 'type', })); } return []; } /** * Used by `buildFragmentResolver` to build a mapping of fragmentNames to paths, nodes, and other useful info */ function buildFragmentRegistry({ generateFilePath }, { documents }, schemaObject) { const duplicateFragmentNames = []; const registry = documents.reduce((prev, documentRecord) => { const fragments = documentRecord.document.definitions.filter(d => d.kind === graphql_1.Kind.FRAGMENT_DEFINITION); for (const fragment of fragments) { const schemaType = schemaObject.getType(fragment.typeCondition.name.value); if (!schemaType) { throw new Error(`Fragment "${fragment.name.value}" is set on non-existing type "${fragment.typeCondition.name.value}"!`); } const filePath = generateFilePath({ location: documentRecord.location, meta: { operations: [], fragments: [fragment] }, }); const fragmentName = fragment.name.value; const possibleTypes = (0, visitor_plugin_common_1.getPossibleTypes)(schemaObject, schemaType); const possibleTypeNames = possibleTypes.map(t => t.name); if (prev[fragmentName] && (0, graphql_1.print)(fragment) !== (0, graphql_1.print)(prev[fragmentName].node)) { duplicateFragmentNames.push(fragmentName); } prev[fragmentName] = { filePath, onType: fragment.typeCondition.name.value, node: fragment, possibleTypes: possibleTypeNames, }; } return prev; }, {}); if (duplicateFragmentNames.length) { throw new Error(`Multiple fragments with the name(s) "${duplicateFragmentNames.join(', ')}" were found.`); } return registry; } /** * Creates a BaseVisitor with standard configuration */ function createBaseVisitor(config, schemaObject) { return new visitor_plugin_common_1.BaseVisitor(config, { scalars: (0, visitor_plugin_common_1.buildScalarsFromConfig)(schemaObject, config), dedupeOperationSuffix: (0, visitor_plugin_common_1.getConfigValue)(config.dedupeOperationSuffix, false), omitOperationSuffix: (0, visitor_plugin_common_1.getConfigValue)(config.omitOperationSuffix, false), fragmentVariablePrefix: (0, visitor_plugin_common_1.getConfigValue)(config.fragmentVariablePrefix, ''), fragmentVariableSuffix: (0, visitor_plugin_common_1.getConfigValue)(config.fragmentVariableSuffix, 'FragmentDoc'), }); } /** * Builds a fragment "resolver" that collects `externalFragments` definitions and `fragmentImportStatements` */ function buildFragmentResolver(collectorOptions, presetOptions, schemaObject, dedupeFragments = false) { const { config } = presetOptions; const baseVisitor = createBaseVisitor(config, schemaObject); const fragmentRegistry = buildFragmentRegistry(collectorOptions, presetOptions, schemaObject); const { baseOutputDir } = presetOptions; const { baseDir, typesImport } = collectorOptions; function resolveFragments(generatedFilePath, documentFileContent) { var _a; var _b; const { fragmentsInUse, usedFragmentTypes, operationFragments } = (0, utils_js_1.analyzeFragmentUsage)(documentFileContent, fragmentRegistry, schemaObject); // graphQLTag operations inline every fragment they transitively spread const { documentMode = visitor_plugin_common_1.DocumentMode.graphQLTag } = config; const isGraphQLTagMode = documentMode === visitor_plugin_common_1.DocumentMode.graphQLTag; const externalFragments = []; const fragmentFileImports = {}; for (const [fragmentName, level] of Object.entries(fragmentsInUse)) { const fragmentDetails = fragmentRegistry[fragmentName]; if (!fragmentDetails) continue; // don't emit imports to the same location if (fragmentDetails.filePath !== generatedFilePath) { const imports = []; const isSpreadDirectly = level === 0 || dedupeFragments; const needsDocument = isGraphQLTagMode ? operationFragments.has(fragmentName) : isSpreadDirectly; if (needsDocument) { imports.push({ name: baseVisitor.getFragmentVariableName(fragmentName), kind: 'document', }); } if (isSpreadDirectly) { imports.push(...createFragmentTypeImports(baseVisitor, fragmentName, fragmentDetails.possibleTypes, usedFragmentTypes[fragmentName] || [])); } if (imports.length > 0) { ((_a = fragmentFileImports[_b = fragmentDetails.filePath]) !== null && _a !== void 0 ? _a : (fragmentFileImports[_b] = [])).push(...imports); } } externalFragments.push({ level, isExternal: true, name: fragmentName, onType: fragmentDetails.onType, node: fragmentDetails.node, }); } return { externalFragments, fragmentImports: Object.entries(fragmentFileImports).map(([fragmentsFilePath, identifiers]) => ({ baseDir, baseOutputDir, outputPath: generatedFilePath, importSource: { path: fragmentsFilePath, identifiers, }, emitLegacyCommonJSImports: presetOptions.config.emitLegacyCommonJSImports, importExtension: presetOptions.config.importExtension, typesImport, })), }; } return resolveFragments; }