Skip to content

feat(cli): restore --watch flag for build-storybook command#34200

Closed
tysoncung wants to merge 2 commits into
storybookjs:nextfrom
tysoncung:fix/build-storybook-watch-mode
Closed

feat(cli): restore --watch flag for build-storybook command#34200
tysoncung wants to merge 2 commits into
storybookjs:nextfrom
tysoncung:fix/build-storybook-watch-mode

Conversation

@tysoncung
Copy link
Copy Markdown

@tysoncung tysoncung commented Mar 18, 2026

Summary

Restores the --watch flag for build-storybook that was removed in #16165.

Problem

The --watch option was originally added in #2762 but regressed during build pipeline refactors. Rather than fix it, #16165 removed the flag entirely. Users have been requesting this feature back (28+ reactions on #15946).

Solution

This PR re-adds the -w, --watch flag and properly wires it through to both builders:

Webpack5 Builder

  • Uses compiler.watch() instead of compiler.run() when options.watch is true
  • Handles rebuild logging and error recovery without crashing the watcher
  • Resolves after first successful build so the rest of the pipeline can complete

Vite Builder

  • Sets build.watch: {} on the vite config when options.watch is true
  • Leverages Vite's native rollup-watch/chokidar integration

Files Changed

  • code/core/src/bin/core.ts — Added -w, --watch option to build command
  • code/core/src/types/modules/core-common.ts — Added watch?: boolean to CLIOptions
  • code/core/src/core-server/build-static.ts — Watch mode logging
  • code/builders/builder-webpack5/src/index.ts — Webpack watch implementation
  • code/builders/builder-vite/src/build.ts — Vite watch implementation

Usage

build-storybook --watch
build-storybook -w

Closes #15946

Summary by CodeRabbit

  • New Features
    • Added a global build "watch" mode (-w, --watch) that automatically rebuilds on changes, logs build progress (including watch-specific messages like "Watching for changes..." and "Building preview in watch mode.."), suppresses the one-time completion message during active watching, and improves per-build logging for warnings and errors.

Re-implement the --watch / -w flag for 'storybook build' that was
previously removed due to being broken.

Changes:
- Add -w/--watch CLI option to the build command
- Add 'watch' property to CLIOptions type
- Implement webpack watch mode using compiler.watch() instead of
  compiler.run() when --watch is passed
- Implement vite watch mode by setting build.watch option
- Handle rebuild logging and error recovery in watch mode
- Prevent premature process exit and 'build completed' message in
  watch mode

The watch flag was originally added via storybookjs#2762 but regressed at some
point when the build pipeline was refactored. The CLI option was
later removed entirely in storybookjs#16165 since it wasn't functional.

This restores the feature by properly wiring the watch option through
to both the webpack5 and vite builders.

Fixes storybookjs#15946
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 18, 2026

📝 Walkthrough

Walkthrough

Adds a watch mode for Storybook builds: a new CLI flag (--watch) is introduced and propagated into core build logic, enabling watch-aware behavior in both Vite and Webpack builders with distinct watch vs single-run control flows and logging.

Changes

Cohort / File(s) Summary
CLI Option & Type
code/core/src/bin/core.ts, code/core/src/types/modules/core-common.ts
Adds -w, --watch flag to the build command and watch?: boolean to CLIOptions; suppresses the success outro when options.watch is true.
Core build logging
code/core/src/core-server/build-static.ts
Adjusts build messaging to log "Building preview in watch mode.." when options.watch is true; no control-flow changes.
Vite builder
code/builders/builder-vite/src/build.ts
When options.watch is truthy, injects an empty build.watch object into the final Vite config before invoking the Vite build.
Webpack builder
code/builders/builder-webpack5/src/index.ts
Adds a watch-aware execution path using compiler.watch() with first-build guards, per-rebuild logging and error handling; retains a single-run compiler.run() path with added stats validation and enhanced logging and error reporting.

Sequence Diagram(s)

