Skip to content

Core: Add ChangeDetectionService and wire up builder-vite#34369

Merged
ghengeveld merged 13 commits into
nextfrom
change-detection-service
Mar 31, 2026
Merged

Core: Add ChangeDetectionService and wire up builder-vite#34369
ghengeveld merged 13 commits into
nextfrom
change-detection-service

Conversation

@ghengeveld
Copy link
Copy Markdown
Member

@ghengeveld ghengeveld commented Mar 27, 2026

Closes #34253

What I did

Implement module-graph change detection for affected stories.

Track changed files from git, trace affected story files through the builder module graph, and surface those results through the dev server status store.

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-34369-sha-7d187a58. Try it out in a new sandbox by running npx storybook@0.0.0-pr-34369-sha-7d187a58 sandbox or in an existing project with npx storybook@0.0.0-pr-34369-sha-7d187a58 upgrade.

More information
Published version 0.0.0-pr-34369-sha-7d187a58
Triggered by @ghengeveld
Repository storybookjs/storybook
Branch change-detection-service
Commit 7d187a58
Datetime Mon Mar 30 13:42:30 UTC 2026 (1774878150)
Workflow run 23747855159

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=34369

Summary by CodeRabbit

  • New Features
    • Added a change-detection system that detects git file changes and marks stories as "modified", "affected", or "new".
    • Story status is automatically published so UIs can reflect change-driven status updates.
    • Preview now starts the change-detection service for non-ignored previews when enabled.
    • Exposes an experimental readiness API so consumers can await change-detection readiness or error states.

Track changed files from git, trace affected story files through the builder module graph, and surface those results through the dev server status store.
@ghengeveld ghengeveld self-assigned this Mar 27, 2026
@ghengeveld ghengeveld changed the title Change detection service Core: Add ChangeDetectionService and wire up builder-vite Mar 27, 2026
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

Implements a server-side change detection pipeline that uses the builder’s module graph plus git working tree changes to compute per-story “new/modified/affected” statuses and publish them via the dev server status store.

Changes:

  • Add ChangeDetectionService (+ readiness signal) and supporting git diff + module-graph tracing utilities in core-server.
  • Wire the service into storybookDevServer() and export the experimental readiness API.
  • Update @storybook/builder-vite to warm up/poll the module graph and emit graph changes for consumers.

Reviewed changes

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

Show a summary per file
File Description
code/core/src/types/modules/core-common.ts Formatting adjustment for type-only imports.
code/core/src/core-server/index.ts Export change detection readiness + service from core-server.
code/core/src/core-server/dev-server.ts Instantiate/start change detection service and dispose on preview build failure.
code/core/src/core-server/change-detection/trace-changed.ts BFS tracer from changed file → reachable story files with distances.
code/core/src/core-server/change-detection/trace-changed.test.ts Unit tests for BFS tracer behavior (distance, cycles, etc.).
code/core/src/core-server/change-detection/service.ts Core service: git diff + module-graph tracing + status store patching + readiness.
code/core/src/core-server/change-detection/service.test.ts Unit tests for service status classification and patch behavior.
code/core/src/core-server/change-detection/readiness.ts Deferred readiness signal implementation.
code/core/src/core-server/change-detection/index.ts Barrel exports for change detection module.
code/core/src/core-server/change-detection/git-diff-provider.ts git CLI provider for staged/unstaged/untracked file detection.
code/core/src/core-server/change-detection/git-diff-provider.test.ts Unit tests for git diff provider.
code/core/src/core-server/change-detection/errors.ts Typed error classes for unavailable/failure conditions.
code/builders/builder-vite/src/index.ts Add module-graph warmup/polling to reliably emit module graph changes.
code/builders/builder-vite/src/index.test.ts Update tests to account for new warmup/polling behavior and watcher hookup timing.
.vscode/settings.json Point VS Code TS SDK to node_modules/typescript.

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

