Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions agent-feedback/bugs.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# Suspected Bugs

Out-of-scope defects noticed while working on something else. Format and rules: [README.md](README.md).

## Emit an error when a `>=` truncates an unquoted attribute-value expression

`src/states/ATTRIBUTE.ts` › `shouldTerminateHtmlAttrValue` | 2026-07-30 | impact:med | effort:low

`shouldTerminateHtmlAttrValue` terminates on the first unenclosed `CODE.CLOSE_ANGLE_BRACKET` with a single look-behind exception for `=>`, and nothing covers the sibling `>=`, so `<const/positive=input.delta >= 0/>` tokenizes as `attrValue "input.delta"`, `openTagEnd`, then text `"= 0/>"` and raises no `onError` at all — marko turns that into `const positive = input.delta` plus a literal `= 0/>` text node, a silent miscompile of an ordinary comparison. Unlike a lone `>`, which is genuinely ambiguous with idiomatic HTML (`<div class=x > text</div>`), a tag close immediately followed by `=` is almost never intended, so this form is decidable here rather than in a consumer's heuristic. Add the look-ahead to the `CLOSE_ANGLE_BRACKET` case: keep terminating, but when `pos !== this.start && data.charCodeAt(pos + 1) === CODE.EQUAL` record it on the `ExpressionMeta` so `ATTRIBUTE.return`'s `ATTR_STAGE.VALUE` case — which already calls `this.emitError` for `INVALID_ATTRIBUTE_VALUE` — can emit `ErrorCode.INVALID_EXPRESSION`; the emit has to land there because `shouldTerminate` is handed no `Parser`. The one false positive to weigh is `<div class=x>=1</div>`, whose text legitimately begins with `=`. Re-verify with `node --input-type=module -e 'import{createParser,TagType}from"./src/index.ts";const p=createParser({onError:e=>console.log("ERR",e.message),onText:r=>console.log("text",JSON.stringify(p.read(r))),onAttrValue:r=>console.log("value",JSON.stringify(p.read(r.value))),onOpenTagName:()=>TagType.html});p.parse("<if=input.delta >= 0>yes</if>")'` — it prints `value "input.delta"` and `text "= 0>yes"` with no `ERR` line today, and `pnpm test` must stay green after the change.

## Treat a backslash-escaped quote as text in parsed-text bodies and parsed strings

`src/states/PARSED_TEXT_CONTENT.ts` › `PARSED_TEXT_CONTENT` | 2026-07-30 | impact:med | effort:low

Inside a `TagType.text` body the only backslash handling is `STATE.checkForPlaceholder`, which declines unless the run of backslashes leads to `${`, so in `PARSED_TEXT_CONTENT.parse` a `\` falls through to the eager text run and the `"`/`'` after it still enters `STATE.PARSED_STRING`; `src/states/PARSED_STRING.ts` › `PARSED_STRING` has the mirror hole, where the quote of `\"` matches `str.quoteCharCode` and closes the string. An odd number of escaped quotes therefore runs to EOF and `PARSED_STRING.parse` emits `INVALID_TEMPLATE_STRING` "EOF reached while parsing string expression" — marko, which maps `<style>`/`<script>` to `TagType.text`, surfaces it as a code frame on the line _after_ the tag. Escaped quotes are legal in CSS selectors and `content:` strings, so `<style>.a\" { color: red }</style>` and `content: 'it\'s'` are both unparseable today; `src/__tests__/fixtures/parsed-text-style-tag` only passes because its escaped quotes happen to come in pairs, and `\2014` parses fine, so the defect is quote-specific rather than backslash-general. The fix is symmetric in the two files: when `checkForPlaceholder` declines a `CODE.BACK_SLASH` and the next char is a quote (the active `quoteCharCode` in `PARSED_STRING`), consume both chars as text instead of letting the quote change state. Re-verify with `node --input-type=module -e 'import{createParser,TagType}from"./src/index.ts";const p=createParser({onError:e=>console.log("ERR",e.message),onText:r=>console.log("text",JSON.stringify(p.read(r))),onOpenTagName:()=>TagType.text});p.parse("<style>.a\\\" { color: red }</style>")'` — it prints `ERR EOF reached while parsing string expression` today and should print a single `text` range; lock it in with a new `src/__tests__/fixtures/` dir plus `pnpm test:update`.