Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 29, 2025

Storybook init adds the eslint-plugin-storybook import but fails to register it in Next.js projects using defineConfig([...]) from eslint/config.

Changes

code/core/src/cli/eslintPlugin.ts

  • Added Case 2b to handle direct export default defineConfig([...]) pattern
  • Extended Case 3 to handle CallExpressions wrapping array arguments (variable assignment pattern)
  • Added tracking for defineConfig imports specifically from "eslint/config"
  • Validates the callee matches the imported defineConfig identifier before modifying
  • Added type guards (t.isExpression) and null checks before unwrapping and modifying arrays

code/core/src/cli/eslintPlugin.test.ts

  • Added test case validating direct export default defineConfig([...]) pattern from "eslint/config"
  • Added test case validating variable assignment pattern const config = defineConfig([...]); export default config;
  • Added test case ensuring defineConfig from other packages is not modified

Behavior

Before:

import storybook from "eslint-plugin-storybook";

// Pattern 1: Variable assignment
const eslintConfig = defineConfig([
  ...nextVitals,
  ...nextTs,
]); // Plugin import added but not registered
export default eslintConfig;

// Pattern 2: Direct export
export default defineConfig([
  ...nextVitals,
  ...nextTs,
]); // Plugin import added but not registered

After:

// Only modifies defineConfig from "eslint/config"
import { defineConfig } from "eslint/config";
import storybook from "eslint-plugin-storybook";

// Pattern 1: Variable assignment - now works
const eslintConfig = defineConfig([
  ...nextVitals,
  ...nextTs,
  ...storybook.configs["flat/recommended"]
]);
export default eslintConfig;

// Pattern 2: Direct export - now works
export default defineConfig([
  ...nextVitals,
  ...nextTs,
  ...storybook.configs["flat/recommended"]
]);

// defineConfig from other packages is left unchanged (import only added)
import { defineConfig } from "some-other-lib";
const config = defineConfig([...]); // Not modified

All existing ESLint config formats (direct arrays, tseslint.config(), legacy .eslintrc) remain unaffected.

Original prompt

This section details on the original issue you should resolve

<issue_title>[Bug]: Storybook plugin not added to eslint.config during init in Next.js projects</issue_title>
<issue_description>### Describe the bug

When initializing Storybook in a Next.js project, the setup modifies the eslint.config file to include an import for the Storybook ESLint plugin, but does not actually register the plugin in the ESLint configuration array.

Current generated eslint.config:

// 👇 only the import is added, but the plugin isn't added to the config
import storybook from "eslint-plugin-storybook";

import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";

const eslintConfig = defineConfig([
  ...nextVitals,
  ...nextTs,
  // Override default ignores of eslint-config-next.
  globalIgnores([
    ".next/**",
    "out/**",
    "build/**",
    "next-env.d.ts",
  ]),
]);

export default eslintConfig;

Expected behaviour
Storybook’s init process should both:

  • Add the eslint-plugin-storybook import and
  • Extend the ESLint configuration with the Storybook plugin, e.g.:

Reproduction steps

  1. Create a new Next.js project (npx gitpick storybookjs/sandboxes/tree/next/nextjs-vite/default-ts/before-storybook)
  2. Run npx storybook@latest init
  3. Inspect the generated eslint.config.mjs (or eslint.config.mjs)

System

-

Additional context

No response</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Summary by CodeRabbit

  • New Features

    • Improved handling of various ESLint flat-config styles (including import aliases and import-only sources) so Storybook lint rules and Next.js presets are merged correctly across different export patterns.
  • Tests

    • Added tests validating merging behavior and import-preservation across defineConfig-style, direct-array, and non-standard export scenarios.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 29, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds AST handling to detect defineConfig imports (including aliased imports) from eslint/config, unwraps defineConfig([...]) call forms in both export default and identifier initializers, and appends Storybook presets into the inner array for flat ESLint configs. New tests cover these scenarios and import-only cases.

Changes

