Recheck project filenames and Word commenters

This commit is contained in:
muena
2026-09-25 10:49:21 +02:00
parent d0d6a29ef6
commit c3ef1a6aed
7 changed files with 51 additions and 10 deletions

View File

@@ -14,7 +14,7 @@ import {
} from "./lib/velum-project";
import type { ProjectFile, ProjectRule, ProjectWhitelistEntry, RunFile } from "./lib/velum-project";
import {
canonicalPerson, groupReviewHits, makeTextBatches, mappingForSelection, mergePeople, spansBySegment,
canonicalPerson, filenameScanText, groupReviewHits, makeTextBatches, mappingForSelection, mergePeople, spansBySegment,
withoutWhitelistedHits, withoutWhitelistedRules,
} from "./lib/project-review";
@@ -117,7 +117,9 @@ async function prepare(root: string, files: ProjectFile[], rules: ProjectRule[],
for (const file of files) {
const data = await fs.readFile(file.absPath);
const hash = digest(data);
if (previousDirectory && previousOutputs.has(file.relPath)
const recheckCommentAuthors = entityTypes.includes("PERSON") && file.kind === "document"
&& extname(file.relPath).toLowerCase() === ".docx" && !latest?.commentAuthorCoverage;
if (!recheckCommentAuthors && previousDirectory && previousOutputs.has(file.relPath)
&& await matchesPreviousOutput(previousDirectory, file.relPath, hash)) {
retained.push({ file, hash });
continue;
@@ -151,8 +153,14 @@ async function prepare(root: string, files: ProjectFile[], rules: ProjectRule[],
mapping = result.mapping;
addHits(draft, documentHits(draft.file, result.hits));
}
const paths = pathSegments(drafts.map((draft) => draft.file));
const pathBatches = makeTextBatches(paths.map((path) => ({ key: path.key, text: path.value })));
// Recheck names even when an in-place file's contents are unchanged. An
// earlier run may have masked the content but left the filename clear.
const paths = pathSegments(files);
const knownTerms = [...Object.keys(mapping), ...Object.values(mapping).map((entry) => entry.original),
...withoutWhitelistedRules(rules, whitelist).map((rule) => rule.value)];
const pathBatches = makeTextBatches(paths.map((path) => ({
key: path.key, text: filenameScanText(path.value, knownTerms),
})));
const byPath = new Map(paths.map((path) => [path.key, path]));
for (const [index, batch] of pathBatches.entries()) {
onProgress(`Dateinamen analysieren: Paket ${index + 1}/${pathBatches.length}`);
@@ -161,7 +169,7 @@ async function prepare(root: string, files: ProjectFile[], rules: ProjectRule[],
const spans = spansBySegment(batch, result.spans);
for (const segment of batch.segments) {
const path = byPath.get(segment.key)!;
path.hits = withoutWhitelistedHits(toUtf16Offsets(path.value, spans.get(path.key) ?? []).map((span, hitIndex) => ({
path.hits = withoutWhitelistedHits(toUtf16Offsets(segment.text, spans.get(path.key) ?? []).map((span, hitIndex) => ({
...span, id: `${path.key}:path:${hitIndex}`, file: path.key,
source: "Datei- oder Ordnername", kind: "path" as const,
context: path.value,
@@ -263,6 +271,7 @@ function HitReview(props: { root: string; rules: ProjectRule[]; whitelist: Proje
selectedPeople.has(source) && availableTargets.has(canonicalPerson(target, personMerges))));
run.mapping = mappingForSelection(props.preview.mapping, props.preview.previousMapping,
acceptedHits, activeMerges);
run.commentAuthorCoverage = props.preview.entityTypes.includes("PERSON");
run.matchModes = props.preview.matchModes;
run.sourceInPlace = props.preview.sourceInPlace || inPlace;
const usedPaths = new Set<string>();
@@ -270,13 +279,16 @@ function HitReview(props: { root: string; rules: ProjectRule[]; whitelist: Proje
for (const retained of props.preview.retained) {
const data = await fs.readFile(retained.file.absPath);
if (digest(data) !== retained.hash) throw new Error(`Datei wurde seit der Vorschau geändert: ${retained.file.relPath}`);
const relPath = safeRelativePath(retained.file.relPath);
const relPath = outputPath(retained.file.relPath, props.preview.paths, accepted,
retained.file.kind === "document", activeMerges);
if (usedPaths.has(relPath)) throw new Error(`Zwei Dateien hätten denselben Ausgabenamen: ${relPath}`);
usedPaths.add(relPath);
const target = resolveWithin(join(directory, "files"), relPath);
await fs.mkdir(dirname(target), { recursive: true, mode: 0o700 });
await fs.writeFile(target, data, { mode: 0o600 });
run.files.push({ source: relPath, output: relPath, kind: retained.file.kind });
const file: RunFile = { source: retained.file.relPath, output: relPath, kind: retained.file.kind };
run.files.push(file);
if (file.source !== file.output) replacements.push(file);
}
for (const draft of props.preview.drafts) {
const data = await fs.readFile(draft.file.absPath);