Skip to content

Builder-Vite: Add onModuleGraphChange method#34323

Merged
ghengeveld merged 9 commits into
nextfrom
module-graph-change-listener
Mar 30, 2026
Merged

Builder-Vite: Add onModuleGraphChange method#34323
ghengeveld merged 9 commits into
nextfrom
module-graph-change-listener

Conversation

@ghengeveld
Copy link
Copy Markdown
Member

@ghengeveld ghengeveld commented Mar 25, 2026

Closes #34251

What I did

  • Add a builder-agnostic ModuleGraph / ModuleNode type to core builder types so builders can expose dependency graph updates
  • Implement Vite module graph conversion and an onModuleGraphChange subscription API in @storybook/builder-vite
  • Add tests for module graph conversion, listener registration, debounce behavior, and cleanup on bail()

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

Caution

This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This pull request has been released as version 0.0.0-pr-34323-sha-af5da75a. Try it out in a new sandbox by running npx storybook@0.0.0-pr-34323-sha-af5da75a sandbox or in an existing project with npx storybook@0.0.0-pr-34323-sha-af5da75a upgrade.

More information
Published version 0.0.0-pr-34323-sha-af5da75a
Triggered by @ghengeveld
Repository storybookjs/storybook
Branch module-graph-change-listener
Commit af5da75a
Datetime Fri Mar 27 14:06:12 UTC 2026 (1774620372)
Workflow run 23650130559

To request a new release of this pull request, mention the @storybookjs/core team.

core team members can create a new canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=34323

Summary by CodeRabbit

  • New Features

    • Added a module-graph API that builds and exposes project dependency graphs plus a subscription hook to receive real-time updates (with unsubscribe), providing a reliable initial notification, debounced change delivery, and support for multiple listeners.
  • Tests

    • Added tests validating graph construction and node identity, relationship integrity, debounced notifications, subscribe/unsubscribe behavior, multi-listener delivery, and cleanup of polling/handlers on shutdown.

- Added `buildModuleGraph` function to create a module graph from Vite's module nodes.
- Introduced `onModuleGraphChange` to allow listeners to react to changes in the module graph.
- Created comprehensive tests for module graph behavior in `index.test.ts`.
- Updated type definitions to include `ModuleGraph` and `ModuleNode` for better type safety.
@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Mar 25, 2026

View your CI Pipeline Execution ↗ for commit 76cbb12


☁️ Nx Cloud last updated this comment at 2026-03-30 08:03:14 UTC

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Mar 25, 2026

View your CI Pipeline Execution ↗ for commit cb10bac


☁️ Nx Cloud last updated this comment at 2026-03-25 16:18:20 UTC

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 25, 2026

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

Constructs a ModuleGraph from Vite's moduleGraph, adds an onModuleGraphChange subscription API with a 100ms-debounced watcher handler and initial polling to emit the first graph, updates bail() to clean up watcher/interval/debounce/listeners, and adds Vitest coverage for graph building and notifications.

Changes

Cohort / File(s) Summary
Type Definitions
code/core/src/types/modules/core-common.ts
Adds exported ModuleNode and ModuleGraph types and extends the Builder interface with optional onModuleGraphChange(cb): () => void.
Module Graph Utility
code/builders/builder-vite/src/utils/build-module-graph.ts
New buildModuleGraph(fileToModulesMap) that converts Vite fileToModulesMap into a deduped ModuleGraph, skipping nodes without resolvable file and preserving importer/importedModules relationships.
Vite Builder Integration
code/builders/builder-vite/src/index.ts
Wires buildModuleGraph into builder: adds listener registry, exported onModuleGraphChange, attaches a 100ms-debounced watcher 'all' handler to rebuild/notify, starts a polling interval to emit initial graph once fileToModulesMap populates, and extends bail() to remove watcher handler, clear interval and debounce timer, and clear listeners.
Tests
code/builders/builder-vite/src/index.test.ts
New Vitest suite mocking createViteServer and watcher; tests buildModuleGraph (node identity, relationship wiring, null-file exclusion, duplicate-file distinct identities) and onModuleGraphChange behavior (unsubscribe, correct callback payload, debounce, event collapsing, multiple listeners, and bail() teardown including interval clearance).

