@storybook/codemod 7.0.0-beta.28 → 7.0.0-beta.29

Sign up to get free protection for your applications and to get access to all the features.
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.28",
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.28",
54
- "@storybook/node-logger": "7.0.0-beta.28",
55
- "@storybook/types": "7.0.0-beta.28",
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": "05179df2dcf366ea1e61b0809e7778afbec094de"
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
  });