Skip to content

fix(compiler-core,runtime-core): preserve slots order on slots instance property#14449

Open
andreww2012 wants to merge 2 commits intovuejs:mainfrom
andreww2012:fix/preserve-slots-order-on-instance-property
Open

fix(compiler-core,runtime-core): preserve slots order on slots instance property#14449
andreww2012 wants to merge 2 commits intovuejs:mainfrom
andreww2012:fix/preserve-slots-order-on-instance-property

Conversation

@andreww2012
Copy link

@andreww2012 andreww2012 commented Feb 12, 2026

Fixes #14425

This change ensures the insertion order of slot names on slots instance property matches the slots order in the template. Previously, all dynamic slots were assigned to slots object after all non-dynamic slots.

Summary by CodeRabbit

  • Bug Fixes

    • Slot ordering now reliably preserves the template-defined order for named slots across conditional rendering, dynamic updates, rerenders, and hot module replacement, preventing unexpected reordering.
  • Tests

    • Added tests validating conditional slot positioning, dynamic slot key updates, and slot-order preservation during component updates and HMR.

@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2026

📝 Walkthrough

Walkthrough

The compiler collects static slot names' template order and emits it to generated render code; the runtime's createSlots accepts an optional order array and reorders slot keys accordingly; componentSlots clears non-internal keys before reassigning to preserve insertion order. Tests added to cover slot-order preservation in various scenarios.

Changes

Cohort / File(s) Summary
Compiler slot transform
packages/compiler-core/src/transforms/vSlot.ts
Collects static slot names into a slotOrder array and emits it as an optional argument to the generated CREATE_SLOTS call when present.
Compiler tests
packages/compiler-core/__tests__/transforms/vSlot.spec.ts
Adds test(s) asserting the generated CREATE_SLOTS args include the ordered named-slot array for a conditional slot placed between static slots.
Runtime createSlots helper
packages/runtime-core/src/helpers/createSlots.ts
Adds optional order?: string[] parameter to createSlots and, when provided, reorders the resulting slots object by delete-and-reinsert to match the given order.
Component slot update logic
packages/runtime-core/src/componentSlots.ts
Clears non-internal slot keys before reassigning compiled/dynamic slots to preserve insertion order; disables redundant deletion checks for that update path.
Runtime tests
packages/runtime-core/__tests__/componentSlots.spec.ts, packages/runtime-core/__tests__/hmr.spec.ts, packages/runtime-core/__tests__/helpers/createSlots.spec.ts
Adds tests verifying $slots key ordering across conditional toggles, HMR remove/re-add cycles, and createSlots behavior when an explicit order array is provided.

Sequence Diagram(s)

sequenceDiagram
  participant Compiler as Compiler (vSlot transform)
  participant Generated as Generated render fn
  participant Runtime as runtime.createSlots
  participant Component as componentSlots.updateSlots

  Compiler->>Generated: emit CREATE_SLOTS(dynamicSlots, needsStable, order?)
  Generated->>Runtime: call createSlots(..., dynamicSlots, order?)
  Runtime->>Runtime: populate slots object\nif order provided -> delete & reinsert keys per order
  Runtime->>Component: return reordered slots object
  Component->>Component: clear non-internal keys (compiled/dynamic path)\nreassign new keys preserving insertion order
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

scope: slots, scope: compiler, :hammer: p3-minor-bug, ready for review

Poem

🐰 I hopped through compiler trees so deep,
I gathered slot names all in a sweep,
Passed them down where runtime plays,
Reinserted keys in ordered arrays,
Now slots march straight — a joyful leap!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: fixing slot order preservation in the slots instance property, which directly addresses the reported issue.
Linked Issues check ✅ Passed The PR implements all required changes to preserve template slot order: compiler-core creates a slotOrder array, runtime-core's createSlots accepts an order parameter, and slots are reordered during updates to match template position.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing slot order preservation via slotOrder tracking in compiler transforms, createSlots order parameter, and slot reassignment during updates with comprehensive test coverage.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
packages/runtime-core/__tests__/helpers/createSlots.spec.ts (1)

77-136: Consider adding a test that verifies actual reordering of dynamic slots.

The current tests cover deduplication, missing slots, empty order, and internal keys — all valuable. However, none of them exercise the case where the order array changes the insertion order of slots relative to how they were passed to createSlots. Since the core bug (#14425) is about preserving template order, a test explicitly demonstrating reordering would strengthen confidence in the fix.

For example:

Suggested additional test
+    it('should reorder dynamic slots according to the order array', () => {
+      const actual = createSlots(
+        record,
+        [
+          { name: 'footer', fn: slot },
+          { name: 'header', fn: slot },
+        ],
+        ['header', 'footer'],
+      )
+
+      expect(Object.keys(actual)).toEqual(['header', 'footer'])
+    })

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.

Copy link

@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

🤖 Fix all issues with AI agents
In `@packages/runtime-core/__tests__/componentSlots.spec.ts`:
- Line 501: Remove the leftover debug console.log call that prints
instance.slots from the test; locate the stray statement
"console.log('instance.slots:', instance.slots)" in the componentSlots.spec test
and delete it so tests don’t emit debug output (no other changes to assertions
or setup needed).

@andreww2012 andreww2012 force-pushed the fix/preserve-slots-order-on-instance-property branch from 8bc4d14 to c168916 Compare February 12, 2026 10:37
Copy link

@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

🤖 Fix all issues with AI agents
In `@packages/compiler-core/src/transforms/vSlot.ts`:
- Around line 390-393: Add unit tests to createSlots.spec.ts that exercise the
createSlots runtime reorder behavior via the createSlots function: (1) a test
with duplicate slot names (simulate v-if/v-else branches) ensuring re-adding the
same slot name is a no-op; (2) a test for mutually exclusive slots where only
the active branch is present and reorder still works; (3) a test where order =
[] and only implicit/default slots exist to verify no changes occur; and (4) a
test where some keys (like '_' or implicit default) are not included in the
order array to confirm they are left untouched. Reference createSlots and the
order parameter in each test and assert the final slots object shape/iteration
order matches expected outcomes.
🧹 Nitpick comments (1)
packages/compiler-core/src/transforms/vSlot.ts (1)

374-394: Consider guarding on slotOrder.length instead of slotsProperties.length.

The current condition (slotsProperties.length > 0) can be true while slotOrder is empty — e.g., when the only static slot is the implicit default (added at lines 331/348 but never pushed to slotOrder), or when all explicit template slots have dynamic names. In that scenario an empty [] literal is emitted in the compiled output and forwarded to createSlots, adding unnecessary bytes and a no-op runtime path.

slotOrder.length > 0 directly expresses the intent ("pass the order array only when there are ordered names") and avoids the empty-array edge case.

Suggested change
     // `#14425`
     // Pass slot names to preserve the template ordering
-    if (slotsProperties.length > 0) {
+    if (slotOrder.length > 0) {
       createSlotsArgs.push(
         createArrayExpression(
           slotOrder.map(name => createSimpleExpression(name, true)),
         ),
       )
     }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Conditionally rendered slots are ending up last in the $slots object

1 participant