Skip to content

Conversation

@JReinhold
Copy link
Contributor

@JReinhold JReinhold commented Feb 21, 2025

Supersedes #26010

Todo

  • Go through all usages/references of importPath in StoryIndexGenerator and ensure their behavior still work. importPath is expected to be a file path, but can now be a virtual module, and I'm not sure yet if it's supported in all cases there.
  • Unit tests
  • Experiment with moving initializedStoryIndexGenerator to a preset instead if possible
  • Test on Windows
  • Ensure Vitest works
    • Also with virtual files
  • Update docs

What I did

This PR does a few things, all based on making the Vite builder depend on the story index for a list of stories, instead of re-globbing the files manually like the StoryIndexGenerator already does.

Untying the Vite builder from the file system means it no longer has to do duplicate file watching to make HMR work, instead StoryIndexGenerator will directly notify the Vite builder when the virtual modules are invalidated.

This enables the feature of custom indexers specifying an alternative path as the importPath for an index, as those are now the single source of truth. The paths can even be virtual modules, or paths with query param suffixes. However virtual modules will not work with the Vitest integration as Vitest doesn't support that.

An important addition to this PR, is that the StoryIndexGenerator is now available as a preset property for anyone to access on the server side. We've been needing this for quite a few features now, and we finally don't have to work around it anymore.

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

In a react-vite sandbox, modify .storybook/main.ts stories and addons to:

  "stories": [
    "../src/**/*.mdx",
    "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)", 
    "../src/**/*.stories-emoji.@(js|jsx|mjs|ts|tsx)", 
    "../src/**/*.stories.json", 
],

  "addons": [
    "@storybook/addon-essentials",
    "@storybook/addon-onboarding",
    "@chromatic-com/storybook",
    "@storybook/experimental-addon-test",
    "@storybook/addon-a11y",
    "./custom-indexers.ts"
  ],

Add the following .storybook/custom-indexers.ts file:

import { readFile } from 'node:fs/promises';
import type { Indexer, PresetProperty, StoryIndexInput } from 'storybook/internal/types';
import type { ViteFinal } from '../../../code/builders/builder-vite/src';
import type { Plugin } from 'vite';
import { readCsf } from 'storybook/internal/csf-tools';

const jsonIndexer: Indexer = {
  test: /stories\.json$/,
  createIndex: async (filename) => {
    const content = JSON.parse(await readFile(filename, 'utf-8')) as { stories: {title: string, name: string}[] };
    const stories: StoryIndexInput[] = content.stories.map((story) => ({
      type: 'story',
      title: story.title,
      name: story.name,
      importPath: `virtual:json-indexer@${filename}.js?title=${story.title}`,
      exportName: story.name
    }));
    return stories;
  },
}

const jsonVitePlugin: Plugin = {
  name: 'json-stories',
  resolveId(id) {
    if (id.startsWith('virtual:json-indexer@')) {
      return `\0${id}`;
    }
  },
  async load(id) {
    if (!id.startsWith('\0virtual:json-indexer@')) {
      return;
    }
    const url = new URL(id.replace('\\0', ''));
    const filePath = url.pathname.replace('json-indexer@', '').replace(/\.js$/, '');
    const title = url.searchParams.get('title');

    const content = JSON.parse(await readFile(filePath, 'utf-8')) as { stories: {title: string, name: string}[] };

    const stories = content.stories.filter((story) => story.title === title);
    const result = `
export default {
title: '${title}',
};

${stories.map((story) => `export const ${story.name} = { render: () => '${story.name}' };`).join('\n')}
`;
    return result;
  },
}

const emojiTransformIndexer: Indexer = {
  test: /stories-emoji\.(m?js|ts)x?$/,
  createIndex: async (fileName, options) => {
    const indexInputs = (await readCsf(fileName, options)).parse().indexInputs;
    return indexInputs.map((input) => ({
      ...input,
      title: `🎉 ${input.title}`,
      name: `😎 ${input.name}`,
      __id: undefined,
      importPath: `${input.importPath}?emoji=true`,
    }));
  },
}

