core icon indicating copy to clipboard operation
core copied to clipboard

fix(compiler-ssr): expand v-model `option` selected inclusion logic

Open alex-snezhko opened this issue 2 months ago β€’ 5 comments

close #13436

Expand cases under which the selected attribute is added onto an option tag in SSR:

  • Consider text nodes/interpolations as direct children of the option
  • Consider v-text

An additional side effect of this PR is to fix the current behavior showcased by this playground example, which I believe to be a bug: link

Example

const app = createSSRApp({
  setup() {
    const r = ref('asdf')
    const val = 'asdf'
    return { r, val }
  },
  template: `
    <select v-model="r">
      <option>{{ val }}</option>
      <option>fdsa</option>
    </select>
  `
})

// SSR output: <select><option selected>asdf</option><option>fdsa</option></select>

Summary by CodeRabbit

  • New Features

    • Improved SSR handling for select v-model so option values resolve from plain text, interpolations, v-text, loops, slots, templates and nested content.
  • Bug Fixes

    • More reliable selected-state computation in SSR for single and multiple select scenarios, including when option values are derived rather than explicitly bound.
  • Tests

    • Added extensive SSR tests covering many select/v-model permutations and mixed option content.

alex-snezhko avatar Oct 05 '25 04:10 alex-snezhko

Walkthrough

Reworks SSR v-model option value extraction to derive values from bound attributes, text directives, or collapsed text/interpolations; adds collapse utility; updates selected-state codegen; and adds extensive SSR tests for

Changes

Cohort / File(s) Summary
SSR v-model transform
packages/compiler-ssr/src/transforms/ssrVModel.ts
Adds internal OptionValue type and findOptionValue() to resolve option values from value bindings, v-text, or collapsed text/interpolations; introduces collapseTextBetweenComments(); updates imports (TemplateLiteral, TextNode, createTemplateLiteral, findDir); and adjusts selected-state SSR codegen to use the derived value source.
SSR compiler tests
packages/compiler-ssr/__tests__/ssrVModel.spec.ts
Adds many new SSR unit tests and inline snapshots covering options with literal text, trimmed/spaced text, interpolations, v-text, v-for, slots/content, optgroups, and template/conditional combinations to validate option value derivation and selected logic.
SSR renderer tests
packages/server-renderer/__tests__/ssrDirectives.spec.ts
Adds two tests verifying interpolation and v-text inside <option> render expected text and selected state in SSR output.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Template as Template (AST)
  participant Compiler as SSR Compiler
  participant Transform as ssrVModel Transform
  participant Helper as Utilities (collapseTextBetweenComments)
  participant Codegen as SSR Codegen
  participant Runtime as SSR Helpers

  Template->>Compiler: parse -> AST
  Compiler->>Transform: apply v-model transform on <select>/<option>
  Transform->>Transform: findOptionValue()\n- if `value` binding -> use bound expr\n- else if `v-text` dir -> use text expr\n- else -> call Helper to collapse TEXT/INTERPOLATION/COMMENT\nHelper-->>Transform: sequence or TemplateLiteral value
  Transform->>Codegen: emit selected logic using derived value\nCodegen->>Runtime: reference _ssrLooseContain/_ssrLooseEqual helpers
  Note right of Runtime: Helpers evaluate at render time to set `selected`

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • vuejs/core#13487 β€” Modifies the same packages/compiler-ssr/src/transforms/ssrVModel.ts area to improve SSR handling of <select>/<option> value extraction and child processing.

Suggested labels

ready to merge, :hammer: p3-minor-bug

Poem