Cohort / File(s) Summary
ESLint plugin implementation
code/core/src/cli/eslintPlugin.ts
Detects imports from eslint/config and records the local alias for defineConfig. Unwraps defineConfig([...]) call forms on export default and identifier initializers; if the inner argument is an array, appends Storybook presets into that array. Preserves existing handling for direct-array exports and other formats.
ESLint plugin tests
code/core/src/cli/eslintPlugin.test.ts
Adds tests covering defineConfig usage from eslint/config (including aliased imports), export default defineConfig(...), identifier-bound defineConfig(...), and import-only/non-standard sources. Verifies Storybook presets are inserted alongside existing presets and that non-eslint/config imports are preserved as import-only.

Sequence Diagram

sequenceDiagram
    participant Test as Test harness
    participant Plugin as ESLint plugin
    participant ImportDetector as Import detector
    participant ConfigHandler as Config handler

    Test->>Plugin: Provide ESLint config source
    Plugin->>ImportDetector: Scan AST for imports (e.g., from "eslint/config") and record local alias for defineConfig
    ImportDetector-->>Plugin: Return alias (or none)
    Plugin->>ConfigHandler: Locate export/default or identifier initializer
    alt export/initializer is defineConfig([...])
        ConfigHandler->>ConfigHandler: Unwrap defineConfig call and extract inner array
        ConfigHandler->>ConfigHandler: Append Storybook preset(s) into inner array
    else export/initializer is array or other supported form
        ConfigHandler->>ConfigHandler: Append Storybook preset(s) directly
    end
    ConfigHandler-->>Plugin: Return modified AST/content
    Plugin-->>Test: Emit updated config content
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

✨ Finishing touches
  • 📝 Generate docstrings

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

Copilot AI changed the title [WIP] Fix Storybook plugin not added to eslint.config during init Oct 29, 2025
Copilot AI requested a review from yannbf October 29, 2025 09:58

