-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Core: Support defineConfig when setting up ESLint plugin #32878
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit 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. 📝 WalkthroughWalkthroughAdds AST handling to detect Changes
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes ✨ Finishing touches
Comment |
code/core/src/cli/eslintPlugin.ts
Outdated
|
|
||
| if (t.isArrayExpression(init)) { | ||
| init.elements.push(t.spreadElement(storybookConfig)); | ||
| } else if (t.isCallExpression(init) && init.arguments.length > 0) { |
There was a problem hiding this comment.
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"
There was a problem hiding this comment.
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.
|
View your CI Pipeline Execution ↗ for commit 372dbc8
☁️ Nx Cloud last updated this comment at |
There was a problem hiding this 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 argumentsdefineConfig({ rules: {} })with non-array first argumentdefineConfig([], extraArg)with multiple argumentsHowever, 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
📒 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.mdcfor 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.tscode/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.tscode/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.tscode/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}: Usevi.mock()with thespy: trueoption for all package and file mocks in Vitest tests
Place all mocks at the top of the test file before any test cases
Usevi.mocked()to type and access the mocked functions in Vitest tests
Implement mock behaviors inbeforeEachblocks 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 withoutvi.mocked()in Vitest tests
Avoid mock implementations outside ofbeforeEachblocks in Vitest tests
Avoid mocking without thespy: trueoption 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 withvi.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
eslintDefineConfigLocalNamevariable follows the same initialization pattern astsEslintLocalName, 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
defineConfigis 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.tscode/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
defineConfigwhen 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
beforeEachblocks per coding guidelines. The mocks forreadFileandwriteFile(lines 453, 475, etc.) usevi.mocked(...).mockResolvedValue()inside test cases rather than inbeforeEach.After fixing this, run these commands from the
codedirectory 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.tsEnsure test coverage exceeds 75% for statements and lines, and that ESLint and Prettier report no issues.
|
@copilot you're missing a use case (and a test case) to support In this example the defineConfig is directly used in a export default. |
- 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>
- 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>
6514870 to
372dbc8
Compare
Package BenchmarksCommit: The following packages have significant changes to their size or dependencies:
|
| 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 |
Storybook init adds the
eslint-plugin-storybookimport but fails to register it in Next.js projects usingdefineConfig([...])fromeslint/config.Changes
code/core/src/cli/eslintPlugin.tsexport default defineConfig([...])patterndefineConfigimports specifically from"eslint/config"defineConfigidentifier before modifyingt.isExpression) and null checks before unwrapping and modifying arrayscode/core/src/cli/eslintPlugin.test.tsexport default defineConfig([...])pattern from"eslint/config"const config = defineConfig([...]); export default config;defineConfigfrom other packages is not modifiedBehavior
Before:
After:
All existing ESLint config formats (direct arrays,
tseslint.config(), legacy.eslintrc) remain unaffected.Original prompt
💡 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
Tests
✏️ Tip: You can customize this high-level summary in your review settings.