Skip to content

Conversation

@BobbieGoede
Copy link
Contributor

@BobbieGoede BobbieGoede commented Mar 23, 2025

This is a quick and dirty implementation of a plugin that automatically appends the HMR code to files that register a pinia store (nuxt only), this only works for very basic setups in this state.

This could be implemented using unplugin so that it works in vue projects (without nuxt) as well, in a separate repo if preferred.

Maybe this functionality was already considered, I couldn't find an old discussion or PR suggesting this 🤔

How it works

Given an example project has the following store:

// my-store.ts
export const myStore = defineStore('my-store', /* ... */)

The plugin checks if it contains defineStore and does not contain acceptHMRUpdate, then walks the top level nodes of the parsed code AST to find the export/variable declaration which uses the defineStore function. This code likely needs to be expanded to cover more use cases.

If an export/variable declaration is found, simply append the necessary HMR code:

// my-store.ts
export const myStore = defineStore('my-store', /* ... */)
+if (import.meta.hot) {
+  import.meta.hot.accept(
+    acceptHMRUpdate(myStore, import.meta.hot)
+  )
+}

Summary by CodeRabbit

  • New Features

    • Automatic Hot Module Replacement (HMR) support for stores during development.
  • Updates

    • Counter now exposes a computed "double" value and the UI displays "Count: X x 2 = Y".
    • Main page shows raw counter state in a debug block for easier inspection.

@netlify
Copy link

netlify bot commented Mar 23, 2025

Deploy Preview for pinia-official canceled.

Name Link
🔨 Latest commit 36f8a4b
🔍 Latest deploy log https://app.netlify.com/sites/pinia-official/deploys/67e07dd45a16de00080f7d81

@BobbieGoede BobbieGoede changed the title feat: automatic hmr code for nuxt module feat: automatic HMR code (nuxt only) Mar 23, 2025
@github-project-automation github-project-automation bot moved this to 🆕 Triaging in Pinia Roadmap Apr 14, 2025
@posva posva moved this from 🆕 Triaging to 🧑‍💻 In progress in Pinia Roadmap Apr 14, 2025
@netlify
Copy link

netlify bot commented Nov 4, 2025

Deploy Preview for pinia-official canceled.

Name Link
🔨 Latest commit 0b05246
🔍 Latest deploy log https://app.netlify.com/projects/pinia-official/deploys/6909d6b39fc8fe00085ea2f5

@posva posva marked this pull request as ready for review November 4, 2025 09:11
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 4, 2025

Warning

Rate limit exceeded

@posva has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 12 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between fa0a72d and 0b05246.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • package.json (0 hunks)
  • packages/nuxt/package.json (1 hunks)
  • packages/nuxt/playground/pages/index.vue (1 hunks)
  • packages/nuxt/playground/stores/counter.ts (1 hunks)
  • packages/nuxt/playground/stores/nested/some-store.ts (0 hunks)
  • packages/nuxt/playground/stores/with-skip-hydrate.ts (0 hunks)
  • packages/nuxt/src/auto-hmr-plugin.ts (1 hunks)
  • packages/nuxt/src/module.ts (2 hunks)

Walkthrough

Adds an auto-HMR Vite plugin that injects Pinia HMR handling into store files and removes manual HMR accept blocks from playground stores; updates a playground page to use direct store accessors and a new getter.

Changes

Cohort / File(s) Summary
Playground page
packages/nuxt/playground/pages/index.vue
Template bindings changed from counter.$state.count to counter.count; added display of counter.double; added a <pre> showing counter.$state.
Playground stores — getters & HMR removal
packages/nuxt/playground/stores/counter.ts, packages/nuxt/playground/stores/nested/some-store.ts, packages/nuxt/playground/stores/with-skip-hydrate.ts
counter.ts: removed getCount getter and added double getter (state.count * 2). All three files: manual HMR acceptance blocks (import.meta.hot.accept(...) / acceptHMRUpdate) removed.
New auto-HMR plugin
packages/nuxt/src/auto-hmr-plugin.ts
Added autoRegisterHMRPlugin(rootDir: string) exporting a Vite plugin that parses files for defineStore usages, injects acceptHMRUpdate import and import.meta.hot.accept(acceptHMRUpdate(...)) code when needed.
Module integration
packages/nuxt/src/module.ts
Imported and registered the auto-HMR plugin in dev mode via addVitePlugin(autoRegisterHMRPlugin(...)).

