diff --git a/scripts/hooks/stop-format-typecheck.js b/scripts/hooks/stop-format-typecheck.js index 8ae580db5..bfca2fcd2 100644 --- a/scripts/hooks/stop-format-typecheck.js +++ b/scripts/hooks/stop-format-typecheck.js @@ -111,6 +111,14 @@ function findTsConfigDir(filePath) { return null; } +function diagnosticMatchesFile(line, paths) { + const normalizedLine = line.replace(/\\/g, '/'); + for (const candidate of paths) { + if (normalizedLine.includes(candidate.replace(/\\/g, '/'))) return true; + } + return false; +} + function typecheckBatch(tsConfigDir, editedFiles, timeoutMs) { const isWin = process.platform === 'win32'; const npxBin = isWin ? 'npx.cmd' : 'npx'; @@ -147,7 +155,7 @@ function typecheckBatch(tsConfigDir, editedFiles, timeoutMs) { const relPath = path.relative(tsConfigDir, filePath); const candidates = new Set([filePath, relPath]); const relevantLines = lines - .filter(line => { for (const c of candidates) { if (line.includes(c)) return true; } return false; }) + .filter(line => diagnosticMatchesFile(line, candidates)) .slice(0, 10); if (relevantLines.length > 0) { process.stderr.write(`[Hook] TypeScript errors in ${path.basename(filePath)}:\n`); @@ -248,4 +256,4 @@ if (require.main === module) { }); } -module.exports = { run, parseAccumulator, isPluginClonePath }; +module.exports = { run, parseAccumulator, isPluginClonePath, diagnosticMatchesFile }; diff --git a/tests/hooks/stop-format-typecheck.test.js b/tests/hooks/stop-format-typecheck.test.js index 564bae693..98c0cfaf7 100644 --- a/tests/hooks/stop-format-typecheck.test.js +++ b/tests/hooks/stop-format-typecheck.test.js @@ -13,7 +13,7 @@ const os = require('os'); const path = require('path'); const accumulator = require('../../scripts/hooks/post-edit-accumulator'); -const { parseAccumulator, isPluginClonePath } = require('../../scripts/hooks/stop-format-typecheck'); +const { parseAccumulator, isPluginClonePath, diagnosticMatchesFile } = require('../../scripts/hooks/stop-format-typecheck'); function test(name, fn) { try { @@ -203,6 +203,12 @@ if (test('parseAccumulator ignores blank lines and trims whitespace', () => { assert.deepStrictEqual(result, ['/tmp/a.ts', '/tmp/b.ts']); })) passed++; else failed++; +if (test('matches forward-slash TypeScript paths against Windows edited paths', () => { + const candidates = new Set(['C:\\project\\src\\broken.ts', 'src\\broken.ts']); + assert.strictEqual(diagnosticMatchesFile('src/broken.ts(1,14): error TS2322', candidates), true); + assert.strictEqual(diagnosticMatchesFile('src/other.ts(1,14): error TS2322', candidates), false); +})) passed++; else failed++; + if (test('stop hook clears accumulator after processing duplicates', () => { cleanAccumFile(); fs.writeFileSync(getAccumFile(), '/nonexistent/x.ts\n/nonexistent/x.ts\n/nonexistent/y.ts\n', 'utf8');