sequenceDiagram
    participant CLI
    participant Core
    participant BuilderSelector
    participant ViteBuilder
    participant WebpackBuilder
    participant Compiler

    CLI->>Core: run build (--watch?)
    Core->>BuilderSelector: select builder (vite|webpack) with options.watch
    alt Vite chosen
        BuilderSelector->>ViteBuilder: invoke build(finalConfig with build.watch if watch)
        ViteBuilder->>ViteBuilder: start Vite build (watch enabled or not)
        ViteBuilder-->>Core: build completed / watching
    else Webpack chosen
        BuilderSelector->>WebpackBuilder: start build(options.watch)
        alt watch=true
            WebpackBuilder->>Compiler: compiler.watch(...)
            Compiler-->>WebpackBuilder: initial build result / errors
            WebpackBuilder->>Core: log first build result (error => fail on first build)
            loop on changes
                Compiler-->>WebpackBuilder: incremental rebuild event (stats/errors)
                WebpackBuilder->>Core: log rebuild result (warnings/errors), continue watching
            end
        else
            WebpackBuilder->>Compiler: compiler.run()
            Compiler-->>WebpackBuilder: stats or error
            WebpackBuilder->>Core: log result and close compiler
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

You can customize the tone of the review comments and chat replies.

Configure the tone_instructions setting to customize the tone of the review comments and chat replies. For example, you can set the tone to Act like a strict teacher, Act like a pirate and more.

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/builders/builder-webpack5/src/index.ts (1)

311-314: Rebuild duration is currently cumulative, not per rebuild

At Line 311, printDuration(startTime) measures time from the initial build start, so later rebuild logs overstate rebuild duration. Consider tracking a per-rebuild start timestamp.

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