Sequence Diagram

sequenceDiagram
    participant ViteServer as Vite Dev Server
    participant Watcher as File Watcher
    participant Debounce as Debounce Handler
    participant GraphBuilder as buildModuleGraph()
    participant Listeners as Registered Listeners

    ViteServer->>Watcher: attach 'all' handler (change/add/unlink)
    Note over Watcher,Debounce: file event(s) emitted
    Watcher->>Debounce: emit event
    Debounce->>Debounce: reset 100ms timer
    Note over Debounce: timer expires
    Debounce->>GraphBuilder: read fileToModulesMap -> build ModuleGraph
    GraphBuilder->>GraphBuilder: dedupe nodes, wire importers/importedModules
    GraphBuilder-->>Debounce: return ModuleGraph
    Debounce->>Listeners: notifyListeners(moduleGraph)
    par notify all callbacks
        Listeners->>Listeners: callback A receives graph
        Listeners->>Listeners: callback B receives graph
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes


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

Copy link
Copy Markdown
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: 4

🧹 Nitpick comments (1)
code/builders/builder-vite/src/index.test.ts (1)

9-11: Use vi.mock() with spy: true option.

Per coding guidelines, all package and file mocks should use the spy: true option for consistent mocking patterns.

Suggested fix
-vi.mock('./vite-server', () => ({
-  createViteServer: vi.fn(),
-}));
+vi.mock('./vite-server', { spy: true });

Then in beforeEach:

vi.mocked(createViteServer).mockResolvedValue(fakeViteServer);

