Files
outline/shared/editor/lib/headingToSlug.ts
T
Tom Moor 0139b91b5d chore: Replace lodash with es-toolkit (#12281)
* chore: Replace lodash with es-toolkit

Migrate all direct lodash imports to es-toolkit/compat for a smaller,
faster, lodash-compatible utility library. Transitive lodash usage from
other packages remains unchanged.

* fix: Restore isPlainObject semantics in CanCan policy

The lodash migration aliased `isObject` to `lodash/isPlainObject` and
the codemod incorrectly mapped the local name to es-toolkit's `isObject`,
which also returns true for arrays and functions. This caused condition
objects in policy definitions to be skipped, breaking authorization
checks across the codebase.

* fix: Restore unicode-aware length counting in validators

es-toolkit/compat's size() returns string.length, while lodash's _.size()
counts unicode code points. Switch to [...value].length to preserve the
previous behavior so multi-byte characters like emoji count as one.
2026-05-06 21:03:47 -04:00

40 lines
1.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { escape } from "es-toolkit/compat";
import type { 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) {
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
// in the document that is as stable as possible
export default function headingToSlug(node: Node, index = 0) {
const slugified = safeSlugify(node.textContent);
if (index === 0) {
return slugified;
}
return `${slugified}-${index}`;
}
export function headingToPersistenceKey(node: Node, id?: string) {
const slug = headingToSlug(node);
return `rme-${id || window?.location.pathname}${slug}`;
}