core/converters/access.converter.ts
core/converters/access.converter.ts is a file in GovLab Quality. 40 lines of code and 10 definitions.
import type { AccessEdit, AccessHit } from "#types/access.types";
import ts from "typescript";
const LINE_BASE = 1;
const PROBE_NAME = "index-access.ts";
const accessAt = function accessAt(source: ts.SourceFile, position: number): ts.PropertyAccessExpression | null {
let result: ts.PropertyAccessExpression | null = null;
const visit = function visit(node: ts.Node): void {
if (position < node.getStart(source) || position >= node.getEnd()) {
return;
}
if (
ts.isPropertyAccessExpression(node) &&
node.name.getStart(source) <= position &&
position < node.name.getEnd()
) {
result = node;
}
node.forEachChild(visit);
};
visit(source);
return result;
};
const editFor = function editFor(access: ts.PropertyAccessExpression): AccessEdit {
const optional = access.questionDotToken ? "?." : "";
return { end: access.name.getEnd(), start: access.expression.getEnd(), text: `${optional}["${access.name.text}"]` };
};
const editsFor = function editsFor(text: string, hits: readonly AccessHit[]): AccessEdit[] {
const source = ts.createSourceFile(PROBE_NAME, text, ts.ScriptTarget.Latest, true);
return hits
.map((hit) =>
accessAt(source, source.getPositionOfLineAndCharacter(hit.line - LINE_BASE, hit.column - LINE_BASE)),
)
.filter((access): access is ts.PropertyAccessExpression => access !== null)
.map(editFor);
};
export const bracketAccess = function bracketAccess(text: string, hits: readonly AccessHit[]): string {
return editsFor(text, hits)
.toSorted((left, right) => right.start - left.start)
.reduce((acc, edit) => acc.slice(0, edit.start) + edit.text + acc.slice(edit.end), text);
};