@graphql-codegen/visitor-plugin-common
Version:
1,266 lines (1,255 loc) • 148 kB
JavaScript
import { Kind, isScalarType, isListType, isNonNullType, isObjectType, isAbstractType, isEnumType, isInterfaceType, GraphQLObjectType, isUnionType, print, isTypeSubTypeOf, SchemaMetaFieldDef, TypeMetaFieldDef } from 'graphql';
import { resolveExternalModuleAndFn, DetailedError, ApolloFederation, getBaseType, oldVisit, removeNonNullWrapper } from '@graphql-codegen/plugin-helpers';
import { pascalCase } from 'change-case-all';
import { resolve, relative, dirname, join, isAbsolute, extname, basename } from 'path';
import parse from 'parse-filepath';
import autoBind from 'auto-bind';
import { getRootTypeNames as getRootTypeNames$1, getRootTypes } from '@graphql-tools/utils';
import { DepGraph } from 'dependency-graph';
import gqlTag from 'graphql-tag';
import { optimizeDocumentNode } from '@graphql-tools/optimize';
import { createHash } from 'crypto';
import { optimizeDocuments } from '@graphql-tools/relay-operation-optimizer';
const DEFAULT_SCALARS = {
ID: 'string',
String: 'string',
Boolean: 'boolean',
Int: 'number',
Float: 'number',
};
function isExternalMapperType(m) {
return !!m.import;
}
var MapperKind;
(function (MapperKind) {
MapperKind[MapperKind["Namespace"] = 0] = "Namespace";
MapperKind[MapperKind["Default"] = 1] = "Default";
MapperKind[MapperKind["Regular"] = 2] = "Regular";
})(MapperKind || (MapperKind = {}));
function prepareLegacy(mapper) {
const items = mapper.split('#');
const isNamespace = items.length === 3;
const isDefault = items[1].trim() === 'default' || items[1].startsWith('default ');
const hasAlias = items[1].includes(' as ');
return {
items,
isDefault,
isNamespace,
hasAlias,
};
}
function prepare(mapper) {
const [source, path] = mapper.split('#');
const isNamespace = path.includes('.');
const isDefault = path.trim() === 'default' || path.startsWith('default ');
const hasAlias = path.includes(' as ');
return {
items: isNamespace ? [source, ...path.split('.')] : [source, path],
isDefault,
isNamespace,
hasAlias,
};
}
function isLegacyMode(mapper) {
return mapper.split('#').length === 3;
}
function parseMapper(mapper, gqlTypeName = null, suffix) {
if (isExternalMapper(mapper)) {
const { isNamespace, isDefault, hasAlias, items } = isLegacyMode(mapper) ? prepareLegacy(mapper) : prepare(mapper);
const mapperKind = isNamespace
? MapperKind.Namespace
: isDefault
? MapperKind.Default
: MapperKind.Regular;
function handleAlias(isDefault = false) {
const [importedType, aliasType] = items[1].split(/\s+as\s+/);
const type = maybeSuffix(aliasType);
return {
importElement: isDefault ? type : `${importedType} as ${type}`,
type,
};
}
function maybeSuffix(type) {
if (suffix) {
return addSuffix(type, suffix);
}
return type;
}
function handle() {
switch (mapperKind) {
// ./my/module#Namespace#Identifier
case MapperKind.Namespace: {
const [, ns, identifier] = items;
return {
type: `${ns}.${identifier}`,
importElement: ns,
};
}
case MapperKind.Default: {
// ./my/module#default as alias
if (hasAlias) {
return handleAlias(true);
}
const type = maybeSuffix(`${gqlTypeName}`);
// ./my/module#default
return {
importElement: type,
type,
};
}
case MapperKind.Regular: {
// ./my/module#Identifier as alias
if (hasAlias) {
return handleAlias();
}
const identifier = items[1];
const type = maybeSuffix(identifier);
// ./my/module#Identifier
return {
type,
importElement: suffix ? `${identifier} as ${type}` : type,
};
}
}
}
const { type, importElement } = handle();
return {
default: isDefault,
isExternal: true,
source: items[0],
type,
import: importElement.replace(/<(.*?)>/g, ''),
};
}
return {
isExternal: false,
type: mapper,
};
}
function addSuffix(element, suffix) {
const generic = element.indexOf('<');
if (generic === -1) {
return `${element}${suffix}`;
}
return `${element.slice(0, generic)}${suffix}${element.slice(generic)}`;
}
function isExternalMapper(value) {
return value.includes('#');
}
function transformMappers(rawMappers, mapperTypeSuffix) {
const result = {};
Object.keys(rawMappers).forEach(gqlTypeName => {
const mapperDef = rawMappers[gqlTypeName];
const parsedMapper = parseMapper(mapperDef, gqlTypeName, mapperTypeSuffix);
result[gqlTypeName] = parsedMapper;
});
return result;
}
function transformDirectiveArgumentAndInputFieldMappings(rawDirectiveArgumentAndInputFieldMappings, directiveArgumentAndInputFieldMappingTypeSuffix) {
const result = {};
Object.keys(rawDirectiveArgumentAndInputFieldMappings).forEach(directive => {
const mapperDef = rawDirectiveArgumentAndInputFieldMappings[directive];
const parsedMapper = parseMapper(mapperDef, directive, directiveArgumentAndInputFieldMappingTypeSuffix);
result[directive] = parsedMapper;
});
return result;
}
function buildMapperImport(source, types, useTypeImports) {
if (!types || types.length === 0) {
return null;
}
const defaultType = types.find(t => t.asDefault === true);
let namedTypes = types.filter(t => !t.asDefault);
if (useTypeImports) {
if (defaultType) {
// default as Baz
namedTypes = [{ identifier: `default as ${defaultType.identifier}` }, ...namedTypes];
}
// { Foo, Bar as BarModel }
const namedImports = namedTypes.length ? `{ ${namedTypes.map(t => t.identifier).join(', ')} }` : '';
// { default as Baz, Foo, Bar as BarModel }
return `import type ${[namedImports].filter(Boolean).join(', ')} from '${source}';`;
}
// { Foo, Bar as BarModel }
const namedImports = namedTypes.length ? `{ ${namedTypes.map(t => t.identifier).join(', ')} }` : '';
// Baz
const defaultImport = defaultType ? defaultType.identifier : '';
// Baz, { Foo, Bar as BarModel }
return `import ${[defaultImport, namedImports].filter(Boolean).join(', ')} from '${source}';`;
}
const getConfigValue = (value, defaultValue) => {
if (value === null || value === undefined) {
return defaultValue;
}
return value;
};
function quoteIfNeeded(array, joinWith = ' & ') {
if (array.length === 0) {
return '';
}
else if (array.length === 1) {
return array[0];
}
else {
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 `${value}`;
}
else {
return `'${value}'`;
}
}
if (typeof value === 'number' ||
(typeof value === 'string' && !isNaN(parseInt(value)) && parseFloat(value).toString() === value)) {
return `${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 ? this._comment : '') +
result +
(this._kind === 'interface' || this._kind === 'enum' || this._kind === 'namespace' || this._kind === 'function'
? ''
: ';') +
'\n');
}
}
function getBaseTypeNode(typeNode) {
if (typeNode.kind === Kind.LIST_TYPE || typeNode.kind === 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 = DEFAULT_SCALARS, defaultScalarType = 'any') {
return buildScalars(schema, config.scalars, defaultScalarsMapping, config.strictScalars ? null : config.defaultScalarType || defaultScalarType);
}
function buildScalars(schema, scalarsMapping, defaultScalarsMapping = DEFAULT_SCALARS, defaultScalarType = 'any') {
const result = {};
Object.keys(defaultScalarsMapping).forEach(name => {
result[name] = parseMapper(defaultScalarsMapping[name]);
});
if (schema) {
const typeMap = schema.getTypeMap();
Object.keys(typeMap)
.map(typeName => typeMap[typeName])
.filter(type => isScalarType(type))
.map((scalarType) => {
var _a;
const { name } = scalarType;
if (typeof scalarsMapping === 'string') {
const value = parseMapper(scalarsMapping + '#' + name, name);
result[name] = value;
}
else if (scalarsMapping && typeof scalarsMapping[name] === 'string') {
const value = parseMapper(scalarsMapping[name], name);
result[name] = value;
}
else if (scalarsMapping && scalarsMapping[name]) {
result[name] = {
isExternal: false,
type: JSON.stringify(scalarsMapping[name]),
};
}
else if ((_a = scalarType.extensions) === null || _a === void 0 ? void 0 : _a.codegenScalarType) {
result[name] = {
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] = {
isExternal: false,
type: defaultScalarType,
};
}
});
}
else if (scalarsMapping) {
if (typeof scalarsMapping === 'string') {
throw new Error('Cannot use string scalars mapping when building without a schema');
}
Object.keys(scalarsMapping).forEach(name => {
if (typeof scalarsMapping[name] === 'string') {
const value = parseMapper(scalarsMapping[name], name);
result[name] = value;
}
else {
result[name] = {
isExternal: false,
type: JSON.stringify(scalarsMapping[name]),
};
}
});
}
return result;
}
function isStringValueNode(node) {
return node && typeof node === 'object' && node.kind === 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(/<{.*}>/, '');
}
const OMIT_TYPE = 'export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;';
const 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' &&
getFieldNodeNameValue(selection1) === getFieldNodeNameValue(selection2));
if (match) {
// recursively merge all selection sets
if (match.kind === 'Field' && match.selectionSet && selection2.selectionSet) {
selection2 = {
...selection2,
selectionSet: mergeSelectionSets(match.selectionSet, selection2.selectionSet),
};
}
}
newSelections.push(selection2);
}
return {
kind: Kind.SELECTION_SET,
selections: newSelections,
};
}
const getFieldNodeNameValue = (node) => {
return (node.alias || node.name).value;
};
function separateSelectionSet(selections) {
return {
fields: selections.filter(s => s.kind === Kind.FIELD),
inlines: selections.filter(s => s.kind === Kind.INLINE_FRAGMENT),
spreads: selections.filter(s => s.kind === Kind.FRAGMENT_SPREAD),
};
}
function getPossibleTypes(schema, type) {
if (isListType(type) || isNonNullType(type)) {
return getPossibleTypes(schema, type.ofType);
}
else if (isObjectType(type)) {
return [type];
}
else if (isAbstractType(type)) {
return schema.getPossibleTypes(type);
}
return [];
}
function hasConditionalDirectives(field) {
var _a;
const CONDITIONAL_DIRECTIVES = ['skip', 'include'];
return (_a = field.directives) === null || _a === void 0 ? void 0 : _a.some(directive => CONDITIONAL_DIRECTIVES.includes(directive.name.value));
}
function wrapTypeWithModifiers(baseType, type, options) {
let currentType = type;
const modifiers = [];
while (currentType) {
if (isNonNullType(currentType)) {
currentType = currentType.ofType;
}
else {
modifiers.push(options.wrapOptional);
}
if (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 Kind.NAMED_TYPE: {
return `Maybe<${baseType}>`;
}
case Kind.NON_NULL_TYPE: {
const innerType = wrapTypeNodeWithModifiers(baseType, typeNode.type);
return clearOptional(innerType);
}
case 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');
}
function getKind(node) {
if (typeof node === 'string') {
return 'typeNames';
}
if (['EnumValueDefinition', 'EnumValue'].includes(node.kind)) {
return 'enumValues';
}
return 'typeNames';
}
function getName(node) {
if (node == null) {
return undefined;
}
if (typeof node === 'string') {
return node;
}
switch (node.kind) {
case 'OperationDefinition':
case 'Variable':
case 'Argument':
case 'FragmentSpread':
case 'FragmentDefinition':
case 'ObjectField':
case 'Directive':
case 'NamedType':
case 'ScalarTypeDefinition':
case 'ObjectTypeDefinition':
case 'FieldDefinition':
case 'InputValueDefinition':
case 'InterfaceTypeDefinition':
case 'UnionTypeDefinition':
case 'EnumTypeDefinition':
case 'EnumValueDefinition':
case 'InputObjectTypeDefinition':
case 'DirectiveDefinition': {
return getName(node.name);
}
case 'Name': {
return node.value;
}
case 'Field': {
return getName(node.alias || node.name);
}
case 'VariableDefinition': {
return getName(node.variable);
}
}
return undefined;
}
function convertFactory(config) {
function resolveConventionName(type) {
if (!config.namingConvention) {
return (str, opts = {}) => {
return convertNameParts(str, pascalCase, getConfigValue((opts || {}).transformUnderscore, false));
};
}
if (typeof config.namingConvention === 'string') {
if (config.namingConvention === 'keep') {
return str => str;
}
return (str, opts = {}) => {
return convertNameParts(str, resolveExternalModuleAndFn(config.namingConvention), getConfigValue((opts || {}).transformUnderscore, false));
};
}
if (typeof config.namingConvention === 'function') {
return (str, opts = {}) => {
return convertNameParts(str, config.namingConvention, getConfigValue((opts || {}).transformUnderscore, false));
};
}
if (typeof config.namingConvention === 'object' && config.namingConvention[type] === 'keep') {
return str => str;
}
if (typeof config.namingConvention === 'object') {
if (!config.namingConvention[type]) {
return (str, opts = {}) => {
const transformUnderscore = config.namingConvention.transformUnderscore || (opts || {}).transformUnderscore;
return convertNameParts(str, pascalCase, getConfigValue(transformUnderscore, false));
};
}
return (str, opts = {}) => {
return convertNameParts(str, resolveExternalModuleAndFn(config.namingConvention[type]), getConfigValue((opts || {}).transformUnderscore, true));
};
}
return config.namingConvention[type];
}
return (node, opts) => {
const prefix = opts && opts.prefix;
const suffix = opts && opts.suffix;
const kind = getKind(node);
const str = [prefix || '', getName(node), suffix || ''].join('');
return resolveConventionName(kind)(str, opts);
};
}
function escapeString(str) {
return str.replace(/\\/g, '\\\\').replace(/\n/g, '\\n').replace(/'/g, "\\'");
}
function parseEnumValues({ schema, mapOrStr = {}, ignoreEnumValuesFromSchema, }) {
const allTypes = schema.getTypeMap();
const allEnums = Object.keys(allTypes).filter(t => isEnumType(allTypes[t]));
if (typeof mapOrStr === 'object') {
if (!ignoreEnumValuesFromSchema) {
for (const enumTypeName of allEnums) {
const enumType = schema.getType(enumTypeName);
for (const { name, value } of enumType.getValues()) {
if (value && value !== name) {
mapOrStr[enumTypeName] = mapOrStr[enumTypeName] || {};
if (typeof mapOrStr[enumTypeName] !== 'string' && !mapOrStr[enumTypeName][name]) {
mapOrStr[enumTypeName][name] = typeof value === 'string' ? escapeString(value) : value;
}
}
}
}
}
const invalidMappings = Object.keys(mapOrStr).filter(gqlName => !allEnums.includes(gqlName));
if (invalidMappings.length > 0) {
throw new DetailedError(`Invalid 'enumValues' mapping!`, `The following types does not exist in your GraphQL schema: ${invalidMappings.join(', ')}`);
}
return Object.keys(mapOrStr).reduce((prev, gqlIdentifier) => {
const pointer = mapOrStr[gqlIdentifier];
if (typeof pointer === 'string') {
const mapper = parseMapper(pointer, gqlIdentifier);
return {
...prev,
[gqlIdentifier]: {
isDefault: mapper.isExternal && mapper.default,
typeIdentifier: gqlIdentifier,
sourceFile: mapper.isExternal ? mapper.source : null,
sourceIdentifier: mapper.type,
importIdentifier: mapper.isExternal ? mapper.import : null,
mappedValues: null,
},
};
}
else if (typeof pointer === 'object') {
return {
...prev,
[gqlIdentifier]: {
isDefault: false,
typeIdentifier: gqlIdentifier,
sourceFile: null,
sourceIdentifier: null,
importIdentifier: null,
mappedValues: pointer,
},
};
}
else {
throw new DetailedError(`Invalid "enumValues" configuration`, `Enum "${gqlIdentifier}": expected string or object (with enum values mapping)`);
}
}, {});
}
else if (typeof mapOrStr === 'string') {
return allEnums
.filter(enumName => !enumName.startsWith('__'))
.reduce((prev, enumName) => {
return {
...prev,
[enumName]: {
isDefault: false,
typeIdentifier: enumName,
sourceFile: mapOrStr,
sourceIdentifier: enumName,
importIdentifier: enumName,
mappedValues: null,
},
};
}, {});
}
return {};
}
const DEFAULT_DECLARATION_KINDS = {
directive: 'type',
scalar: 'type',
input: 'type',
type: 'type',
interface: 'type',
arguments: 'type',
};
function normalizeDeclarationKind(declarationKind) {
if (typeof declarationKind === 'string') {
return {
directive: declarationKind,
scalar: declarationKind,
input: declarationKind,
type: declarationKind,
interface: declarationKind,
arguments: declarationKind,
};
}
return {
...DEFAULT_DECLARATION_KINDS,
...declarationKind,
};
}
const DEFAULT_AVOID_OPTIONALS = {
object: false,
inputValue: false,
field: false,
defaultValue: false,
resolvers: false,
};
function normalizeAvoidOptionals(avoidOptionals) {
if (typeof avoidOptionals === 'boolean') {
return {
object: avoidOptionals,
inputValue: avoidOptionals,
field: avoidOptionals,
defaultValue: avoidOptionals,
resolvers: avoidOptionals,
};
}
return {
...DEFAULT_AVOID_OPTIONALS,
...avoidOptionals,
};
}
function generateFragmentImportStatement(statement, kind) {
const { importSource: fragmentImportSource, ...rest } = statement;
const { identifiers, path, namespace } = fragmentImportSource;
const importSource = {
identifiers: identifiers
.filter(fragmentImport => kind === 'both' || kind === fragmentImport.kind)
.map(({ name }) => name),
path,
namespace,
};
return generateImportStatement({
importSource,
...rest,
typesImport: kind === 'type' ? statement.typesImport : false,
});
}
function generateImportStatement(statement) {
const { baseDir, importSource, outputPath, typesImport } = statement;
const importPath = resolveImportPath(baseDir, outputPath, importSource.path);
const importNames = importSource.identifiers && importSource.identifiers.length
? `{ ${Array.from(new Set(importSource.identifiers)).join(', ')} }`
: '*';
const importAlias = importSource.namespace ? ` as ${importSource.namespace}` : '';
const importStatement = typesImport ? 'import type' : 'import';
return `${importStatement} ${importNames}${importAlias} from '${importPath}';${importAlias ? '\n' : ''}`;
}
function resolveImportPath(baseDir, outputPath, sourcePath) {
const shouldAbsolute = !sourcePath.startsWith('~');
if (shouldAbsolute) {
const absGeneratedFilePath = resolve(baseDir, outputPath);
const absImportFilePath = resolve(baseDir, sourcePath);
return resolveRelativeImport(absGeneratedFilePath, absImportFilePath);
}
else {
return sourcePath.replace(`~`, '');
}
}
function resolveRelativeImport(from, to) {
if (!isAbsolute(from)) {
throw new Error(`Argument 'from' must be an absolute path, '${from}' given.`);
}
if (!isAbsolute(to)) {
throw new Error(`Argument 'to' must be an absolute path, '${to}' given.`);
}
return fixLocalFilePath(clearExtension(relative(dirname(from), to)));
}
function resolveImportSource(source) {
return typeof source === 'string' ? { path: source } : source;
}
function clearExtension(path) {
const parsedPath = parse(path);
return join(parsedPath.dir, parsedPath.name).replace(/\\/g, '/');
}
function fixLocalFilePath(path) {
return !path.startsWith('..') ? `./${path}` : path;
}
class BaseVisitor {
constructor(rawConfig, additionalConfig) {
var _a;
this._declarationBlockConfig = {};
this._parsedConfig = {
convert: convertFactory(rawConfig),
typesPrefix: rawConfig.typesPrefix || '',
typesSuffix: rawConfig.typesSuffix || '',
externalFragments: rawConfig.externalFragments || [],
fragmentImports: rawConfig.fragmentImports || [],
addTypename: !rawConfig.skipTypename,
nonOptionalTypename: !!rawConfig.nonOptionalTypename,
useTypeImports: !!rawConfig.useTypeImports,
dedupeFragments: !!rawConfig.dedupeFragments,
allowEnumStringTypes: !!rawConfig.allowEnumStringTypes,
inlineFragmentTypes: (_a = rawConfig.inlineFragmentTypes) !== null && _a !== void 0 ? _a : 'inline',
...(additionalConfig || {}),
};
this.scalars = {};
Object.keys(this.config.scalars || {}).forEach(key => {
this.scalars[key] = this.config.scalars[key].type;
});
autoBind(this);
}
getVisitorKindContextFromAncestors(ancestors) {
if (!ancestors) {
return [];
}
return ancestors.map(t => t.kind).filter(Boolean);
}
get config() {
return this._parsedConfig;
}
convertName(node, options) {
const useTypesPrefix = typeof (options && options.useTypesPrefix) === 'boolean' ? options.useTypesPrefix : true;
const useTypesSuffix = typeof (options && options.useTypesSuffix) === 'boolean' ? options.useTypesSuffix : true;
let convertedName = '';
if (useTypesPrefix) {
convertedName += this.config.typesPrefix;
}
convertedName += this.config.convert(node, options);
if (useTypesSuffix) {
convertedName += this.config.typesSuffix;
}
return convertedName;
}
getOperationSuffix(node, operationType) {
const { omitOperationSuffix = false, dedupeOperationSuffix = false } = this.config;
const operationName = typeof node === 'string' ? node : node.name ? node.name.value : '';
return omitOperationSuffix
? ''
: dedupeOperationSuffix && operationName.toLowerCase().endsWith(operationType.toLowerCase())
? ''
: operationType;
}
getFragmentSuffix(node) {
return this.getOperationSuffix(node, 'Fragment');
}
getFragmentName(node) {
return this.convertName(node, {
suffix: this.getFragmentSuffix(node),
useTypesPrefix: false,
});
}
getFragmentVariableName(node) {
const { omitOperationSuffix = false, dedupeOperationSuffix = false, fragmentVariableSuffix = 'FragmentDoc', fragmentVariablePrefix = '', } = this.config;
const fragmentName = typeof node === 'string' ? node : node.name.value;
const suffix = omitOperationSuffix
? ''
: dedupeOperationSuffix &&
fragmentName.toLowerCase().endsWith('fragment') &&
fragmentVariableSuffix.toLowerCase().startsWith('fragment')
? fragmentVariableSuffix.substring('fragment'.length)
: fragmentVariableSuffix;
return this.convertName(node, {
prefix: fragmentVariablePrefix,
suffix,
useTypesPrefix: false,
});
}
getPunctuation(_declarationKind) {
return '';
}
}
class OperationVariablesToObject {
constructor(_scalars, _convertName, _namespacedImportName = null, _enumNames = [], _enumPrefix = true, _enumValues = {}, _applyCoercion = false, _directiveArgumentAndInputFieldMappings = {}) {
this._scalars = _scalars;
this._convertName = _convertName;
this._namespacedImportName = _namespacedImportName;
this._enumNames = _enumNames;
this._enumPrefix = _enumPrefix;
this._enumValues = _enumValues;
this._applyCoercion = _applyCoercion;
this._directiveArgumentAndInputFieldMappings = _directiveArgumentAndInputFieldMappings;
autoBind(this);
}
getName(node) {
if (node.name) {
if (typeof node.name === 'string') {
return node.name;
}
return node.name.value;
}
else if (node.variable) {
return node.variable.name.value;
}
return null;
}
transform(variablesNode) {
if (!variablesNode || variablesNode.length === 0) {
return null;
}
return (variablesNode.map(variable => indent(this.transformVariable(variable))).join(`${this.getPunctuation()}\n`) +
this.getPunctuation());
}
getScalar(name) {
const prefix = this._namespacedImportName ? `${this._namespacedImportName}.` : '';
return `${prefix}Scalars['${name}']`;
}
getDirectiveMapping(name) {
return `DirectiveArgumentAndInputFieldMappings['${name}']`;
}
getDirectiveOverrideType(directives) {
if (!this._directiveArgumentAndInputFieldMappings)
return null;
const type = directives
.map(directive => {
const directiveName = directive.name.value;
if (this._directiveArgumentAndInputFieldMappings[directiveName]) {
return this.getDirectiveMapping(directiveName);
}
return null;
})
.reverse()
.find(a => !!a);
return type || null;
}
transformVariable(variable) {
let typeValue = null;
const prefix = this._namespacedImportName ? `${this._namespacedImportName}.` : '';
if (typeof variable.type === 'string') {
typeValue = variable.type;
}
else {
const baseType = getBaseTypeNode(variable.type);
const overrideType = variable.directives ? this.getDirectiveOverrideType(variable.directives) : null;
const typeName = baseType.name.value;
if (overrideType) {
typeValue = overrideType;
}
else if (this._scalars[typeName]) {
typeValue = this.getScalar(typeName);
}
else if (this._enumValues[typeName] && this._enumValues[typeName].sourceFile) {
typeValue = this._enumValues[typeName].typeIdentifier || this._enumValues[typeName].sourceIdentifier;
}
else {
typeValue = `${prefix}${this._convertName(baseType, {
useTypesPrefix: this._enumNames.includes(typeName) ? this._enumPrefix : true,
})}`;
}
}
const fieldName = this.getName(variable);
const fieldType = this.wrapAstTypeWithModifiers(typeValue, variable.type, this._applyCoercion);
const hasDefaultValue = variable.defaultValue != null && typeof variable.defaultValue !== 'undefined';
const isNonNullType = variable.type.kind === Kind.NON_NULL_TYPE;
const formattedFieldString = this.formatFieldString(fieldName, isNonNullType, hasDefaultValue);
const formattedTypeString = this.formatTypeString(fieldType, isNonNullType, hasDefaultValue);
return `${formattedFieldString}: ${formattedTypeString}`;
}
wrapAstTypeWithModifiers(_baseType, _typeNode, _applyCoercion) {
throw new Error(`You must override "wrapAstTypeWithModifiers" of OperationVariablesToObject!`);
}
formatFieldString(fieldName, isNonNullType, _hasDefaultValue) {
return fieldName;
}
formatTypeString(fieldType, isNonNullType, hasDefaultValue) {
const prefix = this._namespacedImportName ? `${this._namespacedImportName}.` : '';
if (hasDefaultValue) {
return `${prefix}Maybe<${fieldType}>`;
}
return fieldType;
}
getPunctuation() {
return ',';
}
}
class BaseTypesVisitor extends BaseVisitor {
constructor(_schema, rawConfig, additionalConfig, defaultScalars = DEFAULT_SCALARS) {
var _a;
super(rawConfig, {
enumPrefix: getConfigValue(rawConfig.enumPrefix, true),
onlyEnums: getConfigValue(rawConfig.onlyEnums, false),
onlyOperationTypes: getConfigValue(rawConfig.onlyOperationTypes, false),
addUnderscoreToArgsType: getConfigValue(rawConfig.addUnderscoreToArgsType, false),
enumValues: parseEnumValues({
schema: _schema,
mapOrStr: rawConfig.enumValues,
ignoreEnumValuesFromSchema: rawConfig.ignoreEnumValuesFromSchema,
}),
declarationKind: normalizeDeclarationKind(rawConfig.declarationKind),
scalars: buildScalarsFromConfig(_schema, rawConfig, defaultScalars),
fieldWrapperValue: getConfigValue(rawConfig.fieldWrapperValue, 'T'),
wrapFieldDefinitions: getConfigValue(rawConfig.wrapFieldDefinitions, false),
entireFieldWrapperValue: getConfigValue(rawConfig.entireFieldWrapperValue, 'T'),
wrapEntireDefinitions: getConfigValue(rawConfig.wrapEntireFieldDefinitions, false),
ignoreEnumValuesFromSchema: getConfigValue(rawConfig.ignoreEnumValuesFromSchema, false),
directiveArgumentAndInputFieldMappings: transformDirectiveArgumentAndInputFieldMappings((_a = rawConfig.directiveArgumentAndInputFieldMappings) !== null && _a !== void 0 ? _a : {}, rawConfig.directiveArgumentAndInputFieldMappingTypeSuffix),
...additionalConfig,
});
this._schema = _schema;
// Note: Missing directive mappers but not a problem since always overriden by implementors
this._argumentsTransformer = new OperationVariablesToObject(this.scalars, this.convertName);
}
getExportPrefix() {
return 'export ';
}
getFieldWrapperValue() {
if (this.config.fieldWrapperValue) {
return `${this.getExportPrefix()}type FieldWrapper<T> = ${this.config.fieldWrapperValue};`;
}
return '';
}
getEntireFieldWrapperValue() {
if (this.config.entireFieldWrapperValue) {
return `${this.getExportPrefix()}type EntireFieldWrapper<T> = ${this.config.entireFieldWrapperValue};`;
}
return '';
}
getScalarsImports() {
return Object.keys(this.config.scalars)
.map(enumName => {
const mappedValue = this.config.scalars[enumName];
if (mappedValue.isExternal) {
return this._buildTypeImport(mappedValue.import, mappedValue.source, mappedValue.default);
}
return null;
})
.filter(a => a);
}
getDirectiveArgumentAndInputFieldMappingsImports() {
return Object.keys(this.config.directiveArgumentAndInputFieldMappings)
.map(directive => {
const mappedValue = this.config.directiveArgumentAndInputFieldMappings[directive];
if (mappedValue.isExternal) {
return this._buildTypeImport(mappedValue.import, mappedValue.source, mappedValue.default);
}
return null;
})
.filter(a => a);
}
get scalarsDefinition() {
if (this.config.onlyEnums)
return '';
const allScalars = Object.keys(this.config.scalars).map(scalarName => {
const scalarValue = this.config.scalars[scalarName].type;
const scalarType = this._schema.getType(scalarName);
const comment = scalarType && scalarType.astNode && scalarType.description ? transformComment(scalarType.description, 1) : '';
const { scalar } = this._parsedConfig.declarationKind;
return comment + indent(`${scalarName}: ${scalarValue}${this.getPunctuation(scalar)}`);
});
return new DeclarationBlock(this._declarationBlockConfig)
.export()
.asKind(this._parsedConfig.declarationKind.scalar)
.withName('Scalars')
.withComment('All built-in and custom scalars, mapped to their actual values')
.withBlock(allScalars.join('\n')).string;
}
get directiveArgumentAndInputFieldMappingsDefinition() {
const directiveEntries = Object.entries(this.config.directiveArgumentAndInputFieldMappings);
if (directiveEntries.length === 0) {
return '';
}
const allDirectives = [];
for (const [directiveName, parsedMapper] of directiveEntries) {
const directiveType = this._schema.getDirective(directiveName);
const comment = (directiveType === null || directiveType === void 0 ? void 0 : directiveType.astNode) && directiveType.description ? transformComment(directiveType.description, 1) : '';
const { directive } = this._parsedConfig.declarationKind;
allDirectives.push(comment + indent(`${directiveName}: ${parsedMapper.type}${this.getPunctuation(directive)}`));
}
return new DeclarationBlock(this._declarationBlockConfig)
.export()
.asKind(this._parsedConfig.declarationKind.directive)
.withName('DirectiveArgumentAndInputFieldMappings')
.withComment('Type overrides using directives')
.withBlock(allDirectives.join('\n')).string;
}
setDeclarationBlockConfig(config) {
this._declarationBlockConfig = config;
}
setArgumentsTransformer(argumentsTransfomer) {
this._argumentsTransformer = argumentsTransfomer;
}
NonNullType(node) {
const asString = node.type;
return asString;
}
getInputObjectDeclarationBlock(node) {
return new DeclarationBlock(this._declarationBlockConfig)
.export()
.asKind(this._parsedConfig.declarationKind.input)
.withName(this.convertName(node))
.withComment(node.description)
.withBlock(node.fields.join('\n'));
}
InputObjectTypeDefinition(node) {
if (this.config.onlyEnums)
return '';
return this.getInputObjectDeclarationBlock(node).string;
}
InputValueDefinition(node) {
if (this.config.onlyEnums)
return '';
const comment = transformComment(node.description, 1);
const { input } = this._parsedConfig.declarationKind;
let type = node.type;
if (node.directives && this.config.directiveArgumentAndInputFieldMappings) {
type = this._getDirectiveOverrideType(node.directives) || type;
}
return comment + indent(`${node.name}: ${type}${this.getPunctuation(input)}`);
}
Name(node) {
return node.value;
}
FieldDefinition(node) {
if (this.config.onlyEnums)
return '';
const typeString = node.type;
const { type } = this._parsedConfig.declarationKind;
const comment = this.getNodeComment(node);
return comment + indent(`${node.name}: ${typeString}${this.getPunctuation(type)}`);
}
UnionTypeDefinition(node, key, parent) {
if (this.config.onlyOperationTypes || this.config.onlyEnums)
return '';
const originalNode = parent[key];
const possibleTypes = originalNode.types
.map(t => (this.scalars[t.name.value] ? this._getScalar(t.name.value) : this.convertName(t)))
.join(' | ');
return new DeclarationBlock(this._declarationBlockConfig)
.export()
.asKind('type')
.withName(this.convertName(node))
.withComment(node.description)
.withContent(possibleTypes).string;
}
mergeInterfaces(interfaces, hasOtherFields) {
return interfaces.join(' & ') + (interfaces.length && hasOtherFields ? ' & ' : '');
}
appendInterfacesAndFieldsToBlock(block, interfaces, fields) {
block.withContent(this.mergeInterfaces(interfaces, fields.length > 0));
block.withBlock(this.mergeAllFields(fields, interfaces.length > 0));
}
getObjectTypeDeclarationBlock(node, originalNode) {
const optionalTypename = this.config.nonOptionalTypename ? '__typename' : '__typename?';
const { type, interface: interfacesType } = this._parsedConfig.declarationKind;
const allFields = [
...(this.config.addTypename
? [
indent(`${this.config.immutableTypes ? 'readonly ' : ''}${optionalTypename}: '${node.name}'${this.getPunctuation(type)}`),
]
: []),
...node.fields,
];
const interfacesNames = originalNode.interfaces ? originalNode.interfaces.map(i => this.convertName(i)) : [];
const declarationBlock = new DeclarationBlock(this._declarationBlockConfig)
.export()
.asKind(type)
.withName(this.convertName(node))
.withComment(node.description);
if (type === 'interface' || type === 'class') {
if (interfacesNames.length > 0) {
const keyword = interfacesType === 'interface' && type === 'class' ? 'implements' : 'extends';
declarationBlock.withContent(`${keyword} ` + interfacesNames.join(', ') + (allFields.length > 0 ? ' ' : ' {}'));
}
declarationBlock.withBlock(this.mergeAllFields(allFields, false));
}
else {
this.appendInterfacesAndFieldsToBlock(declarationBlock, interfacesNames, allFields);
}
return declarationBlock;
}
mergeAllFields(allFields, _hasInterfaces) {
return allFields.join('\n');
}
ObjectTypeDefinition(node, key, parent) {
if (this.config.onlyOperationTypes || this.config.onlyEnums)
return '';
const originalNode = parent[key];
return [this.getObjectTypeDeclarationBlock(node, originalNode).string, this.buildArgumentsBlock(originalNode)]
.filter(f => f)
.join('\n\n');
}
getInterfaceTypeDeclarationBlock(node, _originalNode) {
const declarationBlock = new DeclarationBlock(this._declarationBlockConfig)
.export()
.asKind(this._parsedConfig.declarationKind.interface)
.withName(this.convertName(node))
.withComment(node.description);
return declarationBlock.withBlock(node.fields.join('\n'));
}
InterfaceTypeDefinition(node, key, parent) {
if (this.config.onlyOperationTypes || this.config.onlyEnums)
return '';
const originalNode = parent[key];
return [this.getInterfaceTypeDeclarationBlock(node, originalNode).string, this.buildArgumentsBlock(originalNode)]
.filter(f => f)
.join('\n\n');
}
ScalarTypeDefinition(_node) {
// We empty this because we handle scalars in a different way, see constructor.
return '';
}
_buildTypeImport(identifier, source, asDefault = false) {
const { useTypeImports } = this.config;
if (asDefault) {
if (useTypeImports) {
return `import type { default as ${identifier} } from '${source}';`;
}
return `import ${identifier} from '${source}';`;
}
return `import${useTypeImports ? ' type' : ''} { ${identifier} } from '${source}';`;
}
handleEnumValueMapper(typeIdentifier, importIdentifier, sourceIdentifier, sourceFile) {
const importStatement = this._buildTypeImport(importIdentifier || sourceIdentifier, sourceFile);
if (importIdentifier !== sourceIdentifier || sourceIdentifier !== typeIdentifier) {
return [importStatement, `import ${typeIdentifier} = ${sourceIdentifier};`];
}
return [importStatement];
}
getEnumsImports() {
return Object.keys(this.config.enumValues)
.flatMap(enumName => {
const mappedValue = this.config.enumValues[enumName];
if (mappedValu