Browse Source

chore: Merge branch 'main' into minor

Evan You 2 years ago
parent
commit
58d827cb71
52 changed files with 604 additions and 449 deletions
  1. 4 0
      .github/renovate.json5
  2. 14 0
      CHANGELOG.md
  3. 1 1
      changelogs/CHANGELOG-3.0.md
  4. 24 24
      package.json
  5. 1 1
      packages/compiler-core/__tests__/transforms/vOn.spec.ts
  6. 1 1
      packages/compiler-core/package.json
  7. 6 9
      packages/compiler-core/src/tokenizer.ts
  8. 15 1
      packages/compiler-dom/__tests__/parse.spec.ts
  9. 1 1
      packages/compiler-dom/package.json
  10. 4 4
      packages/compiler-sfc/package.json
  11. 1 1
      packages/compiler-ssr/package.json
  12. 1 1
      packages/dts-test/README.md
  13. 1 0
      packages/reactivity/__tests__/gc.spec.ts
  14. 1 1
      packages/reactivity/package.json
  15. 11 11
      packages/reactivity/src/baseHandlers.ts
  16. 3 2
      packages/reactivity/src/collectionHandlers.ts
  17. 5 0
      packages/reactivity/src/computed.ts
  18. 1 0
      packages/reactivity/src/index.ts
  19. 2 1
      packages/reactivity/src/reactive.ts
  20. 2 1
      packages/reactivity/src/ref.ts
  21. 5 10
      packages/runtime-core/__tests__/apiCreateApp.spec.ts
  22. 0 2
      packages/runtime-core/__tests__/apiOptions.spec.ts
  23. 94 1
      packages/runtime-core/__tests__/components/Suspense.spec.ts
  24. 2 2
      packages/runtime-core/__tests__/hydration.spec.ts
  25. 1 1
      packages/runtime-core/package.json
  26. 10 3
      packages/runtime-core/src/apiComputed.ts
  27. 7 1
      packages/runtime-core/src/apiCreateApp.ts
  28. 26 2
      packages/runtime-core/src/apiDefineComponent.ts
  29. 5 4
      packages/runtime-core/src/component.ts
  30. 1 15
      packages/runtime-core/src/components/BaseTransition.ts
  31. 3 1
      packages/runtime-core/src/components/Suspense.ts
  32. 5 1
      packages/runtime-core/src/index.ts
  33. 9 0
      packages/runtime-dom/__tests__/patchStyle.spec.ts
  34. 1 1
      packages/runtime-dom/package.json
  35. 8 15
      packages/runtime-dom/src/directives/vModel.ts
  36. 10 10
      packages/runtime-dom/src/directives/vShow.ts
  37. 2 1
      packages/runtime-dom/src/modules/props.ts
  38. 26 12
      packages/runtime-dom/src/modules/style.ts
  39. 1 1
      packages/server-renderer/package.json
  40. 0 2
      packages/sfc-playground/index.html
  41. 3 3
      packages/sfc-playground/package.json
  42. 51 62
      packages/sfc-playground/src/App.vue
  43. 11 14
      packages/sfc-playground/src/Header.vue
  44. 2 2
      packages/sfc-playground/src/download/template/package.json
  45. 1 1
      packages/shared/package.json
  46. 1 1
      packages/template-explorer/package.json
  47. 1 1
      packages/vue-compat/package.json
  48. 1 1
      packages/vue/__tests__/e2e/e2eUtils.ts
  49. 1 1
      packages/vue/package.json
  50. 214 217
      pnpm-lock.yaml
  51. 2 0
      rollup.config.js
  52. 1 1
      tsconfig.json

+ 4 - 0
.github/renovate.json5

@@ -50,5 +50,9 @@
     // pinned
     // https://github.com/vuejs/core/issues/10300#issuecomment-1940855364
     'lru-cache',
+
+    // pinned
+    // https://github.com/vuejs/core/commit/a012e39b373f1b6918e5c89856e8f902e1bfa14d
+    '@rollup/plugin-replace',
   ],
 }

+ 14 - 0
CHANGELOG.md

