Handle unreadable detector targets

AI assistance disclosure: Codex implemented and tested this fix under maintainer direction.
This commit is contained in:
Paul Bakaus
2026-09-02 19:01:05 -07:00
parent 2001c81686
commit 840965fc77
4 changed files with 105 additions and 18 deletions
+31 -14
View File
@@ -316,6 +316,10 @@ async function detectCli() {
let allFindings = [];
let hadOperationalFailure = false;
const reportLocalScanFailure = (target, error) => {
hadOperationalFailure = true;
process.stderr.write(`Error: cannot scan ${target}: ${error.message}\n`);
};
if (!process.stdin.isTTY && targets.length === 0) {
allFindings = await handleStdin(scanOptionsFor);
@@ -413,7 +417,11 @@ async function detectCli() {
}
// Build import graph for multi-file awareness
const graph = buildImportGraph(files);
const unreadableFiles = new Set();
const graph = buildImportGraph(files, (file, error) => {
unreadableFiles.add(file);
reportLocalScanFailure(file, error);
});
// Build reverse map: file -> set of files that import it
const importedByMap = new Map();
for (const [importer, imports] of graph) {
@@ -424,24 +432,33 @@ async function detectCli() {
}
for (const file of files) {
// Each file resolves its own project design system (cached by root),
// so a scan spanning sibling projects applies the right rules per file.
const fileOptions = scanOptionsFor(file);
const fileFindings = await detectLocalFile(file, fileOptions);
// Annotate findings with import context
const importers = importedByMap.get(file);
if (importers && importers.size > 0) {
const importerNames = [...importers].map(f => path.basename(f));
for (const f of fileFindings) {
f.importedBy = importerNames;
if (unreadableFiles.has(file)) continue;
try {
// Each file resolves its own project design system (cached by root),
// so a scan spanning sibling projects applies the right rules per file.
const fileOptions = scanOptionsFor(file);
const fileFindings = await detectLocalFile(file, fileOptions);
// Annotate findings with import context
const importers = importedByMap.get(file);
if (importers && importers.size > 0) {
const importerNames = [...importers].map(f => path.basename(f));
for (const f of fileFindings) {
f.importedBy = importerNames;
}
}
allFindings.push(...fileFindings);
} catch (error) {
reportLocalScanFailure(file, error);
}
allFindings.push(...fileFindings);
}
} else if (stat.isFile()) {
if (shouldIgnoreDetectionFile(resolved, process.cwd(), detectionConfig)) continue;
const fileOptions = scanOptionsFor(resolved);
allFindings.push(...await detectLocalFile(resolved, fileOptions));
try {
const fileOptions = scanOptionsFor(resolved);
allFindings.push(...await detectLocalFile(resolved, fileOptions));
} catch (error) {
reportLocalScanFailure(target, error);
}
}
}
} finally {
+9 -2
View File
@@ -81,12 +81,19 @@ function resolveImport(specifier, fromDir, fileSet) {
return null;
}
function buildImportGraph(files) {
function buildImportGraph(files, onReadError = null) {
const fileSet = new Set(files);
const graph = new Map();
for (const file of files) {
const content = fs.readFileSync(file, 'utf-8');
let content;
try {
content = fs.readFileSync(file, 'utf-8');
} catch (error) {
if (!onReadError) throw error;
onReadError(file, error);
continue;
}
const dir = path.dirname(file);
const imports = new Set();