diff --git a/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.test.ts b/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.test.ts index eb20715486f5..8c22d4fbba96 100644 --- a/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.test.ts +++ b/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.test.ts @@ -347,7 +347,7 @@ describe('preview specific functionality', () => { import { type Preview } from '@storybook/react-vite'; export const decorators = [] const preview = { - + parameters: { options: {} } @@ -368,4 +368,42 @@ describe('preview specific functionality', () => { }); `); }); + + it('should add default export when preview only has side-effect imports', async () => { + await expect( + transform(dedent` + import './preview.scss' + `) + ).resolves.toMatchInlineSnapshot(` + import { definePreview } from '@storybook/react-vite'; + + import './preview.scss'; + + export default definePreview({}); + `); + }); + + it('should add default export when preview file is empty', async () => { + await expect(transform('')).resolves.toMatchInlineSnapshot(` + import { definePreview } from '@storybook/react-vite'; + + export default definePreview({}); + `); + }); + + it('should add default export when preview only has multiple side-effect imports', async () => { + await expect( + transform(dedent` + import './preview.scss' + import './global.css' + `) + ).resolves.toMatchInlineSnapshot(` + import { definePreview } from '@storybook/react-vite'; + + import './global.css'; + import './preview.scss'; + + export default definePreview({}); + `); + }); }); diff --git a/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.ts b/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.ts index 631e7677abfb..c289c247ff51 100644 --- a/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.ts +++ b/code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.ts @@ -166,6 +166,21 @@ export async function configToCsfFactory( // Add the new export default declaration programNode.body.push(t.exportDefaultDeclaration(defineConfigCall)); + } else if (configType === 'preview') { + /** + * Scenario 4: No exports (empty file or only side-effect imports) + * + * ``` + * import './preview.scss'; + * ``` + * + * Transform into: `import './preview.scss'; export default definePreview({})` + * + * This is needed because story files using CSF factories import from preview, so the preview + * file must have a default export. + */ + const defineConfigCall = t.callExpression(t.identifier(methodName), [t.objectExpression([])]); + programNode.body.push(t.exportDefaultDeclaration(defineConfigCall)); } const configImport = t.importDeclaration(