fix(compiler-core): allow multiline expression on v-model and v-on (#1234)

This commit is contained in:
Carlos Rodrigues 2020-06-09 22:24:48 +01:00 committed by GitHub
parent c97d1bae56
commit 958b6c80cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 2 deletions

View File

@ -26,6 +26,25 @@ return function render(_ctx, _cache) {
}" }"
`; `;
exports[`compiler: transform v-model simple expression (with multilines) 1`] = `
"const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(\\"input\\", {
modelValue:
model
,
\\"onUpdate:modelValue\\": $event => (
model
= $event)
}, null, 8 /* PROPS */, [\\"modelValue\\", \\"onUpdate:modelValue\\"]))
}
}"
`;
exports[`compiler: transform v-model simple expression (with prefixIdentifiers) 1`] = ` exports[`compiler: transform v-model simple expression (with prefixIdentifiers) 1`] = `
"import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\" "import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"

View File

@ -115,6 +115,43 @@ describe('compiler: transform v-model', () => {
expect(generate(root, { mode: 'module' }).code).toMatchSnapshot() expect(generate(root, { mode: 'module' }).code).toMatchSnapshot()
}) })
test('simple expression (with multilines)', () => {
const root = parseWithVModel('<input v-model="\n model \n" />')
const node = root.children[0] as ElementNode
const props = ((node.codegenNode as VNodeCall).props as ObjectExpression)
.properties
expect(props[0]).toMatchObject({
key: {
content: 'modelValue',
isStatic: true
},
value: {
content: '\n model \n',
isStatic: false
}
})
expect(props[1]).toMatchObject({
key: {
content: 'onUpdate:modelValue',
isStatic: true
},
value: {
children: [
'$event => (',
{
content: '\n model \n',
isStatic: false
},
' = $event)'
]
}
})
expect(generate(root).code).toMatchSnapshot()
})
test('compound expression', () => { test('compound expression', () => {
const root = parseWithVModel('<input v-model="model[index]" />') const root = parseWithVModel('<input v-model="model[index]" />')
const node = root.children[0] as ElementNode const node = root.children[0] as ElementNode

View File

@ -167,6 +167,24 @@ describe('compiler: transform v-on', () => {
}) })
}) })
test('should handle multiple line statement', () => {
const { node } = parseWithVOn(`<div @click="\nfoo();\nbar()\n"/>`)
expect((node.codegenNode as VNodeCall).props).toMatchObject({
properties: [
{
key: { content: `onClick` },
value: {
type: NodeTypes.COMPOUND_EXPRESSION,
// should wrap with `{` for multiple statements
// in this case the return value is discarded and the behavior is
// consistent with 2.x
children: [`$event => {`, { content: `\nfoo();\nbar()\n` }, `}`]
}
}
]
})
})
test('inline statement w/ prefixIdentifiers: true', () => { test('inline statement w/ prefixIdentifiers: true', () => {
const { node } = parseWithVOn(`<div @click="foo($event)"/>`, { const { node } = parseWithVOn(`<div @click="foo($event)"/>`, {
prefixIdentifiers: true prefixIdentifiers: true

View File

@ -21,6 +21,7 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
const expString = const expString =
exp.type === NodeTypes.SIMPLE_EXPRESSION ? exp.content : exp.loc.source exp.type === NodeTypes.SIMPLE_EXPRESSION ? exp.content : exp.loc.source
if (!isMemberExpression(expString)) { if (!isMemberExpression(expString)) {
context.onError( context.onError(
createCompilerError(ErrorCodes.X_V_MODEL_MALFORMED_EXPRESSION, exp.loc) createCompilerError(ErrorCodes.X_V_MODEL_MALFORMED_EXPRESSION, exp.loc)

View File

@ -84,8 +84,10 @@ export const isSimpleIdentifier = (name: string): boolean =>
!nonIdentifierRE.test(name) !nonIdentifierRE.test(name)
const memberExpRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\[[^\]]+\])*$/ const memberExpRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\[[^\]]+\])*$/
export const isMemberExpression = (path: string): boolean => export const isMemberExpression = (path: string): boolean => {
memberExpRE.test(path) if (!path) return false
return memberExpRE.test(path.trim())
}
export function getInnerRange( export function getInnerRange(
loc: SourceLocation, loc: SourceLocation,