checkMirror
uses node:path
What doesn't match between the mirror's files and the codebase, re-analyzing the files as import does. With
keepText, when they match, the codebase keeps each definition's code as the files spell it, so the next write
spells references the way the files do (not as the scratch file it was edited in did).
import type { CodebaseCore } from "#codebase/tcmts/cli/codebase"; import { fileDefinition, filesIn } from "#codebase/tcmts/cli/files"; import { assetFiles, assetFormat, assetName, isAsset, joinName, namesIn, namespaceForFile, sourceFiles, } from "#codebase/tcmts/cli/mirror"; import type { Mirror } from "#codebase/tcmts/cli/mirror"; import fs from "node:fs"; import path from "node:path"; export function checkMirror(cb: CodebaseCore, m: Mirror, keepText = false): string[] { const files = sourceFiles( m.dir, new Set(m.exclude.map((e) => path.resolve(m.dir, e))), m.tests, ); const a = cb.analyzeProject( files, (file, name) => cb.lineageSalt(joinName(m.into, namespaceForFile(m.dir, file), name)), [], cb.compilerOptions(m.dir, "import"), ); const problems = [...a.errors, ...a.diagnostics.map((d) => d.message)]; if (problems.length) return problems; const out: string[] = []; const seen = new Set<string>(); for (const d of a.defs) { const p = joinName(m.into, namespaceForFile(m.dir, d.file), d.nameHint); seen.add(p); const hs = cb.store.hashesAt(p); if (!hs.includes(d.hash)) out.push( `${p}: the file's code hashes to #${d.hash.slice(0, 8)}, the codebase has ${hs.length ? hs.map((h) => `#${h.slice(0, 8)}`).join(", ") : "nothing there"}`, ); } for (const [rel, f] of Object.entries(m.files)) { if (isAsset(rel, m.dir)) continue; for (const leaf of Object.keys(namesIn(cb, f.ns))) if (!seen.has(joinName(f.ns, leaf))) out.push(`${joinName(f.ns, leaf)} is in the codebase but not in the files`); } // files of the codebase that aren't code: the same content for (const file of assetFiles(m.dir, new Set(m.exclude.map((e) => path.resolve(m.dir, e))))) { const name = assetName(m.dir, m.into, file); seen.add(name); const { def } = fileDefinition(cb, name, assetFormat(file), fs.readFileSync(file)); const hs = cb.store.hashesAt(name); if (!hs.includes(def.hash)) out.push( `${name}: the file hashes to #${def.hash.slice(0, 8)}, the codebase has ${hs.length ? hs.map((h) => `#${h.slice(0, 8)}`).join(", ") : "nothing there"}`, ); } for (const name of filesIn(cb, m.into).keys()) if (!seen.has(name)) out.push(`${name} is in the codebase but not in the files`); // (on the mirror's branch, with the command that wrote the files: the text a branch shows is in its history) if (keepText && !out.length) cb.store.transaction(() => { for (const d of a.defs) { const was = cb.store.getDef(d.hash); if (was && was.sourceText !== d.sourceText) cb.store.setText({ ...d, doc: was.doc, docRefs: was.docRefs }, "mirror"); } }); return out; }
uses
- tcmts.cli.codebase.CodebaseCore
- tcmts.cli.files.fileDefinition
- tcmts.cli.files.filesIn
- tcmts.cli.mirror.Mirror
- tcmts.cli.mirror.assetFiles
- tcmts.cli.mirror.assetFormat
- tcmts.cli.mirror.assetName
- tcmts.cli.mirror.isAsset
- tcmts.cli.mirror.joinName
- tcmts.cli.mirror.namesIn
- tcmts.cli.mirror.namespaceForFile
- tcmts.cli.mirror.sourceFiles