Skip to content

feat(compiler-core): resolve slot prop bindings as components#13573

Merged
edison1105 merged 11 commits intovuejs:minorfrom
Fuzzyma:allow-slot-props-components
May 6, 2026
Merged

feat(compiler-core): resolve slot prop bindings as components#13573
edison1105 merged 11 commits intovuejs:minorfrom
Fuzzyma:allow-slot-props-components

Conversation

@Fuzzyma
Copy link
Copy Markdown

@Fuzzyma Fuzzyma commented Jul 5, 2025

close #8553

I had a shot about implementing the issue I raised in #8553.

Happy for any feedback

Summary by CodeRabbit

  • New Features

    • Allow scoped slot bindings to be used as component tags, including
      kebab/camel-cased and namespaced usages. Slot scope bindings take
      precedence over setup bindings during component resolution.
  • Tests

    • Added test coverage validating component behavior when accessed via slot props in standard and SSR compilation modes.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jul 5, 2025

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request enhances the compiler's component resolution to support scoped slot bindings. When element tags reference identifiers from slot props, the compiler now resolves them directly rather than applying standard component resolution helpers, with fallback to normal resolution when identifiers are out of scope.

Changes

Cohort / File(s) Summary
Test Coverage for Scoped Slot Component Resolution
packages/compiler-core/__tests__/transforms/transformElement.spec.ts, packages/compiler-ssr/__tests__/ssrComponent.spec.ts
Added test cases validating that components referenced via scoped slot bindings (destructured and namespaced) are emitted directly into vnode creation without triggering component resolution helpers or setup accessors. Includes regression test confirming normal resolution resumes when bindings go out of scope.
Component Type Resolution Enhancement
packages/compiler-core/src/transforms/transformElement.ts
Introduced resolveScopeReference helper and scoped identifier resolution stage in resolveComponentType. Now checks context.identifiers (for slot-prop tags) before proceeding to setup binding and standard component resolution, supporting dot-qualified tags via namespace resolution.

Sequence Diagram(s)

sequenceDiagram
    participant Compiler
    participant ResolveScopeRef as resolveScopeReference
    participant Identifiers as context.identifiers
    participant SetupResolution as Setup Binding<br/>Resolution
    participant Fallback as RESOLVE_COMPONENT<br/>Fallback
    participant CodeGen as Code Generation

    Compiler->>ResolveScopeRef: resolveComponentType(tag)
    ResolveScopeRef->>Identifiers: Check raw tag + variants<br/>(camelCase, PascalCase)
    alt Found in Slot Scope
        Identifiers-->>ResolveScopeRef: identifier match
        ResolveScopeRef-->>CodeGen: Emit direct reference<br/>(_createVNode(slotProp.Foo))
    else Not in Scope
        Identifiers-->>ResolveScopeRef: no match
        ResolveScopeRef->>SetupResolution: Check setup bindings
        alt Setup Match
            SetupResolution-->>CodeGen: Emit setup accessor
        else No Setup Match
            SetupResolution->>Fallback: Fallback to runtime resolution
            Fallback-->>CodeGen: Emit _resolveComponent("Foo")
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

The changes introduce new logic for identifier scope resolution with supporting test coverage across multiple file types, requiring understanding of component resolution mechanics and slot binding scoping rules.

Suggested labels

scope: compiler, scope: slots

Suggested reviewers

  • sxzz
  • baiwusanyu-c
  • KazariEX

Poem

🐰 A scope so clever, a tag so true,
From slot-prop bindings, components shine through!
No helpers needed when in range,
Direct emission—what a change!
hops gleefully

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title 'feat(compiler-core): resolve slot prop bindings as components' accurately describes the primary change: enabling slot prop variables to be resolved and used as components in the compiler-core package.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@netlify
Copy link
Copy Markdown

netlify Bot commented Jul 5, 2025

Deploy Preview for vue-sfc-playground failed. Why did it fail? →

Name Link
🔨 Latest commit 50f23f9
🔍 Latest deploy log https://app.netlify.com/projects/vue-sfc-playground/deploys/68698d0ea187660008e6f194

