Skip to content

Conversation

@edison1105
Copy link
Member

@edison1105 edison1105 commented Dec 17, 2025

close #11265
close #11275

Summary by CodeRabbit

  • Bug Fixes

    • Prevent v-model on const bindings with a clear compile-time error.
    • Setup-script const bindings targeted by v-model are automatically demoted to let and emit a developer warning.
    • Improved error messaging for related template errors.
  • Tests

    • Added tests covering v-model on const bindings: error reporting and demotion warning.

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings December 17, 2025 08:47
@coderabbitai
Copy link

coderabbitai bot commented Dec 17, 2025

Walkthrough

Disallows v-model on const bindings (emits new error) and demotes reactive const setup bindings targeted by v-model to let with a warning. Adds template analysis to collect v-model identifiers, updates compileScript flow, and includes tests and an enum alignment change.

Changes

Cohort / File(s) Summary
Error definitions
packages/compiler-core/src/errors.ts, packages/compiler-dom/src/errors.ts
Added X_V_MODEL_ON_CONST error code and message in compiler-core; shifted numeric value of X_V_HTML_NO_EXPRESSION in compiler-dom.
v-model transform
packages/compiler-core/src/transforms/vModel.ts
Emit X_V_MODEL_ON_CONST and abort transform when v-model targets LITERAL_CONST or SETUP_CONST.
Compiler-core tests
packages/compiler-core/__tests__/transforms/vModel.spec.ts
New test asserting v-model on a const binding triggers X_V_MODEL_ON_CONST.
Template analysis / import usage
packages/compiler-sfc/src/script/importUsageCheck.ts
Unified template analysis cache to return { usedIds, vModelIds }; added resolveTemplateVModelIdentifiers() to collect simple-identifier v-model targets.
SFC script compilation
packages/compiler-sfc/src/compileScript.ts
Use v-model identifiers to demote reactive const setup bindings to let, update binding metadata and setup state, and emit runtime warnings; imports resolveTemplateVModelIdentifiers.
Compiler-sfc tests
packages/compiler-sfc/__tests__/compileScript.spec.ts
Tests for const→let demotion with warning and for erroring when v-model targets literal const bindings.

Sequence Diagram(s)

sequenceDiagram
    participant Template as Template Parser
    participant Analysis as Template Analysis (importUsageCheck)
    participant SFC as SFC Compiler (compileScript)
    participant Bindings as Setup Bindings Metadata
    participant vModel as v-model Transform

    Template->>Analysis: parse template AST
    Analysis->>SFC: resolveTemplateVModelIdentifiers() → vModelIds
    SFC->>Bindings: check setup bindings for vModelIds

    alt reactive setup const targeted by v-model
        SFC->>SFC: mutate script AST (const → let)
        SFC->>Bindings: update bindingMetadata (SETUP_LET)
        SFC->>SFC: emit warnOnce about demotion
    end

    SFC->>vModel: run v-model transform with updated bindings
    vModel->>Bindings: inspect binding type
    alt literal const detected
        vModel->>vModel: emit X_V_MODEL_ON_CONST error and abort
    else writable binding
        vModel->>vModel: generate assignment/event handler code
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay special attention to:
    • packages/compiler-sfc/src/compileScript.ts: correct AST mutation for const→let (multiple declarators, source maps) and consistent updates to setupBindings/bindingMetadata.
    • packages/compiler-sfc/src/script/importUsageCheck.ts: caching correctness and accurate extraction of only simple-identifier v-model targets.
    • packages/compiler-core/src/transforms/vModel.ts: early error emission path and interaction with existing transform assumptions.
    • Enum index change in packages/compiler-dom/src/errors.ts: ensure alignment with other error enums/consumers.

Possibly related PRs

Suggested labels

scope: compiler

Suggested reviewers

  • sxzz
  • KazariEX
  • baiwusanyu-c

Poem

🐰 I hopped through ASTs and nudged a const to let,
Found v-models binding places they shouldn't get.
I thumped an error, warned a gentle change,
Now bindings dance safe within range.
Carrots for all — compile time delight! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.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 accurately summarizes the main change: demoting const reactive bindings used in v-model to let, which directly addresses the linked issues.
Linked Issues check ✅ Passed The PR comprehensively addresses both issues: #11265 (dev-mode error handling) and #11275 (prevent const binding modifications) through error reporting and const demotion logic.
Out of Scope Changes check ✅ Passed All changes directly support the core objective: adding v-model error validation, template analysis for v-model identifiers, and const-to-let demotion logic. No unrelated modifications detected.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch edison/fix/11265

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48d9ee5 and 5f6b361.

