Skip to content

CSFFactories: Add non-interactive mode and --glob flag#33648

Merged
kasperpeulen merged 5 commits into
nextfrom
kasper/csf-factories-non-interactive-mode
Jan 28, 2026
Merged

CSFFactories: Add non-interactive mode and --glob flag#33648
kasperpeulen merged 5 commits into
nextfrom
kasper/csf-factories-non-interactive-mode

Conversation

@kasperpeulen
Copy link
Copy Markdown
Member

@kasperpeulen kasperpeulen commented Jan 26, 2026

Closes #33639

What I did

Added support for running the csf-factories automigration non-interactively using the --yes flag, and added a --glob option for specifying custom glob patterns.

Changes:

  1. --yes flag support: When passed, the codemod skips all prompts and uses defaults
  2. --glob <pattern> flag: Allows specifying a custom glob pattern for story files
  3. useSubPathImports defaults to false: Safer default since subpath imports don't work in all setups

Behavior:

Mode Glob useSubPathImports
Interactive (default) Prompted Prompted
--glob only Custom pattern Prompted
--yes Default: **/*.{stories,story}.* false
--yes --glob "src/**" Custom pattern false
Sandbox (IN_STORYBOOK_SANDBOX) Limited sandbox glob false

Usage:

# Non-interactive with defaults
npx storybook automigrate csf-factories --yes

# Non-interactive with custom glob
npx storybook automigrate csf-factories --yes --glob "src/**/*.stories.tsx"

# Interactive with custom glob (prompts for useSubPathImports only)
npx storybook automigrate csf-factories --glob "src/**/*.stories.tsx"

Manual Testing Results

All tests performed on react-vite-default-ts sandbox.

Test 1: --yes without --glob

node cli-storybook/dist/bin/index.js automigrate csf-factories --yes

Result: ✅ PASS

  • No prompts shown
  • Used default glob **/*.{stories,story}.*
  • Transformed 83 files across the project
  • Used relative imports (useSubPathImports=false)

Test 2: --yes --glob with pattern

node cli-storybook/dist/bin/index.js automigrate csf-factories --yes --glob "src/stories/*.stories.ts"

Result: ✅ PASS

  • No prompts shown
  • Only matched files in src/stories/ directory
  • Transformed 2 files (Button, Header), skipped 1 (Page already CSF4)
  • Used relative imports

Test 3: --glob with single file

node cli-storybook/dist/bin/index.js automigrate csf-factories --yes --glob "src/stories/Button.stories.ts"

Result: ✅ PASS

  • Only Button.stories.ts was transformed
  • Other story files untouched

Test 4: Sandbox mode (IN_STORYBOOK_SANDBOX=true)

IN_STORYBOOK_SANDBOX=true node cli-storybook/dist/bin/index.js automigrate csf-factories

Result: ✅ PASS

  • No prompts shown (sandbox is always non-interactive)
  • Used limited sandbox glob {stories,src}/**/{Button,Header,Page,...}.stories.*
  • Only transformed Button, Header (2 files), skipped Page (already CSF4)

Test 5: Transformation correctness

Result: ✅ PASS

  • CSF3 stories correctly transformed to CSF4 factory pattern
  • export default metapreview.meta({...})
  • export const Story: Story = {...}export const Story = meta.story({...})
  • Relative imports used: import preview from "../../.storybook/preview"

Test 6: Interactive mode (without --yes) - backwards compatibility

node cli-storybook/dist/bin/index.js automigrate csf-factories

Result: ✅ PASS

  • Shows prompt for useSubPathImports selection (Relative vs Subpath imports)
  • Shows prompt for glob pattern
  • Old interactive behavior preserved

Test 7: --glob without --yes (partial interactive)

node cli-storybook/dist/bin/index.js automigrate csf-factories --glob "src/stories/Button.stories.ts"

Result: ✅ PASS

  • Shows prompt for useSubPathImports selection
  • Skips glob prompt (uses provided --glob value)
  • Allows customizing glob while still being prompted for import style

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

See "Manual Testing Results" section above - 7 test scenarios verified.

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.

Summary by CodeRabbit

Release Notes

New Features

  • Added --glob flag to automigrate command, enabling custom glob patterns for more granular story file selection
  • Introduced non-interactive mode allowing automigrations to execute without prompts or interactive user confirmations
  • Improved csf-factories codemod to support both automated execution modes and custom file pattern targeting for greater flexibility

✏️ Tip: You can customize this high-level summary in your review settings.

