]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: attr coersion compat
authorEvan You <yyx990803@gmail.com>
Wed, 7 Apr 2021 21:36:56 +0000 (17:36 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 7 Apr 2021 21:42:03 +0000 (17:42 -0400)
packages/runtime-core/src/compat/deprecations.ts
packages/runtime-dom/src/modules/attrs.ts

index ff43c44a4ffe7ee504dc23180040e2cf7fba5cea..7173cbdc6e1b3a022293d7db5880d37d5ca42497 100644 (file)
@@ -29,10 +29,14 @@ export const enum DeprecationTypes {
   OPTIONS_BEFORE_DESTROY = 'OPTIONS_BEFORE_DESTROY',
   OPTIONS_DESTROYED = 'OPTIONS_DESTROYED',
 
-  V_ON_KEYCODE_MODIFIER = 'V_ON_KEYCODE_MODIFIER',
+  WATCH_ARRAY = 'WATCH_ARRAY',
   PROPS_DEFAULT_THIS = 'PROPS_DEFAULT_THIS',
+
+  V_ON_KEYCODE_MODIFIER = 'V_ON_KEYCODE_MODIFIER',
   CUSTOM_DIR = 'CUSTOM_DIR',
-  WATCH_ARRAY = 'WATCH_ARRAY',
+
+  ATTR_FALSE_VALUE = 'ATTR_FALSE_VALUE',
+  ATTR_ENUMERATED_COERSION = 'ATTR_ENUMERATED_COERSION',
 
   TRANSITION_CLASSES = 'TRANSITION_CLASSES',
   TRANSITION_GROUP_ROOT = 'TRANSITION_GROUP_ROOT'
@@ -191,11 +195,16 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
     message: `\`destroyed\` has been renamed to \`unmounted\`.`
   },
 
-  [DeprecationTypes.V_ON_KEYCODE_MODIFIER]: {
+  [DeprecationTypes.WATCH_ARRAY]: {
     message:
-      `Using keyCode as v-on modifier is no longer supported. ` +
-      `Use kebab-case key name modifiers instead.`,
-    link: `https://v3.vuejs.org/guide/migration/keycode-modifiers.html`
+      `"watch" option or vm.$watch on an array value will no longer ` +
+      `trigger on array mutation unless the "deep" option is specified. ` +
+      `If current usage is intended, you can disable the compat behavior and ` +
+      `suppress this warning with:` +
+      `\n\n  configureCompat({ ${
+        DeprecationTypes.WATCH_ARRAY
+      }: { enabled: false }})\n`,
+    link: `https://v3.vuejs.org/guide/migration/watch.html`
   },
 
   [DeprecationTypes.PROPS_DEFAULT_THIS]: {
@@ -212,16 +221,38 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
     link: `https://v3.vuejs.org/guide/migration/custom-directives.html`
   },
 
-  [DeprecationTypes.WATCH_ARRAY]: {
+  [DeprecationTypes.V_ON_KEYCODE_MODIFIER]: {
     message:
-      `"watch" option or vm.$watch on an array value will no longer ` +
-      `trigger on array mutation unless the "deep" option is specified. ` +
-      `If current usage is intended, you can disable the compat behavior and ` +
-      `suppress this warning with:` +
+      `Using keyCode as v-on modifier is no longer supported. ` +
+      `Use kebab-case key name modifiers instead.`,
+    link: `https://v3.vuejs.org/guide/migration/keycode-modifiers.html`
+  },
+
+  [DeprecationTypes.ATTR_FALSE_VALUE]: {
+    message: (name: string) =>
+      `Attribute "${name}" with v-bind value \`false\` will render ` +
+      `${name}="false" instead of removing it in Vue 3. To remove the attribute, ` +
+      `use \`null\` or \`undefined\` instead. If the usage is intended, ` +
+      `you can disable the compat behavior and suppress this warning with:` +
       `\n\n  configureCompat({ ${
-        DeprecationTypes.WATCH_ARRAY
+        DeprecationTypes.ATTR_FALSE_VALUE
       }: { enabled: false }})\n`,
-    link: `https://v3.vuejs.org/guide/migration/watch.html`
+    link: `https://v3.vuejs.org/guide/migration/attribute-coercion.html`
+  },
+
+  [DeprecationTypes.ATTR_ENUMERATED_COERSION]: {
+    message: (name: string, value: any, coerced: string) =>
+      `Enumerated attribute "${name}" with v-bind value \`${value}\` will ` +
+      `${
+        value === null ? `be removed` : `render the value as-is`
+      } instead of coercing the value to "${coerced}" in Vue 3. ` +
+      `Always use explicit "true" or "false" values for enumerated attributes. ` +
+      `If the usage is intended, ` +
+      `you can disable the compat behavior and suppress this warning with:` +
+      `\n\n  configureCompat({ ${
+        DeprecationTypes.ATTR_ENUMERATED_COERSION
+      }: { enabled: false }})\n`,
+    link: `https://v3.vuejs.org/guide/migration/attribute-coercion.html`
   },
 
   [DeprecationTypes.TRANSITION_CLASSES]: {
index 3a33d713aa7428abdae210fb9fa49b31484a96a8..975f6cbebe75b1b548cfc006a2f15d3c4a3a645f 100644 (file)
@@ -15,6 +15,10 @@ export function patchAttr(
       el.setAttributeNS(xlinkNS, key, value)
     }
   } else {
+    if (__COMPAT__ && compatCoerceAttr(el, key, value)) {
+      return
+    }
+
     // note we are only checking boolean attributes that don't have a
     // corresponding dom prop of the same name here.
     const isBoolean = isSpecialBooleanAttr(key)
@@ -25,3 +29,46 @@ export function patchAttr(
     }
   }
 }
+
+// 2.x compat
+import { makeMap, NOOP } from '@vue/shared'
+import { compatUtils, DeprecationTypes } from '@vue/runtime-core'
+
+const isEnumeratedAttr = __COMPAT__
+  ? /*#__PURE__*/ makeMap('contenteditable,draggable,spellcheck')
+  : NOOP
+
+export function compatCoerceAttr(
+  el: Element,
+  key: string,
+  value: unknown
+): boolean {
+  if (isEnumeratedAttr(key)) {
+    const v2CocercedValue =
+      value === null
+        ? 'false'
+        : typeof value !== 'boolean' && value !== undefined
+          ? 'true'
+          : null
+    if (
+      v2CocercedValue &&
+      compatUtils.softAssertCompatEnabled(
+        DeprecationTypes.ATTR_ENUMERATED_COERSION,
+        key,
+        value,
+        v2CocercedValue
+      )
+    ) {
+      el.setAttribute(key, v2CocercedValue)
+      return true
+    }
+  } else if (
+    value === false &&
+    !isSpecialBooleanAttr(key) &&
+    compatUtils.softAssertCompatEnabled(DeprecationTypes.ATTR_FALSE_VALUE, key)
+  ) {
+    el.removeAttribute(key)
+    return true
+  }
+  return false
+}