@graphql-codegen/typescript
Version:
GraphQL Code Generator plugin for generating TypeScript types
323 lines (322 loc) • 17.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TsVisitor = exports.MAKE_INCREMENTAL_SIGNATURE = exports.MAKE_EMPTY_SIGNATURE = exports.MAKE_MAYBE_SIGNATURE = exports.MAKE_OPTIONAL_SIGNATURE = exports.EXACT_SIGNATURE = void 0;
const tslib_1 = require("tslib");
const visitor_plugin_common_1 = require("@graphql-codegen/visitor-plugin-common");
const auto_bind_1 = tslib_1.__importDefault(require("auto-bind"));
const graphql_1 = require("graphql");
const typescript_variables_to_object_js_1 = require("./typescript-variables-to-object.js");
exports.EXACT_SIGNATURE = `type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };`;
exports.MAKE_OPTIONAL_SIGNATURE = `type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };`;
exports.MAKE_MAYBE_SIGNATURE = `type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };`;
exports.MAKE_EMPTY_SIGNATURE = `type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };`;
exports.MAKE_INCREMENTAL_SIGNATURE = `type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };`;
class TsVisitor extends visitor_plugin_common_1.BaseTypesVisitor {
constructor(schema, pluginConfig, additionalConfig = {}) {
super(schema, pluginConfig, {
noExport: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.noExport, false),
avoidOptionals: (0, visitor_plugin_common_1.normalizeAvoidOptionals)((0, visitor_plugin_common_1.getConfigValue)(pluginConfig.avoidOptionals, false)),
maybeValue: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.maybeValue, 'T | null'),
inputMaybeValue: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.inputMaybeValue, (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.maybeValue, 'Maybe<T>')),
constEnums: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.constEnums, false),
enumsAsTypes: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.enumsAsTypes, false),
futureProofEnums: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.futureProofEnums, false),
futureProofUnions: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.futureProofUnions, false),
enumsAsConst: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.enumsAsConst, false),
numericEnums: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.numericEnums, false),
onlyEnums: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.onlyEnums, false),
onlyOperationTypes: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.onlyOperationTypes, false),
immutableTypes: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.immutableTypes, false),
useImplementingTypes: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.useImplementingTypes, false),
entireFieldWrapperValue: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.entireFieldWrapperValue, 'T'),
wrapEntireDefinitions: (0, visitor_plugin_common_1.getConfigValue)(pluginConfig.wrapEntireFieldDefinitions, false),
...additionalConfig,
});
(0, auto_bind_1.default)(this);
const enumNames = Object.values(schema.getTypeMap())
.filter(graphql_1.isEnumType)
.map(type => type.name);
this.setArgumentsTransformer(new typescript_variables_to_object_js_1.TypeScriptOperationVariablesToObject(this.scalars, this.convertName, this.config.avoidOptionals, this.config.immutableTypes, null, enumNames, pluginConfig.enumPrefix, pluginConfig.enumSuffix, this.config.enumValues, false, this.config.directiveArgumentAndInputFieldMappings, 'InputMaybe'));
this.setDeclarationBlockConfig({
enumNameValueSeparator: ' =',
ignoreExport: this.config.noExport,
});
}
_getTypeForNode(node, isVisitingInputType) {
const typeAsString = node.name;
if (this.config.useImplementingTypes) {
const allTypesMap = this._schema.getTypeMap();
const implementingTypes = [];
// TODO: Move this to a better place, since we are using this logic in some other places as well.
for (const graphqlType of Object.values(allTypesMap)) {
if (graphqlType instanceof graphql_1.GraphQLObjectType) {
const allInterfaces = graphqlType.getInterfaces();
if (allInterfaces.some(int => typeAsString === int.name)) {
implementingTypes.push(this.convertName(graphqlType.name));
}
}
}
if (implementingTypes.length > 0) {
return implementingTypes.join(' | ');
}
}
const typeString = super._getTypeForNode(node, isVisitingInputType);
const schemaType = this._schema.getType(node.name);
if ((0, graphql_1.isEnumType)(schemaType)) {
// futureProofEnums + enumsAsTypes combination adds the future value to the enum type itself
// so it's not necessary to repeat it in the usage
const futureProofEnumUsageEnabled = this.config.futureProofEnums === true && this.config.enumsAsTypes !== true;
if (futureProofEnumUsageEnabled && this.config.allowEnumStringTypes === true) {
return `${typeString} | '%future added value' | ` + '`${' + typeString + '}`';
}
if (futureProofEnumUsageEnabled) {
return `${typeString} | '%future added value'`;
}
if (this.config.allowEnumStringTypes === true) {
return `${typeString} | ` + '`${' + typeString + '}`';
}
}
return typeString;
}
getWrapperDefinitions() {
if (this.config.onlyEnums)
return [];
const definitions = [
this.getMaybeValue(),
this.getInputMaybeValue(),
this.getExactDefinition(),
this.getMakeOptionalDefinition(),
this.getMakeMaybeDefinition(),
this.getMakeEmptyDefinition(),
this.getIncrementalDefinition(),
];
if (this.config.wrapFieldDefinitions) {
definitions.push(this.getFieldWrapperValue());
}
if (this.config.wrapEntireDefinitions) {
definitions.push(this.getEntireFieldWrapperValue());
}
return definitions;
}
getExactDefinition() {
if (this.config.onlyEnums)
return '';
return `${this.getExportPrefix()}${exports.EXACT_SIGNATURE}`;
}
getMakeOptionalDefinition() {
return `${this.getExportPrefix()}${exports.MAKE_OPTIONAL_SIGNATURE}`;
}
getMakeMaybeDefinition() {
if (this.config.onlyEnums)
return '';
return `${this.getExportPrefix()}${exports.MAKE_MAYBE_SIGNATURE}`;
}
getMakeEmptyDefinition() {
return `${this.getExportPrefix()}${exports.MAKE_EMPTY_SIGNATURE}`;
}
getIncrementalDefinition() {
return `${this.getExportPrefix()}${exports.MAKE_INCREMENTAL_SIGNATURE}`;
}
getMaybeValue() {
return `${this.getExportPrefix()}type Maybe<T> = ${this.config.maybeValue};`;
}
getInputMaybeValue() {
return `${this.getExportPrefix()}type InputMaybe<T> = ${this.config.inputMaybeValue};`;
}
clearOptional(str) {
if (str.startsWith('Maybe')) {
return str.replace(/Maybe<(.*?)>$/, '$1');
}
if (str.startsWith('InputMaybe')) {
return str.replace(/InputMaybe<(.*?)>$/, '$1');
}
return str;
}
getExportPrefix() {
if (this.config.noExport) {
return '';
}
return super.getExportPrefix();
}
getMaybeWrapper(ancestors) {
const currentVisitContext = this.getVisitorKindContextFromAncestors(ancestors);
const isInputContext = currentVisitContext.includes(graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION);
return isInputContext ? 'InputMaybe' : 'Maybe';
}
NamedType(node, key, parent, path, ancestors) {
return `${this.getMaybeWrapper(ancestors)}<${super.NamedType(node, key, parent, path, ancestors)}>`;
}
ListType(node, key, parent, path, ancestors) {
return `${this.getMaybeWrapper(ancestors)}<${super.ListType(node, key, parent, path, ancestors)}>`;
}
UnionTypeDefinition(node, key, parent) {
if (this.config.onlyOperationTypes || this.config.onlyEnums)
return '';
let withFutureAddedValue = [];
if (this.config.futureProofUnions) {
withFutureAddedValue = [
this.config.immutableTypes ? `{ readonly __typename?: "%other" }` : `{ __typename?: "%other" }`,
];
}
const originalNode = parent[key];
const possibleTypes = originalNode.types
.map(t => (this.scalars[t.name.value] ? this._getScalar(t.name.value, 'output') : this.convertName(t)))
.concat(...withFutureAddedValue)
.join(' | ');
return new visitor_plugin_common_1.DeclarationBlock(this._declarationBlockConfig)
.export()
.asKind('type')
.withName(this.convertName(node))
.withComment(node.description)
.withContent(possibleTypes).string;
// return super.UnionTypeDefinition(node, key, parent).concat(withFutureAddedValue).join("");
}
wrapWithListType(str) {
return `${this.config.immutableTypes ? 'ReadonlyArray' : 'Array'}<${str}>`;
}
NonNullType(node) {
const baseValue = super.NonNullType(node);
return this.clearOptional(baseValue);
}
FieldDefinition(node, key, parent) {
const typeString = this.config.wrapEntireDefinitions
? `EntireFieldWrapper<${node.type}>`
: node.type;
const originalFieldNode = parent[key];
const addOptionalSign = !this.config.avoidOptionals.field && originalFieldNode.type.kind !== graphql_1.Kind.NON_NULL_TYPE;
const comment = this.getNodeComment(node);
const { type } = this.config.declarationKind;
return (comment +
(0, visitor_plugin_common_1.indent)(`${this.config.immutableTypes ? 'readonly ' : ''}${node.name}${addOptionalSign ? '?' : ''}: ${typeString}${this.getPunctuation(type)}`));
}
InputValueDefinition(node, key, parent, _path, ancestors) {
const originalFieldNode = parent[key];
const addOptionalSign = !this.config.avoidOptionals.inputValue &&
(originalFieldNode.type.kind !== graphql_1.Kind.NON_NULL_TYPE ||
(!this.config.avoidOptionals.defaultValue && node.defaultValue !== undefined));
const comment = this.getNodeComment(node);
const declarationKind = this.config.declarationKind.type;
let type = node.type;
if (node.directives && this.config.directiveArgumentAndInputFieldMappings) {
type = this._getDirectiveOverrideType(node.directives) || type;
}
const readonlyPrefix = this.config.immutableTypes ? 'readonly ' : '';
const buildFieldDefinition = (isOneOf = false) => {
return `${readonlyPrefix}${node.name}${addOptionalSign && !isOneOf ? '?' : ''}: ${isOneOf ? this.clearOptional(type) : type}${this.getPunctuation(declarationKind)}`;
};
const realParentDef = ancestors?.[ancestors.length - 1];
if (realParentDef) {
const parentType = this._schema.getType(realParentDef.name.value);
if ((0, visitor_plugin_common_1.isOneOfInputObjectType)(parentType)) {
if (originalFieldNode.type.kind === graphql_1.Kind.NON_NULL_TYPE) {
throw new Error('Fields on an input object type can not be non-nullable. It seems like the schema was not validated.');
}
const fieldParts = [];
for (const fieldName of Object.keys(parentType.getFields())) {
// Why the heck is node.name a string and not { value: string } at runtime ?!
if (fieldName === node.name) {
fieldParts.push(buildFieldDefinition(true));
continue;
}
fieldParts.push(`${readonlyPrefix}${fieldName}?: never;`);
}
return comment + (0, visitor_plugin_common_1.indent)(`{ ${fieldParts.join(' ')} }`);
}
}
return comment + (0, visitor_plugin_common_1.indent)(buildFieldDefinition());
}
EnumTypeDefinition(node) {
const enumName = node.name;
// In case of mapped external enum string
if (this.config.enumValues[enumName]?.sourceFile) {
return `export { ${this.config.enumValues[enumName].typeIdentifier} };\n`;
}
const getValueFromConfig = (enumValue) => {
if (typeof this.config.enumValues[enumName]?.mappedValues?.[enumValue] !== 'undefined') {
return this.config.enumValues[enumName].mappedValues[enumValue];
}
return null;
};
const withFutureAddedValue = [
this.config.futureProofEnums ? [(0, visitor_plugin_common_1.indent)('| ' + (0, visitor_plugin_common_1.wrapWithSingleQuotes)('%future added value'))] : [],
];
const enumTypeName = this.convertName(node, {
useTypesPrefix: this.config.enumPrefix,
useTypesSuffix: this.config.enumSuffix,
});
if (this.config.enumsAsTypes) {
return new visitor_plugin_common_1.DeclarationBlock(this._declarationBlockConfig)
.export()
.asKind('type')
.withComment(node.description)
.withName(enumTypeName)
.withContent('\n' +
node.values
.map(enumOption => {
const name = enumOption.name;
const enumValue = getValueFromConfig(name) ?? name;
const comment = (0, visitor_plugin_common_1.transformComment)(enumOption.description, 1);
return comment + (0, visitor_plugin_common_1.indent)('| ' + (0, visitor_plugin_common_1.wrapWithSingleQuotes)(enumValue));
})
.concat(...withFutureAddedValue)
.join('\n')).string;
}
if (this.config.numericEnums) {
const block = new visitor_plugin_common_1.DeclarationBlock(this._declarationBlockConfig)
.export()
.withComment(node.description)
.withName(enumTypeName)
.asKind('enum')
.withBlock(node.values
.map((enumOption, i) => {
const valueFromConfig = getValueFromConfig(enumOption.name);
const enumValue = valueFromConfig ?? i;
const comment = (0, visitor_plugin_common_1.transformComment)(enumOption.description, 1);
const optionName = this.makeValidEnumIdentifier(this.convertName(enumOption, {
useTypesPrefix: false,
transformUnderscore: true,
}));
return comment + (0, visitor_plugin_common_1.indent)(optionName) + ` = ${enumValue}`;
})
.concat(...withFutureAddedValue)
.join(',\n')).string;
return block;
}
if (this.config.enumsAsConst) {
const typeName = `export type ${enumTypeName} = typeof ${enumTypeName}[keyof typeof ${enumTypeName}];`;
const enumAsConst = new visitor_plugin_common_1.DeclarationBlock({
...this._declarationBlockConfig,
blockTransformer: block => {
return block + ' as const';
},
})
.export()
.asKind('const')
.withName(enumTypeName)
.withComment(node.description)
.withBlock(node.values
.map(enumOption => {
const optionName = this.makeValidEnumIdentifier(this.convertName(enumOption, {
useTypesPrefix: false,
transformUnderscore: true,
}));
const comment = (0, visitor_plugin_common_1.transformComment)(enumOption.description, 1);
const name = enumOption.name;
const enumValue = getValueFromConfig(name) ?? name;
return comment + (0, visitor_plugin_common_1.indent)(`${optionName}: ${(0, visitor_plugin_common_1.wrapWithSingleQuotes)(enumValue)}`);
})
.join(',\n')).string;
return [enumAsConst, typeName].join('\n');
}
return new visitor_plugin_common_1.DeclarationBlock(this._declarationBlockConfig)
.export()
.asKind(this.config.constEnums ? 'const enum' : 'enum')
.withName(enumTypeName)
.withComment(node.description)
.withBlock(this.buildEnumValuesBlock(enumName, node.values)).string;
}
getPunctuation(_declarationKind) {
return ';';
}
}
exports.TsVisitor = TsVisitor;