diff --git a/.changeset/bright-clouds-yawn.md b/.changeset/bright-clouds-yawn.md new file mode 100644 index 00000000..d28a9096 --- /dev/null +++ b/.changeset/bright-clouds-yawn.md @@ -0,0 +1,5 @@ +--- +"htmljs-parser": patch +--- + +Avoid continuing expressions after a period if after the whitespace is something that could not be an identifier. diff --git a/src/__tests__/fixtures/scriptlet-eof/__snapshots__/scriptlet-eof.expected.txt b/src/__tests__/fixtures/scriptlet-eof/__snapshots__/scriptlet-eof.expected.txt new file mode 100644 index 00000000..b83cf54f --- /dev/null +++ b/src/__tests__/fixtures/scriptlet-eof/__snapshots__/scriptlet-eof.expected.txt @@ -0,0 +1,27 @@ +1╭─ $ $global. + │ │╰─ scriptlet.value "$global." + ╰─ ╰─ scriptlet " $global." +2├─ +3╭─
+ │ ││ ╰─ openTagEnd + │ │╰─ tagName "header" + ╰─ ╰─ openTagStart +4╭─ + │ │ ││ ││ │ ││ │ ││ ╰─ openTagEnd:selfClosed "/>" + │ │ ││ ││ │ ││ │ │╰─ attrValue.value "\"Marko\"" + │ │ ││ ││ │ ││ │ ╰─ attrValue "=\"Marko\"" + │ │ ││ ││ │ ││ ╰─ attrName "alt" + │ │ ││ ││ │ │╰─ attrValue.value "logo" + │ │ ││ ││ │ ╰─ attrValue "=logo" + │ │ ││ ││ ╰─ attrName "src" + │ │ ││ │╰─ tagShorthandClass.quasis[0] "logo" + │ │ ││ ╰─ tagShorthandClass ".logo" + │ │ │╰─ tagName "img" + │ │ ╰─ openTagStart + ╰─ ╰─ text "\n " +5╭─
+ │ │ │ ╰─ closeTagEnd(header) + │ │ ╰─ closeTagName "header" + │ ├─ text "\n" + ╰─ ╰─ closeTagStart " + + diff --git a/src/states/EXPRESSION.ts b/src/states/EXPRESSION.ts index c1ca28c1..d1954cbf 100644 --- a/src/states/EXPRESSION.ts +++ b/src/states/EXPRESSION.ts @@ -266,12 +266,17 @@ function lookBehindForOperator(data: string, pos: number): number { case CODE.OPEN_ANGLE_BRACKET: case CODE.CLOSE_ANGLE_BRACKET: case CODE.PERCENT: - case CODE.PERIOD: case CODE.PIPE: case CODE.QUESTION: case CODE.TILDE: return curPos; + case CODE.PERIOD: { + // Only matches `.` followed by something that could be an identifier. + const nextPos = lookAheadWhile(isWhitespaceCode, data, pos); + return isWordCode(data.charCodeAt(nextPos)) ? nextPos : -1; + } + // special case -- and ++ case CODE.PLUS: case CODE.HYPHEN: { @@ -324,9 +329,11 @@ function lookAheadForOperator(data: string, pos: number): number { case CODE.OPEN_PAREN: return pos; // defers to base expression state to track block groups. - case CODE.PERIOD: - // Only match a dot if its not ... - return data.charCodeAt(pos + 1) === CODE.PERIOD ? -1 : pos + 1; + case CODE.PERIOD: { + // Only matches `.` followed by something that could be an identifier. + const nextPos = lookAheadWhile(isWhitespaceCode, data, pos + 1); + return isWordCode(data.charCodeAt(nextPos)) ? nextPos : -1; + } default: { for (const keyword of binaryKeywords) { @@ -383,6 +390,7 @@ function isWordCode(code: number) { (code >= CODE.UPPER_A && code <= CODE.UPPER_Z) || (code >= CODE.LOWER_A && code <= CODE.LOWER_Z) || (code >= CODE.NUMBER_0 && code <= CODE.NUMBER_9) || + code == CODE.DOLLAR || code === CODE.UNDERSCORE ); }