fix(compiler-vapor): treat attribute as dynamic if has dynamic key prop

This commit is contained in:
三咲智子 Kevin Deng 2024-02-05 03:11:37 +08:00
parent 6d098b6871
commit 2229d3ce20
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
4 changed files with 96 additions and 12 deletions

View File

@ -169,6 +169,20 @@ export function render(_ctx) {
}" }"
`; `;
exports[`compiler v-bind > dynamic arg w/ static attribute 1`] = `
"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
_setDynamicProps(n1, { [_ctx.id]: _ctx.id, foo: "bar", checked: "" })
})
return n0
}"
`;
exports[`compiler v-bind > no expression (shorthand) 1`] = ` exports[`compiler v-bind > no expression (shorthand) 1`] = `
"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor'; "import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';

View File

@ -167,6 +167,55 @@ describe('compiler v-bind', () => {
) )
}) })
test('dynamic arg w/ static attribute', () => {
const { ir, code } = compileWithVBind(
`<div v-bind:[id]="id" foo="bar" checked />`,
)
expect(code).matchSnapshot()
expect(ir.effect[0].operations[0]).toMatchObject({
type: IRNodeTypes.SET_DYNAMIC_PROPS,
element: 1,
props: [
[
{
key: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'id',
isStatic: false,
},
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'id',
isStatic: false,
},
},
{
key: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'foo',
isStatic: true,
},
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'bar',
isStatic: true,
},
},
{
key: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'checked',
isStatic: true,
},
},
],
],
})
expect(code).contains(
'_setDynamicProps(n1, { [_ctx.id]: _ctx.id, foo: "bar", checked: "" })',
)
})
test('should error if empty expression', () => { test('should error if empty expression', () => {
const onError = vi.fn() const onError = vi.fn()
const { ir, code } = compileWithVBind(`<div v-bind:arg="" />`, { const { ir, code } = compileWithVBind(`<div v-bind:arg="" />`, {

View File

@ -10,7 +10,6 @@ import {
type ParentNode, type ParentNode,
type RootNode, type RootNode,
type SimpleExpressionNode, type SimpleExpressionNode,
type SourceLocation,
type TemplateChildNode, type TemplateChildNode,
type TemplateNode, type TemplateNode,
defaultOnError, defaultOnError,
@ -46,7 +45,6 @@ export type DirectiveTransform = (
export interface DirectiveTransformResult { export interface DirectiveTransformResult {
key: SimpleExpressionNode key: SimpleExpressionNode
value: SimpleExpressionNode value: SimpleExpressionNode
loc: SourceLocation
modifier?: '.' | '^' modifier?: '.' | '^'
runtimeCamelize?: boolean runtimeCamelize?: boolean
} }

View File

@ -6,6 +6,7 @@ import {
NodeTypes, NodeTypes,
type SimpleExpressionNode, type SimpleExpressionNode,
createCompilerError, createCompilerError,
createSimpleExpression,
} from '@vue/compiler-dom' } from '@vue/compiler-dom'
import { isBuiltInDirective, isReservedProp, isVoidTag } from '@vue/shared' import { isBuiltInDirective, isReservedProp, isVoidTag } from '@vue/shared'
import type { import type {
@ -57,16 +58,18 @@ export const transformElement: NodeTransform = (node, context) => {
function buildProps( function buildProps(
node: ElementNode, node: ElementNode,
context: TransformContext<ElementNode>, context: TransformContext<ElementNode>,
props: ElementNode['props'] = node.props, props: (VaporDirectiveNode | AttributeNode)[] = node.props as any,
isComponent: boolean, isComponent: boolean,
) { ) {
const dynamicArgs: PropsExpression[] = [] const dynamicArgs: PropsExpression[] = []
const dynamicExpr: SimpleExpressionNode[] = [] const dynamicExpr: SimpleExpressionNode[] = []
let results: DirectiveTransformResult[] = [] let results: DirectiveTransformResult[] = []
function pushExpressions(...exprs: SimpleExpressionNode[]) { function pushDynamicExpressions(
...exprs: (SimpleExpressionNode | undefined)[]
) {
for (const expr of exprs) { for (const expr of exprs) {
if (!expr.isStatic) dynamicExpr.push(expr) if (expr && !expr.isStatic) dynamicExpr.push(expr)
} }
} }
@ -77,14 +80,22 @@ function buildProps(
} }
} }
for (const prop of props as (VaporDirectiveNode | AttributeNode)[]) { // treat all props as dynamic key
const asDynamic = props.some(
prop =>
prop.type === NodeTypes.DIRECTIVE &&
prop.name === 'bind' &&
(!prop.arg || !prop.arg.isStatic),
)
for (const prop of props) {
if ( if (
prop.type === NodeTypes.DIRECTIVE && prop.type === NodeTypes.DIRECTIVE &&
prop.name === 'bind' && prop.name === 'bind' &&
!prop.arg !prop.arg
) { ) {
if (prop.exp) { if (prop.exp) {
pushExpressions(prop.exp) pushDynamicExpressions(prop.exp)
pushMergeArg() pushMergeArg()
dynamicArgs.push(prop.exp) dynamicArgs.push(prop.exp)
} else { } else {
@ -95,10 +106,10 @@ function buildProps(
continue continue
} }
const result = transformProp(prop, node, context) const result = transformProp(prop, node, context, asDynamic)
if (result) { if (result) {
results.push(result) results.push(result)
pushExpressions(result.key, result.value) asDynamic && pushDynamicExpressions(result.key, result.value)
} }
} }
@ -136,15 +147,27 @@ function transformProp(
prop: VaporDirectiveNode | AttributeNode, prop: VaporDirectiveNode | AttributeNode,
node: ElementNode, node: ElementNode,
context: TransformContext<ElementNode>, context: TransformContext<ElementNode>,
asDynamic: boolean,
): DirectiveTransformResult | void { ): DirectiveTransformResult | void {
const { name } = prop const { name } = prop
if (isReservedProp(name)) return if (isReservedProp(name)) return
if (prop.type === NodeTypes.ATTRIBUTE) { if (prop.type === NodeTypes.ATTRIBUTE) {
if (asDynamic) {
return {
key: createSimpleExpression(prop.name, true, prop.nameLoc),
value: createSimpleExpression(
prop.value ? prop.value.content : '',
true,
prop.value && prop.value.loc,
),
}
} else {
context.template += ` ${name}` context.template += ` ${name}`
if (prop.value) context.template += `="${prop.value.content}"` if (prop.value) context.template += `="${prop.value.content}"`
return return
} }
}
const directiveTransform = context.options.directiveTransforms[name] const directiveTransform = context.options.directiveTransforms[name]
if (directiveTransform) { if (directiveTransform) {