UNPKG

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

Version:

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

145 lines (144 loc) 6.93 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 fragment imports based on possible types and usage */ function createFragmentImports(baseVisitor, fragmentName, possibleTypes, usedTypes) { const fragmentImports = []; // Always include the document import fragmentImports.push({ name: baseVisitor.getFragmentVariableName(fragmentName), kind: 'document', }); const fragmentSuffix = baseVisitor.getFragmentSuffix(fragmentName); if (possibleTypes.length === 1) { fragmentImports.push({ name: baseVisitor.convertName(fragmentName, { useTypesPrefix: true, suffix: fragmentSuffix, }), kind: 'type', }); } else if (possibleTypes.length > 0) { const typesToImport = usedTypes && usedTypes.length > 0 ? usedTypes : possibleTypes; typesToImport.forEach(typeName => { fragmentImports.push({ name: baseVisitor.convertName(fragmentName, { useTypesPrefix: true, suffix: `_${typeName}` + (fragmentSuffix.length > 0 ? `_${fragmentSuffix}` : ''), }), kind: 'type', }); }); } return fragmentImports; } /** * Used by `buildFragmentResolver` to build a mapping of fragmentNames to paths, importNames, and other useful info */ function buildFragmentRegistry(baseVisitor, { 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 fragmentName = fragment.name.value; const filePath = generateFilePath(documentRecord.location); const possibleTypes = (0, visitor_plugin_common_1.getPossibleTypes)(schemaObject, schemaType); const possibleTypeNames = possibleTypes.map(t => t.name); const imports = createFragmentImports(baseVisitor, fragment.name.value, possibleTypeNames); if (prev[fragmentName] && (0, graphql_1.print)(fragment) !== (0, graphql_1.print)(prev[fragmentName].node)) { duplicateFragmentNames.push(fragmentName); } prev[fragmentName] = { filePath, imports, 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(baseVisitor, collectorOptions, presetOptions, schemaObject); const { baseOutputDir } = presetOptions; const { baseDir, typesImport } = collectorOptions; function resolveFragments(generatedFilePath, documentFileContent) { const { fragmentsInUse, usedFragmentTypes } = (0, utils_js_1.analyzeFragmentUsage)(documentFileContent, fragmentRegistry, schemaObject); const externalFragments = []; const fragmentFileImports = {}; for (const [fragmentName, level] of Object.entries(fragmentsInUse)) { const fragmentDetails = fragmentRegistry[fragmentName]; if (!fragmentDetails) continue; // add top level references to the import object // we don't check or global namespace because the calling config can do so if (level === 0 || (dedupeFragments && ['OperationDefinition', 'FragmentDefinition'].includes(documentFileContent.definitions[0].kind))) { if (fragmentDetails.filePath !== generatedFilePath) { // don't emit imports to same location const usedTypesForFragment = usedFragmentTypes[fragmentName] || []; const filteredImports = createFragmentImports(baseVisitor, fragmentName, fragmentDetails.possibleTypes, usedTypesForFragment); if (!fragmentFileImports[fragmentDetails.filePath]) { fragmentFileImports[fragmentDetails.filePath] = []; } fragmentFileImports[fragmentDetails.filePath].push(...filteredImports); } } 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, typesImport, })), }; } return resolveFragments; }