if (t.isArrayExpression(init)) {
init.elements.push(t.spreadElement(storybookConfig));
} else if (t.isCallExpression(init) && init.arguments.length > 0) {
Copy link
Member

Choose a reason for hiding this comment

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

@copilot only if it's defineConfig from import { defineConfig } from "eslint/config"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 72ec435. Now specifically checks that defineConfig is imported from "eslint/config" before modifying the array. Added test to verify we don't modify defineConfig from other sources.

Copilot AI changed the title Fix: Add Storybook ESLint plugin to defineConfig-wrapped configurations Oct 29, 2025
Copilot AI requested a review from yannbf October 29, 2025 14:31
@yannbf yannbf marked this pull request as ready for review November 25, 2025 12:58
@nx-cloud
Copy link

nx-cloud bot commented Nov 25, 2025

View your CI Pipeline Execution ↗ for commit 372dbc8

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

☁️ Nx Cloud last updated this comment at 2026-01-19 10:21:36 UTC

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 (1)
code/core/src/cli/eslintPlugin.test.ts (1)

433-515: Optional: Consider adding edge case tests.

While the current implementation has proper guards (lines 127, 136 in eslintPlugin.ts), you might consider adding explicit tests for edge cases such as:

  • defineConfig() with no arguments
  • defineConfig({ rules: {} }) with non-array first argument
  • defineConfig([], extraArg) with multiple arguments

However, these cases are already handled by the guard clauses and would fall back to import-only behavior (similar to the "custom unknown format" test at line 517), so these tests are nice-to-have rather than essential.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e66a2e and eacafc1.

📒 Files selected for processing (2)
  • code/core/src/cli/eslintPlugin.test.ts (1 hunks)
  • code/core/src/cli/eslintPlugin.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{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/cli/eslintPlugin.test.ts
**/*.{ts,tsx,js,jsx,mjs}

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

Use camelCase for variable and function names

Files:

  • code/core/src/cli/eslintPlugin.test.ts
  • code/core/src/cli/eslintPlugin.ts
**/*.{ts,tsx}

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

**/*.{ts,tsx}: Enable TypeScript strict mode
Export functions from modules for testing purposes

Files:

  • code/core/src/cli/eslintPlugin.test.ts
  • code/core/src/cli/eslintPlugin.ts
**/*.{ts,tsx,js,jsx,json,html,mjs}

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

**/*.{ts,tsx,js,jsx,json,html,mjs}: Use ESLint and Prettier for code style enforcement
Run 'yarn prettier --write ' to format code after making changes
Run 'yarn lint:js:cmd ' to check for ESLint issues after making changes

Files:

  • code/core/src/cli/eslintPlugin.test.ts
  • code/core/src/cli/eslintPlugin.ts
**/*.test.{ts,tsx}

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

**/*.test.{ts,tsx}: Write meaningful unit tests that import and call functions being tested, not just verify syntax patterns
Use 'yarn vitest run --coverage ' to run tests with coverage reports and aim for 75%+ coverage of statements/lines
Focus test coverage on all branches, conditions, edge cases, error paths, and different input variations
Use 'vi.mock()' to mock external dependencies like file system and loggers in unit tests

Files:

  • code/core/src/cli/eslintPlugin.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/cli/eslintPlugin.test.ts
code/**/!(*.test).{ts,tsx,js,mjs}

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

code/**/!(*.test).{ts,tsx,js,mjs}: Use 'logger' from 'storybook/internal/node-logger' for server-side (Node.js) logging, not console.log/console.warn/console.error
Use 'logger' from 'storybook/internal/client-logger' for client-side (browser) logging, not console.log/console.warn/console.error
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/cli/eslintPlugin.ts
🧠 Learnings (16)
📓 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: cylewaitforit
Repo: storybookjs/storybook PR: 31965
File: code/lib/eslint-plugin/src/rules/only-csf3.ts:31-33
Timestamp: 2025-09-24T13:04:58.631Z
Learning: The Storybook ESLint plugin supports ESLint v8.57+ where context.sourceCode is already available as a property since it was introduced in v8.40.0, so no fallback to context.getSourceCode() is needed in rules.
Learnt from: cylewaitforit
Repo: storybookjs/storybook PR: 31965
File: code/lib/eslint-plugin/src/rules/only-csf3.ts:31-33
Timestamp: 2025-09-24T13:04:58.631Z
Learning: The Storybook ESLint plugin supports ESLint v8.57+ where context.sourceCode is already available as a property since it was introduced in v8.40.0, so no fallback to context.getSourceCode() is needed in rules.
📚 Learning: 2025-11-24T17:49:47.649Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T17:49:47.649Z
Learning: Applies to **/*.test.{ts,tsx} : Write meaningful unit tests that import and call functions being tested, not just verify syntax patterns

Applied to files:

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

Applied to files:

  • code/core/src/cli/eslintPlugin.test.ts
📚 Learning: 2025-11-24T17:49:47.649Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T17:49:47.649Z
Learning: Applies to **/*.test.{ts,tsx} : Focus test coverage on all branches, conditions, edge cases, error paths, and different input variations

Applied to files:

  • code/core/src/cli/eslintPlugin.test.ts
📚 Learning: 2025-11-24T17:49:59.268Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.268Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid inline mock implementations within test cases in Vitest tests

Applied to files:

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

Applied to files:

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

Applied to files:

  • code/core/src/cli/eslintPlugin.test.ts
📚 Learning: 2025-11-24T17:49:59.268Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.268Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Keep mock implementations simple and focused in Vitest tests

Applied to files:

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

Applied to files:

  • code/core/src/cli/eslintPlugin.test.ts
📚 Learning: 2025-11-24T17:49:59.268Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.268Z
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/cli/eslintPlugin.test.ts
📚 Learning: 2025-11-24T17:49:47.649Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T17:49:47.649Z
Learning: Applies to **/*.test.{ts,tsx} : Use 'vi.mock()' to mock external dependencies like file system and loggers in unit tests

Applied to files:

  • code/core/src/cli/eslintPlugin.test.ts
📚 Learning: 2025-11-24T17:49:59.268Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.268Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid direct function mocking without `vi.mocked()` in Vitest tests

Applied to files:

  • code/core/src/cli/eslintPlugin.test.ts
📚 Learning: 2025-11-24T17:49:59.268Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.268Z
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/cli/eslintPlugin.test.ts
📚 Learning: 2025-11-24T17:49:59.268Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.268Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Implement mock behaviors in `beforeEach` blocks in Vitest tests

Applied to files:

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

Applied to files:

  • code/core/src/cli/eslintPlugin.test.ts
📚 Learning: 2025-11-24T17:49:47.649Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T17:49:47.649Z
Learning: Applies to **/*.{ts,tsx,js,jsx,json,html,mjs} : Use ESLint and Prettier for code style enforcement

Applied to files:

  • code/core/src/cli/eslintPlugin.ts
🧬 Code graph analysis (1)
code/core/src/cli/eslintPlugin.test.ts (2)
scripts/utils/tools.ts (1)
  • dedent (119-119)
code/core/src/cli/eslintPlugin.ts (1)
  • configureEslintPlugin (219-280)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (7)
code/core/src/cli/eslintPlugin.ts (4)

59-59: LGTM! Variable declaration follows existing patterns.

The eslintDefineConfigLocalName variable follows the same initialization pattern as tsEslintLocalName, maintaining consistency in the codebase.


81-88: LGTM! Import detection correctly identifies defineConfig from eslint/config.

The logic properly:

  • Validates the import source is specifically "eslint/config"
  • Uses appropriate type guards (t.isImportSpecifier, t.isIdentifier)
  • Captures the local name, supporting aliased imports like import { defineConfig as config }

125-140: LGTM! Robust handling of defineConfig with proper safety checks.

The implementation correctly:

  • Validates that defineConfig is imported from "eslint/config" before modification (line 129-130)
  • Uses comprehensive type guards and null checks (lines 127, 128, 134, 136)
  • Handles TypeScript type assertions via unwrapTSExpression (line 135)
  • Only modifies the array when all conditions are met

This addresses the PR objective and the reviewer's concern about ensuring defineConfig is specifically from "eslint/config".


81-140: Reminder: Run linters before committing.

Per coding guidelines, please run the following commands to ensure code style compliance:

yarn lint:prettier code/core/src/cli/eslintPlugin.ts
yarn lint:js:cmd code/core/src/cli/eslintPlugin.ts
code/core/src/cli/eslintPlugin.test.ts (3)

433-480: LGTM! Comprehensive test for Next.js defineConfig pattern.

This test effectively validates:

  • The defineConfig import is detected from "eslint/config"
  • The Storybook config is correctly appended to the array inside defineConfig([...])
  • Existing Next.js imports and configuration are preserved
  • The generated output matches the expected format

The realistic Next.js fixture ensures compatibility with the actual use case from issue #32858.


482-515: LGTM! Critical test ensuring defineConfig from other sources is not modified.

This test is essential for validating that the implementation only modifies defineConfig when it's imported from "eslint/config" and not from other packages. This directly addresses the reviewer's feedback and prevents unintended modifications to third-party config wrappers.

The test confirms:

  • The Storybook import is added (expected behavior)
  • The original defineConfig([...]) call remains unmodified (correct behavior)

433-515: Move mock implementations to beforeEach blocks; verify test coverage and linting manually.

The test cases have inline mock implementations that should be moved to beforeEach blocks per coding guidelines. The mocks for readFile and writeFile (lines 453, 475, etc.) use vi.mocked(...).mockResolvedValue() inside test cases rather than in beforeEach.

After fixing this, run these commands from the code directory to verify test coverage and code style:

NODE_OPTIONS=--max_old_space_size=4096 vitest run code/core/src/cli/eslintPlugin.test.ts --coverage
yarn lint:js:cmd code/core/src/cli/eslintPlugin.test.ts
yarn lint:prettier code/core/src/cli/eslintPlugin.test.ts

Ensure test coverage exceeds 75% for statements and lines, and that ESLint and Prettier report no issues.

@github-actions github-actions bot added the Stale label Dec 6, 2025
@yannbf
Copy link
Member

yannbf commented Dec 22, 2025

@copilot you're missing a use case (and a test case) to support

const mockConfigFile = dedent`import { defineConfig, globalIgnores } from "eslint/config";
        import nextVitals from "eslint-config-next/core-web-vitals";
        import nextTs from "eslint-config-next/typescript";

        export default defineConfig([
          ...nextVitals,
          ...nextTs,
          globalIgnores([
            ".next/**",
            "out/**",
            "build/**",
            "next-env.d.ts",
          ]),
        ]);`;

In this example the defineConfig is directly used in a export default.

Copy link
Contributor Author

Copilot AI commented Dec 22, 2025

@copilot you're missing a use case (and a test case) to support

...

Added in bc75514. Now supports direct export default defineConfig([...]) pattern. Added test case to validate this scenario works correctly.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 14, 2026

Fails
🚫 PR description is missing the mandatory "#### Manual testing" section. Please add it so that reviewers know how to manually test your changes.

Generated by 🚫 dangerJS against 372dbc8

Copilot AI and others added 2 commits January 19, 2026 11:05
- Modified configureFlatConfig to handle CallExpression wrappers like defineConfig([...])
- Added test case for Next.js-style ESLint configuration
- All existing tests continue to pass

Co-authored-by: yannbf <1671563+yannbf@users.noreply.github.com>
Copilot AI and others added 5 commits January 19, 2026 11:05
- Added proper type checks before accessing array arguments
- Verify argument is Expression or TS type before unwrapping
- Addresses code review feedback about unsafe type casting

Co-authored-by: yannbf <1671563+yannbf@users.noreply.github.com>
- Removed redundant TS type checks from type guard
- t.isExpression is sufficient since unwrapTSExpression handles TS types
- All tests continue to pass

Co-authored-by: yannbf <1671563+yannbf@users.noreply.github.com>
- Prevent potential runtime error when unwrapTSExpression returns null/undefined
- Makes code more defensive and robust
- All tests continue to pass

Co-authored-by: yannbf <1671563+yannbf@users.noreply.github.com>
- Track eslintDefineConfigLocalName from "eslint/config" imports
- Check callee matches defineConfig before modifying array
- Add test for defineConfig from non-eslint/config source
- Ensures we don't modify arbitrary function calls

Co-authored-by: yannbf <1671563+yannbf@users.noreply.github.com>
- Added Case 2b to handle export default defineConfig([...]) from "eslint/config"
- Added test case for direct export default defineConfig pattern
- Ensures defineConfig directly exported is modified correctly

Co-authored-by: yannbf <1671563+yannbf@users.noreply.github.com>
@yannbf yannbf force-pushed the copilot/fix-storybook-plugin-eslint-config branch from 6514870 to 372dbc8 Compare January 19, 2026 10:09
@storybook-app-bot
Copy link

Package Benchmarks

Commit: 372dbc8, ran on 19 January 2026 at 10:18:53 UTC

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

@storybook/react-native-web-vite

Before After Difference
Dependency count 159 159 0
Self size 30 KB 30 KB 🎉 -10 B 🎉
Dependency size 23.00 MB 23.12 MB 🚨 +115 KB 🚨
Bundle Size Analyzer Link Link

@storybook/cli

Before After Difference
Dependency count 183 183 0
Self size 775 KB 775 KB 🚨 +139 B 🚨
Dependency size 67.38 MB 67.46 MB 🚨 +75 KB 🚨
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 176 176 0
Self size 30 KB 30 KB 🎉 -40 B 🎉
Dependency size 65.95 MB 66.03 MB 🚨 +75 KB 🚨
Bundle Size Analyzer Link Link
@yannbf yannbf changed the title Fix: Add Storybook ESLint plugin to defineConfig from eslint/config Jan 19, 2026
@yannbf yannbf merged commit a1c13a8 into next Jan 19, 2026
124 of 125 checks passed
@yannbf yannbf deleted the copilot/fix-storybook-plugin-eslint-config branch January 19, 2026 11:44
@github-actions github-actions bot mentioned this pull request Jan 19, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment