typedoc
Version:
Create api documentation for TypeScript projects.
61 lines (60 loc) • 2.38 kB
JavaScript
import { getSimilarValues } from "#utils";
/**
* Responsible for getting a unique anchor for elements within a page.
*/
export class Slugger {
options;
seen = new Map();
serialize(value) {
// There are quite a few trade-offs here. We used to remove HTML tags here,
// but TypeDoc now removes the HTML tags before passing text into the slug
// method, which allows us to skip doing that here. This improves the slugger
// generation for headers which look like the following:
// (html allowed in markdown)
// # test <t>
// (html disallowed in markdown)
// # test <t>
// both of the above should slug to test-t
const slug = value
.trim()
// remove unwanted chars
.replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, "")
// change whitespace to dash
.replace(/\s/g, "-")
// combine adjacent dashes
.replace(/--+/, "-");
// #3065 unfortunately some headers might result in a desired slug which is
// completely empty. In that case, we still need to return *something* so that
// we don't end up generating an empty anchor, which is invalid according to the
// spec. GitHub's slugger rules don't handle this, so I've somewhat arbitrarily
// chosen "_" here. If GitHub ever fixes that issue, this might need to be adjusted.
return slug || "_";
}
constructor(options) {
this.options = options;
}
slug(value) {
const originalSlug = this.serialize(value);
const lowerOriginalSlug = originalSlug.toLocaleLowerCase();
let count = 0;
let slug = lowerOriginalSlug;
if (this.seen.has(lowerOriginalSlug)) {
count = this.seen.get(lowerOriginalSlug);
do {
count++;
slug = `${lowerOriginalSlug}-${count}`;
} while (this.seen.has(slug));
}
this.seen.set(lowerOriginalSlug, count);
if (!this.options.lowercase) {
return count === 0 ? originalSlug : `${originalSlug}-${count}`;
}
return slug;
}
hasAnchor(anchor) {
return this.seen.has(anchor);
}
getSimilarAnchors(anchor) {
return getSimilarValues(this.seen.keys(), anchor);
}
}