Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Conversation

@yannbf
Copy link
Member

@yannbf yannbf commented Oct 15, 2025

Closes #32647, #32626

What I did

The CSF factories syncing in the add command was not working because the main config passed was actually the "old" one, where the new addon wasn't present. This PR fixes that and adds a way to bypass cache when importing the main config file as a module, otherwise the main config would always be resolving with the old values.

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

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 PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the @storybookjs/core team here.

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

Summary by CodeRabbit

  • Bug Fixes
    • Always loads a fresh main configuration (cache bypass) to prevent stale settings.
    • Improves reliability of addon synchronization to reflect the latest project config.
    • Adds more resilient module loading with better handling for mixed ESM/CommonJS setups.
    • Reduces caching-related import issues for more predictable behavior after config changes.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 15, 2025

📝 Walkthrough

Walkthrough

Adds a skipCache option to module importing and propagates it through loadMainConfig; importModule can bypass cache via a timestamped dynamic import or by clearing require.cache. loadMainConfig gains a temporary-file ESM fallback for "require is not defined" errors. CLI add now loads the main config with skipCache true before syncing addons.

Changes

Cohort / File(s) Summary
Module loading utilities
code/core/src/shared/utils/module.ts
importModule signature changed to importModule(path, { skipCache?: boolean } = {}). When skipCache is true, dynamic import appends a timestamp query to bypass cache and the CommonJS fallback clears require.cache[resolvedPath] before require.
Main config loader
code/core/src/common/utils/load-main-config.ts
loadMainConfig now accepts skipCache?: boolean and passes it to importModule(mainPath, { skipCache }). Enhanced error handling: on "require is not defined" errors it writes a temporary CommonJS-shim file, imports that file, then removes it and returns the export; otherwise throws MainFileEvaluationError.
CLI addon sync flow
code/lib/cli-storybook/src/add.ts
Replaced prior mainConfig usage by loading a fresh config via loadMainConfig({ configDir, skipCache: true }) before calling syncStorybookAddons. Removed unused path imports.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant CLI as CLI add
  participant LMC as loadMainConfig
  participant IM as importModule
  participant ESM as Dynamic Import
  participant CJS as CommonJS (require)
  participant TMP as Temp-file ESM fallback

  CLI->>LMC: loadMainConfig({ configDir, skipCache: true })
  LMC->>IM: importModule(mainPath, { skipCache: true })
  alt Dynamic import (ESM)
    IM->>ESM: import(mainPath + "?ts=now")
    ESM-->>IM: module
  else CommonJS fallback
    IM->>CJS: if skipCache delete require.cache[resolvedPath]
    IM->>CJS: require(resolvedPath)
    CJS-->>IM: module or throws
    alt "require is not defined" error in LMC
      LMC->>TMP: write temp .tmp file with CJS shim
      LMC->>IM: import(tempPath)
      IM-->>LMC: module
      LMC->>TMP: remove temp file
    end
  end
  IM-->>LMC: mainConfig module
  LMC-->>CLI: mainConfig
  CLI->>CLI: syncStorybookAddons(mainConfig)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch yann/fix-sb-add-csf-sync

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11e49d4 and 59e030b.

📒 Files selected for processing (2)
  • code/core/src/common/utils/load-main-config.ts (1 hunks)
  • code/lib/cli-storybook/src/add.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/lib/cli-storybook/src/add.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (use yarn lint:js:cmd <file>)

Files:

  • code/core/src/common/utils/load-main-config.ts
**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions from modules when they need to be unit-tested

Files:

  • code/core/src/common/utils/load-main-config.ts
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In application code, use Storybook loggers instead of console.* (client code: storybook/internal/client-logger; server code: storybook/internal/node-logger)

Files:

  • code/core/src/common/utils/load-main-config.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/common/utils/load-main-config.ts
🧬 Code graph analysis (1)
code/core/src/common/utils/load-main-config.ts (1)
code/core/src/shared/utils/module.ts (1)
  • importModule (54-89)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (2)
code/core/src/common/utils/load-main-config.ts (2)

17-17: LGTM! Clean addition of cache-busting capability.

The optional skipCache parameter is well-designed and maintains backward compatibility. This enables callers (like the CLI add command) to force a fresh import of the main config, solving the stale config issue described in the PR.

Also applies to: 21-21


28-28: Correct propagation to importModule.

The skipCache parameter is properly passed to importModule, which will append a timestamp to the import URL and clear require.cache in the fallback path when enabled. This ensures a fresh config is loaded for addon synchronization.


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

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 (2)
code/core/src/shared/utils/module.ts (1)

54-57: Update JSDoc to document the new parameter.

The function signature now accepts a skipCache option, but the JSDoc comments above (lines 37-53) don't mention this parameter. Please update the documentation to explain when and why to use skipCache.