I’m a rabbit in the build, nibbling bits of text,
I fold interpolations, chase comments next.
Value bound or written, I tuck it neatβ€”
Then hop to mark the selected seat. πŸ‡βœ¨

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
βœ… Passed checks (4 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check βœ… Passed The title clearly and concisely describes the primary change of expanding the SSR v-model option selection logic in the compiler-ssr transform, directly reflecting the key modifications without extraneous details.
Linked Issues Check βœ… Passed The PR implements the fix from issue #13436 by introducing a findOptionValue function that derives option values from text nodes, interpolations, and v-text directives and updates the SSR selected-attribute logic accordingly, with matching tests covering the specified scenarios.
Out of Scope Changes Check βœ… Passed All changes pertain exclusively to enhancing SSR v-model option selection and related tests, and no unrelated features or code areas have been modified.
✨ Finishing touches
  • [ ] πŸ“ Generate docstrings
πŸ§ͺ Generate unit tests (beta)
  • [ ] Create PR with unit tests
  • [ ] Post copyable unit tests in a comment

πŸ“œ Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 15d6f922df89216b4a0455559329d8721fcff644 and cced68b97d8c5c8f43f3f9dbcfc133b02c9f65b0.

πŸ“’ Files selected for processing (3)
  • packages/compiler-ssr/__tests__/ssrVModel.spec.ts (1 hunks)
  • packages/compiler-ssr/src/transforms/ssrVModel.ts (4 hunks)
  • packages/server-renderer/__tests__/ssrDirectives.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/compiler-ssr/tests/ssrVModel.spec.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/compiler-ssr/src/transforms/ssrVModel.ts (2)
packages/compiler-core/src/ast.ts (7)
  • PlainElementNode (140-149)
  • ExpressionNode (91-91)
  • TemplateLiteral (449-452)
  • createSimpleExpression (685-698)
  • createTemplateLiteral (801-809)
  • TemplateChildNode (93-102)
  • TextNode (176-179)
packages/compiler-core/src/utils.ts (2)
  • findProp (299-320)
  • findDir (282-297)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: test / unit-test-windows
  • GitHub Check: test / e2e-test
  • GitHub Check: Redirect rules
  • GitHub Check: Header rules
  • GitHub Check: Pages changed
πŸ”‡ Additional comments (2)
packages/server-renderer/__tests__/ssrDirectives.spec.ts (1)

125-145: LGTM! Excellent test coverage for the new option value derivation logic.

The new test cases properly validate:

  1. Interpolation within option text (f{{ zero }}o β†’ "f0o")
  2. v-text directive bindings (opt1Val β†’ "foo", opt2Val β†’ "bar")

Both scenarios confirm that SSR correctly marks options as selected when the derived text content matches the v-model value.

packages/compiler-ssr/src/transforms/ssrVModel.ts (1)

259-295: LGTM! Clean implementation of text node collapsing.

The collapseTextBetweenComments utility correctly:

  • Merges adjacent text nodes
  • Handles spacing between merged nodes (removes duplicate spaces at boundaries)
  • Skips comment nodes
  • Preserves non-text nodes in sequence

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.

coderabbitai[bot] avatar Oct 05 '25 04:10 coderabbitai[bot]

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 102 kB 38.6 kB 34.8 kB
vue.global.prod.js 160 kB 58.7 kB 52.2 kB

Usages

Name Size Gzip Brotli
createApp (CAPI only) 46.7 kB 18.3 kB 16.7 kB
createApp 54.7 kB 21.3 kB 19.5 kB
createSSRApp 58.9 kB 23 kB 21 kB
defineCustomElement 60 kB 23 kB 21 kB
overall 68.8 kB 26.5 kB 24.2 kB

github-actions[bot] avatar Oct 05 '25 04:10 github-actions[bot]

Open in StackBlitz

@vue/compiler-core

npm i https://pkg.pr.new/@vue/compiler-core@13963
@vue/compiler-dom

npm i https://pkg.pr.new/@vue/compiler-dom@13963
@vue/compiler-sfc

npm i https://pkg.pr.new/@vue/compiler-sfc@13963
@vue/compiler-ssr

npm i https://pkg.pr.new/@vue/compiler-ssr@13963
@vue/reactivity

npm i https://pkg.pr.new/@vue/reactivity@13963
@vue/runtime-core

npm i https://pkg.pr.new/@vue/runtime-core@13963
@vue/runtime-dom

npm i https://pkg.pr.new/@vue/runtime-dom@13963
@vue/server-renderer

npm i https://pkg.pr.new/@vue/server-renderer@13963
@vue/shared

npm i https://pkg.pr.new/@vue/shared@13963
vue

npm i https://pkg.pr.new/vue@13963
@vue/compat

npm i https://pkg.pr.new/@vue/compat@13963

commit: cced68b

pkg-pr-new[bot] avatar Oct 05 '25 04:10 pkg-pr-new[bot]

The following example has a bug Playground with this PR

edison1105 avatar Oct 09 '25 02:10 edison1105

@edison1105 updated!

alex-snezhko avatar Oct 12 '25 03:10 alex-snezhko