From 68609884026d10a910c3ada7d708d62685b52427 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Wed, 24 Sep 2025 14:47:52 +0200 Subject: [PATCH] CSF: Enhance config-to-csf-factory to support type wrappers --- .../helpers/config-to-csf-factory.test.ts | 59 ++++++++++++++++++- .../codemod/helpers/config-to-csf-factory.ts | 43 +++++++------- 2 files changed, 80 insertions(+), 22 deletions(-) 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 f0405236345a..d7aeba97d34b 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 @@ -242,7 +242,7 @@ describe('preview specific functionality', () => { }); `); }); - it('should work', async () => { + it('should wrap definePreview for mixed annotations and default export', async () => { await expect( transform(dedent` export const decorators = [1] @@ -259,4 +259,61 @@ describe('preview specific functionality', () => { }); `); }); + + it('should wrap definePreview for const defined preview with type annotations', async () => { + await expect( + transform(dedent` + import { type Preview } from '@storybook/react-vite'; + + const preview = { + decorators: [], + + parameters: { + options: {} + } + } satisfies Preview; + + export default preview; + + `) + ).resolves.toMatchInlineSnapshot(` + import { definePreview } from '@storybook/react-vite'; + + export default definePreview({ + decorators: [], + + parameters: { + options: {}, + }, + }); + `); + }); + + it('should wrap definePreview for mixed annotations and default const export', async () => { + await expect( + transform(dedent` + import { type Preview } from '@storybook/react-vite'; + export const decorators = [] + const preview = { + + parameters: { + options: {} + } + } satisfies Preview; + + export default preview; + + `) + ).resolves.toMatchInlineSnapshot(` + import { definePreview } from '@storybook/react-vite'; + + export default definePreview({ + decorators: [], + + parameters: { + options: {}, + }, + }); + `); + }); }); 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 1db0859b31a9..c49d802979cb 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 @@ -32,6 +32,25 @@ export async function configToCsfFactory( const defineConfigProps = getConfigProperties(exportDecls, { configType }); const hasNamedExports = defineConfigProps.length > 0; + function findDeclarationNodeIndex(declarationName: string): number { + return programNode.body.findIndex( + (n) => + t.isVariableDeclaration(n) && + n.declarations.some((d) => { + let declaration = d.init; + // unwrap TS type annotations + if (t.isTSAsExpression(declaration) || t.isTSSatisfiesExpression(declaration)) { + declaration = declaration.expression; + } + return ( + t.isIdentifier(d.id) && + d.id.name === declarationName && + t.isObjectExpression(declaration) + ); + }) + ); + } + /** * Scenario 1: Mixed exports * @@ -60,16 +79,7 @@ export async function configToCsfFactory( if (t.isExportDefaultDeclaration(node) && t.isIdentifier(node.declaration)) { const declarationName = node.declaration.name; - declarationNodeIndex = programNode.body.findIndex( - (n) => - t.isVariableDeclaration(n) && - n.declarations.some( - (d) => - t.isIdentifier(d.id) && - d.id.name === declarationName && - t.isObjectExpression(d.init) - ) - ); + declarationNodeIndex = findDeclarationNodeIndex(declarationName); if (declarationNodeIndex !== -1) { exportDefaultNode = node; @@ -97,7 +107,7 @@ export async function configToCsfFactory( /** * Scenario 2: Default exports * - * - Syntax 1: `default export const config = {}; export default config;` + * - Syntax 1: `const config = {}; export default config;` * - Syntax 2: `export default {};` * * Transform into: `export default defineMain({})` @@ -112,16 +122,7 @@ export async function configToCsfFactory( if (t.isExportDefaultDeclaration(node) && t.isIdentifier(node.declaration)) { const declarationName = node.declaration.name; - declarationNodeIndex = programNode.body.findIndex( - (n) => - t.isVariableDeclaration(n) && - n.declarations.some( - (d) => - t.isIdentifier(d.id) && - d.id.name === declarationName && - t.isObjectExpression(d.init) - ) - ); + declarationNodeIndex = findDeclarationNodeIndex(declarationName); if (declarationNodeIndex !== -1) { exportDefaultNode = node;