@atlaskit/adf-utils
Version:
Set of utilities to traverse, modify and create ADF documents.
87 lines • 3 kB
JavaScript
import { traverse } from '../traverse/traverse';
// This is the set of marks that we wont allow in duplicate to
// exist on a given node, regardless of their attributes. We do
// not include annotations here, because we do allow duplicate
// annotations as long as they have unique id attributes (valid scenario)
const markDuplicatesDisallowed = new Set(['strong', 'underline', 'textColor', 'link', 'em', 'subsup', 'strike', 'backgroundColor']);
const maybeHasDisallowedDuplicateMarks = node => {
var _node$marks, _node$marks$map;
const markTypes = (_node$marks = node.marks) === null || _node$marks === void 0 ? void 0 : (_node$marks$map = _node$marks.map(mark => mark.type)) === null || _node$marks$map === void 0 ? void 0 : _node$marks$map.filter(markType =>
// For annotations, we are performing the same cheap check
// for duplicates by type, without considering IDs, to quickly determine
// whether there may be potential deduping targets.
// In the actual deduping logic in maybeRemoveDisallowedDuplicateMarks,
// we correctly/safely dedupe annotations by their unique IDs.
markDuplicatesDisallowed.has(markType) || markType === 'annotation');
if (!(markTypes !== null && markTypes !== void 0 && markTypes.length)) {
return false;
}
return new Set(markTypes).size !== markTypes.length;
};
const maybeRemoveDisallowedDuplicateMarks = node => {
const quota = new Map();
const annotationsQuota = new Map();
const discardedMarks = [];
markDuplicatesDisallowed.forEach(mark => {
quota.set(mark, false);
});
if (!node.marks) {
return {
discardedMarks
};
}
const dedupedMarks = node.marks.filter(mark => {
const markType = mark.type;
if (markType === 'annotation') {
var _mark$attrs;
const id = (_mark$attrs = mark.attrs) === null || _mark$attrs === void 0 ? void 0 : _mark$attrs.id;
if (annotationsQuota.has(id)) {
discardedMarks.push(mark);
return false;
} else {
annotationsQuota.set(id, true);
return true;
}
}
if (quota.has(markType)) {
if (!quota.get(markType)) {
quota.set(markType, true);
return true;
} else {
discardedMarks.push(mark);
return false;
}
}
return true;
});
return {
node: {
...node,
marks: dedupedMarks
},
discardedMarks
};
};
export const transformDedupeMarks = adf => {
let isTransformed = false;
const discardedMarks = [];
const transformedAdf = traverse(adf, {
text: node => {
if (maybeHasDisallowedDuplicateMarks(node)) {
const result = maybeRemoveDisallowedDuplicateMarks(node);
const resultDiscardedMarks = result.discardedMarks;
if (resultDiscardedMarks.length) {
discardedMarks.push(...resultDiscardedMarks);
isTransformed = true;
return result.node;
}
}
return;
}
});
return {
transformedAdf,
isTransformed,
discardedMarks
};
};