mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
adbffc0734
* chore: clear mechanical lint warnings Drops 44 oxlint warnings (559 → 515) by fixing easy mechanical rules across the codebase: no-useless-escape, no-duplicate-type-constituents, no-redundant-type-constituents, no-unused-expressions, no-meaningless-void-operator, require-array-sort-compare, await-thenable. * chore: drop callback parameter from useCallback deps The `open` argument is a parameter of the callback, not a closed-over variable, so it doesn't belong in the deps array. * chore: promote cleared lint rules to errors Promotes the rules cleared in this PR from warn to error so future violations fail the lint: - no-unused-expressions - typescript/await-thenable - typescript/no-duplicate-type-constituents - typescript/no-meaningless-void-operator - typescript/require-array-sort-compare Removes the override that suppressed no-useless-escape on source files (the global rule is already error) and fixes the 21 escape violations that this exposed in regex character classes and template literals. * chore: address PR review feedback - usePinnedDocuments: simplify UrlId to plain string instead of the intersection trick. - PlantUML embed: move - to end of character class so it's a literal hyphen rather than a range operator. - checkboxes: type token params as Token | undefined to match the actual call sites that pass tokens[index - 2] etc.
40 lines
1.1 KiB
TypeScript
40 lines
1.1 KiB
TypeScript
import escape from "lodash/escape";
|
||
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}`;
|
||
}
|