Skip to content

Commit fd7d6cf

Browse files
committed
refactor(compile): add independent transform for VBindShorthand
1 parent a47832e commit fd7d6cf

File tree

12 files changed

+117
-47
lines changed

12 files changed

+117
-47
lines changed

packages/compiler-core/__tests__/transforms/vBind.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
helperNameMap,
1818
} from '../../src/runtimeHelpers'
1919
import { transformExpression } from '../../src/transforms/transformExpression'
20+
import { transformVBindShorthand } from '../../src/transforms/transformVBindShorthand'
2021

2122
function parseWithVBind(
2223
template: string,
@@ -25,6 +26,7 @@ function parseWithVBind(
2526
const ast = parse(template)
2627
transform(ast, {
2728
nodeTransforms: [
29+
transformVBindShorthand,
2830
...(options.prefixIdentifiers ? [transformExpression] : []),
2931
transformElement,
3032
],

packages/compiler-core/__tests__/transforms/vFor.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { type CompilerOptions, generate } from '../../src'
2121
import { FRAGMENT, RENDER_LIST, RENDER_SLOT } from '../../src/runtimeHelpers'
2222
import { PatchFlags } from '@vue/shared'
2323
import { createObjectMatcher } from '../testUtils'
24+
import { transformVBindShorthand } from '../../src/transforms/transformVBindShorthand'
2425

2526
export function parseWithForTransform(
2627
template: string,
@@ -32,6 +33,7 @@ export function parseWithForTransform(
3233
const ast = parse(template, options)
3334
transform(ast, {
3435
nodeTransforms: [
36+
transformVBindShorthand,
3537
transformIf,
3638
transformFor,
3739
...(options.prefixIdentifiers ? [transformExpression] : []),

packages/compiler-core/__tests__/transforms/vIf.spec.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ import {
1717
type VNodeCall,
1818
} from '../../src/ast'
1919
import { ErrorCodes } from '../../src/errors'
20-
import { type CompilerOptions, TO_HANDLERS, generate } from '../../src'
20+
import {
21+
type CompilerOptions,
22+
TO_HANDLERS,
23+
generate,
24+
transformVBindShorthand,
25+
} from '../../src'
2126
import {
2227
CREATE_COMMENT,
2328
FRAGMENT,
@@ -35,7 +40,12 @@ function parseWithIfTransform(
3540
) {
3641
const ast = parse(template, options)
3742
transform(ast, {
38-
nodeTransforms: [transformIf, transformSlotOutlet, transformElement],
43+
nodeTransforms: [
44+
transformVBindShorthand,
45+
transformIf,
46+
transformSlotOutlet,
47+
transformElement,
48+
],
3949
...options,
4050
})
4151
if (!options.onError) {
@@ -209,6 +219,16 @@ describe('compiler: v-if', () => {
209219
content: `_ctx.ok`,
210220
})
211221
})
222+
223+
//#11321
224+
test('v-if + :key shorthand', () => {
225+
const { node } = parseWithIfTransform(`<div v-if="ok" :key></div>`)
226+
expect(node.type).toBe(NodeTypes.IF)
227+
expect(node.branches[0].userKey).toMatchObject({
228+
arg: { content: 'key' },
229+
exp: { content: 'key' },
230+
})
231+
})
212232
})
213233

214234
describe('errors', () => {

packages/compiler-core/src/compile.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { transformModel } from './transforms/vModel'
2222
import { transformFilter } from './compat/transformFilter'
2323
import { ErrorCodes, createCompilerError, defaultOnError } from './errors'
2424
import { transformMemo } from './transforms/vMemo'
25+
import { transformVBindShorthand } from './transforms/transformVBindShorthand'
2526

2627
export type TransformPreset = [
2728
NodeTransform[],
@@ -33,6 +34,7 @@ export function getBaseTransformPreset(
3334
): TransformPreset {
3435
return [
3536
[
37+
transformVBindShorthand,
3638
transformOnce,
3739
transformIf,
3840
transformMemo,

packages/compiler-core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export {
6666
buildDirectiveArgs,
6767
type PropsExpression,
6868
} from './transforms/transformElement'
69+
export { transformVBindShorthand } from './transforms/transformVBindShorthand'
6970
export { processSlotOutlet } from './transforms/transformSlotOutlet'
7071
export { getConstantType } from './transforms/cacheStatic'
7172
export { generateCodeFrame } from '@vue/shared'
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { camelize } from '@vue/shared'
2+
import {
3+
NodeTypes,
4+
type SimpleExpressionNode,
5+
createSimpleExpression,
6+
} from '../ast'
7+
import type { NodeTransform } from '../transform'
8+
import { ErrorCodes, createCompilerError } from '../errors'
9+
10+
export const transformVBindShorthand: NodeTransform = (node, context) => {
11+
if (node.type === NodeTypes.ELEMENT) {
12+
for (const prop of node.props) {
13+
// same-name shorthand - :arg is expanded to :arg="arg"
14+
if (
15+
prop.type === NodeTypes.DIRECTIVE &&
16+
prop.name === 'bind' &&
17+
!prop.exp
18+
) {
19+
const arg = prop.arg!
20+
if (arg.type !== NodeTypes.SIMPLE_EXPRESSION || !arg.isStatic) {
21+
// only simple expression is allowed for same-name shorthand
22+
context.onError(
23+
createCompilerError(
24+
ErrorCodes.X_V_BIND_INVALID_SAME_NAME_ARGUMENT,
25+
arg.loc,
26+
),
27+
)
28+
prop.exp = createSimpleExpression('', true, arg.loc)
29+
} else {
30+
const propName = camelize((arg as SimpleExpressionNode).content)
31+
prop.exp = createSimpleExpression(propName, false, arg.loc)
32+
}
33+
}
34+
}
35+
}
36+
}

packages/compiler-core/src/transforms/vBind.ts

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
1-
import type { DirectiveTransform, TransformContext } from '../transform'
1+
import type { DirectiveTransform } from '../transform'
22
import {
3-
type DirectiveNode,
43
type ExpressionNode,
54
NodeTypes,
6-
type SimpleExpressionNode,
75
createObjectProperty,
86
createSimpleExpression,
97
} from '../ast'
108
import { ErrorCodes, createCompilerError } from '../errors'
119
import { camelize } from '@vue/shared'
1210
import { CAMELIZE } from '../runtimeHelpers'
13-
import { processExpression } from './transformExpression'
1411

1512
// v-bind without arg is handled directly in ./transformElement.ts due to its affecting
1613
// codegen for the entire props object. This transform here is only for v-bind
@@ -40,27 +37,6 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
4037
}
4138
}
4239

43-
// same-name shorthand - :arg is expanded to :arg="arg"
44-
if (!exp) {
45-
if (arg.type !== NodeTypes.SIMPLE_EXPRESSION || !arg.isStatic) {
46-
// only simple expression is allowed for same-name shorthand
47-
context.onError(
48-
createCompilerError(
49-
ErrorCodes.X_V_BIND_INVALID_SAME_NAME_ARGUMENT,
50-
arg.loc,
51-
),
52-
)
53-
return {
54-
props: [
55-
createObjectProperty(arg, createSimpleExpression('', true, loc)),
56-
],
57-
}
58-
}
59-
60-
transformBindShorthand(dir, context)
61-
exp = dir.exp!
62-
}
63-
6440
if (arg.type !== NodeTypes.SIMPLE_EXPRESSION) {
6541
arg.children.unshift(`(`)
6642
arg.children.push(`) || ""`)
@@ -92,20 +68,7 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
9268
}
9369

9470
return {
95-
props: [createObjectProperty(arg, exp)],
96-
}
97-
}
98-
99-
export const transformBindShorthand = (
100-
dir: DirectiveNode,
101-
context: TransformContext,
102-
): void => {
103-
const arg = dir.arg!
104-
105-
const propName = camelize((arg as SimpleExpressionNode).content)
106-
dir.exp = createSimpleExpression(propName, false, arg.loc)
107-
if (!__BROWSER__) {
108-
dir.exp = processExpression(dir.exp, context)
71+
props: [createObjectProperty(arg, exp!)],
10972
}
11073
}
11174

packages/compiler-core/src/transforms/vFor.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ import {
4848
import { processExpression } from './transformExpression'
4949
import { validateBrowserExpression } from '../validateExpression'
5050
import { PatchFlags } from '@vue/shared'
51-
import { transformBindShorthand } from './vBind'
5251

5352
export const transformFor: NodeTransform = createStructuralDirectiveTransform(
5453
'for',
@@ -64,10 +63,6 @@ export const transformFor: NodeTransform = createStructuralDirectiveTransform(
6463
const memo = findDir(node, 'memo')
6564
const keyProp = findProp(node, `key`, false, true)
6665
const isDirKey = keyProp && keyProp.type === NodeTypes.DIRECTIVE
67-
if (isDirKey && !keyProp.exp) {
68-
// resolve :key shorthand #10882
69-
transformBindShorthand(keyProp, context)
70-
}
7166
let keyExp =
7267
keyProp &&
7368
(keyProp.type === NodeTypes.ATTRIBUTE

packages/compiler-dom/__tests__/transforms/__snapshots__/vModel.spec.ts.snap

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,22 @@ return function render(_ctx, _cache) {
4848
}"
4949
`;
5050

51+
exports[`compiler: transform v-model > input with v-bind shorthand type after v-model should use dynamic model 1`] = `
52+
"const _Vue = Vue
53+
54+
return function render(_ctx, _cache) {
55+
with (_ctx) {
56+
const { vModelDynamic: _vModelDynamic, withDirectives: _withDirectives, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
57+
58+
return _withDirectives((_openBlock(), _createElementBlock("input", {
59+
"onUpdate:modelValue": $event => ((model) = $event)
60+
}, null, 8 /* PROPS */, ["onUpdate:modelValue"])), [
61+
[_vModelDynamic, model]
62+
])
63+
}
64+
}"
65+
`;
66+
5167
exports[`compiler: transform v-model > modifiers > .lazy 1`] = `
5268
"const _Vue = Vue
5369

packages/compiler-dom/__tests__/transforms/vModel.spec.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
generate,
44
baseParse as parse,
55
transform,
6+
transformVBindShorthand,
67
} from '@vue/compiler-core'
78
import { transformModel } from '../../src/transforms/vModel'
89
import { transformElement } from '../../../compiler-core/src/transforms/transformElement'
@@ -18,7 +19,7 @@ import {
1819
function transformWithModel(template: string, options: CompilerOptions = {}) {
1920
const ast = parse(template)
2021
transform(ast, {
21-
nodeTransforms: [transformElement],
22+
nodeTransforms: [transformVBindShorthand, transformElement],
2223
directiveTransforms: {
2324
model: transformModel,
2425
},
@@ -63,6 +64,14 @@ describe('compiler: transform v-model', () => {
6364
expect(generate(root).code).toMatchSnapshot()
6465
})
6566

67+
// #13169
68+
test('input with v-bind shorthand type after v-model should use dynamic model', () => {
69+
const root = transformWithModel('<input v-model="model" :type/>')
70+
71+
expect(root.helpers).toContain(V_MODEL_DYNAMIC)
72+
expect(generate(root).code).toMatchSnapshot()
73+
})
74+
6675
test('input w/ dynamic v-bind', () => {
6776
const root = transformWithModel('<input v-bind="obj" v-model="model" />')
6877

packages/compiler-ssr/__tests__/ssrTransitionGroup.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,28 @@ describe('transition-group', () => {
101101
`)
102102
})
103103

104+
test('with dynamic tag shorthand', () => {
105+
expect(
106+
compile(
107+
`<transition-group :tag><div v-for="i in list"/></transition-group>`,
108+
).code,
109+
).toMatchInlineSnapshot(`
110+
"const { ssrRenderAttrs: _ssrRenderAttrs, ssrRenderList: _ssrRenderList } = require("vue/server-renderer")
111+
112+
return function ssrRender(_ctx, _push, _parent, _attrs) {
113+
_push(\`<\${
114+
_ctx.tag
115+
}\${
116+
_ssrRenderAttrs(_attrs)
117+
}>\`)
118+
_ssrRenderList(_ctx.list, (i) => {
119+
_push(\`<div></div>\`)
120+
})
121+
_push(\`</\${_ctx.tag}>\`)
122+
}"
123+
`)
124+
})
125+
104126
test('with multi fragments children', () => {
105127
expect(
106128
compile(

packages/compiler-ssr/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
transformExpression,
1414
transformOn,
1515
transformStyle,
16+
transformVBindShorthand,
1617
} from '@vue/compiler-dom'
1718
import { ssrCodegenTransform } from './ssrCodegenTransform'
1819
import { ssrTransformElement } from './transforms/ssrTransformElement'
@@ -55,6 +56,7 @@ export function compile(
5556
...options,
5657
hoistStatic: false,
5758
nodeTransforms: [
59+
transformVBindShorthand,
5860
ssrTransformIf,
5961
ssrTransformFor,
6062
trackVForSlotScopes,

0 commit comments

Comments
 (0)