📒 Files selected for processing (1)
  • packages/compiler-sfc/src/script/importUsageCheck.ts (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/compiler-sfc/src/script/importUsageCheck.ts (5)
packages/compiler-sfc/src/cache.ts (1)
  • createCache (3-11)
packages/compiler-sfc/src/parse.ts (1)
  • SFCDescriptor (72-96)
packages/shared/src/general.ts (1)
  • isBuiltInDirective (91-94)
packages/compiler-core/src/utils.ts (1)
  • isSimpleIdentifier (68-69)
packages/compiler-core/src/ast.ts (1)
  • SimpleExpressionNode (225-247)
⏰ 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). (2)
  • GitHub Check: test / unit-test-windows
  • GitHub Check: test / e2e-test
🔇 Additional comments (9)
packages/compiler-sfc/src/script/importUsageCheck.ts (9)

7-7: LGTM: Import added for v-model validation.

The isSimpleIdentifier import is correctly sourced and necessary for validating v-model target expressions in the new collection logic.


23-26: LGTM: Unified cache structure.

The unified templateAnalysisCache efficiently stores both optional usedIds and mandatory vModelIds, enabling selective collection for performance optimization.


28-32: LGTM: Efficient v-model identifier extraction.

The new public function correctly delegates to resolveTemplateAnalysisResult with collectUsedIds=false, avoiding expensive identifier collection when only v-model targets are needed.


34-36: LGTM: Non-null assertion is safe.

The non-null assertion on usedIds! is safe because resolveTemplateAnalysisResult is called with the default collectUsedIds=true, guaranteeing that usedIds is populated.


45-49: LGTM: Cache logic correctly handles optional usedIds.

The cache condition cached && (!collectUsedIds || cached.usedIds) correctly handles scenarios where:

  • When collectUsedIds=false, any cached result (with vModelIds) is valid
  • When collectUsedIds=true, only cached results with populated usedIds are valid

51-54: LGTM: Conditional initialization for performance.

The conditional initialization of ids based on collectUsedIds correctly implements the performance optimization, while vModelIds is always collected as required.


67-71: LGTM: Consistent guard checks for performance.

The if (ids) guards are correctly and consistently applied throughout the tree walk, ensuring that expensive identifier extraction is skipped when collectUsedIds=false. This achieves the intended performance optimization without affecting correctness.

Also applies to: 75-79, 96-102, 104-113, 115-123, 127-127


81-93: LGTM: v-model collection correctly targets simple identifiers.

The v-model collection logic correctly:

  • Identifies v-model directives
  • Validates expressions are simple identifiers using isSimpleIdentifier
  • Excludes undefined literal
  • Skips member expressions (obj.prop) and computed access (arr[0]), which is appropriate since these cannot be const bindings being directly modified

This aligns with the PR objective to demote const reactive bindings used with v-model.


132-134: LGTM: Result construction and caching.

The result object correctly includes both usedIds (possibly undefined) and vModelIds, and is properly cached using template content as the key for efficient subsequent lookups.


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

@github-actions
Copy link

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 103 kB 39 kB 35.1 kB
vue.global.prod.js 161 kB (+40 B) 59 kB (+17 B) 52.6 kB (+91 B)

Usages

Name Size Gzip Brotli
createApp (CAPI only) 46.9 kB 18.3 kB 16.8 kB
createApp 55 kB 21.4 kB 19.6 kB
createSSRApp 59.3 kB 23.1 kB 21.1 kB
defineCustomElement 60.6 kB 23.1 kB 21.1 kB
overall 69.3 kB 26.6 kB 24.3 kB

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 17, 2025

Open in StackBlitz

@vue/compiler-core

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

@vue/compiler-dom

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

@vue/compiler-sfc

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

@vue/compiler-ssr

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

@vue/reactivity

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

@vue/runtime-core

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

@vue/runtime-dom

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

@vue/server-renderer

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

@vue/shared

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

vue

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

@vue/compat

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

