Skip to content

Commit abd18f4

Browse files
Merge pull request #241 from ember-tooling/nvp/fix-tsbuildinfo-enoent
Don't crash type-aware linting when the project's tsbuildinfo doesn't exist yet
2 parents de9ac2b + 0d0c6f6 commit abd18f4

2 files changed

Lines changed: 55 additions & 2 deletions

File tree

src/parser/ts-patch.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,16 @@ try {
3636
readFile(fname) {
3737
let fileName = fname;
3838
let content = '';
39-
if (fileName.endsWith('tsconfig.tsbuildinfo')) {
40-
return content;
39+
if (fileName.endsWith('.tsbuildinfo')) {
40+
// Incremental build state is optional and never a .gts/.gjs file, so
41+
// delegate to the real host: it returns the content when present and
42+
// reports a missing file as absent (undefined) instead of throwing.
43+
// The watch program reads `tsBuildInfoFile` without a fileExists
44+
// probe (readBuilderProgram -> host.readFile), so a fresh/cleaned
45+
// project with a custom buildinfo path (e.g.
46+
// "declarations/.tsbuildinfo") would otherwise abort linting of
47+
// every file with a Parsing error: ENOENT.
48+
return sys.readFile(fileName);
4149
}
4250

4351
try {

tests/ts-patch.test.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { createRequire } from 'node:module';
3+
import fs from 'node:fs';
4+
import os from 'node:os';
5+
import path from 'node:path';
6+
7+
import { patchTs } from '../src/parser/ts-patch.js';
8+
9+
// Resolve the same typescript instance ts-patch patches (the one
10+
// @typescript-eslint/parser depends on), so we observe the patched ts.sys.
11+
const require = createRequire(import.meta.url);
12+
const parserPath = require.resolve('@typescript-eslint/parser');
13+
const ts = require(require.resolve('typescript', { paths: [parserPath] }));
14+
15+
// Incremental build state is optional input: when `tsBuildInfoFile` points at
16+
// a file that has not been produced yet (fresh checkout, cleaned repo), the
17+
// watch program used for type-aware linting reads it WITHOUT a fileExists
18+
// probe (readBuilderProgram -> host.readFile). ts.sys.readFile's contract is
19+
// to report a missing file as absent, not to throw — a throw here aborts
20+
// linting of every file in the project with
21+
// `Parsing error: ENOENT ... <project>/declarations/.tsbuildinfo`.
22+
describe('patched ts.sys.readFile — .tsbuildinfo handling', () => {
23+
patchTs();
24+
25+
it('treats a missing custom-named .tsbuildinfo as absent instead of throwing', () => {
26+
// e.g. { "tsBuildInfoFile": "declarations/.tsbuildinfo" } — only the
27+
// default `tsconfig.tsbuildinfo` name was guarded before.
28+
const missing = path.join(os.tmpdir(), 'ee-parser-no-such-dir', 'declarations', '.tsbuildinfo');
29+
30+
expect(() => ts.sys.readFile(missing)).not.toThrow();
31+
expect(ts.sys.readFile(missing)).toBeUndefined();
32+
});
33+
34+
it('returns the real content when the .tsbuildinfo exists', () => {
35+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ee-parser-buildinfo-'));
36+
const buildInfo = path.join(dir, 'custom.tsbuildinfo');
37+
fs.writeFileSync(buildInfo, '{"version":"5.9.3"}');
38+
39+
try {
40+
expect(ts.sys.readFile(buildInfo)).toBe('{"version":"5.9.3"}');
41+
} finally {
42+
fs.rmSync(dir, { recursive: true, force: true });
43+
}
44+
});
45+
});

0 commit comments

Comments
 (0)