perf: refactor states to be able to parse multiple characters#220
Conversation
🦋 Changeset detectedLatest commit: c9c2baf The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #220 +/- ##
==========================================
+ Coverage 92.02% 99.33% +7.31%
==========================================
Files 28 28
Lines 1454 4209 +2755
Branches 329 773 +444
==========================================
+ Hits 1338 4181 +2843
+ Misses 54 14 -40
+ Partials 62 14 -48 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
WalkthroughThe PR refactors the htmljs-parser state machine to shift from per-character callbacks ( 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed due to a network error. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/states/HTML_CONTENT.ts (1)
138-141: 💤 Low valueBenign but imprecise: negative index read at position 0.
data.charCodeAt(this.pos - 1)reads index -1 whenthis.posis 0, returningNaN. WhileisWhitespaceCode(NaN)correctly returns false (preventing false positives), this reads an undefined index.In practice, HTML_CONTENT is entered after prior content, so
pos > 0should always hold here. If this assumption is guaranteed, consider adding a comment; otherwise, addthis.pos > 0 &&as a guard.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/states/HTML_CONTENT.ts` around lines 138 - 141, The check in HTML_CONTENT that does data.charCodeAt(this.pos - 1) can read index -1 when this.pos is 0; update the condition to guard against that by adding this.pos > 0 && before calling data.charCodeAt(this.pos - 1) (i.e., change the if that compares code === CODE.FORWARD_SLASH && isWhitespaceCode(...) to first ensure this.pos > 0), referencing the HTML_CONTENT state, CODE.FORWARD_SLASH, isWhitespaceCode, and this.pos; alternatively, if it is guaranteed pos>0 when HTML_CONTENT runs, add a short comment stating that invariant instead of the guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.changeset/petite-signs-dance.md:
- Line 5: Fix typos and small phrasing in the changeset sentence that begins
"Refactor parser to allow individual states to process multiple characters."
Replace "realworld" with "real-world", correct "tempaltes" to "templates", and
change "simplifies things some" to "simplifies things somewhat" (or "simplifies
things a bit") so the release note reads clearly and correctly.
---
Nitpick comments:
In `@src/states/HTML_CONTENT.ts`:
- Around line 138-141: The check in HTML_CONTENT that does
data.charCodeAt(this.pos - 1) can read index -1 when this.pos is 0; update the
condition to guard against that by adding this.pos > 0 && before calling
data.charCodeAt(this.pos - 1) (i.e., change the if that compares code ===
CODE.FORWARD_SLASH && isWhitespaceCode(...) to first ensure this.pos > 0),
referencing the HTML_CONTENT state, CODE.FORWARD_SLASH, isWhitespaceCode, and
this.pos; alternatively, if it is guaranteed pos>0 when HTML_CONTENT runs, add a
short comment stating that invariant instead of the guard.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 018376fe-d258-4708-b663-b6a73d82354d
⛔ Files ignored due to path filters (2)
src/__tests__/fixtures/attr-grouped-no-whitespace/__snapshots__/attr-grouped-no-whitespace.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/regexp-escaped-newline/__snapshots__/regexp-escaped-newline.expected.txtis excluded by!**/__snapshots__/**and included by**
📒 Files selected for processing (27)
.changeset/petite-signs-dance.mdsrc/__tests__/fixtures/attr-grouped-no-whitespace/input.markosrc/__tests__/fixtures/regexp-escaped-newline/input.markosrc/__tests__/validate.test.tssrc/core/Parser.tssrc/states/ATTRIBUTE.tssrc/states/BEGIN_DELIMITED_HTML_BLOCK.tssrc/states/CDATA.tssrc/states/CLOSE_TAG.tssrc/states/CONCISE_HTML_CONTENT.tssrc/states/DECLARATION.tssrc/states/DTD.tssrc/states/EXPRESSION.tssrc/states/HTML_COMMENT.tssrc/states/HTML_CONTENT.tssrc/states/INLINE_SCRIPT.tssrc/states/JS_COMMENT_BLOCK.tssrc/states/JS_COMMENT_LINE.tssrc/states/OPEN_TAG.tssrc/states/PARSED_STRING.tssrc/states/PARSED_TEXT_CONTENT.tssrc/states/PLACEHOLDER.tssrc/states/REGULAR_EXPRESSION.tssrc/states/STRING.tssrc/states/TAG_NAME.tssrc/states/TEMPLATE_STRING.tssrc/util/validators.ts
| "htmljs-parser": patch | ||
| --- | ||
|
|
||
| Refactor parser to allow individual states to process multiple characters. This allows for eager scanning, simplifies things some, and improves performance by about 30% in realworld tempaltes. |
There was a problem hiding this comment.
Fix typos in the changeset description.
The release note has spelling issues (realworld, tempaltes) that should be corrected before publish.
✏️ Proposed fix
-Refactor parser to allow individual states to process multiple characters. This allows for eager scanning, simplifies things some, and improves performance by about 30% in realworld tempaltes.
+Refactor parser to allow individual states to process multiple characters. This allows for eager scanning, simplifies things some, and improves performance by about 30% in real-world templates.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Refactor parser to allow individual states to process multiple characters. This allows for eager scanning, simplifies things some, and improves performance by about 30% in realworld tempaltes. | |
| Refactor parser to allow individual states to process multiple characters. This allows for eager scanning, simplifies things some, and improves performance by about 30% in real-world templates. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.changeset/petite-signs-dance.md at line 5, Fix typos and small phrasing in
the changeset sentence that begins "Refactor parser to allow individual states
to process multiple characters." Replace "realworld" with "real-world", correct
"tempaltes" to "templates", and change "simplifies things some" to "simplifies
things somewhat" (or "simplifies things a bit") so the release note reads
clearly and correctly.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/states/STRING.ts (1)
22-43: 💤 Low valueEdge case: backslash at end of input may skip past EOF without proper handling.
If the input ends with an unescaped backslash (e.g.,
"test\), whenthis.posis at the backslash position (maxPos - 1), the code doesthis.pos += 2, settingpos = maxPos + 1. The loop then exits andemitErroris called, which is correct.However, the escape handling assumes there's always a character after the backslash. While the current behavior (advancing past EOF and emitting error) is functionally acceptable, consider adding a bounds check for clarity and to avoid reading past the string boundary in
data.charCodeAt()on subsequent iterations (though the loop condition prevents this).♻️ Optional: Add explicit bounds check for escaped character
case CODE.BACK_SLASH: - this.pos += 2; // skip \ and escaped char + this.pos += 2; // skip \ and escaped char + // If backslash was at end, loop will exit and emit EOF error break;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/states/STRING.ts` around lines 22 - 43, The parse method in STRING state can advance past EOF when encountering a trailing backslash; add an explicit bounds check when handling CODE.BACK_SLASH inside parse so you only do this.pos += 2 (or advance and consume the escaped char) if this.pos + 1 < maxPos, otherwise set this.pos = maxPos (or increment to maxPos), then break to let emitError(ErrorCode.INVALID_STRING, ...) run; update the BACK_SLASH branch in parse (referencing CODE.BACK_SLASH, this.pos, maxPos, data.charCodeAt, exitState, emitError) to avoid calling data.charCodeAt beyond bounds.src/states/CLOSE_TAG.ts (1)
72-78: 💤 Low valueSide-effect assignment inside object literal reduces readability.
The expression
end: (parser.pos += skip)at line 75 advancesparser.posas a side effect while constructing the range object. While this works correctly (start captures the old value, end captures the new value), it's subtle and could confuse future readers.♻️ Optional: Extract the position update for clarity
+ const closeTagStart = parser.pos; + parser.pos += skip; // Always succeeds since the closing tag name was matched above. ensureExpectedCloseTag(parser, { - start: parser.pos, - end: (parser.pos += skip), + start: closeTagStart, + end: parser.pos, });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/states/CLOSE_TAG.ts` around lines 72 - 78, The object literal passed to ensureExpectedCloseTag currently updates parser.pos as a side effect via end: (parser.pos += skip); instead, first compute the range by saving start = parser.pos; then advance parser.pos by skip (parser.pos += skip) and set end = parser.pos, and pass { start, end } to ensureExpectedCloseTag; update the block around ensureExpectedCloseTag, parser.pos, skip and keep the subsequent parser.exitState() and return true unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/states/CLOSE_TAG.ts`:
- Around line 72-78: The object literal passed to ensureExpectedCloseTag
currently updates parser.pos as a side effect via end: (parser.pos += skip);
instead, first compute the range by saving start = parser.pos; then advance
parser.pos by skip (parser.pos += skip) and set end = parser.pos, and pass {
start, end } to ensureExpectedCloseTag; update the block around
ensureExpectedCloseTag, parser.pos, skip and keep the subsequent
parser.exitState() and return true unchanged.
In `@src/states/STRING.ts`:
- Around line 22-43: The parse method in STRING state can advance past EOF when
encountering a trailing backslash; add an explicit bounds check when handling
CODE.BACK_SLASH inside parse so you only do this.pos += 2 (or advance and
consume the escaped char) if this.pos + 1 < maxPos, otherwise set this.pos =
maxPos (or increment to maxPos), then break to let
emitError(ErrorCode.INVALID_STRING, ...) run; update the BACK_SLASH branch in
parse (referencing CODE.BACK_SLASH, this.pos, maxPos, data.charCodeAt,
exitState, emitError) to avoid calling data.charCodeAt beyond bounds.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c266bf37-a163-4f2a-a9a2-8723dd497f3e
⛔ Files ignored due to path filters (53)
package-lock.jsonis excluded by!**/package-lock.jsonand included by**src/__tests__/fixtures/argument-attr-ternary/__snapshots__/argument-attr-ternary.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/attr-bound-concise/__snapshots__/attr-bound-concise.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/attr-eof-after-name/__snapshots__/attr-eof-after-name.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/attr-eof-default-value/__snapshots__/attr-eof-default-value.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/attr-eof-spread/__snapshots__/attr-eof-spread.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/attr-eof-unnamed/__snapshots__/attr-eof-unnamed.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/attr-grouped-bound/__snapshots__/attr-grouped-bound.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/attr-grouped-comma/__snapshots__/attr-grouped-comma.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/attr-name-hyphens-concise/__snapshots__/attr-name-hyphens-concise.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/attr-operators-crlf/__snapshots__/attr-operators-crlf.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/attr-ternary-as/__snapshots__/attr-ternary-as.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/attrs-crlf/__snapshots__/attrs-crlf.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/cdata-eof/__snapshots__/cdata-eof.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/close-tag-eof/__snapshots__/close-tag-eof.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/comment-js-line-eof/__snapshots__/comment-js-line-eof.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/concise-crlf/__snapshots__/concise-crlf.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/division-after-string/__snapshots__/division-after-string.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/division-after-template/__snapshots__/division-after-template.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/expression-mismatched-group-pair/__snapshots__/expression-mismatched-group-pair.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/html-comment-eof/__snapshots__/html-comment-eof.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/html-text-forward-slash/__snapshots__/html-text-forward-slash.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/invalid-attr-type-params/__snapshots__/invalid-attr-type-params.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/invalid-close-bracket-in-open-tag/__snapshots__/invalid-close-bracket-in-open-tag.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/invalid-code-after-comment-block/__snapshots__/invalid-code-after-comment-block.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/invalid-code-after-semicolon-angle/__snapshots__/invalid-code-after-semicolon-angle.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/invalid-code-after-semicolon-slash/__snapshots__/invalid-code-after-semicolon-slash.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/invalid-line-start-hyphen/__snapshots__/invalid-line-start-hyphen.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/invalid-line-start-slash/__snapshots__/invalid-line-start-slash.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/invalid-nested-attr-group/__snapshots__/invalid-nested-attr-group.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/invalid-tag-type-params-args/__snapshots__/invalid-tag-type-params-args.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/invalid-tag-types-after-args/__snapshots__/invalid-tag-types-after-args.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/mismatched-closing-tag-same-length/__snapshots__/mismatched-closing-tag-same-length.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/mismatched-closing-tag-shorthand/__snapshots__/mismatched-closing-tag-shorthand.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/multiline-html-block-blank-line-crlf/__snapshots__/multiline-html-block-blank-line-crlf.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/multiline-html-block-blank-line/__snapshots__/multiline-html-block-blank-line.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/multiline-html-block-trailing-space/__snapshots__/multiline-html-block-trailing-space.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/open-tag-comments-concise/__snapshots__/open-tag-comments-concise.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/parsed-string-eof/__snapshots__/parsed-string-eof.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/parsed-text-crlf/__snapshots__/parsed-text-crlf.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/parsed-text-empty-close/__snapshots__/parsed-text-empty-close.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/placeholder-empty/__snapshots__/placeholder-empty.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/regexp-eof/__snapshots__/regexp-eof.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/script-mismatched-close/__snapshots__/script-mismatched-close.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/scriptlet-block-space-semi-colon/__snapshots__/scriptlet-block-space-semi-colon.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/semicolon-comments/__snapshots__/semicolon-comments.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/tag-name-placeholder-empty/__snapshots__/tag-name-placeholder-empty.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/tag-name-placeholder-eof/__snapshots__/tag-name-placeholder-eof.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/tag-var-comma-concise/__snapshots__/tag-var-comma-concise.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/template-string-dollar/__snapshots__/template-string-dollar.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/template-string-empty-placeholder/__snapshots__/template-string-empty-placeholder.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/text-block-eof/__snapshots__/text-block-eof.expected.txtis excluded by!**/__snapshots__/**and included by**src/__tests__/fixtures/text-block-trailing-space/__snapshots__/text-block-trailing-space.expected.txtis excluded by!**/__snapshots__/**and included by**
📒 Files selected for processing (71)
.c8rc.jsonpackage.jsonsrc/__tests__/api.test.tssrc/__tests__/fixtures/argument-attr-ternary/input.markosrc/__tests__/fixtures/attr-bound-concise/input.markosrc/__tests__/fixtures/attr-eof-after-name/input.markosrc/__tests__/fixtures/attr-eof-default-value/input.markosrc/__tests__/fixtures/attr-eof-spread/input.markosrc/__tests__/fixtures/attr-eof-unnamed/input.markosrc/__tests__/fixtures/attr-grouped-bound/input.markosrc/__tests__/fixtures/attr-grouped-comma/input.markosrc/__tests__/fixtures/attr-name-hyphens-concise/input.markosrc/__tests__/fixtures/attr-operators-crlf/input.markosrc/__tests__/fixtures/attr-ternary-as/input.markosrc/__tests__/fixtures/attrs-crlf/input.markosrc/__tests__/fixtures/cdata-eof/input.markosrc/__tests__/fixtures/close-tag-eof/input.markosrc/__tests__/fixtures/comment-js-line-eof/input.markosrc/__tests__/fixtures/concise-crlf/input.markosrc/__tests__/fixtures/division-after-string/input.markosrc/__tests__/fixtures/division-after-template/input.markosrc/__tests__/fixtures/expression-mismatched-group-pair/input.markosrc/__tests__/fixtures/html-comment-eof/input.markosrc/__tests__/fixtures/html-text-forward-slash/input.markosrc/__tests__/fixtures/invalid-attr-type-params/input.markosrc/__tests__/fixtures/invalid-close-bracket-in-open-tag/input.markosrc/__tests__/fixtures/invalid-code-after-comment-block/input.markosrc/__tests__/fixtures/invalid-code-after-semicolon-angle/input.markosrc/__tests__/fixtures/invalid-code-after-semicolon-slash/input.markosrc/__tests__/fixtures/invalid-line-start-hyphen/input.markosrc/__tests__/fixtures/invalid-line-start-slash/input.markosrc/__tests__/fixtures/invalid-nested-attr-group/input.markosrc/__tests__/fixtures/invalid-tag-type-params-args/input.markosrc/__tests__/fixtures/invalid-tag-types-after-args/input.markosrc/__tests__/fixtures/mismatched-closing-tag-same-length/input.markosrc/__tests__/fixtures/mismatched-closing-tag-shorthand/input.markosrc/__tests__/fixtures/multiline-html-block-blank-line-crlf/input.markosrc/__tests__/fixtures/multiline-html-block-blank-line/input.markosrc/__tests__/fixtures/multiline-html-block-trailing-space/input.markosrc/__tests__/fixtures/open-tag-comments-concise/input.markosrc/__tests__/fixtures/parsed-string-eof/input.markosrc/__tests__/fixtures/parsed-text-crlf/input.markosrc/__tests__/fixtures/parsed-text-empty-close/input.markosrc/__tests__/fixtures/placeholder-empty/input.markosrc/__tests__/fixtures/regexp-eof/input.markosrc/__tests__/fixtures/script-mismatched-close/input.markosrc/__tests__/fixtures/scriptlet-block-space-semi-colon/input.markosrc/__tests__/fixtures/semicolon-comments/input.markosrc/__tests__/fixtures/tag-name-placeholder-empty/input.markosrc/__tests__/fixtures/tag-name-placeholder-eof/input.markosrc/__tests__/fixtures/tag-var-comma-concise/input.markosrc/__tests__/fixtures/template-string-dollar/input.markosrc/__tests__/fixtures/template-string-empty-placeholder/input.markosrc/__tests__/fixtures/text-block-eof/input.markosrc/__tests__/fixtures/text-block-trailing-space/input.markosrc/core/Parser.tssrc/states/CDATA.tssrc/states/CLOSE_TAG.tssrc/states/CONCISE_HTML_CONTENT.tssrc/states/DECLARATION.tssrc/states/DTD.tssrc/states/EXPRESSION.tssrc/states/HTML_COMMENT.tssrc/states/HTML_CONTENT.tssrc/states/JS_COMMENT_BLOCK.tssrc/states/JS_COMMENT_LINE.tssrc/states/PLACEHOLDER.tssrc/states/REGULAR_EXPRESSION.tssrc/states/STRING.tssrc/states/TAG_NAME.tssrc/util/validators.ts
💤 Files with no reviewable changes (1)
- src/states/TAG_NAME.ts
✅ Files skipped from review due to trivial changes (35)
- src/tests/fixtures/invalid-line-start-slash/input.marko
- src/tests/fixtures/comment-js-line-eof/input.marko
- src/tests/fixtures/tag-name-placeholder-empty/input.marko
- src/tests/fixtures/html-text-forward-slash/input.marko
- src/tests/fixtures/invalid-nested-attr-group/input.marko
- src/tests/fixtures/parsed-text-crlf/input.marko
- src/tests/fixtures/regexp-eof/input.marko
- src/tests/fixtures/attrs-crlf/input.marko
- src/tests/fixtures/text-block-trailing-space/input.marko
- src/tests/fixtures/invalid-close-bracket-in-open-tag/input.marko
- src/tests/fixtures/argument-attr-ternary/input.marko
- src/tests/fixtures/invalid-code-after-semicolon-angle/input.marko
- src/tests/fixtures/scriptlet-block-space-semi-colon/input.marko
- src/tests/fixtures/attr-name-hyphens-concise/input.marko
- src/tests/fixtures/template-string-dollar/input.marko
- src/tests/fixtures/attr-grouped-comma/input.marko
- src/tests/fixtures/invalid-code-after-comment-block/input.marko
- src/tests/fixtures/tag-var-comma-concise/input.marko
- src/tests/fixtures/parsed-string-eof/input.marko
- src/tests/fixtures/parsed-text-empty-close/input.marko
- src/tests/fixtures/mismatched-closing-tag-shorthand/input.marko
- src/tests/fixtures/invalid-tag-type-params-args/input.marko
- src/tests/fixtures/invalid-code-after-semicolon-slash/input.marko
- .c8rc.json
- src/tests/fixtures/attr-eof-spread/input.marko
- src/tests/fixtures/invalid-tag-types-after-args/input.marko
- src/tests/fixtures/attr-operators-crlf/input.marko
- src/tests/fixtures/template-string-empty-placeholder/input.marko
- src/tests/fixtures/script-mismatched-close/input.marko
- src/tests/fixtures/attr-eof-unnamed/input.marko
- src/tests/fixtures/attr-grouped-bound/input.marko
- src/tests/fixtures/mismatched-closing-tag-same-length/input.marko
- src/tests/fixtures/division-after-template/input.marko
- src/tests/fixtures/multiline-html-block-blank-line-crlf/input.marko
- src/tests/fixtures/expression-mismatched-group-pair/input.marko
🚧 Files skipped from review as they are similar to previous changes (10)
- src/states/DECLARATION.ts
- src/states/PLACEHOLDER.ts
- src/states/HTML_COMMENT.ts
- src/states/JS_COMMENT_BLOCK.ts
- src/states/HTML_CONTENT.ts
- src/states/REGULAR_EXPRESSION.ts
- src/states/CONCISE_HTML_CONTENT.ts
- src/util/validators.ts
- src/states/CDATA.ts
- src/states/EXPRESSION.ts
Refactor parser to allow individual states to process multiple characters. This allows for eager scanning, simplifies things some, and improves performance by about 30% in realworld tempaltes.