As per coding guidelines: "Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/builders/builder-vite/src/index.test.ts` around lines 9 - 11, Update the
test mock call to use the spy option and convert direct mock assignments to the
typed mocked helper: change the vi.mock('./vite-server', () => ({
createViteServer: vi.fn(), })); to use vi.mock('./vite-server', { spy: true },
... ) (keeping the same factory) and in the test setup (e.g., beforeEach)
replace any direct assignments with
vi.mocked(createViteServer).mockResolvedValue(fakeViteServer) so the
createViteServer export is properly spied and typed for mocking.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code/builders/builder-vite/src/index.test.ts`:
- Around line 321-333: The test and implementation disagree on event names:
onModuleGraphChange registers a handler on the watcher's 'all' event but bail()
removes 'change' handlers; update the implementation so the same event name is
used everywhere. Locate the onModuleGraphChange function and the bail function
(symbols: onModuleGraphChange, bail) and change the registration/removal to the
same event (either use 'all' for both registration and off call, or register on
'change' and off('change', ...) consistently), and ensure watcher.off is invoked
with the exact handler reference that was passed to watcher.on so the listener
is actually removed.

In `@code/builders/builder-vite/src/index.ts`:
- Around line 120-124: The bail() function is removing watcherChangeHandler from
the wrong event ('change'); since the handler was registered on the 'all' event,
update the removal to use the same event name so the handler is detached
properly: in bail() locate the server?.watcher.off('change',
watcherChangeHandler) call and change it to off('all', watcherChangeHandler)
(keep the watcherChangeHandler guard and set watcherChangeHandler = undefined
afterwards) so the handler registered by the start/registration logic is
actually removed.
- Around line 100-109: Remove the temporary debug console output in this file:
delete the three console.log calls that print MODULE GRAPH and the
moduleGraph/Object.fromEntries output (the block referencing fileToModulesMap
and moduleGraph and mapping node.importedModules). If you need to keep
machine-readable diagnostics instead of removing them, replace the console.*
calls with the Storybook server logger from storybook/internal/node-logger and
log at an appropriate level (e.g., logger.debug) using the same data
transformation; otherwise simply remove the debug block before merge.
- Around line 163-169: The interval created by setInterval (assigned to
waitForModuleGraph) is never cleared when bail() is invoked, which can leak
memory and access server after close; hoist the interval reference (e.g., let
waitForModuleGraph: ReturnType<typeof setInterval> | null) to module scope,
assign it when you call setInterval in the index.ts block, and in the bail()
implementation clearInterval(waitForModuleGraph) and set it to null; also keep
the existing clearInterval(waitForModuleGraph) where the moduleGraph check
succeeds (and ensure watcherChangeHandler is only called after clearing).

---

Nitpick comments:
In `@code/builders/builder-vite/src/index.test.ts`:
- Around line 9-11: Update the test mock call to use the spy option and convert
direct mock assignments to the typed mocked helper: change the
vi.mock('./vite-server', () => ({ createViteServer: vi.fn(), })); to use
vi.mock('./vite-server', { spy: true }, ... ) (keeping the same factory) and in
the test setup (e.g., beforeEach) replace any direct assignments with
vi.mocked(createViteServer).mockResolvedValue(fakeViteServer) so the
createViteServer export is properly spied and typed for mocking.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7bab5d36-38c3-4a8a-bc89-6dac7091025b

📥 Commits

Reviewing files that changed from the base of the PR and between db1ddc8 and 11d7074.

📒 Files selected for processing (3)
  • code/builders/builder-vite/src/index.test.ts
  • code/builders/builder-vite/src/index.ts
  • code/core/src/types/modules/core-common.ts

Comment thread code/builders/builder-vite/src/index.test.ts Outdated
Comment thread code/builders/builder-vite/src/index.ts Outdated
Comment thread code/builders/builder-vite/src/index.ts
Comment thread code/builders/builder-vite/src/index.ts Outdated
Copy link
Copy Markdown
Contributor

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 introduces a builder-agnostic module graph type to Storybook core types and adds an onModuleGraphChange subscription API to @storybook/builder-vite, intended to support dependency tracking updates for server-side change detection.

Changes:

  • Extend the core Builder interface with an optional onModuleGraphChange callback API and introduce shared ModuleGraph / ModuleNode types.
  • Add Vite module graph conversion logic and a debounced watcher-based notifier in @storybook/builder-vite.
  • Add unit tests covering graph conversion, listener registration/unsubscription, debounce behavior, and bail() cleanup.

Reviewed changes

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

File Description
code/core/src/types/modules/core-common.ts Adds onModuleGraphChange to the Builder interface and defines shared module graph/node types.
code/builders/builder-vite/src/index.ts Implements module graph conversion + listener notification and wires it to the Vite watcher and startup timing.
code/builders/builder-vite/src/index.test.ts Adds unit tests for the module graph conversion and change notification API.
Comments suppressed due to low confidence (2)

code/builders/builder-vite/src/index.ts:169

  • The PR description/issue scope calls for an eager startup indexing pass across story entrypoints to build a complete dependency map. This implementation only waits for fileToModulesMap.size > 0 and then emits whatever happens to be in the module graph after waitForRequestsIdle(), which may be incomplete for lazily loaded stories. Either add the explicit indexing pass over story entrypoints, or update the description/semantics so consumers don't assume the graph is complete.
      toJson: () => {
        throw new NoStatsForViteDevError();
      },
    },
    totalTime: process.hrtime(startTime),
  };
};

code/builders/builder-vite/src/index.ts:167

  • waitForModuleGraph uses setInterval but the interval handle isn't stored anywhere that bail() can clear. If Storybook shuts down before the graph is populated (or during tests), this interval can continue running and call into a closing/closed Vite server. Store the interval id and clear it in bail() (and ensure a second start() doesn't leave multiple intervals running).
      toJson: () => {
        throw new NoStatsForViteDevError();
      },
    },
    totalTime: process.hrtime(startTime),

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

Comment thread code/builders/builder-vite/src/index.test.ts Outdated
Comment thread code/builders/builder-vite/src/index.ts Outdated
Comment thread code/builders/builder-vite/src/index.ts
@ghengeveld ghengeveld changed the title [Review Changes]: T1: Add onModuleGraphChange to builder-vite Builder-Vite: Add onModuleGraphChange method to listen for graph updates Mar 25, 2026
@ghengeveld ghengeveld changed the title Builder-Vite: Add onModuleGraphChange method to listen for graph updates Builder-Vite: Add onModuleGraphChange method Mar 25, 2026
- Changed the event listener removal from 'change' to 'all' in the bail function to ensure proper cleanup.
- Updated the test description to reflect the new behavior of removing the all-event watcher during bail.
- Added assertions in tests to verify the listener count before and after bail operations.
Copy link
Copy Markdown
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.

♻️ Duplicate comments (1)
code/builders/builder-vite/src/index.ts (1)

152-158: ⚠️ Potential issue | 🟠 Major

waitForModuleGraph interval not cleared on bail().

If bail() is called before fileToModulesMap.size > 0, the interval continues running. This can cause memory leaks and potential errors if the server is closed while the interval is still polling. This issue was flagged in a previous review but appears unaddressed.

Proposed fix: Store interval at module level and clear in bail()
 let debounce: ReturnType<typeof setTimeout> | undefined;
 let watcherChangeHandler: (() => void) | undefined;
+let waitForModuleGraphInterval: ReturnType<typeof setInterval> | undefined;

In bail():

 export async function bail(): Promise<void> {
+  if (waitForModuleGraphInterval) {
+    clearInterval(waitForModuleGraphInterval);
+    waitForModuleGraphInterval = undefined;
+  }
+
   if (watcherChangeHandler) {
     server?.watcher.off('all', watcherChangeHandler);

In start():

-  const waitForModuleGraph = setInterval(async () => {
+  waitForModuleGraphInterval = setInterval(async () => {
     if (server.moduleGraph.fileToModulesMap.size > 0) {
-      clearInterval(waitForModuleGraph);
+      clearInterval(waitForModuleGraphInterval);
+      waitForModuleGraphInterval = undefined;
       await server.waitForRequestsIdle();
       watcherChangeHandler?.();
     }
   }, 1000);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/builders/builder-vite/src/index.ts` around lines 152 - 158, The interval
