From 8a988f48e976f7ad68494a059973176ecfd43462 Mon Sep 17 00:00:00 2001 From: Dylan Piercey Date: Mon, 23 May 2022 14:41:30 -0700 Subject: [PATCH] fix: bracket mismatch issue (#108) --- .changeset/tidy-moles-train.md | 5 + package-lock.json | 4 +- .../attr-method-shorthand.expected.txt | 160 ++++++++++++------ .../attr-method-shorthand/input.marko | 22 +++ .../__snapshots__/testing.expected.txt | 7 - src/__tests__/fixtures/testing/input.marko | 1 - src/states/ATTRIBUTE.ts | 9 +- src/states/BEGIN_DELIMITED_HTML_BLOCK.ts | 2 +- src/states/CONCISE_HTML_CONTENT.ts | 2 +- src/states/INLINE_SCRIPT.ts | 4 +- src/states/OPEN_TAG.ts | 14 +- src/states/PLACEHOLDER.ts | 2 +- src/states/TAG_NAME.ts | 2 +- 13 files changed, 158 insertions(+), 76 deletions(-) create mode 100644 .changeset/tidy-moles-train.md delete mode 100644 src/__tests__/fixtures/testing/__snapshots__/testing.expected.txt delete mode 100644 src/__tests__/fixtures/testing/input.marko 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 ||