Csf-tools: Prevent false positive ObjectExpression warning on valid default exports#34993
Csf-tools: Prevent false positive ObjectExpression warning on valid default exports#34993surendravarikallu wants to merge 1 commit into
Conversation
…efault exports Storybook logged a warning 'CSF Parsing error: Expected 'ObjectExpression'' when the default export of a preview/story configuration was an imported reference or expression (e.g. an Identifier, CallExpression, or MemberExpression). These resolved exports are valid at runtime but cannot be resolved statically to an ObjectExpression by the parser. - Guard default export checks in ConfigFile to skip warning on Identifier, CallExpression, and MemberExpression nodes. - Guard module.exports checks similarly. - Added tests to cover the warning exclusion logic.
📝 WalkthroughWalkthroughConfigFile.parse() now conditionally emits "Expected ObjectExpression" warnings when parsing export default and module.exports assignments. Instead of warning for all non-ObjectExpression resolutions, it skips Identifier, CallExpression, and MemberExpression forms that cannot be statically resolved, with corresponding test coverage validating no warnings are emitted for these cases. ChangesConfig Parser Warning Suppression
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@code/core/src/csf-tools/ConfigFile.test.ts`:
- Around line 1-5: The test currently calls logger.warn.mockImplementation
inline inside a test; instead, add a module-level
vi.mock('storybook/internal/node-logger', { spy: true }) and use
vi.mocked(logger) to obtain the mocked module, then move the warn mock setup
into a beforeEach (e.g., const mockedLogger = vi.mocked(logger); beforeEach(()
=> mockedLogger.warn.mockImplementation(() => {}))); also add afterEach(() =>
vi.restoreAllMocks()) to reset spies and remove any inline mockImplementation in
individual it(...) blocks; update references to logger.warn in tests to use the
mockedLogger where needed.
In `@code/core/src/csf-tools/ConfigFile.ts`:
- Around line 312-317: Tests currently only cover default-export paths; add
regression tests ensuring no warning is emitted for module.exports when assigned
an identifier, a call expression, or a member expression. In the test suite for
ConfigFile handling (tests that exercise the exportObject guard around
exportObject and checks using
t.isIdentifier/t.isCallExpression/t.isMemberExpression), add three assertions
verifying that "module.exports = config", "module.exports = createConfig(...)",
and "module.exports = obj.value" are treated the same as the default-export
cases (i.e., no warning produced). Target the code path that evaluates
exportObject in ConfigFile (the guard using exportObject and the t.is* helpers)
so the tests fail if the guard regresses for module.exports assignments.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7ee03543-81b8-4847-bf4d-a52141f67d40
📒 Files selected for processing (2)
code/core/src/csf-tools/ConfigFile.test.tscode/core/src/csf-tools/ConfigFile.ts
| import { describe, expect, it, vi } from 'vitest'; | ||
|
|
||
| import { babelPrint } from 'storybook/internal/babel'; | ||
| import { babelPrint, types as t } from 'storybook/internal/babel'; | ||
| import { logger } from 'storybook/internal/node-logger'; | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate and print the relevant sections of the test file.
FILE="code/core/src/csf-tools/ConfigFile.test.ts"
echo "=== File: $FILE ==="
wc -l "$FILE"
rg -n "warn|spyOn\\(logger|warning" "$FILE" || true
# Show imports
sed -n '1,80p' "$FILE"
# Show around the referenced lines (~257-279)
sed -n '220,340p' "$FILE"
# Read the spy mocking rule file for exact constraints.
RULES_FILE=".cursor/rules/spy-mocking.mdc"
echo "=== Rules: $RULES_FILE ==="
ls -la "$RULES_FILE" || true
sed -n '1,200p' "$RULES_FILE" || trueRepository: storybookjs/storybook
Length of output: 7744
Align ConfigFile.test.ts logger warning suppression with Vitest spy-mocking rules.
In code/core/src/csf-tools/ConfigFile.test.ts (around lines 257-279), logger.warn is spied and mockImplementation is set inline inside the test body, which violates .cursor/rules/spy-mocking.mdc (mock implementations must be in beforeEach, and inline mock implementations in test cases should be avoided). It also doesn’t use the vi.mock(..., { spy: true }) + vi.mocked() pattern for module mocking.
Suggested update
-import { describe, expect, it, vi } from 'vitest';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
import { babelPrint, types as t } from 'storybook/internal/babel';
import { logger } from 'storybook/internal/node-logger';
import { dedent } from 'ts-dedent';
import { loadConfig, printConfig } from './ConfigFile.ts';
+
+vi.mock('storybook/internal/node-logger', { spy: true });
+
+const mockedWarn = vi.mocked(logger.warn);
+
+beforeEach(() => {
+ mockedWarn.mockImplementation(() => {});
+});
@@
- it('should not log warning when default export is Identifier, CallExpression, or MemberExpression', () => {
- const warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {});
+ it('should not log warning when default export is Identifier, CallExpression, or MemberExpression', () => {
@@
- expect(warnSpy).not.toHaveBeenCalled();
- warnSpy.mockRestore();
+ expect(mockedWarn).not.toHaveBeenCalled();
});🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@code/core/src/csf-tools/ConfigFile.test.ts` around lines 1 - 5, The test
currently calls logger.warn.mockImplementation inline inside a test; instead,
add a module-level vi.mock('storybook/internal/node-logger', { spy: true }) and
use vi.mocked(logger) to obtain the mocked module, then move the warn mock setup
into a beforeEach (e.g., const mockedLogger = vi.mocked(logger); beforeEach(()
=> mockedLogger.warn.mockImplementation(() => {}))); also add afterEach(() =>
vi.restoreAllMocks()) to reset spies and remove any inline mockImplementation in
individual it(...) blocks; update references to logger.warn in tests to use the
mockedLogger where needed.
| } else if ( | ||
| exportObject && | ||
| !t.isIdentifier(exportObject) && | ||
| !t.isCallExpression(exportObject) && | ||
| !t.isMemberExpression(exportObject) | ||
| ) { |
There was a problem hiding this comment.
Add regression coverage for module.exports unresolved expressions.
This guard changed behavior for module.exports too, but the new tests only assert the default-export path. Please add a counterpart test ensuring no warning for module.exports = config, module.exports = createConfig(...), and module.exports = obj.value.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@code/core/src/csf-tools/ConfigFile.ts` around lines 312 - 317, Tests
currently only cover default-export paths; add regression tests ensuring no
warning is emitted for module.exports when assigned an identifier, a call
expression, or a member expression. In the test suite for ConfigFile handling
(tests that exercise the exportObject guard around exportObject and checks using
t.isIdentifier/t.isCallExpression/t.isMemberExpression), add three assertions
verifying that "module.exports = config", "module.exports = createConfig(...)",
and "module.exports = obj.value" are treated the same as the default-export
cases (i.e., no warning produced). Target the code path that evaluates
exportObject in ConfigFile (the guard using exportObject and the t.is* helpers)
so the tests fail if the guard regresses for module.exports assignments.
|
Thank you for your contribution. I've seen that you have opened a lot of PRs. A comment on the style and format:
Thank you! |
Description
Storybook logged a warning CSF Parsing error: Expected 'ObjectExpression' when the default export of a preview/story configuration was an imported reference or expression (e.g. an Identifier, CallExpression, or MemberExpression) that cannot be parsed statically. These exports are valid at runtime but could not be parsed to an ObjectExpression by the static parser.
This PR:
Manual testing
export default config;(imported reference) and verified that the CSF Parsing error console warning is no longer logged.