The csf-factories automigration now respects the `--yes` flag to skip
interactive prompts. When `--yes` is passed:

- Uses default glob: `**/*.{stories,story}.{js,jsx,ts,tsx,mjs,mjsx,mts,mtsx}`
- Uses default import type: subpath imports

This enables running the automigration in CI or scripts without user input.

Example:
```bash
npx storybook automigrate csf-factories --yes
```

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Jan 26, 2026

View your CI Pipeline Execution ↗ for commit 4d4306a

Command Status Duration Result
nx run-many -t compile,check,knip,test,pretty-d... ✅ Succeeded 9m 26s View ↗

☁️ Nx Cloud last updated this comment at 2026-01-27 09:03:45 UTC

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 26, 2026

📝 Walkthrough

Walkthrough

Threads new optional flags yes and glob through automigrate, multi-project runner, and the CSF-factories codemod so fixes can run non-interactively and receive a file-glob pattern when provided.

Changes

Cohort / File(s) Summary
Automigrate types
code/lib/cli-storybook/src/automigrate/types.ts
Added yes?: boolean and glob?: string to RunOptions<ResultType> and AutofixOptionsFromCLI.
Automigrate flow
code/lib/cli-storybook/src/automigrate/index.ts, code/lib/cli-storybook/src/automigrate/multi-project.ts
Threaded yes and glob from top-level options into runOptions and forwarded them to commandFix.run / fix.run.
CSF-factories codemod
code/lib/cli-storybook/src/codemod/csf-factories.ts
Extended runStoriesCodemod and csfFactories.run signatures to accept yes and glob; compute non-interactive behavior (yes or sandbox) and bypass prompts when appropriate; pass glob into codemod.
CLI
code/lib/cli-storybook/src/bin/run.ts
Added --glob <pattern> CLI option to automigrate command.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant CLI
participant Automigrate
participant FixRunner
participant Codemod
participant Prompt
CLI->>Automigrate: start automigrate (options: maybe yes, maybe glob)
Automigrate->>FixRunner: run fixes (forward yes, glob)
alt yes provided
FixRunner->>Codemod: execute codemod (non-interactive, receives glob)
Codemod-->>FixRunner: results
else yes not provided
FixRunner->>Prompt: request inputs (glob, subpath prompt)
Prompt-->>FixRunner: user responses
FixRunner->>Codemod: execute codemod (with responses)
Codemod-->>FixRunner: results
end
FixRunner-->>Automigrate: aggregated results
Automigrate-->>CLI: report results

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
code/lib/cli-storybook/src/codemod/csf-factories.ts (1)

17-55: Avoid infinite retry when --yes and no files match.
In non-interactive mode, runStoriesCodemod retries on "No files matched" with the same glob, causing infinite recursion and likely CI hangs/stack overflow. Bail out (or surface a clear error) when isNonInteractive is true.

🛠️ Proposed fix
 async function runStoriesCodemod(options: {
   dryRun: boolean | undefined;
   packageManager: JsPackageManager;
   useSubPathImports: boolean;
   previewConfigPath: string;
   yes: boolean | undefined;
 }) {
-  const { dryRun, packageManager, yes, ...codemodOptions } = options;
-  try {
-    const isNonInteractive = yes || optionalEnvToBoolean(process.env.IN_STORYBOOK_SANDBOX);
-    let globString = '**/*.{stories,story}.{js,jsx,ts,tsx,mjs,mjsx,mts,mtsx}';
+  const { dryRun, packageManager, yes, ...codemodOptions } = options;
+  const isNonInteractive = yes || optionalEnvToBoolean(process.env.IN_STORYBOOK_SANDBOX);
+  let globString = '**/*.{stories,story}.{js,jsx,ts,tsx,mjs,mjsx,mts,mtsx}';
+  try {
     if (isNonInteractive && optionalEnvToBoolean(process.env.IN_STORYBOOK_SANDBOX)) {
       // Use sandbox-specific glob only in sandbox mode
       globString = '{stories,src}/**/{Button,Header,Page,button,header,page}.stories.*';
     } else if (!isNonInteractive) {
       logger.log('Please enter the glob for your stories to migrate');
       globString = await prompt.text({
         message: 'glob',
         initialValue: '**/*.{stories,story}.{js,jsx,ts,tsx,mjs,mjsx,mts,mtsx}',
       });
     }
@@
-  } catch (err: any) {
-    if (err.message === 'No files matched') {
-      await runStoriesCodemod(options);
-    } else {
-      throw err;
-    }
-  }
+  } catch (err: any) {
+    if (err.message === 'No files matched') {
+      if (isNonInteractive) {
+        throw err;
+      }
+      return runStoriesCodemod(options);
+    }
+    throw err;
+  }
 }

