@storybook/codemod
Version:
A collection of codemod scripts written with JSCodeshift
98 lines (93 loc) • 4.17 kB
JavaScript
import CJS_COMPAT_NODE_URL_94b6aurt4b from 'node:url';
import CJS_COMPAT_NODE_PATH_94b6aurt4b from 'node:path';
import CJS_COMPAT_NODE_MODULE_94b6aurt4b from "node:module";
var __filename = CJS_COMPAT_NODE_URL_94b6aurt4b.fileURLToPath(import.meta.url);
var __dirname = CJS_COMPAT_NODE_PATH_94b6aurt4b.dirname(__filename);
var require = CJS_COMPAT_NODE_MODULE_94b6aurt4b.createRequire(import.meta.url);
// ------------------------------------------------------------
// end of CJS compatibility banner, injected by Storybook's esbuild configuration
// ------------------------------------------------------------
// src/transforms/upgrade-deprecated-types.ts
import { core as babel, types as t } from "storybook/internal/babel";
import { loadCsf, printCsf } from "storybook/internal/csf-tools";
import { logger } from "storybook/internal/node-logger";
import prettier from "prettier";
var deprecatedTypes = [
"ComponentStory",
"ComponentStoryFn",
"ComponentStoryObj",
"ComponentMeta",
"Story"
];
function migrateType(oldType) {
return oldType === "Story" || oldType === "ComponentStory" ? "StoryFn" : oldType.replace("Component", "");
}
async function transform(info, api, options) {
let csf = loadCsf(info.source, { makeTitle: (title) => title }), fileNode = csf._ast, file = new babel.File(
{ filename: info.path },
{ code: info.source, ast: fileNode }
);
upgradeDeprecatedTypes(file);
let output = printCsf(csf).code;
try {
output = await prettier.format(output, {
...await prettier.resolveConfig(info.path),
filepath: info.path
});
} catch {
logger.log(`Failed applying prettier to ${info.path}.`);
}
return output;
}
var parser = "tsx";
function upgradeDeprecatedTypes(file) {
let importedNamespaces = /* @__PURE__ */ new Set(), typeReferencesToUpdate = /* @__PURE__ */ 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.
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
};