@netlify
Copy link
Copy Markdown

netlify Bot commented Jul 5, 2025

Deploy Preview for vue-next-template-explorer failed. Why did it fail? →

Name Link
🔨 Latest commit 50f23f9
🔍 Latest deploy log https://app.netlify.com/projects/vue-next-template-explorer/deploys/68698d0e89104a00086374aa

@Fuzzyma Fuzzyma marked this pull request as ready for review July 6, 2025 18:38
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/compiler-core/src/transforms/transformElement.ts (1)

388-391: Consider removing or documenting the commented-out code.

The commented-out code for slot scope bindings handling should either be removed if not needed, or documented with a TODO comment explaining the future implementation plan.

-  // const fromSlotScope = checkType(BindingTypes.SLOT_SCOPE)
-  // if (fromSlotScope) {
-  //   return fromSlotScope
-  // }
+  // TODO: Future implementation for slot scope bindings
+  // const fromSlotScope = checkType(BindingTypes.SLOT_SCOPE)
+  // if (fromSlotScope) {
+  //   return fromSlotScope
+  // }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eca0e1c and eb8be28.

📒 Files selected for processing (5)
  • packages/compiler-core/__tests__/transforms/transformElement.spec.ts (2 hunks)
  • packages/compiler-core/src/codegen.ts (1 hunks)
  • packages/compiler-core/src/options.ts (1 hunks)
  • packages/compiler-core/src/transform.ts (2 hunks)
  • packages/compiler-core/src/transforms/transformElement.ts (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
packages/compiler-core/__tests__/transforms/transformElement.spec.ts (2)
packages/compiler-core/src/index.ts (1)
  • BindingTypes (11-11)
packages/compiler-core/src/runtimeHelpers.ts (1)
  • RESOLVE_COMPONENT (26-28)
packages/compiler-core/src/codegen.ts (1)
packages/compiler-core/src/options.ts (1)
  • CodegenOptions (307-349)
🔇 Additional comments (7)
packages/compiler-core/src/options.ts (1)

203-203: LGTM! Well-integrated interface extension.

The addition of the optional identifiers property follows the existing pattern and maintains backward compatibility. The type signature is consistent with identifier tracking used elsewhere in the codebase.

packages/compiler-core/src/codegen.ts (1)

123-126: LGTM! Proper type boundary maintenance.

Adding 'identifiers' to the omitted properties list is correct and maintains type consistency. This follows the established pattern of excluding transform-specific properties from the codegen context.

packages/compiler-core/src/transforms/transformElement.ts (2)

285-300: LGTM! Well-implemented slot prop component resolution.

The new step correctly checks for components from slot props using context.identifiers. The logic properly handles both direct tags and dot notation components, and is appropriately gated for non-browser builds.


302-302: LGTM! Proper step renumbering.

The comment numbering has been correctly updated to reflect the new step insertion.

Also applies to: 319-319, 333-333

packages/compiler-core/src/transform.ts (2)

146-146: LGTM! Well-designed parameter addition.

Adding the optional identifiers parameter with Object.create(null) as the default is a good design choice. It enables external control of identifier tracking while maintaining backward compatibility.


191-191: LGTM! Proper context assignment.

The identifiers assignment correctly integrates the parameter into the transform context.

packages/compiler-core/__tests__/transforms/transformElement.spec.ts (1)

124-141: Well-structured test for slot prop component resolution.

The test correctly verifies that components referenced via scoped slot bindings (e.g., {Foo}) are not resolved as regular components. The test expectations are appropriate:

  • No RESOLVE_COMPONENT helper injection
  • Component not added to components list
  • Root node tag remains as the parent component

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jul 8, 2025

Size Report

Bundles

File Size Gzip Brotli
compiler-dom.global.prod.js 86.6 kB (+136 B) 30.3 kB (+45 B) 26.7 kB (+78 B)
runtime-dom.global.prod.js 113 kB 42.4 kB 38 kB
vue.global.prod.js 172 kB (+136 B) 62.3 kB (+42 B) 55.5 kB (+2 B)

Usages

Name Size Gzip Brotli
createApp (CAPI only) 51.3 kB 20 kB 18.3 kB
createApp 60.4 kB 23.3 kB 21.2 kB
createApp + vaporInteropPlugin 101 kB 36.2 kB 32.8 kB
createVaporApp 27.3 kB 10.6 kB 9.73 kB
createSSRApp 64.9 kB 25.1 kB 22.8 kB
createVaporSSRApp 33.3 kB 12.7 kB 11.6 kB
defineCustomElement 67 kB 25.3 kB 23 kB
defineVaporCustomElement 40 kB 14.2 kB 13.1 kB
overall 75.4 kB 28.7 kB 26.1 kB

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jul 8, 2025

Open in StackBlitz

@vue/compiler-core

pnpm add https://pkg.pr.new/@vue/compiler-core@13573
npm i https://pkg.pr.new/@vue/compiler-core@13573
yarn add https://pkg.pr.new/@vue/compiler-core@13573.tgz

@vue/compiler-dom

pnpm add https://pkg.pr.new/@vue/compiler-dom@13573
npm i https://pkg.pr.new/@vue/compiler-dom@13573
yarn add https://pkg.pr.new/@vue/compiler-dom@13573.tgz

@vue/compiler-sfc

pnpm add https://pkg.pr.new/@vue/compiler-sfc@13573
npm i https://pkg.pr.new/@vue/compiler-sfc@13573
yarn add https://pkg.pr.new/@vue/compiler-sfc@13573.tgz

@vue/compiler-ssr

pnpm add https://pkg.pr.new/@vue/compiler-ssr@13573
npm i https://pkg.pr.new/@vue/compiler-ssr@13573
yarn add https://pkg.pr.new/@vue/compiler-ssr@13573.tgz

@vue/compiler-vapor

pnpm add https://pkg.pr.new/@vue/compiler-vapor@13573
npm i https://pkg.pr.new/@vue/compiler-vapor@13573
yarn add https://pkg.pr.new/@vue/compiler-vapor@13573.tgz

@vue/reactivity

pnpm add https://pkg.pr.new/@vue/reactivity@13573
npm i https://pkg.pr.new/@vue/reactivity@13573
yarn add https://pkg.pr.new/@vue/reactivity@13573.tgz

@vue/runtime-core

pnpm add https://pkg.pr.new/@vue/runtime-core@13573
npm i https://pkg.pr.new/@vue/runtime-core@13573
yarn add https://pkg.pr.new/@vue/runtime-core@13573.tgz

@vue/runtime-dom

pnpm add https://pkg.pr.new/@vue/runtime-dom@13573
npm i https://pkg.pr.new/@vue/runtime-dom@13573
yarn add https://pkg.pr.new/@vue/runtime-dom@13573.tgz

@vue/runtime-vapor

pnpm add https://pkg.pr.new/@vue/runtime-vapor@13573
npm i https://pkg.pr.new/@vue/runtime-vapor@13573
yarn add https://pkg.pr.new/@vue/runtime-vapor@13573.tgz

@vue/server-renderer

pnpm add https://pkg.pr.new/@vue/server-renderer@13573
npm i https://pkg.pr.new/@vue/server-renderer@13573
yarn add https://pkg.pr.new/@vue/server-renderer@13573.tgz

@vue/shared

pnpm add https://pkg.pr.new/@vue/shared@13573
npm i https://pkg.pr.new/@vue/shared@13573
yarn add https://pkg.pr.new/@vue/shared@13573.tgz

vue

pnpm add https://pkg.pr.new/vue@13573
npm i https://pkg.pr.new/vue@13573
yarn add https://pkg.pr.new/vue@13573.tgz

@vue/compat

pnpm add https://pkg.pr.new/@vue/compat@13573
npm i https://pkg.pr.new/@vue/compat@13573
yarn add https://pkg.pr.new/@vue/compat@13573.tgz

commit: ac2c5d2

@gcaaa31928
Copy link
Copy Markdown
Contributor

Is this feature expected to be part of a minor release soon?

@Fuzzyma Fuzzyma force-pushed the allow-slot-props-components branch from 08e9cf2 to dbfa866 Compare April 30, 2026 10:02
@Fuzzyma
Copy link
Copy Markdown
Author

Fuzzyma commented Apr 30, 2026

Suggested final commit message:

feat(compiler-core): resolve slot prop bindings as components

Allow scoped slot bindings to be used as component tags, including
kebab/camel-cased and namespaced usages. Slot scope bindings take
precedence over setup bindings during component resolution.

Didnt want to squash to retain history for now

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/compiler-core/__tests__/transforms/transformElement.spec.ts (1)

229-245: ⚡ Quick win

Add a namespaced out-of-scope fallback regression case.

You test in-scope slotProps.Foo, but there isn’t a paired case proving that the same tag falls back to _resolveComponent(...) once outside the slot scope.

Suggested test shape
+  test('does not resolve namespaced component from inactive scoped slot bindings', () => {
+    const { code } = baseCompile(
+      `<Example v-slot="slotProps"><slot-props.Foo /></Example><slot-props.Foo />`,
+      {
+        prefixIdentifiers: true,
+        isNativeTag: tag => tag !== 'slot-props.Foo',
+        bindingMetadata: { Example: BindingTypes.SETUP_CONST },
+      },
+    )
+
+    expect(code).toContain(`_createVNode(slotProps.Foo)`)
+    expect(code).toContain(`_resolveComponent("slot-props.Foo")`)
+  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/compiler-core/__tests__/transforms/transformElement.spec.ts` around
lines 229 - 245, Add a paired regression test to ensure namespaced tag
"slot-props.Foo" falls back to component resolution outside slot scope: create a
new test (e.g., "resolve namespaced component falls back out of scoped slot")
that calls baseCompile on a template where <slot-props.Foo /> appears outside
the v-slot (opposite of the existing "resolve namespaced component from scoped
slot bindings" test) with the same compile options (prefixIdentifiers: true and
isNativeTag returning false for 'slot-props.Foo'), then assert the generated
code contains a call to _resolveComponent("slot-props.Foo") and/or uses
_component_slot_props fallback and does not treat it as a scoped property (i.e.,
does not contain _createVNode(slotProps.Foo)); this will validate the
out-of-scope fallback behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/compiler-core/__tests__/transforms/transformElement.spec.ts`:
- Around line 229-245: Add a paired regression test to ensure namespaced tag
"slot-props.Foo" falls back to component resolution outside slot scope: create a
new test (e.g., "resolve namespaced component falls back out of scoped slot")
that calls baseCompile on a template where <slot-props.Foo /> appears outside
the v-slot (opposite of the existing "resolve namespaced component from scoped
slot bindings" test) with the same compile options (prefixIdentifiers: true and
isNativeTag returning false for 'slot-props.Foo'), then assert the generated
code contains a call to _resolveComponent("slot-props.Foo") and/or uses
_component_slot_props fallback and does not treat it as a scoped property (i.e.,
does not contain _createVNode(slotProps.Foo)); this will validate the
out-of-scope fallback behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e977eb7a-450c-470b-864e-d1e7c5949161

📥 Commits

Reviewing files that changed from the base of the PR and between dbfa866 and dbc4d96.

📒 Files selected for processing (3)
  • packages/compiler-core/__tests__/transforms/transformElement.spec.ts
  • packages/compiler-core/src/transforms/transformElement.ts
  • packages/compiler-ssr/__tests__/ssrComponent.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/compiler-core/src/transforms/transformElement.ts

@Fuzzyma Fuzzyma changed the title feat: allow slot prop vars as components feat(compiler-core): resolve slot prop bindings as components Apr 30, 2026
@edison1105
Copy link
Copy Markdown
Member

I think this currently does a bit more than “slot props”.

resolveScopeReference() checks context.identifiers, but that map is shared by all active template-scope bindings, not only v-slot props. For example, v-for aliases are also added there, so this PR changes:

<template v-for="Foo in list">
  <Foo />
</template>

from resolving "Foo" as a component to generating createBlock(Foo).

This should stay focused on slot props as components, so I think we need slot-scope-specific tracking instead of reading the generic context.identifiers.

@Fuzzyma
Copy link
Copy Markdown
Author

Fuzzyma commented May 4, 2026

@edison1105 I didnt even think about this case. But I also wonder why it shouldnt be allowed. It feels consistent when all identifiers can be used as components. do you see problems arising from this?

otherwise I will look into it and see if I can keep slot identifier tracking seperate.

@edison1105
Copy link
Copy Markdown
Member

The issue is not whether “all identifiers as components” feels consistent, but that v-for aliases already have established semantics: they are usually data bindings, not component bindings. Including them in component tag resolution can silently change existing behavior when names collide.

For example:

<template v-for="Foo in list">
  <Foo :value="Foo" />
</template>

Previously, the tag <Foo> resolved to the component named/imported Foo, while Foo in props referred to the loop item. With the current implementation, the tag itself resolves to the loop item and generates something like createBlock(Foo). That is not just adding the requested slot-prop feature; it changes how existing templates compile.

If users really want to render a component from a data variable, we already have the explicit form:

<component :is="Foo" />

The slot-prop case is different: the slot provider intentionally exposes a component constructor as part of the slot API, and using <Foo /> is the feature requested here. So I think this should track slot props separately instead of reusing the generic context.identifiers.

Also, since this is a feature rather than a bug fix, it should target the minor branch.

Fuzzyma and others added 9 commits May 4, 2026 18:08
- remove identifiers from context (was only added to allow testing)
- compile snippets in tests to test against compiled output
- add ssr tests

Co-authored-by: Copilot <copilot@github.com>
Properly differentiate between local identifiers (e.g. from `v-for`) and slot
identifiers (e.g. from `v-slot`) during transformation. This ensures that
components are only auto-resolved from scope variables when they originate
specifically from slot props, preventing incorrect resolution when a
component name is shadowed by a `v-for` variable.

- Add `identifierScopes` to `TransformContext` to track scope types.
- Update `addIdentifiers` and `vSlot` transform to mark slot-based identifiers.
- Refactor `resolveComponentType` to use `isSlotScopeIdentifier`.
- Ensure `identifierScopes` are correctly inherited in SSR sub-transforms.
- Add comprehensive tests for shadowed and nested scope scenarios in both
  core and SSR compilers.

Co-authored-by: Copilot <copilot@github.com>
@Fuzzyma Fuzzyma force-pushed the allow-slot-props-components branch from 84d0691 to 97f408a Compare May 4, 2026 16:11
@Fuzzyma Fuzzyma changed the base branch from main to minor May 4, 2026 16:12
@netlify
Copy link
Copy Markdown

netlify Bot commented May 4, 2026

Deploy Preview for vue-next-template-explorer failed. Why did it fail? →

Name Link
🔨 Latest commit 97f408a
🔍 Latest deploy log https://app.netlify.com/projects/vue-next-template-explorer/deploys/69f8c545816ba00008f1218b

@Fuzzyma
Copy link
Copy Markdown
Author

Fuzzyma commented May 4, 2026

@edison1105 I addressed the feedback by tracking the type of the identifier in an extra array and only allow resolution for the type "slot". Is this okay like this?

// EDIT: the build fails because it tries to find the template-explorer in /packages but its in /packages-privat. I assume something is not correctly setup. Maybe in scripts/aliases.js?

@edison1105 edison1105 moved this to Ready To Merge in Next Minor May 6, 2026
@edison1105
Copy link
Copy Markdown
Member

/ecosystem-ci run

@vue-bot
Copy link
Copy Markdown
Contributor

vue-bot commented May 6, 2026

📝 Ran ecosystem CI: Open

suite result latest scheduled
language-tools success success
pinia success success
quasar success success
radix-vue success success
nuxt failure failure
primevue success success
vue-i18n failure success
vuetify failure success
vitepress success success
vueuse failure success
test-utils failure success
vue-simple-compiler success success
router success success
vue-macros failure success
vant success success
vite-plugin-vue success success

@edison1105 edison1105 merged commit e17e603 into vuejs:minor May 6, 2026
14 of 15 checks passed
@github-project-automation github-project-automation Bot moved this from Ready To Merge to Done in Next Minor May 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants