]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: tests for compiler compat
authorEvan You <yyx990803@gmail.com>
Fri, 30 Apr 2021 19:50:32 +0000 (15:50 -0400)
committerEvan You <yyx990803@gmail.com>
Fri, 30 Apr 2021 19:50:32 +0000 (15:50 -0400)
packages/compiler-core/src/parse.ts
packages/compiler-core/src/transforms/transformElement.ts
packages/compiler-dom/src/transforms/vOn.ts
packages/runtime-core/src/compat/attrsFallthrough.ts
packages/runtime-core/src/compat/instance.ts
packages/runtime-core/src/componentProps.ts
packages/vue-compat/__tests__/compiler.spec.ts [new file with mode: 0644]
packages/vue-compat/__tests__/misc.spec.ts
packages/vue-compat/__tests__/refInfor.spec.ts [new file with mode: 0644]

index 784d85fb455e22c90f76603801657bf43ea73f89..579222ec9c7c79145294ef39ebb8736fc5e88322 100644 (file)
@@ -34,6 +34,7 @@ import {
   checkCompatEnabled,
   CompilerCompatOptions,
   CompilerDeprecationTypes,
+  isCompatEnabled,
   warnDeprecation
 } from './compat/compatConfig'
 
@@ -195,6 +196,30 @@ function parseChildren(
           }
         } else if (/[a-z]/i.test(s[1])) {
           node = parseElement(context, ancestors)
+
+          // 2.x <template> with no directive compat
+          if (
+            __COMPAT__ &&
+            isCompatEnabled(
+              CompilerDeprecationTypes.COMPILER_NATIVE_TEMPLATE,
+              context
+            ) &&
+            node &&
+            node.tag === 'template' &&
+            !node.props.some(
+              p =>
+                p.type === NodeTypes.DIRECTIVE &&
+                (p.name === 'if' || p.name === 'for' || p.name === 'slot')
+            )
+          ) {
+            __DEV__ &&
+              warnDeprecation(
+                CompilerDeprecationTypes.COMPILER_NATIVE_TEMPLATE,
+                context,
+                node.loc
+              )
+            node = node.children
+          }
         } else if (s[1] === '?') {
           emitError(
             context,
@@ -421,11 +446,12 @@ function parseElement(
         inlineTemplateProp.loc
       )
     ) {
-      inlineTemplateProp.value!.content = getSelection(
-        context,
-        element.loc.end
-      ).source
-      console.log(inlineTemplateProp)
+      const loc = getSelection(context, element.loc.end)
+      inlineTemplateProp.value = {
+        type: NodeTypes.TEXT,
+        content: loc.source,
+        loc
+      }
     }
   }
 
@@ -540,7 +566,14 @@ function parseTag(
   }
 
   // 2.x deprecation checks
