@storybook/codemod 7.0.0-beta.27 → 7.0.0-beta.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -39
- package/dist/chunk-YH46OF24.mjs +2 -0
- package/dist/index.mjs +1 -1
- package/dist/transforms/csf-2-to-3.d.ts +4 -2
- package/dist/transforms/csf-2-to-3.js +3 -1
- package/dist/transforms/csf-2-to-3.mjs +2 -1
- package/dist/transforms/upgrade-deprecated-types.d.ts +10 -0
- package/dist/transforms/upgrade-deprecated-types.js +2 -0
- package/dist/transforms/upgrade-deprecated-types.mjs +1 -0
- package/jest.config.js +1 -0
- package/package.json +10 -9
- package/src/transforms/__tests__/csf-2-to-3.test.ts +174 -58
- package/src/transforms/__tests__/upgrade-deprecated-types.test.ts +170 -0
- package/src/transforms/csf-2-to-3.ts +144 -23
- package/src/transforms/upgrade-deprecated-types.ts +142 -0
- package/dist/transforms/csf-to-mdx.d.ts +0 -29
- package/dist/transforms/csf-to-mdx.js +0 -3
- package/dist/transforms/csf-to-mdx.mjs +0 -3
- package/src/transforms/__testfixtures__/csf-to-mdx/basic.input.js +0 -20
- package/src/transforms/__testfixtures__/csf-to-mdx/basic.output.snapshot +0 -18
- package/src/transforms/__testfixtures__/csf-to-mdx/component-id.input.js +0 -9
- package/src/transforms/__testfixtures__/csf-to-mdx/component-id.output.snapshot +0 -10
- package/src/transforms/__testfixtures__/csf-to-mdx/decorators.input.js +0 -13
- package/src/transforms/__testfixtures__/csf-to-mdx/decorators.output.snapshot +0 -12
- package/src/transforms/__testfixtures__/csf-to-mdx/exclude-stories.input.js +0 -23
- package/src/transforms/__testfixtures__/csf-to-mdx/exclude-stories.output.snapshot +0 -22
- package/src/transforms/__testfixtures__/csf-to-mdx/parameters.input.js +0 -16
- package/src/transforms/__testfixtures__/csf-to-mdx/parameters.output.snapshot +0 -17
- package/src/transforms/__testfixtures__/csf-to-mdx/story-function.input.js +0 -19
- package/src/transforms/__testfixtures__/csf-to-mdx/story-function.output.snapshot +0 -18
- package/src/transforms/__testfixtures__/csf-to-mdx/story-parameters.input.js +0 -24
- package/src/transforms/__testfixtures__/csf-to-mdx/story-parameters.output.snapshot +0 -22
- package/src/transforms/csf-to-mdx.js +0 -190
package/README.md
CHANGED
@@ -201,45 +201,6 @@ Heuristics:
|
|
201
201
|
- If a file has any default export, it will be skipped
|
202
202
|
- If a file has multiple `storiesOf` declarations, it will convert each one separately. This generates invalid ES6, but you can edit the file by hand to split it into multiple files (or whatever is appropriate).
|
203
203
|
|
204
|
-
### csf-to-mdx
|
205
|
-
|
206
|
-
This converts all of your CSF Component Stories into MDX syntax, which integrates story examples and long-form documentation.
|
207
|
-
|
208
|
-
> NOTE: The output of this transformation may require manual editing after running the transformation. MDX is finnicky about the top-level statements it allows. For example, [variables should be defined with exports](https://mdxjs.com/getting-started/#defining-variables-with-exports), meaning `const foo = 5;` should be rewritten as `export const foo = 5;`. We don't do this transformation automatically, since you may prefer to refactor your stories.
|
209
|
-
|
210
|
-
```sh
|
211
|
-
./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/csf-to-mdx.js . --ignore-pattern "node_modules|dist"
|
212
|
-
```
|
213
|
-
|
214
|
-
For example:
|
215
|
-
|
216
|
-
```js
|
217
|
-
export default {
|
218
|
-
title: 'Button',
|
219
|
-
};
|
220
|
-
|
221
|
-
export const story = () => <Button label="Story 1" />;
|
222
|
-
|
223
|
-
export const story2 = () => <Button label="Story 2" onClick={action('click')} />;
|
224
|
-
story2.story = { name: 'second story' };
|
225
|
-
```
|
226
|
-
|
227
|
-
Becomes:
|
228
|
-
|
229
|
-
```md
|
230
|
-
import { Meta, Story } from '@storybook/addon-docs';
|
231
|
-
|
232
|
-
# Button
|
233
|
-
|
234
|
-
<Meta title='Button'>
|
235
|
-
|
236
|
-
<Story name='story'><Button label="Story 1" /></Story>
|
237
|
-
|
238
|
-
<Story name='second story'>
|
239
|
-
<Button label="Story 2" onClick={action('click')} />
|
240
|
-
</Story>
|
241
|
-
```
|
242
|
-
|
243
204
|
### upgrade-hierarchy-separators
|
244
205
|
|
245
206
|
Starting in 5.3, Storybook is moving to using a single path separator, `/`, to specify the story hierarchy. It previously defaulted to `|` for story "roots" (optional) and either `/` or `.` for denoting paths. This codemod updates the old default to the new default.
|
@@ -0,0 +1,2 @@
|
|
1
|
+
import prettier from"prettier";import*as babel from"@babel/core";import{loadCsf}from"@storybook/csf-tools";import*as recast from"recast";import*as t from"@babel/types";var logger=console,deprecatedTypes=["ComponentStory","ComponentStoryFn","ComponentStoryObj","ComponentMeta","Story"];function migrateType(oldType){return oldType==="Story"||oldType==="ComponentStory"?"StoryFn":oldType.replace("Component","")}function transform(info,api,options){let fileNode=loadCsf(info.source,{makeTitle:title=>title})._ast,file=new babel.File({filename:info.path},{code:info.source,ast:fileNode});upgradeDeprecatedTypes(file);let output=recast.print(file.path.node).code;try{let prettierConfig=prettier.resolveConfig.sync(".",{editorconfig:!0})||{printWidth:100,tabWidth:2,bracketSpacing:!0,trailingComma:"es5",singleQuote:!0};output=prettier.format(output,{...prettierConfig,filepath:info.path})}catch{logger.log(`Failed applying prettier to ${info.path}.`)}return output}var parser="tsx";function upgradeDeprecatedTypes(file){let importedNamespaces=new Set,typeReferencesToUpdate=new Set,existingImports=[];file.path.traverse({ImportDeclaration:path=>{existingImports.push(...path.get("specifiers").map(specifier=>({name:specifier.node.local.name,isAlias:!(specifier.isImportSpecifier()&&t.isIdentifier(specifier.node.imported)&&specifier.node.local.name===specifier.node.imported.name),path:specifier}))),path.node.source.value.startsWith("@storybook")&&path.get("specifiers").forEach(specifier=>{if(specifier.isImportNamespaceSpecifier()&&importedNamespaces.add(specifier.node.local.name),!specifier.isImportSpecifier())return;let imported=specifier.get("imported");if(imported.isIdentifier()&&deprecatedTypes.includes(imported.node.name)){imported.node.name===specifier.node.local.name&&typeReferencesToUpdate.add(specifier.node.local.name);let newType=migrateType(imported.node.name);if(!existingImports.some(it=>it.name===newType))imported.replaceWith(t.identifier(newType)),existingImports.push({name:newType,isAlias:!1,path:specifier});else{let existingImport=existingImports.find(it=>it.name===newType&&it.isAlias);if(existingImport)throw existingImport.path.buildCodeFrameError(`This codemod does not support local imports that are called the same as a storybook import.
|
2
|
+
Rename this local import and try again.`);specifier.remove()}}})}}),file.path.traverse({TSTypeReference:path=>{let typeName=path.get("typeName");if(typeName.isIdentifier())typeReferencesToUpdate.has(typeName.node.name)&&typeName.replaceWith(t.identifier(migrateType(typeName.node.name)));else if(typeName.isTSQualifiedName()){let namespace=typeName.get("left");if(namespace.isIdentifier()&&importedNamespaces.has(namespace.node.name)){let right=typeName.get("right");deprecatedTypes.includes(right.node.name)&&right.replaceWith(t.identifier(migrateType(right.node.name)))}}}})}export{transform,parser,upgradeDeprecatedTypes};
|
package/dist/index.mjs
CHANGED
@@ -1 +1 @@
|
|
1
|
-
import{
|
1
|
+
import{jscodeshiftToPrettierParser}from"./chunk-HBPKIMKE.mjs";import{transformer as transformer2}from"./chunk-B5FMQ3BX.mjs";import{packageNames,transformer}from"./chunk-3OPQTROG.mjs";import fs from"fs";import path from"path";import{promisify}from"util";import globby from"globby";import{sync as spawnSync}from"cross-spawn";var TRANSFORM_DIR=`${__dirname}/transforms`;function listCodemods(){return fs.readdirSync(TRANSFORM_DIR).filter(fname=>fname.endsWith(".js")).map(fname=>fname.slice(0,-3))}var renameAsync=promisify(fs.rename);async function renameFile(file,from,to,{logger}){let newFile=file.replace(from,to);return logger.log(`Rename: ${file} ${newFile}`),renameAsync(file,newFile)}async function runCodemod(codemod,{glob,logger,dryRun,rename,parser}){if(!listCodemods().includes(codemod))throw new Error(`Unknown codemod ${codemod}. Run --list for options.`);let renameParts=null;if(rename&&(renameParts=rename.split(":"),renameParts.length!==2))throw new Error(`Codemod rename: expected format "from:to", got "${rename}"`);let inferredParser=parser;if(!parser){let extension=path.extname(glob).slice(1);jscodeshiftToPrettierParser(extension)!=="babel"&&(inferredParser=extension)}let files=await globby([glob,"!**/node_modules","!**/dist"]);if(logger.log(`=> Applying ${codemod}: ${files.length} files`),!dryRun){let parserArgs=inferredParser?["--parser",inferredParser]:[];spawnSync("npx",["jscodeshift","--no-babel","-t",`${TRANSFORM_DIR}/${codemod}.js`,...parserArgs,...files],{stdio:"inherit",shell:!0})}if(renameParts){let[from,to]=renameParts;logger.log(`=> Renaming ${rename}: ${files.length} files`),await Promise.all(files.map(file=>renameFile(file,new RegExp(`${from}$`),to,{logger})))}}export{listCodemods,packageNames,runCodemod,transformer2 as updateAddonInfo,transformer as updateOrganisationName};
|
@@ -1,6 +1,8 @@
|
|
1
|
-
import { FileInfo, API
|
1
|
+
import { FileInfo, API } from 'jscodeshift';
|
2
2
|
|
3
|
-
declare function transform(
|
3
|
+
declare function transform(info: FileInfo, api: API, options: {
|
4
|
+
parser?: string;
|
5
|
+
}): string;
|
4
6
|
declare const parser = "tsx";
|
5
7
|
|
6
8
|
export { transform as default, parser };
|
@@ -1 +1,3 @@
|
|
1
|
-
var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf,__hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toESM=(mod,isNodeMode,target)=>(target=mod!=null?__create(__getProtoOf(mod)):{},__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:!0}):target,mod)),__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var csf_2_to_3_exports={};__export(csf_2_to_3_exports,{default:()=>transform,parser:()=>parser});module.exports=__toCommonJS(csf_2_to_3_exports);var
|
1
|
+
var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf,__hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toESM=(mod,isNodeMode,target)=>(target=mod!=null?__create(__getProtoOf(mod)):{},__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:!0}):target,mod)),__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var csf_2_to_3_exports={};__export(csf_2_to_3_exports,{default:()=>transform,parser:()=>parser});module.exports=__toCommonJS(csf_2_to_3_exports);var import_prettier2=__toESM(require("prettier")),t2=__toESM(require("@babel/types")),import_types=require("@babel/types"),import_csf_tools2=require("@storybook/csf-tools"),babel2=__toESM(require("@babel/core")),recast2=__toESM(require("recast"));var import_prettier=__toESM(require("prettier")),babel=__toESM(require("@babel/core")),import_csf_tools=require("@storybook/csf-tools"),recast=__toESM(require("recast")),t=__toESM(require("@babel/types"));var deprecatedTypes=["ComponentStory","ComponentStoryFn","ComponentStoryObj","ComponentMeta","Story"];function migrateType(oldType){return oldType==="Story"||oldType==="ComponentStory"?"StoryFn":oldType.replace("Component","")}function upgradeDeprecatedTypes(file){let importedNamespaces=new Set,typeReferencesToUpdate=new Set,existingImports=[];file.path.traverse({ImportDeclaration:path=>{existingImports.push(...path.get("specifiers").map(specifier=>({name:specifier.node.local.name,isAlias:!(specifier.isImportSpecifier()&&t.isIdentifier(specifier.node.imported)&&specifier.node.local.name===specifier.node.imported.name),path:specifier}))),path.node.source.value.startsWith("@storybook")&&path.get("specifiers").forEach(specifier=>{if(specifier.isImportNamespaceSpecifier()&&importedNamespaces.add(specifier.node.local.name),!specifier.isImportSpecifier())return;let imported=specifier.get("imported");if(imported.isIdentifier()&&deprecatedTypes.includes(imported.node.name)){imported.node.name===specifier.node.local.name&&typeReferencesToUpdate.add(specifier.node.local.name);let newType=migrateType(imported.node.name);if(!existingImports.some(it=>it.name===newType))imported.replaceWith(t.identifier(newType)),existingImports.push({name:newType,isAlias:!1,path:specifier});else{let existingImport=existingImports.find(it=>it.name===newType&&it.isAlias);if(existingImport)throw existingImport.path.buildCodeFrameError(`This codemod does not support local imports that are called the same as a storybook import.
|
2
|
+
Rename this local import and try again.`);specifier.remove()}}})}}),file.path.traverse({TSTypeReference:path=>{let typeName=path.get("typeName");if(typeName.isIdentifier())typeReferencesToUpdate.has(typeName.node.name)&&typeName.replaceWith(t.identifier(migrateType(typeName.node.name)));else if(typeName.isTSQualifiedName()){let namespace=typeName.get("left");if(namespace.isIdentifier()&&importedNamespaces.has(namespace.node.name)){let right=typeName.get("right");deprecatedTypes.includes(right.node.name)&&right.replaceWith(t.identifier(migrateType(right.node.name)))}}}})}var logger=console,renameAnnotation=annotation=>annotation==="storyName"?"name":annotation,getTemplateBindVariable=init=>t2.isCallExpression(init)&&t2.isMemberExpression(init.callee)&&t2.isIdentifier(init.callee.object)&&t2.isIdentifier(init.callee.property)&&init.callee.property.name==="bind"&&(init.arguments.length===0||init.arguments.length===1&&t2.isObjectExpression(init.arguments[0])&&init.arguments[0].properties.length===0)?init.callee.object.name:null,isStoryAnnotation=(stmt,objectExports)=>t2.isExpressionStatement(stmt)&&t2.isAssignmentExpression(stmt.expression)&&t2.isMemberExpression(stmt.expression.left)&&t2.isIdentifier(stmt.expression.left.object)&&objectExports[stmt.expression.left.object.name],isTemplateDeclaration=(stmt,templates)=>t2.isVariableDeclaration(stmt)&&stmt.declarations.length===1&&t2.isIdentifier(stmt.declarations[0].id)&&templates[stmt.declarations[0].id.name],getNewExport=(stmt,objectExports)=>{if(t2.isExportNamedDeclaration(stmt)&&t2.isVariableDeclaration(stmt.declaration)&&stmt.declaration.declarations.length===1){let decl=stmt.declaration.declarations[0];if(t2.isVariableDeclarator(decl)&&t2.isIdentifier(decl.id))return objectExports[decl.id.name]}return null},isReactGlobalRenderFn=(csf,storyFn)=>{var _a;if((_a=csf._meta)!=null&&_a.component&&t2.isArrowFunctionExpression(storyFn)&&storyFn.params.length===1&&t2.isJSXElement(storyFn.body)){let{openingElement}=storyFn.body;if(openingElement.selfClosing&&t2.isJSXIdentifier(openingElement.name)&&openingElement.attributes.length===1){let attr=openingElement.attributes[0],param=storyFn.params[0];if(t2.isJSXSpreadAttribute(attr)&&t2.isIdentifier(attr.argument)&&t2.isIdentifier(param)&¶m.name===attr.argument.name&&csf._meta.component===openingElement.name.name)return!0}}return!1},isSimpleCSFStory=(init,annotations)=>annotations.length===0&&t2.isArrowFunctionExpression(init)&&init.params.length===0;function transform(info,api,options){let makeTitle=userTitle=>userTitle||"FIXME",csf=(0,import_csf_tools2.loadCsf)(info.source,{makeTitle});try{csf.parse()}catch(err){return logger.log(`Error ${err}, skipping`),info.source}let file=new babel2.File({filename:info.path},{code:info.source,ast:csf._ast}),importHelper=new StorybookImportHelper(file,info),objectExports={};Object.entries(csf._storyExports).forEach(([key,decl])=>{let annotations=Object.entries(csf._storyAnnotations[key]).map(([annotation,val])=>t2.objectProperty(t2.identifier(renameAnnotation(annotation)),val));if(t2.isVariableDeclarator(decl)){let{init,id}=decl,template=getTemplateBindVariable(init);if(!t2.isArrowFunctionExpression(init)&&!template)return;if(isSimpleCSFStory(init,annotations)){objectExports[key]=t2.exportNamedDeclaration(t2.variableDeclaration("const",[t2.variableDeclarator(importHelper.updateTypeTo(id,"StoryFn"),init)]));return}let storyFn=template&&csf._templates[template];storyFn||(storyFn=init);let renderAnnotation=isReactGlobalRenderFn(csf,storyFn)?[]:[t2.objectProperty(t2.identifier("render"),storyFn)];objectExports[key]=t2.exportNamedDeclaration(t2.variableDeclaration("const",[t2.variableDeclarator(importHelper.updateTypeTo(id,"StoryObj"),t2.objectExpression([...renderAnnotation,...annotations]))]))}}),importHelper.removeDeprecatedStoryImport(),csf._ast.program.body=csf._ast.program.body.reduce((acc,stmt)=>{if(isStoryAnnotation(stmt,objectExports)||isTemplateDeclaration(stmt,csf._templates))return acc;let newExport=getNewExport(stmt,objectExports);return newExport?(acc.push(newExport),acc):(acc.push(stmt),acc)},[]),upgradeDeprecatedTypes(file);let output=recast2.print(csf._ast,{}).code;try{let prettierConfig=import_prettier2.default.resolveConfig.sync(".",{editorconfig:!0})||{printWidth:100,tabWidth:2,bracketSpacing:!0,trailingComma:"es5",singleQuote:!0};output=import_prettier2.default.format(output,{...prettierConfig,filepath:info.path})}catch{logger.log(`Failed applying prettier to ${info.path}.`)}return output}var StorybookImportHelper=class{constructor(file,info){this.getAllSbImportDeclarations=file=>{let found=[];return file.path.traverse({ImportDeclaration:path=>{let source=path.node.source.value;if(source.startsWith("@storybook/csf")||!source.startsWith("@storybook"))return;path.get("specifiers").some(specifier=>{if(specifier.isImportNamespaceSpecifier())throw path.buildCodeFrameError(`This codemod does not support namespace imports for a ${path.node.source.value} package.
|
3
|
+
Replace the namespace import with named imports and try again.`);if(!specifier.isImportSpecifier())return!1;let imported=specifier.get("imported");return imported.isIdentifier()?["Story","StoryFn","StoryObj","Meta","ComponentStory","ComponentStoryFn","ComponentStoryObj","ComponentMeta"].includes(imported.node.name):!1})&&found.push(path)}}),found};this.getOrAddImport=type=>{let sbImport=this.sbImportDeclarations.find(path=>path.node.importKind==="type")??this.sbImportDeclarations[0];if(sbImport==null)return;let specifiers=sbImport.get("specifiers"),importSpecifier2=specifiers.find(specifier=>{if(!specifier.isImportSpecifier())return!1;let imported=specifier.get("imported");return imported.isIdentifier()?imported.node.name===type:!1});return importSpecifier2?importSpecifier2.node.local.name:(specifiers[0].insertBefore(t2.importSpecifier(t2.identifier(type),t2.identifier(type))),type)};this.removeDeprecatedStoryImport=()=>{this.sbImportDeclarations.flatMap(it=>it.get("specifiers")).filter(specifier=>{if(!specifier.isImportSpecifier())return!1;let imported=specifier.get("imported");return imported.isIdentifier()?imported.node.name==="Story":!1}).forEach(path=>path.remove())};this.getAllLocalImports=()=>this.sbImportDeclarations.flatMap(it=>it.get("specifiers")).map(it=>it.node.local.name);this.updateTypeTo=(id,type)=>{if((0,import_types.isIdentifier)(id)&&(0,import_types.isTSTypeAnnotation)(id.typeAnnotation)&&(0,import_types.isTSTypeReference)(id.typeAnnotation.typeAnnotation)&&(0,import_types.isIdentifier)(id.typeAnnotation.typeAnnotation.typeName)){let{name}=id.typeAnnotation.typeAnnotation.typeName;if(this.getAllLocalImports().includes(name)){let localTypeImport=this.getOrAddImport(type);return{...id,typeAnnotation:t2.tsTypeAnnotation(t2.tsTypeReference(t2.identifier(localTypeImport),id.typeAnnotation.typeAnnotation.typeParameters))}}}return id};this.sbImportDeclarations=this.getAllSbImportDeclarations(file)}},parser="tsx";0&&(module.exports={parser});
|
@@ -1 +1,2 @@
|
|
1
|
-
import prettier from"prettier";import*as t from"@babel/types";import{
|
1
|
+
import{upgradeDeprecatedTypes}from"../chunk-YH46OF24.mjs";import prettier from"prettier";import*as t from"@babel/types";import{isIdentifier as isIdentifier2,isTSTypeAnnotation,isTSTypeReference}from"@babel/types";import{loadCsf}from"@storybook/csf-tools";import*as babel from"@babel/core";import*as recast from"recast";var logger=console,renameAnnotation=annotation=>annotation==="storyName"?"name":annotation,getTemplateBindVariable=init=>t.isCallExpression(init)&&t.isMemberExpression(init.callee)&&t.isIdentifier(init.callee.object)&&t.isIdentifier(init.callee.property)&&init.callee.property.name==="bind"&&(init.arguments.length===0||init.arguments.length===1&&t.isObjectExpression(init.arguments[0])&&init.arguments[0].properties.length===0)?init.callee.object.name:null,isStoryAnnotation=(stmt,objectExports)=>t.isExpressionStatement(stmt)&&t.isAssignmentExpression(stmt.expression)&&t.isMemberExpression(stmt.expression.left)&&t.isIdentifier(stmt.expression.left.object)&&objectExports[stmt.expression.left.object.name],isTemplateDeclaration=(stmt,templates)=>t.isVariableDeclaration(stmt)&&stmt.declarations.length===1&&t.isIdentifier(stmt.declarations[0].id)&&templates[stmt.declarations[0].id.name],getNewExport=(stmt,objectExports)=>{if(t.isExportNamedDeclaration(stmt)&&t.isVariableDeclaration(stmt.declaration)&&stmt.declaration.declarations.length===1){let decl=stmt.declaration.declarations[0];if(t.isVariableDeclarator(decl)&&t.isIdentifier(decl.id))return objectExports[decl.id.name]}return null},isReactGlobalRenderFn=(csf,storyFn)=>{if(csf._meta?.component&&t.isArrowFunctionExpression(storyFn)&&storyFn.params.length===1&&t.isJSXElement(storyFn.body)){let{openingElement}=storyFn.body;if(openingElement.selfClosing&&t.isJSXIdentifier(openingElement.name)&&openingElement.attributes.length===1){let attr=openingElement.attributes[0],param=storyFn.params[0];if(t.isJSXSpreadAttribute(attr)&&t.isIdentifier(attr.argument)&&t.isIdentifier(param)&¶m.name===attr.argument.name&&csf._meta.component===openingElement.name.name)return!0}}return!1},isSimpleCSFStory=(init,annotations)=>annotations.length===0&&t.isArrowFunctionExpression(init)&&init.params.length===0;function transform(info,api,options){let makeTitle=userTitle=>userTitle||"FIXME",csf=loadCsf(info.source,{makeTitle});try{csf.parse()}catch(err){return logger.log(`Error ${err}, skipping`),info.source}let file=new babel.File({filename:info.path},{code:info.source,ast:csf._ast}),importHelper=new StorybookImportHelper(file,info),objectExports={};Object.entries(csf._storyExports).forEach(([key,decl])=>{let annotations=Object.entries(csf._storyAnnotations[key]).map(([annotation,val])=>t.objectProperty(t.identifier(renameAnnotation(annotation)),val));if(t.isVariableDeclarator(decl)){let{init,id}=decl,template=getTemplateBindVariable(init);if(!t.isArrowFunctionExpression(init)&&!template)return;if(isSimpleCSFStory(init,annotations)){objectExports[key]=t.exportNamedDeclaration(t.variableDeclaration("const",[t.variableDeclarator(importHelper.updateTypeTo(id,"StoryFn"),init)]));return}let storyFn=template&&csf._templates[template];storyFn||(storyFn=init);let renderAnnotation=isReactGlobalRenderFn(csf,storyFn)?[]:[t.objectProperty(t.identifier("render"),storyFn)];objectExports[key]=t.exportNamedDeclaration(t.variableDeclaration("const",[t.variableDeclarator(importHelper.updateTypeTo(id,"StoryObj"),t.objectExpression([...renderAnnotation,...annotations]))]))}}),importHelper.removeDeprecatedStoryImport(),csf._ast.program.body=csf._ast.program.body.reduce((acc,stmt)=>{if(isStoryAnnotation(stmt,objectExports)||isTemplateDeclaration(stmt,csf._templates))return acc;let newExport=getNewExport(stmt,objectExports);return newExport?(acc.push(newExport),acc):(acc.push(stmt),acc)},[]),upgradeDeprecatedTypes(file);let output=recast.print(csf._ast,{}).code;try{let prettierConfig=prettier.resolveConfig.sync(".",{editorconfig:!0})||{printWidth:100,tabWidth:2,bracketSpacing:!0,trailingComma:"es5",singleQuote:!0};output=prettier.format(output,{...prettierConfig,filepath:info.path})}catch{logger.log(`Failed applying prettier to ${info.path}.`)}return output}var StorybookImportHelper=class{constructor(file,info){this.getAllSbImportDeclarations=file=>{let found=[];return file.path.traverse({ImportDeclaration:path=>{let source=path.node.source.value;if(source.startsWith("@storybook/csf")||!source.startsWith("@storybook"))return;path.get("specifiers").some(specifier=>{if(specifier.isImportNamespaceSpecifier())throw path.buildCodeFrameError(`This codemod does not support namespace imports for a ${path.node.source.value} package.
|
2
|
+
Replace the namespace import with named imports and try again.`);if(!specifier.isImportSpecifier())return!1;let imported=specifier.get("imported");return imported.isIdentifier()?["Story","StoryFn","StoryObj","Meta","ComponentStory","ComponentStoryFn","ComponentStoryObj","ComponentMeta"].includes(imported.node.name):!1})&&found.push(path)}}),found};this.getOrAddImport=type=>{let sbImport=this.sbImportDeclarations.find(path=>path.node.importKind==="type")??this.sbImportDeclarations[0];if(sbImport==null)return;let specifiers=sbImport.get("specifiers"),importSpecifier2=specifiers.find(specifier=>{if(!specifier.isImportSpecifier())return!1;let imported=specifier.get("imported");return imported.isIdentifier()?imported.node.name===type:!1});return importSpecifier2?importSpecifier2.node.local.name:(specifiers[0].insertBefore(t.importSpecifier(t.identifier(type),t.identifier(type))),type)};this.removeDeprecatedStoryImport=()=>{this.sbImportDeclarations.flatMap(it=>it.get("specifiers")).filter(specifier=>{if(!specifier.isImportSpecifier())return!1;let imported=specifier.get("imported");return imported.isIdentifier()?imported.node.name==="Story":!1}).forEach(path=>path.remove())};this.getAllLocalImports=()=>this.sbImportDeclarations.flatMap(it=>it.get("specifiers")).map(it=>it.node.local.name);this.updateTypeTo=(id,type)=>{if(isIdentifier2(id)&&isTSTypeAnnotation(id.typeAnnotation)&&isTSTypeReference(id.typeAnnotation.typeAnnotation)&&isIdentifier2(id.typeAnnotation.typeAnnotation.typeName)){let{name}=id.typeAnnotation.typeAnnotation.typeName;if(this.getAllLocalImports().includes(name)){let localTypeImport=this.getOrAddImport(type);return{...id,typeAnnotation:t.tsTypeAnnotation(t.tsTypeReference(t.identifier(localTypeImport),id.typeAnnotation.typeAnnotation.typeParameters))}}}return id};this.sbImportDeclarations=this.getAllSbImportDeclarations(file)}},parser="tsx";export{transform as default,parser};
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import { FileInfo, API } from 'jscodeshift';
|
2
|
+
import { BabelFile } from '@babel/core';
|
3
|
+
|
4
|
+
declare function transform(info: FileInfo, api: API, options: {
|
5
|
+
parser?: string;
|
6
|
+
}): string;
|
7
|
+
declare const parser = "tsx";
|
8
|
+
declare function upgradeDeprecatedTypes(file: BabelFile): void;
|
9
|
+
|
10
|
+
export { transform as default, parser, upgradeDeprecatedTypes };
|
@@ -0,0 +1,2 @@
|
|
1
|
+
var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf,__hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toESM=(mod,isNodeMode,target)=>(target=mod!=null?__create(__getProtoOf(mod)):{},__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:!0}):target,mod)),__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var upgrade_deprecated_types_exports={};__export(upgrade_deprecated_types_exports,{default:()=>transform,parser:()=>parser,upgradeDeprecatedTypes:()=>upgradeDeprecatedTypes});module.exports=__toCommonJS(upgrade_deprecated_types_exports);var import_prettier=__toESM(require("prettier")),babel=__toESM(require("@babel/core")),import_csf_tools=require("@storybook/csf-tools"),recast=__toESM(require("recast")),t=__toESM(require("@babel/types")),logger=console,deprecatedTypes=["ComponentStory","ComponentStoryFn","ComponentStoryObj","ComponentMeta","Story"];function migrateType(oldType){return oldType==="Story"||oldType==="ComponentStory"?"StoryFn":oldType.replace("Component","")}function transform(info,api,options){let fileNode=(0,import_csf_tools.loadCsf)(info.source,{makeTitle:title=>title})._ast,file=new babel.File({filename:info.path},{code:info.source,ast:fileNode});upgradeDeprecatedTypes(file);let output=recast.print(file.path.node).code;try{let prettierConfig=import_prettier.default.resolveConfig.sync(".",{editorconfig:!0})||{printWidth:100,tabWidth:2,bracketSpacing:!0,trailingComma:"es5",singleQuote:!0};output=import_prettier.default.format(output,{...prettierConfig,filepath:info.path})}catch{logger.log(`Failed applying prettier to ${info.path}.`)}return output}var parser="tsx";function upgradeDeprecatedTypes(file){let importedNamespaces=new Set,typeReferencesToUpdate=new Set,existingImports=[];file.path.traverse({ImportDeclaration:path=>{existingImports.push(...path.get("specifiers").map(specifier=>({name:specifier.node.local.name,isAlias:!(specifier.isImportSpecifier()&&t.isIdentifier(specifier.node.imported)&&specifier.node.local.name===specifier.node.imported.name),path:specifier}))),path.node.source.value.startsWith("@storybook")&&path.get("specifiers").forEach(specifier=>{if(specifier.isImportNamespaceSpecifier()&&importedNamespaces.add(specifier.node.local.name),!specifier.isImportSpecifier())return;let imported=specifier.get("imported");if(imported.isIdentifier()&&deprecatedTypes.includes(imported.node.name)){imported.node.name===specifier.node.local.name&&typeReferencesToUpdate.add(specifier.node.local.name);let newType=migrateType(imported.node.name);if(!existingImports.some(it=>it.name===newType))imported.replaceWith(t.identifier(newType)),existingImports.push({name:newType,isAlias:!1,path:specifier});else{let existingImport=existingImports.find(it=>it.name===newType&&it.isAlias);if(existingImport)throw existingImport.path.buildCodeFrameError(`This codemod does not support local imports that are called the same as a storybook import.
|
2
|
+
Rename this local import and try again.`);specifier.remove()}}})}}),file.path.traverse({TSTypeReference:path=>{let typeName=path.get("typeName");if(typeName.isIdentifier())typeReferencesToUpdate.has(typeName.node.name)&&typeName.replaceWith(t.identifier(migrateType(typeName.node.name)));else if(typeName.isTSQualifiedName()){let namespace=typeName.get("left");if(namespace.isIdentifier()&&importedNamespaces.has(namespace.node.name)){let right=typeName.get("right");deprecatedTypes.includes(right.node.name)&&right.replaceWith(t.identifier(migrateType(right.node.name)))}}}})}0&&(module.exports={parser,upgradeDeprecatedTypes});
|
@@ -0,0 +1 @@
|
|
1
|
+
import{parser,transform,upgradeDeprecatedTypes}from"../chunk-YH46OF24.mjs";export{transform as default,parser,upgradeDeprecatedTypes};
|
package/jest.config.js
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@storybook/codemod",
|
3
|
-
"version": "7.0.0-beta.
|
3
|
+
"version": "7.0.0-beta.29",
|
4
4
|
"description": "A collection of codemod scripts written with JSCodeshift",
|
5
5
|
"keywords": [
|
6
6
|
"storybook"
|
@@ -30,11 +30,11 @@
|
|
30
30
|
"./dist/transforms/add-component-parameters.js": "./dist/transforms/add-component-parameters.js",
|
31
31
|
"./dist/transforms/csf-2-to-3.js": "./dist/transforms/csf-2-to-3.js",
|
32
32
|
"./dist/transforms/csf-hoist-story-annotations.js": "./dist/transforms/csf-hoist-story-annotations.js",
|
33
|
-
"./dist/transforms/csf-to-mdx.js": "./dist/transforms/csf-to-mdx.js",
|
34
33
|
"./dist/transforms/move-builtin-addons.js": "./dist/transforms/move-builtin-addons.js",
|
35
34
|
"./dist/transforms/storiesof-to-csf.js": "./dist/transforms/storiesof-to-csf.js",
|
36
35
|
"./dist/transforms/update-addon-info.js": "./dist/transforms/update-addon-info.js",
|
37
36
|
"./dist/transforms/update-organisation-name.js": "./dist/transforms/update-organisation-name.js",
|
37
|
+
"./dist/transforms/upgrade-deprecated-types.js": "./dist/transforms/upgrade-deprecated-types.js",
|
38
38
|
"./dist/transforms/upgrade-hierarchy-separators.js": "./dist/transforms/upgrade-hierarchy-separators.js",
|
39
39
|
"./package.json": "./package.json"
|
40
40
|
},
|
@@ -48,21 +48,22 @@
|
|
48
48
|
"dependencies": {
|
49
49
|
"@babel/core": "^7.20.2",
|
50
50
|
"@babel/preset-env": "^7.20.2",
|
51
|
-
"@babel/types": "^7.20.
|
51
|
+
"@babel/types": "^7.20.7",
|
52
52
|
"@storybook/csf": "next",
|
53
|
-
"@storybook/csf-tools": "7.0.0-beta.
|
54
|
-
"@storybook/node-logger": "7.0.0-beta.
|
55
|
-
"@storybook/types": "7.0.0-beta.
|
53
|
+
"@storybook/csf-tools": "7.0.0-beta.29",
|
54
|
+
"@storybook/node-logger": "7.0.0-beta.29",
|
55
|
+
"@storybook/types": "7.0.0-beta.29",
|
56
56
|
"cross-spawn": "^7.0.3",
|
57
57
|
"globby": "^11.0.2",
|
58
58
|
"jscodeshift": "^0.13.1",
|
59
59
|
"lodash": "^4.17.21",
|
60
60
|
"prettier": "^2.8.0",
|
61
|
-
"recast": "^0.
|
61
|
+
"recast": "^0.23.1",
|
62
62
|
"util": "^0.12.4"
|
63
63
|
},
|
64
64
|
"devDependencies": {
|
65
65
|
"@types/jscodeshift": "^0.11.6",
|
66
|
+
"ansi-regex": "^5.0.1",
|
66
67
|
"jest": "^29.3.1",
|
67
68
|
"jest-specific-snapshot": "^7.0.0",
|
68
69
|
"typescript": "~4.9.3"
|
@@ -77,13 +78,13 @@
|
|
77
78
|
"./src/transforms/add-component-parameters.js",
|
78
79
|
"./src/transforms/csf-2-to-3.ts",
|
79
80
|
"./src/transforms/csf-hoist-story-annotations.js",
|
80
|
-
"./src/transforms/csf-to-mdx.js",
|
81
81
|
"./src/transforms/move-builtin-addons.js",
|
82
82
|
"./src/transforms/storiesof-to-csf.js",
|
83
83
|
"./src/transforms/update-addon-info.js",
|
84
84
|
"./src/transforms/update-organisation-name.js",
|
85
|
+
"./src/transforms/upgrade-deprecated-types.ts",
|
85
86
|
"./src/transforms/upgrade-hierarchy-separators.js"
|
86
87
|
]
|
87
88
|
},
|
88
|
-
"gitHead": "
|
89
|
+
"gitHead": "c6b2e1a65b1a0f65c52819929344602708212a59"
|
89
90
|
}
|
@@ -1,5 +1,7 @@
|
|
1
|
-
|
1
|
+
import { describe, it, expect } from '@jest/globals';
|
2
2
|
import { dedent } from 'ts-dedent';
|
3
|
+
import type { API } from 'jscodeshift';
|
4
|
+
import ansiRegex from 'ansi-regex';
|
3
5
|
import _transform from '../csf-2-to-3';
|
4
6
|
|
5
7
|
expect.addSnapshotSerializer({
|
@@ -8,9 +10,9 @@ expect.addSnapshotSerializer({
|
|
8
10
|
});
|
9
11
|
|
10
12
|
const jsTransform = (source: string) =>
|
11
|
-
_transform({ source, path: 'Component.stories.js' },
|
13
|
+
_transform({ source, path: 'Component.stories.js' }, {} as API, {}).trim();
|
12
14
|
const tsTransform = (source: string) =>
|
13
|
-
_transform({ source, path: 'Component.stories.ts' },
|
15
|
+
_transform({ source, path: 'Component.stories.ts' }, {} as API, { parser: 'tsx' }).trim();
|
14
16
|
|
15
17
|
describe('csf-2-to-3', () => {
|
16
18
|
describe('javascript', () => {
|
@@ -22,9 +24,7 @@ describe('csf-2-to-3', () => {
|
|
22
24
|
export const B = (args) => <Button {...args} />;
|
23
25
|
`)
|
24
26
|
).toMatchInlineSnapshot(`
|
25
|
-
export default {
|
26
|
-
title: 'Cat',
|
27
|
-
};
|
27
|
+
export default { title: 'Cat' };
|
28
28
|
export const A = () => <Cat />;
|
29
29
|
export const B = {
|
30
30
|
render: (args) => <Button {...args} />,
|
@@ -36,21 +36,19 @@ describe('csf-2-to-3', () => {
|
|
36
36
|
expect(
|
37
37
|
jsTransform(dedent`
|
38
38
|
export default { title: 'Cat' };
|
39
|
+
|
39
40
|
export const A = () => <Cat />;
|
40
41
|
A.storyName = 'foo';
|
41
42
|
A.parameters = { bar: 2 };
|
42
43
|
A.play = () => {};
|
43
44
|
`)
|
44
45
|
).toMatchInlineSnapshot(`
|
45
|
-
export default {
|
46
|
-
|
47
|
-
};
|
46
|
+
export default { title: 'Cat' };
|
47
|
+
|
48
48
|
export const A = {
|
49
49
|
render: () => <Cat />,
|
50
50
|
name: 'foo',
|
51
|
-
parameters: {
|
52
|
-
bar: 2,
|
53
|
-
},
|
51
|
+
parameters: { bar: 2 },
|
54
52
|
play: () => {},
|
55
53
|
};
|
56
54
|
`);
|
@@ -60,19 +58,22 @@ describe('csf-2-to-3', () => {
|
|
60
58
|
expect(
|
61
59
|
jsTransform(dedent`
|
62
60
|
export default { title: 'components/Fruit', includeStories: ['A'] };
|
61
|
+
|
63
62
|
export const A = (args) => <Apple {...args} />;
|
63
|
+
|
64
64
|
export const B = (args) => <Banana {...args} />;
|
65
|
+
|
65
66
|
const C = (args) => <Cherry {...args} />;
|
66
67
|
`)
|
67
68
|
).toMatchInlineSnapshot(`
|
68
|
-
export default {
|
69
|
-
|
70
|
-
includeStories: ['A'],
|
71
|
-
};
|
69
|
+
export default { title: 'components/Fruit', includeStories: ['A'] };
|
70
|
+
|
72
71
|
export const A = {
|
73
72
|
render: (args) => <Apple {...args} />,
|
74
73
|
};
|
74
|
+
|
75
75
|
export const B = (args) => <Banana {...args} />;
|
76
|
+
|
76
77
|
const C = (args) => <Cherry {...args} />;
|
77
78
|
`);
|
78
79
|
});
|
@@ -81,10 +82,12 @@ describe('csf-2-to-3', () => {
|
|
81
82
|
expect(
|
82
83
|
jsTransform(dedent`
|
83
84
|
export const A = () => <Apple />;
|
85
|
+
|
84
86
|
export const B = (args) => <Banana {...args} />;
|
85
87
|
`)
|
86
88
|
).toMatchInlineSnapshot(`
|
87
89
|
export const A = () => <Apple />;
|
90
|
+
|
88
91
|
export const B = (args) => <Banana {...args} />;
|
89
92
|
`);
|
90
93
|
});
|
@@ -97,10 +100,7 @@ describe('csf-2-to-3', () => {
|
|
97
100
|
export const B = (args) => <Banana {...args} />;
|
98
101
|
`)
|
99
102
|
).toMatchInlineSnapshot(`
|
100
|
-
export default {
|
101
|
-
title: 'Cat',
|
102
|
-
component: Cat,
|
103
|
-
};
|
103
|
+
export default { title: 'Cat', component: Cat };
|
104
104
|
export const A = {};
|
105
105
|
export const B = {
|
106
106
|
render: (args) => <Banana {...args} />,
|
@@ -112,15 +112,14 @@ describe('csf-2-to-3', () => {
|
|
112
112
|
expect(
|
113
113
|
jsTransform(dedent`
|
114
114
|
export default { title: 'Cat', component: Cat };
|
115
|
+
|
115
116
|
export const A = {
|
116
117
|
render: (args) => <Cat {...args} />
|
117
118
|
};
|
118
119
|
`)
|
119
120
|
).toMatchInlineSnapshot(`
|
120
|
-
export default {
|
121
|
-
|
122
|
-
component: Cat,
|
123
|
-
};
|
121
|
+
export default { title: 'Cat', component: Cat };
|
122
|
+
|
124
123
|
export const A = {
|
125
124
|
render: (args) => <Cat {...args} />,
|
126
125
|
};
|
@@ -136,14 +135,11 @@ describe('csf-2-to-3', () => {
|
|
136
135
|
A.args = { isPrimary: false };
|
137
136
|
`)
|
138
137
|
).toMatchInlineSnapshot(`
|
139
|
-
export default {
|
140
|
-
|
141
|
-
};
|
138
|
+
export default { title: 'Cat' };
|
139
|
+
|
142
140
|
export const A = {
|
143
141
|
render: (args) => <Cat {...args} />,
|
144
|
-
args: {
|
145
|
-
isPrimary: false,
|
146
|
-
},
|
142
|
+
args: { isPrimary: false },
|
147
143
|
};
|
148
144
|
`);
|
149
145
|
});
|
@@ -152,28 +148,27 @@ describe('csf-2-to-3', () => {
|
|
152
148
|
expect(
|
153
149
|
jsTransform(dedent`
|
154
150
|
export default { title: 'Cat', component: Cat };
|
151
|
+
|
155
152
|
const Template = (args) => <Cat {...args} />;
|
153
|
+
|
156
154
|
export const A = Template.bind({});
|
157
155
|
A.args = { isPrimary: false };
|
156
|
+
|
158
157
|
const Template2 = (args) => <Banana {...args} />;
|
158
|
+
|
159
159
|
export const B = Template2.bind({});
|
160
160
|
B.args = { isPrimary: true };
|
161
161
|
`)
|
162
162
|
).toMatchInlineSnapshot(`
|
163
|
-
export default {
|
164
|
-
|
165
|
-
component: Cat,
|
166
|
-
};
|
163
|
+
export default { title: 'Cat', component: Cat };
|
164
|
+
|
167
165
|
export const A = {
|
168
|
-
args: {
|
169
|
-
isPrimary: false,
|
170
|
-
},
|
166
|
+
args: { isPrimary: false },
|
171
167
|
};
|
168
|
+
|
172
169
|
export const B = {
|
173
170
|
render: (args) => <Banana {...args} />,
|
174
|
-
args: {
|
175
|
-
isPrimary: true,
|
176
|
-
},
|
171
|
+
args: { isPrimary: true },
|
177
172
|
};
|
178
173
|
`);
|
179
174
|
});
|
@@ -182,23 +177,21 @@ describe('csf-2-to-3', () => {
|
|
182
177
|
expect(
|
183
178
|
jsTransform(dedent`
|
184
179
|
export default { title: 'Cat', component: Cat };
|
180
|
+
|
185
181
|
export const A = (args) => <Cat {...args} />;
|
186
182
|
export const B = () => <Cat name="frisky" />;
|
187
183
|
export const C = () => <Cat name="fluffy" />;
|
188
184
|
C.parameters = { foo: 2 };
|
189
185
|
`)
|
190
186
|
).toMatchInlineSnapshot(`
|
191
|
-
export default {
|
192
|
-
|
193
|
-
component: Cat,
|
194
|
-
};
|
187
|
+
export default { title: 'Cat', component: Cat };
|
188
|
+
|
195
189
|
export const A = {};
|
196
190
|
export const B = () => <Cat name="frisky" />;
|
191
|
+
|
197
192
|
export const C = {
|
198
193
|
render: () => <Cat name="fluffy" />,
|
199
|
-
parameters: {
|
200
|
-
foo: 2,
|
201
|
-
},
|
194
|
+
parameters: { foo: 2 },
|
202
195
|
};
|
203
196
|
`);
|
204
197
|
});
|
@@ -213,39 +206,162 @@ describe('csf-2-to-3', () => {
|
|
213
206
|
};
|
214
207
|
`)
|
215
208
|
).toMatchInlineSnapshot(`
|
216
|
-
export default {
|
217
|
-
|
218
|
-
};
|
209
|
+
export default { title: 'Cat' };
|
210
|
+
|
219
211
|
export const A = {
|
220
212
|
render: (args) => <Cat {...args} />,
|
221
|
-
parameters: {
|
222
|
-
foo: 2,
|
223
|
-
},
|
213
|
+
parameters: { foo: 2 },
|
224
214
|
};
|
225
215
|
`);
|
226
216
|
});
|
227
217
|
});
|
228
218
|
|
229
219
|
describe('typescript', () => {
|
230
|
-
it('should
|
220
|
+
it('should error with namespace imports', () => {
|
221
|
+
expect.addSnapshotSerializer({
|
222
|
+
serialize: (value) => value.replace(ansiRegex(), ''),
|
223
|
+
test: () => true,
|
224
|
+
});
|
225
|
+
expect(() =>
|
226
|
+
tsTransform(dedent`
|
227
|
+
import * as SB from '@storybook/react';
|
228
|
+
import { CatProps } from './Cat';
|
229
|
+
|
230
|
+
const meta = { title: 'Cat', component: Cat } as Meta<CatProps>
|
231
|
+
export default meta;
|
232
|
+
|
233
|
+
export const A: SB.StoryFn<CatProps> = () => <Cat />;
|
234
|
+
`)
|
235
|
+
).toThrowErrorMatchingInlineSnapshot(`
|
236
|
+
This codemod does not support namespace imports for a @storybook/react package.
|
237
|
+
Replace the namespace import with named imports and try again.
|
238
|
+
> 1 | import * as SB from '@storybook/react';
|
239
|
+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
240
|
+
2 | import { CatProps } from './Cat';
|
241
|
+
3 |
|
242
|
+
4 | const meta = { title: 'Cat', component: Cat } as Meta<CatProps>
|
243
|
+
`);
|
244
|
+
});
|
245
|
+
it('should keep local names', () => {
|
246
|
+
expect(
|
247
|
+
tsTransform(dedent`
|
248
|
+
import { Meta, StoryObj as CSF3, StoryFn as CSF2 } from '@storybook/react';
|
249
|
+
import { CatProps } from './Cat';
|
250
|
+
|
251
|
+
const meta = { title: 'Cat', component: Cat } satisfies Meta<CatProps>
|
252
|
+
export default meta;
|
253
|
+
|
254
|
+
export const A: CSF2<CatProps> = () => <Cat />;
|
255
|
+
|
256
|
+
export const B: CSF3<CatProps> = {
|
257
|
+
args: { name: "already csf3" }
|
258
|
+
};
|
259
|
+
|
260
|
+
export const C: CSF2<CatProps> = (args) => <Cat {...args} />;
|
261
|
+
C.args = {
|
262
|
+
name: "Fluffy"
|
263
|
+
};
|
264
|
+
`)
|
265
|
+
).toMatchInlineSnapshot(`
|
266
|
+
import { Meta, StoryObj as CSF3, StoryFn as CSF2 } from '@storybook/react';
|
267
|
+
import { CatProps } from './Cat';
|
268
|
+
|
269
|
+
const meta = { title: 'Cat', component: Cat } satisfies Meta<CatProps>;
|
270
|
+
export default meta;
|
271
|
+
|
272
|
+
export const A: CSF2<CatProps> = () => <Cat />;
|
273
|
+
|
274
|
+
export const B: CSF3<CatProps> = {
|
275
|
+
args: { name: 'already csf3' },
|
276
|
+
};
|
277
|
+
|
278
|
+
export const C: CSF3<CatProps> = {
|
279
|
+
args: {
|
280
|
+
name: 'Fluffy',
|
281
|
+
},
|
282
|
+
};
|
283
|
+
`);
|
284
|
+
});
|
285
|
+
|
286
|
+
it('should replace function exports with objects and update type', () => {
|
231
287
|
expect(
|
232
288
|
tsTransform(dedent`
|
233
|
-
|
289
|
+
import { Story, StoryFn, ComponentStory, ComponentStoryObj } from '@storybook/react';
|
290
|
+
|
291
|
+
// some extra whitespace to test
|
292
|
+
|
293
|
+
export default {
|
294
|
+
title: 'Cat',
|
295
|
+
component: Cat,
|
296
|
+
} as Meta<CatProps>;
|
297
|
+
|
234
298
|
export const A: Story<CatProps> = (args) => <Cat {...args} />;
|
299
|
+
A.args = { name: "Fluffy" };
|
300
|
+
|
235
301
|
export const B: any = (args) => <Button {...args} />;
|
302
|
+
|
236
303
|
export const C: Story<CatProps> = () => <Cat />;
|
304
|
+
|
305
|
+
export const D: StoryFn<CatProps> = (args) => <Cat {...args} />;
|
306
|
+
D.args = {
|
307
|
+
name: "Fluffy"
|
308
|
+
};
|
309
|
+
|
310
|
+
export const E: ComponentStory<Cat> = (args) => <Cat {...args} />;
|
311
|
+
E.args = { name: "Fluffy" };
|
312
|
+
|
313
|
+
export const F: Story = (args) => <Cat {...args} />;
|
314
|
+
F.args = {
|
315
|
+
name: "Fluffy"
|
316
|
+
};
|
317
|
+
|
318
|
+
export const G: ComponentStoryObj<typeof Cat> = {
|
319
|
+
args: {
|
320
|
+
name: 'Fluffy',
|
321
|
+
},
|
322
|
+
};
|
237
323
|
`)
|
238
324
|
).toMatchInlineSnapshot(`
|
325
|
+
import { StoryObj, StoryFn } from '@storybook/react';
|
326
|
+
|
327
|
+
// some extra whitespace to test
|
328
|
+
|
239
329
|
export default {
|
240
330
|
title: 'Cat',
|
331
|
+
component: Cat,
|
241
332
|
} as Meta<CatProps>;
|
242
|
-
|
243
|
-
|
333
|
+
|
334
|
+
export const A: StoryObj<CatProps> = {
|
335
|
+
args: { name: 'Fluffy' },
|
244
336
|
};
|
337
|
+
|
245
338
|
export const B: any = {
|
246
339
|
render: (args) => <Button {...args} />,
|
247
340
|
};
|
248
|
-
|
341
|
+
|
342
|
+
export const C: StoryFn<CatProps> = () => <Cat />;
|
343
|
+
|
344
|
+
export const D: StoryObj<CatProps> = {
|
345
|
+
args: {
|
346
|
+
name: 'Fluffy',
|
347
|
+
},
|
348
|
+
};
|
349
|
+
|
350
|
+
export const E: StoryObj<Cat> = {
|
351
|
+
args: { name: 'Fluffy' },
|
352
|
+
};
|
353
|
+
|
354
|
+
export const F: StoryObj = {
|
355
|
+
args: {
|
356
|
+
name: 'Fluffy',
|
357
|
+
},
|
358
|
+
};
|
359
|
+
|
360
|
+
export const G: StoryObj<typeof Cat> = {
|
361
|
+
args: {
|
362
|
+
name: 'Fluffy',
|
363
|
+
},
|
364
|
+
};
|
249
365
|
`);
|
250
366
|
});
|
251
367
|
});
|