perf: Remove markdown serialize from editor render path (#3567)

* perf: Remove markdown serialize from editor render path

* fix: Simplify heading equality check

* perf: Add cache for slugified headings

* tsc
This commit is contained in:
Tom Moor
2022-05-21 12:50:27 -07:00
committed by GitHub
parent 2a6d6f5804
commit c4006cef7b
11 changed files with 115 additions and 57 deletions

View File

@@ -1,11 +1,23 @@
import { EditorView } from "prosemirror-view";
import { Node } from "prosemirror-model";
import headingToSlug from "./headingToSlug";
export default function getHeadings(view: EditorView) {
const headings: { title: string; level: number; id: string }[] = [];
export type Heading = {
title: string;
level: number;
id: string;
};
/**
* Iterates through the document to find all of the headings and their level.
*
* @param doc Prosemirror document node
* @returns Array<Heading>
*/
export default function getHeadings(doc: Node) {
const headings: Heading[] = [];
const previouslySeen = {};
view.state.doc.forEach((node) => {
doc.forEach((node) => {
if (node.type.name === "heading") {
// calculate the optimal id
const id = headingToSlug(node);

View File

@@ -0,0 +1,44 @@
import { Node } from "prosemirror-model";
export type Task = {
text: string;
completed: boolean;
};
/**
* Iterates through the document to find all of the tasks and their completion
* state.
*
* @param doc Prosemirror document node
* @returns Array<Task>
*/
export default function getTasks(doc: Node): Task[] {
const tasks: Task[] = [];
doc.descendants((node) => {
if (!node.isBlock) {
return false;
}
if (node.type.name === "checkbox_list") {
node.content.forEach((listItem) => {
let text = "";
listItem.forEach((contentNode) => {
if (contentNode.type.name === "paragraph") {
text += contentNode.textContent;
}
});
tasks.push({
text,
completed: listItem.attrs.checked,
});
});
}
return true;
});
return tasks;
}

View File

@@ -2,16 +2,25 @@ import { escape } from "lodash";
import { Node } from "prosemirror-model";
import slugify from "slugify";
const cache = new Map<string, string>();
// Slugify, escape, and remove periods from headings so that they are
// compatible with both url hashes AND dom ID's (querySelector does not like
// ID's that begin with a number or a period, for example).
function safeSlugify(text: string) {
return `h-${escape(
if (cache.has(text)) {
return cache.get(text) as string;
}
const slug = `h-${escape(
slugify(text, {
remove: /[!"#$%&'\.()*+,\/:;<=>?@\[\]\\^_`{|}~]/g,
lower: true,
})
)}`;
cache.set(text, slug);
return slug;
}
// calculates a unique slug for this heading based on it's text and position