@storybook-app-bot
Copy link
Copy Markdown

storybook-app-bot Bot commented Jan 26, 2026

Package Benchmarks

Commit: 4d4306a, ran on 27 January 2026 at 09:01:33 UTC

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

@storybook/addon-a11y

Before After Difference
Dependency count 0 2 🚨 +2 🚨
Self size 0 B 184 KB 🚨 +184 KB 🚨
Dependency size 0 B 2.98 MB 🚨 +2.98 MB 🚨
Bundle Size Analyzer Link Link

@storybook/addon-docs

Before After Difference
Dependency count 0 18 🚨 +18 🚨
Self size 0 B 1.64 MB 🚨 +1.64 MB 🚨
Dependency size 0 B 9.25 MB 🚨 +9.25 MB 🚨
Bundle Size Analyzer Link Link

@storybook/addon-links

Before After Difference
Dependency count 0 1 🚨 +1 🚨
Self size 0 B 14 KB 🚨 +14 KB 🚨
Dependency size 0 B 5 KB 🚨 +5 KB 🚨
Bundle Size Analyzer Link Link

@storybook/addon-onboarding

Before After Difference
Dependency count 0 0 0
Self size 0 B 331 KB 🚨 +331 KB 🚨
Dependency size 0 B 670 B 🚨 +670 B 🚨
Bundle Size Analyzer Link Link

storybook-addon-pseudo-states

Before After Difference
Dependency count 0 0 0
Self size 0 B 21 KB 🚨 +21 KB 🚨
Dependency size 0 B 689 B 🚨 +689 B 🚨
Bundle Size Analyzer Link Link

@storybook/addon-themes

Before After Difference
Dependency count 0 1 🚨 +1 🚨
Self size 0 B 18 KB 🚨 +18 KB 🚨
Dependency size 0 B 28 KB 🚨 +28 KB 🚨
Bundle Size Analyzer Link Link

@storybook/addon-vitest

Before After Difference
Dependency count 0 2 🚨 +2 🚨
Self size 0 B 379 KB 🚨 +379 KB 🚨
Dependency size 0 B 338 KB 🚨 +338 KB 🚨
Bundle Size Analyzer Link Link

@storybook/builder-vite

Before After Difference
Dependency count 0 11 🚨 +11 🚨
Self size 0 B 123 KB 🚨 +123 KB 🚨
Dependency size 0 B 1.30 MB 🚨 +1.30 MB 🚨
Bundle Size Analyzer Link Link

@storybook/builder-webpack5

Before After Difference
Dependency count 0 188 🚨 +188 🚨
Self size 0 B 75 KB 🚨 +75 KB 🚨
Dependency size 0 B 32.04 MB 🚨 +32.04 MB 🚨
Bundle Size Analyzer Link Link

storybook

Before After Difference
Dependency count 0 49 🚨 +49 🚨
Self size 0 B 20.39 MB 🚨 +20.39 MB 🚨
Dependency size 0 B 16.52 MB 🚨 +16.52 MB 🚨
Bundle Size Analyzer Link Link

@storybook/angular

Before After Difference
Dependency count 0 188 🚨 +188 🚨
Self size 0 B 139 KB 🚨 +139 KB 🚨
Dependency size 0 B 30.27 MB 🚨 +30.27 MB 🚨
Bundle Size Analyzer Link Link

@storybook/ember

Before After Difference
Dependency count 0 192 🚨 +192 🚨
Self size 0 B 15 KB 🚨 +15 KB 🚨
Dependency size 0 B 28.76 MB 🚨 +28.76 MB 🚨
Bundle Size Analyzer Link Link

@storybook/html-vite

Before After Difference
Dependency count 0 14 🚨 +14 🚨
Self size 0 B 22 KB 🚨 +22 KB 🚨
Dependency size 0 B 1.46 MB 🚨 +1.46 MB 🚨
Bundle Size Analyzer Link Link

@storybook/nextjs

Before After Difference
Dependency count 0 534 🚨 +534 🚨
Self size 0 B 646 KB 🚨 +646 KB 🚨
Dependency size 0 B 59.53 MB 🚨 +59.53 MB 🚨
Bundle Size Analyzer Link Link

@storybook/nextjs-vite

