From 6f8ea357b2b6eb0222a67addfea2d795051627da Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 20 Jun 2025 08:08:34 +0800 Subject: [PATCH 01/18] fix(runtime-vapor): should not fallthrough emit handlers to vdom child (#13500) --- packages/runtime-core/src/index.ts | 4 ++ .../__tests__/componentAttrs.spec.ts | 48 ++++++++++++++++++- packages/runtime-vapor/src/vdomInterop.ts | 11 ++++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index e309554f2..1ed6f21df 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -557,3 +557,7 @@ export { startMeasure, endMeasure } from './profiling' * @internal */ export { initFeatureFlags } from './featureFlags' +/** + * @internal + */ +export { createInternalObject } from './internalObject' diff --git a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts index fc6fae5f0..204dd5990 100644 --- a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts +++ b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts @@ -1,4 +1,11 @@ -import { type Ref, nextTick, ref } from '@vue/runtime-dom' +import { + type Ref, + createApp, + defineComponent, + h, + nextTick, + ref, +} from '@vue/runtime-dom' import { createComponent, defineVaporComponent, @@ -8,6 +15,7 @@ import { setProp, setStyle, template, + vaporInteropPlugin, } from '../src' import { makeRender } from './_utils' import { stringifyStyle } from '@vue/shared' @@ -361,4 +369,42 @@ describe('attribute fallthrough', () => { const el = host.children[0] expect(el.classList.length).toBe(0) }) + + it('should not fallthrough emit handlers to vdom child', () => { + const VDomChild = defineComponent({ + emits: ['click'], + setup(_, { emit }) { + return () => h('button', { onClick: () => emit('click') }, 'click me') + }, + }) + + const fn = vi.fn() + const VaporChild = defineVaporComponent({ + emits: ['click'], + setup() { + return createComponent( + VDomChild as any, + { onClick: () => fn }, + null, + true, + ) + }, + }) + + const App = { + setup() { + return () => h(VaporChild as any) + }, + } + + const root = document.createElement('div') + createApp(App).use(vaporInteropPlugin).mount(root) + + expect(root.innerHTML).toBe('') + const button = root.querySelector('button')! + button.dispatchEvent(new Event('click')) + + // fn should be called once + expect(fn).toHaveBeenCalledTimes(1) + }) }) diff --git a/packages/runtime-vapor/src/vdomInterop.ts b/packages/runtime-vapor/src/vdomInterop.ts index 77228fd72..b916a2c8e 100644 --- a/packages/runtime-vapor/src/vdomInterop.ts +++ b/packages/runtime-vapor/src/vdomInterop.ts @@ -9,9 +9,11 @@ import { type Slots, type VNode, type VaporInteropInterface, + createInternalObject, createVNode, currentInstance, ensureRenderer, + isEmitListener, onScopeDispose, renderSlot, shallowRef, @@ -162,7 +164,14 @@ function createVDOMComponent( // overwrite how the vdom instance handles props vnode.vi = (instance: ComponentInternalInstance) => { instance.props = wrapper.props - instance.attrs = wrapper.attrs + + const attrs = (instance.attrs = createInternalObject()) + for (const key in wrapper.attrs) { + if (!isEmitListener(instance.emitsOptions, key)) { + attrs[key] = wrapper.attrs[key] + } + } + instance.slots = wrapper.slots === EMPTY_OBJ ? EMPTY_OBJ From cb925112f53018b43ce62b6605aa3b4e87b813c6 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 20 Jun 2025 08:11:05 +0800 Subject: [PATCH 02/18] fix(compiler-vapor): don't generate default slot for whitespace when preserved (#13009) --- .../__snapshots__/vSlot.spec.ts.snap | 65 +++++++++++++++++++ .../__tests__/transforms/vSlot.spec.ts | 56 ++++++++++++++++ .../src/transforms/transformText.ts | 11 +++- .../compiler-vapor/src/transforms/vSlot.ts | 21 ++++-- 4 files changed, 146 insertions(+), 7 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap index 5d31be656..f296d7257 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap @@ -274,3 +274,68 @@ export function render(_ctx) { return n6 }" `; + +exports[`compiler: transform slot > with whitespace: 'preserve' > implicit default slot 1`] = ` +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template(" Header ") +const t1 = _template(" ") +const t2 = _template("

") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n4 = _createComponentWithFallback(_component_Comp, null, { + "header": () => { + const n0 = t0() + return n0 + }, + "default": () => { + const n2 = t1() + const n3 = t2() + return [n2, n3] + } + }, true) + return n4 +}" +`; + +exports[`compiler: transform slot > with whitespace: 'preserve' > named default slot + implicit whitespace content 1`] = ` +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template(" Header ") +const t1 = _template(" Default ") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n5 = _createComponentWithFallback(_component_Comp, null, { + "header": () => { + const n0 = t0() + return n0 + }, + "default": () => { + const n3 = t1() + return n3 + } + }, true) + return n5 +}" +`; + +exports[`compiler: transform slot > with whitespace: 'preserve' > should not generate whitespace only default slot 1`] = ` +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template(" Header ") +const t1 = _template(" Footer ") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n5 = _createComponentWithFallback(_component_Comp, null, { + "header": () => { + const n0 = t0() + return n0 + }, + "footer": () => { + const n3 = t1() + return n3 + } + }, true) + return n5 +}" +`; diff --git a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts index 978f988d5..8db1386b3 100644 --- a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts @@ -509,4 +509,60 @@ describe('compiler: transform slot', () => { }) }) }) + + describe(`with whitespace: 'preserve'`, () => { + test('named default slot + implicit whitespace content', () => { + const source = ` + + + + + ` + const { code } = compileWithSlots(source, { + whitespace: 'preserve', + }) + + expect( + `Extraneous children found when component already has explicitly named default slot.`, + ).not.toHaveBeenWarned() + expect(code).toMatchSnapshot() + }) + + test('implicit default slot', () => { + const source = ` + + +

