@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.
Files changed (33) hide show
  1. package/README.md +0 -39
  2. package/dist/chunk-YH46OF24.mjs +2 -0
  3. package/dist/index.mjs +1 -1
  4. package/dist/transforms/csf-2-to-3.d.ts +4 -2
  5. package/dist/transforms/csf-2-to-3.js +3 -1
  6. package/dist/transforms/csf-2-to-3.mjs +2 -1
  7. package/dist/transforms/upgrade-deprecated-types.d.ts +10 -0
  8. package/dist/transforms/upgrade-deprecated-types.js +2 -0
  9. package/dist/transforms/upgrade-deprecated-types.mjs +1 -0
  10. package/jest.config.js +1 -0
  11. package/package.json +10 -9
  12. package/src/transforms/__tests__/csf-2-to-3.test.ts +174 -58
  13. package/src/transforms/__tests__/upgrade-deprecated-types.test.ts +170 -0
  14. package/src/transforms/csf-2-to-3.ts +144 -23
  15. package/src/transforms/upgrade-deprecated-types.ts +142 -0
  16. package/dist/transforms/csf-to-mdx.d.ts +0 -29
  17. package/dist/transforms/csf-to-mdx.js +0 -3
  18. package/dist/transforms/csf-to-mdx.mjs +0 -3
  19. package/src/transforms/__testfixtures__/csf-to-mdx/basic.input.js +0 -20
  20. package/src/transforms/__testfixtures__/csf-to-mdx/basic.output.snapshot +0 -18
  21. package/src/transforms/__testfixtures__/csf-to-mdx/component-id.input.js +0 -9
  22. package/src/transforms/__testfixtures__/csf-to-mdx/component-id.output.snapshot +0 -10
  23. package/src/transforms/__testfixtures__/csf-to-mdx/decorators.input.js +0 -13
  24. package/src/transforms/__testfixtures__/csf-to-mdx/decorators.output.snapshot +0 -12
  25. package/src/transforms/__testfixtures__/csf-to-mdx/exclude-stories.input.js +0 -23
  26. package/src/transforms/__testfixtures__/csf-to-mdx/exclude-stories.output.snapshot +0 -22
  27. package/src/transforms/__testfixtures__/csf-to-mdx/parameters.input.js +0 -16
  28. package/src/transforms/__testfixtures__/csf-to-mdx/parameters.output.snapshot +0 -17
  29. package/src/transforms/__testfixtures__/csf-to-mdx/story-function.input.js +0 -19
  30. package/src/transforms/__testfixtures__/csf-to-mdx/story-function.output.snapshot +0 -18
  31. package/src/transforms/__testfixtures__/csf-to-mdx/story-parameters.input.js +0 -24
  32. package/src/transforms/__testfixtures__/csf-to-mdx/story-parameters.output.snapshot +0 -22
  33. 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{packageNames,transformer}from"./chunk-3OPQTROG.mjs";import{jscodeshiftToPrettierParser}from"./chunk-HBPKIMKE.mjs";import{transformer as transformer2}from"./chunk-B5FMQ3BX.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
+ 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, Options } from 'jscodeshift';
1
+ import { FileInfo, API } from 'jscodeshift';
2
2
 
