Repository: vuejs/vue-next
Pull request #4811 (2021-10-16 11:36:41)

chore: improve BaseTransition
- Be consistent with other judgment. (more performant and memory-efficient)
- Add test for invalid transition mode.


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b4d534028..085c8dbbd 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -26,7 +26,7 @@ jobs:
       - run: pnpm install
 
       - name: Run unit tests
-        run: pnpm run test -- --ci
+        run: pnpm run test
 
   test-dts:
     runs-on: ubuntu-latest
@@ -54,7 +54,7 @@ jobs:
     env:
       CI_JOB_NUMBER: 1
     steps:
-      - uses: actions/checkout@v1
+      - uses: actions/checkout@v2
 
       - name: Install pnpm
         uses: pnpm/action-setup@v2.0.1
diff --git a/.gitignore b/.gitignore
index 0a663edeb..1487e3b7c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ temp
 explorations
 TODOs.md
 *.log
+.idea
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5ba6d2019..f6e6e949f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,33 @@
+## [3.2.22](https://github.com/vuejs/vue-next/compare/v3.2.21...v3.2.22) (2021-11-15)
+
+
+### Bug Fixes
+
+* **compiler-sfc:** add type for props include Function in prod mode ([#4938](https://github.com/vuejs/vue-next/issues/4938)) ([9c42a1e](https://github.com/vuejs/vue-next/commit/9c42a1e2a3385f3b33faed5cdcc430bf8c1fc4b2))
+* **compiler-sfc:** add type for props's properties in prod mode ([#4790](https://github.com/vuejs/vue-next/issues/4790)) ([090df08](https://github.com/vuejs/vue-next/commit/090df0837eb0aedd8a02fd0107b7668ca5c136a1)), closes [#4783](https://github.com/vuejs/vue-next/issues/4783)
+* **compiler-sfc:** externalRE support automatic http/https prefix url pattern ([#4922](https://github.com/vuejs/vue-next/issues/4922)) ([574070f](https://github.com/vuejs/vue-next/commit/574070f43f804fd855f4ee319936ec770a56cef0)), closes [#4920](https://github.com/vuejs/vue-next/issues/4920)
+* **compiler-sfc:** fix expose codegen edge case ([#4919](https://github.com/vuejs/vue-next/issues/4919)) ([31fd590](https://github.com/vuejs/vue-next/commit/31fd590fd47e2dc89b84687ffe26a5c6f05fea34)), closes [#4917](https://github.com/vuejs/vue-next/issues/4917)
+* **devtool:** improve devtools late injection browser env detection ([#4890](https://github.com/vuejs/vue-next/issues/4890)) ([fa2237f](https://github.com/vuejs/vue-next/commit/fa2237f1d824eac511c4246135318594c48dc121))
+* **runtime-core:** improve dedupe listeners when attr fallthrough ([#4912](https://github.com/vuejs/vue-next/issues/4912)) ([b4eb7e3](https://github.com/vuejs/vue-next/commit/b4eb7e3866d7dc722d93a48f4faae1696d4e7023)), closes [#4859](https://github.com/vuejs/vue-next/issues/4859)
+* **types/sfc:** fix withDefaults type inference when using union types ([#4925](https://github.com/vuejs/vue-next/issues/4925)) ([04e5835](https://github.com/vuejs/vue-next/commit/04e58351965caf489ac68e4961ef70448d954912))
+
+
+
+## [3.2.21](https://github.com/vuejs/vue-next/compare/v3.2.20...v3.2.21) (2021-11-02)
+
+
+### Bug Fixes
+
+* **custom-element:** fix custom element props access on initial render ([4b7f76e](https://github.com/vuejs/vue-next/commit/4b7f76e36a7fc650986a20eca258f7a5d912424f)), closes [#4792](https://github.com/vuejs/vue-next/issues/4792)
+* **custom-element:** fix initial attr type casting for programmtically created elements ([3ca8317](https://github.com/vuejs/vue-next/commit/3ca83179d1a798f65e4e70215c511e2f1b64adb6)), closes [#4772](https://github.com/vuejs/vue-next/issues/4772)
+* **devtools:** avoid open handle in non-browser env ([6916d72](https://github.com/vuejs/vue-next/commit/6916d725a06a57e92ff9d046ccf132c305cd0a51)), closes [#4815](https://github.com/vuejs/vue-next/issues/4815)
+* **devtools:** fix memory leak when devtools is not installed ([#4833](https://github.com/vuejs/vue-next/issues/4833)) ([6b32f0d](https://github.com/vuejs/vue-next/commit/6b32f0d976c0aac8bb2c1b78fedd03e76fb391eb)), closes [#4829](https://github.com/vuejs/vue-next/issues/4829)
+* **runtime-core:** add `v-memo` to built-in directives check ([#4787](https://github.com/vuejs/vue-next/issues/4787)) ([5eb7263](https://github.com/vuejs/vue-next/commit/5eb72630a53a8dd82c2b8a9705c21a8075161a3d))
+* **runtime-dom:** fix behavior regression for v-show + style display binding ([3f38d59](https://github.com/vuejs/vue-next/commit/3f38d599f5aacdd3eeaa9475251a24f74e7ae3b4)), closes [#4768](https://github.com/vuejs/vue-next/issues/4768)
+* **types:** fix ref unwrapping type inference for nested shallowReactive & shallowRef ([20a3615](https://github.com/vuejs/vue-next/commit/20a361541cc5faffa82cbf3f2d49639a97b3b678)), closes [#4771](https://github.com/vuejs/vue-next/issues/4771)
+
+
+
 ## [3.2.20](https://github.com/vuejs/vue-next/compare/v3.2.19...v3.2.20) (2021-10-08)
 
 
diff --git a/package.json b/package.json
index 6ef84114c..ad57f52b8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "private": true,
-  "version": "3.2.20",
+  "version": "3.2.22",
   "scripts": {
     "dev": "node scripts/dev.js",
     "build": "node scripts/build.js",
@@ -9,7 +9,9 @@
     "size-baseline": "node scripts/build.js runtime-dom runtime-core reactivity shared -f esm-bundler && cd packages/size-check && vite build",
     "lint": "eslint --ext .ts packages/*/src/**.ts",
     "format": "prettier --write --parser typescript \"packages/**/*.ts?(x)\"",
-    "test": "node scripts/build.js vue -f global -d && jest --runInBand",
+    "test": "run-s test-unit test-e2e",
+    "test-unit": "jest --filter ./scripts/filter-unit.js",
+    "test-e2e": "node scripts/build.js vue -f global -d && jest --filter ./scripts/filter-e2e.js --runInBand",
     "test-dts": "node scripts/build.js shared reactivity runtime-core runtime-dom -dt -f esm-bundler && npm run test-dts-only",
     "test-dts-only": "tsc -p ./test-dts/tsconfig.json && tsc -p ./test-dts/tsconfig.build.json",
     "release": "node scripts/release.js",
@@ -44,10 +46,6 @@
     "node": ">=16.5.0"
   },
   "devDependencies": {
-    "vue": "workspace:*",
-    "@vue/runtime-dom": "workspace:*",
-    "@vue/runtime-core": "workspace:*",
-    "@vue/reactivity": "workspace:*",
     "@babel/types": "^7.12.0",
     "@microsoft/api-extractor": "^7.15.1",
     "@rollup/plugin-commonjs": "^18.0.0",
@@ -59,6 +57,9 @@
     "@types/node": "^16.4.7",
     "@types/puppeteer": "^5.0.0",
     "@typescript-eslint/parser": "^4.1.1",
+    "@vue/reactivity": "workspace:*",
+    "@vue/runtime-core": "workspace:*",
+    "@vue/runtime-dom": "workspace:*",
     "brotli": "^1.3.2",
     "chalk": "^4.1.0",
     "conventional-changelog-cli": "^2.0.31",
@@ -69,6 +70,8 @@
     "fs-extra": "^9.0.1",
     "jest": "^27.1.0",
     "lint-staged": "^10.2.10",
+    "lodash": "^4.17.15",
+    "marked": "^0.7.0",
     "minimist": "^1.2.0",
     "npm-run-all": "^4.1.5",
     "prettier": "^2.3.1",
@@ -81,12 +84,12 @@
     "rollup-plugin-typescript2": "^0.27.2",
     "semver": "^7.3.2",
     "serve": "^12.0.0",
+    "todomvc-app-css": "^2.3.0",
+    "tslib": "^2.3.1",
     "ts-jest": "^27.0.5",
     "typescript": "^4.2.2",
-    "yorkie": "^2.0.0",
-    "lodash": "^4.17.15",
-    "marked": "^0.7.0",
-    "todomvc-app-css": "^2.3.0",
-    "vite": "^2.6.0"
+    "vite": "^2.6.0",
+    "vue": "workspace:*",
+    "yorkie": "^2.0.0"
   }
 }
diff --git a/packages/compiler-core/package.json b/packages/compiler-core/package.json
index 7623227a9..ccf382947 100644
--- a/packages/compiler-core/package.json
+++ b/packages/compiler-core/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/compiler-core",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "description": "@vue/compiler-core",
   "main": "index.js",
   "module": "dist/compiler-core.esm-bundler.js",
@@ -32,7 +32,7 @@
   },
   "homepage": "https://github.com/vuejs/vue-next/tree/master/packages/compiler-core#readme",
   "dependencies": {
-    "@vue/shared": "3.2.20",
+    "@vue/shared": "3.2.22",
     "@babel/parser": "^7.15.0",
     "estree-walker": "^2.0.2",
     "source-map": "^0.6.1"
diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts
index d383e922d..d88bd7b9f 100644
--- a/packages/compiler-core/src/transforms/transformElement.ts
+++ b/packages/compiler-core/src/transforms/transformElement.ts
@@ -891,7 +891,7 @@ function stringifyDynamicPropNames(props: string[]): string {
 }
 
 function isComponentTag(tag: string) {
-  return tag[0].toLowerCase() + tag.slice(1) === 'component'
+  return tag === 'component' || tag === 'Component'
 }
 
 function processInlineRef(
diff --git a/packages/compiler-dom/package.json b/packages/compiler-dom/package.json
index ca22eae91..f9bd8bbc2 100644
--- a/packages/compiler-dom/package.json
+++ b/packages/compiler-dom/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/compiler-dom",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "description": "@vue/compiler-dom",
   "main": "index.js",
   "module": "dist/compiler-dom.esm-bundler.js",
@@ -37,7 +37,7 @@
   },
   "homepage": "https://github.com/vuejs/vue-next/tree/master/packages/compiler-dom#readme",
   "dependencies": {
-    "@vue/shared": "3.2.20",
-    "@vue/compiler-core": "3.2.20"
+    "@vue/shared": "3.2.22",
+    "@vue/compiler-core": "3.2.22"
   }
 }
diff --git a/packages/compiler-sfc/package.json b/packages/compiler-sfc/package.json
index d3fb96abb..48f547b11 100644
--- a/packages/compiler-sfc/package.json
+++ b/packages/compiler-sfc/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/compiler-sfc",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "description": "@vue/compiler-sfc",
   "main": "dist/compiler-sfc.cjs.js",
   "module": "dist/compiler-sfc.esm-browser.js",
@@ -33,11 +33,11 @@
   "homepage": "https://github.com/vuejs/vue-next/tree/master/packages/compiler-sfc#readme",
   "dependencies": {
     "@babel/parser": "^7.15.0",
-    "@vue/compiler-core": "3.2.20",
-    "@vue/compiler-dom": "3.2.20",
-    "@vue/compiler-ssr": "3.2.20",
-    "@vue/ref-transform": "3.2.20",
-    "@vue/shared": "3.2.20",
+    "@vue/compiler-core": "3.2.22",
+    "@vue/compiler-dom": "3.2.22",
+    "@vue/compiler-ssr": "3.2.22",
+    "@vue/ref-transform": "3.2.22",
+    "@vue/shared": "3.2.22",
     "estree-walker": "^2.0.2",
     "magic-string": "^0.25.7",
     "source-map": "^0.6.1",
diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts
index 0e60d0d9e..feaca8bd3 100644
--- a/packages/compiler-sfc/src/compileScript.ts
+++ b/packages/compiler-sfc/src/compileScript.ts
@@ -685,13 +685,22 @@ export function compileScript(
           }
         }
 
+        const { type, required } = props[key]
         if (!isProd) {
-          const { type, required } = props[key]
           return `${key}: { type: ${toRuntimeTypeString(
             type
           )}, required: ${required}${
             defaultString ? `, ${defaultString}` : ``
           } }`
+        } else if (
+          type.some(
+            el => el === 'Boolean' || (defaultString && el === 'Function')
+          )
+        ) {
+          // #4783 production: if boolean or defaultString and function exists, should keep the type.
+          return `${key}: { type: ${toRuntimeTypeString(type)}${
+            defaultString ? `, ${defaultString}` : ``
+          } }`
         } else {
           // production: checks are useless
           return `${key}: ${defaultString ? `{ ${defaultString} }` : 'null'}`
@@ -1367,7 +1376,7 @@ export function compileScript(
   // <script setup> components are closed by default. If the user did not
   // explicitly call `defineExpose`, call expose() with no args.
   const exposeCall =
-    hasDefineExposeCall || options.inlineTemplate ? `` : `  expose()\n`
+    hasDefineExposeCall || options.inlineTemplate ? `` : `  expose();\n`
   if (isTS) {
     // for TS, make sure the exported type is still valid type with
     // correct props information
@@ -1621,15 +1630,10 @@ function extractRuntimeProps(
       m.key.type === 'Identifier'
     ) {
       let type
-      if (!isProd) {
-        if (m.type === 'TSMethodSignature') {
-          type = ['Function']
-        } else if (m.typeAnnotation) {
-          type = inferRuntimeType(
-            m.typeAnnotation.typeAnnotation,
-            declaredTypes
-          )
-        }
+      if (m.type === 'TSMethodSignature') {
+        type = ['Function']
+      } else if (m.typeAnnotation) {
+        type = inferRuntimeType(m.typeAnnotation.typeAnnotation, declaredTypes)
       }
       props[m.key.name] = {
         key: m.key.name,
diff --git a/packages/compiler-sfc/src/parse.ts b/packages/compiler-sfc/src/parse.ts
index 5afdb47f6..946435f6b 100644
--- a/packages/compiler-sfc/src/parse.ts
+++ b/packages/compiler-sfc/src/parse.ts
@@ -404,9 +404,11 @@ function hasSrc(node: ElementNode) {
  * once the empty text nodes (trimmed content) have been filtered out.
  */
 function isEmpty(node: ElementNode) {
-  return (
-    node.children.filter(
-      child => child.type !== NodeTypes.TEXT || child.content.trim() !== ''
-    ).length === 0
-  )
+  for (let i = 0; i < node.children.length; i++) {
+    const child = node.children[i]
+    if (child.type !== NodeTypes.TEXT || child.content.trim() !== '') {
+      return false
+    }
+  }
+  return true
 }
diff --git a/packages/compiler-sfc/src/templateUtils.ts b/packages/compiler-sfc/src/templateUtils.ts
index b1befd927..3f4cb8f6c 100644
--- a/packages/compiler-sfc/src/templateUtils.ts
+++ b/packages/compiler-sfc/src/templateUtils.ts
@@ -6,7 +6,7 @@ export function isRelativeUrl(url: string): boolean {
   return firstChar === '.' || firstChar === '~' || firstChar === '@'
 }
 
-const externalRE = /^https?:\/\//
+const externalRE = /^(https?:)?\/\//
 export function isExternalUrl(url: string): boolean {
   return externalRE.test(url)
 }
diff --git a/packages/compiler-ssr/package.json b/packages/compiler-ssr/package.json
index 899d93f61..b4091cb74 100644
--- a/packages/compiler-ssr/package.json
+++ b/packages/compiler-ssr/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/compiler-ssr",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "description": "@vue/compiler-ssr",
   "main": "dist/compiler-ssr.cjs.js",
   "types": "dist/compiler-ssr.d.ts",
@@ -28,7 +28,7 @@
   },
   "homepage": "https://github.com/vuejs/vue-next/tree/master/packages/compiler-ssr#readme",
   "dependencies": {
-    "@vue/shared": "3.2.20",
-    "@vue/compiler-dom": "3.2.20"
+    "@vue/shared": "3.2.22",
+    "@vue/compiler-dom": "3.2.22"
   }
 }
diff --git a/packages/reactivity/package.json b/packages/reactivity/package.json
index 15d1332fd..18355fa3b 100644
--- a/packages/reactivity/package.json
+++ b/packages/reactivity/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/reactivity",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "description": "@vue/reactivity",
   "main": "index.js",
   "module": "dist/reactivity.esm-bundler.js",
@@ -36,6 +36,6 @@
   },
   "homepage": "https://github.com/vuejs/vue-next/tree/master/packages/reactivity#readme",
   "dependencies": {
-    "@vue/shared": "3.2.20"
+    "@vue/shared": "3.2.22"
   }
 }
diff --git a/packages/reactivity/src/baseHandlers.ts b/packages/reactivity/src/baseHandlers.ts
index 29367007c..0fa1c4bc2 100644
--- a/packages/reactivity/src/baseHandlers.ts
+++ b/packages/reactivity/src/baseHandlers.ts
@@ -7,7 +7,8 @@ import {
   readonlyMap,
   reactiveMap,
   shallowReactiveMap,
-  shallowReadonlyMap
+  shallowReadonlyMap,
+  isReadonly
 } from './reactive'
 import { TrackOpTypes, TriggerOpTypes } from './operations'
 import {
@@ -146,7 +147,7 @@ function createSetter(shallow = false) {
     receiver: object
   ): boolean {
     let oldValue = (target as any)[key]
-    if (!shallow) {
+    if (!shallow && !isReadonly(value)) {
       value = toRaw(value)
       oldValue = toRaw(oldValue)
       if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts
index 11af231b4..c7f7b0515 100644
--- a/packages/reactivity/src/computed.ts
+++ b/packages/reactivity/src/computed.ts
@@ -4,11 +4,11 @@ import { isFunction, NOOP } from '@vue/shared'
 import { ReactiveFlags, toRaw } from './reactive'
 import { Dep } from './dep'
 
-declare const ComoutedRefSymbol: unique symbol
+declare const ComputedRefSymbol: unique symbol
 
 export interface ComputedRef<T = any> extends WritableComputedRef<T> {
   readonly value: T
-  [ComoutedRefSymbol]: true
+  [ComputedRefSymbol]: true
 }
 
 export interface WritableComputedRef<T> extends Ref<T> {
diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts
index 231e15e60..351fc0c26 100644
--- a/packages/reactivity/src/effect.ts
+++ b/packages/reactivity/src/effect.ts
@@ -23,7 +23,7 @@ let effectTrackDepth = 0
 export let trackOpBit = 1
 
 /**
- * The bitwise track markers support at most 30 levels op recursion.
+ * The bitwise track markers support at most 30 levels of recursion.
  * This value is chosen to enable modern JS engines to use a SMI on all platforms.
  * When recursion depth is greater, fall back to using a full cleanup.
  */
diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts
index fb43668e4..d37b6726e 100644
--- a/packages/reactivity/src/ref.ts
+++ b/packages/reactivity/src/ref.ts
@@ -190,9 +190,7 @@ export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
 }
 
 export type ToRefs<T = any> = {
-  // #2687: somehow using ToRef<T[K]> here turns the resulting type into
-  // a union of multiple Ref<*> types instead of a single Ref<* | *> type.
-  [K in keyof T]: T[K] extends Ref ? T[K] : Ref<UnwrapRef<T[K]>>
+  [K in keyof T]: ToRef<T[K]>
 }
 export function toRefs<T extends object>(object: T): ToRefs<T> {
   if (__DEV__ && !isProxy(object)) {
@@ -278,7 +276,7 @@ export type UnwrapRefSimple<T> = T extends
   | RefUnwrapBailTypes[keyof RefUnwrapBailTypes]
   ? T
   : T extends Array<any>
-  ? { [K in keyof T]: UnwrapRefSimple<T[K]> }
+  ? Array<UnwrapRefSimple<T[number]>>
   : T extends object & { [ShallowReactiveMarker]?: never }
   ? {
       [P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]>
diff --git a/packages/ref-transform/README.md b/packages/ref-transform/README.md
index 7de8d6d21..33948af37 100644
--- a/packages/ref-transform/README.md
+++ b/packages/ref-transform/README.md
@@ -6,8 +6,8 @@
 
 ## Basic Rules
 
-- `$()` to turn refs into reative variables
-- `$$()` to access the original refs from reative variables
+- `$()` to turn refs into reactive variables
+- `$$()` to access the original refs from reactive variables
 
 ```js
 import { ref, watch } from 'vue'
diff --git a/packages/ref-transform/package.json b/packages/ref-transform/package.json
index 6eb22d94e..08e65f865 100644
--- a/packages/ref-transform/package.json
+++ b/packages/ref-transform/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/ref-transform",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "description": "@vue/ref-transform",
   "main": "dist/ref-transform.cjs.js",
   "files": [
@@ -29,8 +29,8 @@
   "homepage": "https://github.com/vuejs/vue-next/tree/dev/packages/ref-transform#readme",
   "dependencies": {
     "@babel/parser": "^7.15.0",
-    "@vue/compiler-core": "3.2.20",
-    "@vue/shared": "3.2.20",
+    "@vue/compiler-core": "3.2.22",
+    "@vue/shared": "3.2.22",
     "estree-walker": "^2.0.2",
     "magic-string": "^0.25.7"
   },
diff --git a/packages/runtime-core/package.json b/packages/runtime-core/package.json
index 187d53cbf..c5bb243e6 100644
--- a/packages/runtime-core/package.json
+++ b/packages/runtime-core/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/runtime-core",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "description": "@vue/runtime-core",
   "main": "index.js",
   "module": "dist/runtime-core.esm-bundler.js",
@@ -32,7 +32,7 @@
   },
   "homepage": "https://github.com/vuejs/vue-next/tree/master/packages/runtime-core#readme",
   "dependencies": {
-    "@vue/shared": "3.2.20",
-    "@vue/reactivity": "3.2.20"
+    "@vue/shared": "3.2.22",
+    "@vue/reactivity": "3.2.22"
   }
 }
diff --git a/packages/runtime-core/src/apiSetupHelpers.ts b/packages/runtime-core/src/apiSetupHelpers.ts
index 292b2f42f..02ca5d53e 100644
--- a/packages/runtime-core/src/apiSetupHelpers.ts
+++ b/packages/runtime-core/src/apiSetupHelpers.ts
@@ -127,20 +127,22 @@ export function defineExpose(exposed?: Record<string, any>) {
 type NotUndefined<T> = T extends undefined ? never : T
 
 type InferDefaults<T> = {
-  [K in keyof T]?: NotUndefined<T[K]> extends
-    | number
-    | string
-    | boolean
-    | symbol
-    | Function
-    ? NotUndefined<T[K]>
-    : (props: T) => NotUndefined<T[K]>
+  [K in keyof T]?: InferDefault<T, NotUndefined<T[K]>>
 }
 
-type PropsWithDefaults<Base, Defaults> = Base &
-  {
-    [K in keyof Defaults]: K extends keyof Base ? NotUndefined<Base[K]> : never
-  }
+type InferDefault<P, T> = T extends
+  | null
+  | number
+  | string
+  | boolean
+  | symbol
+  | Function
+  ? T
+  : (props: P) => T
+
+type PropsWithDefaults<Base, Defaults> = Base & {
+  [K in keyof Defaults]: K extends keyof Base ? NotUndefined<Base[K]> : never
+}
 
 /**
  * Vue `<script setup>` compiler macro for providing props default values when
diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts
index 924f40a73..c99949222 100644
--- a/packages/runtime-core/src/componentProps.ts
+++ b/packages/runtime-core/src/componentProps.ts
@@ -39,6 +39,7 @@ import { createPropsDefaultThis } from './compat/props'
 import { isCompatEnabled, softAssertCompatEnabled } from './compat/compatConfig'
 import { DeprecationTypes } from './compat/compatConfig'
 import { shouldSkipAttr } from './compat/attrsFallthrough'
+import { IfAny } from './helpers/typeUtils'
 
 export type ComponentPropsOptions<P = Data> =
   | ComponentObjectPropsOptions<P>
@@ -115,7 +116,7 @@ type InferPropType<T> = [T] extends [null]
     : InferPropType<U>
   : [T] extends [Prop<infer V, infer D>]
   ? unknown extends V
-    ? D
+    ? IfAny<V, V, D>
     : V
   : T
 
diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts
index dd385a815..8298aff42 100644
--- a/packages/runtime-core/src/componentPublicInstance.ts
+++ b/packages/runtime-core/src/componentPublicInstance.ts
@@ -248,11 +248,11 @@ if (__COMPAT__) {
 }
 
 const enum AccessTypes {
+  OTHER,
   SETUP,
   DATA,
   PROPS,
-  CONTEXT,
-  OTHER
+  CONTEXT
 }
 
 export interface ComponentRenderContext {
@@ -437,7 +437,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
   ) {
     let normalizedProps
     return (
-      accessCache![key] !== undefined ||
+      !!accessCache![key] ||
       (data !== EMPTY_OBJ && hasOwn(data, key)) ||
       (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) ||
       ((normalizedProps = propsOptions[0]) && hasOwn(normalizedProps, key)) ||
diff --git a/packages/runtime-core/src/components/BaseTransition.ts b/packages/runtime-core/src/components/BaseTransition.ts
index cb628bf65..27bfb5f52 100644
--- a/packages/runtime-core/src/components/BaseTransition.ts
+++ b/packages/runtime-core/src/components/BaseTransition.ts
@@ -161,7 +161,11 @@ const BaseTransitionImpl: ComponentOptions = {
       const rawProps = toRaw(props)
       const { mode } = rawProps
       // check mode
-      if (__DEV__ && mode && !['in-out', 'out-in', 'default'].includes(mode)) {
+      if (
+        __DEV__ &&
+        mode &&
+        mode !== 'in-out' && mode !== 'out-in' && mode !== 'default'
+      ) {
         warn(`invalid <transition> mode: ${mode}`)
       }
 
diff --git a/packages/runtime-core/src/components/KeepAlive.ts b/packages/runtime-core/src/components/KeepAlive.ts
index 30ad4400d..223a7c602 100644
--- a/packages/runtime-core/src/components/KeepAlive.ts
+++ b/packages/runtime-core/src/components/KeepAlive.ts
@@ -381,7 +381,7 @@ function registerKeepAliveHook(
         }
         current = current.parent
       }
-      hook()
+      return hook()
     })
   injectHook(type, wrappedHook, target)
   // In addition to registering it on the target instance, we walk up the parent
diff --git a/packages/runtime-core/src/devtools.ts b/packages/runtime-core/src/devtools.ts
index b5cbe3060..6bbd3db6e 100644
--- a/packages/runtime-core/src/devtools.ts
+++ b/packages/runtime-core/src/devtools.ts
@@ -33,10 +33,12 @@ export let devtools: DevtoolsHook
 
 let buffer: { event: string; args: any[] }[] = []
 
+let devtoolsNotInstalled = false
+
 function emit(event: string, ...args: any[]) {
   if (devtools) {
     devtools.emit(event, ...args)
-  } else {
+  } else if (!devtoolsNotInstalled) {
     buffer.push({ event, args })
   }
 }
@@ -47,7 +49,17 @@ export function setDevtoolsHook(hook: DevtoolsHook, target: any) {
     devtools.enabled = true
     buffer.forEach(({ event, args }) => devtools.emit(event, ...args))
     buffer = []
-  } else {
+  } else if (
+    // handle late devtools injection - only do this if we are in an actual
+    // browser environment to avoid the timer handle stalling test runner exit
+    // (#4815)
+    // eslint-disable-next-line no-restricted-globals
+    typeof window !== 'undefined' &&
+    // some envs mock window but not fully
+    window.HTMLElement &&
+    // also exclude jsdom
+    !window.navigator?.userAgent?.includes('jsdom')
+  ) {
     const replay = (target.__VUE_DEVTOOLS_HOOK_REPLAY__ =
       target.__VUE_DEVTOOLS_HOOK_REPLAY__ || [])
     replay.push((newHook: DevtoolsHook) => {
@@ -56,8 +68,16 @@ export function setDevtoolsHook(hook: DevtoolsHook, target: any) {
     // clear buffer after 3s - the user probably doesn't have devtools installed
     // at all, and keeping the buffer will cause memory leaks (#4738)
     setTimeout(() => {
-      buffer = []
+      if (!devtools) {
+        target.__VUE_DEVTOOLS_HOOK_REPLAY__ = null
+        devtoolsNotInstalled = true
+        buffer = []
+      }
     }, 3000)
+  } else {
+    // non-browser env, assume not installed
+    devtoolsNotInstalled = true
+    buffer = []
   }
 }
 
diff --git a/packages/runtime-core/src/directives.ts b/packages/runtime-core/src/directives.ts
index 58e56df05..ec13e951b 100644
--- a/packages/runtime-core/src/directives.ts
+++ b/packages/runtime-core/src/directives.ts
@@ -64,7 +64,7 @@ export type Directive<T = any, V = any> =
 export type DirectiveModifiers = Record<string, boolean>
 
 const isBuiltInDirective = /*#__PURE__*/ makeMap(
-  'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text'
+  'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo'
 )
 
 export function validateDirectiveName(name: string) {
diff --git a/packages/runtime-core/src/helpers/typeUtils.ts b/packages/runtime-core/src/helpers/typeUtils.ts
index 204543e6d..8caba54c6 100644
--- a/packages/runtime-core/src/helpers/typeUtils.ts
+++ b/packages/runtime-core/src/helpers/typeUtils.ts
@@ -6,3 +6,5 @@ export type UnionToIntersection<U> = (
 
 // make keys required but keep undefined values
 export type LooseRequired<T> = { [P in string & keyof T]: T[P] }
+
+export type IfAny<T, Y, N> = 0 extends (1 & T) ? Y : N
diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts
index e61e5421e..7cf8b4952 100644
--- a/packages/runtime-core/src/renderer.ts
+++ b/packages/runtime-core/src/renderer.ts
@@ -1200,7 +1200,7 @@ function baseCreateRenderer(
     isSVG,
     optimized
   ) => {
-    // 2.x compat may pre-creaate the component instance before actually
+    // 2.x compat may pre-create the component instance before actually
     // mounting
     const compatMountInstance =
       __COMPAT__ && initialVNode.isCompatRoot && initialVNode.component
@@ -2463,8 +2463,8 @@ export function invokeVNodeHook(
  *
  * #2080
  * Inside keyed `template` fragment static children, if a fragment is moved,
- * the children will always moved so that need inherit el form previous nodes
- * to ensure correct moved position.
+ * the children will always be moved. Therefore, in order to ensure correct move
+ * position, el should be inherited from previous nodes.
  */
 export function traverseStaticChildren(n1: VNode, n2: VNode, shallow = false) {
   const ch1 = n1.children
diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts
index 089147a38..306afbcc3 100644
--- a/packages/runtime-core/src/vnode.ts
+++ b/packages/runtime-core/src/vnode.ts
@@ -791,7 +791,10 @@ export function mergeProps(...args: (Data & VNodeProps)[]) {
       } else if (isOn(key)) {
         const existing = ret[key]
         const incoming = toMerge[key]
-        if (existing !== incoming) {
+        if (
+          existing !== incoming &&
+          !(isArray(existing) && existing.includes(incoming))
+        ) {
           ret[key] = existing
             ? [].concat(existing as any, incoming as any)
             : incoming
diff --git a/packages/runtime-dom/package.json b/packages/runtime-dom/package.json
index 2b0def72d..38423f3d2 100644
--- a/packages/runtime-dom/package.json
+++ b/packages/runtime-dom/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/runtime-dom",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "description": "@vue/runtime-dom",
   "main": "index.js",
   "module": "dist/runtime-dom.esm-bundler.js",
@@ -35,8 +35,8 @@
   },
   "homepage": "https://github.com/vuejs/vue-next/tree/master/packages/runtime-dom#readme",
   "dependencies": {
-    "@vue/shared": "3.2.20",
-    "@vue/runtime-core": "3.2.20",
+    "@vue/shared": "3.2.22",
+    "@vue/runtime-core": "3.2.22",
     "csstype": "^2.6.8"
   }
 }
diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts
index a532da4c6..5127e4a47 100644
--- a/packages/runtime-dom/src/apiCustomElement.ts
+++ b/packages/runtime-dom/src/apiCustomElement.ts
@@ -180,7 +180,6 @@ export class VueElement extends BaseClass {
     this._connected = true
     if (!this._instance) {
       this._resolveDef()
-      this._update()
     }
   }
 
@@ -231,17 +230,15 @@ export class VueElement extends BaseClass {
           }
         }
       }
-      if (numberProps) {
-        this._numberProps = numberProps
-        this._update()
-      }
+      this._numberProps = numberProps
 
       // check if there are props set pre-upgrade or connect
       for (const key of Object.keys(this)) {
         if (key[0] !== '_') {
-          this._setProp(key, this[key as keyof this])
+          this._setProp(key, this[key as keyof this], true, false)
         }
       }
+
       // defining getter/setters on prototype
       for (const key of rawKeys.map(camelize)) {
         Object.defineProperty(this, key, {
@@ -253,7 +250,12 @@ export class VueElement extends BaseClass {
           }
         })
       }
+
+      // apply CSS
       this._applyStyles(styles)
+
+      // initial render
+      this._update()
     }
 
     const asyncDef = (this._def as ComponentOptions).__asyncLoader
@@ -282,10 +284,15 @@ export class VueElement extends BaseClass {
   /**
    * @internal
    */
-  protected _setProp(key: string, val: any, shouldReflect = true) {
+  protected _setProp(
+    key: string,
+    val: any,
+    shouldReflect = true,
+    shouldUpdate = true
+  ) {
     if (val !== this._props[key]) {
       this._props[key] = val
-      if (this._instance) {
+      if (shouldUpdate && this._instance) {
         this._update()
       }
       // reflect
diff --git a/packages/runtime-dom/src/modules/props.ts b/packages/runtime-dom/src/modules/props.ts
index d70692396..5092911e4 100644
--- a/packages/runtime-dom/src/modules/props.ts
+++ b/packages/runtime-dom/src/modules/props.ts
@@ -26,12 +26,23 @@ export function patchDOMProp(
     return
   }
 
-  if (key === 'value' && el.tagName !== 'PROGRESS') {
+  if (
+    key === 'value' &&
+    el.tagName !== 'PROGRESS' &&
+    // custom elements may use _value internally
+    !el.tagName.includes('-')
+  ) {
     // store value as _value as well since
     // non-string values will be stringified.
     el._value = value
     const newValue = value == null ? '' : value
-    if (el.value !== newValue) {
+    if (
+      el.value !== newValue ||
+      // #4956: always set for OPTION elements because its value falls back to
+      // textContent if no value attribute is present. And setting .value for
+      // OPTION has no side effect
+      el.tagName === 'OPTION'
+    ) {
       el.value = newValue
     }
     if (value == null) {
diff --git a/packages/server-renderer/package.json b/packages/server-renderer/package.json
index fd1673afe..123574022 100644
--- a/packages/server-renderer/package.json
+++ b/packages/server-renderer/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/server-renderer",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "description": "@vue/server-renderer",
   "main": "index.js",
   "module": "dist/server-renderer.esm-bundler.js",
@@ -31,10 +31,10 @@
   },
   "homepage": "https://github.com/vuejs/vue-next/tree/master/packages/server-renderer#readme",
   "peerDependencies": {
-    "vue": "3.2.20"
+    "vue": "3.2.22"
   },
   "dependencies": {
-    "@vue/shared": "3.2.20",
-    "@vue/compiler-ssr": "3.2.20"
+    "@vue/shared": "3.2.22",
+    "@vue/compiler-ssr": "3.2.22"
   }
 }
diff --git a/packages/sfc-playground/package.json b/packages/sfc-playground/package.json
index d94d1c1d4..c40350cc3 100644
--- a/packages/sfc-playground/package.json
+++ b/packages/sfc-playground/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/sfc-playground",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "private": true,
   "scripts": {
     "dev": "vite",
@@ -11,8 +11,8 @@
     "@vitejs/plugin-vue": "^1.9.3"
   },
   "dependencies": {
-    "vue": "workspace:*",
-    "@vue/repl": "^0.4.3",
+    "vue": "3.2.22",
+    "@vue/repl": "^0.4.4",
     "file-saver": "^2.0.5",
     "jszip": "^3.6.0"
   }
diff --git a/packages/shared/package.json b/packages/shared/package.json
index 8848c394b..05df8729f 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/shared",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "description": "internal utils shared across @vue packages",
   "main": "index.js",
   "module": "dist/shared.esm-bundler.js",
diff --git a/packages/size-check/package.json b/packages/size-check/package.json
index ff66d19aa..1faa5490e 100644
--- a/packages/size-check/package.json
+++ b/packages/size-check/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/size-check",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "private": true,
   "scripts": {
     "build": "vite build"
diff --git a/packages/size-check/vite.config.js b/packages/size-check/vite.config.js
index a14a91c27..b8d714e0f 100644
--- a/packages/size-check/vite.config.js
+++ b/packages/size-check/vite.config.js
@@ -5,6 +5,7 @@ export default {
       output: {
         entryFileNames: `[name].js`
       }
-    }
+    },
+    minify: 'terser'
   }
 }
diff --git a/packages/template-explorer/package.json b/packages/template-explorer/package.json
index a89fd2fe2..42290f87e 100644
--- a/packages/template-explorer/package.json
+++ b/packages/template-explorer/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/template-explorer",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "private": true,
   "buildOptions": {
     "formats": [
diff --git a/packages/vue-compat/package.json b/packages/vue-compat/package.json
index b86c0cf15..46cedf241 100644
--- a/packages/vue-compat/package.json
+++ b/packages/vue-compat/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/compat",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "description": "Vue 3 compatibility build for Vue 2",
   "main": "index.js",
   "module": "dist/vue.runtime.esm-bundler.js",
@@ -38,6 +38,6 @@
   },
   "homepage": "https://github.com/vuejs/vue-next/tree/master/packages/vue-compat#readme",
   "peerDependencies": {
-    "vue": "3.2.20"
+    "vue": "3.2.22"
   }
 }
diff --git a/packages/vue/package.json b/packages/vue/package.json
index 060c75ac9..c8f914805 100644
--- a/packages/vue/package.json
+++ b/packages/vue/package.json
@@ -1,6 +1,6 @@
 {
   "name": "vue",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "description": "The progressive JavaScript framework for buiding modern web UI.",
   "main": "index.js",
   "module": "dist/vue.runtime.esm-bundler.js",
@@ -21,7 +21,8 @@
         "node": "./index.mjs",
         "default": "./dist/vue.runtime.esm-bundler.js"
       },
-      "require": "./index.js"
+      "require": "./index.js",
+      "types": "./dist/vue.d.ts"
     },
     "./server-renderer": {
       "import": "./server-renderer/index.mjs",
@@ -60,10 +61,10 @@
   },
   "homepage": "https://github.com/vuejs/vue-next/tree/master/packages/vue#readme",
   "dependencies": {
-    "@vue/shared": "3.2.20",
-    "@vue/compiler-dom": "3.2.20",
-    "@vue/runtime-dom": "3.2.20",
-    "@vue/compiler-sfc": "3.2.20",
-    "@vue/server-renderer": "3.2.20"
+    "@vue/shared": "3.2.22",
+    "@vue/compiler-dom": "3.2.22",
+    "@vue/runtime-dom": "3.2.22",
+    "@vue/compiler-sfc": "3.2.22",
+    "@vue/server-renderer": "3.2.22"
   }
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7d44546e4..1fd96811e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -44,6 +44,7 @@ importers:
       serve: ^12.0.0
       todomvc-app-css: ^2.3.0
       ts-jest: ^27.0.5
+      tslib: ^2.3.1
       typescript: ^4.2.2
       vite: ^2.6.0
       vue: workspace:*
@@ -89,6 +90,7 @@ importers:
       serve: 12.0.1
       todomvc-app-css: 2.4.1
       ts-jest: 27.0.5_c167855b7a039eaaa2a6b38847c1969c
+      tslib: 2.3.1
       typescript: 4.4.3
       vite: 2.6.5
       vue: link:packages/vue
@@ -98,7 +100,7 @@ importers:
     specifiers:
       '@babel/parser': ^7.15.0
       '@babel/types': ^7.15.0
-      '@vue/shared': 3.2.20
+      '@vue/shared': 3.2.22
       estree-walker: ^2.0.2
       source-map: ^0.6.1
     dependencies:
@@ -111,8 +113,8 @@ importers:
 
   packages/compiler-dom:
     specifiers:
-      '@vue/compiler-core': 3.2.20
-      '@vue/shared': 3.2.20
+      '@vue/compiler-core': 3.2.22
+      '@vue/shared': 3.2.22
     dependencies:
       '@vue/compiler-core': link:../compiler-core
       '@vue/shared': link:../shared
@@ -123,12 +125,12 @@ importers:
       '@babel/types': ^7.15.0
       '@types/estree': ^0.0.48
       '@types/lru-cache': ^5.1.0
-      '@vue/compiler-core': 3.2.20
-      '@vue/compiler-dom': 3.2.20
-      '@vue/compiler-ssr': 3.2.20
+      '@vue/compiler-core': 3.2.22
+      '@vue/compiler-dom': 3.2.22
+      '@vue/compiler-ssr': 3.2.22
       '@vue/consolidate': ^0.17.3
-      '@vue/ref-transform': 3.2.20
-      '@vue/shared': 3.2.20
+      '@vue/ref-transform': 3.2.22
+      '@vue/shared': 3.2.22
       estree-walker: ^2.0.2
       hash-sum: ^2.0.0
       lru-cache: ^5.1.1
@@ -166,15 +168,15 @@ importers:
 
   packages/compiler-ssr:
     specifiers:
-      '@vue/compiler-dom': 3.2.20
-      '@vue/shared': 3.2.20
+      '@vue/compiler-dom': 3.2.22
+      '@vue/shared': 3.2.22
     dependencies:
       '@vue/compiler-dom': link:../compiler-dom
       '@vue/shared': link:../shared
 
   packages/reactivity:
     specifiers:
-      '@vue/shared': 3.2.20
+      '@vue/shared': 3.2.22
     dependencies:
       '@vue/shared': link:../shared
 
@@ -182,8 +184,8 @@ importers:
     specifiers:
       '@babel/core': ^7.15.0
       '@babel/parser': ^7.15.0
-      '@vue/compiler-core': 3.2.20
-      '@vue/shared': 3.2.20
+      '@vue/compiler-core': 3.2.22
+      '@vue/shared': 3.2.22
       estree-walker: ^2.0.2
       magic-string: ^0.25.7
     dependencies:
@@ -197,16 +199,16 @@ importers:
 
   packages/runtime-core:
     specifiers:
-      '@vue/reactivity': 3.2.20
-      '@vue/shared': 3.2.20
+      '@vue/reactivity': 3.2.22
+      '@vue/shared': 3.2.22
     dependencies:
       '@vue/reactivity': link:../reactivity
       '@vue/shared': link:../shared
 
   packages/runtime-dom:
     specifiers:
-      '@vue/runtime-core': 3.2.20
-      '@vue/shared': 3.2.20
+      '@vue/runtime-core': 3.2.22
+      '@vue/shared': 3.2.22
       csstype: ^2.6.8
     dependencies:
       '@vue/runtime-core': link:../runtime-core
@@ -215,16 +217,16 @@ importers:
 
   packages/runtime-test:
     specifiers:
-      '@vue/runtime-core': 3.2.20
-      '@vue/shared': 3.2.20
+      '@vue/runtime-core': 3.2.22
+      '@vue/shared': 3.2.22
     dependencies:
       '@vue/runtime-core': link:../runtime-core
       '@vue/shared': link:../shared
 
   packages/server-renderer:
     specifiers:
-      '@vue/compiler-ssr': 3.2.20
-      '@vue/shared': 3.2.20
+      '@vue/compiler-ssr': 3.2.22
+      '@vue/shared': 3.2.22
     dependencies:
       '@vue/compiler-ssr': link:../compiler-ssr
       '@vue/shared': link:../shared
@@ -232,12 +234,12 @@ importers:
   packages/sfc-playground:
     specifiers:
       '@vitejs/plugin-vue': ^1.9.3
-      '@vue/repl': ^0.4.3
+      '@vue/repl': ^0.4.4
       file-saver: ^2.0.5
       jszip: ^3.6.0
-      vue: workspace:*
+      vue: 3.2.22
     dependencies:
-      '@vue/repl': 0.4.3
+      '@vue/repl': 0.4.4
       file-saver: 2.0.5
       jszip: 3.7.1
       vue: link:../vue
@@ -263,11 +265,11 @@ importers:
 
   packages/vue:
     specifiers:
-      '@vue/compiler-dom': 3.2.20
-      '@vue/compiler-sfc': 3.2.20
-      '@vue/runtime-dom': 3.2.20
-      '@vue/server-renderer': 3.2.20
-      '@vue/shared': 3.2.20
+      '@vue/compiler-dom': 3.2.22
+      '@vue/compiler-sfc': 3.2.22
+      '@vue/runtime-dom': 3.2.22
+      '@vue/server-renderer': 3.2.22
+      '@vue/shared': 3.2.22
     dependencies:
       '@vue/compiler-dom': link:../compiler-dom
       '@vue/compiler-sfc': link:../compiler-sfc
@@ -1266,8 +1268,8 @@ packages:
     engines: {node: '>= 0.12.0'}
     dev: true
 
-  /@vue/repl/0.4.3:
-    resolution: {integrity: sha512-C9/hBNFjS6ROwWIMZOQpEz04DBONvgTT7s/LL0esXd8yQaO8UEAPJw8f/dfK5141nwcBZgfzbPtfcGMThddx2w==}
+  /@vue/repl/0.4.4:
+    resolution: {integrity: sha512-Mj0Zasc6V7bymDg7DNiN1bW/MZVNS4PmKzDkG9hq9ysrkrZoTHcwA+gcAxF+25Dfn0Tz8WSZD9FuEgEKoA681g==}
     peerDependencies:
       vue: ^3.2.13
     dev: false
@@ -6532,6 +6534,10 @@ packages:
     resolution: {integrity: sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==}
     dev: true
 
+  /tslib/2.3.1:
+    resolution: {integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==}
+    dev: true
+
   /tsutils/3.21.0_typescript@4.4.3:
     resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
     engines: {node: '>= 6'}
diff --git a/scripts/filter-unit.js b/scripts/filter-unit.js
new file mode 100644
index 000000000..d75280790
--- /dev/null
+++ b/scripts/filter-unit.js
@@ -0,0 +1,9 @@
+const { e2eTests } = require('./filter-e2e')
+
+module.exports = list => {
+  return {
+    filtered: list
+      .filter(t => !e2eTests.some(tt => t.includes(tt)))
+      .map(test => ({ test }))
+  }
+}
diff --git a/scripts/release.js b/scripts/release.js
index c932d8799..835cbebe9 100644
--- a/scripts/release.js
+++ b/scripts/release.js
@@ -80,7 +80,7 @@ async function main() {
   step('\nRunning tests...')
   if (!skipTests && !isDryRun) {
     await run(bin('jest'), ['--clearCache'])
-    await run('npm', ['test', '--', '--bail'])
+    await run('pnpm', ['test', '--', '--bail'])
   } else {
     console.log(`(skipped)`)
   }
@@ -92,16 +92,21 @@ async function main() {
   // build all packages with types
   step('\nBuilding all packages...')
   if (!skipBuild && !isDryRun) {
-    await run('npm', ['run', 'build', '--', '--release'])
+    await run('pnpm', ['run', 'build', '--', '--release'])
     // test generated dts files
     step('\nVerifying type declarations...')
-    await run('npm', ['run', 'test-dts-only'])
+    await run('pnpm', ['run', 'test-dts-only'])
   } else {
     console.log(`(skipped)`)
   }
 
   // generate changelog
-  await run(`npm`, ['run', 'changelog'])
+  step('\nGenerating changelog...')
+  await run(`pnpm`, ['run', 'changelog'])
+
+  // update pnpm-lock.yaml
+  step('\nUpdating lockfile...')
+  await run(`pnpm`, ['install', '--prefer-offline'])
 
   const { stdout } = await run('git', ['diff'], { stdio: 'pipe' })
   if (stdout) {


Test patch:
diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
index e0bdd298f..b62e0f18c 100644
--- a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
+++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
@@ -7,7 +7,7 @@ exports[`SFC compile <script setup> <script> and <script setup> co-usage script
       
 export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 
       x()
       
@@ -22,7 +22,7 @@ exports[`SFC compile <script setup> <script> and <script setup> co-usage script
       
 export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 
       x()
       
@@ -101,7 +101,7 @@ exports[`SFC compile <script setup> async/await detection expression statement 1
 
 export default {
   async setup(__props, { expose }) {
-  expose()
+  expose();
 
 let __temp, __restore
 ;(
@@ -120,7 +120,7 @@ exports[`SFC compile <script setup> async/await detection nested await 1`] = `
 
 export default {
   async setup(__props, { expose }) {
-  expose()
+  expose();
 
 let __temp, __restore
 ;(
@@ -144,7 +144,7 @@ exports[`SFC compile <script setup> async/await detection nested await 2`] = `
 
 export default {
   async setup(__props, { expose }) {
-  expose()
+  expose();
 
 let __temp, __restore
 ;(
@@ -168,7 +168,7 @@ exports[`SFC compile <script setup> async/await detection nested await 3`] = `
 
 export default {
   async setup(__props, { expose }) {
-  expose()
+  expose();
 
 let __temp, __restore
 ;(
@@ -197,7 +197,7 @@ exports[`SFC compile <script setup> async/await detection nested leading await i
 
 export default {
   async setup(__props, { expose }) {
-  expose()
+  expose();
 
 let __temp, __restore
 foo()
@@ -223,7 +223,7 @@ exports[`SFC compile <script setup> async/await detection nested statements 1`]
 
 export default {
   async setup(__props, { expose }) {
-  expose()
+  expose();
 
 let __temp, __restore
 if (ok) { (
@@ -246,7 +246,7 @@ exports[`SFC compile <script setup> async/await detection ref 1`] = `
 
 export default {
   async setup(__props, { expose }) {
-  expose()
+  expose();
 
 let __temp, __restore
 let a = _ref(1 + ((
@@ -264,7 +264,7 @@ return { a }
 exports[`SFC compile <script setup> async/await detection should ignore await inside functions 1`] = `
 "export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 async function foo() { await bar }
 return { foo }
 }
@@ -275,7 +275,7 @@ return { foo }
 exports[`SFC compile <script setup> async/await detection should ignore await inside functions 2`] = `
 "export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 const foo = async () => { await bar }
 return { foo }
 }
@@ -286,7 +286,7 @@ return { foo }
 exports[`SFC compile <script setup> async/await detection should ignore await inside functions 3`] = `
 "export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 const obj = { async method() { await bar }}
 return { obj }
 }
@@ -297,7 +297,7 @@ return { obj }
 exports[`SFC compile <script setup> async/await detection should ignore await inside functions 4`] = `
 "export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 const cls = class Foo { async method() { await bar }}
 return { cls }
 }
@@ -310,7 +310,7 @@ exports[`SFC compile <script setup> async/await detection single line conditions
 
 export default {
   async setup(__props, { expose }) {
-  expose()
+  expose();
 
 let __temp, __restore
 if (false) (
@@ -329,7 +329,7 @@ exports[`SFC compile <script setup> async/await detection variable 1`] = `
 
 export default {
   async setup(__props, { expose }) {
-  expose()
+  expose();
 
 let __temp, __restore
 const a = 1 + ((
@@ -347,7 +347,7 @@ return { a }
 exports[`SFC compile <script setup> binding analysis for destructur 1`] = `
 "export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 
       const { foo, b: bar, ['x' + 'y']: baz, x: { y, zz: { z }}} = {}
       
@@ -361,7 +361,7 @@ exports[`SFC compile <script setup> defineEmits() 1`] = `
 "export default {
   emits: ['foo', 'bar'],
   setup(__props, { expose, emit: myEmit }) {
-  expose()
+  expose();
 
 
 
@@ -389,7 +389,7 @@ exports[`SFC compile <script setup> defineProps w/ external definition 1`] = `
 export default {
   props: propsModel,
   setup(__props, { expose }) {
-  expose()
+  expose();
 
 const props = __props
 
@@ -407,7 +407,7 @@ exports[`SFC compile <script setup> defineProps w/ leading code 1`] = `
 export default {
   props: {},
   setup(__props, { expose }) {
-  expose()
+  expose();
 
 const props = __props
 
@@ -424,7 +424,7 @@ exports[`SFC compile <script setup> defineProps() 1`] = `
   foo: String
 },
   setup(__props, { expose }) {
-  expose()
+  expose();
 
 const props = __props
 
@@ -442,7 +442,7 @@ exports[`SFC compile <script setup> defineProps/defineEmits in multi-variable de
   props: ['item'],
   emits: ['a'],
   setup(__props, { expose, emit }) {
-  expose()
+  expose();
 
 const props = __props
 
@@ -459,7 +459,7 @@ exports[`SFC compile <script setup> defineProps/defineEmits in multi-variable de
   props: ['item'],
   emits: ['a'],
   setup(__props, { expose, emit }) {
-  expose()
+  expose();
 
 const props = __props
 
@@ -477,7 +477,7 @@ import { bar, baz } from './x'
         
 export default /*#__PURE__*/_defineComponent({
   setup(__props, { expose }) {
-  expose()
+  expose();
 
         const cond = true
         
@@ -493,7 +493,7 @@ import { FooBar, FooBaz, FooQux, foo } from './x'
         
 export default /*#__PURE__*/_defineComponent({
   setup(__props, { expose }) {
-  expose()
+  expose();
 
         const fooBar: FooBar = 1
         
@@ -509,7 +509,7 @@ import { vMyDir } from './x'
         
 export default /*#__PURE__*/_defineComponent({
   setup(__props, { expose }) {
-  expose()
+  expose();
 
         
 return { vMyDir }
@@ -524,7 +524,7 @@ import { VAR, VAR2, VAR3 } from './x'
         
 export default /*#__PURE__*/_defineComponent({
   setup(__props, { expose }) {
-  expose()
+  expose();
 
         
 return { VAR, VAR3 }
@@ -539,7 +539,7 @@ import { FooBaz, Last } from './x'
         
 export default /*#__PURE__*/_defineComponent({
   setup(__props, { expose }) {
-  expose()
+  expose();
 
         
 return { FooBaz, Last }
@@ -554,7 +554,7 @@ import { x, y, z, x$y } from './x'
       
 export default /*#__PURE__*/_defineComponent({
   setup(__props, { expose }) {
-  expose()
+  expose();
 
       
 return { x, z, x$y }
@@ -576,7 +576,7 @@ export default {
           foo: () => bar > 1
         },
   setup(__props, { expose }) {
-  expose()
+  expose();
 
         
         
@@ -598,7 +598,7 @@ exports[`SFC compile <script setup> errors should allow defineProps/Emit() refer
             foo: bar => bar > 1
           },
   setup(__props, { expose }) {
-  expose()
+  expose();
 
           const bar = 1
           
@@ -616,7 +616,7 @@ import { ref } from 'vue'
       
 export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 
       let foo = _ref(1)
       
@@ -631,7 +631,7 @@ exports[`SFC compile <script setup> imports import dedupe between <script> and <
         
 export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 
         x()
         
@@ -648,7 +648,7 @@ export default {
   props: ['foo'],
   emits: ['bar'],
   setup(__props, { expose }) {
-  expose()
+  expose();
 
       
       
@@ -666,7 +666,7 @@ exports[`SFC compile <script setup> imports should extract comment for import or
         
 export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 
         
 return { a, b }
@@ -681,7 +681,7 @@ exports[`SFC compile <script setup> imports should hoist and expose imports 1`]
         
 export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 
           
 return { ref }
@@ -984,7 +984,7 @@ exports[`SFC compile <script setup> should expose top level declarations 1`] = `
       
 export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 
       let a = 1
       const b = 2
@@ -1008,7 +1008,7 @@ const enum Foo { A = 123 }
         
 export default /*#__PURE__*/_defineComponent({
   setup(__props, { expose }) {
-  expose()
+  expose();
 
         
 return { Foo }
@@ -1024,7 +1024,7 @@ export interface Emits { (e: 'foo' | 'bar'): void }
 export default /*#__PURE__*/_defineComponent({
   emits: [\\"foo\\", \\"bar\\"],
   setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
-  expose()
+  expose();
 
       
       
@@ -1041,7 +1041,7 @@ export type Emits = { (e: 'foo' | 'bar'): void }
 export default /*#__PURE__*/_defineComponent({
   emits: [\\"foo\\", \\"bar\\"],
   setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
-  expose()
+  expose();
 
       
       
@@ -1058,7 +1058,7 @@ interface Emits { (e: 'foo' | 'bar'): void }
 export default /*#__PURE__*/_defineComponent({
   emits: [\\"foo\\", \\"bar\\"],
   setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
-  expose()
+  expose();
 
       
       
@@ -1075,7 +1075,7 @@ export type Emits = (e: 'foo' | 'bar') => void
 export default /*#__PURE__*/_defineComponent({
   emits: [\\"foo\\", \\"bar\\"],
   setup(__props, { expose, emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
-  expose()
+  expose();
 
       
       
@@ -1092,7 +1092,7 @@ type Emits = (e: 'foo' | 'bar') => void
 export default /*#__PURE__*/_defineComponent({
   emits: [\\"foo\\", \\"bar\\"],
   setup(__props, { expose, emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
-  expose()
+  expose();
 
       
       
@@ -1109,7 +1109,7 @@ type Emits = { (e: 'foo' | 'bar'): void }
 export default /*#__PURE__*/_defineComponent({
   emits: [\\"foo\\", \\"bar\\"],
   setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
-  expose()
+  expose();
 
       
       
@@ -1125,7 +1125,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (type li
 export default /*#__PURE__*/_defineComponent({
   emits: [\\"foo\\", \\"bar\\", \\"baz\\"],
   setup(__props, { expose, emit }: { emit: ({(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}), expose: any, slots: any, attrs: any }) {
-  expose()
+  expose();
 
       
       
@@ -1141,7 +1141,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type 1`] = `
 export default /*#__PURE__*/_defineComponent({
   emits: [\\"foo\\", \\"bar\\"],
   setup(__props, { expose, emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
-  expose()
+  expose();
 
       
       
@@ -1160,7 +1160,7 @@ export default /*#__PURE__*/_defineComponent({
     x: { type: Number, required: false }
   },
   setup(__props: any, { expose }) {
-  expose()
+  expose();
 
       
       
@@ -1180,7 +1180,7 @@ export default /*#__PURE__*/_defineComponent({
     x: { type: Number, required: false }
   },
   setup(__props: any, { expose }) {
-  expose()
+  expose();
 
         
       
@@ -1199,7 +1199,7 @@ export default /*#__PURE__*/_defineComponent({
     x: { type: Number, required: false }
   },
   setup(__props: any, { expose }) {
-  expose()
+  expose();
 
       
       
@@ -1218,7 +1218,7 @@ export default /*#__PURE__*/_defineComponent({
     x: { type: Number, required: false }
   },
   setup(__props: any, { expose }) {
-  expose()
+  expose();
 
       
       
@@ -1265,7 +1265,7 @@ export default /*#__PURE__*/_defineComponent({
     foo: { type: [Function, null], required: true }
   },
   setup(__props: any, { expose }) {
-  expose()
+  expose();
 
       
       
@@ -1284,7 +1284,7 @@ export default /*#__PURE__*/_defineComponent({
     x: { type: Number, required: false }
   },
   setup(__props: any, { expose }) {
-  expose()
+  expose();
 
       
       
@@ -1301,7 +1301,7 @@ export default /*#__PURE__*/_defineComponent({
   props: { foo: String },
   emits: ['a', 'b'],
   setup(__props, { expose, emit }) {
-  expose()
+  expose();
 
 const props = __props
 
@@ -1321,7 +1321,7 @@ export interface Foo {}
       
 export default /*#__PURE__*/_defineComponent({
   setup(__props, { expose }) {
-  expose()
+  expose();
 
         
 return {  }
@@ -1336,7 +1336,7 @@ enum Foo { A = 123 }
         
 export default /*#__PURE__*/_defineComponent({
   setup(__props, { expose }) {
-  expose()
+  expose();
 
         
 return { Foo }
@@ -1355,7 +1355,7 @@ enum Foo { A = 123 }
         
 export default /*#__PURE__*/_defineComponent({
   setup(__props, { expose }) {
-  expose()
+  expose();
 
         
 return { D, C, B, Foo }
@@ -1375,7 +1375,7 @@ export default /*#__PURE__*/_defineComponent({
     baz: { type: Boolean, required: true }
   }, { ...defaults }),
   setup(__props: any, { expose }) {
-  expose()
+  expose();
 
 const props = __props as {
         foo?: string
@@ -1402,7 +1402,7 @@ export default /*#__PURE__*/_defineComponent({
     qux: { type: Function, required: false, default() { return 1 } }
   },
   setup(__props: any, { expose }) {
-  expose()
+  expose();
 
 const props = __props as { foo: string, bar?: number, baz: boolean, qux(): number }
 
diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScriptPropsTransform.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScriptPropsTransform.spec.ts.snap
index a8f95c72a..31378a265 100644
--- a/packages/compiler-sfc/__tests__/__snapshots__/compileScriptPropsTransform.spec.ts.snap
+++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScriptPropsTransform.spec.ts.snap
@@ -82,7 +82,10 @@ export default /*#__PURE__*/_defineComponent({
   props: {
     foo: { default: 1 },
     bar: { default: () => {} },
-    baz: null
+    baz: null,
+    boola: { type: Boolean },
+    boolb: { type: [Boolean, Number] },
+    func: { type: Function, default: () => () => {} }
   },
   setup(__props: any) {
 
diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScriptRefTransform.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScriptRefTransform.spec.ts.snap
index 7d7c7c405..43b47e950 100644
--- a/packages/compiler-sfc/__tests__/__snapshots__/compileScriptRefTransform.spec.ts.snap
+++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScriptRefTransform.spec.ts.snap
@@ -5,7 +5,7 @@ exports[`sfc ref transform $ unwrapping 1`] = `
     
 export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 
     let foo = (ref())
     let a = (ref(1))
@@ -26,7 +26,7 @@ exports[`sfc ref transform $ref & $shallowRef declarations 1`] = `
 
 export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 
     let foo = _ref()
     let a = _ref(1)
@@ -47,7 +47,7 @@ exports[`sfc ref transform usage /w typescript 1`] = `
 
 export default /*#__PURE__*/_defineComponent({
   setup(__props, { expose }) {
-  expose()
+  expose();
 
         let msg = _ref<string | number>('foo');
         let bar = _ref <string | number>('bar');
@@ -79,7 +79,7 @@ exports[`sfc ref transform usage with normal <script> + <script setup> 1`] = `
     
 export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 
     let b = _ref(0)
     let c = 0
diff --git a/packages/compiler-sfc/__tests__/__snapshots__/cssVars.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/cssVars.spec.ts.snap
index dd45d4546..5e31fc5ba 100644
--- a/packages/compiler-sfc/__tests__/__snapshots__/cssVars.spec.ts.snap
+++ b/packages/compiler-sfc/__tests__/__snapshots__/cssVars.spec.ts.snap
@@ -54,7 +54,7 @@ exports[`CSS vars injection codegen should ignore comments 1`] = `
 
 export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 
 _useCssVars(_ctx => ({
   \\"xxxxxxxx-width\\": (width)
@@ -71,7 +71,7 @@ exports[`CSS vars injection codegen w/ <script setup> 1`] = `
 
 export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 
 _useCssVars(_ctx => ({
   \\"xxxxxxxx-color\\": (color)
@@ -88,7 +88,7 @@ exports[`CSS vars injection codegen w/ <script setup> using the same var multipl
 
 export default {
   setup(__props, { expose }) {
-  expose()
+  expose();
 
 _useCssVars(_ctx => ({
   \\"xxxxxxxx-color\\": (color)
@@ -128,7 +128,7 @@ export default {
           foo: String
         },
   setup(__props, { expose }) {
-  expose()
+  expose();
 
 _useCssVars(_ctx => ({
   \\"xxxxxxxx-color\\": (color),
diff --git a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap
index 753f3f9a5..37545d772 100644
--- a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap
+++ b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap
@@ -67,6 +67,7 @@ export function render(_ctx, _cache) {
     _createElementVNode(\\"img\\", { src: _imports_1 }),
     _createElementVNode(\\"img\\", { src: _imports_1 }),
     _createElementVNode(\\"img\\", { src: \\"http://example.com/fixtures/logo.png\\" }),
+    _createElementVNode(\\"img\\", { src: \\"//example.com/fixtures/logo.png\\" }),
     _createElementVNode(\\"img\\", { src: \\"/fixtures/logo.png\\" }),
     _createElementVNode(\\"img\\", { src: \\"data:image/png;base64,i\\" })
   ], 64 /* STABLE_FRAGMENT */))
@@ -99,7 +100,8 @@ export function render(_ctx, _cache) {
   return (_openBlock(), _createElementBlock(_Fragment, null, [
     _createElementVNode(\\"img\\", { src: _imports_0 }),
     _createElementVNode(\\"img\\", { src: _imports_1 }),
-    _createElementVNode(\\"img\\", { src: \\"https://foo.bar/baz.png\\" })
+    _createElementVNode(\\"img\\", { src: \\"https://foo.bar/baz.png\\" }),
+    _createElementVNode(\\"img\\", { src: \\"//foo.bar/baz.png\\" })
   ], 64 /* STABLE_FRAGMENT */))
 }"
 `;
diff --git a/packages/compiler-sfc/__tests__/compileScript.spec.ts b/packages/compiler-sfc/__tests__/compileScript.spec.ts
index 694619174..85b9f9a41 100644
--- a/packages/compiler-sfc/__tests__/compileScript.spec.ts
+++ b/packages/compiler-sfc/__tests__/compileScript.spec.ts
@@ -292,7 +292,7 @@ defineExpose({ foo: 123 })
       let foo = $ref(1)
       </script>
       `,
-        { refSugar: true }
+        { refTransform: true }
       )
       assertCode(content)
       expect(content).toMatch(`import { ref } from 'vue'`)
@@ -1090,7 +1090,7 @@ const emit = defineEmits(['a', 'b'])
   describe('async/await detection', () => {
     function assertAwaitDetection(code: string, shouldAsync = true) {
       const { content } = compile(`<script setup>${code}</script>`, {
-        refSugar: true
+        refTransform: true
       })
       if (shouldAsync) {
         expect(content).toMatch(`let __temp, __restore`)
diff --git a/packages/compiler-sfc/__tests__/compileScriptPropsTransform.spec.ts b/packages/compiler-sfc/__tests__/compileScriptPropsTransform.spec.ts
index f388e501d..b71495d72 100644
--- a/packages/compiler-sfc/__tests__/compileScriptPropsTransform.spec.ts
+++ b/packages/compiler-sfc/__tests__/compileScriptPropsTransform.spec.ts
@@ -83,7 +83,7 @@ describe('sfc props transform', () => {
     const { content } = compile(
       `
       <script setup lang="ts">
-      const { foo = 1, bar = {} } = defineProps<{ foo?: number, bar?: object, baz?: any }>()
+      const { foo = 1, bar = {}, func = () => {} } = defineProps<{ foo?: number, bar?: object, baz?: any, boola?: boolean, boolb?: boolean | number, func?: Function }>()
       </script>
     `,
       { isProd: true }
@@ -93,7 +93,10 @@ describe('sfc props transform', () => {
     expect(content).toMatch(`props: {
     foo: { default: 1 },
     bar: { default: () => {} },
-    baz: null
+    baz: null,
+    boola: { type: Boolean },
+    boolb: { type: [Boolean, Number] },
+    func: { type: Function, default: () => () => {} }
   }`)
     assertCode(content)
   })
diff --git a/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts b/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts
index 34dc9efea..d2f525890 100644
--- a/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts
+++ b/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts
@@ -5,7 +5,7 @@ import { compileSFCScript as compile, assertCode } from './utils'
 // transform can be found in <root>/packages/ref-transform/__tests__
 describe('sfc ref transform', () => {
   function compileWithRefTransform(src: string) {
-    return compile(src, { refSugar: true })
+    return compile(src, { refTransform: true })
   }
 
   test('$ unwrapping', () => {
@@ -156,7 +156,7 @@ describe('sfc ref transform', () => {
           bar
         })
       </script>`,
-          { refSugar: true }
+          { refTransform: true }
         )
       ).toThrow(`cannot reference locally declared variables`)
 
@@ -168,7 +168,7 @@ describe('sfc ref transform', () => {
           bar
         })
       </script>`,
-          { refSugar: true }
+          { refTransform: true }
         )
       ).toThrow(`cannot reference locally declared variables`)
     })
diff --git a/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts b/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts
index d07b92266..18e2cb5e9 100644
--- a/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts
+++ b/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts
@@ -29,6 +29,7 @@ describe('compiler sfc: transform asset url', () => {
 			<img src="~fixtures/logo.png"/>
 			<img src="~/fixtures/logo.png"/>
 			<img src="http://example.com/fixtures/logo.png"/>
+			<img src="//example.com/fixtures/logo.png"/>
 			<img src="/fixtures/logo.png"/>
 			<img src="data:image/png;base64,i"/>
 		`)
@@ -76,7 +77,8 @@ describe('compiler sfc: transform asset url', () => {
     const { code } = compileWithAssetUrls(
       `<img src="./bar.png"/>` +
         `<img src="/bar.png"/>` +
-        `<img src="https://foo.bar/baz.png"/>`,
+        `<img src="https://foo.bar/baz.png"/>` +
+        `<img src="//foo.bar/baz.png"/>`,
       {
         includeAbsolute: true
       }
diff --git a/packages/compiler-sfc/__tests__/templateUtils.spec.ts b/packages/compiler-sfc/__tests__/templateUtils.spec.ts
index 95bf619be..a50965733 100644
--- a/packages/compiler-sfc/__tests__/templateUtils.spec.ts
+++ b/packages/compiler-sfc/__tests__/templateUtils.spec.ts
@@ -36,6 +36,12 @@ describe('compiler sfc:templateUtils isExternalUrl', () => {
     const result = isExternalUrl(url)
     expect(result).toBe(true)
   })
+
+  test('should return true when String starts with //', () => {
+    const url = '//vuejs.org/'
+    const result = isExternalUrl(url)
+    expect(result).toBe(true)
+  })
 })
 
 describe('compiler sfc:templateUtils isDataUrl', () => {
diff --git a/packages/reactivity/__tests__/readonly.spec.ts b/packages/reactivity/__tests__/readonly.spec.ts
index 63426bb5e..185bb3107 100644
--- a/packages/reactivity/__tests__/readonly.spec.ts
+++ b/packages/reactivity/__tests__/readonly.spec.ts
@@ -465,4 +465,13 @@ describe('reactivity/readonly', () => {
       'Set operation on key "randomProperty" failed: target is readonly.'
     ).toHaveBeenWarned()
   })
+
+  // #4986
+  test('setting a readonly object as a property of a reactive object should retain readonly proxy', () => {
+    const r = readonly({})
+    const rr = reactive({}) as any
+    rr.foo = r
+    expect(rr.foo).toBe(r)
+    expect(isReadonly(rr.foo)).toBe(true)
+  })
 })
diff --git a/packages/runtime-core/__tests__/componentPublicInstance.spec.ts b/packages/runtime-core/__tests__/componentPublicInstance.spec.ts
index c4c456b5f..0274351f8 100644
--- a/packages/runtime-core/__tests__/componentPublicInstance.spec.ts
+++ b/packages/runtime-core/__tests__/componentPublicInstance.spec.ts
@@ -195,6 +195,11 @@ describe('component: proxy', () => {
     expect('$foobar' in instanceProxy).toBe(false)
     expect('baz' in instanceProxy).toBe(false)
 
+    // #4962 triggering getter should not cause non-existent property to
+    // pass the has check
+    instanceProxy.baz
+    expect('baz' in instanceProxy).toBe(false)
+
     // set non-existent (goes into proxyTarget sink)
     instanceProxy.baz = 1
     expect('baz' in instanceProxy).toBe(true)
diff --git a/packages/runtime-core/__tests__/components/KeepAlive.spec.ts b/packages/runtime-core/__tests__/components/KeepAlive.spec.ts
index 4dce5d144..b60931e1f 100644
--- a/packages/runtime-core/__tests__/components/KeepAlive.spec.ts
+++ b/packages/runtime-core/__tests__/components/KeepAlive.spec.ts
@@ -16,7 +16,9 @@ import {
   cloneVNode,
   provide,
   defineAsyncComponent,
-  Component
+  Component,
+  createApp,
+  onActivated
 } from '@vue/runtime-test'
 import { KeepAliveProps } from '../../src/components/KeepAlive'
 
@@ -874,4 +876,31 @@ describe('KeepAlive', () => {
     await nextTick()
     expect(serializeInner(root)).toBe('<p>1</p>')
   })
+
+  // #4976
+  test('handle error in async onActivated', async () => {
+    const err = new Error('foo')
+    const handler = jest.fn()
+
+    const app = createApp({
+      setup() {
+        return () => h(KeepAlive, null, () => h(Child))
+      }
+    })
+
+    const Child = {
+      setup() {
+        onActivated(async () => {
+          throw err
+        })
+      },
+      render() {}
+    }
+
+    app.config.errorHandler = handler
+    app.mount(nodeOps.createElement('div'))
+
+    await nextTick()
+    expect(handler).toHaveBeenCalledWith(err, {}, 'activated hook')
+  })
 })
diff --git a/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts b/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts
index d358431d6..f23cdf6a2 100644
--- a/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts
+++ b/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts
@@ -11,7 +11,8 @@ import {
   createBlock,
   FunctionalComponent,
   createCommentVNode,
-  Fragment
+  Fragment,
+  withModifiers
 } from '@vue/runtime-dom'
 import { PatchFlags } from '@vue/shared/src'
 
@@ -383,6 +384,45 @@ describe('attribute fallthrough', () => {
     expect(`Extraneous non-emits event listeners`).toHaveBeenWarned()
   })
 
+  it('should dedupe same listeners when $attrs is used during render', () => {
+    const click = jest.fn()
+    const count = ref(0)
+
+    function inc() {
+      count.value++
+      click()
+    }
+
+    const Parent = {
+      render() {
+        return h(Child, { onClick: inc })
+      }
+    }
+
+    const Child = defineComponent({
+      render() {
+        return h(
+          'div',
+          mergeProps(
+            {
+              onClick: withModifiers(() => {}, ['prevent', 'stop'])
+            },
+            this.$attrs
+          )
+        )
+      }
+    })
+
+    const root = document.createElement('div')
+    document.body.appendChild(root)
+    render(h(Parent), root)
+
+    const node = root.children[0] as HTMLElement
+    node.dispatchEvent(new CustomEvent('click'))
+    expect(click).toHaveBeenCalledTimes(1)
+    expect(count.value).toBe(1)
+  })
+
   it('should not warn when $attrs is used during render', () => {
     const Parent = {
       render() {
diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts
index 60b51de38..777f9677d 100644
--- a/packages/runtime-dom/__tests__/customElement.spec.ts
+++ b/packages/runtime-dom/__tests__/customElement.spec.ts
@@ -191,13 +191,21 @@ describe('defineCustomElement', () => {
 
     test('handling properties set before upgrading', () => {
       const E = defineCustomElement({
-        props: ['foo'],
+        props: {
+          foo: String,
+          dataAge: Number
+        },
+        setup(props) {
+          expect(props.foo).toBe('hello')
+          expect(props.dataAge).toBe(5)
+        },
         render() {
           return `foo: ${this.foo}`
         }
       })
       const el = document.createElement('my-el-upgrade') as any
       el.foo = 'hello'
+      el.dataset.age = 5
       container.appendChild(el)
       customElements.define('my-el-upgrade', E)
       expect(el.shadowRoot.innerHTML).toBe(`foo: hello`)
@@ -363,10 +371,10 @@ describe('defineCustomElement', () => {
 
       // should inject styles
       expect(e1.shadowRoot!.innerHTML).toBe(
-        `<div>hello</div><style>div { color: red }</style>`
+        `<style>div { color: red }</style><div>hello</div>`
       )
       expect(e2.shadowRoot!.innerHTML).toBe(
-        `<div>world</div><style>div { color: red }</style>`
+        `<style>div { color: red }</style><div>world</div>`
       )
 
       // attr
@@ -374,7 +382,7 @@ describe('defineCustomElement', () => {
       await nextTick()
       expect((e1 as any).msg).toBe('attr')
       expect(e1.shadowRoot!.innerHTML).toBe(
-        `<div>attr</div><style>div { color: red }</style>`
+        `<style>div { color: red }</style><div>attr</div>`
       )
 
       // props
@@ -382,7 +390,7 @@ describe('defineCustomElement', () => {
       ;(e1 as any).msg = 'prop'
       expect(e1.getAttribute('msg')).toBe('prop')
       expect(e1.shadowRoot!.innerHTML).toBe(
-        `<div>prop</div><style>div { color: red }</style>`
+        `<style>div { color: red }</style><div>prop</div>`
       )
     })
 
@@ -391,6 +399,9 @@ describe('defineCustomElement', () => {
         defineAsyncComponent(() => {
           return Promise.resolve({
             props: ['msg'],
+            setup(props) {
+              expect(typeof props.msg).toBe('string')
+            },
             render(this: any) {
               return h('div', this.msg)
             }
@@ -429,6 +440,9 @@ describe('defineCustomElement', () => {
         defineAsyncComponent(() => {
           return Promise.resolve({
             props: { n: Number },
+            setup(props) {
+              expect(props.n).toBe(20)
+            },
             render(this: any) {
               return h('div', this.n + ',' + typeof this.n)
             }
diff --git a/packages/runtime-dom/__tests__/patchProps.spec.ts b/packages/runtime-dom/__tests__/patchProps.spec.ts
index e1ee7928c..1b97c6ea1 100644
--- a/packages/runtime-dom/__tests__/patchProps.spec.ts
+++ b/packages/runtime-dom/__tests__/patchProps.spec.ts
@@ -25,13 +25,50 @@ describe('runtime-dom: props patching', () => {
     expect((el as any)._value).toBe(obj)
   })
 
+  test('value for custom elements', () => {
+    class TestElement extends HTMLElement {
+      constructor() {
+        super()
+      }
+
+      // intentionally uses _value because this is used in "normal" HTMLElement for storing the object of the set property value
+      private _value: any
+      get value() {
+        return this._value
+      }
+
+      set value(val) {
+        this._value = val
+        this.setterCalled++
+      }
+
+      public setterCalled: number = 0
+    }
+    window.customElements.define('test-element', TestElement)
+    const el = document.createElement('test-element') as TestElement
+    patchProp(el, 'value', null, 'foo')
+    expect(el.value).toBe('foo')
+    expect(el.setterCalled).toBe(1)
+    patchProp(el, 'value', null, null)
+    expect(el.value).toBe('')
+    expect(el.setterCalled).toBe(2)
+    expect(el.getAttribute('value')).toBe(null)
+    const obj = {}
+    patchProp(el, 'value', null, obj)
+    expect(el.value).toBe(obj)
+    expect(el.setterCalled).toBe(3)
+  })
+
   // For <input type="text">, setting el.value won't create a `value` attribute
   // so we need to add tests for other elements
   test('value for non-text input', () => {
     const el = document.createElement('option')
+    el.textContent = 'foo' // #4956
     patchProp(el, 'value', null, 'foo')
+    expect(el.getAttribute('value')).toBe('foo')
     expect(el.value).toBe('foo')
     patchProp(el, 'value', null, null)
+    el.textContent = ''
     expect(el.value).toBe('')
     // #3475
     expect(el.getAttribute('value')).toBe(null)
diff --git a/packages/runtime-test/package.json b/packages/runtime-test/package.json
index df3cb9da0..aeab88bd9 100644
--- a/packages/runtime-test/package.json
+++ b/packages/runtime-test/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/runtime-test",
-  "version": "3.2.20",
+  "version": "3.2.22",
   "description": "@vue/runtime-test",
   "private": true,
   "main": "index.js",
@@ -25,7 +25,7 @@
   },
   "homepage": "https://github.com/vuejs/vue-next/tree/master/packages/runtime-test#readme",
   "dependencies": {
-    "@vue/shared": "3.2.20",
-    "@vue/runtime-core": "3.2.20"
+    "@vue/shared": "3.2.22",
+    "@vue/runtime-core": "3.2.22"
   }
 }
diff --git a/packages/vue/__tests__/Transition.spec.ts b/packages/vue/__tests__/Transition.spec.ts
index bcb1fcb3b..97b3ccd96 100644
--- a/packages/vue/__tests__/Transition.spec.ts
+++ b/packages/vue/__tests__/Transition.spec.ts
@@ -1969,6 +1969,21 @@ describe('e2e: Transition', () => {
     ).toHaveBeenWarned()
   })
 
+  test('warn when invalid transition mode', () => {
+    createApp({
+      template: `
+        <div id="container">
+          <transition name="test" mode="none">
+            <div class="test">content</div>
+          </transition>
+        </div>
+      `
+    }).mount(document.createElement('div'))
+    expect(
+      `invalid <transition> mode: none`
+    ).toHaveBeenWarned()
+  })
+
   // #3227
   test(`HOC w/ merged hooks`, async () => {
     const innerSpy = jest.fn()
diff --git a/packages/vue/__tests__/TransitionGroup.spec.ts b/packages/vue/__tests__/TransitionGroup.spec.ts
index 6ba057638..38d742538 100644
--- a/packages/vue/__tests__/TransitionGroup.spec.ts
+++ b/packages/vue/__tests__/TransitionGroup.spec.ts
@@ -346,7 +346,7 @@ describe('e2e: TransitionGroup', () => {
       )
       // not sure why but we just have to wait really long for this to
       // pass consistently :/
-      await transitionFinish(duration * 4)
+      await transitionFinish(duration * 4 + buffer)
       expect(await html('#container')).toBe(
         `<div class="" style="">a</div>` +
           `<div class="" style="">b</div>` +
diff --git a/scripts/filter-e2e.js b/scripts/filter-e2e.js
new file mode 100644
index 000000000..bbc360228
--- /dev/null
+++ b/scripts/filter-e2e.js
@@ -0,0 +1,11 @@
+const e2eTests = ['/Transition', '/TransitionGroup', '/examples/']
+
+module.exports = list => {
+  return {
+    filtered: list
+      .filter(t => e2eTests.some(tt => t.includes(tt)))
+      .map(test => ({ test }))
+  }
+}
+
+module.exports.e2eTests = e2eTests
diff --git a/test-dts/component.test-d.ts b/test-dts/component.test-d.ts
index 93c3ea410..d8d3934b9 100644
--- a/test-dts/component.test-d.ts
+++ b/test-dts/component.test-d.ts
@@ -10,7 +10,8 @@ import {
   ShallowUnwrapRef,
   FunctionalComponent,
   ComponentPublicInstance,
-  toRefs
+  toRefs,
+  IsAny
 } from './index'
 
 declare function extractComponentOptions<Props, RawBindings>(
@@ -62,6 +63,7 @@ describe('object props', () => {
     ffff: Ref<(a: number, b: string) => { a: boolean }>
     validated: Ref<string | undefined>
     object: Ref<object | undefined>
+    zzz: any
   }
 
   describe('defineComponent', () => {
@@ -130,7 +132,8 @@ describe('object props', () => {
           // validator requires explicit annotation
           validator: (val: unknown) => val !== ''
         },
-        object: Object as PropType<object>
+        object: Object as PropType<object>,
+        zzz: Object as PropType<any>
       },
       setup(props) {
         const refs = toRefs(props)
@@ -152,6 +155,7 @@ describe('object props', () => {
         expectType<ExpectedRefs['ffff']>(refs.ffff)
         expectType<ExpectedRefs['validated']>(refs.validated)
         expectType<ExpectedRefs['object']>(refs.object)
+        expectType<IsAny<typeof props.zzz>>(true)
 
         return {
           setupA: 1,
diff --git a/test-dts/index.d.ts b/test-dts/index.d.ts
index 3d8d288fa..59eadcb92 100644
--- a/test-dts/index.d.ts
+++ b/test-dts/index.d.ts
@@ -14,3 +14,5 @@ export type IsUnion<T, U extends T = T> = (T extends any
   : never) extends false
   ? false
   : true
+
+export type IsAny<T> = 0 extends (1 & T) ? true : false
diff --git a/test-dts/ref.test-d.ts b/test-dts/ref.test-d.ts
index d4462db9e..5b5274c98 100644
--- a/test-dts/ref.test-d.ts
+++ b/test-dts/ref.test-d.ts
@@ -10,8 +10,7 @@ import {
   toRef,
   toRefs,
   ToRefs,
-  shallowReactive,
-  watch
+  shallowReactive
 } from './index'
 
 function plainType(arg: number | Ref<number>) {
@@ -185,28 +184,45 @@ const p2 = proxyRefs(r2)
 expectType<number>(p2.a)
 expectType<Ref<string>>(p2.obj.k)
 
-// toRef
-const obj = {
-  a: 1,
-  b: ref(1)
-}
-expectType<Ref<number>>(toRef(obj, 'a'))
-expectType<Ref<number>>(toRef(obj, 'b'))
+// toRef and toRefs
+{
+  const obj: {
+    a: number
+    b: Ref<number>
+    c: number | string
+  } = {
+    a: 1,
+    b: ref(1),
+    c: 1
+  }
 
-const objWithUnionProp: { a: string | number } = {
-  a: 1
-}
+  // toRef
+  expectType<Ref<number>>(toRef(obj, 'a'))
+  expectType<Ref<number>>(toRef(obj, 'b'))
+  // Should not distribute Refs over union
+  expectType<Ref<number | string>>(toRef(obj, 'c'))
+
+  // toRefs
+  expectType<{
+    a: Ref<number>
+    b: Ref<number>
+    // Should not distribute Refs over union
+    c: Ref<number | string>
+  }>(toRefs(obj))
+
+  // Both should not do any unwrapping
+  const someReactive = shallowReactive({
+    a: {
+      b: ref(42)
+    }
+  })
 
-watch(toRef(objWithUnionProp, 'a'), value => {
-  expectType<string | number>(value)
-})
+  const toRefResult = toRef(someReactive, 'a')
+  const toRefsResult = toRefs(someReactive)
 
-// toRefs
-const objRefs = toRefs(obj)
-expectType<{
-  a: Ref<number>
-  b: Ref<number>
-}>(objRefs)
+  expectType<Ref<number>>(toRefResult.value.b)
+  expectType<Ref<number>>(toRefsResult.a.value.b)
+}
 
 // #2687
 interface AppData {
@@ -238,20 +254,6 @@ function testUnrefGenerics<T>(p: T | Ref<T>) {
 
 testUnrefGenerics(1)
 
-// #4732
-describe('ref in shallow reactive', () => {
-  const baz = shallowReactive({
-    foo: {
-      bar: ref(42)
-    }
-  })
-
-  const foo = toRef(baz, 'foo')
-
-  expectType<Ref<number>>(foo.value.bar)
-  expectType<number>(foo.value.bar.value)
-})
-
 // #4771
 describe('shallow reactive in reactive', () => {
   const baz = reactive({
diff --git a/test-dts/setupHelpers.test-d.ts b/test-dts/setupHelpers.test-d.ts
index 1b4a2ff43..155ba337d 100644
--- a/test-dts/setupHelpers.test-d.ts
+++ b/test-dts/setupHelpers.test-d.ts
@@ -45,6 +45,21 @@ describe('defineProps w/ type declaration + withDefaults', () => {
   res.x.slice()
 })
 
+describe('defineProps w/ union type declaration + withDefaults', () => {
+  withDefaults(
+    defineProps<{
+      union1?: number | number[] | { x: number }
+      union2?: number | number[] | { x: number }
+      union3?: number | number[] | { x: number }
+    }>(),
+    {
+      union1: 123,
+      union2: () => [123],
+      union3: () => ({ x: 123 })
+    }
+  )
+})
+
 describe('defineProps w/ runtime declaration', () => {
   // runtime declaration
   const props = defineProps({