]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-core/v-model): error when v-model is used on scope variable
authorEvan You <yyx990803@gmail.com>
Wed, 16 Oct 2019 18:05:18 +0000 (14:05 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 16 Oct 2019 19:35:04 +0000 (15:35 -0400)
packages/compiler-core/__tests__/transforms/vModel.spec.ts
packages/compiler-core/src/errors.ts
packages/compiler-core/src/transforms/vModel.ts
packages/compiler-dom/src/transforms/vModel.ts

index 42f3d8c524d50ef62ec06897310de50f944733b5..1fde627ac5ce03bc9a3af86e017eb2730dbab823 100644 (file)
@@ -361,7 +361,7 @@ describe('compiler: transform v-model', () => {
 
   test('should mark update handler dynamic if it refers slot scope variables', () => {
     const root = parseWithVModel(
-      '<Comp v-slot="{ foo }"><input v-model="foo"/></Comp>',
+      '<Comp v-slot="{ foo }"><input v-model="foo.bar"/></Comp>',
       {
         prefixIdentifiers: true
       }
@@ -407,5 +407,20 @@ describe('compiler: transform v-model', () => {
         })
       )
     })
+
+    test('used on scope variable', () => {
+      const onError = jest.fn()
+      parseWithVModel('<span v-for="i in list" v-model="i" />', {
+        onError,
+        prefixIdentifiers: true
+      })
+
+      expect(onError).toHaveBeenCalledTimes(1)
+      expect(onError).toHaveBeenCalledWith(
+        expect.objectContaining({
+          code: ErrorCodes.X_V_MODEL_ON_SCOPE_VARIABLE
+        })
+      )
+    })
   })
 })
index 24b2a107d4fba8d96517dd3c8e8df7fa43a722bf..30b839058e0ae04a81814af10c92a3fe85fe0098 100644 (file)
@@ -81,6 +81,7 @@ export const enum ErrorCodes {
   X_V_SLOT_MISPLACED,
   X_V_MODEL_NO_EXPRESSION,
   X_V_MODEL_MALFORMED_EXPRESSION,
+  X_V_MODEL_ON_SCOPE_VARIABLE,
 
   // generic errors
   X_PREFIX_ID_NOT_SUPPORTED,
@@ -171,6 +172,7 @@ export const errorMessages: { [code: number]: string } = {
   [ErrorCodes.X_V_SLOT_MISPLACED]: `v-slot can only be used on components or <template> tags.`,
   [ErrorCodes.X_V_MODEL_NO_EXPRESSION]: `v-model is missing expression.`,
   [ErrorCodes.X_V_MODEL_MALFORMED_EXPRESSION]: `v-model value must be a valid JavaScript member expression.`,
+  [ErrorCodes.X_V_MODEL_ON_SCOPE_VARIABLE]: `v-model cannot be used on v-for or v-slot scope variables because they are not writable.`,
 
   // generic errors
   [ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED]: `"prefixIdentifiers" option is not supported in this build of compiler.`,
index 00dcc84cf8a50955cdd74d0206adabfbcc7c04d8..b4e35c2cd7fc554f6c11e87d54019bdda82d2ef3 100644 (file)
@@ -9,7 +9,7 @@ import {
   createInterpolation
 } from '../ast'
 import { createCompilerError, ErrorCodes } from '../errors'
-import { isMemberExpression } from '../utils'
+import { isMemberExpression, isSimpleIdentifier } from '../utils'
 import { isObject } from '@vue/shared'
 
 export const transformModel: DirectiveTransform = (dir, node, context) => {
@@ -30,6 +30,18 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
     return createTransformProps()
   }
 
+  if (
+    !__BROWSER__ &&
+    context.prefixIdentifiers &&
+    isSimpleIdentifier(expString) &&
+    context.identifiers[expString]
+  ) {
+    context.onError(
+      createCompilerError(ErrorCodes.X_V_MODEL_ON_SCOPE_VARIABLE, exp.loc)
+    )
+    return createTransformProps()
+  }
+
   const propName = arg ? arg : createSimpleExpression('modelValue', true)
   const eventName = arg
     ? arg.type === NodeTypes.SIMPLE_EXPRESSION && arg.isStatic
index 9cc60f49340309b52cb1325ba279044642f1274d..75abf3887e82ff4bd1ef9afd84323cac9707537f 100644 (file)
@@ -15,7 +15,12 @@ import {
 } from '../runtimeHelpers'
 
 export const transformModel: DirectiveTransform = (dir, node, context) => {
-  const res = baseTransform(dir, node, context)
+  const baseResult = baseTransform(dir, node, context)
+  // base transform has errors
+  if (!baseResult.props.length) {
+    return baseResult
+  }
+
   const { tag, tagType } = node
   if (tagType === ElementTypes.ELEMENT) {
     if (dir.arg) {
@@ -63,7 +68,7 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
       // by returning the helper symbol via needRuntime
       // the import will replaced a resovleDirective call.
       if (!isInvalidType) {
-        res.needRuntime = context.helper(directiveToUse)
+        baseResult.needRuntime = context.helper(directiveToUse)
       }
     } else {
       context.onError(
@@ -74,5 +79,5 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
       )
     }
   }
-  return res
+  return baseResult
 }