From e7531d18df3527b647a9f9d3e1ea5017dcb6c483 Mon Sep 17 00:00:00 2001 From: Rizumu Ayaka Date: Mon, 29 Apr 2024 23:14:03 +0800 Subject: [PATCH] feat(compiler-vapor): component with dynamic arguments --- .../transformElement.spec.ts.snap | 29 ++++ .../__snapshots__/vModel.spec.ts.snap | 17 +-- .../transforms/transformElement.spec.ts | 48 +++++++ .../__tests__/transforms/vModel.spec.ts | 42 +++--- .../src/generators/component.ts | 126 ++++++++++-------- .../compiler-vapor/src/generators/prop.ts | 4 +- packages/compiler-vapor/src/ir.ts | 11 +- .../src/transforms/transformElement.ts | 9 +- 8 files changed, 194 insertions(+), 92 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 0fcf9b1e1..cdd5790b5 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap @@ -179,6 +179,35 @@ export function render(_ctx) { }" `; +exports[`compiler: element transform > component with dynamic event arguments 1`] = ` +"import { toHandlerKey as _toHandlerKey } from 'vue'; +import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor'; + +export function render(_ctx) { + const _component_Foo = _resolveComponent("Foo") + const n0 = _createComponent(_component_Foo, [() => ({ + [_toHandlerKey(_ctx.foo-_ctx.bar)]: () => _ctx.bar + }), () => ({ + [_toHandlerKey(_ctx.baz)]: () => _ctx.qux + })], true) + return n0 +}" +`; + +exports[`compiler: element transform > component with dynamic prop arguments 1`] = ` +"import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor'; + +export function render(_ctx) { + const _component_Foo = _resolveComponent("Foo") + const n0 = _createComponent(_component_Foo, [() => ({ + [_ctx.foo-_ctx.bar]: _ctx.bar + }), () => ({ + [_ctx.baz]: _ctx.qux + })], true) + return n0 +}" +`; + exports[`compiler: element transform > invalid html nesting 1`] = ` "import { insert as _insert, template as _template } from 'vue/vapor'; const t0 = _template("
123
") diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vModel.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vModel.spec.ts.snap index a8764d6e1..911ed1486 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vModel.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vModel.spec.ts.snap @@ -62,14 +62,15 @@ exports[`compiler: vModel transform > component > v-model with dynamic arguments export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") - const n0 = _createComponent(_component_Comp, [{ - [_ctx.foo]: () => (_ctx.foo), + const n0 = _createComponent(_component_Comp, [() => ({ + [_ctx.foo]: _ctx.foo, ["onUpdate:" + _ctx.foo]: () => $event => (_ctx.foo = $event), - [_ctx.foo + "Modifiers"]: () => ({ trim: true }), - [_ctx.bar]: () => (_ctx.bar), + [_ctx.foo + "Modifiers"]: () => ({ trim: true }) + }), () => ({ + [_ctx.bar]: _ctx.bar, ["onUpdate:" + _ctx.bar]: () => $event => (_ctx.bar = $event), [_ctx.bar + "Modifiers"]: () => ({ number: true }) - }], true) + })], true) return n0 }" `; @@ -79,10 +80,10 @@ exports[`compiler: vModel transform > component > v-model with dynamic arguments export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") - const n0 = _createComponent(_component_Comp, [{ - [_ctx.arg]: () => (_ctx.foo), + const n0 = _createComponent(_component_Comp, [() => ({ + [_ctx.arg]: _ctx.foo, ["onUpdate:" + _ctx.arg]: () => $event => (_ctx.foo = $event) - }], true) + })], true) return n0 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts index 2fa5245a9..fcadd9447 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts @@ -691,6 +691,54 @@ describe('compiler: element transform', () => { expect(code).contains('_setDynamicEvents(n0, _ctx.obj)') }) + test('component with dynamic prop arguments', () => { + const { code, ir } = compileWithElementTransform( + ``, + ) + expect(code).toMatchSnapshot() + expect(ir.block.operation).toMatchObject([ + { + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Foo', + props: [ + { + key: { content: 'foo-bar' }, + values: [{ content: 'bar' }], + }, + { + key: { content: 'baz' }, + values: [{ content: 'qux' }], + }, + ], + }, + ]) + }) + + test('component with dynamic event arguments', () => { + const { code, ir } = compileWithElementTransform( + ``, + ) + expect(code).toMatchSnapshot() + expect(ir.block.operation).toMatchObject([ + { + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Foo', + props: [ + { + key: { content: 'foo-bar' }, + values: [{ content: 'bar' }], + handler: true, + }, + { + key: { content: 'baz' }, + values: [{ content: 'qux' }], + handler: true, + }, + ], + }, + ]) + }) + test('invalid html nesting', () => { const { code, ir } = compileWithElementTransform( `

123

diff --git a/packages/compiler-vapor/__tests__/transforms/vModel.spec.ts b/packages/compiler-vapor/__tests__/transforms/vModel.spec.ts index 80a22c880..921145643 100644 --- a/packages/compiler-vapor/__tests__/transforms/vModel.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vModel.spec.ts @@ -259,7 +259,7 @@ describe('compiler: vModel transform', () => { const { code, ir } = compileWithVModel('') expect(code).toMatchSnapshot() expect(code).contains( - `[_ctx.arg]: () => (_ctx.foo), + `[_ctx.arg]: _ctx.foo, ["onUpdate:" + _ctx.arg]: () => $event => (_ctx.foo = $event)`, ) expect(ir.block.operation).toMatchObject([ @@ -267,14 +267,12 @@ describe('compiler: vModel transform', () => { type: IRNodeTypes.CREATE_COMPONENT_NODE, tag: 'Comp', props: [ - [ - { - key: { content: 'arg', isStatic: false }, - values: [{ content: 'foo', isStatic: false }], - model: true, - modelModifiers: [], - }, - ], + { + key: { content: 'arg', isStatic: false }, + values: [{ content: 'foo', isStatic: false }], + model: true, + modelModifiers: [], + }, ], }, ]) @@ -349,20 +347,18 @@ describe('compiler: vModel transform', () => { type: IRNodeTypes.CREATE_COMPONENT_NODE, tag: 'Comp', props: [ - [ - { - key: { content: 'foo', isStatic: false }, - values: [{ content: 'foo', isStatic: false }], - model: true, - modelModifiers: ['trim'], - }, - { - key: { content: 'bar', isStatic: false }, - values: [{ content: 'bar', isStatic: false }], - model: true, - modelModifiers: ['number'], - }, - ], + { + key: { content: 'foo', isStatic: false }, + values: [{ content: 'foo', isStatic: false }], + model: true, + modelModifiers: ['trim'], + }, + { + key: { content: 'bar', isStatic: false }, + values: [{ content: 'bar', isStatic: false }], + model: true, + modelModifiers: ['number'], + }, ], }, ]) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index 5f3d060b8..63e7137da 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -1,6 +1,6 @@ import { camelize, extend, isArray } from '@vue/shared' import type { CodegenContext } from '../generate' -import type { CreateComponentIRNode, IRProp } from '../ir' +import type { CreateComponentIRNode, IRProp, IRProps } from '../ir' import { type CodeFragment, NEWLINE, @@ -21,11 +21,11 @@ export function genCreateComponent( oper: CreateComponentIRNode, context: CodegenContext, ): CodeFragment[] { - const { helper, vaporHelper } = context + const { vaporHelper } = context const tag = genTag() const isRoot = oper.root - const rawProps = genRawProps() + const rawProps = genRawProps(oper.props, context) return [ NEWLINE, @@ -49,63 +49,83 @@ export function genCreateComponent( ) } } +} - function genRawProps() { - const props = oper.props - .map(props => { - if (isArray(props)) { - if (!props.length) return - return genStaticProps(props) - } else { - let expr = genExpression(props.value, context) - if (props.handler) expr = genCall(helper('toHandlers'), expr) - return ['() => (', ...expr, ')'] +export function genRawProps(props: IRProps[], context: CodegenContext) { + const frag = props + .map(props => { + if (isArray(props)) { + if (!props.length) return + return genStaticProps(props, context) + } else { + let expr: CodeFragment[] + if ('key' in props) + expr = genMulti( + SEGMENTS_OBJECT_NEWLINE, + genProp(props, context, false), + ) + else { + expr = genExpression(props.value, context) + if (props.handler) expr = genCall(context.helper('toHandlers'), expr) } - }) - .filter(Boolean) - if (props.length) { - return genMulti(SEGMENTS_ARRAY, ...props) - } + return ['() => (', ...expr, ')'] + } + }) + .filter( + Boolean as any as (v: CodeFragment[] | undefined) => v is CodeFragment[], + ) + if (frag.length) { + return genMulti(SEGMENTS_ARRAY, ...frag) } +} - function genStaticProps(props: IRProp[]) { - return genMulti( - SEGMENTS_OBJECT_NEWLINE, - ...props.map(prop => { - return [ - ...genPropKey(prop, context), - ': ', - ...(prop.handler - ? genEventHandler(context, prop.values[0]) - : ['() => (', ...genExpression(prop.values[0], context), ')']), - ...(prop.model - ? [...genModelEvent(prop), ...genModelModifiers(prop)] - : []), - ] - }), - ) +function genStaticProps( + props: IRProp[], + context: CodegenContext, +): CodeFragment[] { + return genMulti( + SEGMENTS_OBJECT_NEWLINE, + ...props.map(prop => genProp(prop, context, true)), + ) +} + +function genProp(prop: IRProp, context: CodegenContext, isStaticArg: boolean) { + return [ + ...genPropKey(prop, context), + ': ', + ...(prop.handler + ? genEventHandler(context, prop.values[0]) + : isStaticArg + ? ['() => (', ...genExpression(prop.values[0], context), ')'] + : genExpression(prop.values[0], context)), + ...(prop.model + ? [...genModelEvent(prop, context), ...genModelModifiers(prop, context)] + : []), + ] +} - function genModelEvent(prop: IRProp): CodeFragment[] { - const name = prop.key.isStatic - ? [JSON.stringify(`onUpdate:${camelize(prop.key.content)}`)] - : ['["onUpdate:" + ', ...genExpression(prop.key, context), ']'] - const handler = genModelHandler(prop.values[0], context) +function genModelEvent(prop: IRProp, context: CodegenContext): CodeFragment[] { + const name = prop.key.isStatic + ? [JSON.stringify(`onUpdate:${camelize(prop.key.content)}`)] + : ['["onUpdate:" + ', ...genExpression(prop.key, context), ']'] + const handler = genModelHandler(prop.values[0], context) - return [',', NEWLINE, ...name, ': ', ...handler] - } + return [',', NEWLINE, ...name, ': ', ...handler] +} - function genModelModifiers(prop: IRProp): CodeFragment[] { - const { key, modelModifiers } = prop - if (!modelModifiers || !modelModifiers.length) return [] +function genModelModifiers( + prop: IRProp, + context: CodegenContext, +): CodeFragment[] { + const { key, modelModifiers } = prop + if (!modelModifiers || !modelModifiers.length) return [] - const modifiersKey = key.isStatic - ? key.content === 'modelValue' - ? [`modelModifiers`] - : [`${key.content}Modifiers`] - : ['[', ...genExpression(key, context), ' + "Modifiers"]'] + const modifiersKey = key.isStatic + ? key.content === 'modelValue' + ? [`modelModifiers`] + : [`${key.content}Modifiers`] + : ['[', ...genExpression(key, context), ' + "Modifiers"]'] - const modifiersVal = genDirectiveModifiers(modelModifiers) - return [',', NEWLINE, ...modifiersKey, `: () => ({ ${modifiersVal} })`] - } - } + const modifiersVal = genDirectiveModifiers(modelModifiers) + return [',', NEWLINE, ...modifiersKey, `: () => ({ ${modifiersVal} })`] } diff --git a/packages/compiler-vapor/src/generators/prop.ts b/packages/compiler-vapor/src/generators/prop.ts index 1f31e4ceb..c91510f23 100644 --- a/packages/compiler-vapor/src/generators/prop.ts +++ b/packages/compiler-vapor/src/generators/prop.ts @@ -73,7 +73,9 @@ export function genDynamicProps( props => Array.isArray(props) ? genLiteralObjectProps(props, context) // static and dynamic arg props - : genExpression(props.value, context), // v-bind="" + : 'key' in props + ? genLiteralObjectProps([props], context) // dynamic arg props + : genExpression(props.value, context), // v-bind="" ), ), ] diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir.ts index e5ba223d7..457b646b0 100644 --- a/packages/compiler-vapor/src/ir.ts +++ b/packages/compiler-vapor/src/ir.ts @@ -83,12 +83,11 @@ export interface ForIRNode extends BaseIRNode { export interface IRProp extends Omit { values: SimpleExpressionNode[] } -export type IRProps = - | IRProp[] - | { - value: SimpleExpressionNode - handler?: boolean - } +export interface IRDynamicProps { + value: SimpleExpressionNode + handler?: boolean +} +export type IRProps = IRProp[] | IRProp | IRDynamicProps export interface SetPropIRNode extends BaseIRNode { type: IRNodeTypes.SET_PROP diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index e0ce97b35..a741b6b14 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -241,8 +241,15 @@ function buildProps( const result = transformProp(prop, node, context) if (result) { - results.push(result) dynamicExpr.push(result.key, result.value) + if (isComponent && !result.key.isStatic) { + // v-bind:[name]="value" or v-on:[name]="value" + pushMergeArg() + dynamicArgs.push(normalizeIRProp(result)) + } else { + // other static props + results.push(result) + } } }