typedoc
Version:
Create api documentation for TypeScript projects.
85 lines (84 loc) • 3.18 kB
JavaScript
import { dirname } from "path";
import * as Util from "util";
import url from "url";
import { DefaultMap } from "./map.js";
/**
* Utility to help type checking ensure that there is no uncovered case.
*/
export function assertNever(x) {
throw new Error(`Expected handling to cover all possible cases, but it didn't cover: ${Util.inspect(x)}`);
}
// From MDN
export function escapeRegExp(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
}
// Based on https://en.wikipedia.org/wiki/Levenshtein_distance#Iterative_with_two_matrix_rows
// Slightly modified for improved match results for options
export function editDistance(s, t) {
if (s.length < t.length)
return editDistance(t, s);
let v0 = Array.from({ length: t.length + 1 }, (_, i) => i);
let v1 = Array.from({ length: t.length + 1 }, () => 0);
for (let i = 0; i < s.length; i++) {
v1[0] = i + 1;
for (let j = 0; j < s.length; j++) {
const deletionCost = v0[j + 1] + 1;
const insertionCost = v1[j] + 1;
let substitutionCost;
if (s[i] === t[j]) {
substitutionCost = v0[j];
}
else if (s[i]?.toUpperCase() === t[j]?.toUpperCase()) {
substitutionCost = v0[j] + 1;
}
else {
substitutionCost = v0[j] + 3;
}
v1[j + 1] = Math.min(deletionCost, insertionCost, substitutionCost);
}
[v0, v1] = [v1, v0];
}
return v0[t.length];
}
export function getSimilarValues(values, compareTo) {
const results = new DefaultMap(() => []);
let lowest = Infinity;
for (const name of values) {
const distance = editDistance(compareTo, name);
lowest = Math.min(lowest, distance);
results.get(distance).push(name);
}
// Experimenting a bit, it seems an edit distance of 3 is roughly the
// right metric for relevant "similar" results without showing obviously wrong suggestions
return results
.get(lowest)
.concat(results.get(lowest + 1), results.get(lowest + 2));
}
export function NonEnumerable(_cls, context) {
context.addInitializer(function () {
Object.defineProperty(this, context.name, {
enumerable: false,
configurable: true,
writable: true,
});
});
}
// transform /abs/path/to/typedoc/dist/lib/utils/general -> /abs/path/to/typedoc
export const TYPEDOC_ROOT = dirname(dirname(dirname(dirname(url.fileURLToPath(import.meta.url)))));
/**
* This is a hack to make it possible to detect and warn about installation setups
* which result in TypeDoc being installed multiple times. If TypeDoc has been loaded
* multiple times, then parts of it will not work as expected.
*/
const loadSymbol = Symbol.for("typedoc_loads");
const pathSymbol = Symbol.for("typedoc_paths");
const g = globalThis;
g[loadSymbol] = (g[loadSymbol] || 0) + 1;
g[pathSymbol] ||= [];
g[pathSymbol].push(TYPEDOC_ROOT);
export function hasBeenLoadedMultipleTimes() {
return g[loadSymbol] !== 1;
}
export function getLoadedPaths() {
return g[pathSymbol] || [];
}