renameEverywhere
uses node:path
Rename definitions at every reference in the mirror's files, as an editor's rename would.
import type { CodebaseCore } from "#codebase/tcmts/cli/codebase"; import { applyEdits, findDeclarationName, nearbyFiles } from "#codebase/tcmts/cli/mirror"; import type { Edit, Mirror } from "#codebase/tcmts/cli/mirror"; import { createProgram } from "#codebase/tcmts/core/program"; import fs from "node:fs"; import path_1 from "node:path"; import { isIdentifier as ts_isIdentifier, isShorthandPropertyAssignment as ts_isShorthandPropertyAssignment, } from "typescript/ast"; import type { NodeHandle as ts_NodeHandle } from "typescript/sync"; export function renameEverywhere( cb: CodebaseCore, m: Mirror, renames: { rel: string; from: string; to: string }[], ): Map<string, string> { const files = Object.keys(m.files).map((rel) => path_1.join(m.dir, rel)); // and the files around the mirror that may use the names (scripts, tests): renamed too, not checked const near = nearbyFiles( m, renames.map((r) => r.from), ); const { program, service } = createProgram( [...files, ...near], cb.compilerOptions(m.dir, "import"), ); const edits = new Map<string, Edit[]>(); for (const r of renames) { const file = path_1.join(m.dir, r.rel); const sf = program.getSourceFile(file); if (!sf) throw new Error(`${r.rel} isn't in the program`); const name = findDeclarationName(sf, r.from); if (!name) throw new Error(`no declaration of ${r.from} in ${r.rel}`); for (const entry of service.getReferencedSymbolsForNode(name, name.getStart(sf))) { for (const ref of entry.references as unknown as ts_NodeHandle[]) { const node = ref.resolve(); if (!node || !ts_isIdentifier(node) || node.text !== r.from) continue; const refFile = node.getSourceFile(); const text = ts_isShorthandPropertyAssignment(node.parent) ? `${r.from}: ${r.to}` : r.to; const list = edits.get(refFile.fileName) ?? []; if (!list.some((e) => e.start === node.getStart(refFile))) list.push({ start: node.getStart(refFile), end: node.end, text }); edits.set(refFile.fileName, list); } } } const outside = new Map<string, string>(); const mirrored = new Set(files); for (const [file, list] of edits) { const text = fs.readFileSync(file, "utf8"); if (!mirrored.has(file)) outside.set(file, text); fs.writeFileSync(file, applyEdits(text, list)); } return outside; }
uses
- tcmts.cli.codebase.CodebaseCore
- tcmts.cli.mirror.Edit
- tcmts.cli.mirror.Mirror
- tcmts.cli.mirror.applyEdits
- tcmts.cli.mirror.findDeclarationName
- tcmts.cli.mirror.nearbyFiles
- tcmts.core.program.createProgram