UNPKG

@prismicio/types-internal

Version:
267 lines (266 loc) 11.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.migrateDocument = exports.collectWidgets = exports.traverseDocument = exports.fillDocumentWithDefaultValues = exports.DocumentLegacy = exports.Document = void 0; const tslib_1 = require("tslib"); const fp_ts_1 = require("fp-ts"); const Either_1 = require("fp-ts/lib/Either"); const function_1 = require("fp-ts/lib/function"); const t = (0, tslib_1.__importStar)(require("io-ts")); const utils_1 = require("../_internal/utils"); const common_1 = require("../common"); const UUID_1 = require("../common/UUID"); const customtypes_1 = require("../customtypes"); const fields_1 = require("./fields"); const LegacyContentCtx_1 = require("./LegacyContentCtx"); exports.Document = t.record(common_1.WidgetKey, fields_1.WidgetContent); const legacyDocReader = t.record(common_1.WidgetKey, t.unknown); /** * `DocumentLegacyCodec` handles decoding and encoding documents to the legacy * format used by Prismic DB, therefore, this function itself is not "legacy". */ const DocumentLegacyCodec = (allTypes, allKeys) => { return new t.Type("Document", (u) => !!u && typeof u === "object", (doc) => { return (0, function_1.pipe)(legacyDocReader.decode(doc), fp_ts_1.either.map((parsedDoc) => { return Object.entries(parsedDoc).reduce((acc, [widgetKey, widgetValue]) => { const widgetCtx = (0, LegacyContentCtx_1.defaultCtx)(widgetKey, allTypes, allKeys); const parsedW = (0, fields_1.WidgetLegacy)(widgetCtx).decode(widgetValue); if (!parsedW || (0, Either_1.isLeft)(parsedW)) return acc; return { ...acc, [widgetKey]: parsedW.right }; }, {}); })); }, (g) => { return Object.entries(g).reduce((acc, [key, value]) => { const widgetCtx = (0, LegacyContentCtx_1.defaultCtx)(key, allTypes); const result = (0, fields_1.WidgetLegacy)(widgetCtx).encode(value); if (!result) return acc; return { content: { ...acc.content, [key]: result.content }, types: { ...acc.types, ...result.types }, keys: { ...acc.keys, ...result.keys }, }; }, { content: {}, types: {}, keys: {} }); }); }; function extractMetadata(data) { const fields = Object.entries(data); const { types, widgets, keys } = fields.reduce((acc, [k, v]) => { if (k.endsWith("_TYPE")) { const decodedValue = LegacyContentCtx_1.FieldOrSliceType.decode(v); if ((0, Either_1.isRight)(decodedValue)) { return { ...acc, types: acc.types.set(k.substring(0, k.length - 5), decodedValue.right), }; } } if (k.endsWith("_KEY")) { const decodedValue = UUID_1.UUID.decode(v); if ((0, Either_1.isRight)(decodedValue)) { return { ...acc, keys: acc.keys.set(k.substring(0, k.length - 4), decodedValue.right), }; } } if (!k.endsWith("_POSITION") && !k.endsWith("_TYPE") && !k.endsWith("_KEY")) { return { ...acc, widgets: { ...acc.widgets, [k]: v, }, }; } return acc; }, { types: new Map(), widgets: {}, keys: new Map(), }); const slugs = data["slugs_INTERNAL"] || []; const uid = data["uid"]; return { widgets, types, keys, uid, slugs, }; } function parseLegacyDocument(legacyDoc, customType) { const result = (0, function_1.pipe)( // ensure it's the right document format first t.record(common_1.WidgetKey, t.unknown).decode(legacyDoc), fp_ts_1.either.chain((doc) => { // extract all metadata, meaning all _TYPES keys from legacy format + the widgets as unknown const { types, widgets, keys } = extractMetadata(doc); // parse the actual widgets return DocumentLegacyCodec(types, keys).decode(widgets); })); return (0, Either_1.isLeft)(result) ? undefined : migrateDocument(result.right, customType); } function encodeToLegacyDocument(document) { const encoded = DocumentLegacyCodec().encode(document); return { ...encoded.content, ...encoded.types, ...encoded.keys }; } exports.DocumentLegacy = { _codec: DocumentLegacyCodec, extractMetadata, parse: parseLegacyDocument, encode: encodeToLegacyDocument, }; function simplifyCustomType(customType) { return { customTypeId: customType === null || customType === void 0 ? void 0 : customType.id, fields: Object.fromEntries((0, customtypes_1.flattenSections)(customType)), }; } function fillDocumentWithDefaultValues(customType, document) { const { fields } = customType && customtypes_1.StaticCustomType.is(customType) ? simplifyCustomType(customType) : customType; return Object.entries(fields).reduce((updatedDocument, [fieldKey, fieldDef]) => { const fieldContent = document[fieldKey]; const updatedField = (() => { switch (fieldDef.type) { case "Group": return (0, fields_1.isGroupContent)(fieldContent) ? (0, fields_1.groupContentWithDefaultValues)(fieldDef, fieldContent) : fieldContent; case "Choice": case "Slices": return (0, fields_1.isSlicesContent)(fieldContent) ? (0, fields_1.slicesContentWithDefaultValues)(fieldDef, fieldContent) : fieldContent; default: return fieldContent === undefined && customtypes_1.NestableWidget.is(fieldDef) ? (0, fields_1.NestableContentDefaultValue)(fieldDef) : fieldContent; } })(); return updatedField ? { ...updatedDocument, [fieldKey]: updatedField, } : updatedDocument; }, document); } exports.fillDocumentWithDefaultValues = fillDocumentWithDefaultValues; /** * @param model: Can be optional if we simply want to loop through the content * without any consideration for the attached model * @param document: The content we actually want to iterate on in an immutable fashion * @param transform: A user function that provides a way to transform any kind * of content wherever it is in a structured Prismic object content. * @returns A transformed document with the user's transformation applied with * the transform function */ function traverseDocument({ document, customType, }) { const model = customType && customtypes_1.StaticCustomType.is(customType) ? simplifyCustomType(customType) : customType; return ({ transformWidget = ({ content }) => content, transformSlice = ({ content }) => content, }) => { const fieldModels = model && Object.entries(model.fields).reduce((acc, [key, def]) => ({ ...acc, [key]: def }), {}); return Object.entries(document).reduce((acc, [key, content]) => { const fieldModel = fieldModels && fieldModels[key]; const path = utils_1.ContentPath.make([ { key: model === null || model === void 0 ? void 0 : model.customTypeId, type: "CustomType" }, { key, type: "Widget" }, ]); const transformedWidget = (() => { switch (content.__TYPE__) { case "SliceContentType": return (0, fields_1.traverseSlices)({ path, key, model: (fieldModel === null || fieldModel === void 0 ? void 0 : fieldModel.type) === "Slices" || (fieldModel === null || fieldModel === void 0 ? void 0 : fieldModel.type) === "Choice" ? fieldModel : undefined, content, })({ transformWidget, transformSlice }); case "GroupContentType": return (0, fields_1.traverseGroupContent)({ path, key, apiId: key, model: (fieldModel === null || fieldModel === void 0 ? void 0 : fieldModel.type) === "Group" ? fieldModel : undefined, content, })(transformWidget); case "RepeatableContent": return (0, fields_1.traverseRepeatableContent)({ path, key, apiId: key, model: (fieldModel === null || fieldModel === void 0 ? void 0 : fieldModel.type) === "Link" ? fieldModel : undefined, content, })(transformWidget); case "TableContent": return (0, fields_1.traverseTableContent)({ path, key, apiId: key, model: (fieldModel === null || fieldModel === void 0 ? void 0 : fieldModel.type) === "Table" ? fieldModel : undefined, content, })(transformWidget); default: return transformWidget({ path, key, apiId: key, model: (fieldModel === null || fieldModel === void 0 ? void 0 : fieldModel.type) !== "Group" && (fieldModel === null || fieldModel === void 0 ? void 0 : fieldModel.type) !== "Slices" && (fieldModel === null || fieldModel === void 0 ? void 0 : fieldModel.type) !== "Choice" ? fieldModel : undefined, content, }); } })(); return { ...acc, ...(transformedWidget ? { [key]: transformedWidget } : {}), }; }, {}); }; } exports.traverseDocument = traverseDocument; // /** // * The goal is to be able to collect all widgets or slices of a given type at any level of nesting inside a prismic content // * // * @param document parsed prismic content // * @param is typeguard to match specifically the type of widget we want to collect // * @returns a record containing the path of the widget as key and the typed collected content as value // */ function collectWidgets(document, is) { const collected = {}; traverseDocument({ document })({ transformWidget: ({ content, path }) => { const key = utils_1.ContentPath.serialize(path); if (is(content, path)) collected[key] = content; return content; }, }); return collected; } exports.collectWidgets = collectWidgets; function migrateDocument(document, customType) { const model = customtypes_1.StaticCustomType.is(customType) ? simplifyCustomType(customType) : customType; const needsMigration = Object.values((0, customtypes_1.collectSharedSlices)(model)).some((slice) => Boolean(slice.legacyPaths)); if (!needsMigration) return document; return traverseDocument({ document, customType, })({ transformSlice: fields_1.migrateSliceItem, }); } exports.migrateDocument = migrateDocument;