diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vModel.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vModel.spec.ts.snap index 2a4f8d0d..cdbe3c3e 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vModel.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vModel.spec.ts.snap @@ -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`] = ` "import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\" diff --git a/packages/compiler-core/__tests__/transforms/vModel.spec.ts b/packages/compiler-core/__tests__/transforms/vModel.spec.ts index ce51602e..42f9c605 100644 --- a/packages/compiler-core/__tests__/transforms/vModel.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vModel.spec.ts @@ -115,6 +115,43 @@ describe('compiler: transform v-model', () => { expect(generate(root, { mode: 'module' }).code).toMatchSnapshot() }) + test('simple expression (with multilines)', () => { + const root = parseWithVModel('') + 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', () => { const root = parseWithVModel('') const node = root.children[0] as ElementNode diff --git a/packages/compiler-core/__tests__/transforms/vOn.spec.ts b/packages/compiler-core/__tests__/transforms/vOn.spec.ts index b015eabb..158daa29 100644 --- a/packages/compiler-core/__tests__/transforms/vOn.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vOn.spec.ts @@ -167,6 +167,24 @@ describe('compiler: transform v-on', () => { }) }) + test('should handle multiple line statement', () => { + const { node } = parseWithVOn(`
`) + 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', () => { const { node } = parseWithVOn(`
`, { prefixIdentifiers: true diff --git a/packages/compiler-core/src/transforms/vModel.ts b/packages/compiler-core/src/transforms/vModel.ts index 26d4ec85..f4ec6ea3 100644 --- a/packages/compiler-core/src/transforms/vModel.ts +++ b/packages/compiler-core/src/transforms/vModel.ts @@ -21,6 +21,7 @@ export const transformModel: DirectiveTransform = (dir, node, context) => { const expString = exp.type === NodeTypes.SIMPLE_EXPRESSION ? exp.content : exp.loc.source + if (!isMemberExpression(expString)) { context.onError( createCompilerError(ErrorCodes.X_V_MODEL_MALFORMED_EXPRESSION, exp.loc) diff --git a/packages/compiler-core/src/utils.ts b/packages/compiler-core/src/utils.ts index e2c5dd1e..9b974fca 100644 --- a/packages/compiler-core/src/utils.ts +++ b/packages/compiler-core/src/utils.ts @@ -84,8 +84,10 @@ export const isSimpleIdentifier = (name: string): boolean => !nonIdentifierRE.test(name) const memberExpRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\[[^\]]+\])*$/ -export const isMemberExpression = (path: string): boolean => - memberExpRE.test(path) +export const isMemberExpression = (path: string): boolean => { + if (!path) return false + return memberExpRE.test(path.trim()) +} export function getInnerRange( loc: SourceLocation,