UNPKG

graphql-sock

Version:

GraphQL Semantic Output Conversion Kit - converts a cutting edge SDL file that supports semantic nullability into a more traditional SDL file legacy tools can support.

175 lines (174 loc) 7.66 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.semanticToNullable = semanticToNullable; exports.semanticToStrict = semanticToStrict; exports.convertFieldConfig = convertFieldConfig; const graphql_1 = require("graphql"); const graphql = __importStar(require("graphql")); // If GraphQL doesn't have this helper function, then it doesn't natively support GraphQLSemanticNonNull const isSemanticNonNullType = // eslint-disable-next-line @typescript-eslint/no-explicit-any (_a = graphql.isSemanticNonNullType) !== null && _a !== void 0 ? _a : (() => false); function convertSchema(schema, toStrict) { const config = schema.toConfig(); const convertType = makeConvertType(toStrict); const derivedSchema = new graphql_1.GraphQLSchema(Object.assign(Object.assign({}, config), { query: convertType(config.query), mutation: convertType(config.mutation), subscription: convertType(config.subscription), types: config.types .filter((t) => !t.name.startsWith("__")) .map((t) => convertType(t)), directives: config.directives.filter((d) => d.name !== "semanticNonNull") })); return derivedSchema; } function semanticToNullable(schema) { return convertSchema(schema, false); } function semanticToStrict(schema) { return convertSchema(schema, true); } function makeConvertType(toStrict) { const cache = new Map(); function convertFields(fields) { return () => { return Object.fromEntries(Object.entries(fields).map(([fieldName, inSpec]) => { const spec = convertFieldConfig(inSpec, toStrict); return [ fieldName, Object.assign(Object.assign({}, spec), { type: convertType(spec.type) }), ]; })); }; } function convertTypes(types) { if (!types) { return undefined; } return () => types.map((t) => convertType(t)); } function convertType(type) { if (!type) { return type; } if (isSemanticNonNullType(type)) { const unwrapped = convertType(type.ofType); // Here's where we do our thing! if (toStrict) { return new graphql_1.GraphQLNonNull(unwrapped); } else { return unwrapped; } } else if ((0, graphql_1.isNonNullType)(type)) { return new graphql_1.GraphQLNonNull(convertType(type.ofType)); } else if ((0, graphql_1.isListType)(type)) { return new graphql_1.GraphQLList(convertType(type.ofType)); } if (type.name.startsWith("__")) { return null; } if (cache.has(type.name)) { return cache.get(type.name); } const newType = (() => { if ((0, graphql_1.isObjectType)(type)) { const config = type.toConfig(); return new graphql_1.GraphQLObjectType(Object.assign(Object.assign({}, config), { fields: convertFields(config.fields), interfaces: convertTypes(config.interfaces) })); } else if ((0, graphql_1.isInterfaceType)(type)) { const config = type.toConfig(); return new graphql_1.GraphQLInterfaceType(Object.assign(Object.assign({}, config), { fields: convertFields(config.fields), interfaces: convertTypes(config.interfaces) })); } else if ((0, graphql_1.isUnionType)(type)) { const config = type.toConfig(); return new graphql_1.GraphQLUnionType(Object.assign(Object.assign({}, config), { types: convertTypes(config.types) })); } else { return type; } })(); cache.set(type.name, newType); return newType; } return convertType; } /** * Takes a GraphQL field config and checks to see if the `@semanticNonNull` * directive was applied; if so, converts to a field config that adds * GraphQLNonNull wrapper types in the relevant places if `toStrict` is true. * * @see {@url https://www.apollographql.com/docs/kotlin/advanced/nullability/#semanticnonnull} */ function convertFieldConfig(spec, toStrict) { var _a, _b, _c, _d; const directive = (_b = (_a = spec.astNode) === null || _a === void 0 ? void 0 : _a.directives) === null || _b === void 0 ? void 0 : _b.find((d) => d.name.value === "semanticNonNull"); if (!directive) { return spec; } /** The AST node with the semanticNonNull directive removed */ const filteredAstNode = Object.assign(Object.assign({}, spec.astNode), { directives: spec.astNode.directives.filter((d) => d.name.value !== "semanticNonNull") }); const levelsArg = (_c = directive.arguments) === null || _c === void 0 ? void 0 : _c.find((a) => a.name.value === "levels"); const levels = ((_d = levelsArg === null || levelsArg === void 0 ? void 0 : levelsArg.value) === null || _d === void 0 ? void 0 : _d.kind) === graphql_1.Kind.LIST ? levelsArg.value.values .filter((v) => v.kind === graphql_1.Kind.INT) .map((v) => Number(v.value)) : [0]; function recurse(type, level) { if (isSemanticNonNullType(type)) { // Strip semantic-non-null types; this should never happen but if someone // uses both semantic-non-null and the `@semanticNonNull` directive, we // want the directive to win (I guess?) return recurse(type.ofType, level); } else if ((0, graphql_1.isNonNullType)(type)) { const inner = recurse(type.ofType, level); if ((0, graphql_1.isNonNullType)(inner)) { return inner; } else { // Carry the non-null through no matter what semantic says return new graphql_1.GraphQLNonNull(inner); } } else if ((0, graphql_1.isListType)(type)) { const inner = new graphql_1.GraphQLList(recurse(type.ofType, level + 1)); if (toStrict && levels.includes(level)) { return new graphql_1.GraphQLNonNull(inner); } else { return inner; } } else { if (toStrict && levels.includes(level)) { return new graphql_1.GraphQLNonNull(type); } else { return type; } } } return Object.assign(Object.assign({}, spec), { type: recurse(spec.type, 0), astNode: filteredAstNode }); }