From b7977d4d755f3c37f4e7955cd018e1304592d532 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 25 Feb 2025 15:23:25 +0800 Subject: [PATCH 1/7] refactor(reactivity): sync alien-signals 1.0.4 changes (#12791) --- packages/reactivity/src/debug.ts | 7 ++-- packages/reactivity/src/system.ts | 54 ++++++++----------------------- 2 files changed, 16 insertions(+), 45 deletions(-) diff --git a/packages/reactivity/src/debug.ts b/packages/reactivity/src/debug.ts index aa7c87c95..5503dc8a1 100644 --- a/packages/reactivity/src/debug.ts +++ b/packages/reactivity/src/debug.ts @@ -69,11 +69,8 @@ function setupFlagsHandler(target: Subscriber): void { }, set(value) { if ( - !( - (target as any)._flags & - (SubscriberFlags.PendingComputed | SubscriberFlags.Dirty) - ) && - !!(value & (SubscriberFlags.PendingComputed | SubscriberFlags.Dirty)) + !((target as any)._flags & SubscriberFlags.Propagated) && + !!(value & SubscriberFlags.Propagated) ) { onTrigger(this) } diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 056ccfdd1..c88914a39 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -// Ported from https://github.com/stackblitz/alien-signals/blob/v1.0.0/src/system.ts +// Ported from https://github.com/stackblitz/alien-signals/blob/v1.0.4/src/system.ts import type { ComputedRefImpl as Computed } from './computed.js' import type { ReactiveEffect as Effect } from './effect.js' @@ -35,7 +35,6 @@ export const enum SubscriberFlags { let batchDepth = 0 let queuedEffects: Effect | undefined let queuedEffectsTail: Effect | undefined -let linkPool: Link | undefined export function startBatch(): void { ++batchDepth @@ -195,24 +194,18 @@ export function processComputedUpdate( computed: Computed, flags: SubscriberFlags, ): void { - if (flags & SubscriberFlags.Dirty) { + if ( + flags & SubscriberFlags.Dirty || + (checkDirty(computed.deps!) + ? true + : ((computed.flags = flags & ~SubscriberFlags.PendingComputed), false)) + ) { if (computed.update()) { const subs = computed.subs if (subs !== undefined) { shallowPropagate(subs) } } - } else if (flags & SubscriberFlags.PendingComputed) { - if (checkDirty(computed.deps!)) { - if (computed.update()) { - const subs = computed.subs - if (subs !== undefined) { - shallowPropagate(subs) - } - } - } else { - computed.flags = flags & ~SubscriberFlags.PendingComputed - } } } @@ -238,22 +231,12 @@ function linkNewDep( nextDep: Link | undefined, depsTail: Link | undefined, ): Link { - let newLink: Link - - if (linkPool !== undefined) { - newLink = linkPool - linkPool = newLink.nextDep - newLink.nextDep = nextDep - newLink.dep = dep - newLink.sub = sub - } else { - newLink = { - dep, - sub, - nextDep, - prevSub: undefined, - nextSub: undefined, - } + const newLink: Link = { + dep, + sub, + nextDep, + prevSub: undefined, + nextSub: undefined, } if (depsTail === undefined) { @@ -327,7 +310,7 @@ function checkDirty(link: Link): boolean { if (sub.update()) { if ((link = subSubs.prevSub!) !== undefined) { subSubs.prevSub = undefined - shallowPropagate(sub.subs!) + shallowPropagate(subSubs) sub = link.sub as Computed } else { sub = subSubs.sub as Computed @@ -400,25 +383,16 @@ function clearTracking(link: Link): void { if (nextSub !== undefined) { nextSub.prevSub = prevSub - link.nextSub = undefined } else { dep.subsTail = prevSub } if (prevSub !== undefined) { prevSub.nextSub = nextSub - link.prevSub = undefined } else { dep.subs = nextSub } - // @ts-expect-error - link.dep = undefined - // @ts-expect-error - link.sub = undefined - link.nextDep = linkPool - linkPool = link - if (dep.subs === undefined && 'deps' in dep) { const depFlags = dep.flags if (!(depFlags & SubscriberFlags.Dirty)) { From fb484f4f3c8ad776ccd27ed16774362c7e645f5a Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 28 Feb 2025 16:59:02 +0800 Subject: [PATCH 2/7] fix(vapor): handle vapor mode with empty script (#12910) --- packages-private/sfc-playground/package.json | 2 +- packages/compiler-sfc/__tests__/parse.spec.ts | 11 +++++++ packages/compiler-sfc/src/parse.ts | 12 +++----- pnpm-lock.yaml | 29 +++++++++++++++---- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/packages-private/sfc-playground/package.json b/packages-private/sfc-playground/package.json index 486971ad5..418b43d22 100644 --- a/packages-private/sfc-playground/package.json +++ b/packages-private/sfc-playground/package.json @@ -13,7 +13,7 @@ "vite": "catalog:" }, "dependencies": { - "@vue/repl": "^4.5.0", + "@vue/repl": "^4.5.1", "file-saver": "^2.0.5", "jszip": "^3.10.1", "vue": "workspace:*" diff --git a/packages/compiler-sfc/__tests__/parse.spec.ts b/packages/compiler-sfc/__tests__/parse.spec.ts index 265655e47..82b8cf98f 100644 --- a/packages/compiler-sfc/__tests__/parse.spec.ts +++ b/packages/compiler-sfc/__tests__/parse.spec.ts @@ -381,6 +381,17 @@ h1 { color: red } }) }) + describe('vapor mode', () => { + test('on empty script', () => { + const { descriptor } = parse(``) + expect(descriptor.vapor).toBe(true) + }) + test('on template', () => { + const { descriptor } = parse(``) + expect(descriptor.vapor).toBe(true) + }) + }) + describe('warnings', () => { function assertWarning(errors: Error[], msg: string) { expect(errors.some(e => e.message.match(msg))).toBe(true) diff --git a/packages/compiler-sfc/src/parse.ts b/packages/compiler-sfc/src/parse.ts index 8e8b23381..98b08a208 100644 --- a/packages/compiler-sfc/src/parse.ts +++ b/packages/compiler-sfc/src/parse.ts @@ -162,8 +162,9 @@ export function parse( ignoreEmpty && node.tag !== 'template' && isEmpty(node) && - !hasSrc(node) + !hasAttr(node, 'src') ) { + descriptor.vapor ||= hasAttr(node, 'vapor') return } switch (node.tag) { @@ -409,13 +410,8 @@ function padContent( } } -function hasSrc(node: ElementNode) { - return node.props.some(p => { - if (p.type !== NodeTypes.ATTRIBUTE) { - return false - } - return p.name === 'src' - }) +function hasAttr(node: ElementNode, name: string) { + return node.props.some(p => p.type === NodeTypes.ATTRIBUTE && p.name === name) } /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ff06e9c68..ef91ac818 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -251,8 +251,8 @@ importers: packages-private/sfc-playground: dependencies: '@vue/repl': - specifier: ^4.5.0 - version: 4.5.0 + specifier: ^4.5.1 + version: 4.5.1 file-saver: specifier: ^2.0.5 version: 2.0.5 @@ -1039,30 +1039,35 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-glibc@2.4.1': resolution: {integrity: sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.4.1': resolution: {integrity: sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.4.1': resolution: {integrity: sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.4.1': resolution: {integrity: sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-win32-arm64@2.4.1': resolution: {integrity: sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==} @@ -1195,51 +1200,61 @@ packages: resolution: {integrity: sha512-0O8ViX+QcBd3ZmGlcFTnYXZKGbFu09EhgD27tgTdGnkcYXLat4KIsBBQeKLR2xZDCXdIBAlWLkiXE1+rJpCxFw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.31.0': resolution: {integrity: sha512-w5IzG0wTVv7B0/SwDnMYmbr2uERQp999q8FMkKG1I+j8hpPX2BYFjWe69xbhbP6J9h2gId/7ogesl9hwblFwwg==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.31.0': resolution: {integrity: sha512-JyFFshbN5xwy6fulZ8B/8qOqENRmDdEkcIMF0Zz+RsfamEW+Zabl5jAb0IozP/8UKnJ7g2FtZZPEUIAlUSX8cA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.31.0': resolution: {integrity: sha512-kpQXQ0UPFeMPmPYksiBL9WS/BDiQEjRGMfklVIsA0Sng347H8W2iexch+IEwaR7OVSKtr2ZFxggt11zVIlZ25g==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.31.0': resolution: {integrity: sha512-pMlxLjt60iQTzt9iBb3jZphFIl55a70wexvo8p+vVFK+7ifTRookdoXX3bOsRdmfD+OKnMozKO6XM4zR0sHRrQ==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.31.0': resolution: {integrity: sha512-D7TXT7I/uKEuWiRkEFbed1UUYZwcJDU4vZQdPTcepK7ecPhzKOYk4Er2YR4uHKme4qDeIh6N3XrLfpuM7vzRWQ==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.31.0': resolution: {integrity: sha512-wal2Tc8O5lMBtoePLBYRKj2CImUCJ4UNGJlLwspx7QApYny7K1cUYlzQ/4IGQBLmm+y0RS7dwc3TDO/pmcneTw==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.31.0': resolution: {integrity: sha512-O1o5EUI0+RRMkK9wiTVpk2tyzXdXefHtRTIjBbmFREmNMy7pFeYXCFGbhKFwISA3UOExlo5GGUuuj3oMKdK6JQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.31.0': resolution: {integrity: sha512-zSoHl356vKnNxwOWnLd60ixHNPRBglxpv2g7q0Cd3Pmr561gf0HiAcUBRL3S1vPqRC17Zo2CX/9cPkqTIiai1g==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.31.0': resolution: {integrity: sha512-ypB/HMtcSGhKUQNiFwqgdclWNRrAYDH8iMYH4etw/ZlGwiTVxBz2tDrGRrPlfZu6QjXwtd+C3Zib5pFqID97ZA==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.31.0': resolution: {integrity: sha512-JuhN2xdI/m8Hr+aVO3vspO7OQfUFO6bKLIRTAy0U15vmWjnZDLrEgCZ2s6+scAYaQVpYSh9tZtRijApw9IXyMw==} @@ -1279,24 +1294,28 @@ packages: engines: {node: '>=10'} cpu: [arm64] os: [linux] + libc: [glibc] '@swc/core-linux-arm64-musl@1.10.8': resolution: {integrity: sha512-wI0Hny8fHbBK/OjJ7eFYP0uDKiCMMMr5OBWGKMRRUvWs2zlGeJQZbwUeCnWuLLXzDfL+feMfh5TieYlqKTTtRw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] + libc: [musl] '@swc/core-linux-x64-gnu@1.10.8': resolution: {integrity: sha512-24FCRUFO8gzPP2eu3soHTm3lk+ktcsIhdM2DTOlXGA+2TBYFWgAZX/yZV+eeRrtIZYSr4OcOWsNWnQ5Ma4budA==} engines: {node: '>=10'} cpu: [x64] os: [linux] + libc: [glibc] '@swc/core-linux-x64-musl@1.10.8': resolution: {integrity: sha512-mBo7M/FmUhoWpUG17MLbS98iRA7t6ThxQBWDJZd322whkN1GqrvumYm2wvvjmoMTeDOPwAL3hIIa5H+Q4vb1zA==} engines: {node: '>=10'} cpu: [x64] os: [linux] + libc: [musl] '@swc/core-win32-arm64-msvc@1.10.8': resolution: {integrity: sha512-rXJ9y77JZZXoZkgFR0mObKa3TethRBJ6Exs/pwhScl9pz4qsfxhj/bQbEu1g1i/ihmd0l+IKZwGSC7Ibh3HA2Q==} @@ -1534,8 +1553,8 @@ packages: '@vue/reactivity@3.5.13': resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==} - '@vue/repl@4.5.0': - resolution: {integrity: sha512-nWQfTzBePs5zN4qIK+vwEMEDHCuWWx2AY0utun37cSD2Qi4C84dlTtO/OL0xDzBB8Ob7250KYzIzDP3N3l3qLg==} + '@vue/repl@4.5.1': + resolution: {integrity: sha512-YYXvFue2GOrZ6EWnoA8yQVKzdCIn45+tpwJHzMof1uwrgyYAVY9ynxCsDYeAuWcpaAeylg/nybhFuqiFy2uvYA==} '@vue/runtime-core@3.5.13': resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==} @@ -4753,7 +4772,7 @@ snapshots: dependencies: '@vue/shared': 3.5.13 - '@vue/repl@4.5.0': {} + '@vue/repl@4.5.1': {} '@vue/runtime-core@3.5.13': dependencies: From 05d9b429a4edc2a10d4fceff3c423c2cdffd6a0a Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 28 Feb 2025 17:02:11 +0800 Subject: [PATCH 3/7] fix(vapor): handle next host node for vapor component (#12820) --- packages/runtime-core/src/renderer.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index fcbfdd042..88012204e 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2418,6 +2418,9 @@ function baseCreateRenderer( const getNextHostNode: NextFn = vnode => { if (vnode.shapeFlag & ShapeFlags.COMPONENT) { + if ((vnode.type as ConcreteComponent).__vapor) { + return hostNextSibling((vnode.component! as any).block) + } return getNextHostNode(vnode.component!.subTree) } if (__FEATURE_SUSPENSE__ && vnode.shapeFlag & ShapeFlags.SUSPENSE) { From ad5bb167096d904a35237dad0dfdaafb33d10151 Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 28 Feb 2025 17:02:52 +0800 Subject: [PATCH 4/7] fix(compiler-vapor): properly cache variable used in object property shorthand (#12815) --- .../__snapshots__/vBind.spec.ts.snap | 26 +++++++++++++++++++ .../__tests__/transforms/vBind.spec.ts | 17 ++++++++++++ .../src/generators/expression.ts | 18 +++++++++++-- 3 files changed, 59 insertions(+), 2 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 24585e39e..6e7d4229d 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap @@ -1,5 +1,20 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`cache multiple access > cache variable used in both property shorthand and normal binding 1`] = ` +"import { setStyle as _setStyle, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("
", true) + +export function render(_ctx) { + const n0 = t0() + _renderEffect(() => { + const _color = _ctx.color + _setStyle(n0, {color: _color}) + _setProp(n0, "id", _color) + }) + return n0 +}" +`; + exports[`cache multiple access > dynamic key bindings with expressions 1`] = ` "import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template("
", true) @@ -60,6 +75,17 @@ export function render(_ctx) { }" `; +exports[`cache multiple access > not cache variable only used in property shorthand 1`] = ` +"import { setStyle as _setStyle, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("
", true) + +export function render(_ctx) { + const n0 = t0() + _renderEffect(() => _setStyle(n0, {color: _ctx.color})) + return n0 +}" +`; + exports[`cache multiple access > object property chain access 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 5025997e2..9a5f6ab69 100644 --- a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts @@ -785,6 +785,23 @@ describe('cache multiple access', () => { expect(code).contains('_setProp(n0, "id", _obj[1][_ctx.baz] + _obj.bar)') }) + test('cache variable used in both property shorthand and normal binding', () => { + const { code } = compileWithVBind(` +
+ `) + expect(code).matchSnapshot() + expect(code).contains('const _color = _ctx.color') + expect(code).contains('_setStyle(n0, {color: _color})') + }) + + test('not cache variable only used in property shorthand', () => { + const { code } = compileWithVBind(` +
+ `) + expect(code).matchSnapshot() + expect(code).not.contains('const _color = _ctx.color') + }) + test('not cache variable and member expression with the same name', () => { const { code } = compileWithVBind(`
diff --git a/packages/compiler-vapor/src/generators/expression.ts b/packages/compiler-vapor/src/generators/expression.ts index e128ccfbe..eedaeeb38 100644 --- a/packages/compiler-vapor/src/generators/expression.ts +++ b/packages/compiler-vapor/src/generators/expression.ts @@ -131,7 +131,11 @@ function genIdentifier( if (idMap && idMap.length) { const replacement = idMap[0] if (isString(replacement)) { - return [[replacement, NewlineType.None, loc]] + if (parent && parent.type === 'ObjectProperty' && parent.shorthand) { + return [[`${name}: ${replacement}`, NewlineType.None, loc]] + } else { + return [[replacement, NewlineType.None, loc]] + } } else { // replacement is an expression - process it again return genExpression(replacement, context, assignment) @@ -292,7 +296,7 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) { } walk(exp.ast, { - enter(currentNode: Node) { + enter(currentNode: Node, parent: Node | null) { if (currentNode.type === 'MemberExpression') { const memberExp = extractMemberExpression( currentNode, @@ -304,6 +308,16 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) { return this.skip() } + // skip shorthand or non-computed property keys + if ( + parent && + parent.type === 'ObjectProperty' && + parent.key === currentNode && + (parent.shorthand || !parent.computed) + ) { + return this.skip() + } + if (currentNode.type === 'Identifier') { registerVariable(currentNode.name, exp, true) } From a95b5e70e6b70c7c7f95c57e72d77cd5d8c1d44d Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 28 Feb 2025 17:03:11 +0800 Subject: [PATCH 5/7] feat(runtime-vapor): expose VaporFragment for jsx-vapor (#12890) --- packages/runtime-vapor/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index 40a847ba8..89b6f50b0 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -5,7 +5,7 @@ export { vaporInteropPlugin } from './vdomInterop' export type { VaporDirective } from './directives/custom' // compiler-use only -export { insert, prepend, remove } from './block' +export { insert, prepend, remove, isFragment, VaporFragment } from './block' export { createComponent, createComponentWithFallback } from './component' export { renderEffect } from './renderEffect' export { createSlot } from './componentSlots' From 5452404b71e4957649af04a3a351ac7bcafd30b6 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 28 Feb 2025 17:06:19 +0800 Subject: [PATCH 6/7] feat(compiler-vapor): add jsx support for setText and createTextNode (#12893) --- packages/compiler-vapor/src/generators/text.ts | 13 +++++++------ packages/compiler-vapor/src/ir/index.ts | 2 ++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/compiler-vapor/src/generators/text.ts b/packages/compiler-vapor/src/generators/text.ts index 3c9835f88..280d0a918 100644 --- a/packages/compiler-vapor/src/generators/text.ts +++ b/packages/compiler-vapor/src/generators/text.ts @@ -14,8 +14,8 @@ export function genSetText( context: CodegenContext, ): CodeFragment[] { const { helper } = context - const { element, values, generated } = oper - const texts = combineValues(values, context) + const { element, values, generated, jsx } = oper + const texts = combineValues(values, context, jsx) return [ NEWLINE, ...genCall(helper('setText'), `${generated ? 'x' : 'n'}${element}`, texts), @@ -27,13 +27,13 @@ export function genCreateTextNode( context: CodegenContext, ): CodeFragment[] { const { helper } = context - const { id, values } = oper + const { id, values, jsx } = oper return [ NEWLINE, `const n${id} = `, ...genCall( helper('createTextNode'), - values && combineValues(values, context), + values && combineValues(values, context, jsx), ), ] } @@ -41,15 +41,16 @@ export function genCreateTextNode( function combineValues( values: SimpleExpressionNode[], context: CodegenContext, + jsx?: boolean, ): CodeFragment[] { return values.flatMap((value, i) => { let exp = genExpression(value, context) - if (getLiteralExpressionValue(value) == null) { + if (!jsx && getLiteralExpressionValue(value) == null) { // dynamic, wrap with toDisplayString exp = genCall(context.helper('toDisplayString'), exp) } if (i > 0) { - exp.unshift(' + ') + exp.unshift(jsx ? ', ' : ' + ') } return exp }) diff --git a/packages/compiler-vapor/src/ir/index.ts b/packages/compiler-vapor/src/ir/index.ts index 1509d3742..6616e35e9 100644 --- a/packages/compiler-vapor/src/ir/index.ts +++ b/packages/compiler-vapor/src/ir/index.ts @@ -121,6 +121,7 @@ export interface SetTextIRNode extends BaseIRNode { element: number values: SimpleExpressionNode[] generated?: boolean // whether this is a generated empty text node by `processTextLikeContainer` + jsx?: boolean } export type KeyOverride = [find: string, replacement: string] @@ -161,6 +162,7 @@ export interface CreateTextNodeIRNode extends BaseIRNode { type: IRNodeTypes.CREATE_TEXT_NODE id: number values?: SimpleExpressionNode[] + jsx?: boolean } export interface InsertNodeIRNode extends BaseIRNode { From bce7164bf0045102e907a520adeb99bb8a2b2a58 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 28 Feb 2025 17:07:55 +0800 Subject: [PATCH 7/7] feat(runtime-vapor): support functional component for defineVaporComponent (#12927) --- packages/runtime-vapor/__tests__/_utils.ts | 5 +++-- .../__tests__/componentProps.spec.ts | 21 +++++++++++++++++++ .../runtime-vapor/src/apiDefineComponent.ts | 17 +++++++++++++-- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/packages/runtime-vapor/__tests__/_utils.ts b/packages/runtime-vapor/__tests__/_utils.ts index c34eb05a0..0ed645544 100644 --- a/packages/runtime-vapor/__tests__/_utils.ts +++ b/packages/runtime-vapor/__tests__/_utils.ts @@ -1,4 +1,4 @@ -import { createVaporApp, defineVaporComponent } from '../src' +import { createVaporApp } from '../src' import type { App } from '@vue/runtime-dom' import type { VaporComponent, VaporComponentInstance } from '../src/component' import type { RawProps } from '../src/componentProps' @@ -36,7 +36,8 @@ export function makeRender( }) function define(comp: C) { - const component = defineVaporComponent(comp as any) + const component = comp as any + component.__vapor = true let instance: VaporComponentInstance | undefined let app: App diff --git a/packages/runtime-vapor/__tests__/componentProps.spec.ts b/packages/runtime-vapor/__tests__/componentProps.spec.ts index 2fd0e9df1..c068e8044 100644 --- a/packages/runtime-vapor/__tests__/componentProps.spec.ts +++ b/packages/runtime-vapor/__tests__/componentProps.spec.ts @@ -127,6 +127,27 @@ describe('component: props', () => { expect(props).toBe(attrs) }) + test('functional defineVaporComponent without declaration', () => { + let props: any + let attrs: any + + const { render } = define( + defineVaporComponent((_props: any, { attrs: _attrs }: any) => { + props = _props + attrs = _attrs + return [] + }), + ) + + render({ foo: () => 1 }) + expect(props).toEqual({}) + expect(attrs).toEqual({ foo: 1 }) + + render({ bar: () => 2 }) + expect(props).toEqual({}) + expect(attrs).toEqual({ bar: 2 }) + }) + test('boolean casting', () => { let props: any const { render } = define({ diff --git a/packages/runtime-vapor/src/apiDefineComponent.ts b/packages/runtime-vapor/src/apiDefineComponent.ts index ed70a6495..430f87cdd 100644 --- a/packages/runtime-vapor/src/apiDefineComponent.ts +++ b/packages/runtime-vapor/src/apiDefineComponent.ts @@ -1,7 +1,20 @@ -import type { VaporComponent } from './component' +import type { ObjectVaporComponent, VaporComponent } from './component' +import { extend, isFunction } from '@vue/shared' /*! #__NO_SIDE_EFFECTS__ */ -export function defineVaporComponent(comp: VaporComponent): VaporComponent { +export function defineVaporComponent( + comp: VaporComponent, + extraOptions?: Omit, +): VaporComponent { + if (isFunction(comp)) { + // #8236: extend call and options.name access are considered side-effects + // by Rollup, so we have to wrap it in a pure-annotated IIFE. + return /*@__PURE__*/ (() => + extend({ name: comp.name }, extraOptions, { + setup: comp, + __vapor: true, + }))() + } // TODO type inference comp.__vapor = true return comp