Sequence Diagram(s)

sequenceDiagram
    participant DevServer as Dev Server
    participant VitePlugin as Auto-HMR Plugin
    participant File as Store File
    participant Vite as Vite Transform

    Note over VitePlugin,File: Build-time transform
    DevServer->>VitePlugin: Scan source files
    VitePlugin->>File: Read file (within rootDir)
    VitePlugin->>Vite: Parse AST, detect defineStore
    Vite->>Vite: Inject import of acceptHMRUpdate\nand HMR accept block (if absent)
    Vite->>File: Emit transformed code

    Note over DevServer: Runtime HMR
    DevServer->>File: Load transformed module
    File->>DevServer: import.meta.hot.accept triggers
    DevServer->>DevServer: acceptHMRUpdate handles store replacement
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • packages/nuxt/src/auto-hmr-plugin.ts — AST parsing, detection heuristics for defineStore, correct extraction of store identifier(s), avoidance of double-injection, and handling of varied export patterns.
    • Ensure transformed code preserves source maps/line numbers and skips virtual modules or non-rootDir files.
    • Verify removed manual HMR blocks in playground stores do not rely on custom HMR logic beyond standard acceptHMRUpdate.
    • packages/nuxt/src/module.ts — confirm plugin registration is gated to dev and correct rootDir is passed.

Poem

🐰 I sniffed the stores, injected a spark,
HMR now hums from light to dark,
Getters doubled, templates lean,
No more manual HMR scene—
Hop, patch, repeat — CodeRabbit's keen 🥕

Pre-merge checks and finishing touches

