UNPKG

@storybook/codemod

Version:

A collection of codemod scripts written with JSCodeshift

153 lines (145 loc) • 6.62 kB
import CJS_COMPAT_NODE_URL_e9k6epg02qk from 'node:url'; import CJS_COMPAT_NODE_PATH_e9k6epg02qk from 'node:path'; import CJS_COMPAT_NODE_MODULE_e9k6epg02qk from "node:module"; var __filename = CJS_COMPAT_NODE_URL_e9k6epg02qk.fileURLToPath(import.meta.url); var __dirname = CJS_COMPAT_NODE_PATH_e9k6epg02qk.dirname(__filename); var require = CJS_COMPAT_NODE_MODULE_e9k6epg02qk.createRequire(import.meta.url); // ------------------------------------------------------------ // end of CJS compatibility banner, injected by Storybook's esbuild configuration // ------------------------------------------------------------ // src/index.ts import { readdirSync } from "node:fs"; import { rename as renameAsync } from "node:fs/promises"; import { extname as extname2, join as join2 } from "node:path"; import { resolvePackageDir } from "storybook/internal/common"; import { sync as spawnSync } from "cross-spawn"; // ../../../node_modules/pathe/dist/shared/pathe.ff20891b.mjs var _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//; function normalizeWindowsPath(input = "") { return input && input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase()); } var _UNC_REGEX = /^[/\\]{2}/, _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/, _DRIVE_LETTER_RE = /^[A-Za-z]:$/; var normalize = function(path2) { if (path2.length === 0) return "."; path2 = normalizeWindowsPath(path2); let isUNCPath = path2.match(_UNC_REGEX), isPathAbsolute = isAbsolute(path2), trailingSeparator = path2[path2.length - 1] === "/"; return path2 = normalizeString(path2, !isPathAbsolute), path2.length === 0 ? isPathAbsolute ? "/" : trailingSeparator ? "./" : "." : (trailingSeparator && (path2 += "/"), _DRIVE_LETTER_RE.test(path2) && (path2 += "/"), isUNCPath ? isPathAbsolute ? `//${path2}` : `//./${path2}` : isPathAbsolute && !isAbsolute(path2) ? `/${path2}` : path2); }; function normalizeString(path2, allowAboveRoot) { let res = "", lastSegmentLength = 0, lastSlash = -1, dots = 0, char = null; for (let index = 0; index <= path2.length; ++index) { if (index < path2.length) char = path2[index]; else { if (char === "/") break; char = "/"; } if (char === "/") { if (!(lastSlash === index - 1 || dots === 1)) if (dots === 2) { if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") { if (res.length > 2) { let lastSlashIndex = res.lastIndexOf("/"); lastSlashIndex === -1 ? (res = "", lastSegmentLength = 0) : (res = res.slice(0, lastSlashIndex), lastSegmentLength = res.length - 1 - res.lastIndexOf("/")), lastSlash = index, dots = 0; continue; } else if (res.length > 0) { res = "", lastSegmentLength = 0, lastSlash = index, dots = 0; continue; } } allowAboveRoot && (res += res.length > 0 ? "/.." : "..", lastSegmentLength = 2); } else res.length > 0 ? res += `/${path2.slice(lastSlash + 1, index)}` : res = path2.slice(lastSlash + 1, index), lastSegmentLength = index - lastSlash - 1; lastSlash = index, dots = 0; } else char === "." && dots !== -1 ? ++dots : dots = -1; } return res; } var isAbsolute = function(p) { return _IS_ABSOLUTE_RE.test(p); }; // src/index.ts import { glob as tinyglobby } from "tinyglobby"; // src/lib/utils.ts import { camelCase, upperFirst } from "es-toolkit/string"; function jscodeshiftToPrettierParser(parser) { let parserMap = { babylon: "babel", flow: "flow", ts: "typescript", tsx: "typescript" }; return parser && parserMap[parser] || "babel"; } // src/index.ts var TRANSFORM_DIR = join2(resolvePackageDir("@storybook/codemod"), "dist", "transforms"); function listCodemods() { return readdirSync(TRANSFORM_DIR).filter((fname) => fname.endsWith(".js")).map((fname) => fname.slice(0, -3)); } 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 = extname2(glob).slice(1); jscodeshiftToPrettierParser(extension) !== "babel" && (inferredParser = extension); } let files = await tinyglobby([normalize(glob), "!**/node_modules", "!**/dist"]), extensions = new Set(files.map((file) => extname2(file).slice(1))), commaSeparatedExtensions = Array.from(extensions).join(","); if (logger.step(`Applying ${codemod}: ${files.length} files`), files.length === 0) { logger.step(`No matching files for glob: ${glob}`); return; } if (!dryRun && files.length > 0) { let parserArgs = inferredParser ? ["--parser", inferredParser] : [], result = spawnSync( "node", [ join2(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 ], { stdio: "inherit" } ); 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) { let [from, to] = renameParts; logger.step(`Renaming ${rename}: ${files.length} files`), await Promise.all( files.map((file) => renameFile(file, new RegExp(`${from}$`), to, { logger })) ); } } export { listCodemods, runCodemod };