]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: warn v-bind object ordering
authorEvan You <yyx990803@gmail.com>
Sat, 17 Apr 2021 20:26:13 +0000 (16:26 -0400)
committerEvan You <yyx990803@gmail.com>
Sun, 18 Apr 2021 00:53:55 +0000 (20:53 -0400)
packages/compiler-core/src/compat/compatConfig.ts
packages/compiler-core/src/transforms/transformElement.ts

index babfa526b8b65b0684dafcfdf251685b9a49fae0..d6870b47804865c12b4366e097b871c3ea12eabc 100644 (file)
@@ -54,10 +54,10 @@ const deprecationData: Record<CompilerDeprecationTypes, DeprecationData> = {
   [CompilerDeprecationTypes.COMPILER_V_BIND_OBJECT_ORDER]: {
     message:
       `v-bind="obj" usage is now order sensitive and behaves like JavaScript ` +
-      `object spread: it will now overwrite an existing attribute that appears ` +
-      `before v-bind in the case of conflicting keys. To retain 2.x behavior, ` +
-      `move v-bind to and make it the first attribute. If all occurences ` +
-      `of this warning are working as intended, you can suppress it.`,
+      `object spread: it will now overwrite an existing non-mergeable attribute ` +
+      `that appears before v-bind in the case of conflict. ` +
+      `To retain 2.x behavior, move v-bind to make it the first attribute. ` +
+      `You can also suppress this warning if the usage is intended.`,
     link: `https://v3.vuejs.org/guide/migration/v-bind.html`
   },
 
@@ -98,17 +98,24 @@ function getCompatValue(
   }
 }
 
+export function isCompatEnabled(
+  key: CompilerDeprecationTypes,
+  context: ParserContext | TransformContext
+) {
+  const mode = getCompatValue('MODE', context)
+  const value = getCompatValue(key, context)
+  // in v3 mode, only enable if explicitly set to true
+  // otherwise enable for any non-false value
+  return mode === 3 ? value === true : value !== false
+}
+
 export function checkCompatEnabled(
   key: CompilerDeprecationTypes,
   context: ParserContext | TransformContext,
   loc: SourceLocation | null,
   ...args: any[]
 ): boolean {
-  const mode = getCompatValue('MODE', context)
-  const value = getCompatValue(key, context)
-  // in v3 mode, only enable if explicitly set to true
-  // otherwise enable for any non-false value
-  const enabled = mode === 3 ? value === true : value !== false
+  const enabled = isCompatEnabled(key, context)
   if (__DEV__ && enabled) {
     warnDeprecation(key, context, loc, ...args)
   }
index cbb456a80a4f437ff257423240e19f1c70cffec0..214acbc59600347adde9c5f653d5d1e9559c6f8a 100644 (file)
@@ -55,6 +55,11 @@ import {
 import { buildSlots } from './vSlot'
 import { getConstantType } from './hoistStatic'
 import { BindingTypes } from '../options'
+import {
+  checkCompatEnabled,
+  CompilerDeprecationTypes,
+  isCompatEnabled
+} from '../compat/compatConfig'
 
 // some directive transforms (e.g. v-model) may return a symbol for runtime
 // import, which should be used instead of a resolveDirective call.
@@ -450,8 +455,8 @@ export function buildProps(
     } else {
       // directives
       const { name, arg, exp, loc } = prop
-      const isBind = name === 'bind'
-      const isOn = name === 'on'
+      const isVBind = name === 'bind'
+      const isVOn = name === 'on'
 
       // skip v-slot - it is handled by its dedicated transform.
       if (name === 'slot') {
@@ -469,17 +474,17 @@ export function buildProps(
       // skip v-is and :is on <component>
       if (
         name === 'is' ||
-        (isBind && isComponentTag(tag) && isBindKey(arg, 'is'))
+        (isVBind && isComponentTag(tag) && isBindKey(arg, 'is'))
       ) {
         continue
       }
       // skip v-on in SSR compilation
-      if (isOn && ssr) {
+      if (isVOn && ssr) {
         continue
       }
 
       // special case for v-bind and v-on with no argument
-      if (!arg && (isBind || isOn)) {
+      if (!arg && (isVBind || isVOn)) {
         hasDynamicKeys = true
         if (exp) {
           if (properties.length) {
@@ -488,7 +493,49 @@ export function buildProps(
             )
             properties = []
           }
-          if (isBind) {
+          if (isVBind) {
+            if (__COMPAT__) {
+              if (__DEV__) {
+                const hasOverridableKeys = mergeArgs.some(arg => {
+                  if (arg.type === NodeTypes.JS_OBJECT_EXPRESSION) {
+                    return arg.properties.some(({ key }) => {
+                      if (
+                        key.type !== NodeTypes.SIMPLE_EXPRESSION ||
+                        !key.isStatic
+                      ) {
+                        return true
+                      }
+                      return (
+                        key.content !== 'class' &&
+                        key.content !== 'style' &&
+                        !isOn(key.content)
+                      )
+                    })
+                  } else {
+                    // dynamic expression
+                    return true
+                  }
+                })
+                if (hasOverridableKeys) {
+                  checkCompatEnabled(
+                    CompilerDeprecationTypes.COMPILER_V_BIND_OBJECT_ORDER,
+                    context,
+                    loc
+                  )
+                }
+              }
+
+              if (
+                isCompatEnabled(
+                  CompilerDeprecationTypes.COMPILER_V_BIND_OBJECT_ORDER,
+                  context
+                )
+              ) {
+                mergeArgs.unshift(exp)
+                continue
+              }
+            }
+
             mergeArgs.push(exp)
           } else {
             // v-on="obj" -> toHandlers(obj)
@@ -502,7 +549,7 @@ export function buildProps(
         } else {
           context.onError(
             createCompilerError(
-              isBind
+              isVBind
                 ? ErrorCodes.X_V_BIND_NO_EXPRESSION
                 : ErrorCodes.X_V_ON_NO_EXPRESSION,
               loc