In `@code/builders/builder-webpack5/src/index.ts` around lines 311 - 314, The
rebuild log uses printDuration(startTime) which measures from the original build
start and thus reports cumulative time; change the code to record a fresh
per-rebuild start timestamp inside the rebuild handler (e.g., create a new
variable like rebuildStart or perRebuildStart at the top of the watcher/rebuild
callback) and pass that timestamp to printDuration instead of the existing
startTime; update any related references in the watcher/rebuild closure (where
logger.info(`Rebuild completed in ${printDuration(startTime)}`) is called) so
each rebuild logs its own duration.
🤖 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/build.ts`:
- Around line 94-99: The current watch enablement in build.ts overwrites any
existing finalConfig.build.watch from viteFinal by setting watch: {}, so
preserve and merge existing settings instead: when options.watch is true, set
finalConfig.build.watch to the existing object (finalConfig.build.watch) or
merge into it rather than replacing it, e.g. assign/merge into
finalConfig.build.watch so user/framework watch options are retained; update the
block that references finalConfig, options.watch and build.watch in build.ts
accordingly.

In `@code/builders/builder-webpack5/src/index.ts`:
- Around line 322-324: Inside the compiler.run() callback where the code
currently does "if (!stats) { throw new WebpackMissingStatsError(); }", replace
the thrown exception with a call to the existing fail() helper (e.g., fail(new
WebpackMissingStatsError())) so the promise is rejected instead of throwing an
uncaught exception; ensure you call return after fail() or otherwise stop
further processing in that callback (mirror the pattern used in the watch path
and other non-watch error branches where fail(...) is used).

In `@code/core/src/core-server/build-static.ts`:
- Around line 175-177: The log incorrectly reports "Building preview..." when
options.watch is true even if preview is skipped; update the conditional around
logger.info (in build-static.ts where options.watch and options.ignorePreview
are checked) to first check options.ignorePreview (or require
!options.ignorePreview) before logging, e.g. only call logger.info('Building
preview in watch mode..') when options.watch && !options.ignorePreview so the
message is suppressed when preview is disabled.

---

Nitpick comments:
In `@code/builders/builder-webpack5/src/index.ts`:
- Around line 311-314: The rebuild log uses printDuration(startTime) which
measures from the original build start and thus reports cumulative time; change
the code to record a fresh per-rebuild start timestamp inside the rebuild
handler (e.g., create a new variable like rebuildStart or perRebuildStart at the
top of the watcher/rebuild callback) and pass that timestamp to printDuration
instead of the existing startTime; update any related references in the
watcher/rebuild closure (where logger.info(`Rebuild completed in
${printDuration(startTime)}`) is called) so each rebuild logs its own duration.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c389ba71-73ae-47c2-a969-fd895ae1be6e

📥 Commits

Reviewing files that changed from the base of the PR and between 06cb6a6 and b19b3a4.

📒 Files selected for processing (5)
  • code/builders/builder-vite/src/build.ts
  • code/builders/builder-webpack5/src/index.ts
  • code/core/src/bin/core.ts
  • code/core/src/core-server/build-static.ts
  • code/core/src/types/modules/core-common.ts

Comment thread code/builders/builder-vite/src/build.ts
Comment thread code/builders/builder-webpack5/src/index.ts
Comment thread code/core/src/core-server/build-static.ts Outdated
- Preserve existing build.watch config in Vite builder
- Use fail() instead of throw in compiler.run() callback for proper promise rejection
- Fix conditional order to check ignorePreview before watch in build-static.ts
- Track per-rebuild timestamp for accurate rebuild duration logging
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 (1)
code/builders/builder-webpack5/src/index.ts (1)

271-317: Consider storing the Watching instance for graceful shutdown.

The compiler.watch() call returns a Watching instance that isn't being stored. While process termination (Ctrl+C) will clean up resources, storing this reference would enable graceful shutdown via the bail function — similar to how compilation is tracked in the starter function.

This is acceptable for the current CLI-driven use case but worth noting for future programmatic usage.

♻️ Potential enhancement for watcher cleanup
+let watching: ReturnType<typeof compiler.watch> | undefined;
+
 if (options.watch) {
   let isFirstBuild = true;
   let rebuildStartTime = startTime;
   logger.info('Watching for changes...');
-  compiler.watch(config.watchOptions || {}, (error, stats) => {
+  watching = compiler.watch(config.watchOptions || {}, (error, stats) => {

Then update the bail function to close watching when present. This could be addressed in a follow-up PR if needed.

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

In `@code/builders/builder-webpack5/src/index.ts` around lines 271 - 317, The
watcher returned by compiler.watch is not stored; capture its Watching instance
(e.g., const watching = compiler.watch(...)) so you can close it on shutdown,
and modify the bail function to call watching.close() (if watching is defined)
for graceful shutdown; ensure the watching variable is accessible to the same
scope as bail (mirror how compilation is tracked in starter) and keep existing
first-build logic (isFirstBuild, succeed, fail) unchanged.
🤖 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-webpack5/src/index.ts`:
- Around line 271-317: The watcher returned by compiler.watch is not stored;
capture its Watching instance (e.g., const watching = compiler.watch(...)) so
you can close it on shutdown, and modify the bail function to call
watching.close() (if watching is defined) for graceful shutdown; ensure the
watching variable is accessible to the same scope as bail (mirror how
compilation is tracked in starter) and keep existing first-build logic
(isFirstBuild, succeed, fail) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b3736a2d-e013-4762-9296-0a3dd9055b1b

📥 Commits

Reviewing files that changed from the base of the PR and between b19b3a4 and 2d8a934.

📒 Files selected for processing (3)
  • code/builders/builder-vite/src/build.ts
  • code/builders/builder-webpack5/src/index.ts
  • code/core/src/core-server/build-static.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • code/core/src/core-server/build-static.ts
  • code/builders/builder-vite/src/build.ts

@valentinpalkovic
Copy link
Copy Markdown
Contributor

There is already a PR open which attempts to fix this: #33482
Closing.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

build-storybook watch mode not working

2 participants