Before After Difference
Dependency count 0 123 🚨 +123 🚨
Self size 0 B 1.12 MB 🚨 +1.12 MB 🚨
Dependency size 0 B 22.11 MB 🚨 +22.11 MB 🚨
Bundle Size Analyzer Link Link

@storybook/preact-vite

Before After Difference
Dependency count 0 14 🚨 +14 🚨
Self size 0 B 13 KB 🚨 +13 KB 🚨
Dependency size 0 B 1.44 MB 🚨 +1.44 MB 🚨
Bundle Size Analyzer Link Link

@storybook/react-native-web-vite

Before After Difference
Dependency count 0 155 🚨 +155 🚨
Self size 0 B 30 KB 🚨 +30 KB 🚨
Dependency size 0 B 23.41 MB 🚨 +23.41 MB 🚨
Bundle Size Analyzer Link Link

@storybook/react-vite

Before After Difference
Dependency count 0 113 🚨 +113 🚨
Self size 0 B 35 KB 🚨 +35 KB 🚨
Dependency size 0 B 19.91 MB 🚨 +19.91 MB 🚨
Bundle Size Analyzer Link Link

@storybook/react-webpack5

Before After Difference
Dependency count 0 274 🚨 +274 🚨
Self size 0 B 24 KB 🚨 +24 KB 🚨
Dependency size 0 B 44.43 MB 🚨 +44.43 MB 🚨
Bundle Size Analyzer Link Link

@storybook/server-webpack5

Before After Difference
Dependency count 0 200 🚨 +200 🚨
Self size 0 B 16 KB 🚨 +16 KB 🚨
Dependency size 0 B 33.30 MB 🚨 +33.30 MB 🚨
Bundle Size Analyzer Link Link

@storybook/svelte-vite

Before After Difference
Dependency count 0 19 🚨 +19 🚨
Self size 0 B 55 KB 🚨 +55 KB 🚨
Dependency size 0 B 26.58 MB 🚨 +26.58 MB 🚨
Bundle Size Analyzer Link Link

@storybook/sveltekit

Before After Difference
Dependency count 0 20 🚨 +20 🚨
Self size 0 B 56 KB 🚨 +56 KB 🚨
Dependency size 0 B 26.63 MB 🚨 +26.63 MB 🚨
Bundle Size Analyzer Link Link

@storybook/vue3-vite

Before After Difference
Dependency count 0 108 🚨 +108 🚨
Self size 0 B 35 KB 🚨 +35 KB 🚨
Dependency size 0 B 43.67 MB 🚨 +43.67 MB 🚨
Bundle Size Analyzer Link Link

@storybook/web-components-vite

Before After Difference
Dependency count 0 15 🚨 +15 🚨
Self size 0 B 19 KB 🚨 +19 KB 🚨
Dependency size 0 B 1.50 MB 🚨 +1.50 MB 🚨
Bundle Size Analyzer Link Link

@storybook/cli

Before After Difference
Dependency count 0 183 🚨 +183 🚨
Self size 0 B 776 KB 🚨 +776 KB 🚨
Dependency size 0 B 67.55 MB 🚨 +67.55 MB 🚨
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 0 176 🚨 +176 🚨
Self size 0 B 30 KB 🚨 +30 KB 🚨
Dependency size 0 B 66.13 MB 🚨 +66.13 MB 🚨
Bundle Size Analyzer Link Link

@storybook/core-webpack

Before After Difference
Dependency count 0 1 🚨 +1 🚨
Self size 0 B 11 KB 🚨 +11 KB 🚨
Dependency size 0 B 28 KB 🚨 +28 KB 🚨
Bundle Size Analyzer Link Link

create-storybook

Before After Difference
Dependency count 0 50 🚨 +50 🚨
Self size 0 B 1000 KB 🚨 +1000 KB 🚨
Dependency size 0 B 36.91 MB 🚨 +36.91 MB 🚨
Bundle Size Analyzer node node

@storybook/csf-plugin

Before After Difference
Dependency count 0 9 🚨 +9 🚨
Self size 0 B 7 KB 🚨 +7 KB 🚨
Dependency size 0 B 1.26 MB 🚨 +1.26 MB 🚨
Bundle Size Analyzer Link Link

eslint-plugin-storybook

Before After Difference
Dependency count 0 20 🚨 +20 🚨
Self size 0 B 131 KB 🚨 +131 KB 🚨
Dependency size 0 B 2.82 MB 🚨 +2.82 MB 🚨
Bundle Size Analyzer Link Link

@storybook/react-dom-shim