Example addition to the JSDoc:

 /**
  * Dynamically imports a module with TypeScript support, falling back to require if necessary.
  *
+ * @param path - The module path to import
+ * @param options - Import options
+ * @param options.skipCache - When true, bypasses module cache to ensure a fresh import
+ *
  * @example Import a TypeScript preset
code/core/src/common/utils/load-main-config.ts (1)

56-56: Consider using skipCache: true for consistency.

While the temporary file at modifiedMainPath is unlikely to be cached, using skipCache: true here would maintain consistency with line 26 and prevent any theoretical caching issues.

Apply this diff:

-        out = await importModule(modifiedMainPath);
+        out = await importModule(modifiedMainPath, { skipCache: true });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee0c7a3 and 11e49d4.

📒 Files selected for processing (3)
  • code/core/src/common/utils/load-main-config.ts (1 hunks)
  • code/core/src/shared/utils/module.ts (2 hunks)
  • code/lib/cli-storybook/src/add.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (use yarn lint:js:cmd <file>)

Files:

  • code/core/src/common/utils/load-main-config.ts
  • code/core/src/shared/utils/module.ts
  • code/lib/cli-storybook/src/add.ts
**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions from modules when they need to be unit-tested

Files:

  • code/core/src/common/utils/load-main-config.ts
  • code/core/src/shared/utils/module.ts
  • code/lib/cli-storybook/src/add.ts
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In application code, use Storybook loggers instead of console.* (client code: storybook/internal/client-logger; server code: storybook/internal/node-logger)

Files:

  • code/core/src/common/utils/load-main-config.ts
  • code/core/src/shared/utils/module.ts
  • code/lib/cli-storybook/src/add.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/common/utils/load-main-config.ts
  • code/core/src/shared/utils/module.ts
  • code/lib/cli-storybook/src/add.ts
🧬 Code graph analysis (2)
code/core/src/common/utils/load-main-config.ts (1)
code/core/src/shared/utils/module.ts (1)
  • importModule (54-89)
code/lib/cli-storybook/src/add.ts (2)
code/core/src/common/utils/load-main-config.ts (1)
  • loadMainConfig (14-69)
code/core/src/common/utils/sync-main-preview-addons.ts (1)
  • syncStorybookAddons (16-25)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Danger JS
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (3)
code/core/src/shared/utils/module.ts (1)

67-69: LGTM! Cache-busting implementation is correct.

The dual approach for bypassing cache is well-designed:

  • ESM path: Query parameter with timestamp prevents module cache hits
  • CJS fallback: Explicit cache deletion before requiring

Both mechanisms correctly target their respective module systems.

Also applies to: 75-77

code/core/src/common/utils/load-main-config.ts (1)

26-26: LGTM! Core fix correctly implemented.

Passing skipCache: true ensures the main config is always loaded fresh, which is essential for the addon synchronization fix described in the PR objectives.

code/lib/cli-storybook/src/add.ts (1)

183-184: LGTM! Critical fix correctly implemented.

This change directly addresses the PR objective by loading a fresh main config after the addon has been written to main.js (line 178). The previous implementation passed the stale mainConfig from line 92, which was loaded before the addon was added, causing the sync logic to miss the newly added addon.

The try/catch wrapper with the TODO comment appropriately handles potential issues while CSF factories is still stabilizing.

@nx-cloud
Copy link

nx-cloud bot commented Oct 15, 2025

View your CI Pipeline Execution ↗ for commit 59e030b

Command Status Duration Result
nx run-many -t build --parallel=3 ✅ Succeeded 49s View ↗

☁️ Nx Cloud last updated this comment at 2025-10-15 10:24:43 UTC

@vanessayuenn vanessayuenn linked an issue Oct 15, 2025 that may be closed by this pull request
// TODO: remove try/catch once CSF factories is shipped, for now gracefully handle any error
try {
await syncStorybookAddons(mainConfig, previewConfigPath!, configDir);
const newMainConfig = await loadMainConfig({ configDir, skipCache: true });
Copy link
Member

Choose a reason for hiding this comment

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

So this is where I was worried about: that loadMainConfig would have a module-cache in NodeJS.

Comment on lines 54 to 79
export async function importModule(
path: string,
{ skipCache = false }: { skipCache?: boolean } = {}
) {
if (!isTypescriptLoaderRegistered) {
const typescriptLoaderUrl = importMetaResolve('storybook/internal/bin/loader');
register(typescriptLoaderUrl, import.meta.url);
isTypescriptLoaderRegistered = true;
}

let mod;
try {
const resolvedPath = win32.isAbsolute(path) ? pathToFileURL(path).href : path;
mod = await import(resolvedPath);
// When applicable, add a hash to the import URL to bypass cache
const importUrl = skipCache ? `${resolvedPath}?${Date.now()}` : resolvedPath;
mod = await import(importUrl);
} catch (importError) {
try {
// fallback to require to support older behavior
// this is relevant for presets that are only available with the "require" condition in a package's export map
const require = createRequire(import.meta.url);
if (skipCache) {
delete require.cache[require.resolve(path)];
}
mod = require(path);
} catch (requireError) {
Copy link
Member

Choose a reason for hiding this comment

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

okay, I assume you've tested this, ✅

Copy link
Member Author

Choose a reason for hiding this comment

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

For sure, all cases are tested

@yannbf yannbf merged commit c8bbfad into next Oct 15, 2025
58 checks passed
@yannbf yannbf deleted the yann/fix-sb-add-csf-sync branch October 15, 2025 11:39
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.

[Bug]: Syncing addons with CSF factories does not work in storybook add [Bug]: SB10 CSF Next - migration not updating addons

3 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.