Comment thread code/core/src/core-server/change-detection/service.ts Outdated
Comment thread code/core/src/core-server/change-detection/service.ts Outdated
Comment thread code/core/src/core-server/change-detection/git-diff-provider.ts Outdated
Comment thread code/builders/builder-vite/src/index.ts Outdated
Comment thread code/builders/builder-vite/src/index.ts
Comment thread code/core/src/core-server/change-detection/trace-changed.ts Outdated
Comment thread code/core/src/core-server/change-detection/service.test.ts Outdated
Comment thread code/core/src/core-server/change-detection/GitDiffProvider.test.ts
Comment thread code/core/src/core-server/change-detection/service.ts Outdated
Comment thread code/core/src/core-server/change-detection/service.ts Outdated
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 27, 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

Introduces a change-detection subsystem that observes Vite module-graph updates, runs git-based diffs, computes per-story impact (new/modified/affected) via dependency traversal, publishes statuses to a status store, and exposes readiness/error states for consumers.

Changes

Cohort / File(s) Summary
Core Change Detection
code/core/src/core-server/change-detection/errors.ts, code/core/src/core-server/change-detection/readiness.ts, code/core/src/core-server/change-detection/index.ts
Added domain errors (ChangeDetectionUnavailableError, ChangeDetectionFailureError) and a readiness API with deferred-promise semantics and reset capability; added a change-detection public barrel.
Git Integration
code/core/src/core-server/change-detection/git-diff-provider.ts, code/core/src/core-server/change-detection/git-diff-provider.test.ts
New GitDiffProvider that runs git commands to produce changed and new file sets; maps git failures to domain errors; includes tests for success and failure paths.
Dependency Tracing
code/core/src/core-server/change-detection/trace-changed.ts, code/core/src/core-server/change-detection/trace-changed.test.ts
Added findAffectedStoryFiles performing BFS over moduleGraph importers to compute affected story files with shortest distances; tests for cycles, multiple paths, and distance semantics.
Service Orchestration
code/core/src/core-server/change-detection/service.ts, code/core/src/core-server/change-detection/service.test.ts
New ChangeDetectionService orchestrates subscriptions to module-graph events, debounced scans combining git diffs and story index, computes/merges statuses, publishes patches to status store, and manages readiness/error lifecycle; extensive tests cover behavior and error cases.
Vite Builder Integration
code/builders/builder-vite/src/index.ts, code/builders/builder-vite/src/index.test.ts
Changed onModuleGraphChange to emit typed events; watcher/polling now starts only if listeners exist; added startup warming, error broadcasting, subscription gating (ViteModuleGraphSubscriptionError), and many test updates/refactors to exercise new lifecycle.
Dev Server Wiring
code/core/src/core-server/dev-server.ts
Wires up ChangeDetectionService for previews (feature-gated), starts it with preview builder events, and ensures disposal on preview start failures.
Status Store & Types
code/core/src/shared/status-store/index.ts, code/core/src/types/modules/status.ts, code/core/src/manager/globals/exports.ts
Added and exported CHANGE_DETECTION_STATUS_TYPE_ID to identify change-detection statuses across status store and globals.
Public API & Errors
code/core/src/core-server/index.ts, code/core/src/server-errors.ts
Re-exported change-detection readiness and ChangeDetectionService; added ViteModuleGraphSubscriptionError for late subscriptions.
Misc / Config
.vscode/settings.json, code/core/src/types/modules/core-common.ts
Updated VS Code TypeScript SDK path; converted some imports to import type; changed Builder onModuleGraphChange signature to accept ModuleGraphChangeEvent.

Sequence Diagram

sequenceDiagram
    participant DevServer as Dev Server
    participant Builder as Vite Builder
    participant ModuleGraph as Module Graph
    participant GitProvider as GitDiffProvider
    participant Service as ChangeDetectionService
    participant StatusStore as Status Store

    DevServer->>Service: construct(start with storyIndexGenerator, statusStore)
    DevServer->>Builder: start and register onModuleGraphChange
    Builder->>ModuleGraph: module graph updates
    Builder->>Service: emit {type:'moduleGraph', moduleGraph}
    Service->>Service: debounce / schedule scan
    Service->>GitProvider: getChangedFiles()
    GitProvider->>GitProvider: run git commands
    GitProvider-->>Service: return {changed, new}
    Service->>Service: findAffectedStoryFiles(changed, moduleGraph)
    Service->>Service: compute per-story statuses (new/modified/affected)
    Service->>StatusStore: applyPatch(statuses)
    Service->>Service: setChangeDetectionReadiness('ready')
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs


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: 3

