@storybook/codemod 7.0.0-rc.0 → 7.0.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/transforms/csf-2-to-3.js +1 -1
- package/dist/transforms/mdx-to-csf.js +1 -1
- package/package.json +5 -5
- package/src/transforms/__tests__/csf-2-to-3.test.ts +78 -2
- package/src/transforms/__tests__/mdx-to-csf.test.ts +22 -5
- package/src/transforms/csf-2-to-3.ts +33 -17
- package/src/transforms/mdx-to-csf.ts +9 -11
@@ -1,3 +1,3 @@
|
|
1
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],
|
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],getNewExport=(stmt,objectExports)=>{if(t2.isExportNamedDeclaration(stmt)&&t2.isVariableDeclaration(stmt.declaration)&&stmt.declaration.declarations.length===1){let decl=stmt.declaration.declarations[0];if(t2.isVariableDeclarator(decl)&&t2.isIdentifier(decl.id))return objectExports[decl.id.name]}return null},isReactGlobalRenderFn=(csf,storyFn)=>{var _a;if((_a=csf._meta)!=null&&_a.component&&t2.isArrowFunctionExpression(storyFn)&&storyFn.params.length===1&&t2.isJSXElement(storyFn.body)){let{openingElement}=storyFn.body;if(openingElement.selfClosing&&t2.isJSXIdentifier(openingElement.name)&&openingElement.attributes.length===1){let attr=openingElement.attributes[0],param=storyFn.params[0];if(t2.isJSXSpreadAttribute(attr)&&t2.isIdentifier(attr.argument)&&t2.isIdentifier(param)&¶m.name===attr.argument.name&&csf._meta.component===openingElement.name.name)return!0}}return!1},isSimpleCSFStory=(init,annotations)=>annotations.length===0&&t2.isArrowFunctionExpression(init)&&init.params.length===0;function removeUnusedTemplates(csf){Object.entries(csf._templates).forEach(([template,templateExpression])=>{let references=[];if(babel2.traverse(csf._ast,{Identifier:path=>{path.node.name===template&&references.push(path)}}),references.length===1){let reference=references[0];reference.parentPath.isVariableDeclarator()&&reference.parentPath.node.init===templateExpression&&reference.parentPath.remove()}})}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&&t2.identifier(template);storyFn||(storyFn=init);let renderAnnotation=isReactGlobalRenderFn(csf,template?csf._templates[template]:storyFn)?[]:[t2.objectProperty(t2.identifier("render"),storyFn)];objectExports[key]=t2.exportNamedDeclaration(t2.variableDeclaration("const",[t2.variableDeclarator(importHelper.updateTypeTo(id,"StoryObj"),t2.objectExpression([...renderAnnotation,...annotations]))]))}}),csf._ast.program.body=csf._ast.program.body.reduce((acc,stmt)=>{let statement=stmt;if(isStoryAnnotation(statement,objectExports))return acc;let newExport=getNewExport(statement,objectExports);return newExport?(acc.push(newExport),acc):(acc.push(statement),acc)},[]),upgradeDeprecatedTypes(file),importHelper.removeDeprecatedStoryImport(),removeUnusedTemplates(csf);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 new Error(`This codemod does not support namespace imports for a ${path.node.source.value} package.
|
3
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});
|
@@ -49,7 +49,7 @@ Defaulting to 2020, but this will stop working in the future.`)),options.ecmaVer
|
|
49
49
|
`),map3)):attributesOnOneLine&&(value+=tracker.move(" "+attributesOnOneLine)),attributesOnTheirOwnLine&&(value+=tracker.move(`
|
50
50
|
`)),selfClosing&&(value+=tracker.move((tightSelfClosing||attributesOnTheirOwnLine?"":" ")+"/")),value+=tracker.move(">"),node3.children&&node3.children.length>0&&(node3.type==="mdxJsxFlowElement"?(tracker.shift(2),value+=tracker.move(`
|
51
51
|
`),value+=tracker.move(indentLines(containerFlow(node3,context,tracker.current()),map3)),value+=tracker.move(`
|
52
|
-
`)):value+=tracker.move(containerPhrasing(node3,context,{...tracker.current(),before:"<",after:">"}))),selfClosing||(value+=tracker.move("</"+(node3.name||"")+">")),exit2(),value}function map3(line,_,blank){return(blank?"":" ")+line}function peekElement(){return"<"}}var mdxjsEsmFromMarkdown={enter:{mdxjsEsm:enterMdxjsEsm},exit:{mdxjsEsm:exitMdxjsEsm,mdxjsEsmData:exitMdxjsEsmData}},mdxjsEsmToMarkdown={handlers:{mdxjsEsm:handleMdxjsEsm}};function enterMdxjsEsm(token){this.enter({type:"mdxjsEsm",value:""},token),this.buffer()}function exitMdxjsEsm(token){let value=this.resume(),node3=this.exit(token),estree=token.estree;node3.value=value,estree&&(node3.data={estree})}function exitMdxjsEsmData(token){this.config.enter.data.call(this,token),this.config.exit.data.call(this,token)}function handleMdxjsEsm(node3){return node3.value||""}function mdxFromMarkdown(){return[mdxExpressionFromMarkdown,mdxJsxFromMarkdown(),mdxjsEsmFromMarkdown]}function mdxToMarkdown(options){return{extensions:[mdxExpressionToMarkdown,mdxJsxToMarkdown(options),mdxjsEsmToMarkdown]}}function remarkMdx(options){let data2=this.data();add("micromarkExtensions",mdxjs(options)),add("fromMarkdownExtensions",mdxFromMarkdown()),add("toMarkdownExtensions",mdxToMarkdown(options));function add(field,value){(data2[field]?data2[field]:data2[field]=[]).push(value)}}var t=__toESM(require("@babel/types")),babel=__toESM(require("@babel/core")),recast=__toESM(require("recast")),path=__toESM(require("path")),import_prettier=__toESM(require("prettier")),fs=__toESM(require("fs")),import_camelCase=__toESM(require("lodash/camelCase")),mdxProcessor=remark().use(remarkMdx);function jscodeshift(info){let parsed=path.parse(info.path),baseName=path.join(parsed.dir,parsed.name.replace(".mdx","").replace(".stories","").replace(".story",""));for(;fs.existsSync(`${baseName}.stories.js`);)baseName+="_";let result=transform(info.source,path.basename(baseName))
|
52
|
+
`)):value+=tracker.move(containerPhrasing(node3,context,{...tracker.current(),before:"<",after:">"}))),selfClosing||(value+=tracker.move("</"+(node3.name||"")+">")),exit2(),value}function map3(line,_,blank){return(blank?"":" ")+line}function peekElement(){return"<"}}var mdxjsEsmFromMarkdown={enter:{mdxjsEsm:enterMdxjsEsm},exit:{mdxjsEsm:exitMdxjsEsm,mdxjsEsmData:exitMdxjsEsmData}},mdxjsEsmToMarkdown={handlers:{mdxjsEsm:handleMdxjsEsm}};function enterMdxjsEsm(token){this.enter({type:"mdxjsEsm",value:""},token),this.buffer()}function exitMdxjsEsm(token){let value=this.resume(),node3=this.exit(token),estree=token.estree;node3.value=value,estree&&(node3.data={estree})}function exitMdxjsEsmData(token){this.config.enter.data.call(this,token),this.config.exit.data.call(this,token)}function handleMdxjsEsm(node3){return node3.value||""}function mdxFromMarkdown(){return[mdxExpressionFromMarkdown,mdxJsxFromMarkdown(),mdxjsEsmFromMarkdown]}function mdxToMarkdown(options){return{extensions:[mdxExpressionToMarkdown,mdxJsxToMarkdown(options),mdxjsEsmToMarkdown]}}function remarkMdx(options){let data2=this.data();add("micromarkExtensions",mdxjs(options)),add("fromMarkdownExtensions",mdxFromMarkdown()),add("toMarkdownExtensions",mdxToMarkdown(options));function add(field,value){(data2[field]?data2[field]:data2[field]=[]).push(value)}}var t=__toESM(require("@babel/types")),babel=__toESM(require("@babel/core")),recast=__toESM(require("recast")),path=__toESM(require("path")),import_prettier=__toESM(require("prettier")),fs=__toESM(require("fs")),import_camelCase=__toESM(require("lodash/camelCase")),mdxProcessor=remark().use(remarkMdx);function jscodeshift(info){let parsed=path.parse(info.path),baseName=path.join(parsed.dir,parsed.name.replace(".mdx","").replace(".stories","").replace(".story",""));for(;fs.existsSync(`${baseName}.stories.js`);)baseName+="_";let result=transform(info.source,path.basename(baseName)),[mdx,csf]=result;return csf!=null&&fs.writeFileSync(`${baseName}.stories.js`,csf),mdx}function transform(source,baseName){let root2=mdxProcessor.parse(source),storyNamespaceName=nameToValidExport(`${baseName}Stories`),metaAttributes=[],storiesMap=new Map;visit(root2,["mdxjsEsm"],node3=>{node3.value=node3.value.replaceAll("@storybook/addon-docs/blocks","@storybook/blocks").replaceAll("@storybook/addon-docs","@storybook/blocks")});let file=getEsmAst(root2);visit(root2,["mdxJsxFlowElement","mdxJsxTextElement"],(node3,index4,parent)=>{if(is(node3,{name:"Meta"})&&(metaAttributes.push(...node3.attributes),node3.attributes=[{type:"mdxJsxAttribute",name:"of",value:{type:"mdxJsxAttributeValueExpression",value:storyNamespaceName}}]),is(node3,{name:"Story"})){let nameAttribute=node3.attributes.find(it=>it.type==="mdxJsxAttribute"&&it.name==="name"),idAttribute=node3.attributes.find(it=>it.type==="mdxJsxAttribute"&&it.name==="id"),storyAttribute=node3.attributes.find(it=>it.type==="mdxJsxAttribute"&&it.name==="story");if(typeof(nameAttribute==null?void 0:nameAttribute.value)=="string"){let name2=nameToValidExport(nameAttribute.value);for(;variableNameExists(name2);)name2+="_";storiesMap.set(name2,{type:"value",attributes:node3.attributes,children:node3.children}),node3.attributes=[{type:"mdxJsxAttribute",name:"of",value:{type:"mdxJsxAttributeValueExpression",value:`${storyNamespaceName}.${name2}`}}],node3.children=[]}else if(idAttribute!=null&&idAttribute.value){let newNode={type:"mdxFlowExpression",value:`/* ${mdxProcessor.stringify({type:"root",children:[node3]}).trim()} is deprecated, please migrate it to <Story of={referenceToStory} /> see: https://storybook.js.org/migration-guides/7.0 */`};return storiesMap.set(idAttribute.value,{type:"id"}),parent.children.splice(index4,0,newNode),[SKIP,index4+2]}else if((storyAttribute==null?void 0:storyAttribute.type)==="mdxJsxAttribute"&&typeof storyAttribute.value=="object"&&storyAttribute.value.type==="mdxJsxAttributeValueExpression"){let name2=storyAttribute.value.value;node3.attributes=[{type:"mdxJsxAttribute",name:"of",value:{type:"mdxJsxAttributeValueExpression",value:`${storyNamespaceName}.${name2}`}}],node3.children=[],storiesMap.set(name2,{type:"reference"})}else return parent.children.splice(index4,1),[SKIP,index4]}});let metaProperties=metaAttributes.flatMap(attribute=>attribute.type==="mdxJsxAttribute"?typeof attribute.value=="string"?[t.objectProperty(t.identifier(attribute.name),t.stringLiteral(attribute.value))]:[t.objectProperty(t.identifier(attribute.name),(0,import_csf_tools.babelParseExpression)(attribute.value.value))]:[]);if(file.path.traverse({ImportDeclaration(path2){path2.node.source.value==="@storybook/blocks"&&path2.remove()},ExportNamedDeclaration(path2){path2.replaceWith(path2.node.declaration)}}),storiesMap.size===0&&metaAttributes.length===0)return[mdxProcessor.stringify(root2),null];addStoriesImport(root2,baseName,storyNamespaceName);let newStatements=[t.exportDefaultDeclaration(t.objectExpression(metaProperties))];function mapChildrenToRender(children){let child=children[0];if(!child)return;if(child.type==="text")return t.arrowFunctionExpression([],t.stringLiteral(child.value));if(child.type==="mdxFlowExpression"||child.type==="mdxTextExpression"){let expression2=(0,import_csf_tools.babelParseExpression)(child.value);return/\.bind\(.*\)/.test(child.value)||t.isIdentifier(expression2)||t.isArrowFunctionExpression(expression2)?expression2:t.arrowFunctionExpression([],expression2)}let expression=(0,import_csf_tools.babelParseExpression)(mdxProcessor.stringify({type:"root",children:[child]}));return t.arrowFunctionExpression([],expression)}function variableNameExists(name2){let found=!1;return file.path.traverse({VariableDeclarator:path2=>{let lVal=path2.node.id;t.isIdentifier(lVal)&&lVal.name===name2&&(found=!0)}}),found}newStatements.push(...[...storiesMap].flatMap(([key,value])=>{if(value.type==="id")return[];if(value.type==="reference")return[t.exportNamedDeclaration(null,[t.exportSpecifier(t.identifier(key),t.identifier(key))])];let renderProperty=mapChildrenToRender(value.children),newObject=t.objectExpression([...renderProperty?[t.objectProperty(t.identifier("render"),mapChildrenToRender(value.children))]:[],...value.attributes.flatMap(attribute=>attribute.type==="mdxJsxAttribute"?typeof attribute.value=="string"?[t.objectProperty(t.identifier(attribute.name),t.stringLiteral(attribute.value))]:[t.objectProperty(t.identifier(attribute.name),(0,import_csf_tools.babelParseExpression)(attribute.value.value))]:[])]);return[t.exportNamedDeclaration(t.variableDeclaration("const",[t.variableDeclarator(t.identifier(key),newObject)]))]})),file.path.node.body=[...file.path.node.body,...newStatements];let newMdx=mdxProcessor.stringify(root2),output=recast.print(file.path.node).code,prettierConfig=import_prettier.default.resolveConfig.sync(".",{editorconfig:!0})||{printWidth:100,tabWidth:2,bracketSpacing:!0,trailingComma:"es5",singleQuote:!0};return output=import_prettier.default.format(output,{...prettierConfig,filepath:"file.jsx"}),[newMdx,output]}function getEsmAst(root2){let esm=[];visit(root2,["mdxjsEsm"],node3=>{esm.push(node3.value)});let esmSource=`${esm.join(`
|
53
53
|
|
54
54
|
`)}`;return new babel.File({filename:"info.path"},{code:esmSource,ast:(0,import_csf_tools.babelParse)(esmSource)})}function addStoriesImport(root2,baseName,storyNamespaceName){let found=!1;visit(root2,["mdxjsEsm"],node3=>{found||(node3.value+=`
|
55
55
|
import * as ${storyNamespaceName} from './${baseName}.stories';`,found=!0)})}function nameToValidExport(name2){let[first,...rest]=Array.from((0,import_camelCase.default)(name2));return`${first.match(/[a-zA-Z_$]/)?first.toUpperCase():`$${first}`}${rest.join("")}`}0&&(module.exports={nameToValidExport,transform});
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@storybook/codemod",
|
3
|
-
"version": "7.0.0-rc.
|
3
|
+
"version": "7.0.0-rc.10",
|
4
4
|
"description": "A collection of codemod scripts written with JSCodeshift",
|
5
5
|
"keywords": [
|
6
6
|
"storybook"
|
@@ -49,9 +49,9 @@
|
|
49
49
|
"@babel/preset-env": "~7.20.2",
|
50
50
|
"@babel/types": "~7.21.2",
|
51
51
|
"@storybook/csf": "next",
|
52
|
-
"@storybook/csf-tools": "7.0.0-rc.
|
53
|
-
"@storybook/node-logger": "7.0.0-rc.
|
54
|
-
"@storybook/types": "7.0.0-rc.
|
52
|
+
"@storybook/csf-tools": "7.0.0-rc.10",
|
53
|
+
"@storybook/node-logger": "7.0.0-rc.10",
|
54
|
+
"@storybook/types": "7.0.0-rc.10",
|
55
55
|
"cross-spawn": "^7.0.3",
|
56
56
|
"globby": "^11.0.2",
|
57
57
|
"jscodeshift": "^0.14.0",
|
@@ -97,5 +97,5 @@
|
|
97
97
|
"cjs"
|
98
98
|
]
|
99
99
|
},
|
100
|
-
"gitHead": "
|
100
|
+
"gitHead": "dd10adf7821c47f0d3a2e6a45d80eea2b906c3a2"
|
101
101
|
}
|
@@ -136,14 +136,60 @@ describe('csf-2-to-3', () => {
|
|
136
136
|
`)
|
137
137
|
).toMatchInlineSnapshot(`
|
138
138
|
export default { title: 'Cat' };
|
139
|
+
const Template = (args) => <Cat {...args} />;
|
139
140
|
|
140
141
|
export const A = {
|
141
|
-
render:
|
142
|
+
render: Template,
|
142
143
|
args: { isPrimary: false },
|
143
144
|
};
|
144
145
|
`);
|
145
146
|
});
|
146
147
|
|
148
|
+
it('should reuse the template when there are multiple Template.bind references but no component defined', () => {
|
149
|
+
expect(
|
150
|
+
jsTransform(dedent`
|
151
|
+
export default { title: 'Cat' };
|
152
|
+
const Template = (args) => <Cat {...args} />;
|
153
|
+
|
154
|
+
export const A = Template.bind({});
|
155
|
+
A.args = { isPrimary: false };
|
156
|
+
|
157
|
+
export const B = Template.bind({});
|
158
|
+
B.args = { isPrimary: true };
|
159
|
+
|
160
|
+
|
161
|
+
export const C = Template.bind({});
|
162
|
+
C.args = { bla: true };
|
163
|
+
|
164
|
+
export const D = Template.bind({});
|
165
|
+
D.args = { bla: false };
|
166
|
+
`)
|
167
|
+
).toMatchInlineSnapshot(`
|
168
|
+
export default { title: 'Cat' };
|
169
|
+
const Template = (args) => <Cat {...args} />;
|
170
|
+
|
171
|
+
export const A = {
|
172
|
+
render: Template,
|
173
|
+
args: { isPrimary: false },
|
174
|
+
};
|
175
|
+
|
176
|
+
export const B = {
|
177
|
+
render: Template,
|
178
|
+
args: { isPrimary: true },
|
179
|
+
};
|
180
|
+
|
181
|
+
export const C = {
|
182
|
+
render: Template,
|
183
|
+
args: { bla: true },
|
184
|
+
};
|
185
|
+
|
186
|
+
export const D = {
|
187
|
+
render: Template,
|
188
|
+
args: { bla: false },
|
189
|
+
};
|
190
|
+
`);
|
191
|
+
});
|
192
|
+
|
147
193
|
it('should remove implicit global render for template.bind', () => {
|
148
194
|
expect(
|
149
195
|
jsTransform(dedent`
|
@@ -166,8 +212,10 @@ describe('csf-2-to-3', () => {
|
|
166
212
|
args: { isPrimary: false },
|
167
213
|
};
|
168
214
|
|
215
|
+
const Template2 = (args) => <Banana {...args} />;
|
216
|
+
|
169
217
|
export const B = {
|
170
|
-
render:
|
218
|
+
render: Template2,
|
171
219
|
args: { isPrimary: true },
|
172
220
|
};
|
173
221
|
`);
|
@@ -359,5 +407,33 @@ describe('csf-2-to-3', () => {
|
|
359
407
|
};
|
360
408
|
`);
|
361
409
|
});
|
410
|
+
|
411
|
+
it('migrate Story type to StoryFn when used in an not exported Template function', () => {
|
412
|
+
expect(
|
413
|
+
tsTransform(dedent`
|
414
|
+
import { Story, Meta } from '@storybook/react'
|
415
|
+
|
416
|
+
export default {
|
417
|
+
component: Cat,
|
418
|
+
} satisfies Meta
|
419
|
+
|
420
|
+
const Template: Story = () => <div>Hello World</div>;
|
421
|
+
|
422
|
+
export const Default = Template.bind({})
|
423
|
+
`)
|
424
|
+
).toMatchInlineSnapshot(`
|
425
|
+
import { StoryFn, Meta } from '@storybook/react';
|
426
|
+
|
427
|
+
export default {
|
428
|
+
component: Cat,
|
429
|
+
} satisfies Meta;
|
430
|
+
|
431
|
+
const Template: StoryFn = () => <div>Hello World</div>;
|
432
|
+
|
433
|
+
export const Default = {
|
434
|
+
render: Template,
|
435
|
+
};
|
436
|
+
`);
|
437
|
+
});
|
362
438
|
});
|
363
439
|
});
|
@@ -15,22 +15,39 @@ beforeEach(() => {
|
|
15
15
|
fs.existsSync.mockImplementation(() => false);
|
16
16
|
});
|
17
17
|
|
18
|
+
test('update import even when no stories can be extracted', () => {
|
19
|
+
const input = dedent`
|
20
|
+
import { Heading } from '@storybook/addon-docs';
|
21
|
+
|
22
|
+
<Heading />
|
23
|
+
`;
|
24
|
+
|
25
|
+
const mdx = jscodeshift({ source: input, path: 'Foobar.stories.mdx' });
|
26
|
+
|
27
|
+
expect(mdx).toMatchInlineSnapshot(`
|
28
|
+
import { Heading } from '@storybook/blocks';
|
29
|
+
|
30
|
+
<Heading />
|
31
|
+
|
32
|
+
`);
|
33
|
+
});
|
34
|
+
|
18
35
|
test('drop invalid story nodes', () => {
|
19
36
|
const input = dedent`
|
20
|
-
import { Meta } from '@storybook/addon-docs';
|
37
|
+
import { Meta, Story } from '@storybook/addon-docs';
|
21
38
|
|
22
39
|
<Meta title="Foobar" />
|
23
40
|
|
24
41
|
<Story>No name!</Story>
|
25
42
|
|
26
|
-
<Story name="Primary">Story</Story>
|
27
|
-
|
43
|
+
<Story name="Primary">Story</Story>
|
28
44
|
`;
|
29
45
|
|
30
46
|
const mdx = jscodeshift({ source: input, path: 'Foobar.stories.mdx' });
|
31
47
|
|
32
48
|
expect(mdx).toMatchInlineSnapshot(`
|
33
|
-
import { Meta } from '@storybook/
|
49
|
+
import { Meta, Story } from '@storybook/blocks';
|
50
|
+
import * as FoobarStories from './Foobar.stories';
|
34
51
|
|
35
52
|
<Meta of={FoobarStories} />
|
36
53
|
|
@@ -96,7 +113,7 @@ test('Comment out story nodes with id', () => {
|
|
96
113
|
|
97
114
|
<Meta of={FoobarStories} />
|
98
115
|
|
99
|
-
{/* <Story id="button--primary" /> is deprecated, please migrate it to <Story of={referenceToStory} /> */}
|
116
|
+
{/* <Story id="button--primary" /> is deprecated, please migrate it to <Story of={referenceToStory} /> see: https://storybook.js.org/migration-guides/7.0 */}
|
100
117
|
|
101
118
|
<Story id="button--primary" />
|
102
119
|
|
@@ -38,12 +38,6 @@ const isStoryAnnotation = (stmt: t.Statement, objectExports: Record<string, any>
|
|
38
38
|
t.isIdentifier(stmt.expression.left.object) &&
|
39
39
|
objectExports[stmt.expression.left.object.name];
|
40
40
|
|
41
|
-
const isTemplateDeclaration = (stmt: t.Statement, templates: Record<string, any>) =>
|
42
|
-
t.isVariableDeclaration(stmt) &&
|
43
|
-
stmt.declarations.length === 1 &&
|
44
|
-
t.isIdentifier(stmt.declarations[0].id) &&
|
45
|
-
templates[stmt.declarations[0].id.name];
|
46
|
-
|
47
41
|
const getNewExport = (stmt: t.Statement, objectExports: Record<string, any>) => {
|
48
42
|
if (
|
49
43
|
t.isExportNamedDeclaration(stmt) &&
|
@@ -94,6 +88,28 @@ const isReactGlobalRenderFn = (csf: CsfFile, storyFn: t.Expression) => {
|
|
94
88
|
const isSimpleCSFStory = (init: t.Expression, annotations: t.ObjectProperty[]) =>
|
95
89
|
annotations.length === 0 && t.isArrowFunctionExpression(init) && init.params.length === 0;
|
96
90
|
|
91
|
+
function removeUnusedTemplates(csf: CsfFile) {
|
92
|
+
Object.entries(csf._templates).forEach(([template, templateExpression]) => {
|
93
|
+
const references: NodePath[] = [];
|
94
|
+
babel.traverse(csf._ast, {
|
95
|
+
Identifier: (path) => {
|
96
|
+
if (path.node.name === template) references.push(path);
|
97
|
+
},
|
98
|
+
});
|
99
|
+
// if there is only one reference and this reference is the variable declaration initializing the template
|
100
|
+
// then we are sure the template is unused
|
101
|
+
if (references.length === 1) {
|
102
|
+
const reference = references[0];
|
103
|
+
if (
|
104
|
+
reference.parentPath.isVariableDeclarator() &&
|
105
|
+
reference.parentPath.node.init === templateExpression
|
106
|
+
) {
|
107
|
+
reference.parentPath.remove();
|
108
|
+
}
|
109
|
+
}
|
110
|
+
});
|
111
|
+
}
|
112
|
+
|
97
113
|
export default function transform(info: FileInfo, api: API, options: { parser?: string }) {
|
98
114
|
const makeTitle = (userTitle?: string) => {
|
99
115
|
return userTitle || 'FIXME';
|
@@ -137,15 +153,18 @@ export default function transform(info: FileInfo, api: API, options: { parser?:
|
|
137
153
|
return;
|
138
154
|
}
|
139
155
|
|
140
|
-
|
141
|
-
// const Template = (args) => <Cat {...args} />;
|
142
|
-
// export const A = Template.bind({});
|
143
|
-
let storyFn: t.Expression = template && (csf._templates[template] as any as t.Expression);
|
156
|
+
let storyFn: t.Expression = template && t.identifier(template);
|
144
157
|
if (!storyFn) {
|
145
158
|
storyFn = init;
|
146
159
|
}
|
147
160
|
|
148
|
-
|
161
|
+
// Remove the render function when we can hoist the template
|
162
|
+
// const Template = (args) => <Cat {...args} />;
|
163
|
+
// export const A = Template.bind({});
|
164
|
+
const renderAnnotation = isReactGlobalRenderFn(
|
165
|
+
csf,
|
166
|
+
template ? csf._templates[template] : storyFn
|
167
|
+
)
|
149
168
|
? []
|
150
169
|
: [t.objectProperty(t.identifier('render'), storyFn)];
|
151
170
|
|
@@ -160,15 +179,10 @@ export default function transform(info: FileInfo, api: API, options: { parser?:
|
|
160
179
|
}
|
161
180
|
});
|
162
181
|
|
163
|
-
importHelper.removeDeprecatedStoryImport();
|
164
|
-
|
165
182
|
csf._ast.program.body = csf._ast.program.body.reduce((acc, stmt) => {
|
166
183
|
const statement = stmt as t.Statement;
|
167
184
|
// remove story annotations & template declarations
|
168
|
-
if (
|
169
|
-
isStoryAnnotation(statement, objectExports) ||
|
170
|
-
isTemplateDeclaration(statement, csf._templates)
|
171
|
-
) {
|
185
|
+
if (isStoryAnnotation(statement, objectExports)) {
|
172
186
|
return acc;
|
173
187
|
}
|
174
188
|
|
@@ -185,6 +199,8 @@ export default function transform(info: FileInfo, api: API, options: { parser?:
|
|
185
199
|
}, []);
|
186
200
|
|
187
201
|
upgradeDeprecatedTypes(file);
|
202
|
+
importHelper.removeDeprecatedStoryImport();
|
203
|
+
removeUnusedTemplates(csf);
|
188
204
|
|
189
205
|
let output = recast.print(csf._ast, {}).code;
|
190
206
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
/* eslint-disable no-param-reassign,@typescript-eslint/no-shadow
|
1
|
+
/* eslint-disable no-param-reassign,@typescript-eslint/no-shadow */
|
2
2
|
import type { FileInfo } from 'jscodeshift';
|
3
3
|
import { babelParse, babelParseExpression } from '@storybook/csf-tools';
|
4
4
|
import { remark } from 'remark';
|
@@ -40,14 +40,11 @@ export default function jscodeshift(info: FileInfo) {
|
|
40
40
|
|
41
41
|
const result = transform(info.source, path.basename(baseName));
|
42
42
|
|
43
|
-
if (result == null) {
|
44
|
-
// We can not make a valid migration.
|
45
|
-
return;
|
46
|
-
}
|
47
|
-
|
48
43
|
const [mdx, csf] = result;
|
49
44
|
|
50
|
-
|
45
|
+
if (csf != null) {
|
46
|
+
fs.writeFileSync(`${baseName}.stories.js`, csf);
|
47
|
+
}
|
51
48
|
|
52
49
|
return mdx;
|
53
50
|
}
|
@@ -80,7 +77,6 @@ export function transform(source: string, baseName: string): [mdx: string, csf:
|
|
80
77
|
});
|
81
78
|
|
82
79
|
const file = getEsmAst(root);
|
83
|
-
addStoriesImport(root, baseName, storyNamespaceName);
|
84
80
|
|
85
81
|
visit(
|
86
82
|
root,
|
@@ -135,7 +131,7 @@ export function transform(source: string, baseName: string): [mdx: string, csf:
|
|
135
131
|
const nodeString = mdxProcessor.stringify({ type: 'root', children: [node] }).trim();
|
136
132
|
const newNode: MdxFlowExpression = {
|
137
133
|
type: 'mdxFlowExpression',
|
138
|
-
value: `/* ${nodeString} is deprecated, please migrate it to <Story of={referenceToStory} /> */`,
|
134
|
+
value: `/* ${nodeString} is deprecated, please migrate it to <Story of={referenceToStory} /> see: https://storybook.js.org/migration-guides/7.0 */`,
|
139
135
|
};
|
140
136
|
storiesMap.set(idAttribute.value as string, { type: 'id' });
|
141
137
|
parent.children.splice(index, 0, newNode);
|
@@ -201,11 +197,13 @@ export function transform(source: string, baseName: string): [mdx: string, csf:
|
|
201
197
|
},
|
202
198
|
});
|
203
199
|
|
204
|
-
if (storiesMap.size === 0) {
|
200
|
+
if (storiesMap.size === 0 && metaAttributes.length === 0) {
|
205
201
|
// A CSF file must have at least one story, so skip migrating if this is the case.
|
206
|
-
return null;
|
202
|
+
return [mdxProcessor.stringify(root), null];
|
207
203
|
}
|
208
204
|
|
205
|
+
addStoriesImport(root, baseName, storyNamespaceName);
|
206
|
+
|
209
207
|
const newStatements: t.Statement[] = [
|
210
208
|
t.exportDefaultDeclaration(t.objectExpression(metaProperties)),
|
211
209
|
];
|