UNPKG

@graphql-codegen/visitor-plugin-common

Version:
537 lines (536 loc) • 20 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getFieldNames = exports.getFieldNodeNameValue = exports.REQUIRE_FIELDS_TYPE = exports.OMIT_TYPE = exports.DeclarationBlock = exports.getConfigValue = void 0; exports.quoteIfNeeded = quoteIfNeeded; exports.block = block; exports.wrapWithSingleQuotes = wrapWithSingleQuotes; exports.breakLine = breakLine; exports.indent = indent; exports.indentMultiline = indentMultiline; exports.transformComment = transformComment; exports.getBaseTypeNode = getBaseTypeNode; exports.convertNameParts = convertNameParts; exports.buildScalarsFromConfig = buildScalarsFromConfig; exports.buildScalars = buildScalars; exports.getRootTypeNames = getRootTypeNames; exports.stripMapperTypeInterpolation = stripMapperTypeInterpolation; exports.mergeSelectionSets = mergeSelectionSets; exports.separateSelectionSet = separateSelectionSet; exports.getPossibleTypes = getPossibleTypes; exports.hasConditionalDirectives = hasConditionalDirectives; exports.hasIncrementalDeliveryDirectives = hasIncrementalDeliveryDirectives; exports.wrapTypeWithModifiers = wrapTypeWithModifiers; exports.removeDescription = removeDescription; exports.wrapTypeNodeWithModifiers = wrapTypeNodeWithModifiers; exports.isOneOfInputObjectType = isOneOfInputObjectType; exports.groupBy = groupBy; exports.flatten = flatten; exports.unique = unique; const graphql_1 = require("graphql"); const mappers_js_1 = require("./mappers.js"); const scalars_js_1 = require("./scalars.js"); const getConfigValue = (value, defaultValue) => { if (value === null || value === undefined) { return defaultValue; } return value; }; exports.getConfigValue = getConfigValue; function quoteIfNeeded(array, joinWith = ' & ') { if (array.length === 0) { return ''; } if (array.length === 1) { return array[0]; } return `(${array.join(joinWith)})`; } function block(array) { return array && array.length !== 0 ? '{\n' + array.join('\n') + '\n}' : ''; } function wrapWithSingleQuotes(value, skipNumericCheck = false) { if (skipNumericCheck) { if (typeof value === 'number') { return String(value); } return `'${value}'`; } if (typeof value === 'number' || (typeof value === 'string' && !Number.isNaN(parseInt(value)) && parseFloat(value).toString() === value)) { return String(value); } return `'${value}'`; } function breakLine(str) { return str + '\n'; } function indent(str, count = 1) { return new Array(count).fill(' ').join('') + str; } function indentMultiline(str, count = 1) { const indentation = new Array(count).fill(' ').join(''); const replaceWith = '\n' + indentation; return indentation + str.replace(/\n/g, replaceWith); } function transformComment(comment, indentLevel = 0, disabled = false) { if (!comment || comment === '' || disabled) { return ''; } if (isStringValueNode(comment)) { comment = comment.value; } comment = comment.split('*/').join('*\\/'); let lines = comment.split('\n'); if (lines.length === 1) { return indent(`/** ${lines[0]} */\n`, indentLevel); } lines = ['/**', ...lines.map(line => ` * ${line}`), ' */\n']; return stripTrailingSpaces(lines.map(line => indent(line, indentLevel)).join('\n')); } class DeclarationBlock { constructor(_config) { this._config = _config; this._decorator = null; this._export = false; this._name = null; this._kind = null; this._methodName = null; this._content = null; this._block = null; this._nameGenerics = null; this._comment = null; this._ignoreBlockWrapper = false; this._config = { blockWrapper: '', blockTransformer: block => block, enumNameValueSeparator: ':', ...this._config, }; } withDecorator(decorator) { this._decorator = decorator; return this; } export(exp = true) { if (!this._config.ignoreExport) { this._export = exp; } return this; } asKind(kind) { this._kind = kind; return this; } withComment(comment, disabled = false) { const nonEmptyComment = !!(isStringValueNode(comment) ? comment.value : comment); if (nonEmptyComment && !disabled) { this._comment = transformComment(comment, 0); } return this; } withMethodCall(methodName, ignoreBlockWrapper = false) { this._methodName = methodName; this._ignoreBlockWrapper = ignoreBlockWrapper; return this; } withBlock(block) { this._block = block; return this; } withContent(content) { this._content = content; return this; } withName(name, generics = null) { this._name = name; this._nameGenerics = generics; return this; } get string() { let result = ''; if (this._decorator) { result += this._decorator + '\n'; } if (this._export) { result += 'export '; } if (this._kind) { let extra = ''; let name = ''; if (['type', 'const', 'var', 'let'].includes(this._kind)) { extra = '= '; } if (this._name) { name = this._name + (this._nameGenerics || '') + ' '; } result += this._kind + ' ' + name + extra; } if (this._block) { if (this._content) { result += this._content; } const blockWrapper = this._ignoreBlockWrapper ? '' : this._config.blockWrapper; const before = '{' + blockWrapper; const after = blockWrapper + '}'; const block = [before, this._block, after].filter(val => !!val).join('\n'); if (this._methodName) { result += `${this._methodName}(${this._config.blockTransformer(block)})`; } else { result += this._config.blockTransformer(block); } } else if (this._content) { result += this._content; } else if (this._kind) { result += this._config.blockTransformer('{}'); } return stripTrailingSpaces((this._comment || '') + result + (this._kind === 'interface' || this._kind === 'enum' || this._kind === 'namespace' || this._kind === 'function' ? '' : ';') + '\n'); } } exports.DeclarationBlock = DeclarationBlock; function getBaseTypeNode(typeNode) { if (typeNode.kind === graphql_1.Kind.LIST_TYPE || typeNode.kind === graphql_1.Kind.NON_NULL_TYPE) { return getBaseTypeNode(typeNode.type); } return typeNode; } function convertNameParts(str, func, removeUnderscore = false) { if (removeUnderscore) { return func(str); } return str .split('_') .map(s => func(s)) .join('_'); } function buildScalarsFromConfig(schema, config, defaultScalarsMapping = scalars_js_1.DEFAULT_SCALARS, defaultScalarType = 'any') { return buildScalars(schema, config.scalars, defaultScalarsMapping, config.strictScalars ? null : config.defaultScalarType || defaultScalarType); } function buildScalars(schema, scalarsMapping, defaultScalarsMapping = scalars_js_1.DEFAULT_SCALARS, defaultScalarType = 'any') { const result = {}; function normalizeScalarType(type) { if (typeof type === 'string') { return { input: type, output: type, }; } return { input: type.input, output: type.output, }; } for (const name of Object.keys(defaultScalarsMapping)) { result[name] = { input: (0, mappers_js_1.parseMapper)(defaultScalarsMapping[name].input), output: (0, mappers_js_1.parseMapper)(defaultScalarsMapping[name].output), }; } if (schema) { const typeMap = schema.getTypeMap(); Object.keys(typeMap) .map(typeName => typeMap[typeName]) .filter(type => (0, graphql_1.isScalarType)(type)) .map((scalarType) => { const { name } = scalarType; if (typeof scalarsMapping === 'string') { const inputMapper = (0, mappers_js_1.parseMapper)(scalarsMapping + '#' + name, name); if (inputMapper.isExternal) { inputMapper.type += "['input']"; } const outputMapper = (0, mappers_js_1.parseMapper)(scalarsMapping + '#' + name, name); if (outputMapper.isExternal) { outputMapper.type += "['output']"; } result[name] = { input: inputMapper, output: outputMapper, }; } else if (scalarsMapping?.[name]) { const mappedScalar = scalarsMapping[name]; if (typeof mappedScalar === 'string') { const normalizedScalar = normalizeScalarType(scalarsMapping[name]); result[name] = { input: (0, mappers_js_1.parseMapper)(normalizedScalar.input, name), output: (0, mappers_js_1.parseMapper)(normalizedScalar.output, name), }; } else if (typeof mappedScalar === 'object' && mappedScalar.input && mappedScalar.output) { result[name] = { input: (0, mappers_js_1.parseMapper)(mappedScalar.input, name), output: (0, mappers_js_1.parseMapper)(mappedScalar.output, name), }; } else { result[name] = { input: { isExternal: false, type: JSON.stringify(mappedScalar), }, output: { isExternal: false, type: JSON.stringify(mappedScalar), }, }; } } else if (scalarType.extensions?.codegenScalarType) { result[name] = { input: { isExternal: false, type: scalarType.extensions.codegenScalarType, }, output: { isExternal: false, type: scalarType.extensions.codegenScalarType, }, }; } else if (!defaultScalarsMapping[name]) { if (defaultScalarType === null) { throw new Error(`Unknown scalar type ${name}. Please override it using the "scalars" configuration field!`); } result[name] = { input: { isExternal: false, type: defaultScalarType, }, output: { isExternal: false, type: defaultScalarType, }, }; } }); } else if (scalarsMapping) { if (typeof scalarsMapping === 'string') { throw new Error('Cannot use string scalars mapping when building without a schema'); } for (const name of Object.keys(scalarsMapping)) { if (typeof scalarsMapping[name] === 'string') { const normalizedScalar = normalizeScalarType(scalarsMapping[name]); result[name] = { input: (0, mappers_js_1.parseMapper)(normalizedScalar.input, name), output: (0, mappers_js_1.parseMapper)(normalizedScalar.output, name), }; } else { const normalizedScalar = normalizeScalarType(scalarsMapping[name]); result[name] = { input: { isExternal: false, type: JSON.stringify(normalizedScalar.input), }, output: { isExternal: false, type: JSON.stringify(normalizedScalar.output), }, }; } } } return result; } function isStringValueNode(node) { return node && typeof node === 'object' && node.kind === graphql_1.Kind.STRING; } // will be removed on next release because tools already has it function getRootTypeNames(schema) { return [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()] .filter(t => t) .map(t => t.name); } function stripMapperTypeInterpolation(identifier) { return identifier.trim().replace(/<{.*}>/, ''); } exports.OMIT_TYPE = 'export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;'; exports.REQUIRE_FIELDS_TYPE = `export type RequireFields<T, K extends keyof T> = Omit<T, K> & { [P in K]-?: NonNullable<T[P]> };`; /** * merge selection sets into a new selection set without mutating the inputs. */ function mergeSelectionSets(selectionSet1, selectionSet2) { const newSelections = [...selectionSet1.selections]; for (let selection2 of selectionSet2.selections) { if (selection2.kind === 'FragmentSpread' || selection2.kind === 'InlineFragment') { newSelections.push(selection2); continue; } if (selection2.kind !== 'Field') { throw new TypeError('Invalid state.'); } const match = newSelections.find(selection1 => selection1.kind === 'Field' && (0, exports.getFieldNodeNameValue)(selection1) === (0, exports.getFieldNodeNameValue)(selection2)); if (match && // recursively merge all selection sets match.kind === 'Field' && match.selectionSet && selection2.selectionSet) { selection2 = { ...selection2, selectionSet: mergeSelectionSets(match.selectionSet, selection2.selectionSet), }; } newSelections.push(selection2); } return { kind: graphql_1.Kind.SELECTION_SET, selections: newSelections, }; } const getFieldNodeNameValue = (node) => { return (node.alias || node.name).value; }; exports.getFieldNodeNameValue = getFieldNodeNameValue; function separateSelectionSet(selections) { return { fields: selections.filter(s => s.kind === graphql_1.Kind.FIELD), inlines: selections.filter(s => s.kind === graphql_1.Kind.INLINE_FRAGMENT), spreads: selections.filter(s => s.kind === graphql_1.Kind.FRAGMENT_SPREAD), }; } function getPossibleTypes(schema, type) { if ((0, graphql_1.isListType)(type) || (0, graphql_1.isNonNullType)(type)) { return getPossibleTypes(schema, type.ofType); } if ((0, graphql_1.isObjectType)(type)) { return [type]; } if ((0, graphql_1.isAbstractType)(type)) { return schema.getPossibleTypes(type); } return []; } function hasConditionalDirectives(field) { const CONDITIONAL_DIRECTIVES = ['skip', 'include']; return field.directives?.some(directive => CONDITIONAL_DIRECTIVES.includes(directive.name.value)); } function hasIncrementalDeliveryDirectives(directives) { const INCREMENTAL_DELIVERY_DIRECTIVES = ['defer']; return directives?.some(directive => INCREMENTAL_DELIVERY_DIRECTIVES.includes(directive.name.value)); } function wrapTypeWithModifiers(baseType, type, options) { let currentType = type; const modifiers = []; while (currentType) { if ((0, graphql_1.isNonNullType)(currentType)) { currentType = currentType.ofType; } else { modifiers.push(options.wrapOptional); } if ((0, graphql_1.isListType)(currentType)) { modifiers.push(options.wrapArray); currentType = currentType.ofType; } else { break; } } return modifiers.reduceRight((result, modifier) => modifier(result), baseType); } function removeDescription(nodes) { return nodes.map(node => ({ ...node, description: undefined })); } function wrapTypeNodeWithModifiers(baseType, typeNode) { switch (typeNode.kind) { case graphql_1.Kind.NAMED_TYPE: { return `Maybe<${baseType}>`; } case graphql_1.Kind.NON_NULL_TYPE: { const innerType = wrapTypeNodeWithModifiers(baseType, typeNode.type); return clearOptional(innerType); } case graphql_1.Kind.LIST_TYPE: { const innerType = wrapTypeNodeWithModifiers(baseType, typeNode.type); return `Maybe<Array<${innerType}>>`; } } } function clearOptional(str) { const rgx = new RegExp(`^Maybe<(.*?)>$`, 'i'); if (str.startsWith(`Maybe`)) { return str.replace(rgx, '$1'); } return str; } function stripTrailingSpaces(str) { return str.replace(/ +\n/g, '\n'); } const isOneOfTypeCache = new WeakMap(); function isOneOfInputObjectType(namedType) { if (!namedType) { return false; } let isOneOfType = isOneOfTypeCache.get(namedType); if (isOneOfType !== undefined) { return isOneOfType; } isOneOfType = (0, graphql_1.isInputObjectType)(namedType) && (namedType.isOneOf || namedType.astNode?.directives?.some(d => d.name.value === 'oneOf')); isOneOfTypeCache.set(namedType, isOneOfType); return isOneOfType; } function groupBy(array, key) { return array.reduce((acc, item) => { const group = (acc[key(item)] ??= []); group.push(item); return acc; }, {}); } function flatten(array) { return [].concat(...array); } function unique(array, key = item => item.toString()) { return Object.values(array.reduce((acc, item) => ({ [key(item)]: item, ...acc }), {})); } function getFullPathFieldName(selection, parentName) { const fullName = 'alias' in selection && selection.alias ? `${selection.alias.value}@${selection.name.value}` : selection.name.value; return parentName ? `${parentName}.${fullName}` : fullName; } const getFieldNames = ({ selections, fieldNames = new Set(), parentName = '', loadedFragments, }) => { for (const selection of selections) { switch (selection.kind) { case graphql_1.Kind.FIELD: { const fieldName = getFullPathFieldName(selection, parentName); fieldNames.add(fieldName); if (selection.selectionSet) { (0, exports.getFieldNames)({ selections: selection.selectionSet.selections, fieldNames, parentName: fieldName, loadedFragments, }); } break; } case graphql_1.Kind.FRAGMENT_SPREAD: { (0, exports.getFieldNames)({ selections: loadedFragments .filter(def => def.name === selection.name.value) .flatMap(s => s.node.selectionSet.selections), fieldNames, parentName, loadedFragments, }); break; } case graphql_1.Kind.INLINE_FRAGMENT: { (0, exports.getFieldNames)({ selections: selection.selectionSet.selections, fieldNames, parentName, loadedFragments }); break; } } } return fieldNames; }; exports.getFieldNames = getFieldNames;