From 2bb0c7cac15c799d3cb62c50cb7682306da32796 Mon Sep 17 00:00:00 2001 From: dpiercey Date: Mon, 11 Aug 2025 11:26:00 -0700 Subject: [PATCH] fix: ts unary operator only in ts expression --- .changeset/khaki-years-mix.md | 5 +++++ .../ts-unary-exression.expected.txt | 21 +++++++++++++++++++ .../fixtures/ts-unary-exression/input.marko | 2 ++ src/states/ATTRIBUTE.ts | 6 ++++-- src/states/EXPRESSION.ts | 16 +++++++++----- src/states/OPEN_TAG.ts | 9 +++++--- 6 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 .changeset/khaki-years-mix.md create mode 100644 src/__tests__/fixtures/ts-unary-exression/__snapshots__/ts-unary-exression.expected.txt create mode 100644 src/__tests__/fixtures/ts-unary-exression/input.marko diff --git a/.changeset/khaki-years-mix.md b/.changeset/khaki-years-mix.md new file mode 100644 index 00000000..62e83b82 --- /dev/null +++ b/.changeset/khaki-years-mix.md @@ -0,0 +1,5 @@ +--- +"htmljs-parser": patch +--- + +Fix regression where typescript unary operators were being used in javascript contexts. diff --git a/src/__tests__/fixtures/ts-unary-exression/__snapshots__/ts-unary-exression.expected.txt b/src/__tests__/fixtures/ts-unary-exression/__snapshots__/ts-unary-exression.expected.txt new file mode 100644 index 00000000..9432b259 --- /dev/null +++ b/src/__tests__/fixtures/ts-unary-exression/__snapshots__/ts-unary-exression.expected.txt @@ -0,0 +1,21 @@ +1╭─ div foo=readonly bar=true + │ │ │ ││ │ │╰─ attrValue.value "true" + │ │ │ ││ │ ╰─ attrValue "=true" + │ │ │ ││ ╰─ attrName "bar" + │ │ │ │╰─ attrValue.value "readonly" + │ │ │ ╰─ attrValue "=readonly" + │ │ ╰─ attrName "foo" + ╰─ ╰─ tagName "div" +2╭─ div foo=val as readonly string[] bar=true + │ │ │ ││ │ │╰─ attrValue.value "true" + │ │ │ ││ │ ╰─ attrValue "=true" + │ │ │ ││ ╰─ attrName "bar" + │ │ │ │╰─ attrValue.value "val as readonly string[]" + │ │ │ ╰─ attrValue "=val as readonly string[]" + │ │ ╰─ attrName "foo" + │ ├─ closeTagEnd(div) + │ ├─ openTagEnd + ╰─ ╰─ tagName "div" +3╭─ + │ ├─ openTagEnd + ╰─ ╰─ closeTagEnd(div) \ No newline at end of file diff --git a/src/__tests__/fixtures/ts-unary-exression/input.marko b/src/__tests__/fixtures/ts-unary-exression/input.marko new file mode 100644 index 00000000..4d4c4a34 --- /dev/null +++ b/src/__tests__/fixtures/ts-unary-exression/input.marko @@ -0,0 +1,2 @@ +div foo=readonly bar=true +div foo=val as readonly string[] bar=true diff --git a/src/states/ATTRIBUTE.ts b/src/states/ATTRIBUTE.ts index 041e6328..95943430 100644 --- a/src/states/ATTRIBUTE.ts +++ b/src/states/ATTRIBUTE.ts @@ -105,8 +105,10 @@ export const ATTRIBUTE: StateDefinition = { attr.stage = ATTR_STAGE.TYPE_PARAMS; this.pos++; // skip < this.forward = 0; - this.enterState(STATE.EXPRESSION).shouldTerminate = - matchesCloseAngleBracket; + const expr = this.enterState(STATE.EXPRESSION); + expr.inType = true; + expr.forceType = true; + expr.shouldTerminate = matchesCloseAngleBracket; } else if (code === CODE.OPEN_CURLY_BRACE && attr.args) { ensureAttrName(this, attr); attr.stage = ATTR_STAGE.BLOCK; diff --git a/src/states/EXPRESSION.ts b/src/states/EXPRESSION.ts index ca0f6a61..05274824 100644 --- a/src/states/EXPRESSION.ts +++ b/src/states/EXPRESSION.ts @@ -31,19 +31,23 @@ export interface ExpressionMeta extends Meta { const shouldTerminate = () => false; const unaryKeywords = [ - "asserts", "async", "await", "class", "function", + "new", + "typeof", + "void", +] as const; + +const tsUnaryKeywords = [ + ...unaryKeywords, + "asserts", "infer", "is", "keyof", - "new", "readonly", - "typeof", "unique", - "void", ] as const; const binaryKeywords = [ @@ -404,7 +408,9 @@ function lookBehindForOperator( } default: { - for (const keyword of unaryKeywords) { + for (const keyword of expression.inType + ? tsUnaryKeywords + : unaryKeywords) { const keywordPos = lookBehindFor(data, curPos, keyword); if (keywordPos !== -1) { return isWordOrPeriodCode(data.charCodeAt(keywordPos - 1)) diff --git a/src/states/OPEN_TAG.ts b/src/states/OPEN_TAG.ts index 69093b41..35596fb1 100644 --- a/src/states/OPEN_TAG.ts +++ b/src/states/OPEN_TAG.ts @@ -343,12 +343,15 @@ export const OPEN_TAG: StateDefinition = { this.enterState(STATE.EXPRESSION).shouldTerminate = matchesPipe; break; - case CODE.OPEN_ANGLE_BRACKET: + case CODE.OPEN_ANGLE_BRACKET: { tag.stage = TAG_STAGE.TYPES; this.pos++; // skip < - this.enterState(STATE.EXPRESSION).shouldTerminate = - matchesCloseAngleBracket; + const expr = this.enterState(STATE.EXPRESSION); + expr.inType = true; + expr.forceType = true; + expr.shouldTerminate = matchesCloseAngleBracket; break; + } default: tag.hasAttrs = true;