Before After Difference
Dependency count 0 0 0
Self size 0 B 18 KB 🚨 +18 KB 🚨
Dependency size 0 B 788 B 🚨 +788 B 🚨
Bundle Size Analyzer Link Link

@storybook/preset-create-react-app

Before After Difference
Dependency count 0 68 🚨 +68 🚨
Self size 0 B 32 KB 🚨 +32 KB 🚨
Dependency size 0 B 5.98 MB 🚨 +5.98 MB 🚨
Bundle Size Analyzer Link Link

@storybook/preset-react-webpack

Before After Difference
Dependency count 0 170 🚨 +170 🚨
Self size 0 B 18 KB 🚨 +18 KB 🚨
Dependency size 0 B 31.29 MB 🚨 +31.29 MB 🚨
Bundle Size Analyzer Link Link

@storybook/preset-server-webpack

Before After Difference
Dependency count 0 10 🚨 +10 🚨
Self size 0 B 7 KB 🚨 +7 KB 🚨
Dependency size 0 B 1.20 MB 🚨 +1.20 MB 🚨
Bundle Size Analyzer Link Link

@storybook/html

Before After Difference
Dependency count 0 2 🚨 +2 🚨
Self size 0 B 29 KB 🚨 +29 KB 🚨
Dependency size 0 B 32 KB 🚨 +32 KB 🚨
Bundle Size Analyzer Link Link

@storybook/preact

Before After Difference
Dependency count 0 2 🚨 +2 🚨
Self size 0 B 16 KB 🚨 +16 KB 🚨
Dependency size 0 B 32 KB 🚨 +32 KB 🚨
Bundle Size Analyzer Link Link

@storybook/react

Before After Difference
Dependency count 0 57 🚨 +57 🚨
Self size 0 B 1.23 MB 🚨 +1.23 MB 🚨
Dependency size 0 B 12.95 MB 🚨 +12.95 MB 🚨
Bundle Size Analyzer Link Link

@storybook/server

Before After Difference
Dependency count 0 3 🚨 +3 🚨
Self size 0 B 8 KB 🚨 +8 KB 🚨
Dependency size 0 B 716 KB 🚨 +716 KB 🚨
Bundle Size Analyzer Link Link

@storybook/svelte

Before After Difference
Dependency count 0 2 🚨 +2 🚨
Self size 0 B 45 KB 🚨 +45 KB 🚨
Dependency size 0 B 230 KB 🚨 +230 KB 🚨
Bundle Size Analyzer Link Link

@storybook/vue3

Before After Difference
Dependency count 0 3 🚨 +3 🚨
Self size 0 B 63 KB 🚨 +63 KB 🚨
Dependency size 0 B 211 KB 🚨 +211 KB 🚨
Bundle Size Analyzer Link Link

@storybook/web-components

Before After Difference
Dependency count 0 3 🚨 +3 🚨
Self size 0 B 61 KB 🚨 +61 KB 🚨
Dependency size 0 B 47 KB 🚨 +47 KB 🚨
Bundle Size Analyzer Link Link

kasperpeulen and others added 2 commits January 26, 2026 20:48
Sandbox always uses limited glob, non-interactive mode uses default glob.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
code/lib/cli-storybook/src/codemod/csf-factories.ts (1)

50-56: Infinite recursion in non-interactive mode when no files match.

When isNonInteractive is true and no files match the glob, the recursive call at line 52 will use the same unchanged options, leading to an infinite loop. The retry logic only makes sense for interactive mode where the user can provide a different glob.

🐛 Proposed fix to break recursion in non-interactive mode
   } catch (err: any) {
     if (err.message === 'No files matched') {
-      await runStoriesCodemod(options);
+      const inSandbox = optionalEnvToBoolean(process.env.IN_STORYBOOK_SANDBOX);
+      const isNonInteractive = options.yes || inSandbox;
+      if (isNonInteractive) {
+        logger.warn('No files matched the glob pattern. Skipping story migration.');
+        return;
+      }
+      await runStoriesCodemod(options);
     } else {
       throw err;
     }
   }

- Add --glob option to automigrate command for csf-factories codemod
- Change useSubPathImports default to false (safer default)
- When glob is provided, it overrides both sandbox and default globs

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@kasperpeulen kasperpeulen changed the title CLI: Add non-interactive mode for csf-factories automigration CLI: Add non-interactive mode and --glob flag for csf-factories Jan 26, 2026
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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
code/lib/cli-storybook/src/codemod/csf-factories.ts (2)

26-55: Avoid infinite retry when no files match in non‑interactive runs

