mirror of https://github.com/vuejs/core.git
refactor(compile): add independent transform for VBindShorthand
This commit is contained in:
parent
a47832e75e
commit
fd7d6cf67e
|
@ -17,6 +17,7 @@ import {
|
||||||
helperNameMap,
|
helperNameMap,
|
||||||
} from '../../src/runtimeHelpers'
|
} from '../../src/runtimeHelpers'
|
||||||
import { transformExpression } from '../../src/transforms/transformExpression'
|
import { transformExpression } from '../../src/transforms/transformExpression'
|
||||||
|
import { transformVBindShorthand } from '../../src/transforms/transformVBindShorthand'
|
||||||
|
|
||||||
function parseWithVBind(
|
function parseWithVBind(
|
||||||
template: string,
|
template: string,
|
||||||
|
@ -25,6 +26,7 @@ function parseWithVBind(
|
||||||
const ast = parse(template)
|
const ast = parse(template)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
nodeTransforms: [
|
nodeTransforms: [
|
||||||
|
transformVBindShorthand,
|
||||||
...(options.prefixIdentifiers ? [transformExpression] : []),
|
...(options.prefixIdentifiers ? [transformExpression] : []),
|
||||||
transformElement,
|
transformElement,
|
||||||
],
|
],
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { type CompilerOptions, generate } from '../../src'
|
||||||
import { FRAGMENT, RENDER_LIST, RENDER_SLOT } from '../../src/runtimeHelpers'
|
import { FRAGMENT, RENDER_LIST, RENDER_SLOT } from '../../src/runtimeHelpers'
|
||||||
import { PatchFlags } from '@vue/shared'
|
import { PatchFlags } from '@vue/shared'
|
||||||
import { createObjectMatcher } from '../testUtils'
|
import { createObjectMatcher } from '../testUtils'
|
||||||
|
import { transformVBindShorthand } from '../../src/transforms/transformVBindShorthand'
|
||||||
|
|
||||||
export function parseWithForTransform(
|
export function parseWithForTransform(
|
||||||
template: string,
|
template: string,
|
||||||
|
@ -32,6 +33,7 @@ export function parseWithForTransform(
|
||||||
const ast = parse(template, options)
|
const ast = parse(template, options)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
nodeTransforms: [
|
nodeTransforms: [
|
||||||
|
transformVBindShorthand,
|
||||||
transformIf,
|
transformIf,
|
||||||
transformFor,
|
transformFor,
|
||||||
...(options.prefixIdentifiers ? [transformExpression] : []),
|
...(options.prefixIdentifiers ? [transformExpression] : []),
|
||||||
|
|
|
@ -17,7 +17,12 @@ import {
|
||||||
type VNodeCall,
|
type VNodeCall,
|
||||||
} from '../../src/ast'
|
} from '../../src/ast'
|
||||||
import { ErrorCodes } from '../../src/errors'
|
import { ErrorCodes } from '../../src/errors'
|
||||||
import { type CompilerOptions, TO_HANDLERS, generate } from '../../src'
|
import {
|
||||||
|
type CompilerOptions,
|
||||||
|
TO_HANDLERS,
|
||||||
|
generate,
|
||||||
|
transformVBindShorthand,
|
||||||
|
} from '../../src'
|
||||||
import {
|
import {
|
||||||
CREATE_COMMENT,
|
CREATE_COMMENT,
|
||||||
FRAGMENT,
|
FRAGMENT,
|
||||||
|
@ -35,7 +40,12 @@ function parseWithIfTransform(
|
||||||
) {
|
) {
|
||||||
const ast = parse(template, options)
|
const ast = parse(template, options)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
nodeTransforms: [transformIf, transformSlotOutlet, transformElement],
|
nodeTransforms: [
|
||||||
|
transformVBindShorthand,
|
||||||
|
transformIf,
|
||||||
|
transformSlotOutlet,
|
||||||
|
transformElement,
|
||||||
|
],
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
if (!options.onError) {
|
if (!options.onError) {
|
||||||
|
@ -209,6 +219,16 @@ describe('compiler: v-if', () => {
|
||||||
content: `_ctx.ok`,
|
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', () => {
|
describe('errors', () => {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { transformModel } from './transforms/vModel'
|
||||||
import { transformFilter } from './compat/transformFilter'
|
import { transformFilter } from './compat/transformFilter'
|
||||||
import { ErrorCodes, createCompilerError, defaultOnError } from './errors'
|
import { ErrorCodes, createCompilerError, defaultOnError } from './errors'
|
||||||
import { transformMemo } from './transforms/vMemo'
|
import { transformMemo } from './transforms/vMemo'
|
||||||
|
import { transformVBindShorthand } from './transforms/transformVBindShorthand'
|
||||||
|
|
||||||
export type TransformPreset = [
|
export type TransformPreset = [
|
||||||
NodeTransform[],
|
NodeTransform[],
|
||||||
|
@ -33,6 +34,7 @@ export function getBaseTransformPreset(
|
||||||
): TransformPreset {
|
): TransformPreset {
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
|
transformVBindShorthand,
|
||||||
transformOnce,
|
transformOnce,
|
||||||
transformIf,
|
transformIf,
|
||||||
transformMemo,
|
transformMemo,
|
||||||
|
|
|
@ -66,6 +66,7 @@ export {
|
||||||
buildDirectiveArgs,
|
buildDirectiveArgs,
|
||||||
type PropsExpression,
|
type PropsExpression,
|
||||||
} from './transforms/transformElement'
|
} from './transforms/transformElement'
|
||||||
|
export { transformVBindShorthand } from './transforms/transformVBindShorthand'
|
||||||
export { processSlotOutlet } from './transforms/transformSlotOutlet'
|
export { processSlotOutlet } from './transforms/transformSlotOutlet'
|
||||||
export { getConstantType } from './transforms/cacheStatic'
|
export { getConstantType } from './transforms/cacheStatic'
|
||||||
export { generateCodeFrame } from '@vue/shared'
|
export { generateCodeFrame } from '@vue/shared'
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,13 @@
|
||||||
import type { DirectiveTransform, TransformContext } from '../transform'
|
import type { DirectiveTransform } from '../transform'
|
||||||
import {
|
import {
|
||||||
type DirectiveNode,
|
|
||||||
type ExpressionNode,
|
type ExpressionNode,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
type SimpleExpressionNode,
|
|
||||||
createObjectProperty,
|
createObjectProperty,
|
||||||
createSimpleExpression,
|
createSimpleExpression,
|
||||||
} from '../ast'
|
} from '../ast'
|
||||||
import { ErrorCodes, createCompilerError } from '../errors'
|
import { ErrorCodes, createCompilerError } from '../errors'
|
||||||
import { camelize } from '@vue/shared'
|
import { camelize } from '@vue/shared'
|
||||||
import { CAMELIZE } from '../runtimeHelpers'
|
import { CAMELIZE } from '../runtimeHelpers'
|
||||||
import { processExpression } from './transformExpression'
|
|
||||||
|
|
||||||
// v-bind without arg is handled directly in ./transformElement.ts due to its affecting
|
// 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
|
// 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) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
if (arg.type !== NodeTypes.SIMPLE_EXPRESSION) {
|
||||||
arg.children.unshift(`(`)
|
arg.children.unshift(`(`)
|
||||||
arg.children.push(`) || ""`)
|
arg.children.push(`) || ""`)
|
||||||
|
@ -92,20 +68,7 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: [createObjectProperty(arg, exp)],
|
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,6 @@ import {
|
||||||
import { processExpression } from './transformExpression'
|
import { processExpression } from './transformExpression'
|
||||||
import { validateBrowserExpression } from '../validateExpression'
|
import { validateBrowserExpression } from '../validateExpression'
|
||||||
import { PatchFlags } from '@vue/shared'
|
import { PatchFlags } from '@vue/shared'
|
||||||
import { transformBindShorthand } from './vBind'
|
|
||||||
|
|
||||||
export const transformFor: NodeTransform = createStructuralDirectiveTransform(
|
export const transformFor: NodeTransform = createStructuralDirectiveTransform(
|
||||||
'for',
|
'for',
|
||||||
|
@ -64,10 +63,6 @@ export const transformFor: NodeTransform = createStructuralDirectiveTransform(
|
||||||
const memo = findDir(node, 'memo')
|
const memo = findDir(node, 'memo')
|
||||||
const keyProp = findProp(node, `key`, false, true)
|
const keyProp = findProp(node, `key`, false, true)
|
||||||
const isDirKey = keyProp && keyProp.type === NodeTypes.DIRECTIVE
|
const isDirKey = keyProp && keyProp.type === NodeTypes.DIRECTIVE
|
||||||
if (isDirKey && !keyProp.exp) {
|
|
||||||
// resolve :key shorthand #10882
|
|
||||||
transformBindShorthand(keyProp, context)
|
|
||||||
}
|
|
||||||
let keyExp =
|
let keyExp =
|
||||||
keyProp &&
|
keyProp &&
|
||||||
(keyProp.type === NodeTypes.ATTRIBUTE
|
(keyProp.type === NodeTypes.ATTRIBUTE
|
||||||
|
|
|
@ -48,6 +48,22 @@ return function render(_ctx, _cache) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
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`] = `
|
exports[`compiler: transform v-model > modifiers > .lazy 1`] = `
|
||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
generate,
|
generate,
|
||||||
baseParse as parse,
|
baseParse as parse,
|
||||||
transform,
|
transform,
|
||||||
|
transformVBindShorthand,
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-core'
|
||||||
import { transformModel } from '../../src/transforms/vModel'
|
import { transformModel } from '../../src/transforms/vModel'
|
||||||
import { transformElement } from '../../../compiler-core/src/transforms/transformElement'
|
import { transformElement } from '../../../compiler-core/src/transforms/transformElement'
|
||||||
|
@ -18,7 +19,7 @@ import {
|
||||||
function transformWithModel(template: string, options: CompilerOptions = {}) {
|
function transformWithModel(template: string, options: CompilerOptions = {}) {
|
||||||
const ast = parse(template)
|
const ast = parse(template)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
nodeTransforms: [transformElement],
|
nodeTransforms: [transformVBindShorthand, transformElement],
|
||||||
directiveTransforms: {
|
directiveTransforms: {
|
||||||
model: transformModel,
|
model: transformModel,
|
||||||
},
|
},
|
||||||
|
@ -63,6 +64,14 @@ describe('compiler: transform v-model', () => {
|
||||||
expect(generate(root).code).toMatchSnapshot()
|
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', () => {
|
test('input w/ dynamic v-bind', () => {
|
||||||
const root = transformWithModel('<input v-bind="obj" v-model="model" />')
|
const root = transformWithModel('<input v-bind="obj" v-model="model" />')
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,28 @@ describe('transition-group', () => {
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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', () => {
|
test('with multi fragments children', () => {
|
||||||
expect(
|
expect(
|
||||||
compile(
|
compile(
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
transformExpression,
|
transformExpression,
|
||||||
transformOn,
|
transformOn,
|
||||||
transformStyle,
|
transformStyle,
|
||||||
|
transformVBindShorthand,
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import { ssrCodegenTransform } from './ssrCodegenTransform'
|
import { ssrCodegenTransform } from './ssrCodegenTransform'
|
||||||
import { ssrTransformElement } from './transforms/ssrTransformElement'
|
import { ssrTransformElement } from './transforms/ssrTransformElement'
|
||||||
|
@ -55,6 +56,7 @@ export function compile(
|
||||||
...options,
|
...options,
|
||||||
hoistStatic: false,
|
hoistStatic: false,
|
||||||
nodeTransforms: [
|
nodeTransforms: [
|
||||||
|
transformVBindShorthand,
|
||||||
ssrTransformIf,
|
ssrTransformIf,
|
||||||
ssrTransformFor,
|
ssrTransformFor,
|
||||||
trackVForSlotScopes,
|
trackVForSlotScopes,
|
||||||
|
|
Loading…
Reference in New Issue