@storybook/codemod
Version:
A collection of codemod scripts written with JSCodeshift
130 lines (124 loc) • 4.63 kB
JavaScript
import CJS_COMPAT_NODE_URL_9smq7eu765a from 'node:url';
import CJS_COMPAT_NODE_PATH_9smq7eu765a from 'node:path';
import CJS_COMPAT_NODE_MODULE_9smq7eu765a from "node:module";
var __filename = CJS_COMPAT_NODE_URL_9smq7eu765a.fileURLToPath(import.meta.url);
var __dirname = CJS_COMPAT_NODE_PATH_9smq7eu765a.dirname(__filename);
var require = CJS_COMPAT_NODE_MODULE_9smq7eu765a.createRequire(import.meta.url);
// ------------------------------------------------------------
// end of CJS compatibility banner, injected by Storybook's esbuild configuration
// ------------------------------------------------------------
import {
__name
} from "./_node-chunks/chunk-HVEWKEW6.js";
// src/index.ts
import { readdirSync } from "node:fs";
import { rename as renameAsync } from "node:fs/promises";
import { extname, join } from "node:path";
import { resolvePackageDir } from "storybook/internal/common";
import { sync as spawnSync } from "cross-spawn";
import { glob as tinyglobby } from "tinyglobby";
// src/lib/utils.ts
import { camelCase, upperFirst } from "es-toolkit/string";
function jscodeshiftToPrettierParser(parser) {
const parserMap = {
babylon: "babel",
flow: "flow",
ts: "typescript",
tsx: "typescript"
};
if (!parser) {
return "babel";
}
return parserMap[parser] || "babel";
}
__name(jscodeshiftToPrettierParser, "jscodeshiftToPrettierParser");
// src/index.ts
var TRANSFORM_DIR = join(resolvePackageDir("@storybook/codemod"), "dist", "transforms");
function listCodemods() {
return readdirSync(TRANSFORM_DIR).filter((fname) => fname.endsWith(".js")).map((fname) => fname.slice(0, -3));
}
__name(listCodemods, "listCodemods");
async function renameFile(file, from, to, { logger }) {
const newFile = file.replace(from, to);
logger.log(`Rename: ${file} ${newFile}`);
return renameAsync(file, newFile);
}
__name(renameFile, "renameFile");
async function runCodemod(codemod, {
glob,
logger,
dryRun,
rename,
parser
}) {
const codemods = listCodemods();
if (!codemods.includes(codemod)) {
throw new Error(`Unknown codemod ${codemod}. Run --list for options.`);
}
let renameParts = null;
if (rename) {
renameParts = rename.split(":");
if (renameParts.length !== 2) {
throw new Error(`Codemod rename: expected format "from:to", got "${rename}"`);
}
}
let inferredParser = parser;
if (!parser) {
const extension = extname(glob).slice(1);
const knownParser = jscodeshiftToPrettierParser(extension);
if (knownParser !== "babel") {
inferredParser = extension;
}
}
const files = await tinyglobby([glob, "!**/node_modules", "!**/dist"]);
const extensions = new Set(files.map((file) => extname(file).slice(1)));
const commaSeparatedExtensions = Array.from(extensions).join(",");
logger.log(`=> Applying ${codemod}: ${files.length} files`);
if (files.length === 0) {
logger.log(`=> No matching files for glob: ${glob}`);
return;
}
if (!dryRun && files.length > 0) {
const parserArgs = inferredParser ? ["--parser", inferredParser] : [];
const result = spawnSync(
"node",
[
join(resolvePackageDir("jscodeshift"), "bin", "jscodeshift"),
// this makes sure codeshift doesn't transform our own source code with babel
// which is faster, and also makes sure the user won't see babel messages such as:
// [BABEL] Note: The code generator has deoptimised the styling of repo/node_modules/prettier/index.js as it exceeds the max of 500KB.
"--no-babel",
`--extensions=${commaSeparatedExtensions}`,
"--fail-on-error",
"-t",
`${TRANSFORM_DIR}/${codemod}.js`,
...parserArgs,
...files.map((file) => `"${file}"`)
],
{
stdio: "inherit",
shell: true
}
);
if (codemod === "mdx-to-csf" && result.status === 1) {
logger.log(
"The codemod was not able to transform the files mentioned above. We have renamed the files to .mdx.broken. Please check the files and rename them back to .mdx after you have either manually transformed them to mdx + csf or fixed the issues so that the codemod can transform them."
);
} else if (result.status === 1) {
logger.log("Skipped renaming because of errors.");
return;
}
}
if (renameParts) {
const [from, to] = renameParts;
logger.log(`=> Renaming ${rename}: ${files.length} files`);
await Promise.all(
files.map((file) => renameFile(file, new RegExp(`${from}$`), to, { logger }))
);
}
}
__name(runCodemod, "runCodemod");
export {
listCodemods,
runCodemod
};