-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Description
Describe the bug
Storybook's CSF parser (csf-tools) incorrectly identifies Zod v4's .meta() method calls as Storybook's CSF Factories meta() function when the object calling .meta() is a directly imported variable.
This causes the error:
BadMetaError: CSF: meta() factory must be imported from .storybook/preview configuration
Note: This bug was originally reported for Storybook 9.x and has been verified to still exist in Storybook 10.3.0-alpha.0 (latest main branch).
Reproduction steps
- Create a story file that imports a Zod schema from an external module
- Use the imported schema with Zod v4's
.meta()method - Run Storybook - the CSF parser will throw
BadMetaError
Minimal reproduction code
External module (schemas.ts):
import { z } from 'zod';
export const mySchema = z.union([
z.number(),
z.string(),
]);Story file (Component.stories.tsx):
import { z } from 'zod';
import { mySchema } from './schemas'; // ← Line 2
import type { Meta, StoryObj } from '@storybook/react';
// This works fine - z.string() returns a new object, not a directly imported variable
const workingSchema = z.object({
name: z.string().meta({ description: 'Name' }),
});
// This FAILS - mySchema is a directly imported variable
const failingSchema = z.object({
value: mySchema.meta({ description: 'Value' }), // ← Triggers BadMetaError
});
const meta = {
title: 'Example',
component: () => <div>Test</div>,
} satisfies Meta;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};Expected behavior
The CSF parser should only detect meta() calls that match the CSF Factories pattern (e.g., preview.meta()), not any .meta() method call on imported variables.
Actual behavior
The parser throws BadMetaError pointing to line 2 (the import statement) because:
- It detects
mySchema.meta(...)as a potential CSF Factory call - It checks if
mySchemais imported from.storybook/preview - Since it's not, it throws the error
Root cause analysis
In code/core/src/csf-tools/CsfFile.ts (lines 829-868):
if (
t.isMemberExpression(callee) &&
t.isIdentifier(callee.property) &&
callee.property.name === 'meta' && // ← Only checks method name is "meta"
node.arguments.length > 0
) {
let rootObject = callee.object;
if (t.isCallExpression(rootObject) && t.isMemberExpression(rootObject.callee)) {
rootObject = rootObject.callee.object;
}
if (t.isIdentifier(rootObject)) {
const configCandidate = path.scope.getBinding(rootObject.name);
const configParent = configCandidate?.path?.parentPath?.node;
if (t.isImportDeclaration(configParent)) {
if (isValidPreviewPath(configParent.source.value)) {
// Valid CSF Factory
} else {
throw new BadMetaError(
'meta() factory must be imported from .storybook/preview configuration',
configParent,
self._options.fileName
);
}
}
}
}The detection logic is too broad - it matches any importedVar.meta(...) pattern instead of specifically targeting CSF Factories' preview.meta() pattern.
Suggested fix
The parser should additionally verify that the .meta() call is actually intended to be a CSF Factory call. Possible approaches:
-
Check the context: Only trigger this check when the
.meta()call result is used to define the story meta (e.g., assigned to a variable that's later used with.story()or exported as default) -
Check naming conventions: CSF Factory imports typically use names like
preview,config, or come from paths containing.storybook/preview -
Skip non-factory files: Only perform this validation in files that have clear CSF Factory indicators (e.g.,
meta.story()calls) -
Graceful handling: Instead of throwing immediately when the import source doesn't match preview config, skip the validation entirely - let non-CSF-Factory
.meta()calls pass through without error
Environment
| Software | Version |
|---|---|
| Storybook | 10.3.0-alpha.0 (also affects 9.x) |
| Framework | @storybook/react-vite |
| Builder | Vite |
| Package Manager | pnpm |
| Operating System | macOS |
| Zod | 3.25.x (v4 API with .meta()) |
Additional context
- Zod v4 introduced the
.meta()method for adding metadata to schemas (see Zod v4 docs) - This naming collision with Storybook's CSF Factories creates a false positive detection
- The issue only occurs when calling
.meta()on directly imported variables, not on intermediate results likez.string().meta() - This is a regression in the sense that any library using a
.meta()method on exported objects will trigger this false positive
Reproduction link
https://github.com/xuzuodong/Storybook-CSF-Parser-Bug-Reproduction
Reproduction steps
No response
System
System:
OS: macOS 26.1
CPU: (8) arm64 Apple M3
Shell: 5.9 - /bin/zsh
Binaries:
Node: 22.12.0 - ~/.nvm/versions/node/v22.12.0/bin/node
Yarn: 1.22.11 - /usr/local/bin/yarn
npm: 8.5.0 - /usr/local/bin/npm
pnpm: 10.12.3 - ~/Library/pnpm/pnpm <----- active
Browsers:
Chrome: 144.0.7559.97
Safari: 26.1
npmPackages:
@storybook/react: ^9.1.0 => 9.1.17
@storybook/react-vite: ^9.1.0 => 9.1.17
storybook: ^9.1.0 => 9.1.17Additional context
No response