UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

172 lines (168 loc) • 7.82 kB
import _defineProperty from "@babel/runtime/helpers/defineProperty"; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } import { validator } from '@atlaskit/adf-utils/validator'; import { ACTION_SUBJECT_ID } from '../analytics'; export var UNSUPPORTED_NODE_ATTRIBUTE = 'unsupportedNodeAttribute'; import { fireUnsupportedEvent } from './track-unsupported-content'; var errorCallbackFor = function errorCallbackFor(marks, validate, dispatchAnalyticsEvent, validationOverrides) { return function (entity, error, options) { return validationErrorHandler(entity, error, _objectSpread(_objectSpread({}, options), {}, { allowNestedTables: validationOverrides === null || validationOverrides === void 0 ? void 0 : validationOverrides.allowNestedTables, allowTableInPanel: validationOverrides === null || validationOverrides === void 0 ? void 0 : validationOverrides.allowTableInPanel }), marks, validate, dispatchAnalyticsEvent); }; }; export var validationErrorHandler = function validationErrorHandler(entity, error, options, marks, validate, dispatchAnalyticsEvent) { if (entity && entity.type === UNSUPPORTED_NODE_ATTRIBUTE) { return entity; } if (options.isMark) { return wrapWithUnsupported(error.meta, 'mark'); } if (options.isNodeAttribute) { var entityType = entity && entity.type ? entity.type : undefined; return { type: UNSUPPORTED_NODE_ATTRIBUTE, attrs: { type: { nodeType: entityType }, unsupported: error.meta } }; } if (entity && marks.indexOf(entity.type) > -1) { return; } /** * There's a inconsistency between ProseMirror and ADF. * `content` is actually optional in ProseMirror. * And, also empty `text` node is not valid. */ if (error.code === 'MISSING_PROPERTIES' && entity.type === 'paragraph') { return { type: 'paragraph', content: [] }; } // Ignored via go/ees007 // eslint-disable-next-line @atlaskit/editor/enforce-todo-comment-format // TODO: We can repair missing content like `panel` without a `paragraph`. if (error.code === 'INVALID_CONTENT_LENGTH') { if (error.meta && options.allowUnsupportedBlock && entity.content) { return getEntityForInvalidContentLength(error, entity, validate, marks, dispatchAnalyticsEvent); } else { // Can't fix it by wrapping if (dispatchAnalyticsEvent) { trackValidationError(dispatchAnalyticsEvent, error, entity); } } } // Nested tables don't actually exist in ADF. In ADF they are stored as an `extension` node with the nested table content serialized in a property of that extension. // Usually when we validate ADF we are validating this `extension` and then later transforming it into nested tables for usage by ProseMirror. // Under certain circumstances however, ADF validation is performed on this already transformed extension and since nested tables aren't allowed by ADF this // produces an error. For example, nested renderers in Confluence `bodiedExtension`'s. The nested renderer receives the already transformed nested tables that originated from the // `extension` node and validates it. This is invalid ADF so an error is thrown. // This override allows an exception to be made for nested tables in the validator in this circumstance. if (options.allowNestedTables) { var meta = error.meta; if (options.allowNestedTables && meta !== null && meta !== void 0 && meta.parentType && ['tableCell', 'tableHeader'].includes(meta === null || meta === void 0 ? void 0 : meta.parentType) && error.code === 'INVALID_CONTENT' && entity.type === 'table') { return entity; } } // panel_c1 is a ProseMirror-only variant: on save, table-in-panel documents are stored as // plain ADF `panel` nodes containing a `table`. The base validator-spec does not permit // `table` inside `panel` (it's gated behind the experiment), so we suppress the // INVALID_CONTENT error when the experiment is active. if (options.allowTableInPanel) { var _meta = error.meta; if ((_meta === null || _meta === void 0 ? void 0 : _meta.parentType) === 'panel' && error.code === 'INVALID_CONTENT' && entity.type === 'table') { return entity; } } if (options.allowUnsupportedBlock) { return wrapWithUnsupported(entity); } if (options.allowUnsupportedInline) { return wrapWithUnsupported(entity, 'inline'); } if (dispatchAnalyticsEvent) { trackValidationError(dispatchAnalyticsEvent, error, entity); } return entity; }; function getEntityForInvalidContentLength(error, entity, validate, marks, dispatchAnalyticsEvent) { var meta = error.meta; if (meta.type === 'maximum') { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion entity.content = entity.content.filter(function (x) { return !!x; }).map(function (child, index) { return index >= meta.requiredLength && child.type !== 'unsupportedBlock' ? wrapWithUnsupported(child) : validate(child, errorCallbackFor(marks, validate, dispatchAnalyticsEvent)).entity; }); } if (meta.type === 'minimum') { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (entity.content.length === 0) { return wrapWithUnsupported(entity); } // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion entity.content = entity.content.filter(function (x) { return !!x; }).map(function (child) { return child.type !== 'unsupportedBlock' ? wrapWithUnsupported(child) : child; }); } return entity; } function trackValidationError(dispatchAnalyticsEvent, error, entity) { if (!dispatchAnalyticsEvent) { return; } fireUnsupportedEvent(dispatchAnalyticsEvent, ACTION_SUBJECT_ID.UNSUPPORTED_ERROR, { type: entity.type || '', ancestry: entity.ancestorHierarchy || '', parentType: entity.parentType || '', marks: entity.marks || [], attrs: entity.attrs || {} }, error.code); } export var validateADFEntity = function validateADFEntity(schema, node, dispatchAnalyticsEvent, validationOverrides) { var nodes = Object.keys(schema.nodes); var marks = Object.keys(schema.marks); var validate = validator(nodes, marks, { allowPrivateAttributes: true }); var emptyDoc = { type: 'doc', content: [] }; var _validate = validate(node, errorCallbackFor(marks, validate, dispatchAnalyticsEvent, validationOverrides)), _validate$entity = _validate.entity, entity = _validate$entity === void 0 ? emptyDoc : _validate$entity; return entity; }; export function wrapWithUnsupported(originalValue) { var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'block'; var unsupportedNodeType; switch (type) { case 'inline': unsupportedNodeType = 'unsupportedInline'; break; case 'mark': unsupportedNodeType = 'unsupportedMark'; break; default: unsupportedNodeType = 'unsupportedBlock'; } return { type: unsupportedNodeType, attrs: { originalValue: originalValue } }; }