Pārlūkot izejas kodu

chore: Merge branch 'main' into minor

daiwei 1 nedēļu atpakaļ
vecāks
revīzija
fe0c915b99

+ 1 - 1
changelogs/CHANGELOG-3.0.md

@@ -132,7 +132,7 @@
 - **runtime-dom:** ensure readonly type prop on textarea is handled patched as attribute ([#2888](https://github.com/vuejs/core/issues/2888)) ([c5d147c](https://github.com/vuejs/core/commit/c5d147c57f75ca38cc334bb27b61a8bc153494bd)), closes [#2766](https://github.com/vuejs/core/issues/2766)
 - kebab-case events are attached correctly on web components, see [#2841](https://github.com/vuejs/core/issues/2841) ([#2847](https://github.com/vuejs/core/issues/2847)) ([b302cbb](https://github.com/vuejs/core/commit/b302cbbbd3fd512f2b8afbd9c873060a40bf8e62))
 - **types:** extract the correct props type for the DateConstructor ([#2676](https://github.com/vuejs/core/issues/2676)) ([48f0d29](https://github.com/vuejs/core/commit/48f0d2944f0f9d2f556e62782fc61985897b2ed4))
-- ensure all published packages contan a LICENCE file (close [#2650](https://github.com/vuejs/core/issues/2650)) ([#2857](https://github.com/vuejs/core/issues/2857)) ([6a48d23](https://github.com/vuejs/core/commit/6a48d23749e418b44ba17cd3e85f478484fd7ffe))
+- ensure all published packages contain a LICENCE file (close [#2650](https://github.com/vuejs/core/issues/2650)) ([#2857](https://github.com/vuejs/core/issues/2857)) ([6a48d23](https://github.com/vuejs/core/commit/6a48d23749e418b44ba17cd3e85f478484fd7ffe))
 - remove superfluous spaces when normalizing class ([#3083](https://github.com/vuejs/core/issues/3083)) ([4b55142](https://github.com/vuejs/core/commit/4b551420fc058c4683219db5d75893f9fc69aa04))
 - **runtime-dom:** enable set form attr to null on form-elements ([#2840](https://github.com/vuejs/core/issues/2840)) ([#2849](https://github.com/vuejs/core/issues/2849)) ([f262438](https://github.com/vuejs/core/commit/f2624380731cc32e71523e8c2c98037e98e09319))
 - **toRef:** ref created from union typed prop can't be used in watch ([#3048](https://github.com/vuejs/core/issues/3048)) ([4ca4666](https://github.com/vuejs/core/commit/4ca4666d58ee8025570dc14f1c163bdeac9c6012))

+ 1 - 1
changelogs/CHANGELOG-3.1.md

@@ -45,7 +45,7 @@
 - **compiler-sfc:** support method signature in defineProps ([afdd2f2](https://github.com/vuejs/core/commit/afdd2f28354ce8cea647279ed25d61e7b9946cf5)), closes [#2983](https://github.com/vuejs/core/issues/2983)
 - **compiler-sfc:** support TS runtime enum in `<script setup>` ([1ffd48a](https://github.com/vuejs/core/commit/1ffd48a2f5fd3eead3ea29dae668b7ed1c6f6130))
 - **runtime-core:** add missing serverPrefetch hook error string ([#4014](https://github.com/vuejs/core/issues/4014)) ([d069796](https://github.com/vuejs/core/commit/d069796b8f0cf8df9aa77d781c4b5429b9411204))
-- **runtime-core:** fix mouting of detached static vnode ([fded1e8](https://github.com/vuejs/core/commit/fded1e8dfa22ca7fecd300c4cbffd6a37b887be8)), closes [#4023](https://github.com/vuejs/core/issues/4023)
+- **runtime-core:** fix mounting of detached static vnode ([fded1e8](https://github.com/vuejs/core/commit/fded1e8dfa22ca7fecd300c4cbffd6a37b887be8)), closes [#4023](https://github.com/vuejs/core/issues/4023)
 - **runtime-dom:** fix static node content caching edge cases ([ba89ca9](https://github.com/vuejs/core/commit/ba89ca9ecafe86292e3adf751671ed5e9ca6e928)), closes [#4023](https://github.com/vuejs/core/issues/4023) [#4031](https://github.com/vuejs/core/issues/4031) [#4037](https://github.com/vuejs/core/issues/4037)
 - **sfc:** allow variables that start with \_ or $ in `<script setup>` ([0b8b576](https://github.com/vuejs/core/commit/0b8b5764287b4814a37034ad4bc6f2b8ac8f8700))
 - **ssr:** ensure behavior consistency between prod/dev when mounting SSR app to empty containers ([33708e8](https://github.com/vuejs/core/commit/33708e8bf44a037070af5c8eabdfe1ccad22bbc2)), closes [#4034](https://github.com/vuejs/core/issues/4034)

+ 17 - 0
changelogs/CHANGELOG-3.5.md

@@ -1,3 +1,20 @@
+## [3.5.32](https://github.com/vuejs/core/compare/v3.5.31...v3.5.32) (2026-04-03)
+
+
+### Bug Fixes
+
+* **runtime-core:** prevent currentInstance leak into sibling render during async setup re-entry ([#14668](https://github.com/vuejs/core/issues/14668)) ([f166353](https://github.com/vuejs/core/commit/f1663535a163057788d3285dec54a245c3efb3ad)), closes [#14667](https://github.com/vuejs/core/issues/14667)
+* **teleport:** handle updates before deferred mount ([#14642](https://github.com/vuejs/core/issues/14642)) ([32b44f1](https://github.com/vuejs/core/commit/32b44f19f67aa30899817a7e79a4510f3b52970a)), closes [#14640](https://github.com/vuejs/core/issues/14640)
+* **types:** allow customRef to have different getter/setter types ([#14639](https://github.com/vuejs/core/issues/14639)) ([e20ddb0](https://github.com/vuejs/core/commit/e20ddb00188e9935884930046fa572eab7c9dcba))
+* **types:** use private branding for shallowReactive ([#14641](https://github.com/vuejs/core/issues/14641)) ([302c47a](https://github.com/vuejs/core/commit/302c47a4994bc8b47b8a2af6693d8cb6bbd4b06b)), closes [#14638](https://github.com/vuejs/core/issues/14638) [#14493](https://github.com/vuejs/core/issues/14493)
+
+
+### Reverts
+
+* Revert "fix(server-renderer): cleanup component effect scopes after SSR render" (#14674) ([219d83b](https://github.com/vuejs/core/commit/219d83bd305ce6fc052941acaaf02e7bc70616a4)), closes [#14674](https://github.com/vuejs/core/issues/14674) [#14669](https://github.com/vuejs/core/issues/14669)
+
+
+
 ## [3.5.31](https://github.com/vuejs/core/compare/v3.5.30...v3.5.31) (2026-03-25)
 
 

+ 8 - 7
package.json

@@ -1,7 +1,7 @@
 {
   "private": true,
   "version": "3.6.0-beta.9",
-  "packageManager": "pnpm@10.28.2",
+  "packageManager": "pnpm@10.33.0",
   "type": "module",
   "scripts": {
     "dev": "node scripts/dev.js",
@@ -53,17 +53,18 @@
     "@babel/types": "catalog:",
     "@rolldown/plugin-node-polyfills": "^1.0.3",
     "@types/hash-sum": "^1.0.2",
-    "@types/node": "^24.12.0",
+    "@types/node": "^24.12.2",
     "@types/semver": "^7.7.1",
     "@types/serve-handler": "^6.1.4",
-    "@vitest/coverage-v8": "^4.1.1",
-    "@vitest/ui": "^4.1.1",
+    "@vitest/coverage-v8": "^4.1.3",
+    "@vitest/ui": "^4.1.3",
     "@vue/consolidate": "1.0.0",
-    "conventional-changelog-cli": "^5.0.0",
+    "conventional-changelog": "^7.2.0",
+    "conventional-changelog-angular": "^8.3.1",
     "enquirer": "^2.4.1",
     "estree-walker": "catalog:",
     "fast-glob": "^3.3.2",
-    "jsdom": "^29.0.1",
+    "jsdom": "^29.0.2",
     "lodash": "^4.17.23",
     "magic-string": "^0.30.21",
     "markdown-table": "^3.0.4",
@@ -88,6 +89,6 @@
     "typescript": "~5.6.2",
     "vite": "catalog:",
     "vitest": "npm:@voidzero-dev/vite-plus-test@latest",
-    "vite-plus": "^0.1.9"
+    "vite-plus": "^0.1.16"
   }
 }

+ 0 - 1
packages-private/benchmark/client/profiling.ts

@@ -1,5 +1,4 @@
 /* oxlint-disable no-console */
-/* oxlint-disable no-restricted-syntax */
 /* oxlint-disable no-restricted-globals */
 import { nextTick } from 'vue'
 

+ 6 - 0
packages-private/dts-test/reactivity.test-d.ts

@@ -140,3 +140,9 @@ describe('shallowReactive marker should not leak into value unions', () => {
   const value = {} as (typeof state)[keyof typeof state]
   expectType<string>(value.title)
 })
+
+describe('shallowReactive type should accept plain object assignment', () => {
+  const shallow = shallowReactive({ a: 1, b: 2 })
+  let values: typeof shallow
+  values = { a: 1, b: 2 }
+})

+ 35 - 0
packages-private/dts-test/ref.test-d.ts

@@ -8,6 +8,7 @@ import {
   type ToRefs,
   type WritableComputedRef,
   computed,
+  customRef,
   isRef,
   proxyRefs,
   reactive,
@@ -320,6 +321,12 @@ expectType<undefined>(p2.u)
 expectType<Ref<string>>(p2.obj.k)
 expectType<{ name: string } | null>(p2.union)
 
+const r3 = shallowReactive({
+  n: ref(1),
+})
+const p3 = proxyRefs(r3)
+expectType<Ref<number>>(p3.n)
+
 // toRef and toRefs
 {
   const obj: {
@@ -436,6 +443,15 @@ describe('shallow reactive in reactive', () => {
   expectType<number>(foo.value.a.b.value)
 })
 
+describe('shallow reactive collection in reactive', () => {
+  const baz = reactive({
+    foo: shallowReactive(new Map([['a', ref(42)]])),
+  })
+
+  const foo = toRef(baz, 'foo')
+  expectType<Ref<number> | undefined>(foo.value.get('a'))
+})
+
 describe('shallow ref in reactive', () => {
   const x = reactive({
     foo: shallowRef({
@@ -548,3 +564,22 @@ expectType<TemplateRef>(tRef)
 
 const tRef2 = useTemplateRef<HTMLElement>('bar')
 expectType<TemplateRef<HTMLElement>>(tRef2)
+
+// #14637 customRef with different getter/setter types
+describe('customRef with different getter/setter types', () => {
+  // customRef should support different getter/setter types like Ref<T, S>
+  const cr = customRef<string, number>((track, trigger) => ({
+    get: () => 'hello',
+    set: (val: number) => {
+      // setter accepts number, getter returns string
+      trigger()
+    },
+  }))
+
+  // getter returns string
+  expectType<string>(cr.value)
+  // setter accepts number
+  cr.value = 123
+  // @ts-expect-error setter doesn't accept string
+  cr.value = 'world'
+})

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

@@ -59,7 +59,7 @@
     "hash-sum": "^2.0.0",
     "lru-cache": "10.1.0",
     "merge-source-map": "^1.1.0",
-    "minimatch": "~10.2.4",
+    "minimatch": "~10.2.5",
     "postcss-modules": "^6.0.1",
     "postcss-selector-parser": "^7.1.1",
     "pug": "^3.0.4",

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

@@ -104,9 +104,16 @@ export function reactive(target: object) {
   )
 }
 
-export declare const ShallowReactiveMarker: unique symbol
+// Use a private class brand instead of a marker property so shallow-reactive
+// types remain distinguishable in `UnwrapRef` without leaking the brand into
+// `keyof`/indexed access types or requiring the property for plain assignment.
+declare class ShallowReactiveBrandClass {
+  private __shallowReactiveBrand?: never
+}
+
+export type ShallowReactiveBrand = ShallowReactiveBrandClass
 
-export type ShallowReactive<T> = T & { [ShallowReactiveMarker]: never }
+export type ShallowReactive<T> = T & ShallowReactiveBrand
 
 /**
  * Shallow version of {@link reactive}.

+ 31 - 26
packages/reactivity/src/ref.ts

@@ -13,7 +13,7 @@ import { onTrack, triggerEventInfos } from './debug'
 import { getDepFromReactive } from './dep'
 import {
   type Builtin,
-  type ShallowReactiveMarker,
+  type ShallowReactiveBrand,
   type Target,
   isProxy,
   isReactive,
@@ -335,27 +335,27 @@ export function proxyRefs<T extends object>(
     : new Proxy(objectWithRefs, shallowUnwrapHandlers)
 }
 
-export type CustomRefFactory<T> = (
+export type CustomRefFactory<T, S = T> = (
   track: () => void,
   trigger: () => void,
 ) => {
   get: () => T
-  set: (value: T) => void
+  set: (value: S) => void
 }
 
-class CustomRefImpl<T> implements ReactiveNode {
+class CustomRefImpl<T, S = T> implements ReactiveNode {
   subs: Link | undefined = undefined
   subsTail: Link | undefined = undefined
   flags: _ReactiveFlags = _ReactiveFlags.None
 
-  private readonly _get: ReturnType<CustomRefFactory<T>>['get']
-  private readonly _set: ReturnType<CustomRefFactory<T>>['set']
+  private readonly _get: ReturnType<CustomRefFactory<T, S>>['get']
+  private readonly _set: ReturnType<CustomRefFactory<T, S>>['set']
 
   public readonly [ReactiveFlags.IS_REF] = true
 
   public _value: T = undefined!
 
-  constructor(factory: CustomRefFactory<T>) {
+  constructor(factory: CustomRefFactory<T, S>) {
     const { get, set } = factory(
       () => trackRef(this),
       () => triggerRef(this as unknown as Ref),
@@ -368,11 +368,11 @@ class CustomRefImpl<T> implements ReactiveNode {
     return this
   }
 
-  get value() {
+  get value(): T {
     return (this._value = this._get())
   }
 
-  set value(newVal) {
+  set value(newVal: S) {
     this._set(newVal)
   }
 }
@@ -384,7 +384,9 @@ class CustomRefImpl<T> implements ReactiveNode {
  * @param factory - The function that receives the `track` and `trigger` callbacks.
  * @see {@link https://vuejs.org/api/reactivity-advanced.html#customref}
  */
-export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
+export function customRef<T, S = T>(
+  factory: CustomRefFactory<T, S>,
+): Ref<T, S> {
   return new CustomRefImpl(factory) as any
 }
 
@@ -592,9 +594,11 @@ function propertyToRef(
  */
 export interface RefUnwrapBailTypes {}
 
-export type ShallowUnwrapRef<T> = {
-  [K in keyof T]: DistributeRef<T[K]>
-}
+export type ShallowUnwrapRef<T> = T extends ShallowReactiveBrand
+  ? T
+  : {
+      [K in keyof T]: DistributeRef<T[K]>
+    }
 
 type DistributeRef<T> = T extends Ref<infer V, unknown> ? V : T
 
@@ -611,19 +615,20 @@ export type UnwrapRefSimple<T> = T extends
   | RefUnwrapBailTypes[keyof RefUnwrapBailTypes]
   | { [RawSymbol]?: true }
   ? T
-  : T extends Map<infer K, infer V>
-    ? Map<K, UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Map<any, any>>>
-    : T extends WeakMap<infer K, infer V>
-      ? WeakMap<K, UnwrapRefSimple<V>> &
-          UnwrapRef<Omit<T, keyof WeakMap<any, any>>>
-      : T extends Set<infer V>
-        ? Set<UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Set<any>>>
-        : T extends WeakSet<infer V>
-          ? WeakSet<UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof WeakSet<any>>>
-          : T extends ReadonlyArray<any>
-            ? { [K in keyof T]: UnwrapRefSimple<T[K]> }
-            : T extends object & { [ShallowReactiveMarker]: never }
-              ? T
+  : T extends ShallowReactiveBrand
+    ? T
+    : T extends Map<infer K, infer V>
+      ? Map<K, UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Map<any, any>>>
+      : T extends WeakMap<infer K, infer V>
+        ? WeakMap<K, UnwrapRefSimple<V>> &
+            UnwrapRef<Omit<T, keyof WeakMap<any, any>>>
+        : T extends Set<infer V>
+          ? Set<UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Set<any>>>
+          : T extends WeakSet<infer V>
+            ? WeakSet<UnwrapRefSimple<V>> &
+                UnwrapRef<Omit<T, keyof WeakSet<any>>>
+            : T extends ReadonlyArray<any>
+              ? { [K in keyof T]: UnwrapRefSimple<T[K]> }
               : T extends object
                 ? {
                     [P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]>

+ 64 - 0
packages/runtime-core/__tests__/apiSetupHelpers.spec.ts

@@ -333,6 +333,70 @@ describe('SFC <script setup> helpers', () => {
       expect(seenUid.two).toBeNull()
     })
 
+    test('should not leak currentInstance to sibling slot render', async () => {
+      let done!: () => void
+      const ready = new Promise<void>(r => {
+        done = r
+      })
+      let innerUid: number | null = null
+      let innerRenderUid: number | null = null
+
+      const Inner = defineComponent({
+        setup(_, { slots }) {
+          innerUid = getCurrentInstance()!.uid
+          return () => {
+            innerRenderUid = getCurrentInstance()!.uid
+            done()
+            return h('div', slots.default?.())
+          }
+        },
+      })
+
+      const Outer = defineComponent({
+        setup(_, { slots }) {
+          return () => h(Inner, null, () => [slots.default?.()])
+        },
+      })
+
+      const AsyncA = defineComponent({
+        async setup() {
+          let __temp: any, __restore: any
+          ;[__temp, __restore] = withAsyncContext(() =>
+            Promise.resolve()
+              .then(() => {})
+              .then(() => {}),
+          )
+          __temp = await __temp
+          __restore()
+          return () => h('div', 'A')
+        },
+      })
+
+      const AsyncB = defineComponent({
+        async setup() {
+          let __temp: any, __restore: any
+          ;[__temp, __restore] = withAsyncContext(() => Promise.resolve())
+          __temp = await __temp
+          __restore()
+          return () => h(Outer, null, () => 'B')
+        },
+      })
+
+      const root = nodeOps.createElement('div')
+      render(
+        h(() => h(Suspense, () => h('div', [h(AsyncA), h(AsyncB)]))),
+        root,
+      )
+
+      await ready
+      expect(
+        'Slot "default" invoked outside of the render function',
+      ).not.toHaveBeenWarned()
+      expect(innerRenderUid).toBe(innerUid)
+      await Promise.resolve()
+      expect(serializeInner(root)).toBe(`<div><div>A</div><div>B</div></div>`)
+    })
+
     test('error handling', async () => {
       const spy = vi.fn()
 

+ 393 - 0
packages/runtime-core/__tests__/components/Suspense.spec.ts

@@ -2476,6 +2476,399 @@ describe('Suspense', () => {
     expect(serializeInner(target)).toBe(``)
   })
 
+  // #12920
+  test('unmount Suspense after async child (with defineAsyncComponent) self-triggered update', async () => {
+    const Comp = defineComponent({
+      setup() {
+        const show = ref(true)
+        onMounted(() => {
+          // trigger update
+          show.value = !show.value
+        })
+        return () =>
+          show.value
+            ? (openBlock(), createElementBlock('div', { key: 0 }, 'show'))
+            : (openBlock(), createElementBlock('div', { key: 1 }, 'hidden'))
+      },
+    })
+
+    const AsyncComp = defineAsyncComp(() => {
+      const p = new Promise(resolve => {
+        resolve(Comp)
+      })
+      deps.push(p.then(() => Promise.resolve()))
+      return p as any
+    })
+
+    const toggle = ref(true)
+    const root = nodeOps.createElement('div')
+    const App = {
+      render() {
+        return (
+          openBlock(),
+          createElementBlock(
+            Fragment,
+            null,
+            [
+              h('h1', null, toggle.value),
+              toggle.value
+                ? (openBlock(),
+                  createBlock(
+                    Suspense,
+                    { key: 0 },
+                    {
+                      default: h(AsyncComp),
+                    },
+                  ))
+                : createCommentVNode('v-if', true),
+            ],
+            PatchFlags.STABLE_FRAGMENT,
+          )
+        )
+      },
+    }
+    render(h(App), root)
+    expect(serializeInner(root)).toBe(`<h1>true</h1><!---->`)
+
+    await Promise.all(deps)
+    await nextTick()
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<h1>true</h1><div>show</div>`)
+
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<h1>true</h1><div>hidden</div>`)
+
+    // unmount suspense
+    toggle.value = false
+    await Promise.all(deps)
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<h1>true</h1><!--v-if-->`)
+  })
+
+  test('unmount Suspense after async child (with async setup) self-triggered update', async () => {
+    const AsyncComp = defineComponent({
+      async setup() {
+        const show = ref(true)
+        onMounted(() => {
+          // trigger update
+          show.value = !show.value
+        })
+        const p = new Promise(r => setTimeout(r, 1))
+        // extra tick needed for Node 12+
+        deps.push(p.then(() => Promise.resolve()))
+        return () =>
+          show.value
+            ? (openBlock(), createElementBlock('div', { key: 0 }, 'show'))
+            : (openBlock(), createElementBlock('div', { key: 1 }, 'hidden'))
+      },
+    })
+
+    const toggle = ref(true)
+    const root = nodeOps.createElement('div')
+    const App = {
+      render() {
+        return (
+          openBlock(),
+          createElementBlock(
+            Fragment,
+            null,
+            [
+              h('h1', null, toggle.value),
+              toggle.value
+                ? (openBlock(),
+                  createBlock(
+                    Suspense,
+                    { key: 0 },
+                    {
+                      default: h(AsyncComp),
+                    },
+                  ))
+                : createCommentVNode('v-if', true),
+            ],
+            PatchFlags.STABLE_FRAGMENT,
+          )
+        )
+      },
+    }
+    render(h(App), root)
+    expect(serializeInner(root)).toBe(`<h1>true</h1><!---->`)
+
+    await Promise.all(deps)
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<h1>true</h1><div>hidden</div>`)
+
+    // unmount suspense
+    toggle.value = false
+    await Promise.all(deps)
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<h1>true</h1><!--v-if-->`)
+  })
+
+  test('propagates host el through wrapper components above Suspense after async child self-triggered update', async () => {
+    const AsyncComp = defineComponent({
+      async setup() {
+        const show = ref(true)
+        onMounted(() => {
+          show.value = false
+        })
+        const p = new Promise(r => setTimeout(r, 1))
+        deps.push(p.then(() => Promise.resolve()))
+        return () =>
+          h(
+            'div',
+            { key: show.value ? 'show' : 'hidden' },
+            show.value ? 'show' : 'hidden',
+          )
+      },
+    })
+
+    const Inner = defineComponent({
+      render() {
+        return h(Suspense, null, {
+          default: () => h(AsyncComp),
+        })
+      },
+    })
+
+    const Outer = defineComponent({
+      render() {
+        return h(Inner)
+      },
+    })
+
+    const root = nodeOps.createElement('div')
+    const vnode = h(Outer)
+    render(vnode, root)
+    expect(serializeInner(root)).toBe(`<!---->`)
+
+    await Promise.all(deps)
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>hidden</div>`)
+
+    const renderedEl = root.children[0]
+    const innerVNode = vnode.component!.subTree
+    const suspenseVNode = innerVNode.component!.subTree
+
+    expect(suspenseVNode.el).toBe(renderedEl)
+    expect(innerVNode.el).toBe(renderedEl)
+    expect(vnode.el).toBe(renderedEl)
+  })
+
+  test('should mount after suspense is resolved', async () => {
+    const target = nodeOps.createElement('div')
+
+    const Async = defineAsyncComponent({
+      render() {
+        return h('div', 'async')
+      },
+    })
+
+    const Comp = {
+      setup() {
+        return () =>
+          h(Suspense, null, {
+            default: h('div', null, [
+              h(Async),
+              h(Teleport, { to: target }, h('div', 'teleported')),
+            ]),
+            fallback: h('div', 'fallback'),
+          })
+      },
+    }
+
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+    expect(serializeInner(root)).toBe(`<div>fallback</div>`)
+    expect(serializeInner(target)).toBe(``)
+
+    await Promise.all(deps)
+    await nextTick()
+    expect(serializeInner(root)).toBe(
+      `<div><div>async</div><!--teleport start--><!--teleport end--></div>`,
+    )
+    expect(serializeInner(target)).toBe(`<div>teleported</div>`)
+  })
+
+  test('should patch teleport before suspense is resolved', async () => {
+    const target = nodeOps.createElement('div')
+    const text = ref('one')
+
+    const Async = defineAsyncComponent({
+      render() {
+        return h('div', 'async')
+      },
+    })
+
+    const Comp = {
+      setup() {
+        return () =>
+          h(Suspense, null, {
+            default: h('div', null, [
+              h(Async),
+              h(Teleport, { to: target }, h('div', text.value)),
+            ]),
+            fallback: h('div', 'fallback'),
+          })
+      },
+    }
+
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+    expect(serializeInner(root)).toBe(`<div>fallback</div>`)
+    expect(serializeInner(target)).toBe(``)
+
+    text.value = 'two'
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>fallback</div>`)
+    expect(serializeInner(target)).toBe(``)
+
+    await Promise.all(deps)
+    await nextTick()
+    expect(serializeInner(root)).toBe(
+      `<div><div>async</div><!--teleport start--><!--teleport end--></div>`,
+    )
+    expect(serializeInner(target)).toBe(`<div>two</div>`)
+  })
+
+  test('should handle disabled teleport updates before suspense is resolved', async () => {
+    const target = nodeOps.createElement('div')
+    const disabled = ref(false)
+
+    const Async = defineAsyncComponent({
+      render() {
+        return h('div', 'async')
+      },
+    })
+
+    const Comp = {
+      setup() {
+        return () =>
+          h(Suspense, null, {
+            default: h('div', null, [
+              h(Async),
+              h(
+                Teleport,
+                { to: target, disabled: disabled.value },
+                h('div', 'teleported'),
+              ),
+            ]),
+            fallback: h('div', 'fallback'),
+          })
+      },
+    }
+
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+    expect(serializeInner(root)).toBe(`<div>fallback</div>`)
+    expect(serializeInner(target)).toBe(``)
+
+    disabled.value = true
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>fallback</div>`)
+    expect(serializeInner(target)).toBe(``)
+
+    await Promise.all(deps)
+    await nextTick()
+    expect(serializeInner(root)).toBe(
+      `<div><div>async</div><!--teleport start--><div>teleported</div><!--teleport end--></div>`,
+    )
+    expect(serializeInner(target)).toBe(``)
+  })
+
+  test('should not mount discarded teleport after suspense is resolved', async () => {
+    const target = nodeOps.createElement('div')
+    const showTeleport = ref(true)
+
+    const Async = defineAsyncComponent({
+      render() {
+        return h('div', 'async')
+      },
+    })
+
+    const Comp = {
+      setup() {
+        return () => {
+          const children = [h(Async)]
+          if (showTeleport.value) {
+            children.push(h(Teleport, { to: target }, h('div', 'teleported')))
+          }
+          return h(Suspense, null, {
+            default: h('div', null, children),
+            fallback: h('div', 'fallback'),
+          })
+        }
+      },
+    }
+
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+    expect(serializeInner(root)).toBe(`<div>fallback</div>`)
+    expect(serializeInner(target)).toBe(``)
+
+    showTeleport.value = false
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>fallback</div>`)
+    expect(serializeInner(target)).toBe(``)
+
+    await Promise.all(deps)
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div><div>async</div></div>`)
+    expect(serializeInner(target)).toBe(``)
+  })
+
+  test('should not process discarded disabled teleport update after suspense is resolved', async () => {
+    const target = nodeOps.createElement('div')
+    const showTeleport = ref(true)
+    const disabled = ref(false)
+
+    const Async = defineAsyncComponent({
+      render() {
+        return h('div', 'async')
+      },
+    })
+
+    const Comp = {
+      setup() {
+        return () => {
+          const children = [h(Async)]
+          if (showTeleport.value) {
+            children.push(
+              h(
+                Teleport,
+                { to: target, disabled: disabled.value },
+                h('div', 'teleported'),
+              ),
+            )
+          }
+          return h(Suspense, null, {
+            default: h('div', null, children),
+            fallback: h('div', 'fallback'),
+          })
+        }
+      },
+    }
+
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+    expect(serializeInner(root)).toBe(`<div>fallback</div>`)
+    expect(serializeInner(target)).toBe(``)
+
+    disabled.value = true
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>fallback</div>`)
+    expect(serializeInner(target)).toBe(``)
+
+    showTeleport.value = false
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>fallback</div>`)
+    expect(serializeInner(target)).toBe(``)
+
+    await Promise.all(deps)
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div><div>async</div></div>`)
+    expect(serializeInner(target)).toBe(``)
+  })
+
   //#11617
   test('update async component before resolve then update again', async () => {
     const arr: boolean[] = []

+ 194 - 0
packages/runtime-core/__tests__/components/Teleport.spec.ts

@@ -146,6 +146,200 @@ describe('renderer: teleport', () => {
       )
     })
 
+    test('should keep the mounted vnode as the patch base across deferred updates', async () => {
+      const root = document.createElement('div')
+      document.body.appendChild(root)
+
+      const show = ref(false)
+      const disabled = ref(false)
+      const text = ref('A')
+      const phase = ref(0)
+
+      const Step1 = {
+        setup() {
+          disabled.value = true
+          text.value = 'B'
+          phase.value = 1
+          return () => h('div', 'step1')
+        },
+      }
+
+      const Step2 = {
+        setup() {
+          disabled.value = false
+          text.value = 'C'
+          return () => h('div', 'step2')
+        },
+      }
+
+      createDOMApp({
+        render() {
+          return show.value
+            ? [
+                h(
+                  Teleport,
+                  { to: '#targetId2', defer: true, disabled: disabled.value },
+                  h('div', text.value),
+                ),
+                phase.value === 0 ? h(Step1) : h(Step2),
+                h('div', { id: 'targetId2' }),
+              ]
+            : [h('div')]
+        },
+      }).mount(root)
+
+      show.value = true
+      await nextTick()
+
+      expect(root.innerHTML).toMatchInlineSnapshot(
+        `"<!--teleport start--><!--teleport end--><div>step2</div><div id="targetId2"><div>C</div></div>"`,
+      )
+    })
+
+    test('should handle disabled teleport updates before deferred mount', async () => {
+      const root = document.createElement('div')
+      const target = document.createElement('div')
+      target.id = 'targetId3'
+      document.body.appendChild(root)
+      document.body.appendChild(target)
+
+      const showTeleport = ref(false)
+      const disabled = ref(false)
+
+      const Step = {
+        setup() {
+          disabled.value = true
+          return () => h('div', 'step')
+        },
+      }
+
+      createDOMApp({
+        render() {
+          return showTeleport.value
+            ? [
+                h(
+                  Teleport,
+                  { to: '#targetId3', defer: true, disabled: disabled.value },
+                  h('div', 'teleported'),
+                ),
+                h(Step),
+              ]
+            : [h('div')]
+        },
+      }).mount(root)
+
+      expect(root.innerHTML).toMatchInlineSnapshot(`"<div></div>"`)
+      expect(target.innerHTML).toBe(``)
+
+      showTeleport.value = true
+      await nextTick()
+
+      expect(root.innerHTML).toMatchInlineSnapshot(
+        `"<!--teleport start--><div>teleported</div><!--teleport end--><div>step</div>"`,
+      )
+      expect(target.innerHTML).toBe(``)
+    })
+
+    test('should not mount discarded teleport after deferred updates', async () => {
+      const root = document.createElement('div')
+      const target = document.createElement('div')
+      target.id = 'targetId4'
+      document.body.appendChild(root)
+      document.body.appendChild(target)
+
+      const showTeleport = ref(false)
+      const phase = ref(0)
+
+      const Step1 = {
+        setup() {
+          phase.value = 1
+          return () => h('div', 'step1')
+        },
+      }
+
+      const Step2 = {
+        setup() {
+          showTeleport.value = false
+          return () => h('div', 'step2')
+        },
+      }
+
+      createDOMApp({
+        render() {
+          return showTeleport.value
+            ? [
+                h(
+                  Teleport,
+                  { to: '#targetId4', defer: true },
+                  h('div', 'teleported'),
+                ),
+                phase.value === 0 ? h(Step1) : h(Step2),
+              ]
+            : [h('div', 'done')]
+        },
+      }).mount(root)
+
+      expect(root.innerHTML).toMatchInlineSnapshot(`"<div>done</div>"`)
+      expect(target.innerHTML).toBe(``)
+
+      showTeleport.value = true
+      await nextTick()
+
+      expect(root.innerHTML).toMatchInlineSnapshot(`"<div>done</div>"`)
+      expect(target.innerHTML).toBe(``)
+    })
+
+    test('should not mount discarded disabled teleport after deferred updates', async () => {
+      const root = document.createElement('div')
+      const target = document.createElement('div')
+      target.id = 'targetId5'
+      document.body.appendChild(root)
+      document.body.appendChild(target)
+
+      const showTeleport = ref(false)
+      const disabled = ref(false)
+      const phase = ref(0)
+
+      const Step1 = {
+        setup() {
+          disabled.value = true
+          phase.value = 1
+          return () => h('div', 'step1')
+        },
+      }
+
+      const Step2 = {
+        setup() {
+          showTeleport.value = false
+          return () => h('div', 'step2')
+        },
+      }
+
+      createDOMApp({
+        render() {
+          return showTeleport.value
+            ? [
+                h(
+                  Teleport,
+                  { to: '#targetId5', defer: true, disabled: disabled.value },
+                  h('div', 'teleported'),
+                ),
+                phase.value === 0 ? h(Step1) : h(Step2),
+              ]
+            : [h('div', 'done')]
+        },
+      }).mount(root)
+
+      expect(root.innerHTML).toMatchInlineSnapshot(`"<div>done</div>"`)
+      expect(target.innerHTML).toBe(``)
+
+      showTeleport.value = true
+      await nextTick()
+
+      expect(root.innerHTML).toMatchInlineSnapshot(`"<div>done</div>"`)
+      expect(target.innerHTML).toBe(``)
+    })
+
     // #13349
     test('handle deferred teleport updates before and after mount', async () => {
       const root = document.createElement('div')

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

@@ -11,9 +11,10 @@ import {
   openBlock,
 } from '../vnode'
 import { ShapeFlags, isArray, isFunction, toNumber } from '@vue/shared'
-import type {
-  ComponentInternalInstance,
-  GenericComponentInstance,
+import {
+  type ComponentInternalInstance,
+  type GenericComponentInstance,
+  setCurrentInstance,
 } from '../component'
 import type { Slots } from '../componentSlots'
 import {
@@ -729,6 +730,10 @@ function createSuspenseBoundary(
           ) {
             return
           }
+          // withAsyncContext defers cleanup to a later microtask, so currentInstance may
+          // still be set when Suspense re-enters another component's render path.
+          // Clear it first.
+          setCurrentInstance(null, undefined)
           // retry from this component
           instance.asyncResolved = true
           onResolve(asyncSetupResult)

+ 97 - 92
packages/runtime-core/src/components/Teleport.ts

@@ -14,6 +14,7 @@ import type { VNode, VNodeArrayChildren, VNodeProps } from '../vnode'
 import { ShapeFlags, isString } from '@vue/shared'
 import { warn } from '../warning'
 import { isHmrUpdating } from '../hmr'
+import { type SchedulerJob, SchedulerJobFlags } from '../scheduler'
 
 export type TeleportVNode = VNode<RendererNode, RendererElement, TeleportProps>
 
@@ -23,6 +24,8 @@ export interface TeleportProps {
   defer?: boolean
 }
 
+const pendingMounts = new WeakMap<VNode, SchedulerJob>()
+
 export const TeleportEndKey: unique symbol = Symbol('_vte')
 
 export const isTeleport = (type: any): boolean => type.__isTeleport
@@ -95,7 +98,7 @@ export const TeleportImpl = {
     } = internals
 
     const disabled = isTeleportDisabled(n2.props)
-    let { shapeFlag, children, dynamicChildren } = n2
+    let { dynamicChildren } = n2
 
     // #3302
     // HMR updated, force full diff
@@ -104,6 +107,70 @@ export const TeleportImpl = {
       dynamicChildren = null
     }
 
+    const mount = (
+      vnode: TeleportVNode,
+      container: RendererElement,
+      anchor: RendererNode,
+    ) => {
+      // Teleport *always* has Array children. This is enforced in both the
+      // compiler and vnode children normalization.
+      if (vnode.shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
+        mountChildren(
+          vnode.children as VNodeArrayChildren,
+          container,
+          anchor,
+          parentComponent,
+          parentSuspense,
+          namespace,
+          slotScopeIds,
+          optimized,
+        )
+      }
+    }
+
+    const mountToTarget = (vnode: TeleportVNode = n2) => {
+      const disabled = isTeleportDisabled(vnode.props)
+      const target = (vnode.target = resolveTarget(vnode.props, querySelector))
+      const targetAnchor = prepareAnchor(target, vnode, createText, insert)
+      if (target) {
+        // #2652 we could be teleporting from a non-SVG tree into an SVG tree
+        if (namespace !== 'svg' && isTargetSVG(target)) {
+          namespace = 'svg'
+        } else if (namespace !== 'mathml' && isTargetMathML(target)) {
+          namespace = 'mathml'
+        }
+
+        // track CE teleport targets
+        if (parentComponent && parentComponent.isCE) {
+          ;(
+            parentComponent.ce!._teleportTargets ||
+            (parentComponent.ce!._teleportTargets = new Set())
+          ).add(target)
+        }
+
+        if (!disabled) {
+          mount(vnode, target, targetAnchor)
+          updateCssVars(vnode, false)
+        }
+      } else if (__DEV__ && !disabled) {
+        warn('Invalid Teleport target on mount:', target, `(${typeof target})`)
+      }
+    }
+
+    const queuePendingMount = (vnode: TeleportVNode) => {
+      const mountJob: SchedulerJob = () => {
+        if (pendingMounts.get(vnode) !== mountJob) return
+        pendingMounts.delete(vnode)
+        if (isTeleportDisabled(vnode.props)) {
+          mount(vnode, container, vnode.anchor!)
+          updateCssVars(vnode, true)
+        }
+        mountToTarget(vnode)
+      }
+      pendingMounts.set(vnode, mountJob)
+      queuePostRenderEffect(mountJob, undefined, parentSuspense)
+    }
+
     if (n1 == null) {
       // insert anchors in the main view
       const placeholder = (n2.el = __DEV__
@@ -115,109 +182,37 @@ export const TeleportImpl = {
       insert(placeholder, container, anchor)
       insert(mainAnchor, container, anchor)
 
-      const mount = (container: RendererElement, anchor: RendererNode) => {
-        // Teleport *always* has Array children. This is enforced in both the
-        // compiler and vnode children normalization.
-        if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
-          mountChildren(
-            children as VNodeArrayChildren,
-            container,
-            anchor,
-            parentComponent,
-            parentSuspense,
-            namespace,
-            slotScopeIds,
-            optimized,
-          )
-        }
-      }
-
-      const mountToTarget = () => {
-        const target = (n2.target = resolveTarget(n2.props, querySelector))
-        const targetAnchor = prepareAnchor(target, n2, createText, insert)
-        if (target) {
-          // #2652 we could be teleporting from a non-SVG tree into an SVG tree
-          if (namespace !== 'svg' && isTargetSVG(target)) {
-            namespace = 'svg'
-          } else if (namespace !== 'mathml' && isTargetMathML(target)) {
-            namespace = 'mathml'
-          }
-
-          // track CE teleport targets
-          if (parentComponent && parentComponent.isCE) {
-            ;(
-              parentComponent.ce!._teleportTargets ||
-              (parentComponent.ce!._teleportTargets = new Set())
-            ).add(target)
-          }
-
-          if (!disabled) {
-            mount(target, targetAnchor)
-            updateCssVars(n2, false)
-          }
-        } else if (__DEV__ && !disabled) {
-          warn(
-            'Invalid Teleport target on mount:',
-            target,
-            `(${typeof target})`,
-          )
-        }
+      if (
+        isTeleportDeferred(n2.props) ||
+        (__FEATURE_SUSPENSE__ && parentSuspense && parentSuspense.pendingBranch)
+      ) {
+        queuePendingMount(n2)
+        return
       }
 
       if (disabled) {
-        mount(container, mainAnchor)
+        mount(n2, container, mainAnchor)
         updateCssVars(n2, true)
       }
 
-      if (
-        isTeleportDeferred(n2.props) ||
-        (parentSuspense && parentSuspense.pendingBranch)
-      ) {
-        n2.el!.__isMounted = false
-        queuePostRenderEffect(
-          () => {
-            if (n2.el!.__isMounted !== false) return
-            mountToTarget()
-            delete n2.el!.__isMounted
-          },
-          undefined,
-          parentSuspense,
-        )
-      } else {
-        mountToTarget()
-      }
+      mountToTarget()
     } else {
       // update content
       n2.el = n1.el
-      n2.targetStart = n1.targetStart
       const mainAnchor = (n2.anchor = n1.anchor)!
-      const target = (n2.target = n1.target)!
-      const targetAnchor = (n2.targetAnchor = n1.targetAnchor)!
-
       // Target mounting may still be pending because of deferred teleport or a
-      // parent suspense buffering post-render effects. In that case, defer the
-      // teleport patch itself until the pending mount effect has run.
-      if (n1.el!.__isMounted === false) {
-        queuePostRenderEffect(
-          () => {
-            TeleportImpl.process(
-              n1,
-              n2,
-              container,
-              anchor,
-              parentComponent,
-              parentSuspense,
-              namespace,
-              slotScopeIds,
-              optimized,
-              internals,
-            )
-          },
-          undefined,
-          parentSuspense,
-        )
+      // parent suspense buffering post-render effects. In that case, replace
+      // the pending mount so the latest vnode goes through the mount flow.
+      const pendingMount = pendingMounts.get(n1)
+      if (pendingMount) {
+        pendingMount.flags! |= SchedulerJobFlags.DISPOSED
+        pendingMounts.delete(n1)
+        queuePendingMount(n2)
         return
       }
+      n2.targetStart = n1.targetStart
+      const target = (n2.target = n1.target)!
+      const targetAnchor = (n2.targetAnchor = n1.targetAnchor)!
       const wasDisabled = isTeleportDisabled(n1.props)
       const currentContainer = wasDisabled ? container : target
       const currentAnchor = wasDisabled ? mainAnchor : targetAnchor
@@ -328,6 +323,17 @@ export const TeleportImpl = {
     const { shapeFlag, children, anchor, targetStart, targetAnchor, props } =
       vnode
 
+    let shouldRemove = doRemove || !isTeleportDisabled(props)
+    // A deferred teleport inside a pending suspense may be unmounted before its
+    // content is ever mounted. Clear the queued mount effect and skip removing
+    // children because nothing has been mounted yet.
+    const pendingMount = pendingMounts.get(vnode)
+    if (pendingMount) {
+      pendingMount.flags! |= SchedulerJobFlags.DISPOSED
+      pendingMounts.delete(vnode)
+      shouldRemove = false
+    }
+
     if (targetStart) {
       hostRemove(targetStart)
     }
@@ -338,7 +344,6 @@ export const TeleportImpl = {
     // an unmounted teleport should always unmount its children whether it's disabled or not
     doRemove && hostRemove(anchor!)
     if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
-      const shouldRemove = doRemove || !isTeleportDisabled(props)
       for (let i = 0; i < (children as VNode[]).length; i++) {
         const child = (children as VNode[])[i]
         unmount(

+ 1 - 1
packages/runtime-vapor/src/vdomInterop.ts

@@ -751,7 +751,7 @@ function createVDOMComponent(
     setCurrentKeepAliveCtx(null)
   }
 
-  const wrapper = new VaporComponentInstance(
+  const wrapper = new VaporComponentInstance<Record<string, unknown>>(
     useBridge ? (comp as any) : { props: component.props },
     rawProps as RawProps,
     rawSlots as RawSlots,

+ 106 - 0
packages/vue/__tests__/e2e/Transition.spec.ts

@@ -2116,6 +2116,112 @@ describe('e2e: Transition', () => {
       E2E_TIMEOUT,
     )
 
+    // #14640
+    test(
+      'switch suspense branches after teleport updates before pending mount finishes',
+      async () => {
+        await page().evaluate(() => {
+          const { createApp, defineComponent, nextTick, ref } = (window as any)
+            .Vue
+
+          const Comp = defineComponent({
+            props: {
+              mode: {
+                type: String,
+                required: true,
+              },
+              count: Number,
+            },
+            emits: ['go', 'back'],
+            async setup() {
+              await new Promise(resolve => setTimeout(resolve, 0))
+            },
+            template: `
+              <div v-if="mode === 'a'">
+                <button @click="$emit('go')">Go</button>
+                <div>{{ count }}</div>
+                <teleport to="body">
+                  <Transition name="fade">
+                    <div>
+                      A Teleport
+                    </div>
+                  </Transition>
+                </teleport>
+              </div>
+
+              <div v-else>
+                <button @click="$emit('back')">Back</button>
+                <teleport to="body">
+                  <div>
+                    B Teleport
+                  </div>
+                </teleport>
+              </div>
+            `,
+          })
+
+          createApp({
+            components: {
+              Comp,
+            },
+            setup() {
+              const count = ref(0)
+              const page = ref('a')
+
+              const switchTo = (key: string) => {
+                page.value = key
+              }
+
+              const handleResolve = async () => {
+                await nextTick()
+                count.value++
+              }
+
+              return {
+                count,
+                page,
+                switchTo,
+                handleResolve,
+              }
+            },
+            template: `
+              <div id="container">
+                <Transition mode="out-in">
+                  <Suspense @resolve="handleResolve">
+                    <Comp
+                      :key="page"
+                      :mode="page"
+                      :count="count"
+                      @go="switchTo('b')"
+                      @back="switchTo('a')"
+                    />
+                  </Suspense>
+                </Transition>
+              </div>
+            `,
+          }).mount('#app')
+        })
+
+        await transitionFinish(60)
+        expect(await html('#container')).toBe(
+          '<div class=""><button>Go</button><div>1</div><!--teleport start--><!--teleport end--></div>',
+        )
+
+        await click('button')
+        await transitionFinish(60)
+        expect(await html('#container')).toBe(
+          '<div class=""><button>Back</button><!--teleport start--><!--teleport end--></div>',
+        )
+
+        await click('button')
+        await transitionFinish(60)
+        expect(await html('#container')).toBe(
+          '<div class=""><button>Go</button><div>3</div><!--teleport start--><!--teleport end--></div>',
+        )
+      },
+      E2E_TIMEOUT,
+    )
+
     // #3963
     test(
       'Suspense fallback should work with transition',

+ 1 - 1
packages/vue/package.json

@@ -94,7 +94,7 @@
   "bugs": {
     "url": "https://github.com/vuejs/core/issues"
   },
-  "homepage": "https://github.com/vuejs/core/tree/main/packages/vue#readme",
+  "homepage": "https://vuejs.org/",
   "dependencies": {
     "@vue/shared": "workspace:*",
     "@vue/compiler-dom": "workspace:*",

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 244 - 239
pnpm-lock.yaml


+ 0 - 1
vite.config.ts

@@ -246,7 +246,6 @@ export default defineConfig({
           'no-restricted-globals': 'off',
           'no-unused-vars': 'off',
           'oxc/no-optional-chaining': 'off',
-          'no-restricted-syntax': 'off',
         },
         globals: {
           suite: 'writable',

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels