feat(language-server): flag the removed data blocks
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { documentDiagnostics } from "../src/index.ts";
|
||||
|
||||
// documentDiagnostics is the language server's actual diagnostic entry point.
|
||||
// The plan's draft guessed a `diagnoseWrn` export that does not exist.
|
||||
|
||||
// These two tests are the plan's original assertions, kept verbatim. They
|
||||
// already passed before this change — the message produced by the parser
|
||||
// named "apis" from the start — so on their own they are a false-green:
|
||||
// they would not catch a regression to the fix below. They stay as cheap
|
||||
// regression guards, not as the load-bearing coverage for this task.
|
||||
test("an ssr data block is flagged and names the replacement", () => {
|
||||
const diagnostics = documentDiagnostics({
|
||||
uri: "file:///removed-ssr.wrn",
|
||||
text: `page P {
|
||||
ssr { api x GET /api/x { response { return data } } }
|
||||
view { <main>x</main> }
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
expect(diagnostics.length).toBeGreaterThan(0);
|
||||
expect(diagnostics[0]!.message).toContain("apis");
|
||||
});
|
||||
|
||||
test("client state is not flagged", () => {
|
||||
const diagnostics = documentDiagnostics({
|
||||
uri: "file:///client-state.wrn",
|
||||
text: `page P {
|
||||
client state { count = 0 }
|
||||
view { <main>x</main> }
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
expect(diagnostics.filter((item) => item.severity === 1)).toEqual([]);
|
||||
});
|
||||
|
||||
// --- Load-bearing tests -----------------------------------------------
|
||||
//
|
||||
// The message alone doesn't prove the diagnostic is useful: before the fix,
|
||||
// documentDiagnostics reported this error at line 0 / character 0 for every
|
||||
// input, no matter where the offending block actually was. These tests pin
|
||||
// the range to the real position of the "ssr" / "client" keyword, with the
|
||||
// block moved off line 1 so a hardcoded line number cannot pass.
|
||||
|
||||
test("an ssr block diagnostic lands on the ssr keyword, not offset zero", () => {
|
||||
const diagnostics = documentDiagnostics({
|
||||
uri: "file:///removed-ssr-position.wrn",
|
||||
text: `page P {
|
||||
ssr { api x GET /api/x { response { return data } } }
|
||||
view { <main>x</main> }
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
expect(diagnostics.length).toBeGreaterThan(0);
|
||||
// " ssr { ... }" — the keyword starts at line 1 (0-indexed), character 2.
|
||||
expect(diagnostics[0]!.range.start).toEqual({ line: 1, character: 2 });
|
||||
});
|
||||
|
||||
test("a client block diagnostic lands on the client keyword when it is not on line 1", () => {
|
||||
const diagnostics = documentDiagnostics({
|
||||
uri: "file:///removed-client-position.wrn",
|
||||
text: `page P {
|
||||
view { <main>x</main> }
|
||||
client { api x GET /api/x { response { return data } } }
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
expect(diagnostics.length).toBeGreaterThan(0);
|
||||
// The client block is on line 2 (0-indexed) here, preceded by "view { ... }"
|
||||
// on line 1 — a diagnostic hardcoded to line 1 would fail this assertion.
|
||||
expect(diagnostics[0]!.range.start).toEqual({ line: 2, character: 2 });
|
||||
});
|
||||
|
||||
// --- Legal `client` forms stay legal ------------------------------------
|
||||
//
|
||||
// `client` is one word with several jobs. Only the `ssr { }` / `client { }`
|
||||
// data-block form was removed; these forms must never be flagged.
|
||||
|
||||
test("client state block produces no error-severity diagnostic", () => {
|
||||
const diagnostics = documentDiagnostics({
|
||||
uri: "file:///client-state-2.wrn",
|
||||
text: `page P {
|
||||
client state { count = 0 }
|
||||
view { <main>x</main> }
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
expect(diagnostics.filter((item) => item.severity === 1)).toEqual([]);
|
||||
});
|
||||
|
||||
test('runtime = "client" produces no error-severity diagnostic', () => {
|
||||
const diagnostics = documentDiagnostics({
|
||||
uri: "file:///runtime-client.wrn",
|
||||
text: `page P {
|
||||
runtime = "client"
|
||||
view { <main>x</main> }
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
expect(diagnostics.filter((item) => item.severity === 1)).toEqual([]);
|
||||
});
|
||||
|
||||
test("client function inside functions { } produces no error-severity diagnostic", () => {
|
||||
const diagnostics = documentDiagnostics({
|
||||
uri: "file:///client-function.wrn",
|
||||
text: `page P {
|
||||
functions { client function go() { console.log(1) } }
|
||||
view { <main>x</main> }
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
expect(diagnostics.filter((item) => item.severity === 1)).toEqual([]);
|
||||
});
|
||||
@@ -685,7 +685,7 @@ export function parse(source: string): PageAst {
|
||||
}
|
||||
if (lx.peek().type === "lbrace") {
|
||||
throw new ParseError(
|
||||
`"${rawMode} { … }" data blocks were removed. Declare API calls in a page-level "apis { }" block, and move mode-scoped helpers into "functions { shared function … }".`,
|
||||
`"${rawMode} { … }" data blocks were removed. Declare API calls in a page-level "apis { }" block, and move mode-scoped helpers into "functions { shared function … }" (at offset ${kw.pos}).`,
|
||||
);
|
||||
}
|
||||
throw new ParseError(`Expected a ${rawMode} state block or hydrate assignment`);
|
||||
|
||||
Reference in New Issue
Block a user