🧹 Nitpick comments (1)
code/core/src/core-server/change-detection/git-diff-provider.ts (1)

43-72: Consider more specific error attribution for parallel git commands.

When the Promise.all rejects, the error is attributed to 'git diff' (line 70), but the failure could originate from any of the three commands including git ls-files. This could make debugging slightly harder.

Consider capturing command context or using Promise.allSettled with individual error handling if precise attribution is needed, though this is a minor concern since all commands run in the same repo context.

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

In `@code/core/src/core-server/change-detection/git-diff-provider.ts` around lines
43 - 72, The Promise.all in getChangedFiles currently maps any rejection to a
generic 'git diff' error via toGitError, which loses which of the three execa
calls failed; update getChangedFiles to attribute errors to the specific command
that failed (e.g., the first execa for staged diff, second for unstaged diff, or
the execa('git', ['ls-files'...]) for untracked) by either using
Promise.allSettled and checking each result for .status === 'rejected' and
calling toGitError with a descriptive command name, or by wrapping each execa
call in its own try/catch that calls this.toGitError(error, '<specific git
command>') before rethrowing so the thrown error contains the precise command
context.
🤖 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.ts`:
- Around line 121-122: The fire-and-forget call to startChangeDetection(options)
can reject during preset resolution/index generation/warmup and leave change
detection half-initialized; make this explicit and observe rejections by calling
it as void startChangeDetection(options).catch(err => { /* handle/log */ });:
update the call site to prefix with void and attach a .catch that logs the error
(use the existing logger in this module or console.error) and perform any
minimal cleanup or state marking so bail() or shutdown sees the failure,
referencing startChangeDetection and bail to ensure the cleanup path remains
correct.
- Around line 88-98: The code in waitForModuleGraph registers and calls
watcherChangeHandler after awaiting server.waitForRequestsIdle(), but bail() may
clear watcherChangeHandler during that await causing server.watcher.on('all',
watcherChangeHandler) or watcherChangeHandler() to be invoked as undefined;
update the block in waitForModuleGraph to re-check that watcherChangeHandler is
still defined after the await (i.e., after server.waitForRequestsIdle()) before
calling server.watcher.on('all', watcherChangeHandler) and before invoking
watcherChangeHandler(), and bail() should continue to be allowed to clear
watcherChangeHandler as before.

In `@code/core/src/core-server/change-detection/git-diff-provider.test.ts`:
- Around line 20-38: Move the vi.mocked(execa) setup into a beforeEach and split
the "staged-new" and "untracked-new" fixtures so the same path isn't returned by
both staged and untracked commands; specifically, in beforeEach mock the
sequence of execa calls that GitDiffProvider('/repo') expects (repo root, staged
list, unstaged list, untracked list) and ensure the staged list contains the
"new" file (e.g. 'src/NewButton.stories.tsx') while the untracked list contains
a different file (e.g. 'src/Button.css'), then remove any inline
vi.mocked(execa) calls from individual it blocks so tests call
GitDiffProvider.getChangedFiles() against the shared beforeEach mocks.

---

Nitpick comments:
In `@code/core/src/core-server/change-detection/git-diff-provider.ts`:
- Around line 43-72: The Promise.all in getChangedFiles currently maps any
rejection to a generic 'git diff' error via toGitError, which loses which of the
three execa calls failed; update getChangedFiles to attribute errors to the
specific command that failed (e.g., the first execa for staged diff, second for
unstaged diff, or the execa('git', ['ls-files'...]) for untracked) by either
using Promise.allSettled and checking each result for .status === 'rejected' and
calling toGitError with a descriptive command name, or by wrapping each execa
call in its own try/catch that calls this.toGitError(error, '<specific git
command>') before rethrowing so the thrown error contains the precise command
context.
🪄 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: cfc75601-eb26-4b8f-8040-78cbf224f12c

📥 Commits

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

📒 Files selected for processing (15)
  • .vscode/settings.json
  • code/builders/builder-vite/src/index.test.ts
  • code/builders/builder-vite/src/index.ts
  • code/core/src/core-server/change-detection/errors.ts
  • code/core/src/core-server/change-detection/git-diff-provider.test.ts
  • code/core/src/core-server/change-detection/git-diff-provider.ts
  • code/core/src/core-server/change-detection/index.ts
  • code/core/src/core-server/change-detection/readiness.ts
  • code/core/src/core-server/change-detection/service.test.ts
  • code/core/src/core-server/change-detection/service.ts
  • code/core/src/core-server/change-detection/trace-changed.test.ts
  • code/core/src/core-server/change-detection/trace-changed.ts
  • code/core/src/core-server/dev-server.ts
  • code/core/src/core-server/index.ts
  • code/core/src/types/modules/core-common.ts

Comment thread code/builders/builder-vite/src/index.ts Outdated
Comment thread code/builders/builder-vite/src/index.ts Outdated
Comment thread code/core/src/core-server/change-detection/git-diff-provider.test.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: 2

🤖 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/core/src/core-server/change-detection/service.ts`:
- Around line 123-131: The dispose() method currently only prevents future
callbacks but doesn't stop already-running scan() tasks from calling
applyPatch() / setChangeDetectionReadiness() or scheduling another run; add a
disposed boolean field (e.g., this.disposed = false initially), set
this.disposed = true in dispose() (alongside clearing debounceTimer and
unsubscribe), and update scan() to check this.disposed after any await points
(notably after awaiting buildStatuses(...) and before calling applyPatch() /
setChangeDetectionReadiness() and before triggering the finally rerun logic) so
that no state changes or reruns occur once disposed.
- Line 1: The code currently uses resolve() and a string replace to strip
repoRoot from absolute file paths (affecting changedStoryFiles), which fails on
Windows due to backslashes; import and use path.relative() instead: compute
repoRelative = relative(repoRoot, absPath) for each path (in the same place
where changedStoryFiles are built), and if you need consistent separators,
normalize to forward slashes (e.g., replace backslashes) before adding to
changedStoryFiles; update the import to include relative from 'node:path' and
remove the fragile `${repoRoot}/` string-replacement logic.
🪄 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: 823b50be-6b9f-4665-ba77-919e4df564db

📥 Commits

Reviewing files that changed from the base of the PR and between 40f3955 and d54f98b.

📒 Files selected for processing (6)
  • code/.storybook/main.ts
  • code/builders/builder-vite/src/index.test.ts
  • code/core/src/core-server/change-detection/index.ts
  • code/core/src/core-server/change-detection/service.ts
  • code/core/src/shared/status-store/index.ts
  • code/core/src/types/modules/status.ts
✅ Files skipped from review due to trivial changes (3)
  • code/.storybook/main.ts
  • code/core/src/shared/status-store/index.ts
  • code/core/src/core-server/change-detection/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/builders/builder-vite/src/index.test.ts

Comment thread code/core/src/core-server/change-detection/service.ts Outdated
…s for changed files and update related tests. Adjusted logic to handle new and changed files more effectively.
@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Mar 30, 2026

View your CI Pipeline Execution ↗ for commit 1110110

Command Status Duration Result
nx run-many -t compile,check,knip,test,lint,fmt... ✅ Succeeded 7m 52s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-31 13:02:33 UTC

Comment thread code/.storybook/main.ts Outdated
Comment thread code/core/src/core-server/change-detection/service.ts Outdated
Comment thread code/core/src/core-server/change-detection/service.ts Outdated
…dule graph subscription. Introduced ViteModuleGraphSubscriptionError for better error messaging. Updated tests to cover new scenarios and ensure robust change detection functionality. Handled review comments.
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

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


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

Comment thread code/core/src/core-server/dev-server.ts Outdated
Comment thread code/core/src/core-server/dev-server.ts Outdated
Comment thread code/core/src/core-server/change-detection/GitDiffProvider.ts
Comment thread code/builders/builder-vite/src/index.ts Outdated
Comment thread 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.

Actionable comments posted: 2

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

96-99: Warm each import path once.

storyIndex.entries is entry-level, so the same CSF file can show up once per story (plus docs entries). Deduping importPath values keeps startup work proportional to files instead of entries.

Proposed refactor
-  await Promise.all(
-    Object.values(storyIndex.entries).map((entry) => server.warmupRequest(entry.importPath))
-  );
+  const importPaths = new Set(
+    Object.values(storyIndex.entries).map((entry) => entry.importPath)
+  );
+
+  await Promise.all(
+    Array.from(importPaths, (importPath) => server.warmupRequest(importPath))
+  );
🤖 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 96 - 99, The current
warm-up calls loop over storyIndex.entries and calls
server.warmupRequest(entry.importPath) for every entry, which duplicates work
for multiple stories from the same CSF file; change this to dedupe by importPath
first (e.g., collect unique entry.importPath values from storyIndex.entries) and
then call server.warmupRequest once per unique importPath so
server.warmupRequest and storyIndex.entries remain the referenced symbols to
locate the change.
🤖 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.ts`:
- Around line 102-123: The polling block around waitForModuleGraph currently
swallows startup timeout/failure; on both the >30s branch and the catch branch
you must surface a terminal failure to ChangeDetectionService so start() can
settle instead of waiting forever. Modify the timeout branch (where
process.hrtime(startTime)[0] > 30) and the catch block to call the
change-detection readiness/failure API (e.g., invoke the ChangeDetectionService
method that marks startup as unavailable/fails readiness or emit its
"unavailable"/error event) rather than only calling clearModuleGraphPolling and
logging; ensure you reference the same
watcherChangeHandler/server.waitForRequestsIdle flow so you still attach the
watcher when successful, but on any warmup/getIndex/waitForRequestsIdle failure
or timeout call the ChangeDetectionService failure signal (or reject its startup
promise) so consumers waiting on start() or the first scan are unblocked with an
error.

In `@code/core/src/core-server/change-detection/service.ts`:
- Around line 189-195: When handling ChangeDetectionUnavailableError in the scan
branch, after calling this.resolveReadiness({ status: 'unavailable', reason:
error.message, error }), immediately invoke the teardown helper that cancels the
scanner/subscription (the teardown function defined earlier near the readiness
setup) so the service unsubscribes/stops scanning and does not re-run git on
subsequent module-graph updates; keep the resolveReadiness call and add the
teardown invocation right after it in the ChangeDetectionUnavailableError
branch.

---

Nitpick comments:
In `@code/builders/builder-vite/src/index.ts`:
- Around line 96-99: The current warm-up calls loop over storyIndex.entries and
calls server.warmupRequest(entry.importPath) for every entry, which duplicates
work for multiple stories from the same CSF file; change this to dedupe by
importPath first (e.g., collect unique entry.importPath values from
storyIndex.entries) and then call server.warmupRequest once per unique
importPath so server.warmupRequest and storyIndex.entries remain the referenced
symbols to locate the change.
🪄 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: 9823a29d-9838-4d0b-b8d9-eae15711f080

📥 Commits

Reviewing files that changed from the base of the PR and between 1c1ca77 and 3842bd0.

📒 Files selected for processing (8)
  • code/builders/builder-vite/src/index.test.ts
  • code/builders/builder-vite/src/index.ts
  • code/core/src/core-server/change-detection/git-diff-provider.test.ts
  • code/core/src/core-server/change-detection/git-diff-provider.ts
  • code/core/src/core-server/change-detection/service.test.ts
  • code/core/src/core-server/change-detection/service.ts
  • code/core/src/core-server/change-detection/trace-changed.ts
  • code/core/src/server-errors.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • code/core/src/core-server/change-detection/trace-changed.ts
  • code/core/src/core-server/change-detection/git-diff-provider.test.ts
  • code/core/src/core-server/change-detection/git-diff-provider.ts
  • code/builders/builder-vite/src/index.test.ts
  • code/core/src/core-server/change-detection/service.test.ts

Comment thread code/builders/builder-vite/src/index.ts
Base automatically changed from module-graph-change-listener to next March 30, 2026 10:29
Comment thread code/core/src/core-server/change-detection/service.ts Outdated
Surface Vite module-graph startup failures and timeouts to ChangeDetectionService so readiness no longer hangs. Stop rescanning after permanent unavailability, dedupe warmup requests by import path, and harden preview cleanup error handling.
@ghengeveld ghengeveld force-pushed the change-detection-service branch from baa4179 to a572bf6 Compare March 30, 2026 13:32
@storybook-app-bot
Copy link
Copy Markdown

storybook-app-bot Bot commented Mar 30, 2026

Package Benchmarks

Commit: 1110110, ran on 31 March 2026 at 13:06:29 UTC

The following packages have significant changes to their size or dependencies:

storybook

Before After Difference
Dependency count 50 50 0
Self size 20.47 MB 20.49 MB 🚨 +18 KB 🚨
Dependency size 16.55 MB 16.55 MB 0 B
Bundle Size Analyzer Link Link

@storybook/cli

Before After Difference
Dependency count 184 184 0
Self size 782 KB 782 KB 🎉 -84 B 🎉
Dependency size 67.70 MB 67.72 MB 🚨 +18 KB 🚨
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 177 177 0
Self size 32 KB 32 KB 0 B
Dependency size 66.22 MB 66.24 MB 🚨 +18 KB 🚨
Bundle Size Analyzer Link Link

create-storybook

Before After Difference
Dependency count 51 51 0
Self size 1.04 MB 1.04 MB 🎉 -72 B 🎉
Dependency size 37.03 MB 37.05 MB 🚨 +18 KB 🚨
Bundle Size Analyzer node node

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 (4)
code/core/src/core-server/change-detection/service.test.ts (2)

93-99: Stub logger.debug here as well.

ChangeDetectionService.start() hits logger.debug() on the enabled/disabled path, so this setup still exercises a real logger method in most tests.

Patch
     vi.mocked(logger.info).mockImplementation(() => undefined);
     vi.mocked(logger.warn).mockImplementation(() => undefined);
     vi.mocked(logger.error).mockImplementation(() => undefined);
+    vi.mocked(logger.debug).mockImplementation(() => undefined);

As per coding guidelines, "Mock external dependencies like file system access and loggers in tests" and "Mock all required properties and methods that the test subject uses in Vitest tests."

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

In `@code/core/src/core-server/change-detection/service.test.ts` around lines 93 -
99, The tests call ChangeDetectionService.start() which triggers logger.debug on
the enabled/disabled path but the beforeEach only stubs logger.info/warn/error;
add a mock for logger.debug in the beforeEach (alongside the existing
vi.mocked(logger.info/warn/error).mockImplementation(...) calls and before
internal_resetChangeDetectionReadiness()) so the real logger.debug is not
exercised during tests; reference the beforeEach setup and the logger.debug
symbol when making the change.

532-588: Add a slash-normalized module-graph case here.

This only exercises join()/native-separator inputs, while code/builders/builder-vite/src/index.test.ts models module-graph entries with slash paths. A C:/repo/... case would pin down that integration boundary and catch separator mismatches earlier.

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

In `@code/core/src/core-server/change-detection/service.test.ts` around lines 532
- 588, Add a second test variant (or extend the existing 'stores changed files
as normalized repo-relative paths' test) that constructs the ModuleGraph using
forward-slash (POSIX) path strings instead of path.join/native separators and
then emits that graph to ChangeDetectionService; specifically, create a
slash-normalized moduleGraph key (e.g. build a string for the module id by
taking workingDir and replacing platform separators with '/' and appending
'/src/Button.module.css'), use that string as the Map key in moduleGraph and
ensure gitDiffProvider.getChangedFiles still returns a repo-relative
'src/Button.module.css' entry, call service.start(...) and
emit(theSlashNormalizedModuleGraph) and assert the same status output from
getStatusStoreByTypeId(CHANGE_DETECTION_STATUS_TYPE_ID). This ensures
ChangeDetectionService normalization logic (used during emit and in methods like
start/emit handling) handles slash-separated module graph entries.
code/builders/builder-vite/src/index.test.ts (2)

305-310: Assert the exported error type too.

This still passes if onModuleGraphChange() starts throwing a generic Error with the same message. Tightening it to ViteModuleGraphSubscriptionError from code/core/src/server-errors.ts would keep the public contract covered.

🤖 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 305 - 310, Update
the test to assert the specific exported error class instead of only the
message: import ViteModuleGraphSubscriptionError from
code/core/src/server-errors.ts and replace the current expect(() =>
onModuleGraphChange(vi.fn())).toThrow('...') with an assertion that the call
throws that class (e.g., expect(() =>
onModuleGraphChange(vi.fn())).toThrowError(ViteModuleGraphSubscriptionError) or
the framework's equivalent to assert instance-of), keeping the original message
check if desired.

267-280: Clear cb before the restart assertion.

The other listener tests reset the spy right after startChangeDetection(). Doing the same here keeps Line 280 about the post-bail() restart only, instead of any startup notification the helper may emit.

Patch
   await startChangeDetection();
+  cb.mockClear();
   expect(fakeViteServer.watcher.listenerCount('all')).toBe(1);
🤖 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 267 - 280, Reset
the spy so the post-restart assertion only checks restart-triggered calls: after
calling startChangeDetection() (and after the first expect on
fakeViteServer.watcher.listenerCount('all')), call cb.mockClear() so any startup
notifications are cleared before calling bail(),
start(createStartArgs(['/src/Button.tsx'])) and advancing timers; this ensures
the later fakeViteServer.watcher.emit('change', '/src/Button.tsx') and
expect(cb).not.toHaveBeenCalled() only reflect restart behavior.
🤖 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 305-310: Update the test to assert the specific exported error
class instead of only the message: import ViteModuleGraphSubscriptionError from
code/core/src/server-errors.ts and replace the current expect(() =>
onModuleGraphChange(vi.fn())).toThrow('...') with an assertion that the call
throws that class (e.g., expect(() =>
onModuleGraphChange(vi.fn())).toThrowError(ViteModuleGraphSubscriptionError) or
the framework's equivalent to assert instance-of), keeping the original message
check if desired.
- Around line 267-280: Reset the spy so the post-restart assertion only checks
restart-triggered calls: after calling startChangeDetection() (and after the
first expect on fakeViteServer.watcher.listenerCount('all')), call
cb.mockClear() so any startup notifications are cleared before calling bail(),
start(createStartArgs(['/src/Button.tsx'])) and advancing timers; this ensures
the later fakeViteServer.watcher.emit('change', '/src/Button.tsx') and
expect(cb).not.toHaveBeenCalled() only reflect restart behavior.

In `@code/core/src/core-server/change-detection/service.test.ts`:
- Around line 93-99: The tests call ChangeDetectionService.start() which
triggers logger.debug on the enabled/disabled path but the beforeEach only stubs
logger.info/warn/error; add a mock for logger.debug in the beforeEach (alongside
the existing vi.mocked(logger.info/warn/error).mockImplementation(...) calls and
before internal_resetChangeDetectionReadiness()) so the real logger.debug is not
exercised during tests; reference the beforeEach setup and the logger.debug
symbol when making the change.
- Around line 532-588: Add a second test variant (or extend the existing 'stores
changed files as normalized repo-relative paths' test) that constructs the
ModuleGraph using forward-slash (POSIX) path strings instead of path.join/native
separators and then emits that graph to ChangeDetectionService; specifically,
create a slash-normalized moduleGraph key (e.g. build a string for the module id
by taking workingDir and replacing platform separators with '/' and appending
'/src/Button.module.css'), use that string as the Map key in moduleGraph and
ensure gitDiffProvider.getChangedFiles still returns a repo-relative
'src/Button.module.css' entry, call service.start(...) and
emit(theSlashNormalizedModuleGraph) and assert the same status output from
getStatusStoreByTypeId(CHANGE_DETECTION_STATUS_TYPE_ID). This ensures
ChangeDetectionService normalization logic (used during emit and in methods like
start/emit handling) handles slash-separated module graph entries.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4c0f5b41-aacb-4f0e-968d-66bd93a0723f

📥 Commits

Reviewing files that changed from the base of the PR and between 3842bd0 and 7d187a5.

📒 Files selected for processing (7)
  • code/builders/builder-vite/src/index.test.ts
  • code/builders/builder-vite/src/index.ts
  • code/core/src/core-server/change-detection/service.test.ts
  • code/core/src/core-server/change-detection/service.ts
  • code/core/src/core-server/dev-server.ts
  • code/core/src/manager/globals/exports.ts
  • code/core/src/types/modules/core-common.ts
✅ Files skipped from review due to trivial changes (1)
  • code/core/src/manager/globals/exports.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • code/core/src/core-server/dev-server.ts
  • code/core/src/types/modules/core-common.ts
  • code/builders/builder-vite/src/index.ts

@ghengeveld ghengeveld merged commit d748443 into next Mar 31, 2026
124 checks passed
@ghengeveld ghengeveld deleted the change-detection-service branch March 31, 2026 13:16
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]: T3 - Server-side change detection engine

3 participants