radashi-helper
Version:
Help with managing your own Radashi
261 lines (252 loc) • 7.14 kB
JavaScript
import {
queryFuncs
} from "./chunk-GDVGEOPF.js";
import {
isBabelNode,
loadRewired,
require_lib,
rewire
} from "./chunk-OCHPTCP5.js";
import {
pullRadashi
} from "./chunk-FEUFDYD5.js";
import {
botCommit,
openInEditor
} from "./chunk-CQEIEK2X.js";
import {
stdio
} from "./chunk-GGEXN2BI.js";
import {
cwdRelative,
projectFolders
} from "./chunk-BKS73LZK.js";
import {
debug,
getEnv
} from "./chunk-DSXY7YVB.js";
import {
RadashiError,
exec,
log
} from "./chunk-JLKIE3A2.js";
import {
__toESM,
defer,
flat,
memo,
reduce,
select,
traverse,
unique
} from "./chunk-VOEQFXI5.js";
// src/fn-override.ts
import { existsSync as existsSync2 } from "node:fs";
import { copyFile as copyFile2, mkdir as mkdir2 } from "node:fs/promises";
import { dirname as dirname2, join as join3 } from "node:path";
// src/rewired/rewireDependents.ts
var import_parser = __toESM(require_lib(), 1);
import { existsSync, readFileSync } from "node:fs";
import { copyFile, mkdir, writeFile } from "node:fs/promises";
import { dirname, join as join2 } from "node:path";
// src/util/getRadashiFuncPaths.ts
import glob from "fast-glob";
import { join } from "node:path";
async function getRadashiFuncPaths(env) {
const srcRoot = join(env.radashiDir ?? env.root, "src");
return select(
(await glob("**/*.ts", { cwd: srcRoot })).sort(),
(f) => f.replace(/\.ts$/, ""),
(f) => f.includes("/")
);
}
// src/rewired/rewireDependents.ts
async function rewireDependents(funcName, env, radashiFuncPaths) {
if (!env.radashiDir) {
throw new RadashiError("No upstream repository exists");
}
radashiFuncPaths ??= await getRadashiFuncPaths(env);
const parseImports = memo((filename) => {
const fileContents = readFileSync(filename, "utf8");
const parseResult = (0, import_parser.parse)(fileContents, {
plugins: [["typescript", { dts: false }]],
sourceType: "module",
sourceFilename: filename
});
const importedNames = /* @__PURE__ */ new Set();
traverse(parseResult.program, (node, _key, _parent, context) => {
if (isBabelNode(node)) {
context.skip();
if (node.type === "ImportDeclaration" && node.source.value === "radashi") {
for (const specifier of node.specifiers) {
importedNames.add(specifier.imported?.name ?? specifier.local.name);
}
}
}
});
return importedNames;
});
const findDependentFiles = memo(
(funcName2, stack = []) => {
const selected = select(
radashiFuncPaths,
(funcPath) => {
const filename = join2(env.radashiDir, "src", funcPath + ".ts");
const importedNames = parseImports(filename);
if (importedNames.has(funcName2)) {
let dependents = [];
if (!stack.includes(funcPath)) {
stack.push(funcPath);
const srcFuncName = funcPath.split("/").at(-1);
dependents = findDependentFiles(srcFuncName, stack);
stack.pop();
}
dependents.unshift(funcPath);
return dependents;
}
return null;
}
);
return unique(flat(selected));
},
{
key: (bestMatchName) => bestMatchName
}
);
const prevRewired = await loadRewired(env);
const dependentFiles = findDependentFiles(funcName).filter((file) => {
return !prevRewired.includes(file) && !existsSync(join2(env.overrideDir, "src", file + ".ts"));
}).sort();
if (!dependentFiles.length) {
return [];
}
await writeFile(
join2(env.overrideDir, "rewired.json"),
JSON.stringify([...prevRewired, ...dependentFiles], null, 2)
);
await tryCopyFile(
join2(env.root, "src/tsconfig.json"),
join2(env.root, "overrides/rewired/tsconfig.json")
);
return reduce(
dependentFiles,
async (copiedFiles, file) => {
if (await rewire(file, env)) {
copiedFiles.push(file);
}
return copiedFiles;
},
[]
);
}
async function tryCopyFile(src, dst) {
debug(`Copying ${cwdRelative(src)} to ${cwdRelative(dst)}`);
if (existsSync(src)) {
try {
await mkdir(dirname(dst), { recursive: true });
await copyFile(src, dst);
return true;
} catch {
}
}
return false;
}
// src/util/isRepoClean.ts
import { exec as exec2 } from "node:child_process";
async function isRepoClean(cwd) {
return new Promise((resolve, reject) => {
exec2("git status --porcelain", { cwd }, (error, stdout, stderr) => {
if (error) {
reject(new Error(`Error executing git status: ${stderr}`));
} else {
resolve(stdout.trim() === "");
}
});
});
}
// src/util/assertRepoClean.ts
async function assertRepoClean(cwd) {
if (!await isRepoClean(cwd)) {
throw new RadashiError(
"Your repository has uncommitted changes. Please commit or stash them before overriding."
);
}
}
// src/fn-override.ts
async function addOverride(query, options = {}) {
const env = options.env ?? getEnv(options.dir);
const { radashiDir } = env;
if (!radashiDir) {
throw new RadashiError("No upstream repository exists");
}
await assertRepoClean(env.root);
await pullRadashi(env);
let bestMatch;
let bestMatchName;
await defer(async (onFinish) => {
if (options.fromBranch) {
await exec("git", ["checkout", options.fromBranch], {
cwd: radashiDir,
stdio
});
onFinish(async () => {
await exec("git", ["checkout", "-"], {
cwd: radashiDir
});
});
}
const funcPaths = await getRadashiFuncPaths(env);
const { funcPath, funcName } = await queryFuncs(query, funcPaths, {
exactMatch: options.exactMatch,
message: "Which function do you want to copy?",
confirmMessage: 'Is "{funcPath}" the function you want to copy?'
});
bestMatch = funcPath;
bestMatchName = funcName;
let copied = 0;
for (const folder of projectFolders) {
const fromPath = join3(
radashiDir,
folder.name,
bestMatch + folder.extension
);
const outPath = join3(
env.overrideDir,
folder.name,
bestMatch + folder.extension
);
const success = await tryCopyFile2(fromPath, outPath);
if (success) {
copied++;
if (folder.name === "src" && options.editor !== false) {
await openInEditor(outPath, env, options.editor);
}
}
}
copied += (await rewireDependents(bestMatchName, env, funcPaths)).length;
log(`${copied} files copied.`);
});
const { default: build } = await import("./build-7F3PTR5C.js");
await build({ env });
log("");
await botCommit(`chore: override ${bestMatch}`, {
cwd: env.root,
add: ["mod.ts", "overrides"]
});
}
async function tryCopyFile2(src, dst) {
debug(`Copying ${cwdRelative(src)} to ${cwdRelative(dst)}`);
if (existsSync2(src)) {
try {
await mkdir2(dirname2(dst), { recursive: true });
await copyFile2(src, dst);
return true;
} catch {
}
}
return false;
}
export {
assertRepoClean,
addOverride
};