✅ Passed checks (2 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: automatic HMR code generation for Pinia stores in Nuxt projects.

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.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 4, 2025

Open in StackBlitz

npm i https://pkg.pr.new/@pinia/nuxt@2954
npm i https://pkg.pr.new/pinia@2954
npm i https://pkg.pr.new/@pinia/testing@2954

commit: 0b05246

@codecov
Copy link

codecov bot commented Nov 4, 2025

Codecov Report

❌ Patch coverage is 83.33333% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.02%. Comparing base (e25e525) to head (0b05246).
⚠️ Report is 3 commits behind head on v3.

Files with missing lines Patch % Lines
packages/nuxt/src/auto-hmr-plugin.ts 82.00% 9 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##               v3    #2954      +/-   ##
==========================================
- Coverage   91.28%   91.02%   -0.26%     
==========================================
  Files          18       19       +1     
  Lines        1618     1672      +54     
  Branches      231      233       +2     
==========================================
+ Hits         1477     1522      +45     
- Misses        139      148       +9     
  Partials        2        2              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@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

🧹 Nitpick comments (4)
packages/nuxt/src/auto-hmr-plugin.ts (4)

5-12: Limited defineStore detection may miss valid use cases.

The function only matches direct calls to an identifier named defineStore. It won't detect:

  • Member expressions: pinia.defineStore(...)
  • Renamed imports: import { defineStore as createStore } from 'pinia'
  • Dynamic or computed calls

Consider whether these cases are relevant for the target use case.


30-32: String-based check may cause false positives.

The check code.includes('acceptHMRUpdate') will skip transformation if the string appears anywhere in the file—including comments, string literals, or unrelated code. While this is a reasonable performance optimization, it could prevent legitimate HMR injection in edge cases.

Consider whether this trade-off is acceptable for the current scope.


37-66: Plugin only handles the first store per file.

The function returns immediately after finding the first defineStore usage (line 55), so files with multiple store declarations will only have HMR injected for the first one. Additionally, only top-level declarations are checked—stores defined inside blocks, IIFEs, or conditional exports won't be detected.

Ensure this limitation is acceptable for the intended use cases, or track it for future enhancement.


56-63: Import is injected without duplication check.

The plugin always prepends import { acceptHMRUpdate } from 'pinia' without verifying whether acceptHMRUpdate is already imported. While most bundlers tolerate duplicate imports gracefully, this could lead to linting warnings or edge-case issues.

Additionally, consider adding a source map to the returned transformation for better debugging experience.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1e9a7d and 6b0ac78.

📒 Files selected for processing (6)
  • packages/nuxt/playground/pages/index.vue (1 hunks)
  • packages/nuxt/playground/stores/counter.ts (1 hunks)
  • packages/nuxt/playground/stores/nested/some-store.ts (0 hunks)
  • packages/nuxt/playground/stores/with-skip-hydrate.ts (0 hunks)
  • packages/nuxt/src/auto-hmr-plugin.ts (1 hunks)
  • packages/nuxt/src/module.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • packages/nuxt/playground/stores/with-skip-hydrate.ts
  • packages/nuxt/playground/stores/nested/some-store.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/nuxt/src/module.ts (1)
packages/nuxt/src/auto-hmr-plugin.ts (1)
  • autoRegisterHMRPlugin (18-68)
🔇 Additional comments (3)
packages/nuxt/src/module.ts (1)

82-85: LGTM! Plugin registration is correctly scoped to development.

The automatic HMR plugin is properly registered only during development mode, and the necessary dependencies (nuxt and resolve) are correctly passed.

packages/nuxt/playground/pages/index.vue (1)

18-20: LGTM! Template updates align with store changes.

The template correctly uses direct property access (counter.count) and displays the new double getter. The debug output is helpful for verifying state during development.

packages/nuxt/playground/stores/counter.ts (1)

19-21: LGTM! Store changes align with automatic HMR approach.

The new double getter is correctly implemented, and the removal of manual HMR code (mentioned in the summary) is consistent with the automatic HMR plugin now handling this functionality.

@posva posva force-pushed the feat/nuxt-automatic-hmr-code-append branch from 6b0ac78 to fa0a72d Compare November 4, 2025 09:29
// append HMR code
return {
code: [
`import { acceptHMRUpdate } from 'pinia'`,
Copy link
Member

Choose a reason for hiding this comment

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

I added this so it works with autoImports: false. Let me know I overlooked anything

Copy link
Contributor

@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: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b0ac78 and fa0a72d.

📒 Files selected for processing (6)
  • packages/nuxt/playground/pages/index.vue (1 hunks)
  • packages/nuxt/playground/stores/counter.ts (1 hunks)
  • packages/nuxt/playground/stores/nested/some-store.ts (0 hunks)
  • packages/nuxt/playground/stores/with-skip-hydrate.ts (0 hunks)
  • packages/nuxt/src/auto-hmr-plugin.ts (1 hunks)
  • packages/nuxt/src/module.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • packages/nuxt/playground/stores/nested/some-store.ts
  • packages/nuxt/playground/stores/with-skip-hydrate.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/nuxt/src/module.ts
🔇 Additional comments (2)
packages/nuxt/playground/pages/index.vue (1)

18-21: Getter wiring looks solid

Binding to counter.double keeps the template aligned with the new getter while retaining the existing increment flow.

packages/nuxt/playground/stores/counter.ts (1)

20-21: New double getter looks good

Straightforward derived state that pairs cleanly with the updated template.

@posva posva force-pushed the feat/nuxt-automatic-hmr-code-append branch from fa0a72d to 0b05246 Compare November 4, 2025 10:34
Copy link
Member

@posva posva left a comment

Choose a reason for hiding this comment

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

Looks good to me. It doesn't seem to impact perf. I wonder if it will need any improvement to support rolldown later on. I had to add filter to transform in unplugin-vue-router for that

@posva posva merged commit 0e9e7e7 into vuejs:v3 Nov 4, 2025
9 checks passed
@github-project-automation github-project-automation bot moved this from 🧑‍💻 In progress to ✅ Done in Pinia Roadmap Nov 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

2 participants