When a top-level `server.preTransformRequests` value is provided in the Vite config, it should act as the default for all environments unless an environment explicitly sets its own `dev.preTransformRequests`. Currently, the top-level `server.preTransformRequests` has no effect; the dev server always forces `preTransformRequests` to `true` for the client environment and `false` for all others, ignoring the user’s explicit setting.

**Acceptance criteria**
- If `server.preTransformRequests` is set to a boolean, that value becomes the effective `preTransformRequests` for every environment that does **not** have its own `dev.preTransformRequests` explicitly defined.
- Environments that do define their own `dev.preTransformRequests` keep their explicit value, regardless of the top-level setting.
- When `server.preTransformRequests` is omitted, the existing behavior must be preserved: `client` environment defaults to `true`, `ssr` and any custom environments default to `false`.

fix: respect top-level `server.preTransformRequests` (#19272)


## Fix merging of `resolve.alias` when using object-style definitions and incorrect priority

When `mergeConfig` is called with two configurations that both define `resolve.alias`, the current behavior does not correctly combine the definitions:

- **Object-style aliases** (`{ find: replacement }`) are not merged property-by-property. If both the base and override configs use object form, the result may discard entries from one side instead of producing a single object that merges all keys, with the override’s values taking precedence.
- **Array-style aliases** (`[{ find, replacement }]`) are concatenated in the wrong order. Aliases are resolved top-down (first match wins), so overriding entries must appear **before** the base entries. Currently, base aliases come first, making the override ineffective for conflicting `find` values.

### Desired behavior

1. When both the base and override `resolve.alias` values are **objects**, merge them into a single object where keys from the override take priority over the base, and keys present only in the base are preserved.
2. When both are **arrays**, the merged array should have the override’s entries **before** the base’s entries so that the override’s aliases are tried first.
3. Merges involving a mix of object and array forms (e.g., base is an array, override is an object) should produce a correctly ordered array with the override's aliases first.
4. The internal alias merging that combines the built-in client alias with the user’s config (used in `resolveConfig`) must also follow these precedence rules.

This ensures that `mergeConfig` and downstream alias resolution consistently honor the contract: later/overriding configurations override earlier ones for the same alias target.


**Title:** SSR crashes on circular imports involving re-exports (`export { ... } from` / `export * from`)

### Steps to reproduce
1. Create a module `a.js` that exports a constant and imports something from `b.js`.
2. Create `b.js` that re-exports the binding from `a.js` using `export { A } from './a'` or `export * from './a'` (forming a circular dependency).
3. Import an entry point that eventually loads `a.js` during SSR (e.g., render a route that eagerly or dynamically imports `a.js`).
4. Start the Vite dev server and navigate to the route that triggers SSR of the circular dependency graph.

### Expected behavior
The SSR module loader should handle the circular dependency without crashing. The final rendered output should contain the expected value (e.g., `A is: __A__`). No `ReferenceError` or “Cannot access before initialization” error should be thrown.

### Actual behavior
The SSR transform currently hoists **all** import declarations and re-export-generated imports to the very top of the file. In a circular dependency, this causes the re-export’s import to be executed before the exported binding is initialized, leading to a crash (e.g., `ReferenceError: Cannot access 'A' before initialization`).

### Detailed requirements
- **Preserve source order for re-exports:**
  For `export { ... } from '...'` and `export * from '...'` statements, the generated import and export logic must be emitted at the position of the original declaration, **not** hoisted to the top. This respects standard ES module evaluation order, where re-exports are not hoisted but executed in place.

- **Maintain hoisting of standard imports:**
  Regular `import` declarations (not part of a re-export) should continue to be hoisted to the top of the transformed file. Their behavior must not change.

- **No regressions for non‑circular modules:**
  Existing tests and user projects that do not involve circular re-exports must produce the same transformed output and work identically.

- **The fix must apply to both development (`ssrTransform` for the dev server) and the build command** (since the transform logic is shared).

### Example scenario
```js
// a.js
import { getB } from './b'
export const A = '__A__'
export const B = getB()

// b.js
export function getB() { return '__B__' }
export { A } from './a'
```
Loading `a.js` during SSR should return a module with `A === '__A__'` and `B === '__B__'`, rather than crashing.

fix(ssr): crash on circular import (#14441)


The `getCssFilesForChunk` function used during HTML builds must collect and order CSS files for an entry chunk correctly under various dependency graph shapes. Currently the logic sits inline inside the build plugin and has no explicit behavioral guarantees beyond simple cases.

We need the function to satisfy the following invariants for any set of entry chunks and their import relations:

1. **Depth-first preorder ordering**
   CSS files from imported chunks appear before the CSS of the importing chunk, recursively. The final list reflects a depth-first traversal of the chunk import graph, with a chunk’s own `viteMetadata.importedCss` appended after all its dependencies’ CSS.

2. **Deduplication per entry**
   A CSS file must appear at most once per entry-point invocation, preserving its earliest position in the traversal. When the same CSS is reachable via multiple import paths (e.g., diamond dependencies), it must still appear exactly once.

3. **Correctness across multiple entry points**
   When several entry chunks share common dependencies, calls for different entries must produce independent, correct lists. No leakage should occur between entries—shared internal state (such as the existing `analyzedImportedCssFiles` cache) must not cause omissions that depend on the order of entry processing.

4. **Circular import safety**
   Circular imports must not cause infinite recursion. The traversal must terminate, and each unique CSS file from the cycle must be included at most once.

5. **Cache behavior**
   While caching is permissible to avoid re-walking subgraphs, the cache must preserve correctness: subsequent traversals for the same chunk (including when reached from different paths within the same entry) should respect the already-seen set and not re-add CSS files that were already included earlier in that specific entry’s result.

The function must handle graph topologies such as: chains, diamonds, multiple shared dependencies with overlapping usage, independent subtrees that intersect, and cycles. The logic should be structured so that its behavior is deterministic and verifiable against these scenarios.

test(html): add tests for `getCssFilesForChunk` (#22016)


**Title:** Virtual modules do not receive inferred module type during dev transforms

**Context:** When a module is transformed (e.g., via `transform` hooks), the `moduleType` option in the transform context should reflect the type inferred from the module’s id, such as `"json"` for `.json` files. This inference currently happens only inside the branch that handles resolved modules (`if (resolved) { ... }`). Virtual modules (e.g., `virtual:/bar.json`) typically do not hit this branch, so the `moduleType` passed to their transform hooks is `undefined` rather than the correct inferred value.

**Request:** Extend the module-type inference so that it also applies to virtual modules. After the load/transform pipeline, if `moduleType` is still `undefined`, infer it from the final module id using the existing `getModuleTypeFromId` utility. The inference should not overwrite a `moduleType` that was already explicitly set by a plugin.

**Acceptance criteria:**
- For a virtual module with an id like `virtual:/bar.json`, the `moduleType` option in any `transform` hook should be `"json"` (as if it were a real `.json` file).
- The inference must only run when `moduleType` is `undefined`, so plugins that deliberately set a custom `moduleType` are not affected.
- The change should be limited to dev mode (the `transformRequest` pipeline) and must not alter the behavior for non-virtual modules that already receive correct types.

fix: set inferred moduleType for virtual modules (#470)


### Description

When using `?worker&inline` to create an inline module worker (`type: 'module'`), the production build output does not produce a working worker. The worker fails to start or silently errors, and the following behaviors are broken in build mode but work correctly in development:

- The `name` option passed to the worker constructor (e.g., `new InlineWorker({ name: 'myName' })`) does not set `self.name` inside the worker.
- `import.meta.url` inside the inlined module worker is either `undefined` or an invalid URL, preventing relative path resolution.
- Classic inlined workers may also be affected by similar naming/resource issues (especially when object URL handling is involved).

### Expected Behavior

1. **Worker execution**
   Inline workers of both `'module'` and `'classic'` types must start and execute correctly in production builds, just as they do during development.

2. **Worker name**
   The `name` option provided to the worker wrapper (e.g., `new WorkerWrapper({ name: 'namedWorker' })`) must be reflected as `self.name` inside the worker.

3. **`import.meta.url` accessibility**
   Inside an inline module worker, `import.meta.url` must resolve to a valid, non‑revoked URL (`blob:` or `data:`) that can be used with `new URL('.', import.meta.url)`. The URL must remain accessible until the worker has successfully started.

4. **Consistency across modes**
   The above behaviors should work identically in both `vite dev` and `vite build` / `vite preview`. No production‑only workarounds should be necessary.

fix(worker): inline es worker does not work in build mode (#14307)


Support `import.meta.glob` patterns supplied as static template literals (no expressions). If a template literal is used, accept it and treat its raw content (including unicode escapes like `\u0068`) as the glob string. Reject template literals that contain `${...}` with a distinct error message (e.g., “Expected glob to be a string, but got dynamic template literal”) instead of the generic “Could only use literals”. This applies to both direct string arguments and string elements inside an array argument.

fix(glob): support static template literals (#9352)


# Trailing slash bypasses `server.fs.deny` check

When `server.fs.strict` is `true` and `server.fs.deny` uses patterns to block specific files, a request whose path ends with a trailing slash (e.g. `/src/.env/` instead of `/src/.env`) can escape the deny check and serve the file. The deny glob is evaluated against the raw path, including the trailing slash, so the pattern fails to match.

`isFileLoadingAllowed` must strip the trailing slash from the path before calling `config.fsDenyGlob`, so the deny check behaves identically for requests with and without a trailing slash.

This applies to paths served both via the public URL and via the `/@fs/` prefix.

fix(dev): trim trailing slash before `server.fs.deny` check (#20968)


### Issue: SSR transform misorders imports from re-export declarations

**Summary**
When a module uses both regular `import` statements and re-export statements (`export { ... } from '...'` or `export * from '...'`), the Vite SSR transform hoists the re-exported module’s import **after** all regular imports, instead of keeping them in their original source order. This breaks module evaluation order in scenarios where:

- Side‑effectful modules are combined with re-exports, e.g.
  ```js
  export * from './dep1.js';
  import './dep2.js';
  ```
  Here `dep1` should be executed before `dep2`, but the produced transform executes `dep2` first.
- Cyclic dependencies that rely on re-exported bindings being available when another module runs (e.g. a cyclic chain where a module re-exports a binding used by a later import).

**Expected behavior**
The SSR transform must emit `__vite_ssr_import__` calls (and corresponding `__vite_ssr_exportAll__` calls) in the **exact order** that the original import/re-export declarations appear in the module. Re-export declarations that have a source (`export … from '…'` / `export * from '…'`) should be treated as import statements for ordering purposes and interleaved with regular imports.

**Affected area**
`packages/vite/src/node/ssr/ssrTransform.ts` – the `ssrTransformScript` function that walks top‑level statements and re-orders imports before generating the transform.

**Constraints / Safety**
- The fix must not introduce duplicate `__vite_ssr_import__` calls for the same re-export specifier.
- It must still correctly wire re‑export bindings to the module’s exports (using `defineExport` and `__vite_ssr_exportAll__`).
- “Bare” re-exports (`export { foo }` without a source) should remain unaffected; they don’t create an import.
- The transformed code must remain functionally correct for all existing valid JavaScript module semantics, including live bindings and default re-exports.
- The solution should handle both named re‑exports (`export { x } from '…'`) and namespace re‑exports (`export * from '…'`), including re‑exports with an `exported` rename (`export * as ns from '…'`).

fix(ssr): fix execution order of re-export (#19841)


Ensure that `ssrTransform` (in `packages/vite/src/node/ssr/ssrTransform.ts`) preserves the line offset of every non-import statement during import rewriting. Currently, hoisting imports to the top shifts line numbers for any code following multi-line imports or code between hoisted imports, breaking source-map fidelity and debugging.

**Required behaviour**
- For multi-line import statements, leave blank lines behind so that code below stays on its original line number.
- When consecutive imports are separated only by whitespace (no non-whitespace tokens between them), do not hoist – their relative order is unchanged and moving them would unnecessarily alter line offsets.
- Hoisting must still occur when non-import code precedes an import, but after hoisting the remaining code must retain its original line numbering.

**Edge cases / constraints**
- Single-line imports followed by non-import code: line offset must remain unchanged.
- Imports separated by whitespace-only lines: do not trigger hoisting.
- Comments between imports: may still trigger hoisting; fixing this is out of scope for now.
- Do not regress the treatment of hashbang lines or other special leading content.

The fix should modify the `ssrTransformScript` function so that the transformed output keeps every non-import statement on exactly the same line as in the input source.

fix(ssrTransform): preserve line offset when transforming imports (#19004)


**Title:** Make injected oxc runtime helper references compatible with variable name mangling

**Context**
During production builds the oxc plugin injects runtime helpers (e.g. `asyncToGenerator`) into output chunks. Currently these helpers are accessed as properties of a `babelHelpers` namespace object:

```js
babelHelpers.asyncToGenerator(function* () { … })
```

Because `asyncToGenerator` is a property access, standard minifiers cannot rename it. This prevents further code-size reduction.

**Desired Behavior**
- The injected helper calls should become standalone variable references so that minifiers can mangle the names (shortening them in minified output).
- The new form must be **length‑preserving** relative to the original `babelHelpers.xxx` pattern – i.e. replace the dot with an underscore and do not change the surrounding code length, so no source‑map offset shift is introduced.
- The helper generation must remain compatible with the existing format‑specific injection points (hashbang handling, `'use strict'`, IIFE/UMD wrappers, etc.) – only the shape of the helpers themselves changes.

**Implementation constraints**
- Each helper export name must be an ASCII‑only JavaScript identifier. If any export name does not match `/^[A-Za-z_$][\w$]*$/`, `generateRuntimeHelpers` must throw a clear error.
- The generated helpers should be exposed as top‑level `var babelHelpers_<name>` declarations, assigned inside a closure so they are available to the surrounding chunk without creating a namespace object property.
- The output code must be consumable by the same wrapper routines (ESM, CJS, IIFE/UMD) without extra assumptions about hoisting beyond what `var` already provides.

**Acceptance criteria**
- A chunk that previously called `babelHelpers.asyncToGenerator(fn)` now calls `babelHelpers_asyncToGenerator(fn)` and the total character count of the code block is identical.
- Running the build with the updated plugin and then minifying with a standard mangler produces smaller final bundles than before (the helper variable names get shortened).
- All existing format‑specific insertion logic (hashbang, `'use strict'`, IIFE‑begin, UMD‑begin) continues to work without source‑map corruption.
- If a helper export would be non‑ASCII‑only, the build fails early with an explicit error.

feat: make oxc runtime helpers name mangle-able (#306)


**Title:** Build fails when `import.meta.env` define values contain non-JSON identifiers, and unknown env properties are not replaced with `undefined`

**Context**
- The `define` plugin uses esbuild’s `define` option, which can only accept JSON-serializable values.
- Users sometimes configure `define: { 'import.meta.env.LEGACY': '__VITE_IS_LEGACY__' }` where the value is a raw identifier (like a build-time flag). This is currently unsupported and throws an error during build.
- Properties of `import.meta.env` that are *not* explicitly defined (e.g. `import.meta.env.UNKNOWN`) are left as-is, causing runtime `undefined` errors that could be replaced at compile time.

**Desired behavior**
1. **Non-JSON values for `import.meta.env` properties** should be allowed. Any string value (including raw identifiers) must be accepted without breaking the esbuild transformation.
2. **Static unknown property access** (`import.meta.env.UNKNOWN`) should be replaced with `undefined` during build, matching the runtime behavior of `import.meta.env` when no such property exists.
3. **Dynamic property access** (`import.meta.env["UNKNOWN"]`) must remain as-is, resolved at runtime.
4. **Bare `import.meta.env` references** (without property access) should continue to be replaced with the complete environment object (including both Vite-injected keys and user-defined env keys).
5. **Sourcemap correctness** must be preserved when additional code (e.g., a variable declaration for the env object) is prepended to the module.

**Implementation notes**
- The current workaround that wraps `import.meta.env` in quotes when the value isn’t JSON-safe is insufficient; a more general solution is needed.
- One possible approach: replace `import.meta.env` with a temporary marker during the esbuild pass, then post-process the output to substitute env properties and insert the full env object only when needed.
- Ensure alignment with the existing handling of `import.meta.env.SSR` and user-defined env overrides so that there are no regressions.

fix(build): handle invalid JSON in import.meta.env (#17648)


**Title:** CJS → ESM default export interop is inconsistent with Node.js semantics

**Background**
When a source file imports a CommonJS dependency, rolldown‑vite rewrites the import to transparent interop helpers (`__esModule` checks, wrapper objects). The current logic always uses the same interop pattern regardless of whether the *importer* itself is treated as CJS or ESM. In Node.js ESM mode the ECMAScript module system has its own well-defined CJS‑interop rules: a default import of a CJS module must resolve to `module.exports` (not `.default` if `__esModule` is set), and a namespace import must always expose the module value under `default`.

**Problem**
The current implementation can produce incorrect results when the importer is an ES module (e.g. a `.mjs` file or when `"type": "module"` is present in the nearest `package.json`). The default import resolution is wrong, and namespace imports lack the mandatory `default` wrapper, breaking parity with Node’s runtime behaviour. This inconsistency is especially visible when consuming pre‑compiled CJS packages that set `__esModule` or when dynamic `import()` is used from an ESM context.

**Acceptance criteria**

1. **Import transformation**
   - When the importing file is an ES module (`isFilePathESM` returns `true`):
     - A default import (`import X from 'cjs-pkg'`) must produce `const X = <cjs-identifier>;` (no `__esModule`/`.default` unwrapping).
     - A namespace import (`import * as X from 'cjs-pkg'`) must always wrap the CJS module in an object that has the module (`module.exports`) as its `default` property, ignoring `__esModule`.
   - When the importing file is *not* an ES module (CJS or unknown):
     - **Behaviour must be flipped** compared to the current code: if the module does **not** have a truthy `__esModule` flag, treat the entire `module.exports` as the default export; otherwise, use the `.default` property. (This aligns with the interop that Babel/TypeScript CJS output expects.)

2. **Resolve pipeline**
   - During builds, the resolve plugin must include `packageJsonPath` in its result for any resolved file (not just deep package entry points). This is necessary so that `isFilePathESM` can later determine the module kind of the importer without re‑scanning the filesystem.

3. **No regressions for existing CJS ↔ CJS scenarios**
   - For importers that are not detected as ESM, the changed default‑import rule must not break existing projects; the most common pattern (`__esModule`‑marked modules) should still resolve `.default` as before, but the logic is reversed to be `!__esModule ? module : module.default`.

**Additional context**
- The helper `interopHelper` (used for namespace imports) already needs a boolean `isNodeMode` parameter; the resolution logic should be conditional on that flag as described above.
- The change only affects transformation of static and dynamic imports of CJS modules through the import analysis plugin.
- Related playground test adjustments (e.g. the `cjs-dynamic` and `cjs` test fixtures) will require updating their checks to match the corrected default export shape. The CSS‑codesplit‑CJS playground currently fails after these changes and should be skipped until an orthogonal fix is delivered.

fix: resolve default export consistently (#444)


**Bug report: Optional peer dependency imports silently succeed instead of throwing a runtime error in production**

**Observed behavior**
When a dependency has an optional peer dependency that is not installed, Vite (rolldown-vite) resolves the import to the internal placeholder `__vite-optional-peer-dep`.

In production builds, if the import originates from a CommonJS `require()` or from a dynamically required module, the placeholder is replaced by an empty module (`export default {}`). The build completes without errors and the app runs, but any code relying on that missing module may fail silently or produce unexpected results.

In development or with static ESM imports, the same placeholder instead throws a runtime error:
```
Could not resolve "<peerDep>" imported by "<parentDep>". Is it installed?
```

**Expected behavior**
An optional peer dependency that is actually used by the code should always cause a runtime error when the package is not installed, regardless of import style (import/require) or build mode (development/production). Silently swallowing the missing module hides a real problem and delays failure to runtime, making the issue extremely hard to diagnose. The error message already exists and is helpful; it should be thrown consistently.

**Impact**
- Developers can unknowingly deploy applications that depend on an optional peer dependency that is not installed, because the production bundle masks the problem.
- Debugging is made significantly harder: the symptom appears only at runtime, often in subtle ways (e.g., missing functionality, undefined values).
- The inconsistency between dev and production creates a false sense of safety during development.

**Proposed fix**
Remove the special-casing that returns a silent empty module for `require` in production. The `__vite-optional-peer-dep` placeholder should always evaluate to an error-throwing module, matching the existing behavior for static imports. This ensures that any missing optional peer dependency that is actually imported leads to an immediate, visible error, prompting the developer to install the required package.

fix: importing an optional peer dep should throw an runtime error (#167)


Support `deep: false` when watching a reactive object, so that only top-level property changes trigger the watch callback. When `deep: false` is explicitly set, nested changes must be ignored. Additionally, for `shallowReactive` sources, the watch should default to shallow tracking: nested mutations (e.g., setting a ref property inside a shallow array element) must not trigger the watcher; only array-level mutations (push, splice, etc.) should fire.

fix(runtime-core): support deep: false when watch reactive (#9928)


**Title:** formatComponentName does not resolve names from the component's own `components` registry

**Background**
`formatComponentName` is used in dev warnings and devtools to derive a human‑readable name for a component. When the component itself has no explicit name (no `name`, `__name`, or `__file`), the function attempts to infer a name by searching backwards through component registries. Today it only examines the **parent** component's `components` option and the **app‑level** `components` registry. This means that a component that is registered locally inside its own `components` (e.g., a self‑recursive component) or discovered solely from its own local registry will be reported as `"Anonymous"`, hurting debuggability.

**Desired behavior**
Extend the instance‑based inference in `formatComponentName` so that, when no explicit name is found, the **instance's own `components` registry** is also checked. The lookup order should be:

1. `Component.name` / `__name` / `__file` (unchanged)
2. `instance.components` (the component's own local registrations) – new
3. `instance.parent`'s `components` option (if a parent exists)
4. `instance.appContext.components`
5. Fall back to `"App"` for the root component or `"Anonymous"` otherwise

This priority ensures that a self‑referencing registration (e.g., `components: { ToggleButton: self }`) is preferred over an ancestor’s or global registration, while still allowing parent/app registrations to resolve when the component itself has no local entry.

**Non‑obvious constraints / edge cases**
- `instance.components` may be `null`; the registry lookup must safely handle `null`/`undefined` without throwing.
- When no `instance` is provided (`instance === null`), the function should skip all registry checks and rely only on the static name sources (existing behavior).
- The root‑component logic (`isRoot`) must not be affected by the new registry lookups; the `App`/`Anonymous` fallback remains the last resort.
- The change should not alter the existing precedence among `name`, `__name`, and `__file` – they still take priority over any registry‑inferred name.

**Affected code**
`formatComponentName` in `packages/runtime-core/src/component.ts`.

dx(runtime-core): check current and parent components in formatComponentName (#7220)


### SSR: Support dynamic components that resolve to elements or VNodes

**Problem**
The SSR compiler and runtime do not correctly handle `<component :is="...">` when the resolved value is a plain element tag (string) or a pre-created VNode (via `createVNode`). Currently the generated code always attempts to render via `ssrRenderComponent`, which expects a component definition. This leads to broken rendering, errors, or silently dropped output.

**Expected behavior**
Dynamic components in SSR should render identically to client-side rendering:

- When `:is` evaluates to a string element tag (e.g., `'div'`, `'p'`), the element should be rendered with its expected attributes and children/slots.
- When `:is` evaluates to a VNode (created with `createVNode`), that VNode should be rendered with its existing type, props, and children – and any additional props or default content provided on the `<component>` host should be merged appropriately.
- Slots and scoped slots passed to the dynamic component must be forwarded to the resolved element or VNode.

Existing behavior for static component tags and built-in components (Suspense, Teleport, etc.) must not regress.

**Required changes**
1. In the SSR compiler transform (`ssrTransformComponent`), for dynamic components (where the resolved type is a call expression from `resolveDynamicComponent`), emit code that:
   - calls `createVNode` with the resolved component value, merged props, and slot content,
   - passes the resulting VNode to a new runtime helper `ssrRenderVNode` (which delegates to the existing `renderVNode` from the server renderer).
2. Static component references should continue to use `ssrRenderComponent` for optimal performance.
3. Expose `renderVNode` as `ssrRenderVNode` from the server renderer package so the compiler can import it as a helper.
4. Update the compiler’s `componentTypeMap` to track whether the resolved type is a dynamic call expression, so the codegen phase can decide between `ssrRenderVNode` and `ssrRenderComponent`.

**Edge cases**
- The dynamic component may resolve to a regular component definition – this must still work and should go through the VNode path without regressions.
- The `<component>` host can have both `is` and arbitrary props (`:prop="value"`) – these must be merged into the resolved element/VNode.
- Default slot content (`<component :is="..."><span>foo</span></component>`) must be forwarded as children of the created VNode.
- Built-in dynamic components (e.g., a dynamic `<component :is="Suspense">`) are already handled elsewhere and should not be affected.

**References**
- Issue: #1508
- Relevant source files:
  - `packages/compiler-ssr/src/transforms/ssrTransformComponent.ts`
  - `packages/server-renderer/src/render.ts`
  - `packages/server-renderer/src/index.ts`

fix(ssr): support dynamic components that resolve to element or vnode


**Title:** `<script setup>`: detect top-level `await` and emit `async setup()` only when needed

**Background**
The `<script setup>` implementation currently wraps its statements inside an `export function setup(...)`. So far the function is always synchronous, which prevents any top-level `await` usage inside `<script setup>`.

**Request**
Parse `<script setup>` content with the `topLevelAwait` plugin enabled, walk the AST for `AwaitExpression` nodes, and decide whether the generated `setup` function should be `async`.

**Behavioral requirements**

- The generated `export function setup(...)` must be `export async function setup(...)` **iff** one or more top-level `await` expressions are found in `<script setup>`.
- `await` inside any nested function must **not** cause the outer `setup` to become async. This includes:
  - function declarations
  - function expressions
  - arrow functions
  - object methods (`{ async foo() {…} }`)
  - class methods (`class { async bar() {…} }`)
- All other top-level constructs should be inspected for `await` – for example:
  - expression statements (`await foo`)
  - declarations (`const a = await foo`)
  - named exports (`export const a = await foo`)
  - control-flow statements (`if (…) { await foo } else { await bar }`)
- The `await` detection walker must use a robust `isFunction` guard that catches all AST node types representing functions/methods, so that method nodes are correctly skipped (earlier code only matched `Function(Expression|Declaration)` and missed `Method` nodes).

**Non-goals**
- This is only about top-level `await` detection; no change to how inner async functions are compiled or how suspense integration works.

**Relevant files**
- `packages/compiler-sfc/src/compileScript.ts` – script compilation and setup wrapping
- `packages/compiler-core/src/transforms/transformExpression.ts` – the `isFunction` helper is duplicated there and should be updated to match method nodes as well, to keep behavior consistent for expression transforms that rely on it.

feat(compiler-sfc): `<script setup>` support (experimental)


Fix `beforeCreate` / `created` invocation for chained `extends` and nested mixins.

## Problem
When a component uses `extends` on an option that itself `extends` a base, or when a mixin declares its own `mixins`, the `beforeCreate` and `created` hooks from those nested ancestors/mixins are silently skipped.

## Expected behavior
All `beforeCreate` hooks should be called in this order:
1. Topmost `extends` ancestor → … → immediate `extends` base.
2. For each declared mixin: first its own `mixins` recursively (depth-first, order-preserving), then the mixin’s own hook.
3. The component’s own hook.

`created` hooks must follow the identical order (extends chain first, then mixins, then self).

The traversal must be recursive so that arbitrarily deep chains of `extends` and `mixins` inside mixins work without altering the relative order of hooks at the same level.

fix(runtime-core): properly call lifecycle hooks in chained mixins & extends (#1974)


**Bug Report: Inline template source maps are missing in production builds**

**Observed behavior**
When `compileScript` is invoked with `inlineTemplate: true` and source maps enabled (`sourceMap: true` or not disabled), the resulting source map covers only the `<script setup>` portion. The inlined render function (compiled from the `<template>`) has no source map mappings back to the original template block. Consequently, any runtime error originating from a template expression—such as a `throw` inside an event handler—is reported with a location inside the generated script rather than the original `<template>` source, making debugging and error tracking nearly impossible in production.

**Expected behavior**
The combined source map should provide correct original positions for **all** code in the final SFC script, including the inlined template render function. Specifically:

- Mappings from the template compiler’s output (the render function) must be merged into the script’s source map.
- The template mappings must be shifted by the exact line offset where the inlined code is inserted into the script (after any setup code and return statement).
- The merged map should preserve both the script’s and template’s original source content.

This ensures that tools consuming the final source map (error-reporting services, browser DevTools, etc.) can pinpoint errors to the correct location inside the `<template>` block, just as they would for standalone templates.

**Impact**
Without this fix, inline-template mode silently discards template source information in production builds. Since inline templates are often used for performance or SSR reasons, any production error inside a template expression becomes untraceable, severely hampering developer experience and production monitoring.

**Relevant context**
- Options: `inlineTemplate: true` in `SFCScriptCompileOptions`, `sourceMap` controls map generation.
- The template compiler already returns a valid `map` for the compiled render function—it just isn’t merged into the script’s final map.
- Correct implementation must account for the line offset between where the template code is appended and the beginning of the generated script, as the template code is typically placed after the script’s return statement.

fix(compile-sfc): handle inline template source map in prod build (#12701)


**Title**: Scheduler: fix job insertion / pre-flush ordering when jobs are queued from a post-flush callback

**Package**: `@vue/runtime-core`
**Area**: global scheduler (`queueJob`, `queuePostFlushCb`, `flushPreFlushCbs`, `flushJobs`)

### Summary

When user code schedules new jobs inside a post-flush callback (registered with `queuePostFlushCb`), the insertion logic can place jobs at incorrect indices, causing the `queue` array to contain holes and violating the ordering rule that jobs with lower `id` must execute before jobs with higher `id`. Similarly, calling `flushPreFlushCbs` explicitly from within a post-flush callback (e.g. during a nested `app.mount()`) may skip the beginning of the pre-flush queue, leaving some pre-jobs unflushed or out-of-order.

### Desired behavior

1. If a post-flush callback calls `queueJob(job2)` (id=2) then `queueJob(job1)` (id=1), after the tick completes the execution order must be **job1 then job2** (ascending by `id`).
2. The internal `queue` array must never contain empty slots (no “holes”) after insertion during any active flush, regardless of whether the main queue has already been cleared.
3. When `flushPreFlushCbs` is invoked inside a post-flush callback, it must process **all** currently queued pre-flush jobs, starting from the front of the queue, in the order dictated by their `id`. No pre-job may be skipped due to an incorrect starting offset.

### Technical context

- The insertion point for a new job is determined by `findInsertionIndex(id)`, which performs a binary search on the remaining portion of the queue. Currently it relies on the `isFlushing` boolean and the current `flushIndex` to decide where to begin the search.
- After the main queue is drained and before the post-flush cbs are executed, the queue is cleared (`queue.length = 0; flushIndex = 0`). Because `isFlushing` remains `true`, `findInsertionIndex` starts at `flushIndex + 1 = 1`, which on an empty array leads to an insertion at index 1, leaving a hole at index 0. Subsequent insertions compound the issue.
- `flushPreFlushCbs` accepts an optional starting index `i`. Its default value uses `isFlushing ? flushIndex + 1 : 0`. When called during post-flush, this evaluates to `1`, causing the function to ignore the element at index 0 even though the queue may have been repopulated during the post-flush callback.

### Constraints

- The fix must preserve the existing behavior when jobs are queued during the **pre-flush** phase and when no flush is active.
- No unnecessary microtasks should be scheduled (i.e. we should not create extra `Promise.resolve().then(...)` if a flush cycle is already pending or in progress, although the precise mechanism may be adjusted).
- Recursive flushes (new jobs added while draining) must still drain completely without hitting artificial recursion limits beyond the existing `RECURSION_LIMIT`.

### Suggested direction

Eliminate boolean state flags (`isFlushing`, `isFlushPending`) and instead use a single `flushIndex` that is `-1` when no flush is active, so that `findInsertionIndex` and `flushPreFlushCbs` always compute the starting offset as `flushIndex + 1`. When the queue is cleared between phases, `flushIndex` should be reset to `-1` so that the next insertion starts at index 0 and `flushPreFlushCbs` starts from the beginning. The pending-flush guard can be expressed in terms of the existing `currentFlushPromise`.

fix(scheduler): job ordering when the post queue is flushing (#12090)


I’m seeing an error when using indexed access types in props type-only SFC macros. For example:

```ts
type T = { bar: number }
type S = { nested: { foo: T['bar'] } }
defineProps<{ target: S['nested'] }>()
```

or even a simple `defineProps<{ a: T['bar'] }>()` triggers an “Unsupported type” / “Unresolvable type” error during SFC compilation, because `TSIndexedAccessType` is not handled in the type resolution phase used by `defineProps` / `defineEmits`. Utility types like `Pick` and `Omit` work fine, but direct indexed access (a very common pattern) is missing.

**Expected behaviour:**
- When the index is a string literal (e.g. `T['bar']`), the compiler should resolve it to the corresponding property type and then continue resolving that type recursively (so `Number` in the example).
- The runtime type inferred for that property should match the resolved type (e.g. `['Number']`).
- Only string literal indices need to be supported; other index types (computed, number, etc.) can stay unsupported with a clear error (consistent with the existing restriction on computed keys).

Without this, any type that relies on `Type['key']` cannot be used in the Vue SFC macros, forcing developers to inline all property type definitions or avoid certain abstractions.

feat(compiler-sfc): support string indexed type in macros


## Issue: SSR falls back incorrectly for empty normal slots

### Problem

When rendering a component on the server that has a `<slot>` with fallback content,
the fallback is not rendered if the provided slot content evaluates to a
comment-only VNode tree (for example, a `v-if="false"` wrapping the actual
content). Instead, the server outputs the comment anchor nodes as real content.
This leads to:

- A visible difference between server-rendered HTML and the client-side DOM.
- The fallback content being suppressed on the server even though the slot is
  logically empty, breaking the intended default behavior.

### Example (conceptual)

Given a component like:

```
<template>
  <component is="button">
    <slot>fallback</slot>
  </component>
</template>
```

If the parent provides the slot as:

```
<ButtonComp>
  <template v-if="false">hello</template>
</ButtonComp>
```

On the server, the rendered output should be:

```
<button>fallback</button>
```

but instead it renders the empty conditional anchors (e.g.,
`<button><!--[--><!--]--></button>`) and omits the fallback entirely.

The same issue occurs when the slot is passed through a wrapper component that
itself contains a `<slot>`.

### Acceptance criteria

1. **Empty slot detection**
   When rendering a normal (non‑scoped) slot on the server, the renderer must
   check whether the slot content consists solely of comment VNodes (generated by
   `v‑if`, dynamic components, or similar constructs that can produce only
   placeholder comments). If no valid (non‑comment) VNode exists, the slot is
   considered **empty**.

2. **Fallback rendering**
   - If a slot is empty and a fallback rendering function is provided, the
     fallback content must be rendered exactly as it would be on the client.
   - If no fallback is provided, nothing should be rendered.

3. **Consistency with client behavior**
   The determination of “valid” content must match the client‑side logic already
   present in `ensureValidVNode` (a helper that returns `false` for arrays that
   contain only comments). The server‑side slot rendering must consult this check
   before rendering any array of VNodes as a normal slot.

4. **Indirect slot propagation**
   The fix must apply when slots are forwarded through intermediate components
   (e.g., a `Wrap` component that has `<slot/>`), and when a slot is rendered
   inside a dynamic `<component>` element.

fix(ssr): ensure content is valid when rendering normal slot (#11491)


**Title:** Cover Vue 2 compat gaps for async component object syntax, `Vue.util.defineReactive`, and legacy render-function event modifiers

**Context**
We currently have partial compat coverage for Vue 2 patterns in `@vue/compat`. Several gaps remain that cause runtime mismatches or missing warnings when migrating real-world v2 codebases.

**Request**
Add compat coverage (implementation and/or tests) for the following v2 behaviors:

1. **Async component object syntax**
   - Support factory functions that return `{ component: Promise<Component>, loading?, error?, delay?, timeout? }` (LegacyAsyncOptions).
   - Ensure the loading placeholder and resolved component work correctly, and the `DeprecationTypes.COMPONENT_ASYNC` warning is triggered.

2. **`Vue.util.defineReactive`**
   - Ensure `defineReactive` can be called on a component instance during `beforeCreate` and that its value becomes template-reactive (updates trigger re-render).
   - When the initial value is an object or array, mutations *to the original value* (e.g., `val.a++`, `val.push(2)`) must be tracked so that dependent effects re-run.

3. **Legacy event prefixes in render-function `on`**
   - In `compatH`, support Vue 2’s common event key prefixes inside `on`:
     - `&` → passive (`onClickPassive`)
     - `~` → once (`onKeyupOnce`)
     - `!` → capture (`onTouchendCapture`)

4. **`nativeOn` handling**
   - When `nativeOn` listeners are specified, map them to `on<Event>Native` keys (e.g., `nativeOn: { click: fn }` → `onClickNative`).
   - Remove the previous deduplication logic that discarded a `nativeOn` listener if an identically-stringified handler already existed under `on`. Instead, always produce separate keys, allowing both non-native and native listeners to coexist independently.

**Acceptance criteria**
- Async components defined with the object syntax render correctly and emit the appropriate deprecation warning.
- `Vue.util.defineReactive` works on instances and deeply tracks mutations to the original passed-in value.
- `h` calls with `on: { '&click': fn, '~keyup': fn, '!touchend': fn }` produce props `onClickPassive`, `onKeyupOnce`, `onTouchendCapture`.
- `nativeOn` events produce distinct `on<Event>Native` props and never silently drop a handler.

**Additional notes**
- Should be compatible with the existing `DeprecationTypes.RENDER_FUNCTION` warning flow.
- No change to the type signature of `compatH` except as needed to allow the new props mapping.


When a custom scheduler is installed via `setComputedScheduler`, computed values must stay in sync with their dependencies on **synchronous get** even though the effects that observe them are flushed asynchronously. Currently, after a dependency change, a downstream computed that depends on an upstream computed may return a stale value if accessed before the scheduler has run (i.e. before the next microtick). This leads to reads of out-of-date data in chained computed chains.

### Expectation / acceptance criteria

1. **Chained computed get returns the new value synchronously**
   - Given a ref `src`, a computed `c1` derived from `src`, and a computed `c2` derived from `c1`, after `src.value` is mutated, reading `c2.value` **immediately** (synchronously, without `await`-ing the scheduler) **must return the recomputed value** based on the new `src.value`.

2. **Normal (non-computed) effects remain asynchronous**
   - The scheduler should still defer the execution of any `effect` (or `watchEffect`) that subscribes to the computed. Those effect callbacks must not be invoked synchronously on `src.value` mutation, but only when the scheduler flushes the queue.
   - After a dependency change and after the scheduler has flushed, the subscriber effect runs at most once per queued flush (i.e. multiple mutations within the same synchronous block result in a single effect run).

3. **Unchanged computed values must not trigger downstream effects**
   - If the recomputation of a computed yields the same value as before (after `setComputedScheduler`), any non-computed effect subscribed to that computed must not be triggered. The scheduler may still be notified, but the effect should be skipped.
   - Chained intermediate computeds may still re-run (invalidation and recomputation happens on access), but the final effect is only invoked if the top-level computed value actually changed.

4. **Scheduler integration invariants**
   - The scheduler is called at most once per synchronous mutation cycle; multiple dependency mutations are aggregated.
   - While the scheduler callback itself is deferred, accessing a computed’s `.value` during the same synchronous block forces a lazy recomputation if the computed is dirty, ensuring that the returned value reflects the current dependencies.

5. **No stale reads across multiple recomputes**
   - Mutating `src` to a value that does not change `c1`’s output but still causes `c1` to recompute (e.g. a value with no net effect on the computed result) must still recompute `c2` on access, keeping the chain consistent. This preserves the existing behaviour where intermediate computeds always recompute when dirty, even if the final visible value is unchanged.

### Context

- The behaviour primarily affects projects using `setComputedScheduler` (e.g. the Vue core renderer), where a microtask-based scheduler is used to batch render effects.
- The issue is specific to **chained computeds** (computed depending on another computed) – standalone computeds already seem to provide up-to-date values on synchronous get.

### Implementation notes (behavioural constraints)

- The synchronous path should only be triggered for **computed effects** (the ones created by `computed()`).
- The scheduler must still be invoked to schedule non-computed effects; it should not be bypassed entirely.
- The internal “dirty” tracking must be updated correctly so that a computed that is about to be accessed sees itself as dirty after a dependency change without waiting for the scheduler callback.
- This must not introduce recursive/overflow issues when deeply nested computed chains are invalidated and synchronously accessed.

refactor: sync value access for chained computed w/ scheduler


**Issue: computed values get stuck on a stale initial value during server-side rendering when state is mutated asynchronously inside setup**

**Context**
Vue 3 SSR previously made computed properties non‑reactive (to avoid a memory leak, #5208). This had the side effect that computed getters were cached immediately and never re‑evaluated, even if a dependency changed later inside the component’s setup.

**Problem**
Consider an async component whose `setup`:

1. creates a computed that depends on a reactive piece of state,
2. reads the computed value (it evaluates to `null`/`undefined` because the state isn’t populated yet),
3. awaits a data fetch that populates the state,
4. reads the computed value again, and finally
5. returns a render function that uses the computed value multiple times in the template.

Current behavior: the computed returns the stale initial value both in the second setup read and throughout the template rendering. The server‑rendered HTML therefore contains the wrong (initial) data.

Desired behavior: the computed must **react to the state change** during setup and return the updated value, while still avoiding redundant evaluations during the actual rendering phase.

**Acceptance criteria**

1. **Reactivity during setup**
   During the component’s setup lifecycle—including any awaited async work—computed properties must behave reactively: if a dependency changes after the first access, subsequent reads (still within setup) must return the new value.

2. **Caching during render**
   Once the server renderer enters the rendering phase for the component (i.e., when it starts producing the SSR output for that component’s subtree), computed getters become **cacheable**. During this phase, if the same computed is accessed multiple times (e.g., in the template), the getter must be called **at most once**, because no further state mutations can occur.

3. **No regressions on memory**
   The earlier SSR memory leak fix must be preserved: computed refs should not create reactive effects that keep references alive after the request completes.

4. **Isolation**
   The caching behaviour must not “leak” between different component instances or different SSR passes.

**Reproduction outline**
```js
const store = reactive({ items: null })
const App = defineComponent(async () => {
  const msg = computed(() => store.items?.join(' '))

  if (!msg.value) await fetchItems()   // fills store.items
  // Here msg.value should be the updated string, not null

  return () => h('div', msg.value + msg.value + msg.value)
})
// Expected SSR output: <div>hello worldhello worldhello world</div>
```

**Relevant implementation touchpoints**
- The `computed` implementation inside `@vue/reactivity` already has an `isSSR` flag that disables reactivity.
- The server renderer in `@vue/server-renderer` initialises rendering in `renderComponentSubTree`. This is the point where caching can be safely enabled for all computed refs belonging to that component.
- The render phase is synchronous and mutation‑free, making it safe to cache computed values until the component’s output is fully generated.

**Additional notes**
- Computed refs that are never accessed during setup should still be able to evaluate lazily on first access during render—caching can be enabled before their first evaluation.
- The change should not affect client‑side rendering.
- The fix must not reintroduce the memory leak addressed in #5208.

fix(ssr): only cache computed getters during render phase


```
Title: Nested custom elements cannot inject values provided by parent's configureApp

Affected APIs: `defineCustomElement` (with `configureApp`), `inject`

Problem:
When a parent custom element provides values via its `configureApp` callback (e.g., `app.provide('token', value)`), those values are not injectable in nested child custom elements. The injection silently fails and returns `undefined`.

Steps to reproduce:
1. Define a parent custom element that calls `app.provide('shared', 'shared')` inside its `configureApp`.
2. Define a child custom element whose `setup()` calls `inject('shared')`.
3. Use the elements in a page as `<parent-el><child-el></child-el></parent-el>`.
4. Observe that the child's injected `shared` value is `undefined`.

Expected:
- Child custom elements should inherit provided values from ancestor custom element contexts, just as they would in a standard nested `createApp` setup.
- If a child's own `configureApp` provides a key that already exists in an inherited context, a clear warning should inform the developer that they are overriding an inherited value.
- The child's own provided values should still override inherited ones if explicitly set.

Impact:
This breaks idiomatic dependency injection patterns for reusable custom element component trees, making it impossible to share configuration via `provide`/`inject` across element boundaries out-of-the-box. Inherited fallbacks work correctly in regular Vue application trees, but custom elements lose the chain.
```

fix(custom-element): allow injecting values from app context in nested elements (#13219)


**Title:** Computed with no reactive dependencies recomputes on every `.value` access after any reactive change

**Affected package:** `@vue/reactivity` (computed refs)

**Description:**

When a computed property’s getter does **not** access any reactive state (e.g., it returns a constant or calls a pure function with no dependencies), the value is re-evaluated each time the computed is read after *any* reactive change in the system (even an unrelated `ref` increment that only bumps `globalVersion`). This leads to unnecessary re-computation, wasted performance, and can cause unexpected side effects if the getter is impure.

**Steps to reproduce:**

```js
const spy = vi.fn()
const c = computed(() => spy())

c.value      // first access → getter runs, spy call count = 1
ref(0).value++  // any reactive change, e.g. increment a random ref
c.value      // second access → getter runs again, spy call count = 2 (WRONG)
```

**Expected behavior:**
If a computed has **no tracked reactive dependencies** (its dependency list is empty) and its value was already evaluated (the computed is not marked dirty), subsequent reads should return the cached value without re-running the getter, regardless of `globalVersion` changes. The only exception is when the computed is explicitly invalidated (e.g., by `trigger` on its own `dep`).

**Actual behavior:**
The computed re-evaluates on every `.value` read after a global version bump, even though the underlying data never changed and no dependency would signal a dirty state.

**Impact:**
Unnecessary recalculations hurt performance, especially in large computed chains where some nodes intentionally avoid reactive tracking (e.g., memoised helpers, derived constants). It also breaks the invariant that a computed is “lazy and cached”—a core expectation of Vue’s reactivity.

**Suggested fix direction (outline only):**
The re-evaluation guard in the internal refresh logic should be broadened: when the computed has no dependencies and has already been evaluated at least once (and is not explicitly dirty), it should skip the re-run, preserving the cached value. The SSR fast-path that already bypasses dirty checking can remain, but the non-SSR path should respect this case.

fix(reactivity): should not recompute if computed does not track reactive data (#12341)


**Title:** `<Suspense>` needs a `suspensible` option to let a nested suspense boundary delegate its pending state to a parent `<Suspense>`

**Background**
Currently every `<Suspense>` boundary manages its own fallback independently. When you have nested async components—for instance, an outer async wrapper that renders an inner async component—both suspense boundaries can show separate fallbacks, resulting in UI flicker or an unnecessarily fragmented loading experience. There is no way to tell a nested `<Suspense>` “don’t show your own fallback; instead extend the parent’s pending state.”

**Desired behavior**
Introduce a new boolean prop `suspensible` on `<Suspense>`. When `suspensible` is `true`:

- The suspense boundary does **not** render its own fallback. The parent `<Suspense>` continues to show its fallback until **all** descendant `suspensible` boundaries have resolved.
- The parent’s resolution is deferred: it stays in the pending/fallback state while any direct child `<Suspense suspensible>` still has unresolved async dependencies.
- The dependency tracking is limited to the immediate parent `<Suspense>`. A `suspensible` boundary does **not** affect a grandparent; it only feeds into the nearest enclosing `<Suspense>`.

**Edge cases & invariants**
1. **No active parent** – If the parent `<Suspense>` is already resolved (or does not exist), `suspensible` has no effect. The boundary falls back to its own independent behavior, showing its own fallback if needed.
2. **Parent resolution** – When a `suspensible` child resolves, it must signal the parent so the parent can decrement its pending dependency count. The parent resolves and shows its default content only when that count reaches zero (i.e., all `suspensible` children have resolved, along with any other async deps the parent tracks directly).
3. **Dynamic children** – Toggling between async components inside a `suspensible` `<Suspense>` should not cause the parent to re-enter fallback if it has already resolved, but should correctly extend the parent’s pending state if the parent is still loading. The parent’s fallback must continue to be displayed until the new async component resolves.
4. **Own fallback ignored** – When `suspensible`, any `fallback` slot provided on the inner `<Suspense>` is effectively unused. The parent’s fallback remains visible instead.
5. **Default `false`** – The default value must be `false` to preserve full backward compatibility; existing nested `<Suspense>` usage remains completely independent.

**Acceptance criteria**
- A `<Suspense suspensible>` inside another `<Suspense>` causes the outer fallback to stay visible until the inner async deps resolve.
- Outer `<Suspense>` event hooks (`onPending`, `onResolve`) reflect the combined timeline.
- The feature does not leak beyond the immediate parent boundary.
- Existing tests and components without `suspensible` are unaffected.

feat(suspense): introduce suspensible option for `<Suspense>` (#6736)


**Title:** Computed dependency cleanup erroneously removes Dep from reactive target’s map

**Context**

When a `computed` re-evaluates and one of its tracked reactive properties is no longer accessed (e.g. due to a branching condition), the cleanup logic removes the subscriber link for that computed from the property’s `Dep`. Currently, this removal can also delete the `Dep` entry from the owning reactive object’s dependency map (`targetMap`), even when there are still other active subscribers—such as other computed or effects—that depend on the same key. This causes the surviving subscribers to silently miss future change notifications.

**Expected Behavior**

Cleaning up a computed’s dependencies should only unlink that particular subscriber from the `Dep`’s internal subscriber list. The `Dep` itself must remain in the reactive target’s key → `Dep` map (`targetMap`) if any other subscriber (computed, effect, etc.) still references it, so that subsequent mutations to the reactive property properly notify all remaining dependants.

**Reproduction Sketch** (conceptual)

```js
const toggle = ref(true)
const state = reactive({ a: 1 })
const p = computed(() => toggle.value ? state.a : 111)
const pp = computed(() => state.a)
effect(() => p.value)

// pp should always reflect state.a
console.log(pp.value)          // 1

toggle.value = false  // p no longer tracks state.a
state.a++             // mutation that should still be observed by pp
console.log(pp.value)          // BUG: still 1, expected 2
```

**Acceptance Criteria**

- After a computed’s dependency cleanup runs (e.g. via `refreshComputed`), any `Dep` that is no longer “used” by that computed must not be removed from the reactive object’s `targetMap` as long as at least one other subscriber is still registered with that `Dep`.
- The fix should not affect normal effect cleanup (where a `Dep` with zero subscribers may be safely removed from the map).
- No observable changes to the standard effect APIs or scheduling behavior outside this specific constelletion of multiple subscribers with overlapping dependencies.

fix(reactivity): do not remove dep from depsMap when cleaning up deps of computed (#11995)


In Node environments (when __BROWSER__ is false), the `isMemberExpression` utility must correctly classify template expression strings by parsing them as real JavaScript/TypeScript expressions rather than relying on a brittle regex. This change is needed because the regex‑based check produces incorrect results for valid TypeScript constructs such as type assertions (`foo as string`, `foo.bar as string`).

**Acceptance criteria**

1. **Parser‑based check in Node**
   - When running outside the browser, `isMemberExpression(path, context)` must parse `path` using the Babel parser with the expression plugins from `context.expressionPlugins` (plus the default babel parser plugins).
   - If parsing fails, the function returns `false` (no thrown error).
   - If parsing succeeds, the function treats `TSAsExpression` and `TSTypeAssertion` nodes by unwrapping them to their inner `expression`.
   - After unwrapping, it returns `true` **only** when the resulting node type is `MemberExpression`, `OptionalMemberExpression`, or `Identifier`.

2. **Browser behavior stays unchanged**
   - In browser builds, `isMemberExpression` continues to use the existing regex‑based logic (`isMemberExpressionBrowser`) and its signature accepts only a `path` string. No context argument is required.

3. **Caller updates**
   - The `v-model` and `v-on` transform passes must call `isMemberExpression` with the transform context available in Node, so that the check respects TypeScript-related configuration.
   - The function signature when building for Node must not break the browser builds; the export is selected at build time based on `__BROWSER__`.

4. **TypeScript‑specific member expressions**
   - `foo as string`              → true   (Identifier after unwrap)
   - `foo.bar as string`         → true   (MemberExpression after unwrap)
   - `foo['bar'] as string`      → true   (MemberExpression after unwrap)
   - `foo[bar as string]`        → true   (MemberExpression with inner TS)
   - `foo() as string`           → false  (CallExpression after unwrap)
   - `a + b as string`           → false  (BinaryExpression after unwrap)

5. **Robustness**
   - The parser‑based check must never throw on any input; all parse errors are caught and treated as `false`.

The regex‑based check is preserved as `isMemberExpressionBrowser` and the parser‑based check as `isMemberExpressionNode`. The public `isMemberExpression` symbol is switched between them automatically based on the `__BROWSER__` flag.

fix(compiler-core): more robust member expression check when running in node


The `watch` option does not correctly merge handlers from mixins when multiple sources define watchers for the same property. Currently, later definitions overwrite earlier ones, so only the last watcher is active. This breaks the expected behavior that all watchers from mixins and the component itself should be preserved and invoked when the watched value changes.

**Acceptance criteria:**

- When a component mixes in multiple mixins that each define a `watch` on the same key, and the component also defines its own `watch` on that key, changing the associated data property must call **all** defined watcher handlers (from each mixin and the component).
- The merge strategy for `watch` should behave like lifecycle hook merging: handlers for the same key are collected into an array so that no definition is silently dropped.
- The merged structure must support all valid watch declaration forms (function, method name string, or object with `handler` and options).

fix(watch): fix watch option merging from mixins


**Bug: `emits` options from `mixins` and `extends` are ignored during emit validation and listener detection**

### Observed Behavior
- A component defines some emits through a `mixin` or `extends`, but does not redeclare them in its own `emits` option.
- Emitting those events in the component does **not** trigger validation (e.g., no warning even if an invalid payload is passed).
- `isEmitListener` returns `false` for listener props that match the mixin/extends emits, causing them to be incorrectly treated as fallthrough attributes.

### Expected Behavior
- Emits options from all mixins and the `extends` component should be merged with the component’s own `emits`, similar to how props are already merged.
- Validation should cover the full merged set, so an emit declared only in a mixin still undergoes validation when called with wrong arguments.
- `isEmitListener` should recognize listener props that correspond to emits from mixins/extends, preventing them from being leaked into `$attrs`.

### Impact
Components that rely on mixins or `extends` to provide common event contracts suffer from broken emit validation and attribute fallthrough behavior, leading to silent bugs and confusing developer experience.

### Desired Resolution
Ensure that the normalized emits object for a component is built by walking `mixins` and `extends` and merging their emit declarations, with the component’s own `emits` taking priority for conflicts. The resulting merged emits should be used in all emit validation and listener checks throughout the runtime.

### Notes
- The `isEmitListener` helper should accept the component definition (not just its own emits array/object) so it can internally resolve the full merged emits.
- The caching of normalized emits (similar to `__props`) would be acceptable for performance, but it is an implementation detail. The key invariant is that the merged emits are available wherever validation and listener identification occur.

fix(runtime-core/emits): merge emits options from mixins/extends


**Problem**
Using `shallowReactive` on built-in collections (Map, Set, WeakMap, WeakSet) currently wraps the contained values with reactive proxies when they are accessed via `.get()`, iterator methods, or `.forEach()`. This violates the contract of “shallow” reactivity: only the collection itself should be reactive-tracked; the items it holds must remain raw.

**Acceptance Criteria**

1. **`Map` access**
   `shallowReactive(new Map([['key', {}]])).get('key')` returns the same raw (non-reactive) object that was inserted. `isReactive(entry)` on that value must be false.

2. **Iteration**
   Iterating a shallow-reactive Set or Map – whether by `for...of`, `.values()`, `.entries()`, `Symbol.iterator`, or `.forEach()` – must yield raw values, never reactive proxies.

3. **Addition of values**
   When a new value is added via `.add()` or `.set()`, the stored value must not be converted to a reactive proxy; it remains as-is. The act of adding still triggers the usual dependency tracking & effect re-runs.

4. **Other collection operations**
   `.has()`, `.size`, `.delete()`, `.clear()` continue to work as normal (tracking/triggering), but they do not cause deep reactivity conversion of any stored values.

5. **Collection types**
   The behavior applies uniformly to `Map`, `Set`, `WeakMap`, and `WeakSet`.

6. **Compatibility**
   - Deep `reactive()` collections remain unaffected – they still deeply wrap values as before.
   - Readonly collections are unaffected.
   - `shallowReactive` on plain objects is unaffected.

**Edge Cases**

- Nested collections: `shallowReactive(new Map([['k', new Map()]]))` should return the inner `Map` raw when accessed – no deep conversion of inner collections either.
- Values marked with `markRaw` remain raw (this was already the case, but must not regress).
- Keys in `Map`/`WeakMap` are not wrapped (already the case); this behavior must remain.

**Outcome**
A `shallowReactive` collection acts as a reactive container for its structure (tracking size, presence, and mutations) while leaving all items untouched, matching the mental model of shallow reactive objects.

fix(reactivity): shallowReactive for collections (#1204)


**Title:** Custom elements: missing attribute reflection, type casting, and handling of pre-upgrade properties

**Package:** `@vue/runtime-dom`

**Description:**
I’m building web components using `defineCustomElement` and have encountered several issues that make the elements behave unexpectedly from a standards/compatibility perspective:

1. **No attribute reflection**
When I update a prop on the custom element instance (e.g., `element.foo = 'hello'`), the corresponding HTML attribute (e.g., `foo`) is not updated. I would expect that for primitive values (`string`, `number`, `boolean` true) the attribute is set or updated, while for falsy values (except booleans) the attribute is removed. Rich data (objects, arrays) should never be reflected.

2. **Attribute changes don’t cast to declared prop types**
If a prop is declared with a constructor like `Number` or `Boolean`, changing the attribute via `setAttribute` leaves the prop value as a raw string. For example:
   - `element.setAttribute('foo', '2e1')` should result in `element.foo === 20` (Number).
   - For a Boolean prop, setting the attribute to any value (even an empty string) should set the prop to `true`; removing the attribute should set it to `false`.

3. **Properties set before upgrade are lost**
If I create an element, set a property on it before the custom element is defined/upgraded, and then append it, the initial property value is discarded. For example:
   ```js
   const el = document.createElement('my-el');
   el.foo = 'hello';
   container.appendChild(el);
   customElements.define('my-el', MyElement); // 'hello' is lost
   ```
   I expect the value set before the upgrade to be preserved and used as the initial prop value.

These issues prevent interoperability with other frameworks that rely on attributes for state, break type-safe prop contracts, and violate the typical custom element lifecycle where pre-upgrade property assignments should be respected.

**Expected behavior:**
- Prop setters should reflect to the hyphenated attribute (and remove it when the value is `null`/`undefined`/`false`, except for Boolean props where `true` sets the attribute to `""` and `false` removes it).
- Attribute changes should coerce the value according to the prop type declaration (Number, Boolean, etc.) before propagating to the prop.
- Properties assigned to the element before definition/upgrade should be captured and treated as the initial prop values.

feat: custom element reflection, casting and edge cases


In the compat build, `Vue.extend` merges lifecycle hooks from mixins and `extends` into arrays (e.g., `created: [fn1, fn2]`), but the runtime fails to call them correctly. Registration code passes the raw array to lifecycle registration APIs, which expect a single function, so only the first hook is invoked or an error occurs. Similarly, the `mergeHook` strategy used during option merging does not correctly flatten the source (`from`) parameter when it is already an array, producing incorrectly nested arrays.

**Required behavior:**
- When a merged lifecycle hook is an array, each function in the array must be registered individually (bound to the instance) using the standard lifecycle registration APIs (`onBeforeMount`, `onMounted`, etc.).
- The `mergeHook` function in option merging must normalize **both** its `to` and `from` arguments to arrays before computing the deduplicated set, so that a plain function on either side or an already-merged array on either side is handled consistently.
- This applies to all lifecycle hooks including the deprecated `beforeDestroy`/`destroyed` ones (when explicitly enabled).

**Out of scope:** Non-compat (Vue 3 only) usage; hooks not originating from `Vue.extend`.

fix(compat): correctly merge lifecycle hooks when using Vue.extend (#3762)


In `<script setup>`, when `setup()` is async, the current instance is lost after any `await` expression. This means lifecycle hooks (e.g., `onMounted`) registered after the `await` silently fail, and other instance-dependent APIs break.

We need the compiler to automatically wrap top-level `await` expressions in `async setup()` so the current instance is saved before the await and restored afterward. The wrapping must only apply to `await` that appears directly in the setup function body (not inside nested functions, where the instance is already captured correctly). The runtime should expose a helper that performs the save/restore using the existing `getCurrentInstance`/`setCurrentInstance` machinery.

**Acceptance criteria**:
- After `await somePromise` in `<script setup>`, subsequent `onMounted(() => ...)` calls work and reference the same component instance as before the await.
- Nested `await` inside regular functions (not in `setup` top-level) are left untouched.
- The wrapped `await` expression’s value is still correctly unwrapped.

feat(sfc): auto restore current instance after await statements in async setup()


When a component’s render returns a fragment whose only non-comment child is a single element (e.g. a template with comments or placeholder v-if comments), inherited attributes (class, style, listeners, etc.) are not applied to that element. The expected behavior is that fallthrough attributes should find and merge onto the single element within the fragment, ignoring comment nodes, and no extraneous-attributes warning should fire when the element is a valid target. This only needs to work in dev mode (comments are stripped in production).

fix(runtime-core): support attr merging on child with root level comments


**Title**: Prevent orphaned reactive effects when created in a stopped effect scope (e.g., async setup resumed after unmount)

**Body**:

When a component's `setup()` contains a top-level `await`, the compiler-emitted `withAsyncContext` helper calls `__restore()` to restore the component’s `EffectScope` after the promise resolves. If the component is unmounted while pending (e.g., the `<Suspense>` boundary is replaced or destroyed before resolution), the component’s scope is stopped. When the async function resumes, any new `watchEffect`, `watch`, or `computed` sees the stopped scope as the active scope (`activeEffectScope`).

The current `ReactiveEffect` constructor correctly avoids pushing the effect into the stopped scope’s list (because `activeEffectScope.active` is false), but **the effect itself is still created with its ACTIVE flag set**. This produces an “orphan” effect: no scope tracks it, yet it can subscribe to reactive dependencies and run forever without any way to stop it.

Similarly, creating a child `EffectScope` inside a stopped parent (non-detached) currently leaves the child in an active, detached state, which is inconsistent with the parent being stopped.

**Expected behavior**:

1. **`ReactiveEffect` constructor**: If an active scope exists but is not active (i.e., `activeEffectScope` is set but `activeEffectScope.active === false`), the newly created effect must be marked as inactive so that it never tracks dependencies and never runs.
2. **`EffectScope` constructor**: If a non-detached scope is created while the parent scope (the then-active scope) is already stopped, the new scope must be created as inactive. Calling `.run()` on such a scope should be a safe no-op (without a DEV warning), mirroring the behavior of a scope that was explicitly stopped.

This ensures that effects and scopes created after the intended owner’s lifetime are effectively dead, preventing memory leaks and unintended side effects.

fix(reactivity): prevent orphan effect when created in a stopped scope (#14778)


**Title:** Nested `shadowRoot: false` custom elements do not re-mount correctly when parent is conditionally rendered

**Description:**
When using `defineCustomElement` with `shadowRoot: false`, toggling the visibility of a parent custom element’s rendered content (e.g., by binding a prop that controls a `v-if`-like condition inside the custom element’s render function) causes any nested custom elements that also use `shadowRoot: false` to fail to render their own slotted content after they are re-attached to the DOM.

**Steps to reproduce:**
1. Define two custom elements, both configured with `shadowRoot: false`.
2. The parent custom element conditionally renders a wrapper (`<div>`) that contains its default slot, controlled by a Boolean prop (e.g., `isShown`).
3. The child custom element renders its default slot inside a wrapper.
4. Compose them so that the child custom element is placed inside the parent’s default slot.
5. Mount the application, then toggle the parent’s `isShown` prop from `true` to `false` and back to `true`.

**Observed behavior:**
- After toggling back to `true`, the child custom element does not render its default slot content (e.g., the text “child” is missing) or fails to appear entirely.

**Expected behavior:**
- After the parent re-shows its content, the nested custom element must re-appear with its slot content intact, exactly matching the initial render.
- The parent custom element must parse its own slots **only once** during its lifecycle, even if it is disconnected and reconnected multiple times, avoiding unnecessary DOM traversal or slot‑source confusion.
- When a nested `shadowRoot: false` custom element is removed from the DOM and later re-inserted, it must perform a **fresh mount** (i.e., a complete re-initialization) rather than attempting to update a stale or partially torn‑down instance.
- The behavior must not regress for custom elements using `shadowRoot: true`.

**Acceptance criteria:**
- After toggling `isShown` off and on, the DOM structure inside the parent custom element is identical to the initial render, including all slot‑projected content of child custom elements.
- Rapid toggling does not cause duplicate slot content, missing elements, or memory leaks.
- The fix must apply to arbitrarily deep nesting of `shadowRoot: false` custom elements.

fix(custom-element): ensure proper remount and prevent redundant slot parsing with shadowRoot false (#13201)


When the TypeScript-based type resolution for SFC macros (e.g., `defineProps`, `defineEmits`) encounters an unresolvable type, the current error messages are unhelpfully generic and often include a code frame that points to the wrong source location. This makes debugging type issues in Vue single file components unnecessarily difficult.

**Acceptance Criteria**

1. **Specific error messages**
   The compiler must produce distinct, human-readable error messages for the following failure scenarios:
   - A type reference that cannot be resolved at all → `Unresolvable type reference`
   - A computed key in a type literal that is not supported → `Unsupported computed key in type referenced by a macro`
   - An unsupported index access type (e.g., `X[K]`) → `Unsupported index type`
   - An import source that cannot be resolved → `Failed to resolve import source "<path>" for type <name>`
   The message must include the exact import source path and the imported type name.

2. **Accurate error location across files and script blocks**
   When the error originates in an imported file (`.ts`, `.tsx`, or `.vue`), the error’s code frame must:
   - Show the **correct filename** of the file that contains the problematic type.
   - Show the **correct source code** of that file.
   - Highlight the exact node using positions **relative to the relevant script block** when the file is a `.vue` SFC (i.e., the `<script setup>` or `<script>` block where the type is actually declared).
   This requires that the `TypeScope` for any file or block carries an `offset` that accounts for the block’s start position within the file, and that `ScriptCompileContext.error` uses this offset (together with the scope’s filename and source) when a scope is provided.

3. **No regressions for nested scopes**
   When resolving types across chains of imports (e.g., `./foo.ts` that re-exports from `./bar.vue`), errors inside the final referenced file must be reported against that file’s source and correct offset, not the top-level component.

4. **Unaltered runtime behavior**
   The resolution logic must otherwise remain unchanged; only the error output and location accuracy are affected. All existing valid type constructs continue to resolve successfully.

refactor: improve type resolve error output


We need to fix a bug in the static fragment optimization where `traverseStaticChildren` does not honor `PatchFlags.BAIL` on child VNodes, leading to incorrect DOM updates.

**Context**
When a fragment is marked `STABLE_FRAGMENT`, the reconciler uses `traverseStaticChildren` during the patch phase to quickly inherit static structure from old children to new children, without performing a full diff. However, if one of those children is itself a block VNode with `patchFlag = BAIL`, that child's `dynamicChildren` array is invalid and the child should be fully re-patched – the static traversal must not recurse into it.

The current implementation unconditionally traverses into all children that are not text nodes (except for the `shallow` guard). This can cause stale or wrong DOM structure when the BAIL child's shape changes between renders.

**Observed problem**
A stable fragment containing a text node followed by a block VNode with `BAIL` (e.g., a keyed element whose inner children change count) will fail to properly update. The old child's `el` may be incorrectly reused or the wrong nodes may be left in the DOM, leading to mismatched output.

**Expected behavior**
`traverseStaticChildren` should skip recursion into any child VNode that carries `PatchFlags.BAIL`. The child should be handled later by the normal `patch` logic with full diffing enabled. This matches the intent of the BAIL flag: the child cannot be statically optimized.

**Suggested fix**
In the `traverseStaticChildren` helper, add a guard before the recursive call: only recurse if the child’s `patchFlag` is not `PatchFlags.BAIL`. The check should be applied after the existing shallow guard.

**Affected API / internal**
- `traverseStaticChildren` (renderer.ts)
- Related: the stable fragment patching path that calls this function after cloning the children array.

fix(runtime-core): avoid traversing static children for vnodes w/ PatchFlags.BAIL (#11115)


In Vue, when `<Suspense>` is placed inside `<Transition mode="out-in">`, changing the default slot to a new async component can cause the previous component’s content to be unmounted twice: once when the Suspense fallback is triggered, and again when the new async component resolves. This double unmount can lead to DOM removal errors (e.g., `NotFoundError`) and lifecycle hooks firing more than once for the leaving component.

### Reproduction scenario
1. Wrap a `<Suspense timeout="0">` with a `<Transition mode="out-in">`.
2. Use a dynamic component inside the Suspense’s default slot.
3. Provide a simple fallback (e.g., “Loading…”).
4. Switch from a synchronous component (`CompA`) to an async component (`CompB`).

### Current (buggy) behavior
- The leave transition for `CompA` begins, and Suspense enters its fallback state.
- While the fallback is waiting for `CompA`’s leave to finish (due to `out-in`), the async `CompB` resolves immediately (timeout=0).
- The Suspense resolve logic attempts to unmount the still-leaving `CompA` a second time, before the fallback has been mounted.
- The same DOM nodes are removed twice, and `onVnodeBeforeUnmount` (or similar hooks) are called again.

### Expected behavior
The previous component (`CompA`) should be unmounted **exactly once** during the entire transition cycle. The resolve step should not unmount the active branch while a fallback mount is pending due to a `Transition` leave delay. After the leave transition completes, the fallback should be mounted (if still needed), and the new content should appear cleanly without duplicate removal attempts or lifecycle calls.

### Acceptance criteria
- When `<Transition mode="out-in">` wraps `<Suspense>` and a switch to an async component occurs, the leaving component’s unmount lifecycle hooks must fire exactly one time.
- No DOM removal errors (e.g., “Failed to execute ‘removeChild’”) are thrown.
- The entering component renders correctly after the leave transition finishes, even if the async dependency resolves during the leave period.
- The fix must not break normal Suspense behavior outside of transitions, nor affect other Transition modes (e.g., `in-out` or `default`).

fix(suspense): avoid unmount activeBranch twice if wrapped in transition (#9392)


Ensure that attribute fallthrough and scopeId inheritance traverse nested `DEV_ROOT_FRAGMENT` fragments.

When a component returns a vnode tree where the render-root is a Fragment with the `DEV_ROOT_FRAGMENT` patch flag, and that fragment’s single non‑comment child is itself another `DEV_ROOT_FRAGMENT` (e.g., from a conditional branch that wraps its result in a dev‑only fragment), attributes like `class` and `style` fail to reach the actual inner element. The same applies to scopeId inheritance.

The root‑resolution logic (`filterSingleRoot` / `getChildRoot`) should walk through any number of `DEV_ROOT_FRAGMENT` wrappers until it finds a plain element root, so that fallthrough attrs and scopeId are set on the correct DOM node. The setroot callback used for dynamic updates should still target the outermost `DEV_ROOT_FRAGMENT` in the chain, preserving correct block replacement during `v-if` toggles.

fix(runtime-core): filter single root for nested DEV_ROOT_FRAGMENT (#8593)


shallowReactive Map/Set loses reactive/readonly wrappers on inserted items.

When values are inserted into a shallowReactive Map (via `.set()`) or Set (via `.add()`), any existing reactive, readonly, or shallow wrapper on the value is stripped, turning it into a raw object. Instead, the original proxy identity should be preserved – inserted reactive values must remain reactive, readonly values must remain readonly, and shallow values must remain shallow. The shallow flag on the collection should only mean that the collection itself is shallow-reactive, not that it unwraps its elements.
```

fix(reactivity): shallowReactive map "unwraps" the nested refs (#8503)


```
fix(compiler-sfc): support `${configDir}` in include/exclude patterns for TS 5.5+

TypeScript 5.5 introduced the `${configDir}` variable usable in tsconfig `include`, `exclude`, and `paths` entries.
When a project (or an extended/referenced config) uses `${configDir}` in its `include` or `exclude` glob patterns, the compiler's type resolver currently fails to match the source file against the correct config. This causes `defineProps` with type imports resolved through path aliases to fail, even though the types are reachable and the paths resolve correctly under `tsc`.

We need to expand `${configDir}` to the directory of the tsconfig that declares the pattern, but **only** when the runtime TypeScript version is 5.5 or later (since older versions do not understand the variable). The expansion must work correctly across `extends` and project references — the variable must resolve relative to the config file in which it appears, not the root config.
```

fix(compiler-sfc): support `${configDir}` in paths for TypeScript 5.5+ (#13491)


The `created` and `beforeCreate` lifecycle hooks (including those declared via mixins, `extends`, and global mixins) do not participate in the component error handling system. Errors thrown inside these hooks are not caught by ancestor `onErrorCaptured` handlers, which breaks the expected error boundary behavior. This is inconsistent with all other lifecycle hooks like `mounted`, `updated`, etc., which correctly propagate errors to registered error handlers.

**Acceptance criteria**

- If a `created` or `beforeCreate` hook throws an error, a parent component’s `onErrorCaptured` callback must be invoked, just as it is for errors from other lifecycle hooks.
- The `info` argument passed to `onErrorCaptured` must be `'created hook'` when the error originates from a `created` hook, and `'beforeCreate hook'` when it originates from a `beforeCreate` hook.
- This must work regardless of how the hook is provided: directly on the component options, via local mixins, via `extends`, or via globally registered mixins.
- The error should not bypass the error handling mechanism and cause an unhandled exception; it must be fully handled by the error boundary if the `onErrorCaptured` handler returns `false` to stop propagation.

fix(runtime-core): error handling for created/beforeCreate hooks


When a dynamic `ref` key changes within the same update cycle (for example because a nested component’s setup mutates the key) or a component unmounts mid-update, the old ref target must not be set with the current value. Any pending post-render set-ref job that would write the new value into the old ref slot should be cancelled, so that `$refs` remains consistent and no stale assignments occur.

**Acceptance criteria**

1. **Unmount in the same tick** – If a child component or effect triggers an unmount of the parent during a render update, the ref that was about to be set must be forced to `null`. After the tick the consumer-visible ref (e.g. a template ref variable or `$refs` entry) must correctly reflect the unmounted state.

2. **Dynamic ref key change** – When `:ref="dynamicKey"` changes from `"oldKey"` to `"newKey"` and the component is still mounted, `$refs.oldKey` must become `null` and `$refs.newKey` must reference the current component instance. No deferred set-ref job should later restore the value under `oldKey`.

3. **Order independence** – The behaviour must be correct regardless of whether the ref key change happens in a parent’s setup, a child’s setup, or an immediate watcher, as long as it is part of the same flush cycle.

4. **No residual jobs** – Cancelled jobs must not leak or keep obsolete references alive.

fix(templateRef): prevent unnecessary set ref on dynamic ref change or component unmount (#12642)


We need to add dedicated appear-specific lifecycle hooks and CSS class handling for the `<Transition>` component, ensuring they are triggered on initial mount when `appear` is `true`, with fallback to the standard `enter` hooks and classes when explicit appear hooks are not provided.

**Problem / Feature Request**

Currently, when a `<Transition>` has `appear` set, it reuses the same hooks (`onBeforeEnter`, `onEnter`, `onAfterEnter`, `onEnterCancelled`) and the same CSS classes (`enterActiveClass`, `enterFromClass`, `enterToClass`) for the initial appear transition. There is no way to define separate appear-specific hooks or classes, and no clear separation between the appear and subsequent enter phases. This leads to the following issues:

1. **No separate appear hooks:** You can’t have different behavior for the initial appear animation vs a later enter animation. The same `onEnter` callback fires in both cases, making it hard to distinguish the two.
2. **CSS class overlap:** The same enter classes are applied during appear, which means you cannot define distinct appear styles (e.g., a different animation or no animation on first mount). If you attempt to set `appearFromClass` etc., they are simply ignored.
3. **Fallback behavior missing:** Even if separate appear hooks existed, there should be a sensible default: if no appear hook is provided, it should fall back to the corresponding enter hook (and for CSS classes, the appear classes should default to the enter classes).

**Desired Behavior**

Introduce four new optional props on `BaseTransitionProps` (and therefore on `Transition`):

- `onBeforeAppear(el: Element)`
- `onAppear(el: Element, done: () => void)`
- `onAfterAppear(el: Element)`
- `onAppearCancelled(el: Element)`

The existing `onBeforeEnter`, `onEnter`, `onAfterEnter`, `onEnterCancelled` remain unchanged.

**Logic for appear (when `appear` is `true` and the component is mounting):**

1. *Hook resolution:*
   - If `onBeforeAppear` is defined, call it; otherwise call `onBeforeEnter`.
   - If `onAppear` is defined, call it (with the same `done` callback signature); otherwise call `onEnter`.
   - After appear finishes, call `onAfterAppear` if defined; else `onAfterEnter`.
   - On appear cancellation, call `onAppearCancelled` if defined; else `onEnterCancelled`.

2. *CSS class handling (DOM Transition):*
   - During appear, apply the appear-specific classes (`appearActiveClass`, `appearFromClass`, `appearToClass`). If any of these are not set, they should fall back to the corresponding enter classes (`enterActiveClass`, `enterFromClass`, `enterToClass`).
   - Once the appear transition finishes, the element should have no transition classes and must be ready for subsequent enter/leave transitions using the standard enter/leave classes.
   - Crucially, after the appear transition ends, the fallback of appear classes to enter classes must be **reset** so that the next enter transition uses the original enter classes, not the appear classes.

3. *Persistence (KeepAlive):*
   When a component is kept alive and re-mounted, it should be treated as an appear, not an enter. So the same appear hooks (with fallback) should fire, not the enter hooks.

4. *No appear hooks provided: full fallback.*
   If none of the appear-specific hooks are supplied, the behavior must be identical to the current behavior where `appear: true` uses the enter hooks and enter classes, with no unintended side effects.

5. *Isolation from subsequent enters:*
   After the appear transition completes (or is cancelled), any future enter transitions (e.g., `v-if` toggling) must **not** call appear hooks. They should use the regular enter hooks exclusively. The CSS classes must also be the regular enter/leave classes, not the appear classes.

**Acceptance Criteria**

- With `<Transition appear>` and all `on*Appear` hooks defined, the appear hooks fire in sequence on mount, and the enter hooks do **not** fire at all.
- With `onAfterAppear` defined, it is called only after the appear transition finishes, not during enter. `onAfterEnter` is not called.
- If `on*Appear` hooks are `undefined`, the component falls back to the corresponding `on*Enter` hook everywhere (preserving backward compatibility).
- DOM `<Transition>` with `appear` and custom appear classes (e.g., `appearFromClass="fade-in"`) uses those classes only for the initial appear, then after appear finishes, the element is clear and subsequent enters use the normal enter classes.
- In KeepAlive scenarios, remounting triggers appear hooks (with fallback), not enter hooks.
- Cancelling an appear transition (e.g., leaving during appear) calls `onAppearCancelled` if defined, else `onEnterCancelled`.
- No breaking changes to existing usage that only defines enter hooks and `appear: true`.

fix(transition): fix appear hooks handling


As a maintainer, I need `compileScript` to report the set of external files that were used to resolve type-only imports during script compilation. This will allow higher-level tooling (e.g., Vite) to watch these files and invalidate the HMR cache when they change.

### Background

When `<script setup lang="ts">` uses `defineProps<T>()` or `defineEmits<T>()` with types imported from other files (`.ts`, `.d.ts`, or even `.vue` files), the compiler resolves those type imports to infer runtime prop/emit information. Currently, the resolved file paths are not exposed anywhere, so if one of those external type files is modified, the HMR cache is not invalidated (leaving the component potentially using stale type information).

### Request

1. **Track resolved type dependencies inside `ScriptCompileContext`.**
   Each time a type import source is successfully resolved (via `resolveTypeFromImport` or similar), add the resolved absolute file path to a set on the context. Use a `Set<string>` to avoid duplicates.

2. **Expose the dependencies on the compiled script block output.**
   After all type resolution is complete, convert the set to an array and include it as the `deps` property on the returned `SFCScriptBlock` object. If no type dependencies were resolved, the property should remain `undefined` (or an empty array — consistency with the existing optional `deps` field is fine).

3. **Include all transitive dependencies.**
   The set should include every file that was actually loaded and parsed to extract type information (e.g., chained imports, re-exports, `.vue` files with `<script lang="ts">`). The exact paths written should be the final resolved paths (post-tsconfig path mapping, node_modules resolution, extension resolution, etc.).

4. **Do not include unresolved or errored imports.**
   Only successfully resolved files that contributed to type inference should be tracked. Failed imports should not pollute the dependency list.

5. **Output format:**
   The `deps` field in the compiled result should be an array of strings (the file paths). The order does not matter.

This effectively completes the existing `TODO (hmr) register dependency file on ctx` comment in `resolveTypeFromImport`.

feat(compiler-sfc): expose type import deps on compiled script block