From 815055c6c59e7f50e58998fafe594455be101282 Mon Sep 17 00:00:00 2001 From: Dylan Piercey Date: Thu, 25 Jun 2026 22:57:01 +0000 Subject: [PATCH] fix(expression): end type annotation at function body brace A `{` following a completed type now ends the return type annotation so a `>` in the body is no longer parsed as a closing generic bracket. Fixes #215 --- .changeset/statement-fn-return-type-body.md | 13 +++++++++++ ...tatement-function-return-type.expected.txt | 23 +++++++++++++++++++ .../input.marko | 13 +++++++++++ src/states/EXPRESSION.ts | 10 ++++++++ 4 files changed, 59 insertions(+) create mode 100644 .changeset/statement-fn-return-type-body.md create mode 100644 src/__tests__/fixtures/ts-statement-function-return-type/__snapshots__/ts-statement-function-return-type.expected.txt create mode 100644 src/__tests__/fixtures/ts-statement-function-return-type/input.marko diff --git a/.changeset/statement-fn-return-type-body.md b/.changeset/statement-fn-return-type-body.md new file mode 100644 index 00000000..f86fc6bd --- /dev/null +++ b/.changeset/statement-fn-return-type-body.md @@ -0,0 +1,13 @@ +--- +"htmljs-parser": patch +--- + +Fix a spurious "Mismatched group" error when a `>` (or other comparison) appears in the body of a statement function that declares a return type, e.g.: + +```marko +export function a(): b { + return c > d; +} +``` + +Previously the type-parsing state from the return type annotation leaked into the function body, so a `>` was treated as a closing generic bracket. A `{` that follows a completed type now correctly ends the annotation and parses the block as a value. diff --git a/src/__tests__/fixtures/ts-statement-function-return-type/__snapshots__/ts-statement-function-return-type.expected.txt b/src/__tests__/fixtures/ts-statement-function-return-type/__snapshots__/ts-statement-function-return-type.expected.txt new file mode 100644 index 00000000..dd5bc2b5 --- /dev/null +++ b/src/__tests__/fixtures/ts-statement-function-return-type/__snapshots__/ts-statement-function-return-type.expected.txt @@ -0,0 +1,23 @@ +1╭─ export function a(): b { + ╰─ ╰─ tagName "export" +2├─ return c > d; +3├─ } +4╭─ + ╰─ ╰─ openTagEnd +5╭─ static function e(x: T): Array { + ╰─ ╰─ tagName "static" +6├─ return x > 0 ? f : g; +7├─ } +8╭─ + ╰─ ╰─ openTagEnd +9╭─ export function h(): { i: 1 } { + ╰─ ╰─ tagName "export" +10├─ return j > k; +11├─ } +12╭─ + ╰─ ╰─ openTagEnd +13╭─ div + ╰─ ╰─ tagName "div" +14╭─ + │ ├─ openTagEnd + ╰─ ╰─ closeTagEnd(div) \ No newline at end of file diff --git a/src/__tests__/fixtures/ts-statement-function-return-type/input.marko b/src/__tests__/fixtures/ts-statement-function-return-type/input.marko new file mode 100644 index 00000000..45551cdd --- /dev/null +++ b/src/__tests__/fixtures/ts-statement-function-return-type/input.marko @@ -0,0 +1,13 @@ +export function a(): b { + return c > d; +} + +static function e(x: T): Array { + return x > 0 ? f : g; +} + +export function h(): { i: 1 } { + return j > k; +} + +div diff --git a/src/states/EXPRESSION.ts b/src/states/EXPRESSION.ts index 6df0e367..a7e1bbb4 100644 --- a/src/states/EXPRESSION.ts +++ b/src/states/EXPRESSION.ts @@ -253,6 +253,16 @@ export const EXPRESSION: StateDefinition = { this.pos++; break; case CODE.OPEN_CURLY_BRACE: + if (expression.inType && !expression.forceType) { + const prevPos = lookBehindWhile( + isWhitespaceCode, + data, + this.pos - 1, + ); + if (lookBehindForOperator(expression, data, prevPos) === -1) { + expression.inType = false; + } + } expression.groupStack.push(CODE.CLOSE_CURLY_BRACE); this.pos++; break;