UNPKG

@dossierhq/core

Version:

The core Dossier library used by clients and server alike, used to interact with schema and entities directly, as well as remotely through a client.

177 lines 7.94 kB
/// <reference types="./ContentTransformer.d.ts" /> import { notOk, ok } from '../ErrorResult.js'; import { contentValuePathToString } from './ContentPath.js'; import { isComponentItemField, isRichTextComponentNode, isRichTextItemField, isStringItemField, } from './ContentTypeUtils.js'; import { checkFieldItemTraversable, checkFieldTraversable } from './ContentUtils.js'; import { transformRichText } from './RichTextTransformer.js'; export const IDENTITY_TRANSFORMER = { transformField(_schema, _path, _fieldSpec, value) { return ok(value); }, transformFieldItem(_schema, _path, _fieldSpec, value) { return ok(value); }, transformRichTextNode(_schema, _path, _fieldSpec, node) { return ok(node); }, }; export function transformEntityFields(schema, path, entity, transformer, options) { const typeSpec = schema.getEntityTypeSpecification(entity.info.type); if (!typeSpec) { return notOk.BadRequest(`${contentValuePathToString(path)}: Couldn’t find spec for entity type ${entity.info.type}`); } const transformResult = transformContentFields(schema, path, typeSpec, 'entity', entity.fields, transformer, options); if (transformResult.isError()) return transformResult; if (transformResult.value === entity.fields) return ok(entity.fields); return ok(transformResult.value); } export function transformComponent(schema, path, component, transformer, options) { if (!component.type) { return notOk.BadRequest(`${contentValuePathToString([...path, 'type'])}: Missing a component type`); } const typeSpec = schema.getComponentTypeSpecification(component.type); if (!typeSpec) { return notOk.BadRequest(`${contentValuePathToString(path)}: Couldn’t find spec for component type ${component.type}`); } const transformResult = transformContentFields(schema, path, typeSpec, 'component', component, transformer, options); if (transformResult.isError()) return transformResult; if (transformResult.value === component) return ok(component); return ok({ ...transformResult.value, type: component.type }); } function transformContentFields(schema, path, typeSpec, kind, fields, transformer, options) { const extraFieldNames = new Set(Object.keys(fields)); if (kind === 'component') { extraFieldNames.delete('type'); } const excludeOmittedEntityFields = !!(options && 'excludeOmittedEntityFields' in options && options.excludeOmittedEntityFields); const keepExtraFields = !!options?.keepExtraFields; let changedFields = false; const newFields = {}; for (const fieldSpec of typeSpec.fields) { const fieldPath = [...path, fieldSpec.name]; const originalValue = fields[fieldSpec.name]; extraFieldNames.delete(fieldSpec.name); if (kind === 'entity' && excludeOmittedEntityFields && !(fieldSpec.name in fields)) { continue; } const transformResult = transformContentField(schema, fieldPath, fieldSpec, originalValue, transformer, options); if (transformResult.isError()) return transformResult; let transformedValue = transformResult.value; if (transformedValue === undefined) { transformedValue = null; } if (transformedValue !== originalValue) { changedFields = true; } newFields[fieldSpec.name] = transformedValue; } if (extraFieldNames.size > 0) { if (keepExtraFields) { if (changedFields) { // Copy extra fields as is when we want to keep them for (const fieldName of extraFieldNames) { newFields[fieldName] = fields[fieldName]; } } } else { // So that we only return the fields that we know about changedFields = true; } } if (!changedFields) { return ok(fields); } return ok(newFields); } export function transformContentField(schema, path, fieldSpec, originalValue, transformer, options) { const traversableError = checkFieldTraversable(fieldSpec, originalValue); if (traversableError) { return notOk.BadRequest(`${contentValuePathToString([...path, ...traversableError.path])}: ${traversableError.message}`); } const transformFieldResult = transformer.transformField(schema, path, fieldSpec, originalValue); if (transformFieldResult.isError()) return transformFieldResult; let value = transformFieldResult.value; if (value === null || value === undefined) { return ok(null); } if (fieldSpec.list) { let changedItems = false; const newItems = []; for (let i = 0; i < value.length; i += 1) { const fieldItemPath = [...path, i]; const fieldItem = value[i]; const transformFieldValueResult = transformContentFieldValue(schema, fieldItemPath, fieldSpec, fieldItem, transformer, options); if (transformFieldValueResult.isError()) return transformFieldValueResult; const newFieldItem = transformFieldValueResult.value; if (newFieldItem !== fieldItem) { changedItems = true; } if (newFieldItem !== null && newFieldItem !== undefined) { newItems.push(newFieldItem); } } if (changedItems) { value = newItems; } if (Array.isArray(value) && value.length === 0) { value = null; } } else { const transformFieldValueResult = transformContentFieldValue(schema, path, fieldSpec, value, transformer, options); if (transformFieldValueResult.isError()) return transformFieldValueResult; value = transformFieldValueResult.value; } return ok(value); } function transformContentFieldValue(schema, path, fieldSpec, originalValue, transformer, options) { const traversableError = checkFieldItemTraversable(fieldSpec, originalValue); if (traversableError) { return notOk.BadRequest(`${contentValuePathToString([...path, ...traversableError.path])}: ${traversableError.message}`); } const transformFieldItemResult = transformer.transformFieldItem(schema, path, fieldSpec, originalValue); if (transformFieldItemResult.isError()) return transformFieldItemResult; const value = transformFieldItemResult.value; if (isComponentItemField(fieldSpec, value) && value) { return transformComponent(schema, path, value, transformer, options); } else if (isRichTextItemField(fieldSpec, value) && value) { return transformRichText(path, value, (path, node) => { const nodeResult = transformer.transformRichTextNode(schema, path, fieldSpec, node); if (nodeResult.isError()) return nodeResult; const transformedNode = nodeResult.value; if (transformedNode && isRichTextComponentNode(transformedNode)) { const component = transformedNode.data; const componentResult = transformComponent(schema, path, component, transformer, options); if (componentResult.isError()) return componentResult; const transformedComponent = componentResult.value; if (transformedComponent !== component) { return ok(transformedComponent ? { ...transformedNode, data: transformedComponent } : null); } } return ok(transformedNode); }); } else if (isStringItemField(fieldSpec, value)) { //TODO support trimming of strings? if (!value) { return ok(null); // Empty string => null } } return ok(value); } //# sourceMappingURL=ContentTransformer.js.map