If --yes (or sandbox) is used and the glob yields no matches, the catch block re-invokes runStoriesCodemod with the same options, creating an infinite retry loop and eventual stack overflow. Retry should only happen when the user can change the glob interactively; otherwise surface the error.

🔧 Proposed fix (guard retry when non‑interactive or explicit glob)
 async function runStoriesCodemod(options: {
   dryRun: boolean | undefined;
   packageManager: JsPackageManager;
   useSubPathImports: boolean;
   previewConfigPath: string;
   yes: boolean | undefined;
   glob: string | undefined;
 }) {
   const { dryRun, packageManager, yes, glob, ...codemodOptions } = options;
-  try {
-    const inSandbox = optionalEnvToBoolean(process.env.IN_STORYBOOK_SANDBOX);
-    const isNonInteractive = yes || inSandbox;
-    let globString = glob ?? '**/*.{stories,story}.{js,jsx,ts,tsx,mjs,mjsx,mts,mtsx}';
+  const inSandbox = optionalEnvToBoolean(process.env.IN_STORYBOOK_SANDBOX);
+  const isNonInteractive = yes || inSandbox;
+  const canRetry = !isNonInteractive && !glob;
+  let globString = glob ?? '**/*.{stories,story}.{js,jsx,ts,tsx,mjs,mjsx,mts,mtsx}';
+
+  try {
     if (!glob && inSandbox) {
       // Sandbox uses limited glob for faster testing (unless glob explicitly provided)
       globString = '{stories,src}/**/{Button,Header,Page,button,header,page}.stories.*';
     } else if (!glob && !isNonInteractive) {
       logger.log('Please enter the glob for your stories to migrate');
       globString = await prompt.text({
         message: 'glob',
         initialValue: globString,
       });
     }
 
     logger.step('Applying codemod on your stories, this might take some time...');
 
     await packageManager.runPackageCommand({
       args: ['storybook', 'migrate', 'csf-2-to-3', `--glob="${globString}"`],
     });
 
     await runCodemod(globString, (info) => storyToCsfFactory(info, codemodOptions), {
       dryRun,
     });
   } catch (err: any) {
     if (err.message === 'No files matched') {
-      await runStoriesCodemod(options);
+      if (!canRetry) {
+        throw err;
+      }
+      await runStoriesCodemod(options);
     } else {
       throw err;
     }
   }
 }

73-96: --yes currently defaults to relative imports, not subpath imports

The PR objective says non‑interactive --yes should use subpath imports by default. Right now, useSubPathImports stays false when prompts are skipped, so the import map is never added. If the intent is to default to subpath imports for --yes, initialize it from yes.

💡 Proposed fix (default to subpath imports when `--yes` is used)
-    let useSubPathImports = false;
-    const isNonInteractive = yes || optionalEnvToBoolean(process.env.IN_STORYBOOK_SANDBOX);
+    const isNonInteractive = yes || optionalEnvToBoolean(process.env.IN_STORYBOOK_SANDBOX);
+    let useSubPathImports = Boolean(yes);

@kasperpeulen kasperpeulen changed the title CLI: Add non-interactive mode and --glob flag for csf-factories CSFFactories: Add non-interactive mode and --glob flag Jan 26, 2026
Change condition from `!yes || !inSandbox` to `!yes && !inSandbox` so
that either --yes flag or sandbox environment skips the prompt.
@kasperpeulen kasperpeulen merged commit e9c8a87 into next Jan 28, 2026
124 checks passed
@kasperpeulen kasperpeulen deleted the kasper/csf-factories-non-interactive-mode branch January 28, 2026 09:10
@github-actions github-actions Bot mentioned this pull request Jan 28, 2026
24 tasks
@kasperpeulen kasperpeulen added the patch:yes Bugfix & documentation PR that need to be picked to main branch label Jan 30, 2026
@github-actions github-actions Bot mentioned this pull request Feb 2, 2026
11 tasks
valentinpalkovic pushed a commit that referenced this pull request Feb 2, 2026
…nteractive-mode

CSFFactories: Add non-interactive mode and --glob flag
(cherry picked from commit e9c8a87)
@github-actions github-actions Bot mentioned this pull request Feb 2, 2026
13 tasks
@github-actions github-actions Bot added the patch:done Patch/release PRs already cherry-picked to main/release branch label Feb 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:normal feature request patch:done Patch/release PRs already cherry-picked to main/release branch patch:yes Bugfix & documentation PR that need to be picked to main branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Followup CSF factories: Codemod fails for different use cases

2 participants