diff --git a/.changeset/tidy-moles-train.md b/.changeset/tidy-moles-train.md
new file mode 100644
index 00000000..b7190f20
--- /dev/null
+++ b/.changeset/tidy-moles-train.md
@@ -0,0 +1,5 @@
+---
+"htmljs-parser": patch
+---
+
+Fix issue where parser would sometimes not consume enough characters and cause a bracket mismatch
diff --git a/package-lock.json b/package-lock.json
index d2d5075f..ef4c422f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "htmljs-parser",
- "version": "3.3.0",
+ "version": "3.3.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "htmljs-parser",
- "version": "3.3.0",
+ "version": "3.3.4",
"license": "MIT",
"devDependencies": {
"@changesets/changelog-github": "^0.4.4",
diff --git a/src/__tests__/fixtures/attr-method-shorthand/__snapshots__/attr-method-shorthand.expected.txt b/src/__tests__/fixtures/attr-method-shorthand/__snapshots__/attr-method-shorthand.expected.txt
index d17cc6e9..ba418c85 100644
--- a/src/__tests__/fixtures/attr-method-shorthand/__snapshots__/attr-method-shorthand.expected.txt
+++ b/src/__tests__/fixtures/attr-method-shorthand/__snapshots__/attr-method-shorthand.expected.txt
@@ -1,30 +1,66 @@
-1╭─
+ │ │ │ ││ ││╰─ openTagEnd:selfClosed "/>"
+ │ │ │ ││ │╰─ attrMethod.body.value
+ │ │ │ ││ ╰─ attrMethod.body "{}"
│ │ │ │╰─ attrMethod.params.value "event"
│ │ │ ├─ attrMethod.params "(event)"
- │ │ │ ╰─ attrMethod "(event) { \n console.log(\"hello\"); \n event.preventDefault();\n}"
+ │ │ │ ╰─ attrMethod "(event){}"
│ │ ╰─ attrName "onclick"
╰─ ╰─ tagName "foo"
-2├─ console.log("hello");
-3├─ event.preventDefault();
-4╭─ }/>
- ╰─ ╰─ openTagEnd:selfClosed "/>"
-5├─
-6╭─
+4├─ console.log("hello");
+5├─ event.preventDefault();
+6╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
-10├─
-11╭─
+ ╰─ ╰─ openTagEnd:selfClosed "/>"
+12├─
+13╭─
+ ╰─ ╰─ openTagEnd:selfClosed "/>"
+17├─
+18╭─
+ ╰─ ╰─ openTagEnd:selfClosed "/>"
+22├─
+23╭─
+24├─ console.log("hello");
+25├─ event.preventDefault();
+26╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
-15├─
-16╭─
+29├─ console.log("hello");
+30├─ event.preventDefault();
+31╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
-20├─
-21╭─ foo onclick(event) {
+32├─
+33╭─ foo onclick(event){
+ │ │ │ ││ │╰─ attrMethod.body.value " \n console.log(\"hello\"); \n event.preventDefault();\n"
+ │ │ │ ││ ╰─ attrMethod.body "{ \n console.log(\"hello\"); \n event.preventDefault();\n}"
+ │ │ │ │╰─ attrMethod.params.value "event"
+ │ │ │ ├─ attrMethod.params "(event)"
+ │ │ │ ╰─ attrMethod "(event){ \n console.log(\"hello\"); \n event.preventDefault();\n}"
+ │ │ ╰─ attrName "onclick"
+ ╰─ ╰─ tagName "foo"
+34├─ console.log("hello");
+35├─ event.preventDefault();
+36├─ }
+37╭─
+ ╰─ ╰─ openTagEnd
+38╭─ foo onclick(event) {
│ │ │ ││ │╰─ attrMethod.body.value " \n console.log(\"hello\"); \n event.preventDefault();\n"
│ │ │ ││ ╰─ attrMethod.body "{ \n console.log(\"hello\"); \n event.preventDefault();\n}"
│ │ │ │╰─ attrMethod.params.value "event"
│ │ │ ├─ attrMethod.params "(event)"
│ │ │ ╰─ attrMethod "(event) { \n console.log(\"hello\"); \n event.preventDefault();\n}"
│ │ ╰─ attrName "onclick"
+ │ ├─ closeTag(foo)
╰─ ╰─ tagName "foo"
-22├─ console.log("hello");
-23├─ event.preventDefault();
-24├─ }
-25╭─
+39├─ console.log("hello");
+40├─ event.preventDefault();
+41├─ }
+42╭─
╰─ ╰─ openTagEnd
-26╭─ foo onclick (event) {
+43╭─ foo onclick (event) {
│ │ │ ││ │╰─ attrMethod.body.value " \n console.log(\"hello\"); \n event.preventDefault();\n"
│ │ │ ││ ╰─ attrMethod.body "{ \n console.log(\"hello\"); \n event.preventDefault();\n}"
│ │ │ │╰─ attrMethod.params.value "event"
@@ -72,12 +122,26 @@
│ │ ╰─ attrName "onclick"
│ ├─ closeTag(foo)
╰─ ╰─ tagName "foo"
-27├─ console.log("hello");
-28├─ event.preventDefault();
-29├─ }
-30╭─
+44├─ console.log("hello");
+45├─ event.preventDefault();
+46├─ }
+47╭─
+ ╰─ ╰─ openTagEnd
+48╭─ foo(event){
+ │ │ ││ │╰─ attrMethod.body.value " \n console.log(\"hello\"); \n event.preventDefault();\n"
+ │ │ ││ ╰─ attrMethod.body "{ \n console.log(\"hello\"); \n event.preventDefault();\n}"
+ │ │ │╰─ attrMethod.params.value "event"
+ │ │ ├─ attrMethod.params "(event)"
+ │ │ ├─ attrMethod "(event){ \n console.log(\"hello\"); \n event.preventDefault();\n}"
+ │ │ ╰─ attrName
+ │ ├─ closeTag(foo)
+ ╰─ ╰─ tagName "foo"
+49├─ console.log("hello");
+50├─ event.preventDefault();
+51├─ }
+52╭─
╰─ ╰─ openTagEnd
-31╭─ foo(event) {
+53╭─ foo(event) {
│ │ ││ │╰─ attrMethod.body.value " \n console.log(\"hello\"); \n event.preventDefault();\n"
│ │ ││ ╰─ attrMethod.body "{ \n console.log(\"hello\"); \n event.preventDefault();\n}"
│ │ │╰─ attrMethod.params.value "event"
@@ -86,12 +150,12 @@
│ │ ╰─ attrName
│ ├─ closeTag(foo)
╰─ ╰─ tagName "foo"
-32├─ console.log("hello");
-33├─ event.preventDefault();
-34├─ }
-35╭─
+54├─ console.log("hello");
+55├─ event.preventDefault();
+56├─ }
+57╭─
╰─ ╰─ openTagEnd
-36╭─ foo (event) {
+58╭─ foo (event) {
│ │ ││ │╰─ attrMethod.body.value " \n console.log(\"hello\"); \n event.preventDefault();\n"
│ │ ││ ╰─ attrMethod.body "{ \n console.log(\"hello\"); \n event.preventDefault();\n}"
│ │ │╰─ attrMethod.params.value "event"
@@ -100,8 +164,8 @@
│ │ ╰─ attrName
│ ├─ closeTag(foo)
╰─ ╰─ tagName "foo"
-37├─ console.log("hello");
-38├─ event.preventDefault();
-39╭─ }
+59├─ console.log("hello");
+60├─ event.preventDefault();
+61╭─ }
│ ├─ closeTag(foo)
╰─ ╰─ openTagEnd
\ No newline at end of file
diff --git a/src/__tests__/fixtures/attr-method-shorthand/input.marko b/src/__tests__/fixtures/attr-method-shorthand/input.marko
index 73a823a2..89cc975e 100644
--- a/src/__tests__/fixtures/attr-method-shorthand/input.marko
+++ b/src/__tests__/fixtures/attr-method-shorthand/input.marko
@@ -1,3 +1,10 @@
+
+
+
+
+
+
+foo onclick(event){
+ console.log("hello");
+ event.preventDefault();
+}
+
foo onclick(event) {
console.log("hello");
event.preventDefault();
@@ -28,6 +45,11 @@ foo onclick (event) {
event.preventDefault();
}
+foo(event){
+ console.log("hello");
+ event.preventDefault();
+}
+
foo(event) {
console.log("hello");
event.preventDefault();
diff --git a/src/__tests__/fixtures/testing/__snapshots__/testing.expected.txt b/src/__tests__/fixtures/testing/__snapshots__/testing.expected.txt
deleted file mode 100644
index 1b6b2d1a..00000000
--- a/src/__tests__/fixtures/testing/__snapshots__/testing.expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-1╭─
- │ │ ││ │ │ ╰─ openTagEnd:selfClosed "/>"
- │ │ ││ │ ╰─ tagShorthandClass.quasis[1] "-again"
- │ │ ││ ╰─ tagShorthandClass.expressions[0] "${world}"
- │ │ │╰─ tagShorthandClass.quasis[0] "hello-"
- │ │ ╰─ tagShorthandClass ".hello-${world}-again"
- ╰─ ╰─ tagName "div"
\ No newline at end of file
diff --git a/src/__tests__/fixtures/testing/input.marko b/src/__tests__/fixtures/testing/input.marko
deleted file mode 100644
index 38247a3c..00000000
--- a/src/__tests__/fixtures/testing/input.marko
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/states/ATTRIBUTE.ts b/src/states/ATTRIBUTE.ts
index f554c6ec..869f6ee5 100644
--- a/src/states/ATTRIBUTE.ts
+++ b/src/states/ATTRIBUTE.ts
@@ -90,6 +90,7 @@ export const ATTRIBUTE: StateDefinition = {
(code === CODE.PERIOD && this.lookAheadFor(".."))
) {
attr.valueStart = this.pos;
+ this.forward = 0;
if (code === CODE.COLON) {
ensureAttrName(this, attr);
@@ -111,31 +112,29 @@ export const ATTRIBUTE: StateDefinition = {
expr.terminator = this.isConcise
? CONCISE_VALUE_TERMINATORS
: HTML_VALUE_TERMINATORS;
-
- this.pos--;
} else if (code === CODE.OPEN_PAREN) {
ensureAttrName(this, attr);
attr.stage = ATTR_STAGE.ARGUMENT;
this.pos++; // skip (
+ this.forward = 0;
this.enterState(STATE.EXPRESSION).terminator = CODE.CLOSE_PAREN;
- this.pos--;
} else if (code === CODE.OPEN_CURLY_BRACE && attr.args) {
ensureAttrName(this, attr);
attr.stage = ATTR_STAGE.BLOCK;
this.pos++; // skip {
+ this.forward = 0;
const expr = this.enterState(STATE.EXPRESSION);
expr.terminatedByWhitespace = false;
expr.terminator = CODE.CLOSE_CURLY_BRACE;
- this.pos--;
} else if (attr.stage === ATTR_STAGE.UNKNOWN) {
attr.stage = ATTR_STAGE.NAME;
+ this.forward = 0;
const expr = this.enterState(STATE.EXPRESSION);
expr.terminatedByWhitespace = true;
expr.skipOperators = true;
expr.terminator = this.isConcise
? CONCISE_NAME_TERMINATORS
: HTML_NAME_TERMINATORS;
- this.pos--;
} else {
this.exitState();
}
diff --git a/src/states/BEGIN_DELIMITED_HTML_BLOCK.ts b/src/states/BEGIN_DELIMITED_HTML_BLOCK.ts
index 1b63d82f..6e0e4711 100644
--- a/src/states/BEGIN_DELIMITED_HTML_BLOCK.ts
+++ b/src/states/BEGIN_DELIMITED_HTML_BLOCK.ts
@@ -39,8 +39,8 @@ export const BEGIN_DELIMITED_HTML_BLOCK: StateDefinition
const startPos = this.pos;
if (!this.consumeWhitespaceOnLine()) {
this.pos = startPos + 1;
+ this.forward = 0;
this.beginHtmlBlock(undefined, true);
- this.pos--;
}
}
},
diff --git a/src/states/CONCISE_HTML_CONTENT.ts b/src/states/CONCISE_HTML_CONTENT.ts
index b9a99b37..f910a336 100644
--- a/src/states/CONCISE_HTML_CONTENT.ts
+++ b/src/states/CONCISE_HTML_CONTENT.ts
@@ -119,7 +119,7 @@ export const CONCISE_HTML_CONTENT: StateDefinition = {
}
this.enterState(STATE.OPEN_TAG);
- this.pos--; // START_TAG_NAME expects to start at the first character
+ this.forward = 0; // START_TAG_NAME expects to start at the first character
}
},
diff --git a/src/states/INLINE_SCRIPT.ts b/src/states/INLINE_SCRIPT.ts
index 99cdbb0c..2b8c462d 100644
--- a/src/states/INLINE_SCRIPT.ts
+++ b/src/states/INLINE_SCRIPT.ts
@@ -38,17 +38,17 @@ export const INLINE_SCRIPT: StateDefinition = {
eof() {},
char(code, inlineScript) {
+ this.forward = 0;
+
if (code === CODE.OPEN_CURLY_BRACE) {
inlineScript.block = true;
this.pos++; // skip {
const expr = this.enterState(STATE.EXPRESSION);
expr.terminator = CODE.CLOSE_CURLY_BRACE;
expr.skipOperators = true;
- this.pos--;
} else {
const expr = this.enterState(STATE.EXPRESSION);
expr.terminatedByEOL = true;
- this.pos--;
}
},
diff --git a/src/states/OPEN_TAG.ts b/src/states/OPEN_TAG.ts
index 4514e0f1..35bc5cc3 100644
--- a/src/states/OPEN_TAG.ts
+++ b/src/states/OPEN_TAG.ts
@@ -287,11 +287,12 @@ export const OPEN_TAG: StateDefinition = {
// ignore whitespace within element...
} else if (code === CODE.COMMA) {
this.pos++; // skip ,
+ this.forward = 0;
this.consumeWhitespace();
- this.pos--;
} else if (code === CODE.FORWARD_SLASH && !tag.hasAttrs) {
tag.stage = TAG_STAGE.VAR;
this.pos++; // skip /
+ this.forward = 0;
if (isWhitespaceCode(this.lookAtCharCodeAhead(0))) {
return this.emitError(
@@ -306,7 +307,6 @@ export const OPEN_TAG: StateDefinition = {
expr.terminator = this.isConcise
? CONCISE_TAG_VAR_TERMINATORS
: HTML_TAG_VAR_TERMINATORS;
- this.pos--;
} else if (code === CODE.OPEN_PAREN && !tag.hasAttrs) {
if (tag.hasArgs) {
this.emitError(
@@ -318,26 +318,26 @@ export const OPEN_TAG: StateDefinition = {
}
tag.stage = TAG_STAGE.ARGUMENT;
this.pos++; // skip (
+ this.forward = 0;
const expr = this.enterState(STATE.EXPRESSION);
expr.skipOperators = true;
expr.terminator = CODE.CLOSE_PAREN;
- this.pos--;
} else if (code === CODE.PIPE && !tag.hasAttrs) {
tag.stage = TAG_STAGE.PARAMS;
this.pos++; // skip |
+ this.forward = 0;
const expr = this.enterState(STATE.EXPRESSION);
expr.skipOperators = true;
expr.terminator = CODE.PIPE;
- this.pos--;
} else {
+ this.forward = 0;
+
if (tag.tagName) {
this.enterState(STATE.ATTRIBUTE);
tag.hasAttrs = true;
} else {
this.enterState(STATE.TAG_NAME);
}
-
- this.pos--;
}
},
@@ -381,7 +381,7 @@ export const OPEN_TAG: StateDefinition = {
attr.start = start;
attr.args = { start, end, value };
tag.hasAttrs = true;
- this.pos--;
+ this.forward = 0;
} else {
tag.hasArgs = true;
this.options.onTagArgs?.({
diff --git a/src/states/PLACEHOLDER.ts b/src/states/PLACEHOLDER.ts
index 37de2e54..c3a887db 100644
--- a/src/states/PLACEHOLDER.ts
+++ b/src/states/PLACEHOLDER.ts
@@ -93,8 +93,8 @@ export function checkForPlaceholder(parser: Parser, code: number) {
parser.endText();
parser.enterState(PLACEHOLDER).escape = escape;
parser.pos += escape ? 2 : 3; // skip ${ or $!{
+ parser.forward = 0;
parser.enterState(STATE.EXPRESSION).terminator = CODE.CLOSE_CURLY_BRACE;
- parser.pos--;
return true;
}
}
diff --git a/src/states/TAG_NAME.ts b/src/states/TAG_NAME.ts
index 1e35fa0a..e53a0d08 100644
--- a/src/states/TAG_NAME.ts
+++ b/src/states/TAG_NAME.ts
@@ -110,8 +110,8 @@ export const TAG_NAME: StateDefinition = {
this.lookAtCharCodeAhead(1) === CODE.OPEN_CURLY_BRACE
) {
this.pos += 2; // skip ${
+ this.forward = 0;
this.enterState(STATE.EXPRESSION).terminator = CODE.CLOSE_CURLY_BRACE;
- this.pos--;
} else if (
isWhitespaceCode(code) ||
code === CODE.EQUAL ||