-  if (__COMPAT__ && __DEV__ && !__TEST__) {
+  if (
+    __COMPAT__ &&
+    __DEV__ &&
+    isCompatEnabled(
+      CompilerDeprecationTypes.COMPILER_V_IF_V_FOR_PRECEDENCE,
+      context
+    )
+  ) {
     let hasIf = false
     let hasFor = false
     for (let i = 0; i < props.length; i++) {
index bf040467d007e2e4f66e41f5823251120ecaa144..dba1a99b487d13bbdefb4773209edfecb3817876 100644 (file)
@@ -41,8 +41,7 @@ import {
   TELEPORT,
   KEEP_ALIVE,
   SUSPENSE,
-  UNREF,
-  FRAGMENT
+  UNREF
 } from '../runtimeHelpers'
 import {
   getInnerRange,
@@ -92,19 +91,6 @@ export const transformElement: NodeTransform = (node, context) => {
       ? resolveComponentType(node as ComponentNode, context)
       : `"${tag}"`
 
-    // 2.x <template> with no directives compat
-    if (
-      __COMPAT__ &&
-      tag === 'template' &&
-      checkCompatEnabled(
-        CompilerDeprecationTypes.COMPILER_NATIVE_TEMPLATE,
-        context,
-        node.loc
-      )
-    ) {
-      vnodeTag = context.helper(FRAGMENT)
-    }
-
     const isDynamicComponent =
       isObject(vnodeTag) && vnodeTag.callee === RESOLVE_DYNAMIC_COMPONENT
 
index 7ff4b7b6cedacf05fe206c72e4c30d1c16e8de0e..d2207b3fc80e7485b7d08684c8a9bea9c9581ea1 100644 (file)
@@ -9,8 +9,10 @@ import {
   ExpressionNode,
   SimpleExpressionNode,
   isStaticExp,
-  warnDeprecation,
-  CompilerDeprecationTypes
+  CompilerDeprecationTypes,
+  TransformContext,
+  SourceLocation,
+  checkCompatEnabled
 } from '@vue/compiler-core'
 import { V_ON_WITH_MODIFIERS, V_ON_WITH_KEYS } from '../runtimeHelpers'
 import { makeMap, capitalize } from '@vue/shared'
@@ -31,7 +33,12 @@ const isKeyboardEvent = /*#__PURE__*/ makeMap(
   true
 )
 
-const resolveModifiers = (key: ExpressionNode, modifiers: string[]) => {
+const resolveModifiers = (
+  key: ExpressionNode,
+  modifiers: string[],
+  context: TransformContext,
+  loc: SourceLocation
+) => {
   const keyModifiers = []
   const nonKeyModifiers = []
   const eventOptionModifiers = []
@@ -39,7 +46,17 @@ const resolveModifiers = (key: ExpressionNode, modifiers: string[]) => {
   for (let i = 0; i < modifiers.length; i++) {
     const modifier = modifiers[i]
 
-    if (isEventOptionModifier(modifier)) {
+    if (
+      __COMPAT__ &&
+      modifier === 'native' &&
+      checkCompatEnabled(
+        CompilerDeprecationTypes.COMPILER_V_ON_NATIVE,
+        context,
+        loc
+      )
+    ) {
+      eventOptionModifiers.push(modifier)
+    } else if (isEventOptionModifier(modifier)) {
       // eventOptionModifiers: modifiers for addEventListener() options,
       // e.g. .passive & .capture
       eventOptionModifiers.push(modifier)
@@ -94,20 +111,12 @@ export const transformOn: DirectiveTransform = (dir, node, context) => {
     const { modifiers } = dir
     if (!modifiers.length) return baseResult
 
-    if (__COMPAT__ && __DEV__ && modifiers.includes('native')) {
-      warnDeprecation(
-        CompilerDeprecationTypes.COMPILER_V_ON_NATIVE,
-        context,
-        dir.loc
-      )
-    }
-
     let { key, value: handlerExp } = baseResult.props[0]
     const {
       keyModifiers,
       nonKeyModifiers,
       eventOptionModifiers
-    } = resolveModifiers(key, modifiers)
+    } = resolveModifiers(key, modifiers, context, dir.loc)
 
     // normalize click.right and click.middle since they don't actually fire
     if (nonKeyModifiers.includes('right')) {
index a23890b618178f795095e1d10b4d67474566c5dd..e2fc730e8a11fcd4ba6c396874cdeeb2ef0e90fb 100644 (file)
@@ -4,9 +4,11 @@ import { DeprecationTypes, isCompatEnabled } from './compatConfig'
 
 export function shouldSkipAttr(
   key: string,
-  value: any,
   instance: ComponentInternalInstance
 ): boolean {
+  if (key === 'is') {
+    return true
+  }
   if (
     (key === 'class' || key === 'style') &&
     isCompatEnabled(DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE, instance)
index 065e357bec58635f36a48432f14cd40542c22f3d..6b138063028c663bf53c7aedfa98bd330196adae 100644 (file)
@@ -114,6 +114,7 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {
     $listeners: getCompatListeners
   } as PublicPropertiesMap)
 
+  /* istanbul ignore if */
   if (isCompatEnabled(DeprecationTypes.PRIVATE_APIS, null)) {
     extend(map, {
       $vnode: i => i.vnode,
index 5d38325d9463747442e00cd2ed4a2feebf5f8911..a72356184a0f54b34c93930d1aea5f533cef7ec1 100644 (file)
@@ -20,7 +20,8 @@ import {
   isReservedProp,
   EMPTY_ARR,
   def,
-  extend
+  extend,
+  isOn
 } from '@vue/shared'
 import { warn } from './warning'
 import {
@@ -207,7 +208,7 @@ export function updateProps(
       // the props.
       const propsToUpdate = instance.vnode.dynamicProps!
       for (let i = 0; i < propsToUpdate.length; i++) {
-        const key = propsToUpdate[i]
+        let key = propsToUpdate[i]
         // PROPS flag guarantees rawProps to be non-null
         const value = rawProps![key]
         if (options) {
@@ -229,8 +230,12 @@ export function updateProps(
             )
           }
         } else {
-          if (__COMPAT__ && shouldSkipAttr(key, attrs[key], instance)) {
-            continue
+          if (__COMPAT__) {
+            if (isOn(key) && key.endsWith('Native')) {
+              key = key.slice(0, -6) // remove Native postfix
+            } else if (shouldSkipAttr(key, instance)) {
+              continue
+            }
           }
           if (value !== attrs[key]) {
             attrs[key] = value
@@ -308,7 +313,7 @@ function setFullProps(
   const [options, needCastKeys] = instance.propsOptions
   let hasAttrsChanged = false
   if (rawProps) {
-    for (const key in rawProps) {
+    for (let key in rawProps) {
       // key, ref are reserved and never passed down
       if (isReservedProp(key)) {
         continue
@@ -337,8 +342,12 @@ function setFullProps(
         // Any non-declared (either as a prop or an emitted event) props are put
         // into a separate `attrs` object for spreading. Make sure to preserve
         // original key casing
-        if (__COMPAT__ && shouldSkipAttr(key, attrs[key], instance)) {
-          continue
+        if (__COMPAT__) {
+          if (isOn(key) && key.endsWith('Native')) {
+            key = key.slice(0, -6) // remove Native postfix
+          } else if (shouldSkipAttr(key, instance)) {
+            continue
+          }
         }
         if (value !== attrs[key]) {
           attrs[key] = value
diff --git a/packages/vue-compat/__tests__/compiler.spec.ts b/packages/vue-compat/__tests__/compiler.spec.ts
new file mode 100644 (file)
index 0000000..f06db1d
--- /dev/null
@@ -0,0 +1,132 @@
+import Vue from '@vue/compat'
+import { nextTick } from '@vue/runtime-core'
+import { CompilerDeprecationTypes } from '../../compiler-core/src'
+import { toggleDeprecationWarning } from '../../runtime-core/src/compat/compatConfig'
+import { triggerEvent } from './utils'
+
+beforeEach(() => {
+  toggleDeprecationWarning(false)
+  Vue.configureCompat({
+    MODE: 2
+  })
+})
+
+afterEach(() => {
+  toggleDeprecationWarning(false)
+  Vue.configureCompat({ MODE: 3 })
+})
+
+// COMPILER_V_FOR_REF is tested in ./refInfor.spec.ts
+// COMPILER_FILTERS is tested in ./filters.spec.ts
+
+test('COMPILER_IS_ON_ELEMENT', () => {
+  const MyButton = {
+    template: `<div><slot/></div>`
+  }
+
+  const vm = new Vue({
+    template: `<button is="my-button">text</button>`,
+    components: {
+      MyButton
+    }
+  }).$mount()
+
+  expect(vm.$el.outerHTML).toBe(`<div>text</div>`)
+  expect(CompilerDeprecationTypes.COMPILER_IS_ON_ELEMENT).toHaveBeenWarned()
+})
+
+test('COMPILER_V_BIND_SYNC', async () => {
+  const MyButton = {
+    props: ['foo'],
+    template: `<button @click="$emit('update:foo', 1)">{{ foo }}</button>`
+  }
+
+  const vm = new Vue({
+    data() {
+      return {
+        foo: 0
+      }
+    },
+    template: `<my-button :foo.sync="foo" />`,
+    components: {
+      MyButton
+    }
+  }).$mount()
+
+  expect(vm.$el.textContent).toBe(`0`)
+
+  triggerEvent(vm.$el, 'click')
+  await nextTick()
+  expect(vm.$el.textContent).toBe(`1`)
+
+  expect(CompilerDeprecationTypes.COMPILER_V_BIND_SYNC).toHaveBeenWarned()
+})
+
+test('COMPILER_V_BIND_PROP', () => {
+  const vm = new Vue({
+    template: `<div :id.prop="'foo'"/>`
+  }).$mount()
+  expect(vm.$el.id).toBe('foo')
+  expect(CompilerDeprecationTypes.COMPILER_V_BIND_PROP).toHaveBeenWarned()
+})
+
+test('COMPILER_V_BIND_OBJECT_ORDER', () => {
+  const vm = new Vue({
+    template: `<div id="foo" v-bind="{ id: 'bar', class: 'baz' }" />`
+  }).$mount()
+  expect(vm.$el.id).toBe('foo')
+  expect(vm.$el.className).toBe('baz')
+  expect(
+    CompilerDeprecationTypes.COMPILER_V_BIND_OBJECT_ORDER
+  ).toHaveBeenWarned()
+})
+
+test('COMPILER_V_ON_NATIVE', () => {
+  const spy = jest.fn()
+  const vm = new Vue({
+    template: `<child @click="spy" @click.native="spy" />`,
+    components: {
+      child: {
+        template: `<button />`
+      }
+    },
+    methods: {
+      spy
+    }
+  }).$mount()
+
+  triggerEvent(vm.$el, 'click')
+  expect(spy).toHaveBeenCalledTimes(1)
+  expect(CompilerDeprecationTypes.COMPILER_V_ON_NATIVE).toHaveBeenWarned()
+})
+
+test('COMPILER_V_IF_V_FOR_PRECEDENCE', () => {
+  new Vue({ template: `<div v-if="true" v-for="i in 1"/>` }).$mount()
+  expect(
+    CompilerDeprecationTypes.COMPILER_V_IF_V_FOR_PRECEDENCE
+  ).toHaveBeenWarned()
+})
+
+test('COMPILER_NATIVE_TEMPLATE', () => {
+  const vm = new Vue({
+    template: `<div><template><div/></template></div>`
+  }).$mount()
+  expect(vm.$el.innerHTML).toBe(`<div></div>`)
+  expect(CompilerDeprecationTypes.COMPILER_NATIVE_TEMPLATE).toHaveBeenWarned()
+})
+
+test('COMPILER_INLINE_TEMPLATE', () => {
+  const vm = new Vue({
+    template: `<foo inline-template><div>{{ n }}</div></foo>`,
+    components: {
+      foo: {
+        data() {
+          return { n: 123 }
+        }
+      }
+    }
+  }).$mount()
+
+  expect(vm.$el.outerHTML).toBe(`<div>123</div>`)
+  expect(CompilerDeprecationTypes.COMPILER_INLINE_TEMPLATE).toHaveBeenWarned()
+})
index 8dc5762c0254d1f3675039287b007562bffca418..eba512d0888d8adb7eacdf4789c12c7c2a75af6f 100644 (file)
@@ -87,43 +87,6 @@ test('PROPS_DEFAULT_THIS', () => {
   ).toHaveBeenWarned()
 })
 
-test('V_FOR_REF', async () => {
-  const vm = new Vue({
-    data() {
-      return {
-        ok: true,
-        list: [1, 2, 3]
-      }
-    },
-    template: `
-    <template v-if="ok">
-      <li v-for="i in list" ref="list">{{ i }}</li>
-    </template>
-    `
-  }).$mount() as any
-
-  const mapRefs = () => vm.$refs.list.map((el: HTMLElement) => el.textContent)
-  expect(mapRefs()).toMatchObject(['1', '2', '3'])
-
-  expect(deprecationData[DeprecationTypes.V_FOR_REF].message).toHaveBeenWarned()
-
-  vm.list.push(4)
-  await nextTick()
-  expect(mapRefs()).toMatchObject(['1', '2', '3', '4'])
-
-  vm.list.shift()
-  await nextTick()
-  expect(mapRefs()).toMatchObject(['2', '3', '4'])
-
-  vm.ok = !vm.ok
-  await nextTick()
-  expect(mapRefs()).toMatchObject([])
-
-  vm.ok = !vm.ok
-  await nextTick()
-  expect(mapRefs()).toMatchObject(['2', '3', '4'])
-})
-
 test('V_ON_KEYCODE_MODIFIER', () => {
   const spy = jest.fn()
   const vm = new Vue({
diff --git a/packages/vue-compat/__tests__/refInfor.spec.ts b/packages/vue-compat/__tests__/refInfor.spec.ts
new file mode 100644 (file)
index 0000000..af1a78a
--- /dev/null
@@ -0,0 +1,57 @@
+import Vue from '@vue/compat'
+import { nextTick } from '../../runtime-core/src/scheduler'
+import {
+  DeprecationTypes,
+  deprecationData,
+  toggleDeprecationWarning
+} from '../../runtime-core/src/compat/compatConfig'
+
+beforeEach(() => {
+  toggleDeprecationWarning(true)
+  Vue.configureCompat({
+    MODE: 2,
+    GLOBAL_MOUNT: 'suppress-warning'
+  })
+})
+
+afterEach(() => {
+  toggleDeprecationWarning(false)
+  Vue.configureCompat({ MODE: 3 })
+})
+
+test('V_FOR_REF', async () => {
+  const vm = new Vue({
+    data() {
+      return {
+        ok: true,
+        list: [1, 2, 3]
+      }
+    },
+    template: `
+    <template v-if="ok">
+      <li v-for="i in list" ref="list">{{ i }}</li>
+    </template>
+    `
+  }).$mount() as any
+
+  const mapRefs = () => vm.$refs.list.map((el: HTMLElement) => el.textContent)
+  expect(mapRefs()).toMatchObject(['1', '2', '3'])
+
+  expect(deprecationData[DeprecationTypes.V_FOR_REF].message).toHaveBeenWarned()
+
+  vm.list.push(4)
+  await nextTick()
+  expect(mapRefs()).toMatchObject(['1', '2', '3', '4'])
+
+  vm.list.shift()
+  await nextTick()
+  expect(mapRefs()).toMatchObject(['2', '3', '4'])
+
+  vm.ok = !vm.ok
+  await nextTick()
+  expect(mapRefs()).toMatchObject([])
+
+  vm.ok = !vm.ok
+  await nextTick()
+  expect(mapRefs()).toMatchObject(['2', '3', '4'])
+})