core/converters/definition.converter.ts
core/converters/definition.converter.ts is a file in Bane's Lab Site. 199 lines of code and 45 definitions.
import type { AnatomyFolder, DefinitionLocation } from "#types/anatomy.types";
import {
EXTENSION_SEPARATOR,
PATH_SEPARATOR,
SELF_IMPORT_MARK,
TYPESCRIPT_EXTENSION,
} from "#configuration/constants/anatomy.constants";
import { localPath, qualifiedPath, treeOf } from "#core/converters/folder.converter";
import type { DefinitionIndex } from "#types/definition.types";
import type { EvidenceNode } from "#types/evidence.types";
import { TREE_TAB } from "#core/ids/anatomy.ids";
import { packageFileOf } from "#core/resolvers/specifier.resolver";
const RELATIVE_HERE = ".";
const RELATIVE_UP = "..";
interface Held {
readonly files: Set<string>;
readonly folders: Set<string>;
readonly moduleNames: Map<string, DefinitionLocation[]>;
readonly names: Map<string, DefinitionLocation[]>;
}
const collect = function collect(folder: AnatomyFolder, held: Held): void {
held.folders.add(folder.path);
for (const file of folder.files) {
held.files.add(file.path);
for (const definition of file.definitions) {
const entry = { file: file.path, line: definition.line, name: definition.name };
held.names.set(definition.name, [...(held.names.get(definition.name) ?? []), entry]);
if (!definition.local) {
held.moduleNames.set(definition.name, [...(held.moduleNames.get(definition.name) ?? []), entry]);
}
}
}
for (const child of folder.folders) {
collect(child, held);
}
};
const heldOf = function heldOf(roots: readonly AnatomyFolder[]): Held {
const held: Held = { files: new Set(), folders: new Set(), moduleNames: new Map(), names: new Map() };
for (const root of roots) {
collect(root, held);
}
return held;
};
const bare = function bare(text: string): string {
const trimmed = text.trim();
const unrooted = trimmed.startsWith(RELATIVE_HERE + PATH_SEPARATOR)
? trimmed.slice(RELATIVE_HERE.length + PATH_SEPARATOR.length)
: trimmed;
return unrooted.endsWith(PATH_SEPARATOR) ? unrooted.slice(0, -PATH_SEPARATOR.length) : unrooted;
};
const step = function step(out: string[], segment: string): string[] {
if (segment === RELATIVE_UP) {
return out.slice(0, -1);
}
return segment === RELATIVE_HERE || segment.length === 0 ? out : [...out, segment];
};
const normalize = function normalize(segments: readonly string[]): string {
return segments.reduce<string[]>(step, []).join(PATH_SEPARATOR);
};
const endsWithTail = function endsWithTail(path: string, tail: string): boolean {
const local = localPath(path);
return local.endsWith(PATH_SEPARATOR + tail) || local === tail;
};
const treeFrom = function treeFrom(from: string | null): string {
return from === null ? TREE_TAB : treeOf(from);
};
const containsInOrder = function containsInOrder(path: string, parts: readonly string[]): boolean {
let at = 0;
for (const part of parts) {
const found = path.indexOf(part, at);
if (found === -1) {
return false;
}
at = found + part.length;
}
return true;
};
const inFile = function inFile(location: DefinitionLocation, file: string | null): boolean {
return file === null || location.file === file || location.file.endsWith(PATH_SEPARATOR + file);
};
const existingIn = function existingIn(files: ReadonlySet<string>, candidate: string): string | null {
if (files.has(candidate)) {
return candidate;
}
const typed = candidate + EXTENSION_SEPARATOR + TYPESCRIPT_EXTENSION;
return files.has(typed) ? typed : null;
};
type SuffixLookup = (tail: string, tree?: string | null) => string | null;
const lastSegment = function lastSegment(path: string): string {
return path.slice(path.lastIndexOf(PATH_SEPARATOR) + 1);
};
const suffixLookupOf = function suffixLookupOf(files: ReadonlySet<string>): SuffixLookup {
const byName = new Map<string, string[]>();
for (const path of files) {
const name = lastSegment(path);
byName.set(name, [...(byName.get(name) ?? []), path]);
}
return (tail, tree = null) => {
const matches = (byName.get(lastSegment(tail)) ?? []).filter(
(path) => (tree === null || treeOf(path) === tree) && endsWithTail(path, tail),
);
return matches.length === 1 ? (matches[0] ?? null) : null;
};
};
export const specifierResolverOf = function specifierResolverOf(
files: ReadonlySet<string>,
): (specifier: string, fromPath: string) => string | null {
const bySuffixIn = suffixLookupOf(files);
return (specifier, fromPath) => {
const tree = treeOf(fromPath);
if (specifier.startsWith(SELF_IMPORT_MARK)) {
const tail = specifier.slice(SELF_IMPORT_MARK.length);
return (
existingIn(files, qualifiedPath(tree, tail)) ??
bySuffixIn(tail + EXTENSION_SEPARATOR + TYPESCRIPT_EXTENSION, tree) ??
bySuffixIn(tail, tree)
);
}
if (!specifier.startsWith(RELATIVE_HERE)) {
return packageFileOf(specifier, (path) => existingIn(files, path));
}
const base = localPath(fromPath).split(PATH_SEPARATOR).slice(0, -1);
const local = normalize([...base, ...specifier.split(PATH_SEPARATOR)]);
return existingIn(files, qualifiedPath(tree, local));
};
};
export const definitionIndexOf = function definitionIndexOf(roots: readonly AnatomyFolder[]): DefinitionIndex {
const held = heldOf(roots);
const definitionsNamed = function definitionsNamed(name: string): readonly DefinitionLocation[] {
return held.names.get(name) ?? [];
};
const bySuffix = suffixLookupOf(held.files);
const resolvePath = function resolvePath(text: string, from: string | null = null): string | null {
const candidate = bare(text);
if (candidate.length === 0) {
return null;
}
const own = qualifiedPath(treeFrom(from), candidate);
if (held.files.has(own)) {
return own;
}
return (from === null ? null : bySuffix(candidate, treeFrom(from))) ?? bySuffix(candidate);
};
const resolveFolder = function resolveFolder(text: string, from: string | null = null): string | null {
const candidate = bare(text);
if (candidate.length === 0) {
return null;
}
const own = qualifiedPath(treeFrom(from), candidate);
return held.folders.has(own) ? own : null;
};
const resolveOwnPath = function resolveOwnPath(text: string, from: string): string | null {
const candidate = bare(text);
const tree = treeOf(from);
const own = qualifiedPath(tree, candidate);
if (candidate.length === 0 || held.files.has(own)) {
return candidate.length === 0 ? null : own;
}
return bySuffix(candidate, tree);
};
const resolveSpecifier = specifierResolverOf(held.files);
const citedDefinition = function citedDefinition(
name: string,
file: string | null = null,
): DefinitionLocation | null {
const found = definitionsNamed(name).filter((location) => inFile(location, file));
const site = found.filter((location) => treeOf(location.file) === TREE_TAB);
const [only] = found.length === 1 ? found : site;
return (found.length === 1 || site.length === 1) && only !== undefined ? only : null;
};
return {
citedDefinition,
definitionsNamed,
filesMatching: function filesMatching(parts, from) {
const tree = treeOf(from);
return [...held.files].filter((path) => treeOf(path) === tree && containsInOrder(localPath(path), parts));
},
listDefinitions: function listDefinitions() {
return [...held.names.values()].flat();
},
locateNode: function locateNode(node: EvidenceNode) {
if (node.kind === "definition") {
return citedDefinition(node.name, node.file)?.file ?? null;
}
return node.kind === "file" ? resolvePath(node.name) : resolveFolder(node.words.join(PATH_SEPARATOR));
},
moduleDefinitionsNamed: function moduleDefinitionsNamed(name) {
return held.moduleNames.get(name) ?? [];
},
moduleDefinitionsNamedFrom: function moduleDefinitionsNamedFrom(name, from) {
const all = held.moduleNames.get(name) ?? [];
return all.filter((location) => treeOf(location.file) === treeOf(from));
},
resolveFolder,
resolveOwnPath,
resolvePath,
resolveSpecifier,
};
};