fix(watcher): close deleted opened documents

This commit is contained in:
2026-05-20 06:38:21 -04:00
parent 14749a6449
commit 3f3cb4cdbf
2 changed files with 147 additions and 18 deletions

View File

@@ -0,0 +1,114 @@
import { describe, it, before, after } from "node:test";
import * as assert from "node:assert/strict";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import {
setTestSocket,
stopTestDaemon,
runCliJson,
requireServer,
} from "../helpers.ts";
const skip = requireServer("typescript-language-server");
async function pollUntil<T>(
fn: () => Promise<T>,
predicate: (v: T) => boolean,
timeoutMs: number,
intervalMs = 250,
): Promise<T> {
const deadline = Date.now() + timeoutMs;
let last: T = await fn();
while (Date.now() < deadline) {
if (predicate(last)) return last;
await new Promise((r) => setTimeout(r, intervalMs));
last = await fn();
}
return last;
}
interface DiagResult {
[serverId: string]: { diagnostics?: { message: string }[] };
}
describe("watcher: typescript handles deleted opened files", { skip: skip ?? undefined }, () => {
let tmpDir: string;
let mainFile: string;
let helperFile: string;
const env = { ...process.env };
let cleanup: () => void;
before(async () => {
delete env.NODE_OPTIONS;
cleanup = setTestSocket(env);
await stopTestDaemon(env);
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-lsp-ts-watch-"));
fs.writeFileSync(
path.join(tmpDir, ".pi-lsp.json"),
JSON.stringify({ disable: ["oxlint"] }),
);
fs.writeFileSync(
path.join(tmpDir, "tsconfig.json"),
JSON.stringify(
{
compilerOptions: {
target: "ES2022",
module: "ESNext",
moduleResolution: "Bundler",
strict: true,
noEmit: true,
},
include: ["*.ts"],
},
null,
2,
),
);
mainFile = path.join(tmpDir, "main.ts");
helperFile = path.join(tmpDir, "helper.ts");
fs.writeFileSync(
mainFile,
'import { helper } from "./helper";\n\nconsole.log(helper());\n',
);
fs.writeFileSync(helperFile, "export function helper(): number {\n return 1;\n}\n");
});
after(async () => {
await stopTestDaemon(env);
cleanup();
fs.rmSync(tmpDir, { recursive: true, force: true });
});
it("reports missing module after an opened imported file is deleted", async () => {
const initial = (await runCliJson(
[mainFile, "diagnostics", '{"timeoutMs":5000}'],
env,
)) as DiagResult;
assert.deepEqual(initial["typescript-language-server"]?.diagnostics ?? [], []);
await runCliJson([helperFile, "diagnostics", '{"timeoutMs":5000}'], env);
fs.rmSync(helperFile);
const result = await pollUntil(
async () =>
(await runCliJson(
[mainFile, "diagnostics", '{"timeoutMs":3000}'],
env,
)) as DiagResult,
(r) => {
const diags = r["typescript-language-server"]?.diagnostics ?? [];
return diags.some((d) => d.message.includes("Cannot find module './helper'"));
},
15000,
500,
);
const diags = result["typescript-language-server"]?.diagnostics ?? [];
assert.ok(
diags.some((d) => d.message.includes("Cannot find module './helper'")),
`Expected missing-module diagnostic after deleting opened helper.ts, got: ${JSON.stringify(diags)}`,
);
});
});