created as waitForModuleGraph inside start() can keep running if bail() is
called before server.moduleGraph.fileToModulesMap.size > 0; make the interval
reference module-scoped (move declaration of waitForModuleGraph outside start or
attach it to a higher-scope variable) so bail() can call
clearInterval(waitForModuleGraph), and also clearInterval(waitForModuleGraph)
immediately before/after you call clearInterval in the successful branch (inside
the setInterval when size > 0) to ensure it’s always cleared; update bail() to
check and clearInterval(waitForModuleGraph) and ensure any code that awaits
server.waitForRequestsIdle() still runs after the interval is cleared (use the
same waitForModuleGraph identifier in start(), in the setInterval callback, and
in bail()).
🧹 Nitpick comments (1)
code/builders/builder-vite/src/index.test.ts (1)

9-11: Consider using spy: true option for vi.mock.

Per coding guidelines, vi.mock() should use the spy: true option for file mocks. However, since this is a complete mock replacement that doesn't need to call through to the original, this is acceptable.

Optional: Use spy option
-vi.mock('./vite-server', () => ({
-  createViteServer: vi.fn(),
-}));
+vi.mock('./vite-server', { spy: true });

Then in beforeEach:

vi.mocked(createViteServer).mockResolvedValue(fakeViteServer);

As per coding guidelines: "Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/builders/builder-vite/src/index.test.ts` around lines 9 - 11, The test
currently replaces the entire module with vi.mock('./vite-server', () => ({
createViteServer: vi.fn() })), but per guidelines you should call vi.mock with
the spy: true option so mocks can be treated as spies; update the mock call to
use vi.mock('./vite-server', { spy: true }, /* optional factory if still
replacing */) or simply vi.mock('./vite-server', { spy: true }) and then in
setup use vi.mocked(createViteServer).mockResolvedValue(fakeViteServer) to set
the resolved value for createViteServer; ensure you reference the
createViteServer symbol from the vite-server module when switching to vi.mocked.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@code/builders/builder-vite/src/index.ts`:
- Around line 152-158: The interval created as waitForModuleGraph inside start()
can keep running if bail() is called before
server.moduleGraph.fileToModulesMap.size > 0; make the interval reference
module-scoped (move declaration of waitForModuleGraph outside start or attach it
to a higher-scope variable) so bail() can call
clearInterval(waitForModuleGraph), and also clearInterval(waitForModuleGraph)
immediately before/after you call clearInterval in the successful branch (inside
the setInterval when size > 0) to ensure it’s always cleared; update bail() to
check and clearInterval(waitForModuleGraph) and ensure any code that awaits
server.waitForRequestsIdle() still runs after the interval is cleared (use the
same waitForModuleGraph identifier in start(), in the setInterval callback, and
in bail()).

---

Nitpick comments:
In `@code/builders/builder-vite/src/index.test.ts`:
- Around line 9-11: The test currently replaces the entire module with
vi.mock('./vite-server', () => ({ createViteServer: vi.fn() })), but per
guidelines you should call vi.mock with the spy: true option so mocks can be
treated as spies; update the mock call to use vi.mock('./vite-server', { spy:
true }, /* optional factory if still replacing */) or simply
vi.mock('./vite-server', { spy: true }) and then in setup use
vi.mocked(createViteServer).mockResolvedValue(fakeViteServer) to set the
resolved value for createViteServer; ensure you reference the createViteServer
symbol from the vite-server module when switching to vi.mocked.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c07e2497-f0d3-4bf1-b377-ce99b158fc44

📥 Commits

Reviewing files that changed from the base of the PR and between cb10bac and c72d208.

📒 Files selected for processing (2)
  • code/builders/builder-vite/src/index.test.ts
  • code/builders/builder-vite/src/index.ts

Copy link
Copy Markdown
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.

🧹 Nitpick comments (2)
code/builders/builder-vite/src/index.test.ts (1)

9-11: Use spy: true option for vi.mock per coding guidelines.

The mock should use the spy: true option to align with the project's spy mocking conventions.

Suggested fix
-vi.mock('./vite-server', () => ({
-  createViteServer: vi.fn(),
-}));
+vi.mock('./vite-server', { spy: true });

The mock implementation in beforeEach at line 202 already uses vi.mocked(createViteServer).mockResolvedValue(...), which will work correctly with this change.

As per coding guidelines: "Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/builders/builder-vite/src/index.test.ts` around lines 9 - 11, Update the
module mock to use the Vitest spy option: change the existing
vi.mock('./vite-server', () => ({ createViteServer: vi.fn() })) call to include
the { spy: true } option so Vitest treats the mock as a spy; keep the same
exported symbol name createViteServer since the tests (e.g., the beforeEach that
calls vi.mocked(createViteServer).mockResolvedValue(...)) rely on that export.
code/builders/builder-vite/src/index.ts (1)

