From 4685dac18f90090871b9a093397d96419f53cdff Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 26 Sep 2025 20:55:01 +0800 Subject: [PATCH 1/3] fix(compiler-core): improve handling of v-bind shorthand for in-DOM templates --- .../src/transforms/transformVBindShorthand.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/compiler-core/src/transforms/transformVBindShorthand.ts b/packages/compiler-core/src/transforms/transformVBindShorthand.ts index ff5b51047a3..d91d56ba141 100644 --- a/packages/compiler-core/src/transforms/transformVBindShorthand.ts +++ b/packages/compiler-core/src/transforms/transformVBindShorthand.ts @@ -15,7 +15,12 @@ export const transformVBindShorthand: NodeTransform = (node, context) => { if ( prop.type === NodeTypes.DIRECTIVE && prop.name === 'bind' && - !prop.exp + (!prop.exp || + // #13930 :foo will be processed as :foo="" in in-DOM template + (__BROWSER__ && + prop.exp.type === NodeTypes.SIMPLE_EXPRESSION && + !prop.exp.isStatic && + !prop.exp.content)) ) { const arg = prop.arg! if (arg.type !== NodeTypes.SIMPLE_EXPRESSION || !arg.isStatic) { From 8536f8907da9f8fc9030f4469ce2938095650bdb Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 26 Sep 2025 22:02:29 +0800 Subject: [PATCH 2/3] test: add test --- .../__tests__/transforms/vBind.spec.ts | 21 +++++++++++++++++++ packages/compiler-core/src/parser.ts | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/compiler-core/__tests__/transforms/vBind.spec.ts b/packages/compiler-core/__tests__/transforms/vBind.spec.ts index 2221d926e52..3c4b290e8b0 100644 --- a/packages/compiler-core/__tests__/transforms/vBind.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vBind.spec.ts @@ -112,6 +112,27 @@ describe('compiler: transform v-bind', () => { }) }) + test('no expression (shorthand) in-DOM template', () => { + try { + __BROWSER__ = true + // in-DOM templates will be parsed by the browser into :id="" + const node = parseWithVBind(`
`) + const props = (node.codegenNode as VNodeCall).props as ObjectExpression + expect(props.properties[0]).toMatchObject({ + key: { + content: `id`, + isStatic: true, + }, + value: { + content: `id`, + isStatic: false, + }, + }) + } finally { + __BROWSER__ = false + } + }) + test('dynamic arg', () => { const node = parseWithVBind(`
`) const props = (node.codegenNode as VNodeCall).props as CallExpression diff --git a/packages/compiler-core/src/parser.ts b/packages/compiler-core/src/parser.ts index a6e25681d75..2d85289fc68 100644 --- a/packages/compiler-core/src/parser.ts +++ b/packages/compiler-core/src/parser.ts @@ -1054,7 +1054,7 @@ export function baseParse(input: string, options?: ParserOptions): RootNode { `[@vue/compiler-core] decodeEntities option is passed but will be ` + `ignored in non-browser builds.`, ) - } else if (__BROWSER__ && !currentOptions.decodeEntities) { + } else if (__BROWSER__ && !__TEST__ && !currentOptions.decodeEntities) { throw new Error( `[@vue/compiler-core] decodeEntities option is required in browser builds.`, ) From 338b236dab47f1fedad0d9aa675d6a6b7ab9b4af Mon Sep 17 00:00:00 2001 From: daiwei Date: Sun, 28 Sep 2025 09:31:09 +0800 Subject: [PATCH 3/3] chore: tweaks --- packages/compiler-core/__tests__/transforms/vBind.spec.ts | 4 ++-- .../compiler-core/src/transforms/transformVBindShorthand.ts | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/compiler-core/__tests__/transforms/vBind.spec.ts b/packages/compiler-core/__tests__/transforms/vBind.spec.ts index 3c4b290e8b0..ff21351d05d 100644 --- a/packages/compiler-core/__tests__/transforms/vBind.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vBind.spec.ts @@ -112,10 +112,10 @@ describe('compiler: transform v-bind', () => { }) }) - test('no expression (shorthand) in-DOM template', () => { + test('no expression (shorthand) in-DOM templates', () => { try { __BROWSER__ = true - // in-DOM templates will be parsed by the browser into :id="" + // :id in in-DOM templates will be parsed into :id="" by browser const node = parseWithVBind(`
`) const props = (node.codegenNode as VNodeCall).props as ObjectExpression expect(props.properties[0]).toMatchObject({ diff --git a/packages/compiler-core/src/transforms/transformVBindShorthand.ts b/packages/compiler-core/src/transforms/transformVBindShorthand.ts index d91d56ba141..a6b989e734a 100644 --- a/packages/compiler-core/src/transforms/transformVBindShorthand.ts +++ b/packages/compiler-core/src/transforms/transformVBindShorthand.ts @@ -16,11 +16,10 @@ export const transformVBindShorthand: NodeTransform = (node, context) => { prop.type === NodeTypes.DIRECTIVE && prop.name === 'bind' && (!prop.exp || - // #13930 :foo will be processed as :foo="" in in-DOM template + // #13930 :foo in in-DOM templates will be parsed into :foo="" by browser (__BROWSER__ && prop.exp.type === NodeTypes.SIMPLE_EXPRESSION && - !prop.exp.isStatic && - !prop.exp.content)) + !prop.exp.content.trim())) ) { const arg = prop.arg! if (arg.type !== NodeTypes.SIMPLE_EXPRESSION || !arg.isStatic) {