helperNameMap,
} from '../../src/runtimeHelpers'
import { transformExpression } from '../../src/transforms/transformExpression'
+import { transformVBindShorthand } from '../../src/transforms/transformVBindShorthand'
function parseWithVBind(
template: string,
const ast = parse(template)
transform(ast, {
nodeTransforms: [
+ transformVBindShorthand,
...(options.prefixIdentifiers ? [transformExpression] : []),
transformElement,
],
import { FRAGMENT, RENDER_LIST, RENDER_SLOT } from '../../src/runtimeHelpers'
import { PatchFlags } from '@vue/shared'
import { createObjectMatcher } from '../testUtils'
+import { transformVBindShorthand } from '../../src/transforms/transformVBindShorthand'
export function parseWithForTransform(
template: string,
const ast = parse(template, options)
transform(ast, {
nodeTransforms: [
+ transformVBindShorthand,
transformIf,
transformFor,
...(options.prefixIdentifiers ? [transformExpression] : []),
type VNodeCall,
} from '../../src/ast'
import { ErrorCodes } from '../../src/errors'
-import { type CompilerOptions, TO_HANDLERS, generate } from '../../src'
+import {
+ type CompilerOptions,
+ TO_HANDLERS,
+ generate,
+ transformVBindShorthand,
+} from '../../src'
import {
CREATE_COMMENT,
FRAGMENT,
) {
const ast = parse(template, options)
transform(ast, {
- nodeTransforms: [transformIf, transformSlotOutlet, transformElement],
+ nodeTransforms: [
+ transformVBindShorthand,
+ transformIf,
+ transformSlotOutlet,
+ transformElement,
+ ],
...options,
})
if (!options.onError) {
content: `_ctx.ok`,
})
})
+
+ //#11321
+ test('v-if + :key shorthand', () => {
+ const { node } = parseWithIfTransform(`<div v-if="ok" :key></div>`)
+ expect(node.type).toBe(NodeTypes.IF)
+ expect(node.branches[0].userKey).toMatchObject({
+ arg: { content: 'key' },
+ exp: { content: 'key' },
+ })
+ })
})
describe('errors', () => {
import { transformFilter } from './compat/transformFilter'
import { ErrorCodes, createCompilerError, defaultOnError } from './errors'
import { transformMemo } from './transforms/vMemo'
+import { transformVBindShorthand } from './transforms/transformVBindShorthand'
export type TransformPreset = [
NodeTransform[],
): TransformPreset {
return [
[
+ transformVBindShorthand,
transformOnce,
transformIf,
transformMemo,
buildDirectiveArgs,
type PropsExpression,
} from './transforms/transformElement'
+export { transformVBindShorthand } from './transforms/transformVBindShorthand'
export { processSlotOutlet } from './transforms/transformSlotOutlet'
export { getConstantType } from './transforms/cacheStatic'
export { generateCodeFrame } from '@vue/shared'
--- /dev/null
+import { camelize } from '@vue/shared'
+import {
+ NodeTypes,
+ type SimpleExpressionNode,
+ createSimpleExpression,
+} from '../ast'
+import type { NodeTransform } from '../transform'
+import { ErrorCodes, createCompilerError } from '../errors'
+
+export const transformVBindShorthand: NodeTransform = (node, context) => {
+ if (node.type === NodeTypes.ELEMENT) {
+ for (const prop of node.props) {
+ // same-name shorthand - :arg is expanded to :arg="arg"
+ if (
+ prop.type === NodeTypes.DIRECTIVE &&
+ prop.name === 'bind' &&
+ !prop.exp
+ ) {
+ const arg = prop.arg!
+ if (arg.type !== NodeTypes.SIMPLE_EXPRESSION || !arg.isStatic) {
+ // only simple expression is allowed for same-name shorthand
+ context.onError(
+ createCompilerError(
+ ErrorCodes.X_V_BIND_INVALID_SAME_NAME_ARGUMENT,
+ arg.loc,
+ ),
+ )
+ prop.exp = createSimpleExpression('', true, arg.loc)
+ } else {
+ const propName = camelize((arg as SimpleExpressionNode).content)
+ prop.exp = createSimpleExpression(propName, false, arg.loc)
+ }
+ }
+ }
+ }
+}
-import type { DirectiveTransform, TransformContext } from '../transform'
+import type { DirectiveTransform } from '../transform'
import {
- type DirectiveNode,
type ExpressionNode,
NodeTypes,
- type SimpleExpressionNode,
createObjectProperty,
createSimpleExpression,
} from '../ast'
import { ErrorCodes, createCompilerError } from '../errors'
import { camelize } from '@vue/shared'
import { CAMELIZE } from '../runtimeHelpers'
-import { processExpression } from './transformExpression'
// v-bind without arg is handled directly in ./transformElement.ts due to its affecting
// codegen for the entire props object. This transform here is only for v-bind
}
}
- // same-name shorthand - :arg is expanded to :arg="arg"
- if (!exp) {
- if (arg.type !== NodeTypes.SIMPLE_EXPRESSION || !arg.isStatic) {
- // only simple expression is allowed for same-name shorthand
- context.onError(
- createCompilerError(
- ErrorCodes.X_V_BIND_INVALID_SAME_NAME_ARGUMENT,
- arg.loc,
- ),
- )
- return {
- props: [
- createObjectProperty(arg, createSimpleExpression('', true, loc)),
- ],
- }
- }
-
- transformBindShorthand(dir, context)
- exp = dir.exp!
- }
-
if (arg.type !== NodeTypes.SIMPLE_EXPRESSION) {
arg.children.unshift(`(`)
arg.children.push(`) || ""`)
}
return {
- props: [createObjectProperty(arg, exp)],
- }
-}
-
-export const transformBindShorthand = (
- dir: DirectiveNode,
- context: TransformContext,
-): void => {
- const arg = dir.arg!
-
- const propName = camelize((arg as SimpleExpressionNode).content)
- dir.exp = createSimpleExpression(propName, false, arg.loc)
- if (!__BROWSER__) {
- dir.exp = processExpression(dir.exp, context)
+ props: [createObjectProperty(arg, exp!)],
}
}
import { processExpression } from './transformExpression'
import { validateBrowserExpression } from '../validateExpression'
import { PatchFlags } from '@vue/shared'
-import { transformBindShorthand } from './vBind'
export const transformFor: NodeTransform = createStructuralDirectiveTransform(
'for',
const memo = findDir(node, 'memo')
const keyProp = findProp(node, `key`, false, true)
const isDirKey = keyProp && keyProp.type === NodeTypes.DIRECTIVE
- if (isDirKey && !keyProp.exp) {
- // resolve :key shorthand #10882
- transformBindShorthand(keyProp, context)
- }
let keyExp =
keyProp &&
(keyProp.type === NodeTypes.ATTRIBUTE
}"
`;
+exports[`compiler: transform v-model > input with v-bind shorthand type after v-model should use dynamic model 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { vModelDynamic: _vModelDynamic, withDirectives: _withDirectives, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
+
+ return _withDirectives((_openBlock(), _createElementBlock("input", {
+ "onUpdate:modelValue": $event => ((model) = $event)
+ }, null, 8 /* PROPS */, ["onUpdate:modelValue"])), [
+ [_vModelDynamic, model]
+ ])
+ }
+}"
+`;
+
exports[`compiler: transform v-model > modifiers > .lazy 1`] = `
"const _Vue = Vue
generate,
baseParse as parse,
transform,
+ transformVBindShorthand,
} from '@vue/compiler-core'
import { transformModel } from '../../src/transforms/vModel'
import { transformElement } from '../../../compiler-core/src/transforms/transformElement'
function transformWithModel(template: string, options: CompilerOptions = {}) {
const ast = parse(template)
transform(ast, {
- nodeTransforms: [transformElement],
+ nodeTransforms: [transformVBindShorthand, transformElement],
directiveTransforms: {
model: transformModel,
},
expect(generate(root).code).toMatchSnapshot()
})
+ // #13169
+ test('input with v-bind shorthand type after v-model should use dynamic model', () => {
+ const root = transformWithModel('<input v-model="model" :type/>')
+
+ expect(root.helpers).toContain(V_MODEL_DYNAMIC)
+ expect(generate(root).code).toMatchSnapshot()
+ })
+
test('input w/ dynamic v-bind', () => {
const root = transformWithModel('<input v-bind="obj" v-model="model" />')
`)
})
+ test('with dynamic tag shorthand', () => {
+ expect(
+ compile(
+ `<transition-group :tag><div v-for="i in list"/></transition-group>`,
+ ).code,
+ ).toMatchInlineSnapshot(`
+ "const { ssrRenderAttrs: _ssrRenderAttrs, ssrRenderList: _ssrRenderList } = require("vue/server-renderer")
+
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<\${
+ _ctx.tag
+ }\${
+ _ssrRenderAttrs(_attrs)
+ }>\`)
+ _ssrRenderList(_ctx.list, (i) => {
+ _push(\`<div></div>\`)
+ })
+ _push(\`</\${_ctx.tag}>\`)
+ }"
+ `)
+ })
+
test('with multi fragments children', () => {
expect(
compile(
transformExpression,
transformOn,
transformStyle,
+ transformVBindShorthand,
} from '@vue/compiler-dom'
import { ssrCodegenTransform } from './ssrCodegenTransform'
import { ssrTransformElement } from './transforms/ssrTransformElement'
...options,
hoistStatic: false,
nodeTransforms: [
+ transformVBindShorthand,
ssrTransformIf,
ssrTransformFor,
trackVForSlotScopes,