+ + ` + const { code } = compileWithSlots(source, { + whitespace: 'preserve', + }) + + expect( + `Extraneous children found when component already has explicitly named default slot.`, + ).not.toHaveBeenWarned() + expect(code).toMatchSnapshot() + }) + + test('should not generate whitespace only default slot', () => { + const source = ` + + + + + ` + const { code, ir } = compileWithSlots(source, { + whitespace: 'preserve', + }) + + const slots = (ir.block.dynamic.children[0].operation as any).slots[0] + .slots + // should be: header, footer (no default) + expect(Object.keys(slots).length).toBe(2) + expect(!!slots['default']).toBe(false) + + expect(code).toMatchSnapshot() + }) + }) }) diff --git a/packages/compiler-vapor/src/transforms/transformText.ts b/packages/compiler-vapor/src/transforms/transformText.ts index 7cdb1b1a7..5f858058f 100644 --- a/packages/compiler-vapor/src/transforms/transformText.ts +++ b/packages/compiler-vapor/src/transforms/transformText.ts @@ -23,6 +23,13 @@ const seen = new WeakMap< WeakSet >() +export function markNonTemplate( + node: TemplateChildNode, + context: TransformContext, +): void { + seen.get(context.root)!.add(node) +} + export const transformText: NodeTransform = (node, context) => { if (!seen.has(context.root)) seen.set(context.root, new WeakSet()) if (seen.get(context.root)!.has(node)) { @@ -68,7 +75,7 @@ export const transformText: NodeTransform = (node, context) => { prev.type === NodeTypes.TEXT ) { // mark leading text node for skipping - seen.get(context.root)!.add(prev) + markNonTemplate(prev, context) } } } @@ -143,7 +150,7 @@ function processTextContainer( } function createTextLikeExpression(node: TextLike, context: TransformContext) { - seen.get(context.root)!.add(node) + markNonTemplate(node, context) if (node.type === NodeTypes.TEXT) { return createSimpleExpression(node.content, true, node.loc) } else { diff --git a/packages/compiler-vapor/src/transforms/vSlot.ts b/packages/compiler-vapor/src/transforms/vSlot.ts index d1bf1c6b0..d1f08d614 100644 --- a/packages/compiler-vapor/src/transforms/vSlot.ts +++ b/packages/compiler-vapor/src/transforms/vSlot.ts @@ -24,6 +24,7 @@ import { type VaporDirectiveNode, } from '../ir' import { findDir, resolveExpression } from '../utils' +import { markNonTemplate } from './transformText' export const transformVSlot: NodeTransform = (node, context) => { if (node.type !== NodeTypes.ELEMENT) return @@ -66,11 +67,21 @@ function transformComponentSlot( ) { const { children } = node const arg = dir && dir.arg - const nonSlotTemplateChildren = children.filter( - n => - isNonWhitespaceContent(node) && - !(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot)), - ) + + // whitespace: 'preserve' + const emptyTextNodes: TemplateChildNode[] = [] + const nonSlotTemplateChildren = children.filter(n => { + if (isNonWhitespaceContent(n)) { + return !(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot)) + } else { + emptyTextNodes.push(n) + } + }) + if (!nonSlotTemplateChildren.length) { + emptyTextNodes.forEach(n => { + markNonTemplate(n, context) + }) + } const [block, onExit] = createSlotBlock(node, dir, context) From a6e49662571e94c9e11c0ad1d783f47c1727bf7d Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 20 Jun 2025 08:11:40 +0800 Subject: [PATCH 03/18] fix(runtime-vapor): dynamic component fallback rendering with insertion state (#13492) --- .../apiCreateDynamicComponent.spec.ts | 31 +++++++++++++++++-- packages/runtime-vapor/src/component.ts | 12 +++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts b/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts index 2f6cd7b3f..87dc922dd 100644 --- a/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts +++ b/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts @@ -1,6 +1,13 @@ -import { shallowRef } from '@vue/reactivity' -import { nextTick } from '@vue/runtime-dom' -import { createDynamicComponent } from '../src' +import { ref, shallowRef } from '@vue/reactivity' +import { nextTick, resolveDynamicComponent } from '@vue/runtime-dom' +import { + createComponentWithFallback, + createDynamicComponent, + renderEffect, + setHtml, + setInsertionState, + template, +} from '../src' import { makeRender } from './_utils' const define = makeRender() @@ -54,4 +61,22 @@ describe('api: createDynamicComponent', () => { await nextTick() expect(html()).toBe('') }) + + test('render fallback with insertionState', async () => { + const { html, mount } = define({ + setup() { + const html = ref('hi') + const n1 = template('

', true)() as any + setInsertionState(n1) + const n0 = createComponentWithFallback( + resolveDynamicComponent('button') as any, + ) as any + renderEffect(() => setHtml(n0, html.value)) + return n1 + }, + }).create() + + mount() + expect(html()).toBe('
') + }) }) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 03675475b..c6602ec96 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -476,6 +476,14 @@ export function createComponentWithFallback( return createComponent(comp, rawProps, rawSlots, isSingleRoot) } + const _insertionParent = insertionParent + const _insertionAnchor = insertionAnchor + if (isHydrating) { + locateHydrationNode() + } else { + resetInsertionState() + } + const el = document.createElement(comp) // mark single root ;(el as any).$root = isSingleRoot @@ -494,6 +502,10 @@ export function createComponentWithFallback( } } + if (!isHydrating && _insertionParent) { + insert(el, _insertionParent, _insertionAnchor) + } + return el } From ed05053fc151e9576e02816df5635972b02bd7b9 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 20 Jun 2025 08:16:47 +0800 Subject: [PATCH 04/18] fix(compiler-vapor): prevent caching UpdateExpression (#13346) --- .../__snapshots__/expression.spec.ts.snap | 22 ++++++++++ .../transformTemplateRef.spec.ts.snap | 9 +++- .../__tests__/transforms/expression.spec.ts | 20 ++++++++- .../transforms/transformTemplateRef.spec.ts | 13 ++++-- .../src/generators/expression.ts | 44 ++++++++++++++++--- 5 files changed, 96 insertions(+), 12 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap index 518c2a5fe..454e50e9c 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap @@ -32,3 +32,25 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { return n0 }" `; + +exports[`compiler: expression > update expression 1`] = ` +"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("
", true) + +export function render(_ctx) { + const n1 = t0() + const n0 = _child(n1) + const x1 = _child(n1) + _renderEffect(() => { + const _String = String + const _foo = _ctx.foo + + _setText(n0, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar)) + _setText(x1, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar)) + _setProp(n1, "id", _String(_foo.id++)) + _setProp(n1, "foo", _foo) + _setProp(n1, "bar", _ctx.bar++) + }) + return n1 +}" +`; diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap index 4a691056a..cb520a4b2 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap @@ -21,7 +21,14 @@ export function render(_ctx) { const _setTemplateRef = _createTemplateRefSetter() const n0 = t0() let r0 - _renderEffect(() => r0 = _setTemplateRef(n0, bar => _ctx.foo = bar, r0)) + _renderEffect(() => { + const _foo = _ctx.foo + r0 = _setTemplateRef(n0, bar => { + _foo.value = bar + ;({ baz: _ctx.baz } = bar) + console.log(_foo.value, _ctx.baz) + }, r0) + }) return n0 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/expression.spec.ts b/packages/compiler-vapor/__tests__/transforms/expression.spec.ts index c97decd9d..5983bde67 100644 --- a/packages/compiler-vapor/__tests__/transforms/expression.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/expression.spec.ts @@ -1,9 +1,15 @@ import { BindingTypes } from '@vue/compiler-dom' -import { transformChildren, transformText } from '../../src' +import { + transformChildren, + transformElement, + transformText, + transformVBind, +} from '../../src' import { makeCompile } from './_utils' const compileWithExpression = makeCompile({ - nodeTransforms: [transformChildren, transformText], + nodeTransforms: [transformElement, transformChildren, transformText], + directiveTransforms: { bind: transformVBind }, }) describe('compiler: expression', () => { @@ -31,4 +37,14 @@ describe('compiler: expression', () => { expect(code).toMatchSnapshot() expect(code).contains(`$props['bar']`) }) + + test('update expression', () => { + const { code } = compileWithExpression(` +
+ {{ String(foo.id++) }} {{ foo }} {{ bar }} +
+ `) + expect(code).toMatchSnapshot() + expect(code).contains(`_String(_foo.id++)`) + }) }) diff --git a/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts index f026675e4..b6bc479a0 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts @@ -83,7 +83,11 @@ describe('compiler: template ref transform', () => { test('function ref', () => { const { ir, code } = compileWithTransformRef( - `
`, + `
`, ) expect(ir.block.dynamic.children[0]).toMatchObject({ id: 0, @@ -103,7 +107,6 @@ describe('compiler: template ref transform', () => { type: IRNodeTypes.SET_TEMPLATE_REF, element: 0, value: { - content: 'bar => foo = bar', isStatic: false, }, }, @@ -112,7 +115,11 @@ describe('compiler: template ref transform', () => { ]) expect(code).toMatchSnapshot() expect(code).contains('const _setTemplateRef = _createTemplateRefSetter()') - expect(code).contains('_setTemplateRef(n0, bar => _ctx.foo = bar, r0)') + expect(code).contains(`_setTemplateRef(n0, bar => { + _foo.value = bar + ;({ baz: _ctx.baz } = bar) + console.log(_foo.value, _ctx.baz) + }, r0)`) }) test('ref + v-if', () => { diff --git a/packages/compiler-vapor/src/generators/expression.ts b/packages/compiler-vapor/src/generators/expression.ts index eab50c625..76b04f58d 100644 --- a/packages/compiler-vapor/src/generators/expression.ts +++ b/packages/compiler-vapor/src/generators/expression.ts @@ -244,8 +244,13 @@ export function processExpressions( expressions: SimpleExpressionNode[], ): DeclarationResult { // analyze variables - const { seenVariable, variableToExpMap, expToVariableMap, seenIdentifier } = - analyzeExpressions(expressions) + const { + seenVariable, + variableToExpMap, + expToVariableMap, + seenIdentifier, + updatedVariable, + } = analyzeExpressions(expressions) // process repeated identifiers and member expressions // e.g., `foo[baz]` will be transformed into `foo_baz` @@ -255,6 +260,7 @@ export function processExpressions( variableToExpMap, expToVariableMap, seenIdentifier, + updatedVariable, ) // process duplicate expressions after identifier and member expression handling. @@ -263,6 +269,8 @@ export function processExpressions( context, expressions, varDeclarations, + updatedVariable, + expToVariableMap, ) return genDeclarations([...varDeclarations, ...expDeclarations], context) @@ -273,11 +281,13 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) { const variableToExpMap = new Map>() const expToVariableMap = new Map() const seenIdentifier = new Set() + const updatedVariable = new Set() const registerVariable = ( name: string, exp: SimpleExpressionNode, isIdentifier: boolean, + parentStack: Node[] = [], ) => { if (isIdentifier) seenIdentifier.add(name) seenVariable[name] = (seenVariable[name] || 0) + 1 @@ -286,6 +296,13 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) { (variableToExpMap.get(name) || new Set()).add(exp), ) expToVariableMap.set(exp, (expToVariableMap.get(exp) || []).concat(name)) + if ( + parentStack.some( + p => p.type === 'UpdateExpression' || p.type === 'AssignmentExpression', + ) + ) { + updatedVariable.add(name) + } } for (const exp of expressions) { @@ -299,14 +316,20 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) { const memberExp = extractMemberExpression(parent, name => { registerVariable(name, exp, true) }) - registerVariable(memberExp, exp, false) + registerVariable(memberExp, exp, false, parentStack) } else if (!parentStack.some(isMemberExpression)) { - registerVariable(currentNode.name, exp, true) + registerVariable(currentNode.name, exp, true, parentStack) } }) } - return { seenVariable, seenIdentifier, variableToExpMap, expToVariableMap } + return { + seenVariable, + seenIdentifier, + variableToExpMap, + expToVariableMap, + updatedVariable, + } } function processRepeatedVariables( @@ -315,9 +338,11 @@ function processRepeatedVariables( variableToExpMap: Map>, expToVariableMap: Map, seenIdentifier: Set, + updatedVariable: Set, ): DeclarationValue[] { const declarations: DeclarationValue[] = [] for (const [name, exps] of variableToExpMap) { + if (updatedVariable.has(name)) continue if (seenVariable[name] > 1 && exps.size > 0) { const isIdentifier = seenIdentifier.has(name) const varName = isIdentifier ? name : genVarName(name) @@ -409,12 +434,19 @@ function processRepeatedExpressions( context: CodegenContext, expressions: SimpleExpressionNode[], varDeclarations: DeclarationValue[], + updatedVariable: Set, + expToVariableMap: Map, ): DeclarationValue[] { const declarations: DeclarationValue[] = [] const seenExp = expressions.reduce( (acc, exp) => { + const variables = expToVariableMap.get(exp) // only handle expressions that are not identifiers - if (exp.ast && exp.ast.type !== 'Identifier') { + if ( + exp.ast && + exp.ast.type !== 'Identifier' && + !(variables && variables.some(v => updatedVariable.has(v))) + ) { acc[exp.content] = (acc[exp.content] || 0) + 1 } return acc From 504fa10de39d5a2526f14828083c2bbc1d230684 Mon Sep 17 00:00:00 2001 From: Runyasak Chaengnaimuang Date: Fri, 20 Jun 2025 07:17:54 +0700 Subject: [PATCH 05/18] fix(compiler-vapor): prevent v-for components from being single root (#13149) --- .../transformElement.spec.ts.snap | 12 + .../transforms/transformElement.spec.ts | 19 +- .../src/transforms/transformElement.ts | 2 +- pnpm-lock.yaml | 341 +++++++++--------- 4 files changed, 199 insertions(+), 175 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap index e5ee0bdde..7aa56aa9c 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap @@ -188,6 +188,18 @@ export function render(_ctx) { }" `; +exports[`compiler: element transform > component > v-for on component should not mark as single root 1`] = ` +"import { createComponent as _createComponent, createFor as _createFor } from 'vue'; + +export function render(_ctx, $props, $emit, $attrs, $slots) { + const n0 = _createFor(() => (_ctx.items), (_for_item0) => { + const n2 = _createComponent(_ctx.Comp) + return n2 + }, (item) => (item), 2) + return n0 +}" +`; + exports[`compiler: element transform > component > v-on expression is a function call 1`] = ` "import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue'; diff --git a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts index 3b306386c..99b2ceaf7 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts @@ -6,6 +6,7 @@ import { transformElement, transformText, transformVBind, + transformVFor, transformVOn, } from '../../src' import { @@ -15,7 +16,12 @@ import { } from '@vue/compiler-core' const compileWithElementTransform = makeCompile({ - nodeTransforms: [transformElement, transformChildren, transformText], + nodeTransforms: [ + transformVFor, + transformElement, + transformChildren, + transformText, + ], directiveTransforms: { bind: transformVBind, on: transformVOn, @@ -170,6 +176,17 @@ describe('compiler: element transform', () => { expect(code).contains('_createComponent(_ctx.Comp)') }) + test('v-for on component should not mark as single root', () => { + const { code } = compileWithElementTransform( + ``, + { + bindingMetadata: { Comp: BindingTypes.SETUP_CONST }, + }, + ) + expect(code).toMatchSnapshot() + expect(code).contains('_createComponent(_ctx.Comp)') + }) + test('static props', () => { const { code, ir } = compileWithElementTransform( ``, diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index 8ecd0205f..9839fb808 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -137,7 +137,7 @@ function transformComponentElement( tag, props: propsResult[0] ? propsResult[1] : [propsResult[1]], asset, - root: singleRoot, + root: singleRoot && !context.inVFor, slots: [...context.slots], once: context.inVOnce, dynamic: dynamicComponent, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fb8e48f5f..a8c6e9bb4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,22 +40,22 @@ importers: version: 7.27.6 '@rollup/plugin-alias': specifier: ^5.1.1 - version: 5.1.1(rollup@4.43.0) + version: 5.1.1(rollup@4.44.0) '@rollup/plugin-commonjs': specifier: ^28.0.3 - version: 28.0.6(rollup@4.43.0) + version: 28.0.6(rollup@4.44.0) '@rollup/plugin-json': specifier: ^6.1.0 - version: 6.1.0(rollup@4.43.0) + version: 6.1.0(rollup@4.44.0) '@rollup/plugin-node-resolve': specifier: ^16.0.1 - version: 16.0.1(rollup@4.43.0) + version: 16.0.1(rollup@4.44.0) '@rollup/plugin-replace': specifier: 5.0.4 - version: 5.0.4(rollup@4.43.0) + version: 5.0.4(rollup@4.44.0) '@swc/core': specifier: ^1.11.24 - version: 1.12.1 + version: 1.12.3 '@types/hash-sum': specifier: ^1.0.2 version: 1.0.2 @@ -142,16 +142,16 @@ importers: version: 6.0.1 rollup: specifier: ^4.40.2 - version: 4.43.0 + version: 4.44.0 rollup-plugin-dts: specifier: ^6.2.1 - version: 6.2.1(rollup@4.43.0)(typescript@5.6.3) + version: 6.2.1(rollup@4.44.0)(typescript@5.6.3) rollup-plugin-esbuild: specifier: ^6.2.1 - version: 6.2.1(esbuild@0.25.5)(rollup@4.43.0) + version: 6.2.1(esbuild@0.25.5)(rollup@4.44.0) rollup-plugin-polyfill-node: specifier: ^0.13.0 - version: 0.13.0(rollup@4.43.0) + version: 0.13.0(rollup@4.44.0) semver: specifier: ^7.7.1 version: 7.7.2 @@ -246,7 +246,7 @@ importers: version: 0.4.1(@types/node@22.15.32)(sass@1.89.2)(vite@6.3.5(@types/node@22.15.32)(sass@1.89.2)(yaml@2.8.0)) vite-plugin-inspect: specifier: ^0.8.7 - version: 0.8.9(rollup@4.43.0)(vite@6.3.5(@types/node@22.15.32)(sass@1.89.2)(yaml@2.8.0)) + version: 0.8.9(rollup@4.44.0)(vite@6.3.5(@types/node@22.15.32)(sass@1.89.2)(yaml@2.8.0)) packages-private/sfc-playground: dependencies: @@ -1188,168 +1188,168 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.43.0': - resolution: {integrity: sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==} + '@rollup/rollup-android-arm-eabi@4.44.0': + resolution: {integrity: sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.43.0': - resolution: {integrity: sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==} + '@rollup/rollup-android-arm64@4.44.0': + resolution: {integrity: sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.43.0': - resolution: {integrity: sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==} + '@rollup/rollup-darwin-arm64@4.44.0': + resolution: {integrity: sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.43.0': - resolution: {integrity: sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==} + '@rollup/rollup-darwin-x64@4.44.0': + resolution: {integrity: sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.43.0': - resolution: {integrity: sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==} + '@rollup/rollup-freebsd-arm64@4.44.0': + resolution: {integrity: sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.43.0': - resolution: {integrity: sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==} + '@rollup/rollup-freebsd-x64@4.44.0': + resolution: {integrity: sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.43.0': - resolution: {integrity: sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==} + '@rollup/rollup-linux-arm-gnueabihf@4.44.0': + resolution: {integrity: sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.43.0': - resolution: {integrity: sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==} + '@rollup/rollup-linux-arm-musleabihf@4.44.0': + resolution: {integrity: sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.43.0': - resolution: {integrity: sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==} + '@rollup/rollup-linux-arm64-gnu@4.44.0': + resolution: {integrity: sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.43.0': - resolution: {integrity: sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==} + '@rollup/rollup-linux-arm64-musl@4.44.0': + resolution: {integrity: sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.43.0': - resolution: {integrity: sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==} + '@rollup/rollup-linux-loongarch64-gnu@4.44.0': + resolution: {integrity: sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.43.0': - resolution: {integrity: sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==} + '@rollup/rollup-linux-powerpc64le-gnu@4.44.0': + resolution: {integrity: sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.43.0': - resolution: {integrity: sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==} + '@rollup/rollup-linux-riscv64-gnu@4.44.0': + resolution: {integrity: sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.43.0': - resolution: {integrity: sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==} + '@rollup/rollup-linux-riscv64-musl@4.44.0': + resolution: {integrity: sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.43.0': - resolution: {integrity: sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==} + '@rollup/rollup-linux-s390x-gnu@4.44.0': + resolution: {integrity: sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.43.0': - resolution: {integrity: sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==} + '@rollup/rollup-linux-x64-gnu@4.44.0': + resolution: {integrity: sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.43.0': - resolution: {integrity: sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==} + '@rollup/rollup-linux-x64-musl@4.44.0': + resolution: {integrity: sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.43.0': - resolution: {integrity: sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==} + '@rollup/rollup-win32-arm64-msvc@4.44.0': + resolution: {integrity: sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.43.0': - resolution: {integrity: sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==} + '@rollup/rollup-win32-ia32-msvc@4.44.0': + resolution: {integrity: sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.43.0': - resolution: {integrity: sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==} + '@rollup/rollup-win32-x64-msvc@4.44.0': + resolution: {integrity: sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==} cpu: [x64] os: [win32] - '@swc/core-darwin-arm64@1.12.1': - resolution: {integrity: sha512-nUjWVcJ3YS2N40ZbKwYO2RJ4+o2tWYRzNOcIQp05FqW0+aoUCVMdAUUzQinPDynfgwVshDAXCKemY8X7nN5MaA==} + '@swc/core-darwin-arm64@1.12.3': + resolution: {integrity: sha512-QCV9vQ/s27AMxm8j8MTDL/nDoiEMrANiENRrWnb0Fxvz/O39CajPVShp/W7HlOkzt1GYtUXPdQJpSKylugfrWw==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.12.1': - resolution: {integrity: sha512-OGm4a4d3OeJn+tRt8H/eiHgTFrJbS6r8mi/Ob65tAEXZGHN900T2kR7c5ALr0V2hBOQ8BfhexwPoQlGQP/B95w==} + '@swc/core-darwin-x64@1.12.3': + resolution: {integrity: sha512-LylCMfzGhdvl5tyKaTT9ePetHUX7wSsST7hxWiHzS+cUMj7FnhcfdEr6kcNVT7y1RJn3fCvuv7T98ZB+T2q3HA==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.12.1': - resolution: {integrity: sha512-76YeeQKyK0EtNkQiNBZ0nbVGooPf9IucY0WqVXVpaU4wuG7ZyLEE2ZAIgXafIuzODGQoLfetue7I8boMxh1/MA==} + '@swc/core-linux-arm-gnueabihf@1.12.3': + resolution: {integrity: sha512-DQODb7S+q+pwQY41Azcavwb2rb4rGxP70niScRDxB9X68hHOM9D0w9fxzC+Nr3AHcPSmVJUYUIiq5h38O5hVgQ==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.12.1': - resolution: {integrity: sha512-BxJDIJPq1+aCh9UsaSAN6wo3tuln8UhNXruOrzTI8/ElIig/3sAueDM6Eq7GvZSGGSA7ljhNATMJ0elD7lFatQ==} + '@swc/core-linux-arm64-gnu@1.12.3': + resolution: {integrity: sha512-nTxtJSq78AjeaQBueYImoFBs5j7qXbgOxtirpyt8jE29NQBd0VFzDzRBhkr6I9jq0hNiChgMkqBN4eUkEQjytg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.12.1': - resolution: {integrity: sha512-NhLdbffSXvY0/FwUSAl4hKBlpe5GHQGXK8DxTo3HHjLsD9sCPYieo3vG0NQoUYAy4ZUY1WeGjyxeq4qZddJzEQ==} + '@swc/core-linux-arm64-musl@1.12.3': + resolution: {integrity: sha512-lBGvC5UgPSxqLr/y1NZxQhyRQ7nXy3/Ec1Z47YNXtqtpKiG1EcOGPyS0UZgwiYQkXqq8NBFMHnyHmpKnXTvRDA==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.12.1': - resolution: {integrity: sha512-CrYnV8SZIgArQ9LKH0xEF95PKXzX9WkRSc5j55arOSBeDCeDUQk1Bg/iKdnDiuj5HC1hZpvzwMzSBJjv+Z70jA==} + '@swc/core-linux-x64-gnu@1.12.3': + resolution: {integrity: sha512-61wZ8hwxNYzBY9MCWB50v90ICzdIhOuPk1O1qXswz9AXw5O6iQStEBHQ1rozPkfQ/rmhepk0pOf/6LCwssJOwg==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.12.1': - resolution: {integrity: sha512-BQMl3d0HaGB0/h2xcKlGtjk/cGRn2tnbsaChAKcjFdCepblKBCz1pgO/mL7w5iXq3s57wMDUn++71/a5RAkZOA==} + '@swc/core-linux-x64-musl@1.12.3': + resolution: {integrity: sha512-NNeBiTpCgWt80vumTKVoaj6Fa/ZjUcaNQNM7np3PIgB8EbuXfyztboV7vUxpkmD/lUgsk8GlEFYViHvo6VMefQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.12.1': - resolution: {integrity: sha512-b7NeGnpqTfmIGtUqXBl0KqoSmOnH64nRZoT5l4BAGdvwY7nxitWR94CqZuwyLPty/bLywmyDA9uO12Kvgb3+gg==} + '@swc/core-win32-arm64-msvc@1.12.3': + resolution: {integrity: sha512-fxraM7exaPb1/W0CoHW45EFNOQUQh0nonBEcNFm2iv095mziBwttyxZyQBoDkQocpkd5NtsZw3xW5FTBPnn+Vw==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.12.1': - resolution: {integrity: sha512-iU/29X2D7cHBp1to62cUg/5Xk8K+lyOJiKIGGW5rdzTW/c2zz3d/ehgpzVP/rqC4NVr88MXspqHU4il5gmDajw==} + '@swc/core-win32-ia32-msvc@1.12.3': + resolution: {integrity: sha512-FFIhMPXIDjRcewomwbYGPvem7Fj76AsuzbRahnAyp+OzJwrrtxVmra/kyUCfj4kix7vdGByY0WvVfiVCf5b7Mg==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.12.1': - resolution: {integrity: sha512-+Zh+JKDwiFqV5N9yAd2DhYVGPORGh9cfenu1ptr9yge+eHAf7vZJcC3rnj6QMR1QJh0Y5VC9+YBjRFjZVA7XDw==} + '@swc/core-win32-x64-msvc@1.12.3': + resolution: {integrity: sha512-Sf4iSg+IYT5AzFSDDmii08DfeKcvtkVxIuo+uS8BJMbiLjFNjgMkkVlBthknGyJcSK15ncg9248XjnM4jU8DZA==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.12.1': - resolution: {integrity: sha512-aKXdDTqxTVFl/bKQZ3EQUjEMBEoF6JBv29moMZq0kbVO43na6u/u+3Vcbhbrh+A2N0X5OL4RaveuWfAjEgOmeA==} + '@swc/core@1.12.3': + resolution: {integrity: sha512-c4NeXW8P3gPqcFwtm+4aH+F2Cj5KJLMiLaKhSj3mpv19glq+jmekomdktAw/VHyjsXlsmouOeNWrk8rVlkCRsg==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -1378,9 +1378,6 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/estree@1.0.7': - resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} - '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -3329,8 +3326,8 @@ packages: peerDependencies: rollup: ^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 - rollup@4.43.0: - resolution: {integrity: sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==} + rollup@4.44.0: + resolution: {integrity: sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -4370,13 +4367,13 @@ snapshots: - bare-buffer - supports-color - '@rollup/plugin-alias@5.1.1(rollup@4.43.0)': + '@rollup/plugin-alias@5.1.1(rollup@4.44.0)': optionalDependencies: - rollup: 4.43.0 + rollup: 4.44.0 - '@rollup/plugin-commonjs@28.0.6(rollup@4.43.0)': + '@rollup/plugin-commonjs@28.0.6(rollup@4.44.0)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.43.0) + '@rollup/pluginutils': 5.2.0(rollup@4.44.0) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.4.6(picomatch@4.0.2) @@ -4384,152 +4381,152 @@ snapshots: magic-string: 0.30.17 picomatch: 4.0.2 optionalDependencies: - rollup: 4.43.0 + rollup: 4.44.0 - '@rollup/plugin-inject@5.0.5(rollup@4.43.0)': + '@rollup/plugin-inject@5.0.5(rollup@4.44.0)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.43.0) + '@rollup/pluginutils': 5.2.0(rollup@4.44.0) estree-walker: 2.0.2 magic-string: 0.30.17 optionalDependencies: - rollup: 4.43.0 + rollup: 4.44.0 - '@rollup/plugin-json@6.1.0(rollup@4.43.0)': + '@rollup/plugin-json@6.1.0(rollup@4.44.0)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.43.0) + '@rollup/pluginutils': 5.2.0(rollup@4.44.0) optionalDependencies: - rollup: 4.43.0 + rollup: 4.44.0 - '@rollup/plugin-node-resolve@16.0.1(rollup@4.43.0)': + '@rollup/plugin-node-resolve@16.0.1(rollup@4.44.0)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.43.0) + '@rollup/pluginutils': 5.2.0(rollup@4.44.0) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.10 optionalDependencies: - rollup: 4.43.0 + rollup: 4.44.0 - '@rollup/plugin-replace@5.0.4(rollup@4.43.0)': + '@rollup/plugin-replace@5.0.4(rollup@4.44.0)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.43.0) + '@rollup/pluginutils': 5.2.0(rollup@4.44.0) magic-string: 0.30.17 optionalDependencies: - rollup: 4.43.0 + rollup: 4.44.0 - '@rollup/pluginutils@5.2.0(rollup@4.43.0)': + '@rollup/pluginutils@5.2.0(rollup@4.44.0)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.43.0 + rollup: 4.44.0 - '@rollup/rollup-android-arm-eabi@4.43.0': + '@rollup/rollup-android-arm-eabi@4.44.0': optional: true - '@rollup/rollup-android-arm64@4.43.0': + '@rollup/rollup-android-arm64@4.44.0': optional: true - '@rollup/rollup-darwin-arm64@4.43.0': + '@rollup/rollup-darwin-arm64@4.44.0': optional: true - '@rollup/rollup-darwin-x64@4.43.0': + '@rollup/rollup-darwin-x64@4.44.0': optional: true - '@rollup/rollup-freebsd-arm64@4.43.0': + '@rollup/rollup-freebsd-arm64@4.44.0': optional: true - '@rollup/rollup-freebsd-x64@4.43.0': + '@rollup/rollup-freebsd-x64@4.44.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.43.0': + '@rollup/rollup-linux-arm-gnueabihf@4.44.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.43.0': + '@rollup/rollup-linux-arm-musleabihf@4.44.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.43.0': + '@rollup/rollup-linux-arm64-gnu@4.44.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.43.0': + '@rollup/rollup-linux-arm64-musl@4.44.0': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.43.0': + '@rollup/rollup-linux-loongarch64-gnu@4.44.0': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.43.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.44.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.43.0': + '@rollup/rollup-linux-riscv64-gnu@4.44.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.43.0': + '@rollup/rollup-linux-riscv64-musl@4.44.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.43.0': + '@rollup/rollup-linux-s390x-gnu@4.44.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.43.0': + '@rollup/rollup-linux-x64-gnu@4.44.0': optional: true - '@rollup/rollup-linux-x64-musl@4.43.0': + '@rollup/rollup-linux-x64-musl@4.44.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.43.0': + '@rollup/rollup-win32-arm64-msvc@4.44.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.43.0': + '@rollup/rollup-win32-ia32-msvc@4.44.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.43.0': + '@rollup/rollup-win32-x64-msvc@4.44.0': optional: true - '@swc/core-darwin-arm64@1.12.1': + '@swc/core-darwin-arm64@1.12.3': optional: true - '@swc/core-darwin-x64@1.12.1': + '@swc/core-darwin-x64@1.12.3': optional: true - '@swc/core-linux-arm-gnueabihf@1.12.1': + '@swc/core-linux-arm-gnueabihf@1.12.3': optional: true - '@swc/core-linux-arm64-gnu@1.12.1': + '@swc/core-linux-arm64-gnu@1.12.3': optional: true - '@swc/core-linux-arm64-musl@1.12.1': + '@swc/core-linux-arm64-musl@1.12.3': optional: true - '@swc/core-linux-x64-gnu@1.12.1': + '@swc/core-linux-x64-gnu@1.12.3': optional: true - '@swc/core-linux-x64-musl@1.12.1': + '@swc/core-linux-x64-musl@1.12.3': optional: true - '@swc/core-win32-arm64-msvc@1.12.1': + '@swc/core-win32-arm64-msvc@1.12.3': optional: true - '@swc/core-win32-ia32-msvc@1.12.1': + '@swc/core-win32-ia32-msvc@1.12.3': optional: true - '@swc/core-win32-x64-msvc@1.12.1': + '@swc/core-win32-x64-msvc@1.12.3': optional: true - '@swc/core@1.12.1': + '@swc/core@1.12.3': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.23 optionalDependencies: - '@swc/core-darwin-arm64': 1.12.1 - '@swc/core-darwin-x64': 1.12.1 - '@swc/core-linux-arm-gnueabihf': 1.12.1 - '@swc/core-linux-arm64-gnu': 1.12.1 - '@swc/core-linux-arm64-musl': 1.12.1 - '@swc/core-linux-x64-gnu': 1.12.1 - '@swc/core-linux-x64-musl': 1.12.1 - '@swc/core-win32-arm64-msvc': 1.12.1 - '@swc/core-win32-ia32-msvc': 1.12.1 - '@swc/core-win32-x64-msvc': 1.12.1 + '@swc/core-darwin-arm64': 1.12.3 + '@swc/core-darwin-x64': 1.12.3 + '@swc/core-linux-arm-gnueabihf': 1.12.3 + '@swc/core-linux-arm64-gnu': 1.12.3 + '@swc/core-linux-arm64-musl': 1.12.3 + '@swc/core-linux-x64-gnu': 1.12.3 + '@swc/core-linux-x64-musl': 1.12.3 + '@swc/core-win32-arm64-msvc': 1.12.3 + '@swc/core-win32-ia32-msvc': 1.12.3 + '@swc/core-win32-x64-msvc': 1.12.3 '@swc/counter@0.1.3': {} @@ -4554,8 +4551,6 @@ snapshots: '@types/deep-eql@4.0.2': {} - '@types/estree@1.0.7': {} - '@types/estree@1.0.8': {} '@types/hash-sum@1.0.2': {} @@ -6623,54 +6618,54 @@ snapshots: glob: 11.0.3 package-json-from-dist: 1.0.1 - rollup-plugin-dts@6.2.1(rollup@4.43.0)(typescript@5.6.3): + rollup-plugin-dts@6.2.1(rollup@4.44.0)(typescript@5.6.3): dependencies: magic-string: 0.30.17 - rollup: 4.43.0 + rollup: 4.44.0 typescript: 5.6.3 optionalDependencies: '@babel/code-frame': 7.27.1 - rollup-plugin-esbuild@6.2.1(esbuild@0.25.5)(rollup@4.43.0): + rollup-plugin-esbuild@6.2.1(esbuild@0.25.5)(rollup@4.44.0): dependencies: debug: 4.4.1 es-module-lexer: 1.7.0 esbuild: 0.25.5 get-tsconfig: 4.10.1 - rollup: 4.43.0 + rollup: 4.44.0 unplugin-utils: 0.2.4 transitivePeerDependencies: - supports-color - rollup-plugin-polyfill-node@0.13.0(rollup@4.43.0): + rollup-plugin-polyfill-node@0.13.0(rollup@4.44.0): dependencies: - '@rollup/plugin-inject': 5.0.5(rollup@4.43.0) - rollup: 4.43.0 + '@rollup/plugin-inject': 5.0.5(rollup@4.44.0) + rollup: 4.44.0 - rollup@4.43.0: + rollup@4.44.0: dependencies: - '@types/estree': 1.0.7 + '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.43.0 - '@rollup/rollup-android-arm64': 4.43.0 - '@rollup/rollup-darwin-arm64': 4.43.0 - '@rollup/rollup-darwin-x64': 4.43.0 - '@rollup/rollup-freebsd-arm64': 4.43.0 - '@rollup/rollup-freebsd-x64': 4.43.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.43.0 - '@rollup/rollup-linux-arm-musleabihf': 4.43.0 - '@rollup/rollup-linux-arm64-gnu': 4.43.0 - '@rollup/rollup-linux-arm64-musl': 4.43.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.43.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.43.0 - '@rollup/rollup-linux-riscv64-gnu': 4.43.0 - '@rollup/rollup-linux-riscv64-musl': 4.43.0 - '@rollup/rollup-linux-s390x-gnu': 4.43.0 - '@rollup/rollup-linux-x64-gnu': 4.43.0 - '@rollup/rollup-linux-x64-musl': 4.43.0 - '@rollup/rollup-win32-arm64-msvc': 4.43.0 - '@rollup/rollup-win32-ia32-msvc': 4.43.0 - '@rollup/rollup-win32-x64-msvc': 4.43.0 + '@rollup/rollup-android-arm-eabi': 4.44.0 + '@rollup/rollup-android-arm64': 4.44.0 + '@rollup/rollup-darwin-arm64': 4.44.0 + '@rollup/rollup-darwin-x64': 4.44.0 + '@rollup/rollup-freebsd-arm64': 4.44.0 + '@rollup/rollup-freebsd-x64': 4.44.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.44.0 + '@rollup/rollup-linux-arm-musleabihf': 4.44.0 + '@rollup/rollup-linux-arm64-gnu': 4.44.0 + '@rollup/rollup-linux-arm64-musl': 4.44.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.44.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.44.0 + '@rollup/rollup-linux-riscv64-gnu': 4.44.0 + '@rollup/rollup-linux-riscv64-musl': 4.44.0 + '@rollup/rollup-linux-s390x-gnu': 4.44.0 + '@rollup/rollup-linux-x64-gnu': 4.44.0 + '@rollup/rollup-linux-x64-musl': 4.44.0 + '@rollup/rollup-win32-arm64-msvc': 4.44.0 + '@rollup/rollup-win32-ia32-msvc': 4.44.0 + '@rollup/rollup-win32-x64-msvc': 4.44.0 fsevents: 2.3.3 rrweb-cssom@0.8.0: {} @@ -7086,10 +7081,10 @@ snapshots: - tsx - yaml - vite-plugin-inspect@0.8.9(rollup@4.43.0)(vite@6.3.5(@types/node@22.15.32)(sass@1.89.2)(yaml@2.8.0)): + vite-plugin-inspect@0.8.9(rollup@4.44.0)(vite@6.3.5(@types/node@22.15.32)(sass@1.89.2)(yaml@2.8.0)): dependencies: '@antfu/utils': 0.7.10 - '@rollup/pluginutils': 5.2.0(rollup@4.43.0) + '@rollup/pluginutils': 5.2.0(rollup@4.44.0) debug: 4.4.1 error-stack-parser-es: 0.1.5 fs-extra: 11.3.0 @@ -7106,7 +7101,7 @@ snapshots: dependencies: esbuild: 0.21.5 postcss: 8.5.6 - rollup: 4.43.0 + rollup: 4.44.0 optionalDependencies: '@types/node': 22.15.32 fsevents: 2.3.3 @@ -7118,7 +7113,7 @@ snapshots: fdir: 6.4.6(picomatch@4.0.2) picomatch: 4.0.2 postcss: 8.5.6 - rollup: 4.43.0 + rollup: 4.44.0 tinyglobby: 0.2.14 optionalDependencies: '@types/node': 22.15.32 From b9dc8658cb09ec2deceb8d0a775185780c82be3c Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 20 Jun 2025 08:29:50 +0800 Subject: [PATCH 06/18] fix(runtime-vapor): properly mount component when using setInsertionState (#13041) --- .../runtime-vapor/__tests__/component.spec.ts | 25 +++++++++++++++++++ packages/runtime-vapor/src/component.ts | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/runtime-vapor/__tests__/component.spec.ts b/packages/runtime-vapor/__tests__/component.spec.ts index 5fdff8eaf..22294b1e7 100644 --- a/packages/runtime-vapor/__tests__/component.spec.ts +++ b/packages/runtime-vapor/__tests__/component.spec.ts @@ -2,6 +2,7 @@ import { type Ref, inject, nextTick, + onMounted, onUpdated, provide, ref, @@ -13,6 +14,7 @@ import { createIf, createTextNode, renderEffect, + setInsertionState, template, } from '../src' import { makeRender } from './_utils' @@ -266,6 +268,29 @@ describe('component', () => { expect(spy).toHaveBeenCalledTimes(2) }) + it('properly mount child component when using setInsertionState', async () => { + const spy = vi.fn() + + const { component: Comp } = define({ + setup() { + onMounted(spy) + return template('

hi

')() + }, + }) + + const { host } = define({ + setup() { + const n2 = template('
', true)() + setInsertionState(n2 as any) + createComponent(Comp) + return n2 + }, + }).render() + + expect(host.innerHTML).toBe('

hi

') + expect(spy).toHaveBeenCalledTimes(1) + }) + it('unmount component', async () => { const { host, app, instance } = define(() => { const count = ref(0) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index c6602ec96..deec96cb0 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -276,7 +276,7 @@ export function createComponent( onScopeDispose(() => unmountComponent(instance), true) if (!isHydrating && _insertionParent) { - insert(instance.block, _insertionParent, _insertionAnchor) + mountComponent(instance, _insertionParent, _insertionAnchor) } return instance From b96447dd41a9c1c9476d610ceb00b17b2aaea3f8 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 20 Jun 2025 08:30:36 +0800 Subject: [PATCH 07/18] chore(compiler-vapor): use compiler-dom instead of compiler-core (#13364) --- .../__tests__/transforms/transformElement.spec.ts | 2 +- .../__tests__/transforms/transformSlotOutlet.spec.ts | 2 +- packages/compiler-vapor/__tests__/transforms/vIf.spec.ts | 2 +- packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts | 2 +- packages/compiler-vapor/src/generators/component.ts | 2 +- packages/compiler-vapor/src/generators/prop.ts | 2 +- packages/compiler-vapor/src/transforms/transformSlotOutlet.ts | 2 +- packages/compiler-vapor/src/transforms/vSlot.ts | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts index 99b2ceaf7..a693db4ad 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts @@ -13,7 +13,7 @@ import { type BindingMetadata, BindingTypes, NodeTypes, -} from '@vue/compiler-core' +} from '@vue/compiler-dom' const compileWithElementTransform = makeCompile({ nodeTransforms: [ diff --git a/packages/compiler-vapor/__tests__/transforms/transformSlotOutlet.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformSlotOutlet.spec.ts index 599351132..389c665a1 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformSlotOutlet.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformSlotOutlet.spec.ts @@ -1,4 +1,4 @@ -import { ErrorCodes, NodeTypes } from '@vue/compiler-core' +import { ErrorCodes, NodeTypes } from '@vue/compiler-dom' import { IRNodeTypes, transformChildren, diff --git a/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts b/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts index f51988e2e..e5fd61add 100644 --- a/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts @@ -10,7 +10,7 @@ import { transformVOnce, transformVText, } from '../../src' -import { NodeTypes } from '@vue/compiler-core' +import { NodeTypes } from '@vue/compiler-dom' const compileWithVIf = makeCompile({ nodeTransforms: [ diff --git a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts index 8db1386b3..909162fe3 100644 --- a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts @@ -1,4 +1,4 @@ -import { ErrorCodes, NodeTypes } from '@vue/compiler-core' +import { ErrorCodes, NodeTypes } from '@vue/compiler-dom' import { IRNodeTypes, IRSlotType, diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index 7c232db75..10705a2c7 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -34,7 +34,7 @@ import { isMemberExpression, toValidAssetId, walkIdentifiers, -} from '@vue/compiler-core' +} from '@vue/compiler-dom' import { genEventHandler } from './event' import { genDirectiveModifiers, genDirectivesForElement } from './directive' import { genBlock } from './block' diff --git a/packages/compiler-vapor/src/generators/prop.ts b/packages/compiler-vapor/src/generators/prop.ts index 62fca087e..42f063331 100644 --- a/packages/compiler-vapor/src/generators/prop.ts +++ b/packages/compiler-vapor/src/generators/prop.ts @@ -2,7 +2,7 @@ import { NewlineType, type SimpleExpressionNode, isSimpleIdentifier, -} from '@vue/compiler-core' +} from '@vue/compiler-dom' import type { CodegenContext } from '../generate' import { IRDynamicPropsKind, diff --git a/packages/compiler-vapor/src/transforms/transformSlotOutlet.ts b/packages/compiler-vapor/src/transforms/transformSlotOutlet.ts index 83b4aa2d2..e76fcdde6 100644 --- a/packages/compiler-vapor/src/transforms/transformSlotOutlet.ts +++ b/packages/compiler-vapor/src/transforms/transformSlotOutlet.ts @@ -9,7 +9,7 @@ import { createSimpleExpression, isStaticArgOf, isStaticExp, -} from '@vue/compiler-core' +} from '@vue/compiler-dom' import type { NodeTransform, TransformContext } from '../transform' import { type BlockIRNode, diff --git a/packages/compiler-vapor/src/transforms/vSlot.ts b/packages/compiler-vapor/src/transforms/vSlot.ts index d1f08d614..3e78913a2 100644 --- a/packages/compiler-vapor/src/transforms/vSlot.ts +++ b/packages/compiler-vapor/src/transforms/vSlot.ts @@ -8,7 +8,7 @@ import { createCompilerError, isTemplateNode, isVSlot, -} from '@vue/compiler-core' +} from '@vue/compiler-dom' import type { NodeTransform, TransformContext } from '../transform' import { newBlock } from './utils' import { From d65675d902a2735a3471a6aba52134bd384b003b Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 20 Jun 2025 08:38:24 +0800 Subject: [PATCH 08/18] fix(runtime-vapor): dynamic component work with insertionState (#13142) --- .../apiCreateDynamicComponent.spec.ts | 31 +++++++++++++++++++ .../src/apiCreateDynamicComponent.ts | 22 ++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts b/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts index 87dc922dd..e912af285 100644 --- a/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts +++ b/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts @@ -3,6 +3,7 @@ import { nextTick, resolveDynamicComponent } from '@vue/runtime-dom' import { createComponentWithFallback, createDynamicComponent, + defineVaporComponent, renderEffect, setHtml, setInsertionState, @@ -79,4 +80,34 @@ describe('api: createDynamicComponent', () => { mount() expect(html()).toBe('
') }) + + test('switch dynamic component children', async () => { + const CompA = defineVaporComponent({ + setup() { + return template('
A
')() + }, + }) + const CompB = defineVaporComponent({ + setup() { + return template('
B
')() + }, + }) + + const current = shallowRef(CompA) + const { html } = define({ + setup() { + const t1 = template('
') + const n2 = t1() as any + setInsertionState(n2) + createDynamicComponent(() => current.value) + return n2 + }, + }).render() + + expect(html()).toBe('
A
') + + current.value = CompB + await nextTick() + expect(html()).toBe('
B
') + }) }) diff --git a/packages/runtime-vapor/src/apiCreateDynamicComponent.ts b/packages/runtime-vapor/src/apiCreateDynamicComponent.ts index 2126611d7..945e0f38d 100644 --- a/packages/runtime-vapor/src/apiCreateDynamicComponent.ts +++ b/packages/runtime-vapor/src/apiCreateDynamicComponent.ts @@ -1,9 +1,15 @@ import { resolveDynamicComponent } from '@vue/runtime-dom' -import { DynamicFragment, type VaporFragment } from './block' +import { DynamicFragment, type VaporFragment, insert } from './block' import { createComponentWithFallback } from './component' import { renderEffect } from './renderEffect' import type { RawProps } from './componentProps' import type { RawSlots } from './componentSlots' +import { + insertionAnchor, + insertionParent, + resetInsertionState, +} from './insertionState' +import { isHydrating, locateHydrationNode } from './dom/hydration' export function createDynamicComponent( getter: () => any, @@ -11,9 +17,18 @@ export function createDynamicComponent( rawSlots?: RawSlots | null, isSingleRoot?: boolean, ): VaporFragment { + const _insertionParent = insertionParent + const _insertionAnchor = insertionAnchor + if (isHydrating) { + locateHydrationNode() + } else { + resetInsertionState() + } + const frag = __DEV__ ? new DynamicFragment('dynamic-component') : new DynamicFragment() + renderEffect(() => { const value = getter() frag.update( @@ -27,5 +42,10 @@ export function createDynamicComponent( value, ) }) + + if (!isHydrating && _insertionParent) { + insert(frag, _insertionParent, _insertionAnchor) + } + return frag } From 6f6ab1ab3c41c5332243f1dab530bc2e011d9e19 Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 20 Jun 2025 08:40:52 +0800 Subject: [PATCH 09/18] fix(runtime-vapor): dynamic component attrs fallthrough (#13466) --- .../__tests__/componentAttrs.spec.ts | 40 ++++++++++++++++++- packages/runtime-vapor/src/component.ts | 31 ++++++++++---- packages/runtime-vapor/src/componentProps.ts | 3 +- 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts index 204dd5990..109b1aa2f 100644 --- a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts +++ b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts @@ -8,6 +8,8 @@ import { } from '@vue/runtime-dom' import { createComponent, + createDynamicComponent, + createSlot, defineVaporComponent, renderEffect, setClass, @@ -285,7 +287,43 @@ describe('attribute fallthrough', () => { expect(getCSS()).not.toContain('font-size:bold') }) - test('parent value should take priority', async () => { + it('should fallthrough attrs to dynamic component', async () => { + const Comp = defineVaporComponent({ + setup() { + const n1 = createDynamicComponent( + () => 'button', + null, + { + default: () => { + const n0 = createSlot('default', null) + return n0 + }, + }, + true, + ) + return n1 + }, + }) + + const { html } = define({ + setup() { + return createComponent( + Comp, + { + class: () => 'foo', + }, + null, + true, + ) + }, + }).render() + + expect(html()).toBe( + '', + ) + }) + + it('parent value should take priority', async () => { const parentVal = ref('parent') const childVal = ref('child') diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index deec96cb0..af15133db 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -25,7 +25,7 @@ import { unregisterHMR, warn, } from '@vue/runtime-dom' -import { type Block, insert, isBlock, remove } from './block' +import { type Block, DynamicFragment, insert, isBlock, remove } from './block' import { type ShallowRef, markRaw, @@ -255,14 +255,16 @@ export function createComponent( if ( instance.hasFallthrough && component.inheritAttrs !== false && - instance.block instanceof Element && Object.keys(instance.attrs).length ) { - renderEffect(() => { - isApplyingFallthroughProps = true - setDynamicProps(instance.block as Element, [instance.attrs]) - isApplyingFallthroughProps = false - }) + const el = getRootElement(instance) + if (el) { + renderEffect(() => { + isApplyingFallthroughProps = true + setDynamicProps(el, [instance.attrs]) + isApplyingFallthroughProps = false + }) + } } resetTracking() @@ -563,3 +565,18 @@ export function getExposed( ) } } + +function getRootElement({ + block, +}: VaporComponentInstance): Element | undefined { + if (block instanceof Element) { + return block + } + + if (block instanceof DynamicFragment) { + const { nodes } = block + if (nodes instanceof Element && (nodes as any).$root) { + return nodes + } + } +} diff --git a/packages/runtime-vapor/src/componentProps.ts b/packages/runtime-vapor/src/componentProps.ts index a5e9daad2..7a0e9ed92 100644 --- a/packages/runtime-vapor/src/componentProps.ts +++ b/packages/runtime-vapor/src/componentProps.ts @@ -210,7 +210,8 @@ export function hasAttrFromRawProps(rawProps: RawProps, key: string): boolean { if (dynamicSources) { let i = dynamicSources.length while (i--) { - if (hasOwn(resolveSource(dynamicSources[i]), key)) { + const source = resolveSource(dynamicSources[i]) + if (source && hasOwn(source, key)) { return true } } From 2250d415b92aa4882d592e16676ffc815d55b762 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 20 Jun 2025 08:43:15 +0800 Subject: [PATCH 10/18] fix(compiler-vapor): remove types for expressions (#13395) --- .../transforms/__snapshots__/vOn.spec.ts.snap | 12 ++++++++++++ .../compiler-vapor/__tests__/transforms/vOn.spec.ts | 13 +++++++++++++ .../compiler-vapor/src/generators/expression.ts | 10 +++++++--- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap index cb1a05d2a..dd00e5526 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap @@ -123,6 +123,18 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { }" `; +exports[`v-on > expression with type 1`] = ` +"import { delegateEvents as _delegateEvents, template as _template } from 'vue'; +const t0 = _template("
", true) +_delegateEvents("click") + +export function render(_ctx, $props, $emit, $attrs, $slots) { + const n0 = t0() + n0.$evtclick = e => _ctx.handleClick(e) + return n0 +}" +`; + exports[`v-on > function expression w/ prefixIdentifiers: true 1`] = ` "import { delegateEvents as _delegateEvents, template as _template } from 'vue'; const t0 = _template("
", true) diff --git a/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts b/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts index 1ce216936..aca88c791 100644 --- a/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts @@ -682,4 +682,17 @@ describe('v-on', () => { '_delegate(n0, "click", _withModifiers(e => _ctx.test(e), ["stop"]))', ) }) + + test('expression with type', () => { + const { code } = compileWithVOn( + `
`, + { + bindingMetadata: { + handleClick: BindingTypes.SETUP_CONST, + }, + }, + ) + expect(code).matchSnapshot() + expect(code).include('n0.$evtclick = e => _ctx.handleClick(e)') + }) }) diff --git a/packages/compiler-vapor/src/generators/expression.ts b/packages/compiler-vapor/src/generators/expression.ts index 76b04f58d..845c8bedd 100644 --- a/packages/compiler-vapor/src/generators/expression.ts +++ b/packages/compiler-vapor/src/generators/expression.ts @@ -10,6 +10,7 @@ import { NewlineType, type SimpleExpressionNode, type SourceLocation, + TS_NODE_TYPES, advancePositionWithClone, createSimpleExpression, isInDestructureAssignment, @@ -63,6 +64,7 @@ export function genExpression( let hasMemberExpression = false if (ids.length) { const [frag, push] = buildCodeFragment() + const isTSNode = ast && TS_NODE_TYPES.includes(ast.type) ids .sort((a, b) => a.start! - b.start!) .forEach((id, i) => { @@ -71,8 +73,10 @@ export function genExpression( const end = id.end! - 1 const last = ids[i - 1] - const leadingText = content.slice(last ? last.end! - 1 : 0, start) - if (leadingText.length) push([leadingText, NewlineType.Unknown]) + if (!(isTSNode && i === 0)) { + const leadingText = content.slice(last ? last.end! - 1 : 0, start) + if (leadingText.length) push([leadingText, NewlineType.Unknown]) + } const source = content.slice(start, end) const parentStack = parentStackMap.get(id)! @@ -99,7 +103,7 @@ export function genExpression( ), ) - if (i === ids.length - 1 && end < content.length) { + if (i === ids.length - 1 && end < content.length && !isTSNode) { push([content.slice(end), NewlineType.Unknown]) } }) From 430216a9f55a4c6b882667b327c736d91a64cd04 Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 20 Jun 2025 08:44:35 +0800 Subject: [PATCH 11/18] fix(runtime-vapor): should not warn invalid watch source when directly watching props (#13384) --- .../__tests__/componentProps.spec.ts | 30 +++++++++++++++++++ packages/runtime-vapor/src/componentProps.ts | 4 +++ 2 files changed, 34 insertions(+) diff --git a/packages/runtime-vapor/__tests__/componentProps.spec.ts b/packages/runtime-vapor/__tests__/componentProps.spec.ts index c068e8044..55b492408 100644 --- a/packages/runtime-vapor/__tests__/componentProps.spec.ts +++ b/packages/runtime-vapor/__tests__/componentProps.spec.ts @@ -497,6 +497,36 @@ describe('component: props', () => { expect(changeSpy).toHaveBeenCalledTimes(1) }) + test('should not warn invalid watch source when directly watching props', async () => { + const changeSpy = vi.fn() + const { render, html } = define({ + props: { + foo: { + type: String, + }, + }, + setup(props: any) { + watch(props, changeSpy) + const t0 = template('

') + const n0 = t0() + renderEffect(() => { + setElementText(n0, String(props.foo)) + }) + return n0 + }, + }) + + const foo = ref('foo') + render({ foo: () => foo.value }) + expect(html()).toBe(`

foo

`) + expect('Invalid watch source').not.toHaveBeenWarned() + + foo.value = 'bar' + await nextTick() + expect(html()).toBe(`

bar

`) + expect(changeSpy).toHaveBeenCalledTimes(1) + }) + test('support null in required + multiple-type declarations', () => { const { render } = define({ props: { diff --git a/packages/runtime-vapor/src/componentProps.ts b/packages/runtime-vapor/src/componentProps.ts index 7a0e9ed92..9cf65c571 100644 --- a/packages/runtime-vapor/src/componentProps.ts +++ b/packages/runtime-vapor/src/componentProps.ts @@ -21,6 +21,7 @@ import { validateProps, warn, } from '@vue/runtime-dom' +import { ReactiveFlags } from '@vue/reactivity' import { normalizeEmitsOptions } from './componentEmits' import { renderEffect } from './renderEffect' @@ -63,6 +64,9 @@ export function getPropsProxyHandlers( : YES const getProp = (instance: VaporComponentInstance, key: string | symbol) => { + // this enables direct watching of props and prevents `Invalid watch source` DEV warnings. + if (key === ReactiveFlags.IS_REACTIVE) return true + if (!isProp(key)) return const rawProps = instance.rawProps const dynamicSources = rawProps.$ From bf7424aa290408ab80fe44a2863535a0609609cb Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 20 Jun 2025 08:55:50 +0800 Subject: [PATCH 12/18] fix(compiler-vapor): correct execution order of operations (#13351) --- .../__snapshots__/compile.spec.ts.snap | 42 ++++++++++-- .../compiler-vapor/__tests__/compile.spec.ts | 25 +++++++ .../transformChildren.spec.ts.snap | 1 - .../__snapshots__/vBind.spec.ts.snap | 13 +--- packages/compiler-vapor/src/compile.ts | 2 +- .../src/generators/operation.ts | 6 +- packages/compiler-vapor/src/ir/index.ts | 1 - packages/compiler-vapor/src/transform.ts | 28 ++------ .../src/transforms/transformElement.ts | 67 +++++++++++++------ .../compiler-vapor/src/transforms/utils.ts | 1 - 10 files changed, 121 insertions(+), 65 deletions(-) diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index d4a8b6827..b10a98d32 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -149,7 +149,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { `; exports[`compile > directives > v-pre > should not affect siblings after it 1`] = ` -"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, child as _child, setProp as _setProp, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template("
{{ bar }}
") const t1 = _template("
") @@ -161,8 +161,8 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { _setInsertionState(n3, 0) const n1 = _createComponentWithFallback(_component_Comp) _renderEffect(() => { - _setText(n2, _toDisplayString(_ctx.bar)) _setProp(n3, "id", _ctx.foo) + _setText(n2, _toDisplayString(_ctx.bar)) }) return [n0, n3] }" @@ -180,7 +180,7 @@ export function render(_ctx) { `; exports[`compile > dynamic root nodes and interpolation 1`] = ` -"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, delegateEvents as _delegateEvents, template as _template } from 'vue'; +"import { child as _child, setProp as _setProp, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, delegateEvents as _delegateEvents, template as _template } from 'vue'; const t0 = _template("", true) _delegateEvents("click") @@ -190,13 +190,47 @@ export function render(_ctx) { n0.$evtclick = e => _ctx.handleClick(e) _renderEffect(() => { const _count = _ctx.count - _setText(x0, _toDisplayString(_count) + "foo" + _toDisplayString(_count) + "foo" + _toDisplayString(_count)) _setProp(n0, "id", _count) + _setText(x0, _toDisplayString(_count) + "foo" + _toDisplayString(_count) + "foo" + _toDisplayString(_count)) }) return n0 }" `; +exports[`compile > execution order > basic 1`] = ` +"import { child as _child, setProp as _setProp, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("
", true) + +export function render(_ctx) { + const n0 = t0() + const x0 = _child(n0) + _renderEffect(() => { + _setProp(n0, "id", _ctx.foo) + _setText(x0, _toDisplayString(_ctx.bar)) + }) + return n0 +}" +`; + +exports[`compile > execution order > with v-once 1`] = ` +"import { child as _child, next as _next, nthChild as _nthChild, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("

", true) + +export function render(_ctx) { + const n3 = t0() + const n0 = _child(n3) + const n1 = _next(n0) + const n2 = _nthChild(n3, 3) + const x0 = _child(n0) + _setText(x0, _toDisplayString(_ctx.foo)) + _renderEffect(() => { + _setText(n1, " " + _toDisplayString(_ctx.bar)) + _setText(n2, " " + _toDisplayString(_ctx.baz)) + }) + return n3 +}" +`; + exports[`compile > expression parsing > interpolation 1`] = ` " const n0 = t0() diff --git a/packages/compiler-vapor/__tests__/compile.spec.ts b/packages/compiler-vapor/__tests__/compile.spec.ts index 3a2ce41f0..178021d13 100644 --- a/packages/compiler-vapor/__tests__/compile.spec.ts +++ b/packages/compiler-vapor/__tests__/compile.spec.ts @@ -237,4 +237,29 @@ describe('compile', () => { expect(code).toMatchSnapshot() }) }) + + describe('execution order', () => { + test('basic', () => { + const code = compile(`
{{ bar }}
`) + expect(code).matchSnapshot() + expect(code).contains( + `_setProp(n0, "id", _ctx.foo) + _setText(x0, _toDisplayString(_ctx.bar))`, + ) + }) + test('with v-once', () => { + const code = compile( + `
+ {{ foo }} + {{ bar }}
+ {{ baz }} +
`, + ) + expect(code).matchSnapshot() + expect(code).contains( + `_setText(n1, " " + _toDisplayString(_ctx.bar)) + _setText(n2, " " + _toDisplayString(_ctx.baz))`, + ) + }) + }) }) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap index 5ae8a94f5..4a8caa659 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap @@ -67,7 +67,6 @@ export function render(_ctx) { const x2 = _child(n2) _renderEffect(() => { const _msg = _ctx.msg - _setText(x0, _toDisplayString(_msg)) _setText(x1, _toDisplayString(_msg)) _setText(x2, _toDisplayString(_msg)) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap index 9ffac2fb9..4e34c1818 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap @@ -55,7 +55,6 @@ export function render(_ctx) { const _foo = _ctx.foo const _bar = _ctx.bar const _foo_bar_baz = _foo[_bar(_ctx.baz)] - _setProp(n0, "id", _foo_bar_baz) _setProp(n1, "id", _foo_bar_baz) _setProp(n2, "id", _bar() + _foo) @@ -107,7 +106,6 @@ export function render(_ctx) { _renderEffect(() => { const _obj = _ctx.obj const _obj_foo_baz_obj_bar = _obj['foo']['baz'] + _obj.bar - _setProp(n0, "id", _obj_foo_baz_obj_bar) _setProp(n1, "id", _obj_foo_baz_obj_bar) }) @@ -126,7 +124,6 @@ export function render(_ctx) { _renderEffect(() => { const _foo = _ctx.foo const _foo_bar = _foo + _ctx.bar - _setProp(n0, "id", _foo_bar) _setProp(n1, "id", _foo_bar) _setProp(n2, "id", _foo + _foo_bar) @@ -144,7 +141,6 @@ export function render(_ctx) { const n1 = t0() _renderEffect(() => { const _foo_bar = _ctx.foo + _ctx.bar - _setProp(n0, "id", _foo_bar) _setProp(n1, "id", _foo_bar) }) @@ -177,7 +173,6 @@ export function render(_ctx) { const n1 = t0() _renderEffect(() => { const _foo = _ctx.foo - _setClass(n0, _foo) _setClass(n1, _foo) }) @@ -498,15 +493,13 @@ export function render(_ctx) { _setAttr(n0, "form", _ctx.form) _setAttr(n1, "list", _ctx.list) _setAttr(n2, "type", _ctx.type) - _setAttr(n3, "width", _width) - _setAttr(n4, "width", _width) - _setAttr(n5, "width", _width) - _setAttr(n6, "width", _width) - _setAttr(n3, "height", _height) + _setAttr(n4, "width", _width) _setAttr(n4, "height", _height) + _setAttr(n5, "width", _width) _setAttr(n5, "height", _height) + _setAttr(n6, "width", _width) _setAttr(n6, "height", _height) }) return [n0, n1, n2, n3, n4, n5, n6] diff --git a/packages/compiler-vapor/src/compile.ts b/packages/compiler-vapor/src/compile.ts index f84eafcbe..c39037a47 100644 --- a/packages/compiler-vapor/src/compile.ts +++ b/packages/compiler-vapor/src/compile.ts @@ -81,8 +81,8 @@ export function getBaseTransformPreset(): TransformPreset { transformVFor, transformSlotOutlet, transformTemplateRef, - transformText, transformElement, + transformText, transformVSlot, transformComment, transformChildren, diff --git a/packages/compiler-vapor/src/generators/operation.ts b/packages/compiler-vapor/src/generators/operation.ts index a3bf5cc21..563d72f1e 100644 --- a/packages/compiler-vapor/src/generators/operation.ts +++ b/packages/compiler-vapor/src/generators/operation.ts @@ -99,10 +99,8 @@ export function genEffects( effects: IREffect[], context: CodegenContext, ): CodeFragment[] { - const { - helper, - block: { expressions }, - } = context + const { helper } = context + const expressions = effects.flatMap(effect => effect.expressions) const [frag, push, unshift] = buildCodeFragment() let operationsCount = 0 const { ids, frag: declarationFrags } = processExpressions( diff --git a/packages/compiler-vapor/src/ir/index.ts b/packages/compiler-vapor/src/ir/index.ts index da6361132..18f0139ab 100644 --- a/packages/compiler-vapor/src/ir/index.ts +++ b/packages/compiler-vapor/src/ir/index.ts @@ -52,7 +52,6 @@ export interface BlockIRNode extends BaseIRNode { tempId: number effect: IREffect[] operation: OperationNode[] - expressions: SimpleExpressionNode[] returns: number[] } diff --git a/packages/compiler-vapor/src/transform.ts b/packages/compiler-vapor/src/transform.ts index 287bf671a..946c89b73 100644 --- a/packages/compiler-vapor/src/transform.ts +++ b/packages/compiler-vapor/src/transform.ts @@ -140,8 +140,10 @@ export class TransformContext { registerEffect( expressions: SimpleExpressionNode[], - ...operations: OperationNode[] + operation: OperationNode | OperationNode[], + getIndex = (): number => this.block.effect.length, ): void { + const operations = [operation].flat() expressions = expressions.filter(exp => !isConstantExpression(exp)) if ( this.inVOnce || @@ -153,26 +155,10 @@ export class TransformContext { return this.registerOperation(...operations) } - this.block.expressions.push(...expressions) - const existing = this.block.effect.find(e => - isSameExpression(e.expressions, expressions), - ) - if (existing) { - existing.operations.push(...operations) - } else { - this.block.effect.push({ - expressions, - operations, - }) - } - - function isSameExpression( - a: SimpleExpressionNode[], - b: SimpleExpressionNode[], - ) { - if (a.length !== b.length) return false - return a.every((exp, i) => exp.content === b[i].content) - } + this.block.effect.splice(getIndex(), 0, { + expressions, + operations, + }) } registerOperation(...node: OperationNode[]): void { diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index 9839fb808..05153e729 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -44,6 +44,8 @@ export const isReservedProp: (key: string) => boolean = /*#__PURE__*/ makeMap( ) export const transformElement: NodeTransform = (node, context) => { + let effectIndex = context.block.effect.length + const getEffectIndex = () => effectIndex++ return function postTransformElement() { ;({ node } = context) if ( @@ -62,6 +64,7 @@ export const transformElement: NodeTransform = (node, context) => { context as TransformContext, isComponent, isDynamicComponent, + getEffectIndex, ) let { parent } = context @@ -78,13 +81,23 @@ export const transformElement: NodeTransform = (node, context) => { parent.node.children.filter(child => child.type !== NodeTypes.COMMENT) .length === 1 - ;(isComponent ? transformComponentElement : transformNativeElement)( - node as any, - propsResult, - singleRoot, - context as TransformContext, - isDynamicComponent, - ) + if (isComponent) { + transformComponentElement( + node as ComponentNode, + propsResult, + singleRoot, + context, + isDynamicComponent, + ) + } else { + transformNativeElement( + node as PlainElementNode, + propsResult, + singleRoot, + context, + getEffectIndex, + ) + } } } @@ -183,7 +196,8 @@ function transformNativeElement( node: PlainElementNode, propsResult: PropsResult, singleRoot: boolean, - context: TransformContext, + context: TransformContext, + getEffectIndex: () => number, ) { const { tag } = node const { scopeId } = context.options @@ -196,12 +210,16 @@ function transformNativeElement( const dynamicProps: string[] = [] if (propsResult[0] /* dynamic props */) { const [, dynamicArgs, expressions] = propsResult - context.registerEffect(expressions, { - type: IRNodeTypes.SET_DYNAMIC_PROPS, - element: context.reference(), - props: dynamicArgs, - root: singleRoot, - }) + context.registerEffect( + expressions, + { + type: IRNodeTypes.SET_DYNAMIC_PROPS, + element: context.reference(), + props: dynamicArgs, + root: singleRoot, + }, + getEffectIndex, + ) } else { for (const prop of propsResult[1]) { const { key, values } = prop @@ -210,13 +228,17 @@ function transformNativeElement( if (values[0].content) template += `="${values[0].content}"` } else { dynamicProps.push(key.content) - context.registerEffect(values, { - type: IRNodeTypes.SET_PROP, - element: context.reference(), - prop, - root: singleRoot, - tag, - }) + context.registerEffect( + values, + { + type: IRNodeTypes.SET_PROP, + element: context.reference(), + prop, + root: singleRoot, + tag, + }, + getEffectIndex, + ) } } } @@ -253,6 +275,7 @@ export function buildProps( context: TransformContext, isComponent: boolean, isDynamicComponent?: boolean, + getEffectIndex?: () => number, ): PropsResult { const props = node.props as (VaporDirectiveNode | AttributeNode)[] if (props.length === 0) return [false, []] @@ -299,12 +322,12 @@ export function buildProps( } else { context.registerEffect( [prop.exp], - { type: IRNodeTypes.SET_DYNAMIC_EVENTS, element: context.reference(), event: prop.exp, }, + getEffectIndex, ) } } else { diff --git a/packages/compiler-vapor/src/transforms/utils.ts b/packages/compiler-vapor/src/transforms/utils.ts index b8e7adc60..f7d0594fe 100644 --- a/packages/compiler-vapor/src/transforms/utils.ts +++ b/packages/compiler-vapor/src/transforms/utils.ts @@ -29,7 +29,6 @@ export const newBlock = (node: BlockIRNode['node']): BlockIRNode => ({ effect: [], operation: [], returns: [], - expressions: [], tempId: 0, }) From c86bf7b11fa0cd0765d43361a3d375d384b3d1eb Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 20 Jun 2025 08:59:51 +0800 Subject: [PATCH 13/18] chore: update snap --- .../transforms/__snapshots__/expression.spec.ts.snap | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap index 454e50e9c..7e157236b 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap @@ -34,7 +34,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { `; exports[`compiler: expression > update expression 1`] = ` -"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; +"import { child as _child, setProp as _setProp, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template("
", true) export function render(_ctx) { @@ -44,12 +44,11 @@ export function render(_ctx) { _renderEffect(() => { const _String = String const _foo = _ctx.foo - - _setText(n0, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar)) - _setText(x1, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar)) _setProp(n1, "id", _String(_foo.id++)) _setProp(n1, "foo", _foo) _setProp(n1, "bar", _ctx.bar++) + _setText(n0, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar)) + _setText(x1, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar)) }) return n1 }" From 48a1370405abfcb1258e4e4cf7ea247cfba5a5a9 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 26 Jun 2025 14:44:39 +0800 Subject: [PATCH 14/18] fix(compiler-vapor): properly cache variable with optional chaining (#13519) --- .../transforms/__snapshots__/vBind.spec.ts.snap | 14 ++++++++++++++ .../__tests__/transforms/vBind.spec.ts | 7 +++++++ .../compiler-vapor/src/generators/expression.ts | 1 + 3 files changed, 22 insertions(+) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap index 4e34c1818..62a68ca42 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap @@ -113,6 +113,20 @@ export function render(_ctx) { }" `; +exports[`cache multiple access > optional chaining 1`] = ` +"import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("
", true) + +export function render(_ctx) { + const n0 = t0() + _renderEffect(() => { + const _obj = _ctx.obj + _setProp(n0, "id", _obj?.foo + _obj?.bar) + }) + return n0 +}" +`; + exports[`cache multiple access > repeated expression in expressions 1`] = ` "import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template("
") diff --git a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts index 60c3ebf0c..c062c96ba 100644 --- a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts @@ -794,6 +794,13 @@ describe('cache multiple access', () => { expect(code).contains('_setStyle(n0, {color: _color})') }) + test('optional chaining', () => { + const { code } = compileWithVBind(`
`) + expect(code).matchSnapshot() + expect(code).contains('const _obj = _ctx.obj') + expect(code).contains('_setProp(n0, "id", _obj?.foo + _obj?.bar)') + }) + test('not cache variable only used in property shorthand', () => { const { code } = compileWithVBind(`
diff --git a/packages/compiler-vapor/src/generators/expression.ts b/packages/compiler-vapor/src/generators/expression.ts index 845c8bedd..e2c3c0e17 100644 --- a/packages/compiler-vapor/src/generators/expression.ts +++ b/packages/compiler-vapor/src/generators/expression.ts @@ -588,6 +588,7 @@ function extractMemberExpression( case 'CallExpression': // foo[bar(baz)] return `${extractMemberExpression(exp.callee, onIdentifier)}(${exp.arguments.map(arg => extractMemberExpression(arg, onIdentifier)).join(', ')})` case 'MemberExpression': // foo[bar.baz] + case 'OptionalMemberExpression': // foo?.bar const object = extractMemberExpression(exp.object, onIdentifier) const prop = exp.computed ? `[${extractMemberExpression(exp.property, onIdentifier)}]` From 2074d66c909bcfcc1423c52ec09469b0c7a0d47c Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 26 Jun 2025 14:46:21 +0800 Subject: [PATCH 15/18] fix(compiler-sfc): always use defineComponent in SSR mode (#13525) --- packages/compiler-sfc/src/compileScript.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index 5825aa032..eb3b2d119 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -984,7 +984,7 @@ export function compileScript( ctx.s.prependLeft( startOffset, `\n${genDefaultAs} /*@__PURE__*/${ctx.helper( - vapor ? `defineVaporComponent` : `defineComponent`, + vapor && !ssr ? `defineVaporComponent` : `defineComponent`, )}({${def}${runtimeOptions}\n ${ hasAwait ? `async ` : `` }setup(${args}) {\n${exposeCall}`, From 280829bf73d58d6b17218b3df9b5e38ee8b7f720 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 26 Jun 2025 15:34:47 +0800 Subject: [PATCH 16/18] fix(runtime-vapor): ensure props are shallow reactive in VDOM component (#13502) --- packages/runtime-vapor/src/vdomInterop.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/runtime-vapor/src/vdomInterop.ts b/packages/runtime-vapor/src/vdomInterop.ts index b916a2c8e..e277024d7 100644 --- a/packages/runtime-vapor/src/vdomInterop.ts +++ b/packages/runtime-vapor/src/vdomInterop.ts @@ -16,6 +16,7 @@ import { isEmitListener, onScopeDispose, renderSlot, + shallowReactive, shallowRef, simpleSetCurrentInstance, } from '@vue/runtime-dom' @@ -163,7 +164,8 @@ function createVDOMComponent( // overwrite how the vdom instance handles props vnode.vi = (instance: ComponentInternalInstance) => { - instance.props = wrapper.props + // ensure props are shallow reactive to align with VDOM behavior. + instance.props = shallowReactive(wrapper.props) const attrs = (instance.attrs = createInternalObject()) for (const key in wrapper.attrs) { From 66f16ee5db347a572d4d8437c3ec8aa3e3a84e32 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 26 Jun 2025 15:35:27 +0800 Subject: [PATCH 17/18] fix(compiler-vapor): properly handle static ref in inline mode (#13257) --- .../transformTemplateRef.spec.ts.snap | 9 ++ .../transforms/transformTemplateRef.spec.ts | 11 +++ .../src/generators/templateRef.ts | 20 ++++- pnpm-lock.yaml | 88 +++++++++---------- 4 files changed, 83 insertions(+), 45 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap index cb520a4b2..2d64e1ffe 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap @@ -63,6 +63,15 @@ export function render(_ctx) { }" `; +exports[`compiler: template ref transform > static ref (inline mode) 1`] = ` +" + const _setTemplateRef = _createTemplateRefSetter() + const n0 = t0() + _setTemplateRef(n0, foo) + return n0 +" +`; + exports[`compiler: template ref transform > static ref 1`] = ` "import { createTemplateRefSetter as _createTemplateRefSetter, template as _template } from 'vue'; const t0 = _template("
", true) diff --git a/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts index b6bc479a0..2c883d10c 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts @@ -1,3 +1,4 @@ +import { BindingTypes } from '@vue/compiler-dom' import { DynamicFlag, type ForIRNode, @@ -48,6 +49,16 @@ describe('compiler: template ref transform', () => { expect(code).contains('_setTemplateRef(n0, "foo")') }) + test('static ref (inline mode)', () => { + const { code } = compileWithTransformRef(`
`, { + inline: true, + bindingMetadata: { foo: BindingTypes.SETUP_REF }, + }) + expect(code).matchSnapshot() + // pass the actual ref + expect(code).contains('_setTemplateRef(n0, foo)') + }) + test('dynamic ref', () => { const { ir, code } = compileWithTransformRef(`
`) diff --git a/packages/compiler-vapor/src/generators/templateRef.ts b/packages/compiler-vapor/src/generators/templateRef.ts index a4d6d546e..af8facc57 100644 --- a/packages/compiler-vapor/src/generators/templateRef.ts +++ b/packages/compiler-vapor/src/generators/templateRef.ts @@ -2,6 +2,7 @@ import { genExpression } from './expression' import type { CodegenContext } from '../generate' import type { DeclareOldRefIRNode, SetTemplateRefIRNode } from '../ir' import { type CodeFragment, NEWLINE, genCall } from './utils' +import { BindingTypes, type SimpleExpressionNode } from '@vue/compiler-dom' export const setTemplateRefIdent = `_setTemplateRef` @@ -15,7 +16,7 @@ export function genSetTemplateRef( ...genCall( setTemplateRefIdent, // will be generated in root scope `n${oper.element}`, - genExpression(oper.value, context), + genRefValue(oper.value, context), oper.effect ? `r${oper.element}` : oper.refFor ? 'void 0' : undefined, oper.refFor && 'true', ), @@ -25,3 +26,20 @@ export function genSetTemplateRef( export function genDeclareOldRef(oper: DeclareOldRefIRNode): CodeFragment[] { return [NEWLINE, `let r${oper.id}`] } + +function genRefValue(value: SimpleExpressionNode, context: CodegenContext) { + // in inline mode there is no setupState object, so we can't use string + // keys to set the ref. Instead, we need to transform it to pass the + // actual ref instead. + if (!__BROWSER__ && value && context.options.inline) { + const binding = context.options.bindingMetadata[value.content] + if ( + binding === BindingTypes.SETUP_LET || + binding === BindingTypes.SETUP_REF || + binding === BindingTypes.SETUP_MAYBE_REF + ) { + return [value.content] + } + } + return genExpression(value, context) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a8c6e9bb4..52d16261e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,7 +55,7 @@ importers: version: 5.0.4(rollup@4.44.0) '@swc/core': specifier: ^1.11.24 - version: 1.12.3 + version: 1.12.4 '@types/hash-sum': specifier: ^1.0.2 version: 1.0.2 @@ -1288,68 +1288,68 @@ packages: cpu: [x64] os: [win32] - '@swc/core-darwin-arm64@1.12.3': - resolution: {integrity: sha512-QCV9vQ/s27AMxm8j8MTDL/nDoiEMrANiENRrWnb0Fxvz/O39CajPVShp/W7HlOkzt1GYtUXPdQJpSKylugfrWw==} + '@swc/core-darwin-arm64@1.12.4': + resolution: {integrity: sha512-HihKfeitjZU2ab94Zf893sxzFryLKX0TweGsNXXOLNtkSMLw50auuYfpRM0BOL9/uXXtuCWgRIF6P030SAX5xQ==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.12.3': - resolution: {integrity: sha512-LylCMfzGhdvl5tyKaTT9ePetHUX7wSsST7hxWiHzS+cUMj7FnhcfdEr6kcNVT7y1RJn3fCvuv7T98ZB+T2q3HA==} + '@swc/core-darwin-x64@1.12.4': + resolution: {integrity: sha512-meYCXHyYb6RDdu2N5PNAf0EelyxPBFhRcVo4kBFLuvuNb0m6EUg///VWy8MUMXq9/s9uzGS9kJVXXdRdr/d6FA==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.12.3': - resolution: {integrity: sha512-DQODb7S+q+pwQY41Azcavwb2rb4rGxP70niScRDxB9X68hHOM9D0w9fxzC+Nr3AHcPSmVJUYUIiq5h38O5hVgQ==} + '@swc/core-linux-arm-gnueabihf@1.12.4': + resolution: {integrity: sha512-szfDbf7mE8V64of0q/LSqbk+em+T+TD3uqnH40Z7Qu/aL8vi5CHgyLjWG2SLkLLpyjgkAUF6AKrupgnBYcC2NA==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.12.3': - resolution: {integrity: sha512-nTxtJSq78AjeaQBueYImoFBs5j7qXbgOxtirpyt8jE29NQBd0VFzDzRBhkr6I9jq0hNiChgMkqBN4eUkEQjytg==} + '@swc/core-linux-arm64-gnu@1.12.4': + resolution: {integrity: sha512-n0IY76w+Scx8m3HIVRvLkoResuwsQgjDfAk9bxn99dq4leQO+mE0fkPl0Yw/1BIsPh+kxGfopIJH9zsZ1Z2YrA==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.12.3': - resolution: {integrity: sha512-lBGvC5UgPSxqLr/y1NZxQhyRQ7nXy3/Ec1Z47YNXtqtpKiG1EcOGPyS0UZgwiYQkXqq8NBFMHnyHmpKnXTvRDA==} + '@swc/core-linux-arm64-musl@1.12.4': + resolution: {integrity: sha512-wE5jmFi5cEQyLy8WmCWmNwfKETrnzy2D8YNi/xpYWpLPWqPhcelpa6tswkfYlbsMmmOh7hQNoTba1QdGu0jvHQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.12.3': - resolution: {integrity: sha512-61wZ8hwxNYzBY9MCWB50v90ICzdIhOuPk1O1qXswz9AXw5O6iQStEBHQ1rozPkfQ/rmhepk0pOf/6LCwssJOwg==} + '@swc/core-linux-x64-gnu@1.12.4': + resolution: {integrity: sha512-6S50Xd/7ePjEwrXyHMxpKTZ+KBrgUwMA8hQPbArUOwH4S5vHBr51heL0iXbUkppn1bkSr0J0IbOove5hzn+iqQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.12.3': - resolution: {integrity: sha512-NNeBiTpCgWt80vumTKVoaj6Fa/ZjUcaNQNM7np3PIgB8EbuXfyztboV7vUxpkmD/lUgsk8GlEFYViHvo6VMefQ==} + '@swc/core-linux-x64-musl@1.12.4': + resolution: {integrity: sha512-hbYRyaHhC13vYKuGG5BrAG5fjjWEQFfQetuFp/4QKEoXDzdnabJoixxWTQACDL3m0JW32nJ+gUzsYIPtFYkwXg==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.12.3': - resolution: {integrity: sha512-fxraM7exaPb1/W0CoHW45EFNOQUQh0nonBEcNFm2iv095mziBwttyxZyQBoDkQocpkd5NtsZw3xW5FTBPnn+Vw==} + '@swc/core-win32-arm64-msvc@1.12.4': + resolution: {integrity: sha512-e6EbfjPL8GA/bb1lc9Omtxjlz+1ThTsAuBsy4Q3Kpbuh6B3jclg8KzxU/6t91v23wG593mieTyR5f3Pr7X3AWw==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.12.3': - resolution: {integrity: sha512-FFIhMPXIDjRcewomwbYGPvem7Fj76AsuzbRahnAyp+OzJwrrtxVmra/kyUCfj4kix7vdGByY0WvVfiVCf5b7Mg==} + '@swc/core-win32-ia32-msvc@1.12.4': + resolution: {integrity: sha512-RG2FzmllBTUf4EksANlIvLckcBrLZEA0t13LIa6L213UZKQfEuDNHezqESgoVhJMg2S/tWauitATOCFgZNSmjg==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.12.3': - resolution: {integrity: sha512-Sf4iSg+IYT5AzFSDDmii08DfeKcvtkVxIuo+uS8BJMbiLjFNjgMkkVlBthknGyJcSK15ncg9248XjnM4jU8DZA==} + '@swc/core-win32-x64-msvc@1.12.4': + resolution: {integrity: sha512-oRHKnZlR83zaMeVUCmHENa4j5uNRAWbmEpjYbzRcfC45LPFNWKGWGAGERLx0u87XMUtTGqnVYxnBTHN/rzDHOw==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.12.3': - resolution: {integrity: sha512-c4NeXW8P3gPqcFwtm+4aH+F2Cj5KJLMiLaKhSj3mpv19glq+jmekomdktAw/VHyjsXlsmouOeNWrk8rVlkCRsg==} + '@swc/core@1.12.4': + resolution: {integrity: sha512-hn30ebV4njAn0NAUM+3a0qCF+MJgqTNSrfA/hUAbC6TVjOQy2OYGQwkUvCu/V7S2+rZxrUsTpKOnZ7qqECZV9Q==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -4482,51 +4482,51 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.44.0': optional: true - '@swc/core-darwin-arm64@1.12.3': + '@swc/core-darwin-arm64@1.12.4': optional: true - '@swc/core-darwin-x64@1.12.3': + '@swc/core-darwin-x64@1.12.4': optional: true - '@swc/core-linux-arm-gnueabihf@1.12.3': + '@swc/core-linux-arm-gnueabihf@1.12.4': optional: true - '@swc/core-linux-arm64-gnu@1.12.3': + '@swc/core-linux-arm64-gnu@1.12.4': optional: true - '@swc/core-linux-arm64-musl@1.12.3': + '@swc/core-linux-arm64-musl@1.12.4': optional: true - '@swc/core-linux-x64-gnu@1.12.3': + '@swc/core-linux-x64-gnu@1.12.4': optional: true - '@swc/core-linux-x64-musl@1.12.3': + '@swc/core-linux-x64-musl@1.12.4': optional: true - '@swc/core-win32-arm64-msvc@1.12.3': + '@swc/core-win32-arm64-msvc@1.12.4': optional: true - '@swc/core-win32-ia32-msvc@1.12.3': + '@swc/core-win32-ia32-msvc@1.12.4': optional: true - '@swc/core-win32-x64-msvc@1.12.3': + '@swc/core-win32-x64-msvc@1.12.4': optional: true - '@swc/core@1.12.3': + '@swc/core@1.12.4': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.23 optionalDependencies: - '@swc/core-darwin-arm64': 1.12.3 - '@swc/core-darwin-x64': 1.12.3 - '@swc/core-linux-arm-gnueabihf': 1.12.3 - '@swc/core-linux-arm64-gnu': 1.12.3 - '@swc/core-linux-arm64-musl': 1.12.3 - '@swc/core-linux-x64-gnu': 1.12.3 - '@swc/core-linux-x64-musl': 1.12.3 - '@swc/core-win32-arm64-msvc': 1.12.3 - '@swc/core-win32-ia32-msvc': 1.12.3 - '@swc/core-win32-x64-msvc': 1.12.3 + '@swc/core-darwin-arm64': 1.12.4 + '@swc/core-darwin-x64': 1.12.4 + '@swc/core-linux-arm-gnueabihf': 1.12.4 + '@swc/core-linux-arm64-gnu': 1.12.4 + '@swc/core-linux-arm64-musl': 1.12.4 + '@swc/core-linux-x64-gnu': 1.12.4 + '@swc/core-linux-x64-musl': 1.12.4 + '@swc/core-win32-arm64-msvc': 1.12.4 + '@swc/core-win32-ia32-msvc': 1.12.4 + '@swc/core-win32-x64-msvc': 1.12.4 '@swc/counter@0.1.3': {} From bb4ae25793961728a069e77f8a16a963091d8cbe Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 26 Jun 2025 15:41:25 +0800 Subject: [PATCH 18/18] fix(compiler-vapor): handle variable name substring edge cases (#13520) --- .../__snapshots__/vBind.spec.ts.snap | 29 +++++ .../__tests__/transforms/vBind.spec.ts | 19 ++++ .../src/generators/expression.ts | 104 +++++++++++++++--- 3 files changed, 134 insertions(+), 18 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap index 62a68ca42..4ea0db55f 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap @@ -113,6 +113,21 @@ export function render(_ctx) { }" `; +exports[`cache multiple access > object property name substring cases 1`] = ` +"import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("
", true) + +export function render(_ctx) { + const n0 = t0() + _renderEffect(() => { + const _p = _ctx.p + const _p_title = _p.title + _setProp(n0, "id", _p_title + _p.titles + _p_title) + }) + return n0 +}" +`; + exports[`cache multiple access > optional chaining 1`] = ` "import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template("
", true) @@ -194,6 +209,20 @@ export function render(_ctx) { }" `; +exports[`cache multiple access > variable name substring edge cases 1`] = ` +"import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("
", true) + +export function render(_ctx) { + const n0 = t0() + _renderEffect(() => { + const _title = _ctx.title + _setProp(n0, "id", _title + _ctx.titles + _title) + }) + return n0 +}" +`; + exports[`compiler v-bind > .attr modifier 1`] = ` "import { setAttr as _setAttr, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template("
", true) diff --git a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts index c062c96ba..e96186c27 100644 --- a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts @@ -785,6 +785,25 @@ describe('cache multiple access', () => { expect(code).contains('_setProp(n0, "id", _obj[1][_ctx.baz] + _obj.bar)') }) + test('variable name substring edge cases', () => { + const { code } = compileWithVBind( + `
`, + ) + expect(code).matchSnapshot() + expect(code).contains('const _title = _ctx.title') + expect(code).contains('_setProp(n0, "id", _title + _ctx.titles + _title)') + }) + + test('object property name substring cases', () => { + const { code } = compileWithVBind( + `
`, + ) + expect(code).matchSnapshot() + expect(code).contains('const _p = _ctx.p') + expect(code).contains('const _p_title = _p.title') + expect(code).contains('_setProp(n0, "id", _p_title + _p.titles + _p_title)') + }) + test('cache variable used in both property shorthand and normal binding', () => { const { code } = compileWithVBind(`
diff --git a/packages/compiler-vapor/src/generators/expression.ts b/packages/compiler-vapor/src/generators/expression.ts index e2c3c0e17..a8fbc8f83 100644 --- a/packages/compiler-vapor/src/generators/expression.ts +++ b/packages/compiler-vapor/src/generators/expression.ts @@ -283,7 +283,13 @@ export function processExpressions( function analyzeExpressions(expressions: SimpleExpressionNode[]) { const seenVariable: Record = Object.create(null) const variableToExpMap = new Map>() - const expToVariableMap = new Map() + const expToVariableMap = new Map< + SimpleExpressionNode, + Array<{ + name: string + loc?: { start: number; end: number } + }> + >() const seenIdentifier = new Set() const updatedVariable = new Set() @@ -291,6 +297,7 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) { name: string, exp: SimpleExpressionNode, isIdentifier: boolean, + loc?: { start: number; end: number }, parentStack: Node[] = [], ) => { if (isIdentifier) seenIdentifier.add(name) @@ -299,7 +306,11 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) { name, (variableToExpMap.get(name) || new Set()).add(exp), ) - expToVariableMap.set(exp, (expToVariableMap.get(exp) || []).concat(name)) + + const variables = expToVariableMap.get(exp) || [] + variables.push({ name, loc }) + expToVariableMap.set(exp, variables) + if ( parentStack.some( p => p.type === 'UpdateExpression' || p.type === 'AssignmentExpression', @@ -317,12 +328,27 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) { walkIdentifiers(exp.ast, (currentNode, parent, parentStack) => { if (parent && isMemberExpression(parent)) { - const memberExp = extractMemberExpression(parent, name => { - registerVariable(name, exp, true) + const memberExp = extractMemberExpression(parent, id => { + registerVariable(id.name, exp, true, { + start: id.start!, + end: id.end!, + }) }) - registerVariable(memberExp, exp, false, parentStack) + registerVariable( + memberExp, + exp, + false, + { start: parent.start!, end: parent.end! }, + parentStack, + ) } else if (!parentStack.some(isMemberExpression)) { - registerVariable(currentNode.name, exp, true, parentStack) + registerVariable( + currentNode.name, + exp, + true, + { start: currentNode.start!, end: currentNode.end! }, + parentStack, + ) } }) } @@ -340,11 +366,22 @@ function processRepeatedVariables( context: CodegenContext, seenVariable: Record, variableToExpMap: Map>, - expToVariableMap: Map, + expToVariableMap: Map< + SimpleExpressionNode, + Array<{ name: string; loc?: { start: number; end: number } }> + >, seenIdentifier: Set, updatedVariable: Set, ): DeclarationValue[] { const declarations: DeclarationValue[] = [] + const expToReplacementMap = new Map< + SimpleExpressionNode, + Array<{ + name: string + locs: { start: number; end: number }[] + }> + >() + for (const [name, exps] of variableToExpMap) { if (updatedVariable.has(name)) continue if (seenVariable[name] > 1 && exps.size > 0) { @@ -356,12 +393,20 @@ function processRepeatedVariables( // e.g., foo[baz] -> foo_baz. // for identifiers, we don't need to replace the content - they will be // replaced during context.withId(..., ids) - const replaceRE = new RegExp(escapeRegExp(name), 'g') exps.forEach(node => { - if (node.ast) { - node.content = node.content.replace(replaceRE, varName) - // re-parse the expression - node.ast = parseExp(context, node.content) + if (node.ast && varName !== name) { + const replacements = expToReplacementMap.get(node) || [] + replacements.push({ + name: varName, + locs: expToVariableMap.get(node)!.reduce( + (locs, v) => { + if (v.name === name && v.loc) locs.push(v.loc) + return locs + }, + [] as { start: number; end: number }[], + ), + }) + expToReplacementMap.set(node, replacements) } }) @@ -384,15 +429,35 @@ function processRepeatedVariables( } } + for (const [exp, replacements] of expToReplacementMap) { + replacements + .flatMap(({ name, locs }) => + locs.map(({ start, end }) => ({ start, end, name })), + ) + .sort((a, b) => b.end - a.end) + .forEach(({ start, end, name }) => { + exp.content = + exp.content.slice(0, start - 1) + name + exp.content.slice(end - 1) + }) + + // re-parse the expression + exp.ast = parseExp(context, exp.content) + } + return declarations } function shouldDeclareVariable( name: string, - expToVariableMap: Map, + expToVariableMap: Map< + SimpleExpressionNode, + Array<{ name: string; loc?: { start: number; end: number } }> + >, exps: Set, ): boolean { - const vars = Array.from(exps, exp => expToVariableMap.get(exp)!) + const vars = Array.from(exps, exp => + expToVariableMap.get(exp)!.map(v => v.name), + ) // assume name equals to `foo` // if each expression only references `foo`, declaration is needed // to avoid reactivity tracking @@ -439,12 +504,15 @@ function processRepeatedExpressions( expressions: SimpleExpressionNode[], varDeclarations: DeclarationValue[], updatedVariable: Set, - expToVariableMap: Map, + expToVariableMap: Map< + SimpleExpressionNode, + Array<{ name: string; loc?: { start: number; end: number } }> + >, ): DeclarationValue[] { const declarations: DeclarationValue[] = [] const seenExp = expressions.reduce( (acc, exp) => { - const variables = expToVariableMap.get(exp) + const variables = expToVariableMap.get(exp)!.map(v => v.name) // only handle expressions that are not identifiers if ( exp.ast && @@ -572,12 +640,12 @@ function genVarName(exp: string): string { function extractMemberExpression( exp: Node, - onIdentifier: (name: string) => void, + onIdentifier: (id: Identifier) => void, ): string { if (!exp) return '' switch (exp.type) { case 'Identifier': // foo[bar] - onIdentifier(exp.name) + onIdentifier(exp) return exp.name case 'StringLiteral': // foo['bar'] return exp.extra ? (exp.extra.raw as string) : exp.value