149-165: Consider extracting the debounce delay as a constant.

The 100ms debounce delay (line 151) and 1000ms polling interval (line 158) are reasonable values but are hardcoded. For maintainability, consider extracting these as named constants.

Suggested refactor
+const MODULE_GRAPH_CHANGE_DEBOUNCE_MS = 100;
+const MODULE_GRAPH_POLL_INTERVAL_MS = 1000;
+
 // ... inside start():
   watcherChangeHandler = () => {
     clearTimeout(debounce);
     debounce = setTimeout(() => {
       notifyListeners(buildModuleGraph(server.moduleGraph.fileToModulesMap));
-    }, 100);
+    }, MODULE_GRAPH_CHANGE_DEBOUNCE_MS);
   };

   server.watcher.on('all', watcherChangeHandler);

-  waitForModuleGraph = setInterval(async () => {
+  waitForModuleGraph = setInterval(async () => {
     if (server.moduleGraph.fileToModulesMap.size > 0) {
       // ...
     }
-  }, 1000);
+  }, MODULE_GRAPH_POLL_INTERVAL_MS);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/builders/builder-vite/src/index.ts` around lines 149 - 165, Extract the
hardcoded delays into named constants (e.g. DEBOUNCE_DELAY_MS and
POLL_INTERVAL_MS) and use them in watcherChangeHandler's setTimeout and the
setInterval for waitForModuleGraph; specifically replace the literal 100 used in
setTimeout inside watcherChangeHandler (which debounces
notifyListeners(buildModuleGraph(server.moduleGraph.fileToModulesMap))) with
DEBOUNCE_DELAY_MS and replace the literal 1000 used in setInterval that checks
server.moduleGraph.fileToModulesMap and calls
server.waitForRequestsIdle()/watcherChangeHandler with POLL_INTERVAL_MS so the
timing values are centralized and easier to adjust.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@code/builders/builder-vite/src/index.test.ts`:
- Around line 9-11: Update the module mock to use the Vitest spy option: change
the existing vi.mock('./vite-server', () => ({ createViteServer: vi.fn() }))
call to include the { spy: true } option so Vitest treats the mock as a spy;
keep the same exported symbol name createViteServer since the tests (e.g., the
beforeEach that calls vi.mocked(createViteServer).mockResolvedValue(...)) rely
on that export.