const emojiVitePlugin: Plugin = {
  name: 'emoji-stories',
  async transform(code, id) {
    if (!id.endsWith('emoji=true')) {
      return;
    }
    const transformed = code
      .replaceAll(/(title:\s*['"])/g, '$1🎉 ')
      .replaceAll(/(name:\s*['"])/g, '$1😎 ');

    return transformed;
  }
}

export const experimental_indexers: PresetProperty<'experimental_indexers'> = (existingIndexers) => {
  return [jsonIndexer, emojiTransformIndexer].concat(existingIndexers || []);
};

export const viteFinal: ViteFinal = (config) => {
  return {
    ...config,
    plugins: [
      ...(config.plugins ?? []),
      jsonVitePlugin,
      emojiVitePlugin,
    ]
  };
};

Add the following file to src/stories/my.stories.json:

{
  "stories": [
    {
      "title": "Bit",
      "name": "First"
    },
    {
      "title": "Bit",
      "name": "Second"
    },
    {
      "title": "Bit",
      "name": "Third"
    },
    {
      "title": "Bot",
      "name": "First"
    },
    {
      "title": "Bot",
      "name": "Second"
    },
    {
      "title": "Bot",
      "name": "Third"
    }
  ]
}

And this file too: src/stories.Flimflom.stories-emoji.ts:

import { fn } from 'storybook/test';

import { Button } from './Button';

const meta = {
  component: Button,
  title: 'Flimflom',
  tags: ['autodocs'],
  args: { onClick: fn() },
};

export default meta;

export const Primary = {
  name: 'Primary',
  args: {
    primary: true,
    label: 'Button',
  },
};

export const Secondary = {
  name: 'Secondary',
  args: {
    label: 'Button',
  },
};

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 canary-release-pr.yml --field pr=<PR_NUMBER>

Summary by CodeRabbit

  • Refactor

    • Story indexing unified behind a shared generator and consolidated virtual-file handling for more consistent builds.
  • Performance

    • Debounced file-watching, batched invalidation, and optimized import-path handling for faster, more reliable dev-server updates.
  • Public API / Config

    • Exposed a configurable story-index generator and made index input importPath optional with clarified defaults.
  • Documentation

    • Clarified importPath defaults and Vite vs Webpack guidance for indexer inputs.
  • Tooling

    • Added a VS Code workspace and minor lint/config cleanup.

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

@nx-cloud
Copy link

nx-cloud bot commented Feb 21, 2025

View your CI Pipeline Execution ↗ for commit 43774a7

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

☁️ Nx Cloud last updated this comment at 2025-12-18 20:50:13 UTC

@JReinhold JReinhold changed the title Vite: Support custom importPath in inexers Vite: Support custom importPath in indexers Feb 24, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 29, 2025

📝 Walkthrough

Walkthrough

Introduces a preset-backed, promise-based StoryIndexGenerator singleton with onInvalidated listeners; rewires builder-vite to consume StoryIndex objects and deduplicated import paths; unifies virtual-module loading; batches file events and simplifies invalidate(path, removed); makes importPath optional in index inputs.

Changes

Cohort / File(s) Summary
ESLint & Tooling
code/.eslintrc.js, storybook.code-workspace
Removed a restricted-import rule entry for vite; added a VS Code workspace file with editor/TypeScript and vitest settings.
Builder-Vite — Import generation & tests
code/builders/builder-vite/src/codegen-importfn-script.ts, code/builders/builder-vite/src/codegen-importfn-script.test.ts, code/builders/builder-vite/src/list-stories.ts, code/builders/builder-vite/src/utils/unique-import-paths.ts
Replaced stories-array API with generateImportFnScriptCode(index: StoryIndex): string; removed listStories; added getUniqueImportPaths(); updated path normalization, virtual import handling, and tests to accept StoryIndex shape.
Builder-Vite — Optimize/plugin/virtual IDs
code/builders/builder-vite/src/optimizeDeps.ts, code/builders/builder-vite/src/plugins/code-generator-plugin.ts, code/builders/builder-vite/src/virtual-file-names.ts, code/builders/builder-vite/src/vite-config.test.ts
Switched optimizeDeps to use StoryIndex-derived entries; plugin now awaits a StoryIndexGenerator promise, subscribes to onInvalidated, consolidates virtual-module resolve/load, removed VIRTUAL_PREVIEW_FILE; removed an ESLint comment.
Core Server — Build & Dev wiring
code/core/src/core-server/build-static.ts, code/core/src/core-server/dev-server.ts
Replaced local story discovery with presets-provided StoryIndexGenerator promise; dev/build flows await the generator and use it for index/manifest generation; index JSON route/registering updated.
Presets & StoryIndexGenerator
code/core/src/core-server/presets/common-preset.ts, code/core/src/core-server/utils/StoryIndexGenerator.ts, code/core/src/core-server/utils/StoryIndexGenerator.test.ts
Added storyIndexGenerator preset returning a cached singleton promise; StoryIndexGenerator gained onInvalidated() listener API and its invalidate() signature changed to (path, removed); tests updated accordingly.
Index JSON & helpers / telemetry
code/core/src/core-server/utils/index-json.ts, code/core/src/core-server/utils/index-json.test.ts, code/core/src/core-server/utils/getStoryIndexGenerator.ts, code/core/src/core-server/utils/doTelemetry.ts
Renamed useStoriesJsonregisterIndexJsonRoute and extractStoriesJsonwriteIndexJson; functions now accept storyIndexGeneratorPromise and await it; removed getStoryIndexGenerator module; telemetry now awaits the generator promise.
Watch & invalidation batching
code/core/src/core-server/utils/watch-story-specifiers.ts, code/core/src/core-server/utils/watch-story-specifiers.test.ts
onInvalidate callback now (path, removed); introduced 100ms debounce and event batching for Watchpack events; tests adapted to fake timers and batched assertions.
CSF indexing & tests
code/core/src/csf-tools/CsfFile.ts, code/core/src/csf-tools/CsfFile.test.ts
Removed importPath from CsfFile.indexInputs entries and updated snapshots/tests to the new shape.
Types & docs
code/core/src/types/modules/core-common.ts, code/core/src/types/modules/indexer.ts, docs/api/main-config/main-config-indexers.mdx
Added optional storyIndexGenerator?: StoryIndexGenerator to StorybookConfigRaw; made importPath?: Path optional in BaseIndexInput (default documented as fileName) and added a Vite-specific usage note.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Preset as Preset System
    participant Gen as StoryIndexGenerator (promise)
    participant Dev as Core Dev Server
    participant Builder as Builder-Vite Plugin
    participant Vite as Vite Dev Server

    Dev->>Preset: request storyIndexGenerator preset
    Preset-->>Dev: storyIndexGeneratorPromise
    Dev->>Gen: await storyIndexGeneratorPromise.getIndex()
    Gen-->>Dev: StoryIndex

    Vite->>Builder: import virtual STORIES_FILE
    Builder->>Gen: await storyIndexGeneratorPromise.getIndex()
    Gen-->>Builder: StoryIndex
    Builder->>Builder: generate importFn script from StoryIndex
    Builder-->>Vite: serve virtual module content

    Note over Gen,Builder: On file changes (batched):
    Gen->>Gen: invalidate(path, removed)
    Gen-->>Builder: notify via onInvalidated()
    Builder->>Vite: invalidate virtual module / HMR
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas requiring extra attention:
    • singleton promise caching and lifecycle in common-preset.ts
    • onInvalidated registration/unsubscribe and usage in code-generator-plugin.ts
    • batching/debounce correctness and edge cases in watch-story-specifiers.ts
    • mapping from invalidate(importPath) to specifier caches in StoryIndexGenerator.ts
    • path normalization, virtual import handling, and Windows/POSIX consistency in codegen-importfn-script.ts

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings

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

@storybook-app-bot
Copy link

storybook-app-bot bot commented Oct 1, 2025

Package Benchmarks

Commit: 43774a7, ran on 18 December 2025 at 20:54:14 UTC

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

@storybook/builder-vite

Before After Difference
Dependency count 17 17 0
Self size 325 KB 125 KB 🎉 -200 KB 🎉
Dependency size 2.00 MB 2.00 MB 🎉 -12 B 🎉
Bundle Size Analyzer Link Link

@storybook/angular

Before After Difference
Dependency count 191 192 🚨 +1 🚨
Self size 118 KB 118 KB 0 B
Dependency size 30.15 MB 30.39 MB 🚨 +241 KB 🚨
Bundle Size Analyzer Link Link

@storybook/html-vite

Before After Difference
Dependency count 20 20 0
Self size 22 KB 22 KB 0 B
Dependency size 2.36 MB 2.16 MB 🎉 -200 KB 🎉
Bundle Size Analyzer Link Link

@storybook/nextjs-vite

Before After Difference
Dependency count 127 127 0
Self size 1.12 MB 1.12 MB 0 B
Dependency size 21.97 MB 21.77 MB 🎉 -200 KB 🎉
Bundle Size Analyzer Link Link

@storybook/preact-vite

Before After Difference
Dependency count 20 20 0
Self size 13 KB 13 KB 0 B
Dependency size 2.35 MB 2.15 MB 🎉 -200 KB 🎉
Bundle Size Analyzer Link Link

@storybook/react-native-web-vite

Before After Difference
Dependency count 159 159 0
Self size 30 KB 30 KB 🚨 +18 B 🚨
Dependency size 23.15 MB 22.95 MB 🎉 -200 KB 🎉
Bundle Size Analyzer Link Link

@storybook/react-vite

Before After Difference
Dependency count 117 117 0
Self size 35 KB 35 KB 🎉 -18 B 🎉
Dependency size 19.77 MB 19.57 MB 🎉 -200 KB 🎉
Bundle Size Analyzer Link Link

@storybook/svelte-vite

Before After Difference
Dependency count 24 24 0
Self size 55 KB 55 KB 0 B
Dependency size 27.02 MB 26.82 MB 🎉 -200 KB 🎉
Bundle Size Analyzer Link Link

@storybook/sveltekit

Before After Difference
Dependency count 25 25 0
Self size 56 KB 56 KB 🎉 -24 B 🎉
Dependency size 27.08 MB 26.88 MB 🎉 -200 KB 🎉
Bundle Size Analyzer Link Link

@storybook/vue3-vite

Before After Difference
Dependency count 114 114 0
Self size 35 KB 35 KB 0 B
Dependency size 44.18 MB 43.98 MB 🎉 -200 KB 🎉
Bundle Size Analyzer Link Link

@storybook/web-components-vite

Before After Difference
Dependency count 21 21 0
Self size 19 KB 19 KB 🚨 +12 B 🚨
Dependency size 2.39 MB 2.19 MB 🎉 -200 KB 🎉
Bundle Size Analyzer Link Link

}

invalidate(specifier: NormalizedStoriesSpecifier, importPath: Path, removed: boolean) {
invalidate(importPath: Path, removed: boolean) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason I'm removing the specifier from the invalidate signature, is because it meant that the StoryIndexGenerator implicitly relied on the specifiers to be referential equal, as they were used as keys for Map lookups. This was not obvious, so during my refactor I was creating multiple specifiers with normalizeStories, and they would not be referential equal, which I spent a lot of time debugging.

This change means we rely on the importPath to lookup the specifier, similar to what used to happen in watchStorySpecifier. importPath is just a string, so equality is simpler.

Copy link
Member

Choose a reason for hiding this comment

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

Did you think about what happens if multiple specifiers match the same import path?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't think about that case specifically, and that also doesn't work today on next. What I did here, was just copy the existing check that happens in watchStorySpecifier, and run that same check in StoryIndexGenerator too, so no behavior changes: https://github.com/storybookjs/storybook/pull/30612/changes#diff-e677174c1c4567773a801f4662a199c0975fe538f86bdf43d3362dc2c58c3033R72-R76

I've confirmed the behavior is the same between next and this branch, by having the following stories globs:

[
  "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)",
  {
    directory: "../src/stories",
    titlePrefix: "Extra",
    files: "Button.@(mdx|stories.@(js|jsx|mjs|ts|tsx))",
  },
]

Which correctly shows the Button stories twice in the sidebar, but when updating the file to add another story, only the first entry is updated in the sidebar, the second stays static.

This seems weird anyway, is there an actual use case for this that I'm missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Funnily enough, the change I'm making here actually makes it easier to support that use case, if that is something that we want?

@JReinhold JReinhold marked this pull request as ready for review December 16, 2025 13:53
@JReinhold JReinhold changed the title Vite: Support custom importPath in indexers Core and Vite: Use story index as source of truth for Vite paths Dec 16, 2025
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: 4

🧹 Nitpick comments (6)
storybook.code-workspace (1)

18-19: Minor: Redundant Vitest configuration.

Both vitest.workspaceConfig and vitest.rootConfig point to the same file ./code/vitest.workspace.ts. Verify if this is intentional or if one should point to a different configuration.

code/core/src/core-server/utils/watch-story-specifiers.test.ts (1)

11-11: Missing spy: true option on mock.

Per coding guidelines, use vi.mock() with the spy: true option for all package mocks in Vitest tests.

-vi.mock('watchpack');
+vi.mock('watchpack', { spy: true });
code/builders/builder-vite/src/plugins/code-generator-plugin.ts (1)

26-34: Consider storing the unsubscribe function for cleanup.

The onInvalidated method returns an unsubscribe function that isn't captured. While this may not be an issue in typical dev server lifecycles, storing and calling it on server close would be cleaner.

     async configureServer(server) {
-      (await storyIndexGeneratorPromise).onInvalidated(() => {
+      const unsubscribe = (await storyIndexGeneratorPromise).onInvalidated(() => {
         // TODO: this is only necessary when new files are added.
         // Changes and removals are already watched and handled by Vite, so they actually trigger a double HMR event right now.
         server.watcher.emit(
           'change',
           getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE)
         );
       });
+      server.httpServer?.on('close', unsubscribe);
     },
code/core/src/core-server/utils/index-json.test.ts (1)

18-18: Missing spy: true option on Watchpack mock.

Per coding guidelines, use vi.mock() with the spy: true option for package mocks.

-vi.mock('watchpack');
+vi.mock('watchpack', { spy: true });
code/builders/builder-vite/src/codegen-importfn-script.test.ts (1)

9-9: Consider adding mock cleanup for consistency.

The process.cwd() spy is created in each test but not explicitly restored. While Vitest's default behavior handles this, adding an afterEach with vi.restoreAllMocks() would make the cleanup explicit and prevent potential test pollution if the default behavior changes.

import { afterEach, describe, expect, it, vi } from 'vitest';

// Add after imports
afterEach(() => {
  vi.restoreAllMocks();
});
code/core/src/core-server/utils/index-json.ts (1)

17-24: Parameter naming inconsistency with the new convention.

The parameter is named initializedStoryIndexGenerator but the new convention elsewhere uses storyIndexGeneratorPromise. While this works, the inconsistency could cause confusion when reading code across files.

 export async function writeIndexJson(
   outputFile: string,
-  initializedStoryIndexGenerator: Promise<StoryIndexGenerator>
+  storyIndexGeneratorPromise: Promise<StoryIndexGenerator>
 ) {
-  const generator = await initializedStoryIndexGenerator;
+  const generator = await storyIndexGeneratorPromise;
   const storyIndex = await generator.getIndex();
   await writeFile(outputFile, JSON.stringify(storyIndex));
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8b8df12 and e5ece54.

📒 Files selected for processing (26)
  • code/.eslintrc.js (0 hunks)
  • code/builders/builder-vite/src/codegen-importfn-script.test.ts (3 hunks)
  • code/builders/builder-vite/src/codegen-importfn-script.ts (1 hunks)
  • code/builders/builder-vite/src/list-stories.ts (0 hunks)
  • code/builders/builder-vite/src/optimizeDeps.ts (3 hunks)
  • code/builders/builder-vite/src/plugins/code-generator-plugin.ts (3 hunks)
  • code/builders/builder-vite/src/utils/unique-import-paths.ts (1 hunks)
  • code/builders/builder-vite/src/virtual-file-names.ts (0 hunks)
  • code/builders/builder-vite/src/vite-config.test.ts (0 hunks)
  • code/core/src/core-server/build-static.ts (3 hunks)
  • code/core/src/core-server/dev-server.ts (7 hunks)
  • code/core/src/core-server/presets/common-preset.ts (4 hunks)
  • code/core/src/core-server/utils/StoryIndexGenerator.test.ts (9 hunks)
  • code/core/src/core-server/utils/StoryIndexGenerator.ts (7 hunks)
  • code/core/src/core-server/utils/doTelemetry.ts (1 hunks)
  • code/core/src/core-server/utils/getStoryIndexGenerator.ts (0 hunks)
  • code/core/src/core-server/utils/index-json.test.ts (10 hunks)
  • code/core/src/core-server/utils/index-json.ts (3 hunks)
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts (5 hunks)
  • code/core/src/core-server/utils/watch-story-specifiers.ts (3 hunks)
  • code/core/src/csf-tools/CsfFile.test.ts (8 hunks)
  • code/core/src/csf-tools/CsfFile.ts (0 hunks)
  • code/core/src/types/modules/core-common.ts (1 hunks)
  • code/core/src/types/modules/indexer.ts (1 hunks)
  • docs/api/main-config/main-config-indexers.mdx (1 hunks)
  • storybook.code-workspace (1 hunks)
💤 Files with no reviewable changes (6)
  • code/.eslintrc.js
  • code/builders/builder-vite/src/vite-config.test.ts
  • code/builders/builder-vite/src/virtual-file-names.ts
  • code/builders/builder-vite/src/list-stories.ts
  • code/core/src/core-server/utils/getStoryIndexGenerator.ts
  • code/core/src/csf-tools/CsfFile.ts
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{js,jsx,json,html,ts,tsx,mjs}

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

Use ESLint and Prettier configurations that are enforced in the codebase

Files:

  • code/core/src/core-server/utils/doTelemetry.ts
  • code/builders/builder-vite/src/optimizeDeps.ts
  • code/core/src/types/modules/indexer.ts
  • code/builders/builder-vite/src/utils/unique-import-paths.ts
  • code/core/src/core-server/dev-server.ts
  • code/core/src/csf-tools/CsfFile.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.ts
  • code/builders/builder-vite/src/plugins/code-generator-plugin.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.test.ts
  • code/builders/builder-vite/src/codegen-importfn-script.ts
  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.ts
  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/core-server/build-static.ts
  • code/core/src/types/modules/core-common.ts
  • code/core/src/core-server/utils/index-json.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
**/*.{ts,tsx}

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

Enable TypeScript strict mode

Files:

  • code/core/src/core-server/utils/doTelemetry.ts
  • code/builders/builder-vite/src/optimizeDeps.ts
  • code/core/src/types/modules/indexer.ts
  • code/builders/builder-vite/src/utils/unique-import-paths.ts
  • code/core/src/core-server/dev-server.ts
  • code/core/src/csf-tools/CsfFile.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.ts
  • code/builders/builder-vite/src/plugins/code-generator-plugin.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.test.ts
  • code/builders/builder-vite/src/codegen-importfn-script.ts
  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.ts
  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/core-server/build-static.ts
  • code/core/src/types/modules/core-common.ts
  • code/core/src/core-server/utils/index-json.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
code/**/*.{ts,tsx,js,jsx,mjs}

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

code/**/*.{ts,tsx,js,jsx,mjs}: Use server-side logger from 'storybook/internal/node-logger' for Node.js code
Use client-side logger from 'storybook/internal/client-logger' for browser code
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/core-server/utils/doTelemetry.ts
  • code/builders/builder-vite/src/optimizeDeps.ts
  • code/core/src/types/modules/indexer.ts
  • code/builders/builder-vite/src/utils/unique-import-paths.ts
  • code/core/src/core-server/dev-server.ts
  • code/core/src/csf-tools/CsfFile.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.ts
  • code/builders/builder-vite/src/plugins/code-generator-plugin.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.test.ts
  • code/builders/builder-vite/src/codegen-importfn-script.ts
  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.ts
  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/core-server/build-static.ts
  • code/core/src/types/modules/core-common.ts
  • code/core/src/core-server/utils/index-json.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
code/**/*.{ts,tsx,js,jsx}

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

Export functions that need to be tested from their modules

Files:

  • code/core/src/core-server/utils/doTelemetry.ts
  • code/builders/builder-vite/src/optimizeDeps.ts
  • code/core/src/types/modules/indexer.ts
  • code/builders/builder-vite/src/utils/unique-import-paths.ts
  • code/core/src/core-server/dev-server.ts
  • code/core/src/csf-tools/CsfFile.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.ts
  • code/builders/builder-vite/src/plugins/code-generator-plugin.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.test.ts
  • code/builders/builder-vite/src/codegen-importfn-script.ts
  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.ts
  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/core-server/build-static.ts
  • code/core/src/types/modules/core-common.ts
  • code/core/src/core-server/utils/index-json.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
code/**/*.{js,jsx,json,html,ts,tsx,mjs}

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

code/**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier with --write flag to format code before committing
Run ESLint with yarn lint:js:cmd to check for linting issues and fix errors before committing

Files:

  • code/core/src/core-server/utils/doTelemetry.ts
  • code/builders/builder-vite/src/optimizeDeps.ts
  • code/core/src/types/modules/indexer.ts
  • code/builders/builder-vite/src/utils/unique-import-paths.ts
  • code/core/src/core-server/dev-server.ts
  • code/core/src/csf-tools/CsfFile.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.ts
  • code/builders/builder-vite/src/plugins/code-generator-plugin.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.test.ts
  • code/builders/builder-vite/src/codegen-importfn-script.ts
  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.ts
  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/core-server/build-static.ts
  • code/core/src/types/modules/core-common.ts
  • code/core/src/core-server/utils/index-json.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{test,spec}.{ts,tsx}: Test files should follow the naming pattern *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx
Follow the spy mocking rules defined in .cursor/rules/spy-mocking.mdc for consistent mocking patterns with Vitest

Files:

  • code/core/src/csf-tools/CsfFile.test.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.test.ts
  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/spy-mocking.mdc)

**/*.test.{ts,tsx,js,jsx}: Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests
Place all mocks at the top of the test file before any test cases
Use vi.mocked() to type and access the mocked functions in Vitest tests
Implement mock behaviors in beforeEach blocks in Vitest tests
Mock all required dependencies that the test subject uses
Each mock implementation should return a Promise for async functions in Vitest
Mock implementations should match the expected return type of the original function
Mock all required properties and methods that the test subject uses in Vitest tests
Avoid direct function mocking without vi.mocked() in Vitest tests
Avoid mock implementations outside of beforeEach blocks in Vitest tests
Avoid mocking without the spy: true option in Vitest tests
Avoid inline mock implementations within test cases in Vitest tests
Avoid mocking only a subset of required dependencies in Vitest tests
Mock at the highest level of abstraction needed in Vitest tests
Keep mock implementations simple and focused in Vitest tests
Use type-safe mocking with vi.mocked() in Vitest tests
Document complex mock behaviors in Vitest tests
Group related mocks together in Vitest tests

Files:

  • code/core/src/csf-tools/CsfFile.test.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.test.ts
  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
code/**/*.{test,spec}.{ts,tsx,js,jsx}

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

code/**/*.{test,spec}.{ts,tsx,js,jsx}: Write meaningful unit tests that actually import and call the functions being tested
Mock external dependencies using vi.mock() for file system, loggers, and other external dependencies in tests
Aim for high test coverage of business logic (75%+ for statements/lines) using coverage reports

Files:

  • code/core/src/csf-tools/CsfFile.test.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.test.ts
  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
🧠 Learnings (31)
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx,mjs} : Use server-side logger from 'storybook/internal/node-logger' for Node.js code

Applied to files:

  • code/builders/builder-vite/src/optimizeDeps.ts
  • code/core/src/core-server/dev-server.ts
  • code/builders/builder-vite/src/plugins/code-generator-plugin.ts
  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/core-server/build-static.ts
  • code/core/src/core-server/utils/index-json.ts
  • code/core/src/core-server/utils/index-json.test.ts
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/builders/builder-vite/src/optimizeDeps.ts
  • code/core/src/core-server/dev-server.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.test.ts
  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/types/modules/core-common.ts
  • code/core/src/core-server/utils/index-json.ts
📚 Learning: 2025-11-24T17:49:31.838Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T17:49:31.838Z
Learning: Applies to code/vitest.workspace.ts : Vitest configuration is centralized in `code/vitest.workspace.ts` for workspace setup

Applied to files:

  • storybook.code-workspace
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to **/*.{js,jsx,json,html,ts,tsx,mjs} : Use ESLint and Prettier configurations that are enforced in the codebase

Applied to files:

  • storybook.code-workspace
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/core/src/core-server/dev-server.ts
  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/types/modules/core-common.ts
  • code/core/src/core-server/utils/index-json.ts
  • code/core/src/core-server/utils/index-json.test.ts
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx,mjs} : Use client-side logger from 'storybook/internal/client-logger' for browser code

Applied to files:

  • code/core/src/core-server/dev-server.ts
  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/core-server/utils/index-json.ts
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Follow existing patterns and conventions in the Storybook codebase

Applied to files:

  • code/core/src/core-server/dev-server.ts
  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/core-server/utils/index-json.ts
📚 Learning: 2025-11-05T09:37:25.920Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.

Applied to files:

  • code/core/src/core-server/dev-server.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Document complex mock behaviors in Vitest tests

Applied to files:

  • code/core/src/csf-tools/CsfFile.test.ts
  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
📚 Learning: 2025-10-01T15:24:01.060Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.

Applied to files:

  • code/core/src/csf-tools/CsfFile.test.ts
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to code/**/*.{test,spec}.{ts,tsx,js,jsx} : Write meaningful unit tests that actually import and call the functions being tested

Applied to files:

  • code/core/src/csf-tools/CsfFile.test.ts
  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid inline mock implementations within test cases in Vitest tests

Applied to files:

  • code/core/src/csf-tools/CsfFile.test.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.test.ts
  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mock implementations outside of `beforeEach` blocks in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/StoryIndexGenerator.test.ts
  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
📚 Learning: 2025-11-24T17:49:31.838Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T17:49:31.838Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Follow the spy mocking rules defined in `.cursor/rules/spy-mocking.mdc` for consistent mocking patterns with Vitest

Applied to files:

  • code/core/src/core-server/utils/StoryIndexGenerator.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Implement mock behaviors in `beforeEach` blocks in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/StoryIndexGenerator.test.ts
  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx} : Export functions that need to be tested from their modules

Applied to files:

  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking only a subset of required dependencies in Vitest tests

Applied to files:

  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Keep mock implementations simple and focused in Vitest tests

Applied to files:

  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Each mock implementation should return a Promise for async functions in Vitest

Applied to files:

  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to code/**/*.{test,spec}.{ts,tsx,js,jsx} : Mock external dependencies using vi.mock() for file system, loggers, and other external dependencies in tests

Applied to files:

  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid direct function mocking without `vi.mocked()` in Vitest tests

Applied to files:

  • code/builders/builder-vite/src/codegen-importfn-script.test.ts
  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: The useGlobals hook from storybook/manager-api returns a tuple where the third element (storyGlobals) is typed as Globals, not Globals | undefined. This means TypeScript guarantees it's always defined, making the `in` operator safe to use without additional null checks.

Applied to files:

  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/core-server/utils/index-json.ts
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: In viewport tool code, when using the `useGlobals` hook from storybook/manager-api, the third returned value `storyGlobals` is guaranteed by TypeScript to be defined (not undefined/null), making the `in` operator safe to use without additional null checks.

Applied to files:

  • code/core/src/core-server/presets/common-preset.ts
📚 Learning: 2025-09-17T07:31:04.432Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32484
File: code/core/package.json:326-326
Timestamp: 2025-09-17T07:31:04.432Z
Learning: In Storybook's core package, dependencies like `open` are bundled into the final distribution during the build process, so they should remain in devDependencies rather than being moved to dependencies. End users don't need these packages as separate runtime dependencies since they're included in the bundled code.

Applied to files:

  • code/core/src/core-server/build-static.ts
📚 Learning: 2025-09-29T13:20:23.346Z
Learnt from: mrginglymus
Repo: storybookjs/storybook PR: 32556
File: code/core/package.json:309-313
Timestamp: 2025-09-29T13:20:23.346Z
Learning: The `fast-printf` dependency in Storybook's core package is bundled into the final distribution during the build process, so it should remain in devDependencies rather than being moved to dependencies, following the same pattern as other bundled dependencies like `open`.

Applied to files:

  • code/core/src/core-server/build-static.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Group related mocks together in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required properties and methods that the test subject uses in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use `vi.mock()` with the `spy: true` option for all package and file mocks in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
  • code/core/src/core-server/utils/index-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking without the `spy: true` option in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock implementations should match the expected return type of the original function

Applied to files:

  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required dependencies that the test subject uses

Applied to files:

  • code/core/src/core-server/utils/watch-story-specifiers.test.ts
🧬 Code graph analysis (11)
code/core/src/core-server/utils/doTelemetry.ts (2)
code/core/src/core-server/utils/StoryIndexGenerator.ts (1)
  • StoryIndexGenerator (105-906)
code/core/src/core-server/withTelemetry.ts (1)
  • sendTelemetryError (89-149)
code/builders/builder-vite/src/optimizeDeps.ts (3)
code/core/src/core-server/presets/common-preset.ts (1)
  • storyIndexGenerator (298-331)
code/core/src/core-server/utils/StoryIndexGenerator.ts (1)
  • StoryIndexGenerator (105-906)
code/builders/builder-vite/src/utils/unique-import-paths.ts (1)
  • getUniqueImportPaths (4-6)
code/core/src/core-server/dev-server.ts (3)
code/core/src/common/utils/normalize-stories.ts (1)
  • normalizeStories (108-114)
code/core/src/core-server/utils/StoryIndexGenerator.ts (1)
  • StoryIndexGenerator (105-906)
code/core/src/core-server/utils/index-json.ts (1)
  • registerIndexJsonRoute (26-67)
code/core/src/core-server/utils/watch-story-specifiers.ts (1)
scripts/task.ts (1)
  • Path (44-44)
code/builders/builder-vite/src/codegen-importfn-script.ts (1)
code/builders/builder-vite/src/utils/unique-import-paths.ts (1)
  • getUniqueImportPaths (4-6)
code/core/src/core-server/utils/StoryIndexGenerator.ts (2)
code/core/src/csf-tools/CsfFile.ts (1)
  • indexInputs (980-1040)
scripts/task.ts (1)
  • Path (44-44)
code/core/src/core-server/presets/common-preset.ts (2)
code/core/src/core-server/utils/StoryIndexGenerator.ts (1)
  • StoryIndexGenerator (105-906)
code/core/src/common/utils/normalize-stories.ts (1)
  • normalizeStories (108-114)
code/core/src/types/modules/core-common.ts (1)
code/core/src/core-server/utils/StoryIndexGenerator.ts (1)
  • StoryIndexGenerator (105-906)
code/core/src/core-server/utils/index-json.ts (2)
code/core/src/core-server/utils/StoryIndexGenerator.ts (1)
  • StoryIndexGenerator (105-906)
code/core/src/core-server/utils/watch-story-specifiers.ts (1)
  • watchStorySpecifiers (42-159)
code/core/src/core-server/utils/watch-story-specifiers.test.ts (2)
code/core/src/common/utils/normalize-stories.ts (1)
  • normalizeStoriesEntry (38-100)
code/core/src/core-server/utils/watch-story-specifiers.ts (1)
  • watchStorySpecifiers (42-159)
code/core/src/core-server/utils/index-json.test.ts (1)
code/core/src/core-server/utils/index-json.ts (1)
  • registerIndexJsonRoute (26-67)
🪛 Biome (2.1.2)
code/builders/builder-vite/src/plugins/code-generator-plugin.ts

[error] 68-68: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 69-69: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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: normal
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: nx
🔇 Additional comments (28)
code/builders/builder-vite/src/utils/unique-import-paths.ts (1)

1-6: LGTM!

The utility function is well-implemented with clear purpose and concise logic. The use of Set for deduplication is appropriate.

code/core/src/types/modules/core-common.ts (1)

535-535: LGTM - Public API extension.

The addition of the optional storyIndexGenerator property to StorybookConfigRaw is well-typed and maintains backward compatibility. This enables the preset-based initialization pattern described in the PR objectives.

code/core/src/csf-tools/CsfFile.test.ts (1)

2110-2156: LGTM - Snapshot updates reflect API changes.

The removal of importPath from indexInputs snapshots aligns with the PR's shift to a StoryIndex-centric approach where import paths are derived from the index rather than stored in individual inputs.

code/builders/builder-vite/src/optimizeDeps.ts (2)

39-39: LGTM - Clean integration of new utility.

The use of getUniqueImportPaths(index) to derive optimization entries from the story index is straightforward and aligns with the StoryIndex-based architecture.


19-27: The fallback in this code is not a silent failure mechanism as suggested. The StoryIndexGenerator.getIndex() method either returns a valid StoryIndex or throws an error via this.lastError—it does not return undefined or null under normal circumstances. The nullish coalescing operator (??) fallback is unreachable under the current implementation and appears to be defensive programming rather than masking indexing errors.

Errors from the story index generation are explicitly thrown and propagated, not silently suppressed. If this code is problematic, it's not because of the error-handling logic shown here—the existing TODO comment (lines 16-17) already flags this function as outdated and in need of reworking.

Likely an incorrect or invalid review comment.

code/core/src/core-server/utils/watch-story-specifiers.ts (3)

42-46: LGTM - API simplification.

The signature change from (specifier, path, removed) to (path, removed) simplifies the invalidation callback by removing the specifier parameter, aligning with the path-based invalidation approach used by StoryIndexGenerator.


119-142: LGTM - Effective event batching.

The batching mechanism with a 100ms debounce effectively deduplicates rapid Watchpack events. The approach of storing the last event type per path and processing them in bulk is sound.


144-156: LGTM - Event handlers properly integrated.

The event handlers correctly delegate to the batching queue, maintaining the existing logic for determining removal status from the mtime parameter.

code/core/src/core-server/build-static.ts (2)

99-103: LGTM - Cleaner initialization via presets.

The shift from local construction to preset-based retrieval of storyIndexGenerator improves code organization and enables sharing the generator instance across the codebase.

Also applies to: 138-142


144-148: LGTM - Type cast is safe in this context.

The call to writeIndexJson with the type cast to Promise<StoryIndexGenerator> is safe because the code is within the !options.ignorePreview block, ensuring the generator will be initialized.

code/core/src/core-server/utils/doTelemetry.ts (2)

19-34: Improved error handling with early return.

The error handling correctly catches failures from getIndexAndStats(), reports them to telemetry, and returns early to avoid sending incomplete telemetry data. This prevents cascading errors while ensuring the failure is tracked.


36-50: LGTM - Payload construction preserved.

The telemetry payload construction logic is correctly preserved, with appropriate conditional handling when indexAndStats is available. The invariant check ensures versionCheck is defined when versionUpdates is enabled.

code/core/src/core-server/utils/watch-story-specifiers.test.ts (2)

23-35: Well-structured timer and event flushing setup.

The fake timer lifecycle management and the flushEvents helper correctly support the new batching behavior in watchStorySpecifiers. This pattern ensures deterministic test execution for debounced events.


91-114: Good use of real timers for filesystem I/O test.

Correctly switches to real timers for the globby-based directory scanning test, with a clear explanatory comment. The vi.waitFor() pattern is appropriate for asserting on async operations.

code/builders/builder-vite/src/plugins/code-generator-plugin.ts (1)

65-83: Clean refactor to switch-based virtual module loading.

The unified switch pattern for handling virtual files is cleaner than previous branching logic. The implicit undefined return for unmatched IDs correctly defers to other plugins.

code/core/src/core-server/utils/index-json.test.ts (1)

581-637: Comprehensive debounce behavior test.

Good coverage of the batching and debouncing interaction, testing both leading and trailing edge emissions. The use of vi.importActual to test real debounce behavior alongside mocked implementations is a solid pattern.

code/core/src/core-server/utils/StoryIndexGenerator.ts (2)

438-446: Clean helper for custom importPath support.

The toImportPath helper correctly handles the three cases: undefined (falls back to fileName), virtual modules (preserved as-is), and filesystem paths (normalized to relative). This enables the custom importPath feature for Vite indexers.


861-866: Well-designed invalidation listener API.

The onInvalidated method follows a clean subscription pattern with proper unsubscribe capability. This enables the Vite builder to react to index changes for HMR.

docs/api/main-config/main-config-indexers.mdx (1)

120-127: Clear documentation of importPath behavior and limitations.

The added default value and the Vite-only warning accurately reflect the implementation. This provides clear guidance for users wanting to use custom importPath values with virtual modules.

code/core/src/types/modules/indexer.ts (1)

106-107: LGTM! Clear documentation for the optional importPath field.

The type change from required to optional is well-documented, explaining that it defaults to the fileName argument passed to createIndex. This enables custom indexers to specify alternative import paths (including virtual modules) while maintaining backward compatibility.

code/core/src/core-server/utils/StoryIndexGenerator.test.ts (1)

2121-2121: LGTM! Test updates correctly reflect the simplified invalidate API.

All invalidate() calls have been updated from the three-argument signature (specifier, path, removed) to the new two-argument signature (path, removed). The tests maintain comprehensive coverage for file changes, file removals, and dependent invalidation scenarios.

code/builders/builder-vite/src/codegen-importfn-script.test.ts (1)

8-45: Tests correctly validate the new StoryIndex-based API.

The test properly validates:

  1. POSIX path handling with correct normalization
  2. Virtual module imports (virtual:story.js) are passed through unchanged
  3. The generated importFn structure matches expectations
code/core/src/core-server/dev-server.ts (2)

49-59: LGTM! Clean integration of promise-based generator pattern.

The setup correctly:

  1. Obtains the generator promise via presets
  2. Passes all required parameters to registerIndexJsonRoute
  3. Maintains proper async flow throughout

42-47: The review comment describing duplicate story normalization as problematic is inaccurate.

The implementation is sound. Both dev-server.ts and common-preset.ts call normalizeStories independently, but this does not cause issues. The invalidation mechanism in StoryIndexGenerator uses regex pattern matching (via importPathMatcher.exec()), not object reference comparison. When files change, watchStorySpecifiers invokes the generator's invalidate() method with only the file path, and the generator locates the matching specifier in its own cache using regex matching. Since normalizeStories is deterministic, both calls produce specifiers with identical regex patterns, making the separate instances functionally equivalent.

Likely an incorrect or invalid review comment.

code/builders/builder-vite/src/codegen-importfn-script.ts (1)

16-35: LGTM! Clean refactor to index-based import generation.

The implementation correctly:

  1. Handles virtual modules by passing them through unchanged
  2. Normalizes relative paths to always start with ./ or ../
  3. Uses absolute paths for the actual import to ensure correct resolution

The use of pathe ensures cross-platform compatibility for path operations.

code/core/src/core-server/utils/index-json.ts (1)

26-66: LGTM! Clean route registration with proper async handling.

The function correctly:

  1. Sets up debounced invalidation for the server channel
  2. Watches story specifiers and config files for changes
  3. Invalidates the generator appropriately on file changes
  4. Serves the index via the /index.json route with proper error handling

The double-await pattern on line 59 is correct for the promise-wrapped generator.

code/core/src/core-server/presets/common-preset.ts (2)

306-328: LGTM! Well-structured async initialization.

The async IIFE pattern correctly:

  1. Gathers all required configuration
  2. Normalizes stories with proper context
  3. Fetches indexers and docs in parallel for efficiency
  4. Creates and initializes the generator before returning

The initialization is properly awaited before returning the generator instance.


294-304: The module-level singleton pattern is intentional for caching the generator within a dev server session. Tests are unaffected as they instantiate StoryIndexGenerator directly rather than through the preset system, and hot reload is handled by the generator's invalidateAll() method which clears caches on config changes.

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 (3)
code/builders/builder-vite/src/plugins/code-generator-plugin.ts (1)

71-75: Consider removing optional chaining if typings guarantee non-null values.

The optional chaining on line 73 (storyIndexGenerator?.getIndex()) suggests defensive programming, but a past review indicated that typings should ensure getIndex always returns an index. If the presets system guarantees that storyIndexGeneratorPromise resolves to a non-null StoryIndexGenerator, the optional chaining is unnecessary.

Apply this diff if typings guarantee non-null values:

         const storyIndexGenerator = await storyIndexGeneratorPromise;
-        const index = await storyIndexGenerator?.getIndex();
+        const index = await storyIndexGenerator.getIndex();
         return generateImportFnScriptCode(index);
code/core/src/core-server/utils/StoryIndexGenerator.ts (2)

438-446: Clarify the expected format of custom importPath values.

The toImportPath helper assumes that non-virtual path values are absolute. If a custom indexer mistakenly provides a relative path (e.g., './stories/Button.js'), then relative(workingDir, path) may produce unexpected results because Node.js's relative function treats both arguments as relative to the current working directory when the second argument is relative.

Consider either:

  1. Documenting that custom indexers must provide absolute paths (or virtual paths), or
  2. Adding defensive handling to resolve relative paths first:
 const toImportPath = (path: string | undefined) => {
   if (!path) {
     return importPath;
   }
   if (path.startsWith('virtual:')) {
     return path;
   }
+  const absolutePath = resolve(this.options.workingDir, path);
-  return slash(normalizeStoryPath(relative(this.options.workingDir, path)));
+  return slash(normalizeStoryPath(relative(this.options.workingDir, absolutePath)));
 };

If the API contract already requires absolute paths, then this is fine as-is and just needs documentation.


806-806: Consider error handling for listener notifications.

The invalidation listener pattern is well-implemented with a standard subscribe/unsubscribe API. However, listener invocations (lines 806, 858) lack error handling. If a listener throws, subsequent listeners won't be called, which could lead to inconsistent state.

Consider wrapping listener calls in try-catch blocks:

-    this.invalidationListeners.forEach((listener) => listener());
+    this.invalidationListeners.forEach((listener) => {
+      try {
+        listener();
+      } catch (err) {
+        logger.error('Error in invalidation listener:', err);
+      }
+    });

This ensures all listeners are called even if one fails, improving robustness.

Also applies to: 858-859, 861-866

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9c58c6 and c3941b1.

📒 Files selected for processing (4)
  • code/builders/builder-vite/src/plugins/code-generator-plugin.ts (3 hunks)
  • code/core/src/core-server/dev-server.ts (7 hunks)
  • code/core/src/core-server/utils/StoryIndexGenerator.ts (7 hunks)
  • code/core/src/core-server/utils/index-json.test.ts (10 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • code/core/src/core-server/dev-server.ts
  • code/core/src/core-server/utils/index-json.test.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,json,html,ts,tsx,mjs}

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

Use ESLint and Prettier configurations that are enforced in the codebase

Files:

  • code/builders/builder-vite/src/plugins/code-generator-plugin.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.ts
**/*.{ts,tsx}

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

Enable TypeScript strict mode

Files:

  • code/builders/builder-vite/src/plugins/code-generator-plugin.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.ts
code/**/*.{ts,tsx,js,jsx,mjs}

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

code/**/*.{ts,tsx,js,jsx,mjs}: Use server-side logger from 'storybook/internal/node-logger' for Node.js code
Use client-side logger from 'storybook/internal/client-logger' for browser code
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/builders/builder-vite/src/plugins/code-generator-plugin.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.ts
code/**/*.{ts,tsx,js,jsx}

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

Export functions that need to be tested from their modules

Files:

  • code/builders/builder-vite/src/plugins/code-generator-plugin.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.ts
code/**/*.{js,jsx,json,html,ts,tsx,mjs}

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

code/**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier with --write flag to format code before committing
Run ESLint with yarn lint:js:cmd to check for linting issues and fix errors before committing

Files:

  • code/builders/builder-vite/src/plugins/code-generator-plugin.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/builders/builder-vite/src/plugins/code-generator-plugin.ts
  • code/core/src/core-server/utils/StoryIndexGenerator.ts
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx,mjs} : Use server-side logger from 'storybook/internal/node-logger' for Node.js code

Applied to files:

  • code/builders/builder-vite/src/plugins/code-generator-plugin.ts
🧬 Code graph analysis (1)
code/core/src/core-server/utils/StoryIndexGenerator.ts (2)
code/core/src/csf-tools/CsfFile.ts (1)
  • indexInputs (980-1040)
scripts/task.ts (1)
  • Path (44-44)
⏰ 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: normal
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: nx
🔇 Additional comments (6)
code/builders/builder-vite/src/plugins/code-generator-plugin.ts (2)

30-38: Acknowledge the TODO about double HMR events.

The TODO comment indicates that changes and removals currently trigger duplicate HMR events. While this doesn't break functionality, it may impact developer experience during hot reload.

Consider verifying the impact of these double HMR events on development workflow, or track this as a follow-up optimization task.


59-67: LGTM! Generalized virtual file resolution.

The approach now handles multiple virtual file IDs generically, which is cleaner than hard-coded cases. The explicit undefined return is good practice for Vite plugins.

code/core/src/core-server/utils/StoryIndexGenerator.ts (4)

124-125: LGTM: Listener infrastructure added.

The invalidation listener Set is properly initialized and typed.


448-484: LGTM: Story entries now use toImportPath for flexible import paths.

The use of toImportPath(input.importPath) correctly supports custom indexers providing alternative import paths, including virtual modules.


496-504: LGTM: Autodocs entries now use processed story data.

Using storyEntries[0] instead of indexInputs[0] ensures that autodocs entries use the same transformed importPath and title as their corresponding story entries, maintaining consistency.


809-817: LGTM: Dynamic specifier lookup avoids referential equality issues.

The signature change to invalidate(importPath, removed) removes the implicit reliance on specifier referential equality, as explained in the PR author's comment. The Array.from() conversion is correctly applied.

}

invalidate(specifier: NormalizedStoriesSpecifier, importPath: Path, removed: boolean) {
invalidate(importPath: Path, removed: boolean) {
Copy link
Member

Choose a reason for hiding this comment

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

Did you think about what happens if multiple specifiers match the same import path?

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

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c3941b1 and 2f8ceda.

📒 Files selected for processing (1)
  • code/core/src/core-server/build-static.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,json,html,ts,tsx,mjs}

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

Use ESLint and Prettier configurations that are enforced in the codebase

Files:

  • code/core/src/core-server/build-static.ts
**/*.{ts,tsx}

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

Enable TypeScript strict mode

Files:

  • code/core/src/core-server/build-static.ts
code/**/*.{ts,tsx,js,jsx,mjs}

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

code/**/*.{ts,tsx,js,jsx,mjs}: Use server-side logger from 'storybook/internal/node-logger' for Node.js code
Use client-side logger from 'storybook/internal/client-logger' for browser code
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/core-server/build-static.ts
code/**/*.{ts,tsx,js,jsx}

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

Export functions that need to be tested from their modules

Files:

  • code/core/src/core-server/build-static.ts
code/**/*.{js,jsx,json,html,ts,tsx,mjs}

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

code/**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier with --write flag to format code before committing
Run ESLint with yarn lint:js:cmd to check for linting issues and fix errors before committing

Files:

  • code/core/src/core-server/build-static.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx,mjs} : Use server-side logger from 'storybook/internal/node-logger' for Node.js code

Applied to files:

  • code/core/src/core-server/build-static.ts
📚 Learning: 2025-09-17T07:31:04.432Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32484
File: code/core/package.json:326-326
Timestamp: 2025-09-17T07:31:04.432Z
Learning: In Storybook's core package, dependencies like `open` are bundled into the final distribution during the build process, so they should remain in devDependencies rather than being moved to dependencies. End users don't need these packages as separate runtime dependencies since they're included in the bundled code.

Applied to files:

  • code/core/src/core-server/build-static.ts
📚 Learning: 2025-09-29T13:20:23.346Z
Learnt from: mrginglymus
Repo: storybookjs/storybook PR: 32556
File: code/core/package.json:309-313
Timestamp: 2025-09-29T13:20:23.346Z
Learning: The `fast-printf` dependency in Storybook's core package is bundled into the final distribution during the build process, so it should remain in devDependencies rather than being moved to dependencies, following the same pattern as other bundled dependencies like `open`.

Applied to files:

  • code/core/src/core-server/build-static.ts
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/core/src/core-server/build-static.ts
⏰ 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: normal
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: nx
🔇 Additional comments (4)
code/core/src/core-server/build-static.ts (4)

22-26: LGTM!

Type-only import for StoryIndexGenerator is appropriate since it's only used in type positions. The new writeIndexJson import aligns with the refactored index utilities.


99-103: LGTM!

Simplified parallel initialization reflects the new architecture where story index generation is sourced from presets rather than constructed locally.


154-155: LGTM!

Proper null guard before using indexGenerator for manifest generation.


225-226: LGTM!

Optional chaining safely handles potentially undefined generator, and the surrounding try-catch ensures telemetry failures don't break the build.

@JReinhold JReinhold merged commit e66b664 into next Dec 19, 2025
68 checks passed
@JReinhold JReinhold deleted the jeppe/custom-indexer-importpath branch December 19, 2025 09:35
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.

4 participants