@pothos/core
Version:
Pothos (formerly GiraphQL) is a plugin based schema builder for creating code-first GraphQL schemas in typescript
139 lines (138 loc) • 5.42 kB
JavaScript
import { PothosSchemaError } from '../errors.js';
import { unwrapInputFieldType } from './params.js';
export function resolveInputTypeConfig(type, buildCache) {
if (type.kind === "List") {
return resolveInputTypeConfig(type.type, buildCache);
}
const config = buildCache.getTypeConfig(type.ref);
if (config.kind === "Enum" || config.kind === "Scalar" || config.kind === "InputObject") {
return config;
}
throw new PothosSchemaError(`Unexpected config type ${config.kind} for input ref ${String(type.ref)}`);
}
export function mapInputFields(inputs, buildCache, mapper, cache = new Map()) {
const filterMappings = new Map();
const hasMappings = new Map();
return filterMapped(internalMapInputFields(inputs, buildCache, mapper, cache));
function filterMapped(map) {
if (filterMappings.has(map)) {
return filterMappings.get(map);
}
const filtered = new Map();
filterMappings.set(map, filtered);
map.forEach((mapping, fieldName) => {
if (mapping.kind === "Enum" || mapping.kind === "Scalar") {
filtered.set(fieldName, mapping);
return;
}
const hasNestedMappings = mapping.fields.map ? checkForMappings(mapping.fields.map, hasMappings) : false;
if (mapping.value !== null || hasNestedMappings) {
const filteredTypeFields = mapping.fields.map ? filterMapped(mapping.fields.map) : null;
const mappingForType = {
...mapping,
fields: {
configs: mapping.fields.configs,
map: filteredTypeFields
}
};
filtered.set(fieldName, mappingForType);
}
});
return filtered.size > 0 ? filtered : null;
}
function checkForMappings(map, hasMappings) {
if (hasMappings.has(map)) {
return hasMappings.get(map);
}
hasMappings.set(map, false);
let result = false;
for (const mapping of map.values()) {
if (mapping.value !== null) {
result = true;
}
else if (mapping.kind === "InputObject" && mapping.fields.map && checkForMappings(mapping.fields.map, hasMappings)) {
result = true;
}
}
hasMappings.set(map, result);
return result;
}
}
function internalMapInputFields(inputs, buildCache, mapper, seenTypes) {
const map = new Map();
for (const [fieldName, inputField] of Object.entries(inputs)) {
const typeConfig = resolveInputTypeConfig(inputField.type, buildCache);
const fieldMapping = mapper(inputField);
if (typeConfig.kind === "Enum" || typeConfig.kind === "Scalar") {
if (fieldMapping !== null) {
map.set(fieldName, {
kind: typeConfig.kind,
isList: inputField.type.kind === "List",
listDepth: getListDepth(inputField.type),
config: inputField,
value: fieldMapping
});
}
continue;
}
const inputFieldConfigs = buildCache.getInputTypeFieldConfigs(unwrapInputFieldType(inputField.type));
if (!seenTypes.has(typeConfig.name)) {
const typeEntry = {
configs: inputFieldConfigs,
map: new Map()
};
seenTypes.set(typeConfig.name, typeEntry);
typeEntry.map = internalMapInputFields(inputFieldConfigs, buildCache, mapper, seenTypes);
}
const typeFields = seenTypes.get(typeConfig.name);
map.set(fieldName, {
kind: typeConfig.kind,
isList: inputField.type.kind === "List",
listDepth: getListDepth(inputField.type),
config: inputField,
value: fieldMapping,
fields: typeFields
});
}
return map;
}
export function createInputValueMapper(argMap, mapValue) {
return function mapObject(obj, map = argMap, ...args) {
const mapped = {
...obj
};
map.forEach((field, fieldName) => {
let fieldVal = obj[fieldName];
if (fieldVal === null || fieldVal === undefined) {
return;
}
if (field.kind === "InputObject" && field.fields.map) {
fieldVal = mapListValue(fieldVal, field.listDepth, (val) => val && mapObject(val, field.fields.map, ...args));
mapped[fieldName] = fieldVal;
}
if (field.kind !== "InputObject" || field.value !== null) {
mapped[fieldName] = mapListValue(fieldVal, field.listDepth, (val) => val == null ? val : mapValue(val, field, ...args));
}
});
return mapped;
};
}
function getListDepth(type) {
let depth = 0;
let current = type;
while (current.kind === "List") {
depth++;
current = current.type;
}
return depth;
}
function mapListValue(value, listDepth, mapper) {
if (listDepth === 0) {
return mapper(value);
}
if (!Array.isArray(value)) {
return value;
}
return value.map((item) => listDepth > 1 ? mapListValue(item, listDepth - 1, mapper) : mapper(item));
}
//# sourceMappingURL=input.js.map