fix compiler authoring traps and showcase generation order

This commit is contained in:
2026-08-09 14:22:12 +05:30
parent 2e5c0cc953
commit 09ebd44b6c
29 changed files with 1437 additions and 5101 deletions
+21
View File
@@ -1499,3 +1499,24 @@ test("a boolean attribute bound to a loop variable binds on the client", () => {
expect(code).not.toContain('__wireBooleanAttr("checked"');
expect(code).toContain("data-wrn-bind-");
});
test("block comments inside props report the authoring restriction", () => {
expect(() =>
compileWireFile(`component Example {
props {
/* use a line comment */
label: string = "Example"
}
view { <span>{label}</span> }
}`),
).toThrow("Block comments are not allowed inside props {}; use // line comments instead");
});
test("state page reports its collision with the page keyword", () => {
expect(() =>
compileWireFile(`page Example {
state page = 1
view { <span>{page}</span> }
}`),
).toThrow("State name 'page' collides with the WRN 'page' keyword");
});
+15 -1
View File
@@ -439,7 +439,14 @@ export function parse(source: string): PageAst {
// props { name: Type = <default>; @event name = function }
lx.next();
expect("lbrace");
while (lx.peek().type !== "rbrace") {
while (true) {
if (lx.startsWithBlockComment()) {
throw new ParseError(
"Block comments are not allowed inside props {}; use // line comments instead",
"WRN-PROPS-BLOCK-COMMENT",
);
}
if (lx.peek().type === "rbrace") break;
const t = lx.peek();
if (t.type === "eof") throw new ParseError("Unexpected end of input inside props");
if (t.type === "at") {
@@ -829,6 +836,13 @@ export function parse(source: string): PageAst {
const declaredStates = new Set(states.map((state) => state.name));
if (declaredStates.has("page")) {
throw new ParseError(
"State name 'page' collides with the WRN 'page' keyword; choose another state name",
"WRN-STATE-RESERVED-NAME",
);
}
for (const watcher of watches) {
if (!declaredStates.has(watcher.state)) {
throw new ParseError(`Cannot watch undeclared state '${watcher.state}'`);
+6
View File
@@ -55,6 +55,12 @@ export class Lexer {
}
}
/** True when the next non-trivia characters open a block comment. */
startsWithBlockComment(): boolean {
this.skipTrivia();
return this.src[this.pos] === "/" && this.src[this.pos + 1] === "*";
}
/** Read and consume the next structural token. */
next(): Token {
this.skipTrivia();