In `@code/builders/builder-vite/src/index.ts`:
- Around line 149-165: Extract the hardcoded delays into named constants (e.g.
DEBOUNCE_DELAY_MS and POLL_INTERVAL_MS) and use them in watcherChangeHandler's
setTimeout and the setInterval for waitForModuleGraph; specifically replace the
literal 100 used in setTimeout inside watcherChangeHandler (which debounces
notifyListeners(buildModuleGraph(server.moduleGraph.fileToModulesMap))) with
DEBOUNCE_DELAY_MS and replace the literal 1000 used in setInterval that checks
server.moduleGraph.fileToModulesMap and calls
server.waitForRequestsIdle()/watcherChangeHandler with POLL_INTERVAL_MS so the
timing values are centralized and easier to adjust.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 08accd7e-aa09-46fb-a024-432dd4d77fa9

📥 Commits

Reviewing files that changed from the base of the PR and between c72d208 and f1626c8.

📒 Files selected for processing (2)
  • code/builders/builder-vite/src/index.test.ts
  • code/builders/builder-vite/src/index.ts

Comment thread code/builders/builder-vite/src/index.test.ts
Comment thread code/builders/builder-vite/src/index.ts
Comment thread code/builders/builder-vite/src/index.ts Outdated
Comment thread code/builders/builder-vite/src/index.ts Outdated
Comment thread code/builders/builder-vite/src/index.ts Outdated
Comment thread code/builders/builder-vite/src/index.ts Outdated
Comment thread code/builders/builder-vite/src/index.ts Outdated
Copy link
Copy Markdown
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code/builders/builder-vite/src/utils/build-module-graph.ts`:
- Around line 49-57: When resolving neighbor nodes in buildModuleGraph, ensure
getOrCreateModuleNode uses fileToModulesMap to find a module when
viteModuleNode.file === null so edges from viteModuleNode.importers and
viteModuleNode.importedModules are not dropped: modify the neighbor resolution
in the loops that iterate over viteModuleNode.importers and
viteModuleNode.importedModules to fall back to lookup in fileToModulesMap (or
other map used later) when getOrCreateModuleNode(importer|importedModule)
returns undefined due to a null file, and then add that returned node to
moduleNode.importers/moduleNode.importedModules as before; also add a regression
test that creates a vite node with file: null discovered first via
importers/importedModules and later populated in fileToModulesMap to assert the
edge is preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2de6baba-584f-4b5b-b233-aafca3ba2e20

📥 Commits

Reviewing files that changed from the base of the PR and between f1626c8 and af5da75.

📒 Files selected for processing (2)
  • code/builders/builder-vite/src/index.ts
  • code/builders/builder-vite/src/utils/build-module-graph.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/builders/builder-vite/src/index.ts

Comment thread code/builders/builder-vite/src/utils/build-module-graph.ts
@storybook-app-bot
Copy link
Copy Markdown

storybook-app-bot Bot commented Mar 27, 2026

Package Benchmarks

Commit: 76cbb12, ran on 30 March 2026 at 08:15:25 UTC

No significant changes detected, all good. 👏

@ghengeveld ghengeveld changed the title Builder-Vite: Add onModuleGraphChange method Core: Add story change detection service and link it to builder-vite Mar 27, 2026
@ghengeveld ghengeveld changed the title Core: Add story change detection service and link it to builder-vite Builder-Vite: Add onModuleGraphChange method Mar 27, 2026
@ghengeveld ghengeveld force-pushed the module-graph-change-listener branch from 40f3955 to af5da75 Compare March 27, 2026 14:04
@ghengeveld ghengeveld merged commit df78590 into next Mar 30, 2026
123 of 124 checks passed
@ghengeveld ghengeveld deleted the module-graph-change-listener branch March 30, 2026 10:29
@github-actions github-actions Bot mentioned this pull request Mar 30, 2026
18 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Review Changes]: T1 - Add onModuleGraphChange to builder-vite

3 participants