@pothos/core
Version:
Pothos (formerly GiraphQL) is a plugin based schema builder for creating code-first GraphQL schemas in typescript
517 lines (516 loc) • 20.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "BuildCache", {
enumerable: true,
get: function() {
return BuildCache;
}
});
const _graphql = require("graphql");
const _errors = require("./errors");
const _plugins = require("./plugins");
const _builtinscalar = require("./refs/builtin-scalar");
const _types = require("./types");
const _utils = require("./utils");
function _define_property(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
class BuildCache {
getTypeConfig(ref, kind) {
const baseConfig = this.configStore.getTypeConfig(ref, kind);
if (!this.typeConfigs.has(baseConfig.name)) {
this.typeConfigs.set(baseConfig.name, this.plugin.onTypeConfig(baseConfig));
}
const typeConfig = this.typeConfigs.get(baseConfig.name);
return typeConfig;
}
getInputTypeFieldConfigs(ref) {
const typeConfig = this.getTypeConfig(ref, 'InputObject');
const builtType = this.types.get(typeConfig.name);
if (!builtType) {
throw new _errors.PothosSchemaError(`Input type ${typeConfig.name} has not been built yet`);
}
const fields = builtType.getFields();
const fieldConfigs = {};
for (const fieldName of Object.keys(fields)){
var _fields_fieldName_extensions;
fieldConfigs[fieldName] = (_fields_fieldName_extensions = fields[fieldName].extensions) === null || _fields_fieldName_extensions === void 0 ? void 0 : _fields_fieldName_extensions.pothosConfig;
}
return fieldConfigs;
}
getImplementers(iface) {
if (this.implementers.has(iface.name)) {
return this.implementers.get(iface.name);
}
const implementers = [
...this.configStore.typeConfigs.values()
].filter((config)=>config.kind === 'Object' && config.interfaces.find((i)=>this.configStore.getTypeConfig(i).name === iface.name));
this.implementers.set(iface.name, implementers);
return implementers;
}
buildAll() {
this.configStore.prepareForBuild();
for (const config of this.configStore.typeConfigs.values()){
if (config.kind === 'Enum' || config.kind === 'Scalar') {
this.buildTypeFromConfig(config);
}
}
for (const config of this.configStore.typeConfigs.values()){
if (config.kind === 'InputObject') {
this.buildTypeFromConfig(config);
}
}
for (const type of this.types.values()){
if (type instanceof _graphql.GraphQLInputObjectType) {
type.getFields();
}
}
for (const config of this.configStore.typeConfigs.values()){
if (config.kind === 'Interface') {
this.buildTypeFromConfig(config);
}
}
for (const config of this.configStore.typeConfigs.values()){
if (config.kind === 'Object') {
this.buildTypeFromConfig(config);
}
}
for (const config of this.configStore.typeConfigs.values()){
if (config.kind === 'Union') {
this.buildTypeFromConfig(config);
}
}
for (const config of this.configStore.typeConfigs.values()){
if (config.kind === 'Query' || config.kind === 'Mutation' || config.kind === 'Subscription') {
this.buildTypeFromConfig(config);
}
}
for (const type of this.types.values()){
if (type instanceof _graphql.GraphQLObjectType || type instanceof _graphql.GraphQLInterfaceType) {
type.getFields();
} else if (type instanceof _graphql.GraphQLUnionType) {
type.getTypes();
}
}
}
buildTypeFromConfig(baseConfig) {
const config = this.getTypeConfig(baseConfig.name);
const { name } = config;
this.typeConfigs.set(name, config);
switch(config.kind){
case 'Enum':
this.addType(name, this.buildEnum(config));
break;
case 'InputObject':
this.addType(name, this.buildInputObject(config));
break;
case 'Interface':
this.addType(name, this.buildInterface(config));
break;
case 'Scalar':
this.addType(name, this.buildScalar(config));
break;
case 'Union':
this.addType(name, this.buildUnion(config));
break;
case 'Object':
case 'Query':
case 'Mutation':
case 'Subscription':
this.addType(name, this.buildObject(config));
break;
default:
(0, _utils.assertNever)(config);
}
}
addType(ref, type) {
if (this.types.has(ref)) {
throw new _errors.PothosSchemaError(`reference or name has already been used to create another type (${type.name})`);
}
this.types.set(ref, type);
}
buildOutputTypeParam(type) {
if (type.kind === 'List') {
if (type.nullable) {
return new _graphql.GraphQLList(this.buildOutputTypeParam(type.type));
}
return new _graphql.GraphQLNonNull(new _graphql.GraphQLList(this.buildOutputTypeParam(type.type)));
}
if (type.nullable) {
return this.getOutputType(type.ref);
}
return new _graphql.GraphQLNonNull(this.getOutputType(type.ref));
}
buildInputTypeParam(type) {
if (type.kind === 'List') {
if (type.required) {
return new _graphql.GraphQLNonNull(new _graphql.GraphQLList(this.buildInputTypeParam(type.type)));
}
return new _graphql.GraphQLList(this.buildInputTypeParam(type.type));
}
if (type.required) {
return new _graphql.GraphQLNonNull(this.getInputType(type.ref));
}
return this.getInputType(type.ref);
}
buildFields(fields) {
const built = {};
for (const [fieldName, originalConfig] of fields){
if (!this.outputFieldConfigs.has(originalConfig)) {
this.outputFieldConfigs.set(originalConfig, this.plugin.onOutputFieldConfig(originalConfig));
}
const updatedConfig = this.outputFieldConfigs.get(originalConfig);
if (!updatedConfig) {
continue;
}
const config = {
...updatedConfig
};
const argMap = new Map();
for (const argName of Object.keys(config.args)){
argMap.set(argName, config.args[argName]);
}
const args = this.buildInputFields(argMap);
const argConfigs = {};
for (const argName of Object.keys(config.args)){
argConfigs[argName] = this.inputFieldConfigs.get(config.args[argName]);
}
config.args = argConfigs;
var _config_resolve;
const resolve = this.plugin.wrapResolve((_config_resolve = config.resolve) !== null && _config_resolve !== void 0 ? _config_resolve : _graphql.defaultFieldResolver, config);
const subscribe = this.plugin.wrapSubscribe(config.subscribe, config);
var _config_resolve1;
built[fieldName] = {
...config,
type: this.buildOutputTypeParam(config.type),
args,
extensions: {
...config.extensions,
pothosResolveWrapped: resolve !== ((_config_resolve1 = config.resolve) !== null && _config_resolve1 !== void 0 ? _config_resolve1 : _graphql.defaultFieldResolver),
pothosSubscribeWrapped: subscribe !== config.subscribe,
pothosOptions: config.pothosOptions,
pothosConfig: config,
pothosArgMappers: config.argMappers
},
resolve: resolve === _graphql.defaultFieldResolver ? undefined : resolve,
subscribe
};
}
return built;
}
buildInputFields(fields) {
const built = {};
for (const [fieldName, originalConfig] of fields){
if (!this.inputFieldConfigs.has(originalConfig)) {
this.inputFieldConfigs.set(originalConfig, this.plugin.onInputFieldConfig(originalConfig));
}
const config = this.inputFieldConfigs.get(originalConfig);
if (config) {
built[fieldName] = {
...config,
type: this.buildInputTypeParam(config.type),
extensions: {
...config.extensions,
pothosOptions: config.pothosOptions,
pothosConfig: config
}
};
}
}
return built;
}
getInterfaceFields(type) {
const interfaceFields = type.getInterfaces().reduce((all, iface)=>Object.assign(all, this.getFields(iface)), {});
const configs = this.configStore.getFields(type.name, 'Interface');
const fields = this.buildFields(configs);
return {
...interfaceFields,
...fields
};
}
getObjectFields(type) {
const interfaceFields = type.getInterfaces().reduce((all, iface)=>Object.assign(all, this.getFields(iface)), {});
const objectFields = this.buildFields(this.configStore.getFields(type.name, 'Object'));
return {
...interfaceFields,
...objectFields
};
}
getRootFields(type) {
return this.buildFields(this.configStore.getFields(type.name, 'Object'));
}
getFields(type) {
if (type instanceof _graphql.GraphQLObjectType) {
const config = this.configStore.getTypeConfig(type.name);
if (config.kind === 'Query' || config.kind === 'Mutation' || config.kind === 'Subscription') {
return this.getRootFields(type);
}
return this.getObjectFields(type);
}
if (type instanceof _graphql.GraphQLInterfaceType) {
return this.getInterfaceFields(type);
}
throw new _errors.PothosSchemaError(`Type ${type.name} does not have fields to resolve`);
}
getInputFields(type) {
return this.buildInputFields(this.configStore.getFields(type.name, 'InputObject'));
}
getType(ref) {
if (ref instanceof _builtinscalar.BuiltinScalarRef) {
return ref.type;
}
const typeConfig = this.configStore.getTypeConfig(ref);
const type = this.types.get(typeConfig.name);
if (!type) {
this.buildTypeFromConfig(typeConfig);
return this.types.get(typeConfig.name);
}
return type;
}
getOutputType(ref) {
const type = this.getType(ref);
if (type instanceof _graphql.GraphQLInputObjectType) {
throw new _errors.PothosSchemaError(`Expected ${String(ref)} to be an output type but it was defined as an InputObject`);
}
return type;
}
getInputType(ref) {
const type = this.getType(ref);
if (!type) {
throw new _errors.PothosSchemaError(`Missing implementation of for type ${String(ref)}`);
}
if (type instanceof _graphql.GraphQLObjectType) {
throw new _errors.PothosSchemaError(`Expected ${type.name} to be an input type but it was defined as a GraphQLObjectType`);
}
if (type instanceof _graphql.GraphQLInterfaceType) {
throw new _errors.PothosSchemaError(`Expected ${type.name} to be an input type but it was defined as a GraphQLInterfaceType`);
}
if (type instanceof _graphql.GraphQLUnionType) {
throw new _errors.PothosSchemaError(`Expected ${String(ref)} to be an input type but it was defined as an GraphQLUnionType`);
}
return type;
}
getTypeOfKind(ref, kind) {
const type = this.getType(ref);
switch(kind){
case 'Object':
case 'Query':
case 'Mutation':
case 'Subscription':
if (type instanceof _graphql.GraphQLObjectType) {
return type;
}
break;
case 'Interface':
if (type instanceof _graphql.GraphQLInterfaceType) {
return type;
}
break;
case 'Union':
if (type instanceof _graphql.GraphQLUnionType) {
return type;
}
break;
case 'Enum':
if (type instanceof _graphql.GraphQLEnumType) {
return type;
}
break;
case 'Scalar':
if (type instanceof _graphql.GraphQLScalarType) {
return type;
}
break;
case 'InputObject':
if (type instanceof _graphql.GraphQLScalarType) {
return type;
}
break;
default:
break;
}
throw new _errors.PothosSchemaError(`Expected ${String(ref)} to be of type ${kind}`);
}
buildObject(config) {
var _config_isTypeOf;
const type = new _graphql.GraphQLObjectType({
...config,
extensions: {
...config.extensions,
pothosOptions: config.pothosOptions,
pothosConfig: config
},
fields: ()=>this.getFields(type),
isTypeOf: config.kind === 'Object' ? this.plugin.wrapIsTypeOf((_config_isTypeOf = config.isTypeOf) !== null && _config_isTypeOf !== void 0 ? _config_isTypeOf : undefined, config) : undefined,
interfaces: config.kind === 'Object' ? ()=>config.interfaces.map((iface)=>this.getTypeOfKind(iface, 'Interface')) : undefined
});
return type;
}
buildInterface(config) {
const resolveType = (parent, context, info)=>{
const typeBrand = (0, _utils.getTypeBrand)(parent);
if (typeBrand) {
if (typeof typeBrand === 'string') {
return typeBrand;
}
return this.getTypeConfig(typeBrand).name;
}
var _config_resolveType;
const resolver = (_config_resolveType = config.resolveType) !== null && _config_resolveType !== void 0 ? _config_resolveType : _graphql.defaultTypeResolver;
return resolver(parent, context, info, type);
};
const type = new _graphql.GraphQLInterfaceType({
...config,
extensions: {
...config.extensions,
pothosOptions: config.pothosOptions,
pothosConfig: config
},
interfaces: ()=>config.interfaces.map((iface)=>this.getTypeOfKind(iface, 'Interface')),
fields: ()=>this.getFields(type),
resolveType: this.plugin.wrapResolveType(resolveType, config)
});
return type;
}
buildUnion(config) {
const resolveType = (parent, context, info, type)=>{
if (typeof parent === 'object' && parent !== null && _types.typeBrandKey in parent) {
const typeBrand = parent[_types.typeBrandKey];
if (typeof typeBrand === 'string') {
return typeBrand;
}
return this.getTypeConfig(typeBrand).name;
}
if (!config.resolveType) {
return (0, _graphql.defaultTypeResolver)(parent, context, info, type);
}
const resultOrPromise = config.resolveType(parent, context, info, type);
const getResult = (result)=>{
if (typeof result === 'string' || !result) {
return result;
}
if (result instanceof _graphql.GraphQLObjectType) {
return result.name;
}
try {
const typeConfig = this.configStore.getTypeConfig(result);
return typeConfig.name;
} catch {
// ignore
}
return result;
};
return (0, _utils.isThenable)(resultOrPromise) ? resultOrPromise.then(getResult) : getResult(resultOrPromise);
};
return new _graphql.GraphQLUnionType({
...config,
extensions: {
...config.extensions,
pothosOptions: config.pothosOptions,
pothosConfig: config
},
types: ()=>config.types.map((member)=>this.getTypeOfKind(member, 'Object')),
resolveType: this.plugin.wrapResolveType(resolveType, config)
});
}
buildInputObject(config) {
const type = new _graphql.GraphQLInputObjectType({
...config,
extensions: {
...config.extensions,
pothosOptions: config.pothosOptions,
pothosConfig: config
},
fields: ()=>this.getInputFields(type)
});
return type;
}
buildScalar(config) {
if (config.name === 'ID') {
return _graphql.GraphQLID;
}
if (config.name === 'Int') {
return _graphql.GraphQLInt;
}
if (config.name === 'Float') {
return _graphql.GraphQLFloat;
}
if (config.name === 'Boolean') {
return _graphql.GraphQLBoolean;
}
if (config.name === 'String') {
return _graphql.GraphQLString;
}
return new _graphql.GraphQLScalarType({
...config,
extensions: {
...config.extensions,
pothosOptions: config.pothosOptions,
pothosConfig: config
}
});
}
buildEnum(config) {
const values = {};
const configValues = typeof config.values === 'function' ? config.values() : config.values;
for (const key of Object.keys(config.values)){
const original = configValues[key];
if (!this.enumValueConfigs.has(original)) {
this.enumValueConfigs.set(original, this.plugin.onEnumValueConfig(original));
}
const valueConfig = this.enumValueConfigs.get(original);
if (valueConfig) {
values[key] = this.enumValueConfigs.get(original);
}
}
return new _graphql.GraphQLEnumType({
...config,
values,
extensions: {
...config.extensions,
pothosOptions: config.pothosOptions,
pothosConfig: config
}
});
}
constructor(builder, options){
_define_property(this, "types", new Map());
_define_property(this, "builder", void 0);
_define_property(this, "plugin", void 0);
_define_property(this, "options", void 0);
_define_property(this, "configStore", void 0);
_define_property(this, "pluginList", void 0);
_define_property(this, "implementers", new Map());
_define_property(this, "typeConfigs", new Map());
_define_property(this, "enumValueConfigs", new Map());
_define_property(this, "outputFieldConfigs", new Map());
_define_property(this, "inputFieldConfigs", new Map());
this.builder = builder;
this.configStore = builder.configStore;
this.options = options;
const plugins = {};
var _builder_options_plugins;
this.pluginList = ((_builder_options_plugins = builder.options.plugins) !== null && _builder_options_plugins !== void 0 ? _builder_options_plugins : []).map((pluginName)=>{
const Plugin = this.builder.constructor.plugins[pluginName];
if (!Plugin) {
throw new _errors.PothosError(`No plugin named ${pluginName} was registered`);
}
plugins[pluginName] = new Plugin(this, pluginName);
return plugins[pluginName];
});
this.plugin = new _plugins.MergedPlugins(this, this.pluginList);
}
}
//# sourceMappingURL=build-cache.js.map