commit: 5f6b361

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

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d8a2de4 and 7546248.

⛔ Files ignored due to path filters (1)
  • packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (6)
  • packages/compiler-core/__tests__/transforms/vModel.spec.ts (1 hunks)
  • packages/compiler-core/src/errors.ts (2 hunks)
  • packages/compiler-core/src/transforms/vModel.ts (1 hunks)
  • packages/compiler-dom/src/errors.ts (1 hunks)
  • packages/compiler-sfc/__tests__/compileScript.spec.ts (3 hunks)
  • packages/compiler-sfc/src/compileScript.ts (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
packages/compiler-core/__tests__/transforms/vModel.spec.ts (1)
packages/compiler-core/src/index.ts (2)
  • BindingTypes (11-11)
  • ErrorCodes (32-32)
packages/compiler-sfc/__tests__/compileScript.spec.ts (3)
packages/compiler-sfc/src/warn.ts (1)
  • warnOnce (3-10)
packages/compiler-core/src/index.ts (1)
  • BindingTypes (11-11)
packages/compiler-sfc/__tests__/utils.ts (1)
  • assertCode (27-42)
packages/compiler-sfc/src/compileScript.ts (3)
packages/compiler-sfc/src/index.ts (1)
  • SFCDescriptor (51-51)
packages/compiler-core/src/ast.ts (1)
  • TemplateChildNode (93-102)
packages/compiler-core/src/utils.ts (1)
  • isSimpleIdentifier (68-69)
⏰ 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). (2)
  • GitHub Check: CodeQL analysis (javascript-typescript)
  • GitHub Check: Agent
🔇 Additional comments (8)
packages/compiler-dom/src/errors.ts (1)

24-24: LGTM: Error code realignment.

The numeric value update correctly shifts the DOM error codes to accommodate the new X_V_MODEL_ON_CONST error added to the core ErrorCodes enum, preventing collision with the extension point.

packages/compiler-sfc/src/compileScript.ts (1)

796-843: Verify demotion behavior is correctly scoped to reactive const bindings.

The demotion logic specifically targets SETUP_REACTIVE_CONST bindings (line 806), which is the correct design since:

  • LITERAL_CONST bindings (e.g., const foo = 1) cannot be made writable and are properly rejected by the transform error at packages/compiler-core/src/transforms/vModel.ts:52-58
  • SETUP_REF bindings work with v-model via .value assignment
  • Only SETUP_REACTIVE_CONST (e.g., const foo = reactive({})) can be "rescued" by demotion to let

Confirm this aligns with the intended behavior and that the transform error provides clear guidance when literal const bindings are used with v-model.

packages/compiler-sfc/__tests__/compileScript.spec.ts (2)

1-18: LGTM: Proper test infrastructure for warning verification.

The Vitest mock setup correctly intercepts warning calls, enabling the tests to verify that warnings are emitted during const binding demotion.


87-156: LGTM: Comprehensive test coverage for v-model const binding behavior.

The tests properly verify:

  1. Reactive const bindings are demoted to let with appropriate warnings (both normal and inline template modes)
  2. Literal const bindings correctly throw errors when used with v-model
  3. Binding metadata is updated to reflect the demotion (SETUP_LET)

This aligns with the intended behavior where only reactive const can be "rescued" by demotion, while literal const must error.

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

91-91: LGTM: New error code for const binding validation.

The error code addition is properly positioned in the enum and integrates with the transform logic that validates v-model usage on constant bindings.


180-180: LGTM: Clear error message.

The error message clearly explains the constraint and helps developers understand why v-model cannot be used on const bindings.

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

586-601: LGTM: Test correctly verifies const binding rejection.

The test properly validates that using v-model on a LITERAL_CONST binding triggers the X_V_MODEL_ON_CONST error, following the same pattern as the existing props validation test.

packages/compiler-core/src/transforms/vModel.ts (1)

51-58: LGTM: Proper validation for const bindings.

The guard correctly rejects both LITERAL_CONST and SETUP_CONST bindings from v-model usage, as these are not writable. This complements the SFC compiler's demotion logic for SETUP_REACTIVE_CONST, which can be converted to let.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes an issue where const reactive bindings (created via reactive()) cannot be properly updated when used with v-model. The fix implements a two-part solution: demoting const to let for reactive bindings in v-model, and adding compile-time validation to error on non-reactive const bindings.

