UNPKG

apollo-codegen-core

Version:
306 lines 13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.compileToIR = exports.stripProp = void 0; const graphql_1 = require("graphql"); const graphql_2 = require("../utilities/graphql"); const apollo_language_server_1 = require("apollo-language-server"); function stripProp(propName, obj) { let cloned = JSON.parse(JSON.stringify(obj)); for (let prop in cloned) { if (prop === propName) delete cloned[prop]; else if (typeof cloned[prop] === "object") { cloned[prop] = stripProp(propName, cloned[prop]); } } return cloned; } exports.stripProp = stripProp; function compileToIR(schema, document, options = { exposeTypeNodes: true, }) { if (options.addTypename) { document = (0, apollo_language_server_1.withTypenameFieldAddedWhereNeeded)(document); } const compiler = new Compiler(schema, options); const operations = Object.create(null); const fragments = Object.create(null); for (const definition of document.definitions) { switch (definition.kind) { case graphql_1.Kind.OPERATION_DEFINITION: const operation = compiler.compileOperation(definition); operations[operation.operationName] = operation; break; case graphql_1.Kind.FRAGMENT_DEFINITION: const fragment = compiler.compileFragment(definition); fragments[fragment.fragmentName] = fragment; break; } } for (const fragmentSpread of compiler.unresolvedFragmentSpreads) { const fragment = fragments[fragmentSpread.fragmentName]; if (!fragment) { throw new Error(`Cannot find fragment "${fragmentSpread.fragmentName}"`); } const possibleTypes = fragment.selectionSet.possibleTypes.filter((type) => fragmentSpread.selectionSet.possibleTypes.includes(type)); fragmentSpread.isConditional = fragment.selectionSet.possibleTypes.some((type) => !fragmentSpread.selectionSet.possibleTypes.includes(type)); fragmentSpread.selectionSet = { possibleTypes, selections: fragment.selectionSet.selections, }; } const typesUsed = compiler.typesUsed; const unionTypes = compiler.unionTypes; const interfaceTypes = compiler.interfaceTypes; return { schema, typesUsed, operations, fragments, options, unionTypes, interfaceTypes, }; } exports.compileToIR = compileToIR; class Compiler { constructor(schema, options) { this.unresolvedFragmentSpreads = []; this.schema = schema; this.options = options; this.typesUsedSet = new Set(); this.unionTypesSet = new Set(); this.interfaceTypesMap = new Map(); } addTypeUsed(type) { if (this.typesUsedSet.has(type)) return; if ((0, graphql_1.isEnumType)(type) || (0, graphql_1.isInputObjectType)(type) || ((0, graphql_1.isScalarType)(type) && !(0, graphql_1.isSpecifiedScalarType)(type))) { this.typesUsedSet.add(type); } if ((0, graphql_1.isInputObjectType)(type)) { for (const field of Object.values(type.getFields())) { this.addTypeUsed((0, graphql_1.getNamedType)(field.type)); } } } get typesUsed() { return Array.from(this.typesUsedSet); } addUnionType(type) { if ((0, graphql_1.isUnionType)(type)) { if (this.unionTypesSet.has(type)) return; this.unionTypesSet.add(type); } } get unionTypes() { return Array.from(this.unionTypesSet); } addInterfaceType(type) { if ((0, graphql_1.isInterfaceType)(type)) { if (this.interfaceTypesMap.has(type)) return; this.interfaceTypesMap.set(type, this.possibleTypesForType(type)); } } get interfaceTypes() { return this.interfaceTypesMap; } compileOperation(operationDefinition) { if (!operationDefinition.name) { throw new Error("Operations should be named"); } const filePath = (0, graphql_2.filePathForNode)(operationDefinition); const operationName = operationDefinition.name.value; const operationType = operationDefinition.operation; const variables = (operationDefinition.variableDefinitions || []).map((node) => { const name = node.variable.name.value; const type = (0, graphql_1.typeFromAST)(this.schema, node.type); this.addTypeUsed((0, graphql_1.getNamedType)(type)); const typeNode = this.options.exposeTypeNodes && type ? stripProp("loc", (0, graphql_1.parseType)(type.toString())) : undefined; return { name, type: type, typeNode, }; }); const source = (0, graphql_1.print)(operationDefinition); const rootType = (0, graphql_2.getOperationRootType)(this.schema, operationDefinition); return { filePath, operationName, operationType, variables, source, rootType, selectionSet: this.compileSelectionSet(operationDefinition.selectionSet, rootType), }; } compileFragment(fragmentDefinition) { const fragmentName = fragmentDefinition.name.value; const filePath = (0, graphql_2.filePathForNode)(fragmentDefinition); const source = (0, graphql_1.print)(fragmentDefinition); const type = (0, graphql_1.typeFromAST)(this.schema, fragmentDefinition.typeCondition); const typeNode = this.options.exposeTypeNodes ? stripProp("loc", (0, graphql_1.parseType)(type.toString())) : undefined; return { fragmentName, filePath, source, type, selectionSet: this.compileSelectionSet(fragmentDefinition.selectionSet, type), typeNode, }; } compileSelectionSet(selectionSetNode, parentType, possibleTypes = this.possibleTypesForType(parentType), visitedFragments = new Set()) { return { possibleTypes, selections: selectionSetNode.selections .map((selectionNode) => wrapInBooleanConditionsIfNeeded(this.compileSelection(selectionNode, parentType, possibleTypes, visitedFragments), selectionNode, possibleTypes)) .filter((x) => x), }; } compileSelection(selectionNode, parentType, possibleTypes, visitedFragments) { switch (selectionNode.kind) { case graphql_1.Kind.FIELD: { const name = selectionNode.name.value; const alias = selectionNode.alias ? selectionNode.alias.value : undefined; const fieldDef = (0, graphql_2.getFieldDef)(this.schema, parentType, selectionNode); if (!fieldDef) { throw new graphql_1.GraphQLError(`Cannot query field "${name}" on type "${String(parentType)}"`, [selectionNode]); } const fieldType = fieldDef.type; const typeNode = this.options.exposeTypeNodes ? stripProp("loc", (0, graphql_1.parseType)(fieldType.toString())) : undefined; const unmodifiedFieldType = (0, graphql_1.getNamedType)(fieldType); this.addTypeUsed(unmodifiedFieldType); this.addUnionType(unmodifiedFieldType); this.addInterfaceType(unmodifiedFieldType); const { description, isDeprecated, deprecationReason } = fieldDef; const responseKey = alias || name; const args = selectionNode.arguments && selectionNode.arguments.length > 0 ? selectionNode.arguments.map((arg) => { const name = arg.name.value; const argDef = fieldDef.args.find((argDef) => argDef.name === arg.name.value); const argDefType = (argDef && argDef.type) || undefined; const argDeftypeNode = this.options.exposeTypeNodes && argDefType ? stripProp("loc", (0, graphql_1.parseType)(argDefType.toString())) : undefined; return { name, value: (0, graphql_2.valueFromValueNode)(arg.value), type: argDefType, typeNode: argDeftypeNode, }; }) : undefined; let field = { kind: "Field", responseKey, name, alias, args, type: fieldType, typeNode, description: !(0, graphql_2.isMetaFieldName)(name) && description ? description : undefined, isDeprecated, deprecationReason: deprecationReason || undefined, }; if ((0, graphql_1.isCompositeType)(unmodifiedFieldType)) { const selectionSetNode = selectionNode.selectionSet; if (!selectionSetNode) { throw new graphql_1.GraphQLError(`Composite field "${name}" on type "${String(parentType)}" requires selection set`, [selectionNode]); } field.selectionSet = this.compileSelectionSet(selectionNode.selectionSet, unmodifiedFieldType); } return field; } case graphql_1.Kind.INLINE_FRAGMENT: { const typeNode = selectionNode.typeCondition; const type = typeNode ? (0, graphql_1.typeFromAST)(this.schema, typeNode) : parentType; const possibleTypesForTypeCondition = this.possibleTypesForType(type).filter((type) => possibleTypes.includes(type)); const typeConditiontypeNode = this.options.exposeTypeNodes ? stripProp("loc", (0, graphql_1.parseType)(type.toString())) : undefined; return { kind: "TypeCondition", type, typeNode: typeConditiontypeNode, selectionSet: this.compileSelectionSet(selectionNode.selectionSet, type, possibleTypesForTypeCondition), }; } case graphql_1.Kind.FRAGMENT_SPREAD: { const fragmentName = selectionNode.name.value; if (visitedFragments.has(fragmentName)) return null; visitedFragments.add(fragmentName); const fragmentSpread = { kind: "FragmentSpread", fragmentName, selectionSet: { possibleTypes, selections: [], }, }; this.unresolvedFragmentSpreads.push(fragmentSpread); return fragmentSpread; } } } possibleTypesForType(type) { if ((0, graphql_1.isAbstractType)(type)) { return Array.from(this.schema.getPossibleTypes(type)) || []; } else { return [type]; } } } function wrapInBooleanConditionsIfNeeded(selection, selectionNode, possibleTypes) { if (!selection) return null; if (!selectionNode.directives) return selection; for (const directive of selectionNode.directives) { const directiveName = directive.name.value; if (directiveName === "skip" || directiveName === "include") { if (!directive.arguments) continue; const value = directive.arguments[0].value; switch (value.kind) { case "BooleanValue": if (directiveName === "skip") { return value.value ? null : selection; } else { return value.value ? selection : null; } break; case "Variable": selection = { kind: "BooleanCondition", variableName: value.name.value, inverted: directiveName === "skip", selectionSet: { possibleTypes, selections: [selection], }, }; break; } } } return selection; } //# sourceMappingURL=index.js.map