From 72b3379fb37d2c4e76976bcb4bb0312b376068a3 Mon Sep 17 00:00:00 2001 From: Dylan Piercey Date: Sat, 11 Mar 2023 14:54:52 -0700 Subject: [PATCH] fix: ensure unary expression continuation is a word boundary --- .changeset/stale-impalas-visit.md | 5 ++ .../attr-complex-unary.expected.txt | 80 +++++++++++++++++++ .../fixtures/attr-complex-unary/input.marko | 17 ++++ src/states/EXPRESSION.ts | 3 +- 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 .changeset/stale-impalas-visit.md create mode 100644 src/__tests__/fixtures/attr-complex-unary/__snapshots__/attr-complex-unary.expected.txt create mode 100644 src/__tests__/fixtures/attr-complex-unary/input.marko diff --git a/.changeset/stale-impalas-visit.md b/.changeset/stale-impalas-visit.md new file mode 100644 index 00000000..06c542de --- /dev/null +++ b/.changeset/stale-impalas-visit.md @@ -0,0 +1,5 @@ +--- +"htmljs-parser": patch +--- + +Fix regression where the parser would continue unary keyword expressions even if the keyword was inside a word boundary. Eg `
` would cause the parser to see the expression as `thing_` and `new x`. diff --git a/src/__tests__/fixtures/attr-complex-unary/__snapshots__/attr-complex-unary.expected.txt b/src/__tests__/fixtures/attr-complex-unary/__snapshots__/attr-complex-unary.expected.txt new file mode 100644 index 00000000..75c9de49 --- /dev/null +++ b/src/__tests__/fixtures/attr-complex-unary/__snapshots__/attr-complex-unary.expected.txt @@ -0,0 +1,80 @@ +1╭─ tag a = class b {} + │ │ │ │ ╰─ attrValue.value "class b {}" + │ │ │ ╰─ attrValue "= class b {}" + │ │ ╰─ attrName + ╰─ ╰─ tagName "tag" +2╭─ + ╰─ ╰─ openTagEnd +3╭─ tag a = class, b + │ │ │ │ │ ╰─ attrName + │ │ │ │ ╰─ attrValue.value "class" + │ │ │ ╰─ attrValue "= class" + │ │ ╰─ attrName + │ ├─ closeTagEnd(tag) + ╰─ ╰─ tagName "tag" +4╭─ + ╰─ ╰─ openTagEnd +5╭─ + │ ││ │ │ │ ││ │ ╰─ closeTagEnd(tag) + │ ││ │ │ │ ││ ╰─ closeTagName "tag" + │ ││ │ │ │ │╰─ closeTagStart " + │ ││ │ │ │ ╰─ openTagEnd:selfClosed "/>" + │ ││ │ │ ╰─ attrValue.value "class" + │ ││ │ ╰─ attrValue "= class" + │ ││ ╰─ attrName + │ │╰─ tagName "tag" + ╰─ ╰─ openTagStart +8├─ +9╭─ tag a = classthing b + │ │ │ │ │ ╰─ attrName + │ │ │ │ ╰─ attrValue.value "classthing" + │ │ │ ╰─ attrValue "= classthing" + │ │ ╰─ attrName + ╰─ ╰─ tagName "tag" +10╭─ + ╰─ ╰─ openTagEnd +11╭─ tag a = testclass b + │ │ │ │ │ ╰─ attrName + │ │ │ │ ╰─ attrValue.value "testclass" + │ │ │ ╰─ attrValue "= testclass" + │ │ ╰─ attrName + │ ├─ closeTagEnd(tag) + ╰─ ╰─ tagName "tag" +12╭─ + ╰─ ╰─ openTagEnd +13╭─ tag a = test_class b + │ │ │ │ │ ╰─ attrName + │ │ │ │ ╰─ attrValue.value "test_class" + │ │ │ ╰─ attrValue "= test_class" + │ │ ╰─ attrName + │ ├─ closeTagEnd(tag) + ╰─ ╰─ tagName "tag" +14╭─ + ╰─ ╰─ openTagEnd +15╭─ tag a = test$class b + │ │ │ │ │ ╰─ attrName + │ │ │ │ ╰─ attrValue.value "test$class" + │ │ │ ╰─ attrValue "= test$class" + │ │ ╰─ attrName + │ ├─ closeTagEnd(tag) + ╰─ ╰─ tagName "tag" +16╭─ + ╰─ ╰─ openTagEnd +17╭─ tag a = test+class b + │ │ │ │ ╰─ attrValue.value "test+class b" + │ │ │ ╰─ attrValue "= test+class b" + │ │ ╰─ attrName + │ ├─ closeTagEnd(tag) + ╰─ ╰─ tagName "tag" +18╭─ + │ ├─ openTagEnd + ╰─ ╰─ closeTagEnd(tag) \ No newline at end of file diff --git a/src/__tests__/fixtures/attr-complex-unary/input.marko b/src/__tests__/fixtures/attr-complex-unary/input.marko new file mode 100644 index 00000000..e9e73b2e --- /dev/null +++ b/src/__tests__/fixtures/attr-complex-unary/input.marko @@ -0,0 +1,17 @@ +tag a = class b {} + +tag a = class, b + + + + + +tag a = classthing b + +tag a = testclass b + +tag a = test_class b + +tag a = test$class b + +tag a = test+class b diff --git a/src/states/EXPRESSION.ts b/src/states/EXPRESSION.ts index d1954cbf..a0e91bfa 100644 --- a/src/states/EXPRESSION.ts +++ b/src/states/EXPRESSION.ts @@ -296,7 +296,8 @@ function lookBehindForOperator(data: string, pos: number): number { for (const keyword of unaryKeywords) { const keywordPos = lookBehindFor(data, curPos, keyword); if (keywordPos !== -1) { - return data.charCodeAt(keywordPos - 1) === CODE.PERIOD + const prevCode = data.charCodeAt(keywordPos - 1); + return prevCode === CODE.PERIOD || isWordCode(prevCode) ? -1 : keywordPos; }