Key changes:

  • Demotes const declarations of reactive bindings to let when they're used as v-model targets, enabling proper two-way binding
  • Adds compile-time error checking to prevent v-model usage on literal const and non-reactive const bindings
  • Adds new error code X_V_MODEL_ON_CONST with descriptive error message

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/compiler-sfc/src/compileScript.ts Adds resolveTemplateVModelIdentifiers function to scan template AST for v-model usage, and implements logic to transform const to let for reactive bindings used in v-model
packages/compiler-sfc/tests/compileScript.spec.ts Adds test mocking for warn functions and three test cases covering reactive binding demotion (both inline and non-inline modes) and error handling for literal const bindings
packages/compiler-sfc/tests/snapshots/compileScript.spec.ts.snap Adds snapshots for the new test cases showing transformed output with let instead of const
packages/compiler-dom/src/errors.ts Updates DOMErrorCodes enum start value from 53 to 54 to account for new error code added to core
packages/compiler-core/src/transforms/vModel.ts Adds validation check to error when v-model is used on LITERAL_CONST or SETUP_CONST bindings
packages/compiler-core/src/errors.ts Adds new error code X_V_MODEL_ON_CONST with error message explaining const bindings are not writable
packages/compiler-core/tests/transforms/vModel.spec.ts Adds test case verifying error is thrown when v-model is used on const binding

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@edison1105
Copy link
Member Author

/ecosystem-ci run

@vue-bot
Copy link
Contributor

vue-bot commented Dec 17, 2025

📝 Ran ecosystem CI: Open

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

@edison1105 edison1105 added ready to merge The PR is ready to be merged. scope: sfc 🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. labels Dec 17, 2025
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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/compiler-sfc/src/script/importUsageCheck.ts (1)

53-114: Template traversal misses v-model directives inside v-if/v-for structures.

The walk function handles NodeTypes.ELEMENT and NodeTypes.INTERPOLATION, but TemplateChildNode also includes IfNode and ForNode. When an element has v-if or v-for, Vue's parser wraps it in these structural nodes. Without handling them, v-model directives inside conditional/loop blocks are not detected.

For example, <input v-if="show" v-model="foo"> creates an IfNode, and the v-model on the input won't be collected.

Apply this diff to handle structural node types:

       case NodeTypes.INTERPOLATION:
         extractIdentifiers(ids, node.content)
         break
+      case NodeTypes.IF:
+        node.branches.forEach(branch => branch.children.forEach(walk))
+        break
+      case NodeTypes.FOR:
+        node.children.forEach(walk)
+        break
     }
   }

You'll also need to import NodeTypes values for IF and FOR if not already available.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e1745b and 48d9ee5.

📒 Files selected for processing (2)
  • packages/compiler-sfc/src/compileScript.ts (2 hunks)
  • packages/compiler-sfc/src/script/importUsageCheck.ts (4 hunks)
🔇 Additional comments (5)
packages/compiler-sfc/src/script/importUsageCheck.ts (3)

23-36: Clean cache unification.

The refactored cache structure and the two accessor functions (resolveTemplateVModelIdentifiers, resolveTemplateUsedIdentifiers) provide a clean separation of concerns while avoiding duplicate template traversals.


38-49: LGTM!

The unified analysis function correctly caches and returns both identifier sets.


72-84: Correct filtering for simple identifiers.

The logic appropriately captures only simple identifier expressions for v-model targets, excluding complex expressions like member access which wouldn't need declaration demotion.

packages/compiler-sfc/src/compileScript.ts (2)

67-70: LGTM!

Clean import addition for the new helper function.


766-813: Well-structured demotion logic with correct placement.

The fix correctly addresses the v-model const binding issue. A few observations:

  1. The guard condition properly ensures template analysis is only performed when a valid template AST exists.
  2. When a const declaration has multiple declarators (e.g., const foo = reactive({}), bar = ref(0)), the entire declaration becomes let, but only the demoted identifier's binding metadata is updated. This is acceptable since Vue uses binding metadata (not the source keyword) for setter generation.
  3. The warning message clearly explains the transformation to developers.

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

Labels

🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. ready to merge The PR is ready to be merged. scope: sfc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

When in dev mode, modifying constants declared with const using v-model does not result in an error.

3 participants