@graphql-codegen/typescript-type-graphql
Version:
GraphQL Code Generator plugin for generating TypeGraphQL compatible TypeScript types
315 lines (314 loc) • 14.3 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeGraphQLVisitor = 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_1 = require("@graphql-codegen/typescript");
const MAYBE_REGEX = /^Maybe<(.*?)>$/;
const ARRAY_REGEX = /^Array<(.*?)>$/;
const SCALAR_REGEX = /^Scalars\['(.*?)'\]$/;
const GRAPHQL_TYPES = ['Query', 'Mutation', 'Subscription'];
const SCALARS = ['ID', 'String', 'Boolean', 'Int', 'Float'];
const TYPE_GRAPHQL_SCALARS = ['ID', 'Int', 'Float'];
function escapeString(str) {
return ("'" +
String(str || '')
.replace(/\\/g, '\\\\')
.replace(/\n/g, '\\n')
.replace(/'/g, "\\'") +
"'");
}
function formatDecoratorOptions(options, isFirstArgument = true) {
if (!Object.keys(options).length) {
return '';
}
return ((isFirstArgument ? '' : ', ') +
('{ ' +
Object.entries(options)
.map(([key, value]) => `${key}: ${value}`)
.join(', ') +
' }'));
}
const FIX_DECORATOR_SIGNATURE = `type FixDecorator<T> = T;`;
function getTypeGraphQLNullableValue(type) {
if (type.isNullable) {
if (type.isItemsNullable) {
return "'itemsAndList'";
}
return 'true';
}
if (type.isItemsNullable) {
return "'items'";
}
return undefined;
}
class TypeGraphQLVisitor extends typescript_1.TsVisitor {
constructor(schema, pluginConfig, additionalConfig = {}) {
super(schema, pluginConfig, {
avoidOptionals: pluginConfig.avoidOptionals || false,
maybeValue: pluginConfig.maybeValue || 'T | null',
constEnums: pluginConfig.constEnums || false,
enumsAsTypes: pluginConfig.enumsAsTypes || false,
immutableTypes: pluginConfig.immutableTypes || false,
declarationKind: {
type: 'class',
interface: 'abstract class',
arguments: 'class',
input: 'class',
scalar: 'type',
},
decoratorName: {
type: 'ObjectType',
interface: 'InterfaceType',
arguments: 'ArgsType',
field: 'Field',
input: 'InputType',
...pluginConfig.decoratorName,
},
decorateTypes: pluginConfig.decorateTypes || undefined,
...additionalConfig,
});
(0, auto_bind_1.default)(this);
this.typescriptVisitor = new typescript_1.TsVisitor(schema, pluginConfig, additionalConfig);
const enumNames = Object.values(schema.getTypeMap())
.map(type => (type instanceof graphql_1.GraphQLEnumType ? type.name : undefined))
.filter(t => t);
this.setArgumentsTransformer(new typescript_1.TypeScriptOperationVariablesToObject(this.scalars, this.convertName, this.config.avoidOptionals, this.config.immutableTypes, null, enumNames, this.config.enumPrefix, this.config.enumValues, undefined, undefined, 'Maybe'));
this.setDeclarationBlockConfig({
enumNameValueSeparator: ' =',
});
}
getDecoratorOptions(node) {
const decoratorOptions = {};
if (node.description) {
// Add description as TypeGraphQL description instead of comment
decoratorOptions.description = escapeString(node.description);
node.description = undefined;
}
return decoratorOptions;
}
getWrapperDefinitions() {
return [...super.getWrapperDefinitions(), this.getFixDecoratorDefinition()];
}
getFixDecoratorDefinition() {
return `${this.getExportPrefix()}${FIX_DECORATOR_SIGNATURE}`;
}
getMaybeWrapper() {
return 'Maybe';
}
buildArgumentsBlock(node) {
const fieldsWithArguments = node.fields.filter(field => field.arguments && field.arguments.length > 0) || [];
return fieldsWithArguments
.map(field => {
const name = node.name.value +
(this.config.addUnderscoreToArgsType ? '_' : '') +
this.convertName(field, {
useTypesPrefix: false,
useTypesSuffix: false,
}) +
'Args';
if (this.hasTypeDecorators(name)) {
return this.getArgumentsObjectTypeDefinition(node, name, field);
}
return this.typescriptVisitor.getArgumentsObjectTypeDefinition(node, name, field);
})
.join('\n\n');
}
ObjectTypeDefinition(node, key, parent) {
const isGraphQLType = GRAPHQL_TYPES.includes(node.name);
if (!isGraphQLType && !this.hasTypeDecorators(node.name)) {
return this.typescriptVisitor.ObjectTypeDefinition(node, key, parent);
}
const typeDecorator = this.config.decoratorName.type;
const originalNode = parent[key];
const decoratorOptions = this.getDecoratorOptions(node);
let declarationBlock;
if (isGraphQLType) {
declarationBlock = this.typescriptVisitor.getObjectTypeDeclarationBlock(node, originalNode);
}
else {
declarationBlock = this.getObjectTypeDeclarationBlock(node, originalNode);
// Add type-graphql ObjectType decorator
const interfaces = originalNode.interfaces.map(i => this.convertName(i));
if (interfaces.length > 1) {
decoratorOptions.implements = `[${interfaces.join(', ')}]`;
}
else if (interfaces.length === 1) {
decoratorOptions.implements = interfaces[0];
}
declarationBlock = declarationBlock.withDecorator(`@TypeGraphQL.${typeDecorator}(${formatDecoratorOptions(decoratorOptions)})`);
}
return [declarationBlock.string, this.buildArgumentsBlock(originalNode)].filter(f => f).join('\n\n');
}
InputObjectTypeDefinition(node) {
if (!this.hasTypeDecorators(node.name)) {
return this.typescriptVisitor.InputObjectTypeDefinition(node);
}
const typeDecorator = this.config.decoratorName.input;
const decoratorOptions = this.getDecoratorOptions(node);
let declarationBlock = this.getInputObjectDeclarationBlock(node);
// Add type-graphql InputType decorator
declarationBlock = declarationBlock.withDecorator(`@TypeGraphQL.${typeDecorator}(${formatDecoratorOptions(decoratorOptions)})`);
return declarationBlock.string;
}
getArgumentsObjectDeclarationBlock(node, name, field) {
return new visitor_plugin_common_1.DeclarationBlock(this._declarationBlockConfig)
.export()
.asKind(this._parsedConfig.declarationKind.arguments)
.withName(this.convertName(name))
.withComment(node.description)
.withBlock(field.arguments.map(argument => this.InputValueDefinition(argument)).join('\n'));
}
getArgumentsObjectTypeDefinition(node, name, field) {
const typeDecorator = this.config.decoratorName.arguments;
let declarationBlock = this.getArgumentsObjectDeclarationBlock(node, name, field);
// Add type-graphql Args decorator
declarationBlock = declarationBlock.withDecorator(`@TypeGraphQL.${typeDecorator}()`);
return declarationBlock.string;
}
InterfaceTypeDefinition(node, key, parent) {
if (!this.hasTypeDecorators(node.name)) {
return this.typescriptVisitor.InterfaceTypeDefinition(node, key, parent);
}
const interfaceDecorator = this.config.decoratorName.interface;
const originalNode = parent[key];
const decoratorOptions = this.getDecoratorOptions(node);
const declarationBlock = this.getInterfaceTypeDeclarationBlock(node, originalNode).withDecorator(`@TypeGraphQL.${interfaceDecorator}(${formatDecoratorOptions(decoratorOptions)})`);
return [declarationBlock.string, this.buildArgumentsBlock(originalNode)].filter(f => f).join('\n\n');
}
buildTypeString(type) {
if (!type.isArray && !type.isScalar && !type.isNullable) {
type.type = `FixDecorator<${type.type}>`;
}
if (type.isScalar) {
type.type = `Scalars['${type.type}']`;
}
if (type.isArray) {
type.type = `Array<${type.type}>`;
}
if (type.isNullable) {
type.type = `Maybe<${type.type}>`;
}
return type.type;
}
parseType(rawType) {
const typeNode = rawType;
if (typeNode.kind === 'NamedType') {
return {
type: typeNode.name.value,
isNullable: true,
isArray: false,
isItemsNullable: false,
isScalar: SCALARS.includes(typeNode.name.value),
};
}
if (typeNode.kind === 'NonNullType') {
return {
...this.parseType(typeNode.type),
isNullable: false,
};
}
if (typeNode.kind === 'ListType') {
return {
...this.parseType(typeNode.type),
isArray: true,
isNullable: true,
};
}
const isNullable = !!rawType.match(MAYBE_REGEX);
const nonNullableType = rawType.replace(MAYBE_REGEX, '$1');
const isArray = !!nonNullableType.match(ARRAY_REGEX);
const singularType = nonNullableType.replace(ARRAY_REGEX, '$1');
const isSingularTypeNullable = !!singularType.match(MAYBE_REGEX);
const singularNonNullableType = singularType.replace(MAYBE_REGEX, '$1');
const isScalar = !!singularNonNullableType.match(SCALAR_REGEX);
const type = singularNonNullableType.replace(SCALAR_REGEX, (match, type) => {
if (TYPE_GRAPHQL_SCALARS.includes(type)) {
// This is a TypeGraphQL type
return `TypeGraphQL.${type}`;
}
if (global[type]) {
// This is a JS native type
return type;
}
if (this.scalars[type]) {
// This is a type specified in the configuration
return this.scalars[type];
}
throw new Error(`Unknown scalar type ${type}`);
});
return { type, isNullable, isArray, isScalar, isItemsNullable: isArray && isSingularTypeNullable };
}
fixDecorator(type, typeString) {
return type.isArray || type.isNullable || type.isScalar ? typeString : `FixDecorator<${typeString}>`;
}
FieldDefinition(node, key, parent, path, ancestors) {
const parentName = ancestors === null || ancestors === void 0 ? void 0 : ancestors[ancestors.length - 1].name.value;
if (!this.hasTypeDecorators(parentName)) {
return this.typescriptVisitor.FieldDefinition(node, key, parent);
}
const fieldDecorator = this.config.decoratorName.field;
let typeString = node.type;
const type = this.parseType(typeString);
const decoratorOptions = this.getDecoratorOptions(node);
const nullableValue = getTypeGraphQLNullableValue(type);
if (nullableValue) {
decoratorOptions.nullable = nullableValue;
}
const decorator = '\n' +
(0, visitor_plugin_common_1.indent)(`@TypeGraphQL.${fieldDecorator}(type => ${type.isArray ? `[${type.type}]` : type.type}${formatDecoratorOptions(decoratorOptions, false)})`) +
'\n';
typeString = this.fixDecorator(type, typeString);
return (decorator +
(0, visitor_plugin_common_1.indent)(`${this.config.immutableTypes ? 'readonly ' : ''}${node.name}${type.isNullable ? '?' : '!'}: ${typeString};`));
}
InputValueDefinition(node, key, parent, path, ancestors) {
const parentName = ancestors === null || ancestors === void 0 ? void 0 : ancestors[ancestors.length - 1].name.value;
if (parent && !this.hasTypeDecorators(parentName)) {
return this.typescriptVisitor.InputValueDefinition(node, key, parent, path, ancestors);
}
const fieldDecorator = this.config.decoratorName.field;
const rawType = node.type;
const type = this.parseType(rawType);
const typeGraphQLType = type.isScalar && TYPE_GRAPHQL_SCALARS.includes(type.type) ? `TypeGraphQL.${type.type}` : type.type;
const decoratorOptions = this.getDecoratorOptions(node);
const nullableValue = getTypeGraphQLNullableValue(type);
if (nullableValue) {
decoratorOptions.nullable = nullableValue;
}
const decorator = '\n' +
(0, visitor_plugin_common_1.indent)(`@TypeGraphQL.${fieldDecorator}(type => ${type.isArray ? `[${typeGraphQLType}]` : typeGraphQLType}${formatDecoratorOptions(decoratorOptions, false)})`) +
'\n';
const nameString = node.name.kind ? node.name.value : node.name;
const typeString = rawType.kind
? this.buildTypeString(type)
: this.fixDecorator(type, rawType);
return (decorator +
(0, visitor_plugin_common_1.indent)(`${this.config.immutableTypes ? 'readonly ' : ''}${nameString}${type.isNullable ? '?' : '!'}: ${typeString};`));
}
EnumTypeDefinition(node) {
if (!this.hasTypeDecorators(node.name)) {
return this.typescriptVisitor.EnumTypeDefinition(node);
}
return (super.EnumTypeDefinition(node) +
`TypeGraphQL.registerEnumType(${this.convertName(node)}, { name: '${this.convertName(node)}' });\n`);
}
clearOptional(str) {
if (str.startsWith('Maybe')) {
return str.replace(/Maybe<(.*?)>$/, '$1');
}
return str;
}
hasTypeDecorators(typeName) {
if (GRAPHQL_TYPES.includes(typeName)) {
return false;
}
if (!this.config.decorateTypes) {
return true;
}
return this.config.decorateTypes.some(filter => filter === typeName);
}
}
exports.TypeGraphQLVisitor = TypeGraphQLVisitor;
;