UNPKG

@stephansama/auto-readme

Version:

Generate lists and tables for your README automagically based on your repository and comments

726 lines (710 loc) 28.3 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__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( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { run: () => run }); module.exports = __toCommonJS(index_exports); var import_mdast_util_from_markdown2 = require("mdast-util-from-markdown"); var cp2 = __toESM(require("child_process"), 1); var fsp3 = __toESM(require("fs/promises"), 1); var import_ora = __toESM(require("ora"), 1); // src/args.ts var import_debug = __toESM(require("debug"), 1); var import_yargs = __toESM(require("yargs"), 1); var import_helpers = require("yargs/helpers"); var import_zod2 = __toESM(require("zod"), 1); // src/schema.js var import_zod = require("zod"); var actionsSchema = import_zod.z.enum(["ACTION", "PKG", "USAGE", "WORKSPACE", "ZOD"]).describe("Comment action options"); var formatsSchema = import_zod.z.enum(["LIST", "TABLE"]).default("TABLE").optional(); var languageSchema = import_zod.z.enum(["JS", "RS"]).optional().default("JS"); var headingsSchema = import_zod.z.enum([ "default", "description", "devDependency", "downloads", "name", "private", "required", "version" ]).describe("Table heading options"); var tableHeadingsSchema = import_zod.z.record(actionsSchema, headingsSchema.array().optional()).optional().describe("Table heading action configuration").default({ ACTION: ["name", "required", "default", "description"], PKG: ["name", "version", "devDependency"], WORKSPACE: ["name", "version", "downloads", "description"], ZOD: [] }); var templatesSchema = import_zod.z.object({ downloadImage: import_zod.z.string().optional().default("https://img.shields.io/npm/dw/{{name}}?labelColor=211F1F"), emojis: import_zod.z.record(headingsSchema, import_zod.z.string()).optional().describe("Table heading emojis used when enabled").default({ default: "\u2699\uFE0F", description: "\u{1F4DD}", devDependency: "\u{1F4BB}", downloads: "\u{1F4E5}", name: "\u{1F3F7}\uFE0F", private: "\u{1F512}", required: "", version: "" }), registryUrl: import_zod.z.string().optional().default("https://www.npmjs.com/package/{{name}}"), versionImage: import_zod.z.string().optional().default( "https://img.shields.io/npm/v/{{uri_name}}?logo=npm&logoColor=red&color=211F1F&labelColor=211F1F" ) }); var defaultTemplates = templatesSchema.parse({}); var defaultTableHeadings = tableHeadingsSchema.parse(void 0); var _configSchema = import_zod.z.object({ affectedRegexes: import_zod.z.string().array().optional().default([]), collapseHeadings: import_zod.z.string().array().optional().default([]), defaultLanguage: languageSchema.meta({ alias: "l", description: "Default language to infer projects from" }), disableEmojis: import_zod.z.boolean().default(false).meta({ alias: "e", description: "Whether or not to use emojis in markdown table headings" }), disableMarkdownHeadings: import_zod.z.boolean().default(false).meta({ description: "Whether or not to display markdown headings" }), enableToc: import_zod.z.boolean().default(false).meta({ alias: "t", description: "generate table of contents for readmes" }), enableUsage: import_zod.z.boolean().optional().default(false).meta({ description: "Whether or not to enable usage plugin" }), headings: tableHeadingsSchema.optional().default(defaultTableHeadings).describe("List of headings for different table outputs"), onlyReadmes: import_zod.z.boolean().default(true).meta({ alias: "r", description: "Whether or not to only traverse readmes" }), onlyShowPublicPackages: import_zod.z.boolean().default(false).meta({ alias: "p", description: "Only show public packages in workspaces" }), removeScope: import_zod.z.string().optional().default("").meta({ description: "Remove common workspace scope" }), templates: templatesSchema.optional().default(defaultTemplates).describe( "Handlebars templates used to fuel list and table generation" ), tocHeading: import_zod.z.string().optional().default("Table of contents").meta({ description: "Markdown heading used to generate table of contents" }), usageFile: import_zod.z.string().optional().default("").meta({ description: "Workspace level usage file" }), usageHeading: import_zod.z.string().optional().default("Usage").meta({ description: "Markdown heading used to generate usage example" }), verbose: import_zod.z.boolean().default(false).meta({ alias: "v", description: "whether or not to display verbose logging" }) }); var configSchema = _configSchema.optional(); // src/args.ts var complexOptions = [ "affectedRegexes", "collapseHeadings", "headings", "templates" ]; var args = { ...zodToYargs(), changes: { alias: "g", default: false, description: "Check only changed git files", type: "boolean" }, check: { alias: "k", default: false, description: "Do not write to files. Only check for changes", type: "boolean" }, config: { alias: "c", description: "Path to config file", type: "string" } }; async function parseArgs() { const yargsInstance = (0, import_yargs.default)((0, import_helpers.hideBin)(process.argv)).options(args).help("h").alias("h", "help").epilogue(`--> @stephansama open-source ${(/* @__PURE__ */ new Date()).getFullYear()}`); const parsed = await yargsInstance.wrap(yargsInstance.terminalWidth()).parse(); if (parsed.verbose) import_debug.default.enable("autoreadme*"); return parsed; } function zodToYargs() { const { shape } = configSchema.unwrap(); const entries = Object.entries(shape).map(([key, value]) => { if (complexOptions.includes(key)) return []; if (value.def.innerType instanceof import_zod2.default.ZodObject) return []; const meta = value.meta(); const { innerType } = value.def; const isBoolean = innerType instanceof import_zod2.default.ZodBoolean; const isNumber = innerType instanceof import_zod2.default.ZodNumber; const isArray = innerType instanceof import_zod2.default.ZodArray; const yargType = isArray && "array" || isNumber && "number" || isBoolean && "boolean" || "string"; const options = { default: value.def.defaultValue, type: yargType }; if (meta?.alias) options.alias = meta.alias; if (meta?.description) options.description = meta.description; return [key, options]; }); return Object.fromEntries(entries); } // src/comment.ts var import_mdast_comment_marker = require("mdast-comment-marker"); // src/log.ts var import_debug2 = __toESM(require("debug"), 1); var error = (0, import_debug2.default)("autoreadme:error"); var info = (0, import_debug2.default)("autoreadme:info"); var warn = (0, import_debug2.default)("autoreadme:warn"); function ERROR(...rest) { const [first, ...remaining] = rest; error(`${first} %O`, ...remaining); } function INFO(...rest) { const [first, ...remaining] = rest; info(`${first} %O`, ...remaining); } function WARN(...rest) { const [first, ...remaining] = rest; warn(`${first} %O`, ...remaining); } // src/comment.ts var SEPARATOR = "-"; function loadAstComments(root) { return root.children.map((child) => child.type === "html" && getComment(child)).filter((f) => f !== false); } function parseComment(comment) { const input = trimComment(comment); const [type, ...parameters] = input.split(" "); const [first, second, third] = type.split(SEPARATOR); INFO("parsing inputs", { first, second, third }); const languageInput = third ? first : void 0; const actionInput = third ? second : first; const formatInput = third ? third : second; const language = languageSchema.parse(languageInput); const action = actionsSchema.parse(actionInput); const format = formatsSchema.parse(formatInput); const isStart = comment.includes("start"); const parsed = { action, format, isStart, language, parameters }; INFO(`Parsed comment ${comment}`, parsed); return parsed; } var startComment = "<!--"; var endComment = "-->"; function trimComment(comment) { return comment.replace(startComment, "").replace(/start|end/, "").replace(endComment, "").trim(); } function getComment(comment) { if (!isComment(comment.value)) return false; const marker = (0, import_mdast_comment_marker.commentMarker)(comment); if (!marker) return false; return parseComment(comment.value); } function isComment(comment) { return comment.startsWith(startComment) && comment.endsWith(endComment); } // src/config.ts var import_toml = __toESM(require("@iarna/toml"), 1); var import_cosmiconfig = require("cosmiconfig"); var import_deepmerge = __toESM(require("deepmerge"), 1); var moduleName = "autoreadme"; var searchPlaces = getSearchPlaces(); var loaders = { [".toml"]: loadToml }; async function loadConfig(args2) { const opts2 = { loaders, searchPlaces }; if (args2.config) opts2.searchPlaces = [args2.config]; const explorer = (0, import_cosmiconfig.cosmiconfig)(moduleName, opts2); const search = await explorer.search(); if (!search) { const location = args2.config ? " at location: " + args2.config : ""; WARN(`no config file found`, location); INFO("using default configuration"); } else { INFO("found configuration file at: ", search.filepath); INFO("loaded cosmiconfig", search.config); } args2 = removeFalsy(args2); INFO("merging config with args", args2); return configSchema.parse( (0, import_deepmerge.default)(search?.config || {}, args2, { arrayMerge: (_, sourceArray) => sourceArray }) ); } function loadToml(_filepath, content) { return import_toml.default.parse(content); } function getSearchPlaces() { return [ ...(0, import_cosmiconfig.getDefaultSearchPlaces)(moduleName), `.${moduleName}rc.toml`, `.config/.${moduleName}rc`, `.config/${moduleName}rc.toml`, `.config/.${moduleName}rc.toml`, `.config/.${moduleName}rc.json`, `.config/.${moduleName}rc.yaml`, `.config/.${moduleName}rc.yml` ]; } function removeFalsy(obj) { return Object.fromEntries( Object.entries(obj).map(([k, v]) => !v ? false : [k, v]).filter((e) => Boolean(e)) ); } // src/data.ts var import_get_packages = require("@manypkg/get-packages"); var fs2 = __toESM(require("fs"), 1); var fsp2 = __toESM(require("fs/promises"), 1); var path2 = __toESM(require("path"), 1); var import_pkg_types = require("pkg-types"); var yaml = __toESM(require("yaml"), 1); var import_zod2md = require("zod2md"); // src/utils.ts var import_fast_glob = __toESM(require("fast-glob"), 1); var cp = __toESM(require("child_process"), 1); var fs = __toESM(require("fs"), 1); var fsp = __toESM(require("fs/promises"), 1); var path = __toESM(require("path"), 1); var sh = String.raw; var opts = { encoding: "utf8" }; var ignore = ["**/node_modules/**"]; var matches = [ /.*README\.md$/gi, /.*Cargo\.toml$/gi, /.*action\.ya?ml$/gi, /.*package\.json$/gi, /.*pnpm-workspace\.yaml$/gi ]; async function fileExists(file) { return await fsp.access(file).then(() => true).catch(() => false); } function findAffectedMarkdowns(root, config) { const affected = cp.execSync(sh`git diff --cached --name-only --diff-filter=MACT`, opts).trim().split("\n").filter(Boolean); if (!affected.length) ERROR("no staged files found"); if (config.affectedRegexes?.length) { INFO("adding the following expressions: ", config.affectedRegexes); } const allMatches = [ ...matches, ...config.affectedRegexes?.map((r) => new RegExp(r)) || [] ]; INFO("Checking affected files against regexes", affected, allMatches); const eligible = affected.filter((a) => allMatches.some((m) => a.match(m))); INFO("Found the following eligible affected files", eligible); const md = eligible.map((e) => findNearestReadme(root, path.resolve(e))); const rootMd = path.join(root, "README.md"); const dedupe = [...new Set(md), rootMd].filter( (s) => Boolean(s) ); INFO("Found the following readmes", dedupe); return dedupe; } function findNearestReadme(gitRoot, inputFile, maxRotations = 15) { let dir = path.dirname(inputFile); let rotations = 0; while (true) { const option = path.join(dir, "README.md"); if (fs.existsSync(option)) return option; const parent = path.dirname(dir); if (parent === dir || dir === gitRoot || ++rotations > maxRotations) { break; } dir = parent; } return null; } function getGitRoot() { const root = cp.execSync(sh`git rev-parse --show-toplevel`, opts).trim(); if (!root) { throw new Error("must be ran within a git directory."); } INFO("found git root at location: ", root); return root; } async function getMarkdownPaths(cwd, config) { const pattern = `**/${config?.onlyReadmes ? "README" : "*"}.md`; const readmes = await (0, import_fast_glob.default)(pattern, { cwd, ignore }); return readmes.map((readme) => path.resolve(cwd, readme)); } // src/data.ts function createFindParameter(parameterList) { return function(parameterName) { return parameterList?.find((p) => p.startsWith(parameterName))?.replace(parameterName + "=", "")?.replace(/"/gi, "")?.replace(/_/gi, " "); }; } async function loadActionData(actions, file, root) { const startActions = actions.filter((action) => action.isStart); return await Promise.all( startActions.map(async (action) => { const find = createFindParameter(action.parameters); switch (action.action) { case "ACTION": { const baseDir = path2.dirname(file); const actionYaml = await loadActionYaml(baseDir); return { action: action.action, actionYaml, parameters: action.parameters }; } case "PKG": { const inputPath = find("path"); const filename = inputPath ? path2.resolve(path2.dirname(file), inputPath) : path2.dirname(file); const pkgJson = await (0, import_pkg_types.readPackageJSON)(filename); return { action: action.action, parameters: action.parameters, pkgJson }; } case "USAGE": { return { action: action.action, parameters: action.parameters }; } case "WORKSPACE": { const workspaces = await (0, import_get_packages.getPackages)(process.cwd()); const pnpmPath = path2.resolve(root, "pnpm-workspace.yaml"); const isPnpm = fs2.existsSync(pnpmPath); return { action: action.action, isPnpm, parameters: action.parameters, root, workspaces }; } case "ZOD": { if (action.format === "LIST") { throw new Error("cannot display zod in list format"); } const inputPath = find("path"); if (!inputPath) { const error2 = `no path found for zod table at markdown file ${file}`; throw new Error(error2); } const body = await (0, import_zod2md.zod2md)({ entry: path2.resolve(path2.dirname(file), inputPath), title: find("title") || "Zod Schema" }); return { action: action.action, body, parameters: action.parameters }; } default: throw new Error("feature not yet implemented"); } }) ); } async function loadActionYaml(baseDir) { const actionYmlPath = path2.resolve(baseDir, "action.yml"); const actionYamlPath = path2.resolve(baseDir, "action.yaml"); const actualPath = await fileExists(actionYamlPath) && actionYamlPath || await fileExists(actionYmlPath) && actionYmlPath; if (!actualPath) { const locations = [actionYmlPath, actionYamlPath]; const error2 = `no yaml file found at locations: ${locations}`; throw new Error(error2); } const actionFile = await fsp2.readFile(actualPath, { encoding: "utf8" }); return yaml.parse(actionFile); } // src/pipeline.ts var path4 = __toESM(require("path"), 1); var import_remark = require("remark"); var import_remark_code_import = __toESM(require("remark-code-import"), 1); var import_remark_collapse = __toESM(require("remark-collapse"), 1); var import_remark_toc = __toESM(require("remark-toc"), 1); var import_remark_usage = __toESM(require("remark-usage"), 1); var import_vfile = require("vfile"); // src/plugin.ts var import_handlebars = __toESM(require("handlebars"), 1); var import_markdown_table = require("markdown-table"); var import_mdast_util_from_markdown = require("mdast-util-from-markdown"); var import_mdast_zone = require("mdast-zone"); var import_node_path = __toESM(require("path"), 1); function createHeading(headings, disableEmojis = false, emojis = defaultTemplates.emojis) { return headings.map( (h) => `${disableEmojis ? "" : emojis[h] + " "}${h?.at(0)?.toUpperCase() + h?.slice(1)}` ); } function wrapRequired(required, input) { if (!required) return input; return `<b>*${input}</b>`; } var autoReadmeRemarkPlugin = (config, data) => (tree) => { (0, import_mdast_zone.zone)(tree, /.*ZOD.*/gi, function(start, _, end) { const zod = data.find((d) => d?.action === "ZOD"); if (!zod?.body) { throw new Error("unable to load zod body"); } const ast = (0, import_mdast_util_from_markdown.fromMarkdown)(zod.body); return [start, ast, end]; }); (0, import_mdast_zone.zone)(tree, /.*ACTION.*/gi, function(start, _, end) { const value = start.type === "html" && start.value; const options = value && parseComment(value); if (!options) throw new Error("not able to parse comment"); const first = data.find((d) => d?.action === "ACTION"); const inputs = first?.actionYaml?.inputs || {}; const heading = `### ${config.disableEmojis ? "" : "\u{1F9F0}"} actions`; if (options.format === "LIST") { const body2 = `${heading} ` + Object.entries(inputs).sort((a) => a[1].required ? -1 : 1).map(([key, value2]) => { return `- ${wrapRequired(value2.required, key)}: (default: ${value2.default}) ${value2.description}`; }).join("\n"); const ast2 = (0, import_mdast_util_from_markdown.fromMarkdown)(body2); return [start, ast2, end]; } const headings = config.headings?.ACTION?.length && config.headings.ACTION || defaultTableHeadings.ACTION; const table = (0, import_markdown_table.markdownTable)([ createHeading( headings, config.disableEmojis, config.templates?.emojis ), ...Object.entries(inputs).map( ([k, v]) => headings.map((heading2) => v[heading2] || k).map(String) ) ]); const body = [heading, "", table].join("\n"); const ast = (0, import_mdast_util_from_markdown.fromMarkdown)(body); return [start, ast, end]; }); (0, import_mdast_zone.zone)(tree, /.*WORKSPACE.*/gi, function(start, _, end) { const value = start.type === "html" && start.value; const comment = value && parseComment(value); const workspace = data.find((d) => d?.action === "WORKSPACE"); const templates = loadTemplates(config.templates); const packages = workspace?.workspaces?.packages || []; const headings = config.headings?.WORKSPACE?.length && config.headings?.WORKSPACE || defaultTableHeadings.WORKSPACE; if (comment && comment.format === "LIST") { } const tableHeadings = createHeading( headings, config.disableEmojis, config.templates?.emojis ); const table = (0, import_markdown_table.markdownTable)([ tableHeadings, ...packages.filter( (pkg) => config.onlyShowPublicPackages ? !pkg.packageJson.private : true ).map((pkg) => { const { name } = pkg.packageJson; return headings.map((heading2) => { if (heading2 === "name") { const scoped = config.removeScope ? name.replace(config.removeScope, "") : name; return `[${scoped}](${import_node_path.default.relative( process.cwd(), import_node_path.default.resolve(pkg.dir, "README.md") )})`; } if (heading2 === "version") { return `![npm version image](${templates.versionImage( { uri_name: encodeURIComponent(name) } )})`; } if (heading2 === "downloads") { return `![npm downloads](${templates.downloadImage( { name } )})`; } if (heading2 === "description") { return pkg.packageJson?.description; } return ``; }); }) ]); const heading = `### ${config.disableEmojis ? "" : "\u{1F3ED}"} workspace`; const body = [heading, "", table].join("\n"); const ast = (0, import_mdast_util_from_markdown.fromMarkdown)(body); return [start, ast, end]; }); (0, import_mdast_zone.zone)(tree, /.*PKG.*/gi, function(start, _, end) { const value = start.type === "html" && start.value; const comment = value && parseComment(value); const first = data.find((d) => d?.action === "PKG"); const templates = loadTemplates(config.templates); const headings = config.headings?.PKG?.length && config.headings?.PKG || defaultTableHeadings.PKG; if (comment && comment.format === "LIST") { const ast = (0, import_mdast_util_from_markdown.fromMarkdown)(""); return [start, ast, end]; } function mapDependencies(isDev) { return function([name, version]) { const url = templates.registryUrl({ name }); return headings.map((key) => { if (key === "devDependency") { if (config.disableEmojis) { return `\`${isDev}\``; } return `${isDev ? "\u2328\uFE0F" : "\u{1F465}"}`; } if (key === "name") { return `[${name}](${url})`; } if (key === "version") { if (["workspace", "catalog", "*"].some( (type) => version.includes(type) )) { return `\`${version}\``; } return `![npm version](${templates.versionImage({ uri_name: encodeURIComponent(name) })})`; } }); }; } const { dependencies = {}, devDependencies = {} } = first?.pkgJson || {}; const table = (0, import_markdown_table.markdownTable)([ createHeading( headings, config.disableEmojis, config.templates?.emojis ), ...Object.entries(devDependencies).map(mapDependencies(true)), ...Object.entries(dependencies).map(mapDependencies(false)) ]); const heading = `### ${config.disableEmojis ? "" : "\u{1F4E6}"} packages`; const body = [heading, "", table].join("\n"); const tableAst = (0, import_mdast_util_from_markdown.fromMarkdown)(body); return [start, tableAst, end]; }); }; function loadTemplates(templates) { if (!templates) throw new Error("failed to load templates"); return Object.fromEntries( Object.entries(templates).map(([key, value]) => { if (typeof value !== "string") return []; return [key, import_handlebars.default.compile(value)]; }) ); } // src/pipeline.ts async function parse2(file, filepath, root, config, data) { const pipeline = (0, import_remark.remark)().use(autoReadmeRemarkPlugin, config, data).use(import_remark_code_import.default, {}); const usage = data.find((d) => d.action === "USAGE"); if (usage?.action === "USAGE" || config.enableUsage) { const find = createFindParameter(usage?.parameters || []); const examplePath = find("path"); const dirname4 = path4.dirname(filepath); const resolvePath = examplePath && path4.resolve(dirname4, examplePath); const relativeProjectPath = config.usageFile && path4.relative(root, path4.resolve(dirname4, config.usageFile)); const example = examplePath && resolvePath && path4.relative(root, resolvePath) || relativeProjectPath || void 0; if (example && await fileExists(example)) { INFO("generating usage section"); pipeline.use(import_remark_usage.default, { example, heading: config.usageHeading }); } else { WARN("not able to find example file for readme", filepath, example); } } if (config.enableToc) { INFO("generating table of contents section"); pipeline.use(import_remark_toc.default, { heading: config.tocHeading }); } if (config.enableToc || config.collapseHeadings?.length) { const additional = config.collapseHeadings?.length ? config.collapseHeadings : []; const headings = [...additional, config.tocHeading]; pipeline.use(import_remark_collapse.default, { test: { ignoreFinalDefinitions: true, test: (value, _) => { return headings.some((i) => value.trim() === i?.trim()); } } }); } const vfile = new import_vfile.VFile({ path: path4.resolve(filepath), value: file }); const markdown = await pipeline.process(vfile); return markdown.toString(); } // src/index.ts async function run() { const args2 = await parseArgs(); const config = await loadConfig(args2) || {}; INFO("Loaded the following configuration:", config); const root = getGitRoot(); const isAffected = args2.changes ? "affected" : ""; INFO(`Loading ${!isAffected ? "all " : "affected "}files`); const paths = isAffected ? findAffectedMarkdowns(root, config) : await getMarkdownPaths(root, config); INFO("Loaded the following files:", paths.join("\n")); const type = args2.onlyReadmes ? "readmes" : "all markdown files"; if (!paths.length) { return ERROR(`no ${isAffected} readmes found to update`); } const spinner = !args2.verbose && (0, import_ora.default)(`Updating ${type}`).start(); await Promise.all( paths.map(async (path5) => { const file = await fsp3.readFile(path5, { encoding: "utf8" }); const actions = (() => { const ast = (0, import_mdast_util_from_markdown2.fromMarkdown)(file); return loadAstComments(ast); })(); if (!actions.length) { WARN(`no action comments found in`, path5); if (!config.enableUsage || !config.enableToc) { return ERROR("no action or plugins found"); } else { INFO("plugins enabled. continuing parsing", path5); } } const data = await loadActionData(actions, path5, root); INFO("Loaded comment action data", data); const content = await parse2(file, path5, root, config, data); await fsp3.writeFile(path5, content); }) ); const opts2 = { stdio: "inherit" }; INFO("formatting with prettier"); cp2.execFileSync("prettier", ["--write", ...paths], opts2); if (isAffected) { INFO("adding affected files to git stage"); cp2.execFileSync("git", ["add", ...paths], opts2); } if (spinner) spinner.stop(); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { run }); //# sourceMappingURL=index.cjs.map