@@ -1,3 +1,17 @@
+## [3.4.20](https://github.com/vuejs/core/compare/v3.4.19...v3.4.20) (2024-02-26)
+
+
+### Bug Fixes
+
+* **parser:** should not treat uppercase components as special tags ([e0e0253](https://github.com/vuejs/core/commit/e0e02535cdea1aeb1cfaff0d61d4b2555e555c36)), closes [#10395](https://github.com/vuejs/core/issues/10395)
+* **runtime-dom:** avoid always resetting nullish option value ([ff130c4](https://github.com/vuejs/core/commit/ff130c470204086edaa093fb8fdc1247c69cba69)), closes [#10396](https://github.com/vuejs/core/issues/10396)
+* **runtime-dom:** fix nested v-show priority regression ([364f890](https://github.com/vuejs/core/commit/364f8902c8657faec7c3a4d70a5b2c856567e92d)), closes [#10338](https://github.com/vuejs/core/issues/10338)
+* **runtime-dom:** v-bind style should clear previous css string value ([#10373](https://github.com/vuejs/core/issues/10373)) ([e2d3235](https://github.com/vuejs/core/commit/e2d323538e71d404e729148fd19a08bbc2e3da9b)), closes [#10352](https://github.com/vuejs/core/issues/10352)
+* **suspense:** handle suspense switching with nested suspense  ([#10184](https://github.com/vuejs/core/issues/10184)) ([0f3da05](https://github.com/vuejs/core/commit/0f3da05ea201761529bb95594df1e2cee20b7107)), closes [#10098](https://github.com/vuejs/core/issues/10098)
+* **types:** better typing for direct setup signature of defineComponent ([#10357](https://github.com/vuejs/core/issues/10357)) ([eadce5b](https://github.com/vuejs/core/commit/eadce5b75356656fd2209ebdb406d34823c961b7)), closes [#8604](https://github.com/vuejs/core/issues/8604) [#8855](https://github.com/vuejs/core/issues/8855)
+
+
+
 ## [3.4.19](https://github.com/vuejs/core/compare/v3.4.18...v3.4.19) (2024-02-13)
 
 

+ 1 - 1
changelogs/CHANGELOG-3.0.md

@@ -773,7 +773,7 @@ may cause build issues in projects still using TS 3.x.
 - **types:** adjust type exports for manual render function and tooling usage ([e4dc03a](https://github.com/vuejs/core/commit/e4dc03a8b17d5e9f167de6a62a645878ac7ef3e2)), closes [#1329](https://github.com/vuejs/core/issues/1329)
 - **types:** mixins/extends support in TypeScript ([#626](https://github.com/vuejs/core/issues/626)) ([d3c436a](https://github.com/vuejs/core/commit/d3c436ae2e66b75b7f2ed574dadda3f0e1fdce73))
 - **types:** support typing directive value via generic argument ([#1007](https://github.com/vuejs/core/issues/1007)) ([419b86d](https://github.com/vuejs/core/commit/419b86d1908f2a0521e6a7eafcbee764e9ee59a0)), closes [#998](https://github.com/vuejs/core/issues/998)
-- **types:** update to Typescript 3.9 ([#1106](https://github.com/vuejs/core/issues/1106)) ([97dedeb](https://github.com/vuejs/core/commit/97dedebd8097116a16209664a1ca38392b964da3))
+- **types:** update to TypeScript 3.9 ([#1106](https://github.com/vuejs/core/issues/1106)) ([97dedeb](https://github.com/vuejs/core/commit/97dedebd8097116a16209664a1ca38392b964da3))
 
 ### Performance Improvements
 

+ 24 - 24
package.json

@@ -1,7 +1,7 @@
 {
   "private": true,
-  "version": "3.4.19",
-  "packageManager": "pnpm@8.15.1",
+  "version": "3.4.20",
+  "packageManager": "pnpm@8.15.4",
   "type": "module",
   "scripts": {
     "dev": "node scripts/dev.js",
@@ -70,49 +70,49 @@
     "@rollup/plugin-terser": "^0.4.4",
     "@types/hash-sum": "^1.0.2",
     "@types/minimist": "^1.2.5",
-    "@types/node": "^20.11.16",
-    "@types/semver": "^7.5.6",
-    "@typescript-eslint/eslint-plugin": "^6.18.1",
-    "@typescript-eslint/parser": "^6.18.1",
-    "@vitest/coverage-istanbul": "^1.2.2",
-    "@vue/consolidate": "0.17.3",
+    "@types/node": "^20.11.20",
+    "@types/semver": "^7.5.8",
+    "@typescript-eslint/eslint-plugin": "^7.0.2",
+    "@typescript-eslint/parser": "^7.0.2",
+    "@vitest/coverage-istanbul": "^1.3.1",
+    "@vue/consolidate": "1.0.0",
     "conventional-changelog-cli": "^4.1.0",
     "enquirer": "^2.4.1",
-    "esbuild": "^0.20.0",
+    "esbuild": "^0.20.1",
     "esbuild-plugin-polyfill-node": "^0.3.0",
-    "eslint": "^8.56.0",
-    "eslint-define-config": "^1.24.1",
+    "eslint": "^8.57.0",
+    "eslint-define-config": "^2.1.0",
     "eslint-plugin-import": "npm:eslint-plugin-i@^2.29.1",
-    "eslint-plugin-jest": "^27.6.3",
+    "eslint-plugin-jest": "^27.9.0",
     "estree-walker": "^2.0.2",
     "execa": "^8.0.1",
-    "jsdom": "^23.2.0",
-    "lint-staged": "^15.2.0",
+    "jsdom": "^24.0.0",
+    "lint-staged": "^15.2.2",
     "lodash": "^4.17.21",
-    "magic-string": "^0.30.6",
+    "magic-string": "^0.30.7",
     "markdown-table": "^3.0.3",
-    "marked": "^11.2.0",
+    "marked": "^12.0.0",
     "minimist": "^1.2.8",
     "npm-run-all2": "^6.1.2",
     "picocolors": "^1.0.0",
-    "prettier": "^3.2.2",
+    "prettier": "^3.2.5",
     "pretty-bytes": "^6.1.1",
     "pug": "^3.0.2",
-    "puppeteer": "~21.11.0",
+    "puppeteer": "~22.2.0",
     "rimraf": "^5.0.5",
-    "rollup": "4.9.2",
+    "rollup": "^4.12.0",
     "rollup-plugin-dts": "^6.1.0",
     "rollup-plugin-esbuild": "^6.1.1",
     "rollup-plugin-polyfill-node": "^0.13.0",
-    "semver": "^7.5.4",
+    "semver": "^7.6.0",
     "serve": "^14.2.1",
     "simple-git-hooks": "^2.9.0",
-    "terser": "^5.27.0",
+    "terser": "^5.28.1",
     "todomvc-app-css": "^2.4.3",
     "tslib": "^2.6.2",
-    "tsx": "^4.7.0",
+    "tsx": "^4.7.1",
     "typescript": "^5.2.2",
-    "vite": "^5.0.12",
-    "vitest": "^1.2.2"
+    "vite": "^5.1.4",
+    "vitest": "^1.3.1"
   }
 }

+ 1 - 1
packages/compiler-core/__tests__/transforms/vOn.spec.ts

@@ -271,7 +271,7 @@ describe('compiler: transform v-on', () => {
     })
   })
 
-  test('should NOT wrap as function if expression is already function expression (with Typescript)', () => {
+  test('should NOT wrap as function if expression is already function expression (with TypeScript)', () => {
     const { node } = parseWithVOn(`<div @click="(e: any): any => foo(e)"/>`)
     expect((node.codegenNode as VNodeCall).props).toMatchObject({
       properties: [

+ 1 - 1
packages/compiler-core/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vue/compiler-core",
-  "version": "3.4.19",
+  "version": "3.4.20",
   "description": "@vue/compiler-core",
   "main": "index.js",
   "module": "dist/compiler-core.esm-bundler.js",

+ 6 - 9
packages/compiler-core/src/tokenizer.ts

@@ -553,12 +553,11 @@ export default class Tokenizer {
         // HTML mode
         // - <script>, <style> RAWTEXT
         // - <title>, <textarea> RCDATA
-        const lower = c | 0x20
-        if (lower === 116 /* t */) {
+        if (c === 116 /* t */) {
           this.state = State.BeforeSpecialT
         } else {
           this.state =
-            lower === 115 /* s */ ? State.BeforeSpecialS : State.InTagName
+            c === 115 /* s */ ? State.BeforeSpecialS : State.InTagName
         }
       } else {
         this.state = State.InTagName
@@ -862,10 +861,9 @@ export default class Tokenizer {
     }
   }
   private stateBeforeSpecialS(c: number): void {
-    const lower = c | 0x20
-    if (lower === Sequences.ScriptEnd[3]) {
+    if (c === Sequences.ScriptEnd[3]) {
       this.startSpecial(Sequences.ScriptEnd, 4)
-    } else if (lower === Sequences.StyleEnd[3]) {
+    } else if (c === Sequences.StyleEnd[3]) {
       this.startSpecial(Sequences.StyleEnd, 4)
     } else {
       this.state = State.InTagName
@@ -873,10 +871,9 @@ export default class Tokenizer {
     }
   }
   private stateBeforeSpecialT(c: number): void {
-    const lower = c | 0x20
-    if (lower === Sequences.TitleEnd[3]) {
+    if (c === Sequences.TitleEnd[3]) {
       this.startSpecial(Sequences.TitleEnd, 4)
-    } else if (lower === Sequences.TextareaEnd[3]) {
+    } else if (c === Sequences.TextareaEnd[3]) {
       this.startSpecial(Sequences.TextareaEnd, 4)
     } else {
       this.state = State.InTagName

+ 15 - 1
packages/compiler-dom/__tests__/parse.spec.ts

@@ -20,7 +20,7 @@ describe('DOM parser', () => {
       )
       const element = ast.children[0] as ElementNode
       const text = element.children[0] as TextNode
-
+      expect(element.children.length).toBe(1)
       expect(text).toStrictEqual({
         type: NodeTypes.TEXT,
         content: 'some<div>text</div>and<!--comment-->',
@@ -32,6 +32,20 @@ describe('DOM parser', () => {
       })
     })
 
+    test('should not treat Uppercase component as special tag', () => {
+      const ast = parse(
+        '<TextArea>some<div>text</div>and<!--comment--></TextArea>',
+        parserOptions,
+      )
+      const element = ast.children[0] as ElementNode
+      expect(element.children.map(n => n.type)).toMatchObject([
+        NodeTypes.TEXT,
+        NodeTypes.ELEMENT,
+        NodeTypes.TEXT,
+        NodeTypes.COMMENT,
+      ])
+    })
+
     test('textarea handles entities', () => {
       const ast = parse('<textarea>&amp;</textarea>', parserOptions)
       const element = ast.children[0] as ElementNode

+ 1 - 1
packages/compiler-dom/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vue/compiler-dom",
-  "version": "3.4.19",
+  "version": "3.4.20",
   "description": "@vue/compiler-dom",
   "main": "index.js",
   "module": "dist/compiler-dom.esm-bundler.js",

+ 4 - 4
packages/compiler-sfc/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vue/compiler-sfc",
-  "version": "3.4.19",
+  "version": "3.4.20",
   "description": "@vue/compiler-sfc",
   "main": "dist/compiler-sfc.cjs.js",
   "module": "dist/compiler-sfc.esm-browser.js",
@@ -48,8 +48,8 @@
     "@vue/compiler-ssr": "workspace:*",
     "@vue/shared": "workspace:*",
     "estree-walker": "^2.0.2",
-    "magic-string": "^0.30.6",
-    "postcss": "^8.4.33",
+    "magic-string": "^0.30.7",
+    "postcss": "^8.4.35",
     "source-map-js": "^1.0.2"
   },
   "devDependencies": {
@@ -62,6 +62,6 @@
     "postcss-modules": "^6.0.0",
     "postcss-selector-parser": "^6.0.15",
     "pug": "^3.0.2",
-    "sass": "^1.70.0"
+    "sass": "^1.71.1"
   }
 }

+ 1 - 1
packages/compiler-ssr/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vue/compiler-ssr",
-  "version": "3.4.19",
+  "version": "3.4.20",
   "description": "@vue/compiler-ssr",
   "main": "dist/compiler-ssr.cjs.js",
   "types": "dist/compiler-ssr.d.ts",

+ 1 - 1
packages/dts-test/README.md

@@ -1,6 +1,6 @@
 # dts-test
 
-Tests Typescript types to ensure the types remain as expected.
+Tests TypeScript types to ensure the types remain as expected.
 
 - This directory is included in the root `tsconfig.json`, where package imports are aliased to `src` directories, so in IDEs and the `pnpm check` script the types are validated against source code.
 

+ 1 - 0
packages/reactivity/__tests__/gc.spec.ts

@@ -21,6 +21,7 @@ describe.skipIf(!global.gc)('reactivity/gc', () => {
   // #9233
   it('should release computed cache', async () => {
     const src = ref<{} | undefined>({})
+    // @ts-expect-error ES2021 API
     const srcRef = new WeakRef(src.value!)
 
     let c: ComputedRef | undefined = computed(() => src.value)

+ 1 - 1
packages/reactivity/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vue/reactivity",
-  "version": "3.4.19",
+  "version": "3.4.20",
   "description": "@vue/reactivity",
   "main": "index.js",
   "module": "dist/reactivity.esm-bundler.js",

+ 11 - 11
packages/reactivity/src/baseHandlers.ts

@@ -84,26 +84,26 @@ function hasOwnProperty(this: object, key: string) {
 class BaseReactiveHandler implements ProxyHandler<Target> {
   constructor(
     protected readonly _isReadonly = false,
-    protected readonly _shallow = false,
+    protected readonly _isShallow = false,
   ) {}
 
   get(target: Target, key: string | symbol, receiver: object) {
     const isReadonly = this._isReadonly,
-      shallow = this._shallow
+      isShallow = this._isShallow
     if (key === ReactiveFlags.IS_REACTIVE) {
       return !isReadonly
     } else if (key === ReactiveFlags.IS_READONLY) {
       return isReadonly
     } else if (key === ReactiveFlags.IS_SHALLOW) {
-      return shallow
+      return isShallow
     } else if (key === ReactiveFlags.RAW) {
       if (
         receiver ===
           (isReadonly
-            ? shallow
+            ? isShallow
               ? shallowReadonlyMap
               : readonlyMap
-            : shallow
+            : isShallow
               ? shallowReactiveMap
               : reactiveMap
           ).get(target) ||
@@ -145,7 +145,7 @@ class BaseReactiveHandler implements ProxyHandler<Target> {
       track(target, TrackOpTypes.GET, key)
     }
 
-    if (shallow) {
+    if (isShallow) {
       return res
     }
 
@@ -166,8 +166,8 @@ class BaseReactiveHandler implements ProxyHandler<Target> {
 }
 
 class MutableReactiveHandler extends BaseReactiveHandler {
-  constructor(shallow = false) {
-    super(false, shallow)
+  constructor(isShallow = false) {
+    super(false, isShallow)
   }
 
   set(
@@ -177,7 +177,7 @@ class MutableReactiveHandler extends BaseReactiveHandler {
     receiver: object,
   ): boolean {
     let oldValue = (target as any)[key]
-    if (!this._shallow) {
+    if (!this._isShallow) {
       const isOldValueReadonly = isReadonly(oldValue)
       if (!isShallow(value) && !isReadonly(value)) {
         oldValue = toRaw(oldValue)
@@ -239,8 +239,8 @@ class MutableReactiveHandler extends BaseReactiveHandler {
 }
 
 class ReadonlyReactiveHandler extends BaseReactiveHandler {
-  constructor(shallow = false) {
-    super(true, shallow)
+  constructor(isShallow = false) {
+    super(true, isShallow)
   }
 
   set(target: object, key: string | symbol) {

+ 3 - 2
packages/reactivity/src/collectionHandlers.ts

@@ -2,6 +2,7 @@ import { toRaw, toReactive, toReadonly } from './reactive'
 import { ITERATE_KEY, MAP_KEY_ITERATE_KEY, track, trigger } from './dep'
 import { ReactiveFlags, TrackOpTypes, TriggerOpTypes } from './constants'
 import { capitalize, hasChanged, hasOwn, isMap, toRawType } from '@vue/shared'
+import { warn } from './warning'
 
 type CollectionTypes = IterableCollections | WeakCollections
 
@@ -218,7 +219,7 @@ function createReadonlyMethod(type: TriggerOpTypes): Function {
   return function (this: CollectionTypes, ...args: unknown[]) {
     if (__DEV__) {
       const key = args[0] ? `on key "${args[0]}" ` : ``
-      console.warn(
+      warn(
         `${capitalize(type)} operation ${key}failed: target is readonly.`,
         toRaw(this),
       )
@@ -392,7 +393,7 @@ function checkIdentityKeys(
   const rawKey = toRaw(key)
   if (rawKey !== key && has.call(target, rawKey)) {
     const type = toRawType(target)
-    console.warn(
+    warn(
       `Reactive ${type} contains both the raw and reactive ` +
         `versions of the same object${type === `Map` ? ` as keys` : ``}, ` +
         `which can lead to inconsistencies. ` +

+ 5 - 0
packages/reactivity/src/computed.ts

@@ -60,6 +60,11 @@ export class ComputedRefImpl<T = any> implements Subscriber {
   // dev only
   onTrigger?: (event: DebuggerEvent) => void
 
+  /**
+   * Dev only
+   */
+  _warnRecursive?: boolean
+
   constructor(
     public fn: ComputedGetter<T>,
     private readonly setter: ComputedSetter<T> | undefined,

+ 1 - 0
packages/reactivity/src/index.ts

@@ -43,6 +43,7 @@ export {
   type WritableComputedOptions,
   type ComputedGetter,
   type ComputedSetter,
+  type ComputedRefImpl,
 } from './computed'
 export {
   effect,

+ 2 - 1
packages/reactivity/src/reactive.ts

@@ -13,6 +13,7 @@ import {
 } from './collectionHandlers'
 import type { RawSymbol, Ref, UnwrapRefSimple } from './ref'
 import { ReactiveFlags } from './constants'
+import { warn } from './warning'
 
 export interface Target {
   [ReactiveFlags.SKIP]?: boolean
@@ -247,7 +248,7 @@ function createReactiveObject(
 ) {
   if (!isObject(target)) {
     if (__DEV__) {
-      console.warn(`value cannot be made reactive: ${String(target)}`)
+      warn(`value cannot be made reactive: ${String(target)}`)
     }
     return target
   }

+ 2 - 1
packages/reactivity/src/ref.ts

@@ -17,6 +17,7 @@ import {
 } from './reactive'
 import type { ComputedRef } from './computed'
 import { TrackOpTypes, TriggerOpTypes } from './constants'
+import { warn } from './warning'
 
 declare const RefSymbol: unique symbol
 export declare const RawSymbol: unique symbol
@@ -319,7 +320,7 @@ export type ToRefs<T = any> = {
  */
 export function toRefs<T extends object>(object: T): ToRefs<T> {
   if (__DEV__ && !isProxy(object)) {
-    console.warn(`toRefs() expects a reactive object but received a plain one.`)
+    warn(`toRefs() expects a reactive object but received a plain one.`)
   }
   const ret: any = isArray(object) ? new Array(object.length) : {}
   for (const key in object) {

+ 5 - 10
packages/runtime-core/__tests__/apiCreateApp.spec.ts

@@ -143,10 +143,10 @@ describe('api: createApp', () => {
       },
       setup() {
         // resolve in setup
-        const FooBar = resolveComponent('foo-bar') as any
+        const FooBar = resolveComponent('foo-bar')
         return () => {
           // resolve in render
-          const BarBaz = resolveComponent('bar-baz') as any
+          const BarBaz = resolveComponent('bar-baz')
           return h('div', [h(FooBar), h(BarBaz)])
         }
       },
@@ -182,10 +182,10 @@ describe('api: createApp', () => {
       },
       setup() {
         // resolve in setup
-        const FooBar = resolveDirective('foo-bar')!
+        const FooBar = resolveDirective('foo-bar')
         return () => {
           // resolve in render
-          const BarBaz = resolveDirective('bar-baz')!
+          const BarBaz = resolveDirective('bar-baz')
           return withDirectives(h('div'), [[FooBar], [BarBaz]])
         }
       },
@@ -350,7 +350,7 @@ describe('api: createApp', () => {
 
     const handler = vi.fn((err, instance, info) => {
       expect(err).toBe(error)
-      expect((instance as any).count).toBe(count.value)
+      expect(instance.count).toBe(count.value)
       expect(info).toBe(`render function`)
     })
 
@@ -450,11 +450,6 @@ describe('api: createApp', () => {
       }
 
       const app = createApp(Root)
-      Object.defineProperty(app.config, 'isNativeTag', {
-        value: isNativeTag,
-        writable: false,
-      })
-
       app.mount(nodeOps.createElement('div'))
       expect(
         `Do not use built-in directive ids as custom directive id: bind`,

+ 0 - 2
packages/runtime-core/__tests__/apiOptions.spec.ts

@@ -790,10 +790,8 @@ describe('api: options', () => {
       data() {},
     }
     defineComponent({
-      // @ts-expect-error edge case after #7963, unlikely to happen in practice
       // since the user will want to type the mixins themselves.
       mixins: [defineComponent(MixinA), defineComponent(MixinB)],
-      // @ts-expect-error
       data() {},
     })
   })

+ 94 - 1
packages/runtime-core/__tests__/components/Suspense.spec.ts

@@ -22,7 +22,7 @@ import {
   watch,
   watchEffect,
 } from '@vue/runtime-test'
-import { createApp, defineComponent } from 'vue'
+import { computed, createApp, defineComponent, inject, provide } from 'vue'
 import type { RawSlots } from 'packages/runtime-core/src/componentSlots'
 import { resetSuspenseId } from '../../src/components/Suspense'
 
@@ -1039,6 +1039,99 @@ describe('Suspense', () => {
     expect(serializeInner(root)).toBe(`<div>foo<div>foo nested</div></div>`)
   })
 
+  // #10098
+  test('switching branches w/ nested suspense', async () => {
+    const RouterView = {
+      setup(_: any, { slots }: any) {
+        const route = inject('route') as any
+        const depth = inject('depth', 0)
+        provide('depth', depth + 1)
+        return () => {
+          const current = route.value[depth]
+          return slots.default({ Component: current })[0]
+        }
+      },
+    }
+
+    const OuterB = defineAsyncComponent({
+      setup: () => {
+        return () =>
+          h(RouterView, null, {
+            default: ({ Component }: any) => [
+              h(Suspense, null, {
+                default: () => h(Component),
+              }),
+            ],
+          })
+      },
+    })
+
+    const InnerB = defineAsyncComponent({
+      setup: () => {
+        return () => h('div', 'innerB')
+      },
+    })
+
+    const OuterA = defineAsyncComponent({
+      setup: () => {
+        return () =>
+          h(RouterView, null, {
+            default: ({ Component }: any) => [
+              h(Suspense, null, {
+                default: () => h(Component),
+              }),
+            ],
+          })
+      },
+    })
+
+    const InnerA = defineAsyncComponent({
+      setup: () => {
+        return () => h('div', 'innerA')
+      },
+    })
+
+    const toggle = ref(true)
+    const route = computed(() => {
+      return toggle.value ? [OuterA, InnerA] : [OuterB, InnerB]
+    })
+
+    const Comp = {
+      setup() {
+        provide('route', route)
+        return () =>
+          h(RouterView, null, {
+            default: ({ Component }: any) => [
+              h(Suspense, null, {
+                default: () => h(Component),
+              }),
+            ],
+          })
+      },
+    }
+
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+    await Promise.all(deps)
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<!---->`)
+
+    await Promise.all(deps)
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>innerA</div>`)
+
+    deps.length = 0
+
+    toggle.value = false
+    await nextTick()
+    // toggle again
+    toggle.value = true
+
+    await Promise.all(deps)
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>innerA</div>`)
+  })
+
   test('branch switch to 3rd branch before resolve', async () => {
     const calls: string[] = []
 

+ 2 - 2
packages/runtime-core/__tests__/hydration.spec.ts

@@ -26,7 +26,7 @@ import {
 } from '@vue/runtime-dom'
 import { type SSRContext, renderToString } from '@vue/server-renderer'
 import { PatchFlags } from '@vue/shared'
-import { vShowOldKey } from '../../runtime-dom/src/directives/vShow'
+import { vShowOriginalDisplay } from '../../runtime-dom/src/directives/vShow'
 
 function mountWithHydration(html: string, render: () => any) {
   const container = document.createElement('div')
@@ -1252,7 +1252,7 @@ describe('SSR hydration', () => {
         foo
       </div>
     `)
-    expect((container.firstChild as any)[vShowOldKey]).toBe('')
+    expect((container.firstChild as any)[vShowOriginalDisplay]).toBe('')
     expect(vnode.el).toBe(container.firstChild)
     expect(`mismatch`).not.toHaveBeenWarned()
   })

+ 1 - 1
packages/runtime-core/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vue/runtime-core",
-  "version": "3.4.19",
+  "version": "3.4.20",
   "description": "@vue/runtime-core",
   "main": "index.js",
   "module": "dist/runtime-core.esm-bundler.js",

+ 10 - 3
packages/runtime-core/src/apiComputed.ts

@@ -1,10 +1,17 @@
-import { computed as _computed } from '@vue/reactivity'
-import { isInSSRComponentSetup } from './component'
+import { type ComputedRefImpl, computed as _computed } from '@vue/reactivity'
+import { getCurrentInstance, isInSSRComponentSetup } from './component'
 
 export const computed: typeof _computed = (
   getterOrOptions: any,
   debugOptions?: any,
 ) => {
   // @ts-expect-error
-  return _computed(getterOrOptions, debugOptions, isInSSRComponentSetup)
+  const c = _computed(getterOrOptions, debugOptions, isInSSRComponentSetup)
+  if (__DEV__) {
+    const i = getCurrentInstance()
+    if (i && i.appContext.config.warnRecursiveComputed) {
+      ;(c as unknown as ComputedRefImpl<any>)._warnRecursive = true
+    }
+  }
+  return c
 }

+ 7 - 1
packages/runtime-core/src/apiCreateApp.ts

@@ -84,7 +84,7 @@ export type OptionMergeFunction = (to: unknown, from: unknown) => any
 
 export interface AppConfig {
   // @private
-  readonly isNativeTag?: (tag: string) => boolean
+  readonly isNativeTag: (tag: string) => boolean
 
   performance: boolean
   optionMergeStrategies: Record<string, OptionMergeFunction>
@@ -110,6 +110,12 @@ export interface AppConfig {
    * @deprecated use config.compilerOptions.isCustomElement
    */
   isCustomElement?: (tag: string) => boolean
+
+  /**
+   * TODO document for 3.5
+   * Enable warnings for computed getters that recursively trigger itself.
+   */
+  warnRecursiveComputed?: boolean
 }
 
 export interface AppContext {

+ 26 - 2
packages/runtime-core/src/apiDefineComponent.ts

@@ -89,6 +89,30 @@ export type DefineComponent<
   > &
   PP
 
+export type DefineSetupFnComponent<
+  P extends Record<string, any>,
+  E extends EmitsOptions = {},
+  S extends SlotsType = SlotsType,
+  Props = P & EmitsToProps<E>,
+  PP = PublicProps,
+> = new (
+  props: Props & PP,
+) => CreateComponentPublicInstance<
+  Props,
+  {},
+  {},
+  {},
+  {},
+  ComponentOptionsMixin,
+  ComponentOptionsMixin,
+  E,
+  PP,
+  {},
+  false,
+  {},
+  S
+>
+
 // defineComponent is a utility that is primarily used for type inference
 // when declaring components. Type inference is provided in the component
 // options (provided as the argument). The returned value has artificial types
@@ -111,7 +135,7 @@ export function defineComponent<
     emits?: E | EE[]
     slots?: S
   },
-): (props: Props & EmitsToProps<E>) => any
+): DefineSetupFnComponent<Props, E, S>
 export function defineComponent<
   Props extends Record<string, any>,
   E extends EmitsOptions = {},
@@ -127,7 +151,7 @@ export function defineComponent<
     emits?: E | EE[]
     slots?: S
   },
-): (props: Props & EmitsToProps<E>) => any
+): DefineSetupFnComponent<Props, E, S>
 
 // overload 2: object format with no props
 // (uses user defined props interface)

+ 5 - 4
packages/runtime-core/src/component.ts

@@ -61,7 +61,6 @@ import {
 import {
   EMPTY_OBJ,
   type IfAny,
-  NO,
   NOOP,
   ShapeFlags,
   extend,
@@ -712,9 +711,11 @@ export const unsetCurrentInstance = () => {
 
 const isBuiltInTag = /*#__PURE__*/ makeMap('slot,component')
 
-export function validateComponentName(name: string, config: AppConfig) {
-  const appIsNativeTag = config.isNativeTag || NO
-  if (isBuiltInTag(name) || appIsNativeTag(name)) {
+export function validateComponentName(
+  name: string,
+  { isNativeTag }: AppConfig,
+) {
+  if (isBuiltInTag(name) || isNativeTag(name)) {
     warn(
       'Do not use built-in or reserved HTML elements as component id: ' + name,
     )

+ 1 - 15
packages/runtime-core/src/components/BaseTransition.ts

@@ -144,8 +144,6 @@ const BaseTransitionImpl: ComponentOptions = {
     const instance = getCurrentInstance()!
     const state = useTransitionState()
 
-    let prevTransitionKey: any
-
     return () => {
       const children =
         slots.default && getTransitionRawChildren(slots.default(), true)
@@ -211,23 +209,11 @@ const BaseTransitionImpl: ComponentOptions = {
       const oldChild = instance.subTree
       const oldInnerChild = oldChild && getKeepAliveChild(oldChild)
 
-      let transitionKeyChanged = false
-      const { getTransitionKey } = innerChild.type as any
-      if (getTransitionKey) {
-        const key = getTransitionKey()
-        if (prevTransitionKey === undefined) {
-          prevTransitionKey = key
-        } else if (key !== prevTransitionKey) {
-          prevTransitionKey = key
-          transitionKeyChanged = true
-        }
-      }
-
       // handle mode
       if (
         oldInnerChild &&
         oldInnerChild.type !== Comment &&
-        (!isSameVNodeType(innerChild, oldInnerChild) || transitionKeyChanged)
+        !isSameVNodeType(innerChild, oldInnerChild)
       ) {
         const leavingHooks = resolveTransitionHooks(
           oldInnerChild,

+ 3 - 1
packages/runtime-core/src/components/Suspense.ts

@@ -100,7 +100,9 @@ export const SuspenseImpl = {
       // it is necessary to skip the current patch to avoid multiple mounts
       // of inner components.
       if (parentSuspense && parentSuspense.deps > 0) {
-        n2.suspense = n1.suspense
+        n2.suspense = n1.suspense!
+        n2.suspense.vnode = n2
+        n2.el = n1.el
         return
       }
       patchSuspense(

+ 5 - 1
packages/runtime-core/src/index.ts

@@ -250,7 +250,11 @@ export type {
   AllowedComponentProps,
   ComponentInstance,
 } from './component'
-export type { DefineComponent, PublicProps } from './apiDefineComponent'
+export type {
+  DefineComponent,
+  DefineSetupFnComponent,
+  PublicProps,
+} from './apiDefineComponent'
 export type {
   ComponentOptions,
   ComponentOptionsMixin,

+ 9 - 0
packages/runtime-dom/__tests__/patchStyle.spec.ts

@@ -158,4 +158,13 @@ describe(`runtime-dom: style patching`, () => {
     )
     expect(el.style.display).toBe('flex')
   })
+
+  it('should clear previous css string value', () => {
+    const el = document.createElement('div')
+    patchProp(el, 'style', {}, 'color:red')
+    expect(el.style.cssText.replace(/\s/g, '')).toBe('color:red;')
+
+    patchProp(el, 'style', 'color:red', { fontSize: '12px' })
+    expect(el.style.cssText.replace(/\s/g, '')).toBe('font-size:12px;')
+  })
 })

+ 1 - 1
packages/runtime-dom/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vue/runtime-dom",
-  "version": "3.4.19",
+  "version": "3.4.20",
   "description": "@vue/runtime-dom",
   "main": "index.js",
   "module": "dist/runtime-dom.esm-bundler.js",

+ 8 - 15
packages/runtime-dom/src/directives/vModel.ts

@@ -209,25 +209,20 @@ export const vModelSelect: ModelDirective<HTMLSelectElement> = {
   },
   // set value in mounted & updated because <select> relies on its children
   // <option>s.
-  mounted(el, { value, oldValue, modifiers: { number } }) {
-    setSelected(el, value, oldValue, number)
+  mounted(el, { value, modifiers: { number } }) {
+    setSelected(el, value, number)
   },
   beforeUpdate(el, _binding, vnode) {
     el[assignKey] = getModelAssigner(vnode)
   },
-  updated(el, { value, oldValue, modifiers: { number } }) {
+  updated(el, { value, modifiers: { number } }) {
     if (!el._assigning) {
-      setSelected(el, value, oldValue, number)
+      setSelected(el, value, number)
     }
   },
 }
 
-function setSelected(
-  el: HTMLSelectElement,
-  value: any,
-  oldValue: any,
-  number: boolean,
-) {
+function setSelected(el: HTMLSelectElement, value: any, number: boolean) {
   const isMultiple = el.multiple
   const isArrayValue = isArray(value)
   if (isMultiple && !isArrayValue && !isSet(value)) {
@@ -256,11 +251,9 @@ function setSelected(
       } else {
         option.selected = value.has(optionValue)
       }
-    } else {
-      if (looseEqual(getValue(option), value)) {
-        if (el.selectedIndex !== i) el.selectedIndex = i
-        return
-      }
+    } else if (looseEqual(getValue(option), value)) {
+      if (el.selectedIndex !== i) el.selectedIndex = i
+      return
     }
   }
   if (!isMultiple && el.selectedIndex !== -1) {

+ 10 - 10
packages/runtime-dom/src/directives/vShow.ts

@@ -1,15 +1,18 @@
 import type { ObjectDirective } from '@vue/runtime-core'
 
-export const vShowOldKey = Symbol('_vod')
+export const vShowOriginalDisplay = Symbol('_vod')
+export const vShowHidden = Symbol('_vsh')
 
-interface VShowElement extends HTMLElement {
+export interface VShowElement extends HTMLElement {
   // _vod = vue original display
-  [vShowOldKey]: string
+  [vShowOriginalDisplay]: string
+  [vShowHidden]: boolean
 }
 
 export const vShow: ObjectDirective<VShowElement> & { name?: 'show' } = {
   beforeMount(el, { value }, { transition }) {
-    el[vShowOldKey] = el.style.display === 'none' ? '' : el.style.display
+    el[vShowOriginalDisplay] =
+      el.style.display === 'none' ? '' : el.style.display
     if (transition && value) {
       transition.beforeEnter(el)
     } else {
@@ -22,11 +25,7 @@ export const vShow: ObjectDirective<VShowElement> & { name?: 'show' } = {
     }
   },
   updated(el, { value, oldValue }, { transition }) {
-    if (
-      !value === !oldValue &&
-      (el.style.display === el[vShowOldKey] || !value)
-    )
-      return
+    if (!value === !oldValue) return
     if (transition) {
       if (value) {
         transition.beforeEnter(el)
@@ -51,7 +50,8 @@ if (__DEV__) {
 }
 
 function setDisplay(el: VShowElement, value: unknown): void {
-  el.style.display = value ? el[vShowOldKey] : 'none'
+  el.style.display = value ? el[vShowOriginalDisplay] : 'none'
+  el[vShowHidden] = !value
 }
 
 // SSR vnode transforms, only used when user includes client-oriented render

+ 2 - 1
packages/runtime-dom/src/modules/props.ts

@@ -39,7 +39,8 @@ export function patchDOMProp(
     el._value = value
     // #4956: <option> value will fallback to its text content so we need to
     // compare against its attribute value instead.
-    const oldValue = tag === 'OPTION' ? el.getAttribute('value') : el.value
+    const oldValue =
+      tag === 'OPTION' ? el.getAttribute('value') || '' : el.value
     const newValue = value == null ? '' : value
     if (oldValue !== newValue) {
       el.value = newValue

+ 26 - 12
packages/runtime-dom/src/modules/style.ts

@@ -1,6 +1,10 @@
 import { capitalize, hyphenate, isArray, isString } from '@vue/shared'
 import { camelize, warn } from '@vue/runtime-core'
-import { vShowOldKey } from '../directives/vShow'
+import {
+  type VShowElement,
+  vShowHidden,
+  vShowOriginalDisplay,
+} from '../directives/vShow'
 import { CSS_VAR_TEXT } from '../helpers/useCssVars'
 
 type Style = string | Record<string, string | string[]> | null
@@ -10,13 +14,21 @@ const displayRE = /(^|;)\s*display\s*:/
 export function patchStyle(el: Element, prev: Style, next: Style) {
   const style = (el as HTMLElement).style
   const isCssString = isString(next)
-  const currentDisplay = style.display
   let hasControlledDisplay = false
   if (next && !isCssString) {
-    if (prev && !isString(prev)) {
-      for (const key in prev) {
-        if (next[key] == null) {
-          setStyle(style, key, '')
+    if (prev) {
+      if (!isString(prev)) {
+        for (const key in prev) {
+          if (next[key] == null) {
+            setStyle(style, key, '')
+          }
+        }
+      } else {
+        for (const prevStyle of prev.split(';')) {
+          const key = prevStyle.slice(0, prevStyle.indexOf(':')).trim()
+          if (next[key] == null) {
+            setStyle(style, key, '')
+          }
         }
       }
     }
@@ -41,12 +53,14 @@ export function patchStyle(el: Element, prev: Style, next: Style) {
       el.removeAttribute('style')
     }
   }
-  // indicates that the `display` of the element is controlled by `v-show`,
-  // so we always keep the current `display` value regardless of the `style`
-  // value, thus handing over control to `v-show`.
-  if (vShowOldKey in el) {
-    el[vShowOldKey] = hasControlledDisplay ? style.display : ''
-    style.display = currentDisplay
+  // indicates the element also has `v-show`.
+  if (vShowOriginalDisplay in el) {
+    // make v-show respect the current v-bind style display when shown
+    el[vShowOriginalDisplay] = hasControlledDisplay ? style.display : ''
+    // if v-show is in hidden state, v-show has higher priority
+    if ((el as VShowElement)[vShowHidden]) {
+      style.display = 'none'
+    }
   }
 }
 

+ 1 - 1
packages/server-renderer/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vue/server-renderer",
-  "version": "3.4.19",
+  "version": "3.4.20",
   "description": "@vue/server-renderer",
   "main": "index.js",
   "module": "dist/server-renderer.esm-bundler.js",

+ 0 - 2
packages/sfc-playground/index.html

@@ -7,8 +7,6 @@
     <link rel="icon" type="image/svg" href="/logo.svg" />
     <title>Vue SFC Playground</title>
     <script>
-      // process shim for old versions of @vue/compiler-sfc dependency
-       window.process = { env: {} }
        const savedPreferDark = localStorage.getItem('vue-sfc-playground-prefer-dark')
        if (
          savedPreferDark === 'true' ||

+ 3 - 3
packages/sfc-playground/package.json

@@ -9,11 +9,11 @@
     "serve": "vite preview"
   },
   "devDependencies": {
-    "@vitejs/plugin-vue": "^5.0.3",
-    "vite": "^5.0.12"
+    "@vitejs/plugin-vue": "^5.0.4",
+    "vite": "^5.1.4"
   },
   "dependencies": {
-    "@vue/repl": "^3.1.1",
+    "@vue/repl": "^4.1.1",
     "file-saver": "^2.0.5",
     "jszip": "^3.10.1",
     "vue": "workspace:*"

+ 51 - 62
packages/sfc-playground/src/App.vue

@@ -1,22 +1,8 @@
 <script setup lang="ts">
 import Header from './Header.vue'
-import { Repl, ReplStore, SFCOptions } from '@vue/repl'
-import type Monaco from '@vue/repl/monaco-editor'
-import type CodeMirror from '@vue/repl/codemirror-editor'
-import { ref, watchEffect, onMounted } from 'vue'
-import { shallowRef } from 'vue'
-
-const EditorComponent = shallowRef<typeof Monaco | typeof CodeMirror>()
-
-if (import.meta.env.DEV) {
-  import('@vue/repl/codemirror-editor').then(
-    mod => (EditorComponent.value = mod.default),
-  )
-} else {
-  import('@vue/repl/monaco-editor').then(
-    mod => (EditorComponent.value = mod.default),
-  )
-}
+import { Repl, useStore, SFCOptions, useVueImportMap } from '@vue/repl'
+import Monaco from '@vue/repl/monaco-editor'
+import { ref, watchEffect, onMounted, computed } from 'vue'
 
 const replRef = ref<InstanceType<typeof Repl>>()
 
@@ -26,78 +12,80 @@ const setVH = () => {
 window.addEventListener('resize', setVH)
 setVH()
 
-const useProdMode = ref(false)
 const useSSRMode = ref(false)
 
+const { productionMode, vueVersion, importMap } = useVueImportMap({
+  runtimeDev: import.meta.env.PROD
+    ? `${location.origin}/vue.runtime.esm-browser.js`
+    : `${location.origin}/src/vue-dev-proxy`,
+  runtimeProd: import.meta.env.PROD
+    ? `${location.origin}/vue.runtime.esm-browser.prod.js`
+    : `${location.origin}/src/vue-dev-proxy-prod`,
+  serverRenderer: import.meta.env.PROD
+    ? `${location.origin}/server-renderer.esm-browser.js`
+    : `${location.origin}/src/vue-server-renderer-dev-proxy`,
+})
+
 let hash = location.hash.slice(1)
 if (hash.startsWith('__DEV__')) {
   hash = hash.slice(7)
-  useProdMode.value = false
+  productionMode.value = false
 }
 if (hash.startsWith('__PROD__')) {
   hash = hash.slice(8)
-  useProdMode.value = true
+  productionMode.value = true
 }
 if (hash.startsWith('__SSR__')) {
   hash = hash.slice(7)
   useSSRMode.value = true
 }
 
-const store = new ReplStore({
-  serializedState: hash,
-  productionMode: useProdMode.value,
-  defaultVueRuntimeURL: import.meta.env.PROD
-    ? `${location.origin}/vue.runtime.esm-browser.js`
-    : `${location.origin}/src/vue-dev-proxy`,
-  defaultVueRuntimeProdURL: import.meta.env.PROD
-    ? `${location.origin}/vue.runtime.esm-browser.prod.js`
-    : `${location.origin}/src/vue-dev-proxy-prod`,
-  defaultVueServerRendererURL: import.meta.env.PROD
-    ? `${location.origin}/server-renderer.esm-browser.js`
-    : `${location.origin}/src/vue-server-renderer-dev-proxy`,
-})
-
 // enable experimental features
-const sfcOptions: SFCOptions = {
-  script: {
-    inlineTemplate: useProdMode.value,
-    isProd: useProdMode.value,
-    propsDestructure: true,
-  },
-  style: {
-    isProd: useProdMode.value,
-  },
-  template: {
-    isProd: useProdMode.value,
-    compilerOptions: {
-      isCustomElement: (tag: string) => tag === 'mjx-container',
+const sfcOptions = computed(
+  (): SFCOptions => ({
+    script: {
+      inlineTemplate: productionMode.value,
+      isProd: productionMode.value,
+      propsDestructure: true,
+    },
+    style: {
+      isProd: productionMode.value,
+    },
+    template: {
+      isProd: productionMode.value,
+      compilerOptions: {
+        isCustomElement: (tag: string) => tag === 'mjx-container',
+      },
     },
+  }),
+)
+
+const store = useStore(
+  {
+    builtinImportMap: importMap,
+    vueVersion,
+    sfcOptions,
   },
-}
+  hash,
+)
+// @ts-expect-error
+globalThis.store = store
 
 // persist state
 watchEffect(() => {
   const newHash = store
     .serialize()
     .replace(/^#/, useSSRMode.value ? `#__SSR__` : `#`)
-    .replace(/^#/, useProdMode.value ? `#__PROD__` : `#`)
+    .replace(/^#/, productionMode.value ? `#__PROD__` : `#`)
   history.replaceState({}, '', newHash)
 })
 
 function toggleProdMode() {
-  const isProd = (useProdMode.value = !useProdMode.value)
-  sfcOptions.script!.inlineTemplate =
-    sfcOptions.script!.isProd =
-    sfcOptions.template!.isProd =
-    sfcOptions.style!.isProd =
-      isProd
-  store.toggleProduction()
-  store.setFiles(store.getFiles())
+  productionMode.value = !productionMode.value
 }
 
 function toggleSSR() {
   useSSRMode.value = !useSSRMode.value
-  store.setFiles(store.getFiles())
 }
 
 function reloadPage() {
@@ -111,13 +99,16 @@ function toggleTheme(isDark: boolean) {
 onMounted(() => {
   const cls = document.documentElement.classList
   toggleTheme(cls.contains('dark'))
+
+  // @ts-expect-error process shim for old versions of @vue/compiler-sfc dependency
+  window.process = { env: {} }
 })
 </script>
 
 <template>
   <Header
     :store="store"
-    :prod="useProdMode"
+    :prod="productionMode"
     :ssr="useSSRMode"
     @toggle-theme="toggleTheme"
     @toggle-prod="toggleProdMode"
@@ -125,17 +116,15 @@ onMounted(() => {
     @reload-page="reloadPage"
   />
   <Repl
-    v-if="EditorComponent"
     ref="replRef"
     :theme="theme"
-    :editor="EditorComponent"
+    :editor="Monaco"
     @keydown.ctrl.s.prevent
     @keydown.meta.s.prevent
     :ssr="useSSRMode"
     :store="store"
     :showCompileOutput="true"
     :autoResize="true"
-    :sfcOptions="sfcOptions"
     :clearConsole="false"
     :preview-options="{
       customCode: {

+ 11 - 14
packages/sfc-playground/src/Header.vue

@@ -1,13 +1,13 @@
 <script setup lang="ts">
+import { computed } from 'vue'
+import type { ReplStore } from '@vue/repl'
 import { downloadProject } from './download/download'
-import { ref } from 'vue'
 import Sun from './icons/Sun.vue'
 import Moon from './icons/Moon.vue'
 import Share from './icons/Share.vue'
 import Download from './icons/Download.vue'
 import GitHub from './icons/GitHub.vue'
 import Reload from './icons/Reload.vue'
-import type { ReplStore } from '@vue/repl'
 import VersionSelect from './VersionSelect.vue'
 
 const props = defineProps<{
@@ -25,23 +25,20 @@ const emit = defineEmits([
 const { store } = props
 
 const currentCommit = __COMMIT__
-const vueVersion = ref(`@${currentCommit}`)
 
-const vueURL = store.getImportMap().imports.vue
-if (vueURL && !vueURL.startsWith(location.origin)) {
-  const versionMatch = vueURL.match(/runtime-dom@([^/]+)/)
-  if (versionMatch) vueVersion.value = versionMatch[1]
-}
+const vueVersion = computed(() => {
+  if (store.loading) {
+    return 'loading...'
+  }
+  return store.vueVersion || `@${__COMMIT__}`
+})
 
 async function setVueVersion(v: string) {
-  vueVersion.value = `loading...`
-  await store.setVueVersion(v)
-  vueVersion.value = v
+  store.vueVersion = v
 }
 
 function resetVueVersion() {
-  store.resetVueVersion()
-  vueVersion.value = `@${currentCommit}`
+  store.vueVersion = null
 }
 
 async function copyLink(e: MouseEvent) {
@@ -73,7 +70,7 @@ function toggleDark() {
     </h1>
     <div class="links">
       <VersionSelect
-        v-model="store.state.typescriptVersion"
+        v-model="store.typescriptVersion"
         pkg="typescript"
         label="TypeScript Version"
       />

+ 2 - 2
packages/sfc-playground/src/download/template/package.json

@@ -11,7 +11,7 @@
     "vue": "^3.4.0"
   },
   "devDependencies": {
-    "@vitejs/plugin-vue": "^5.0.3",
-    "vite": "^5.0.12"
+    "@vitejs/plugin-vue": "^5.0.4",
+    "vite": "^5.1.4"
   }
 }

+ 1 - 1
packages/shared/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vue/shared",
-  "version": "3.4.19",
+  "version": "3.4.20",
   "description": "internal utils shared across @vue packages",
   "main": "index.js",
   "module": "dist/shared.esm-bundler.js",

+ 1 - 1
packages/template-explorer/package.json

@@ -11,7 +11,7 @@
     "enableNonBrowserBranches": true
   },
   "dependencies": {
-    "monaco-editor": "^0.45.0",
+    "monaco-editor": "^0.46.0",
     "source-map-js": "^1.0.2"
   }
 }

+ 1 - 1
packages/vue-compat/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vue/compat",
-  "version": "3.4.19",
+  "version": "3.4.20",
   "description": "Vue 3 compatibility build for Vue 2",
   "main": "index.js",
   "module": "dist/vue.runtime.esm-bundler.js",

+ 1 - 1
packages/vue/__tests__/e2e/e2eUtils.ts

@@ -9,7 +9,7 @@ export const E2E_TIMEOUT = 30 * 1000
 
 const puppeteerOptions: PuppeteerLaunchOptions = {
   args: process.env.CI ? ['--no-sandbox', '--disable-setuid-sandbox'] : [],
-  headless: 'new',
+  headless: true,
 }
 
 const maxTries = 30

+ 1 - 1
packages/vue/package.json

@@ -1,6 +1,6 @@
 {
   "name": "vue",
-  "version": "3.4.19",
+  "version": "3.4.20",
   "description": "The progressive JavaScript framework for building modern web UI.",
   "main": "index.js",
   "module": "dist/vue.runtime.esm-bundler.js",

File diff suppressed because it is too large
+ 214 - 217
pnpm-lock.yaml


+ 2 - 0
rollup.config.js

@@ -143,6 +143,8 @@ function createConfig(format, output, plugins = []) {
   }
   output.sourcemap = !!process.env.SOURCE_MAP
   output.externalLiveBindings = false
+  // https://github.com/rollup/rollup/pull/5380
+  output.reexportProtoFromExternal = false
 
   if (isGlobalBuild) {
     output.name = packageOptions.name

+ 1 - 1
tsconfig.json

@@ -18,7 +18,7 @@
     "esModuleInterop": true,
     "removeComments": false,
     "jsx": "preserve",
-    "lib": ["esnext", "dom"],
+    "lib": ["es2016", "dom"],
     "types": ["vitest/globals", "puppeteer", "node"],
     "rootDir": ".",
     "paths": {

Some files were not shown because too many files changed in this diff