3
- declare function transform({ source, path }: FileInfo, api: API, options: Options): string;
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 import_prettier=__toESM(require("prettier")),t=__toESM(require("@babel/types")),import_csf_tools=require("@storybook/csf-tools"),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)=>{var _a;if((_a=csf._meta)!=null&&_a.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)&&param.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({source,path},api,options){let csf=(0,import_csf_tools.loadCsf)(source,{makeTitle:userTitle=>userTitle||"FIXME"});try{csf.parse()}catch(err){return logger.log(`Error ${err}, skipping`),source}let 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||isSimpleCSFStory(init,annotations))return;let storyFn=template&&csf._templates[template];storyFn||(storyFn=init);let keyId=t.identifier(key),{typeAnnotation}=id;typeAnnotation&&(keyId.typeAnnotation=typeAnnotation);let renderAnnotation=isReactGlobalRenderFn(csf,storyFn)?[]:[t.objectProperty(t.identifier("render"),storyFn)];objectExports[key]=t.exportNamedDeclaration(t.variableDeclaration("const",[t.variableDeclarator(keyId,t.objectExpression([...renderAnnotation,...annotations]))]))}});let updatedBody=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)},[]);csf._ast.program.body=updatedBody;let output=(0,import_csf_tools.formatCsf)(csf);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:path})}catch{logger.log(`Failed applying prettier to ${path}.`)}return output}var parser="tsx";0&&(module.exports={parser});
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)&&param.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{formatCsf,loadCsf}from"@storybook/csf-tools";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)&&param.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({source,path},api,options){let csf=loadCsf(source,{makeTitle:userTitle=>userTitle||"FIXME"});try{csf.parse()}catch(err){return logger.log(`Error ${err}, skipping`),source}let 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||isSimpleCSFStory(init,annotations))return;let storyFn=template&&csf._templates[template];storyFn||(storyFn=init);let keyId=t.identifier(key),{typeAnnotation}=id;typeAnnotation&&(keyId.typeAnnotation=typeAnnotation);let renderAnnotation=isReactGlobalRenderFn(csf,storyFn)?[]:[t.objectProperty(t.identifier("render"),storyFn)];objectExports[key]=t.exportNamedDeclaration(t.variableDeclaration("const",[t.variableDeclarator(keyId,t.objectExpression([...renderAnnotation,...annotations]))]))}});let updatedBody=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)},[]);csf._ast.program.body=updatedBody;let output=formatCsf(csf);try{let prettierConfig=prettier.resolveConfig.sync(".",{editorconfig:!0})||{printWidth:100,tabWidth:2,bracketSpacing:!0,trailingComma:"es5",singleQuote:!0};output=prettier.format(output,{...prettierConfig,filepath:path})}catch{logger.log(`Failed applying prettier to ${path}.`)}return output}var parser="tsx";export{transform as default,parser};
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)&&param.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
@@ -1,6 +1,7 @@
1
1
  const path = require('path');
2
2
  const baseConfig = require('../../jest.config.node');
3
3
 
4
+ /** @type {import('jest').Config} */
4
5
  module.exports = {
5
6
  ...baseConfig,
6
7
  displayName: __dirname.split(path.sep).slice(-2).join(path.posix.sep),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storybook/codemod",
3
- "version": "7.0.0-beta.27",
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.2",
51
+ "@babel/types": "^7.20.7",
52
52
  "@storybook/csf": "next",
53
- "@storybook/csf-tools": "7.0.0-beta.27",
54
- "@storybook/node-logger": "7.0.0-beta.27",
55
- "@storybook/types": "7.0.0-beta.27",
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.19.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": "1abdc0888cba8f23b12a8b9a777f4af6ed15ffe2"
89
+ "gitHead": "c6b2e1a65b1a0f65c52819929344602708212a59"
89
90
  }
@@ -1,5 +1,7 @@
1
- // import { describe, it, expect } from '@jest/globals';
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' }, null, {}).trim();
13
+ _transform({ source, path: 'Component.stories.js' }, {} as API, {}).trim();
12
14
  const tsTransform = (source: string) =>
13
- _transform({ source, path: 'Component.stories.ts' }, null, { parser: 'tsx' }).trim();
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
- title: 'Cat',
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
- title: 'components/Fruit',
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
- title: 'Cat',
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
- title: 'Cat',
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
- title: 'Cat',
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
- title: 'Cat',
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
- title: 'Cat',
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 replace function exports with objects', () => {
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
- export default { title: 'Cat' } as Meta<CatProps>;
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
- export const A: Story<CatProps> = {
243
- render: (args) => <Cat {...args} />,
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
- export const C: Story<CatProps> = () => <Cat />;
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
  });