mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-24 15:39:44 +01:00
163 lines
4.1 KiB
JavaScript
163 lines
4.1 KiB
JavaScript
|
|
import { promises as fs } from "fs";
|
||
|
|
import path from "path";
|
||
|
|
import os from "os";
|
||
|
|
import { exec } from "child_process";
|
||
|
|
import readline from "readline";
|
||
|
|
|
||
|
|
const PROJECT_ROOT = path.resolve(
|
||
|
|
new URL(import.meta.url).pathname,
|
||
|
|
"..",
|
||
|
|
".."
|
||
|
|
); // scripts/.. -> mobile
|
||
|
|
const args = process.argv.slice(2);
|
||
|
|
|
||
|
|
const FLAGS = {
|
||
|
|
yes: args.includes("-y") || args.includes("--yes"),
|
||
|
|
dryRun: args.includes("--dry-run"),
|
||
|
|
all: args.includes("--all"),
|
||
|
|
verbose: args.includes("-v") || args.includes("--verbose")
|
||
|
|
};
|
||
|
|
|
||
|
|
const DEFAULT_TARGETS = [
|
||
|
|
"node_modules",
|
||
|
|
"android/build",
|
||
|
|
"android/app/build",
|
||
|
|
"ios/Pods",
|
||
|
|
"ios/DerivedData" // sometimes used as DerivedData path
|
||
|
|
];
|
||
|
|
|
||
|
|
const EXTRA_TARGETS = [
|
||
|
|
"android/.gradle",
|
||
|
|
".gradle",
|
||
|
|
"ios/build",
|
||
|
|
"ios/Podfile.lock",
|
||
|
|
"ios/xcuserdata",
|
||
|
|
".expo",
|
||
|
|
".expo-shared",
|
||
|
|
".vscode",
|
||
|
|
"build",
|
||
|
|
".idea"
|
||
|
|
];
|
||
|
|
|
||
|
|
const targets = new Set([
|
||
|
|
...DEFAULT_TARGETS,
|
||
|
|
...(FLAGS.all ? EXTRA_TARGETS : [])
|
||
|
|
]);
|
||
|
|
|
||
|
|
function resolveTargets(root) {
|
||
|
|
return [...targets].map((t) => ({ rel: t, abs: path.resolve(root, t) }));
|
||
|
|
}
|
||
|
|
|
||
|
|
async function pathExists(p) {
|
||
|
|
try {
|
||
|
|
await fs.access(p);
|
||
|
|
return true;
|
||
|
|
} catch {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function execPromise(cmd) {
|
||
|
|
return new Promise((res, rej) => {
|
||
|
|
exec(cmd, { maxBuffer: 1024 * 1024 * 10 }, (err, stdout, stderr) => {
|
||
|
|
if (err) return rej({ err, stdout, stderr });
|
||
|
|
res({ stdout, stderr });
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
async function removeWithNode(p) {
|
||
|
|
// fs.rm is available in Node 14.14+; fallback to rmdir if necessary
|
||
|
|
if (fs.rm) {
|
||
|
|
await fs.rm(p, { recursive: true, force: true });
|
||
|
|
} else if (fs.rmdir) {
|
||
|
|
// rmdir with recursive option deprecated in some nodes but try
|
||
|
|
await fs.rmdir(p, { recursive: true });
|
||
|
|
} else {
|
||
|
|
throw new Error("No supported fs remove method found");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function removePath(p) {
|
||
|
|
if (FLAGS.verbose) console.log("Removing:", p);
|
||
|
|
try {
|
||
|
|
// Prefer Node API
|
||
|
|
await removeWithNode(p);
|
||
|
|
return { ok: true };
|
||
|
|
} catch (nodeErr) {
|
||
|
|
// Fallback to shell command
|
||
|
|
const isWin = os.platform() === "win32";
|
||
|
|
const cmd = isWin ? `rd /s /q "${p}"` : `rm -rf "${p}"`;
|
||
|
|
try {
|
||
|
|
await execPromise(cmd);
|
||
|
|
return { ok: true, fallback: true };
|
||
|
|
} catch (shellErr) {
|
||
|
|
return { ok: false, error: shellErr };
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function confirmPrompt(message) {
|
||
|
|
if (FLAGS.yes) return true;
|
||
|
|
const rl = readline.createInterface({
|
||
|
|
input: process.stdin,
|
||
|
|
output: process.stdout
|
||
|
|
});
|
||
|
|
const answer = await new Promise((res) =>
|
||
|
|
rl.question(`${message} (y/N): `, (ans) => {
|
||
|
|
rl.close();
|
||
|
|
res(ans);
|
||
|
|
})
|
||
|
|
);
|
||
|
|
return /^y(es)?$/i.test(answer.trim());
|
||
|
|
}
|
||
|
|
|
||
|
|
async function run() {
|
||
|
|
console.log("Project root:", PROJECT_ROOT);
|
||
|
|
const resolved = resolveTargets(PROJECT_ROOT);
|
||
|
|
const existing = [];
|
||
|
|
for (const t of resolved) {
|
||
|
|
if (await pathExists(t.abs)) existing.push(t);
|
||
|
|
}
|
||
|
|
if (existing.length === 0) {
|
||
|
|
console.log("No matching targets found. Nothing to do.");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
console.log("Will remove the following paths:");
|
||
|
|
for (const e of existing) console.log(" -", e.rel);
|
||
|
|
if (FLAGS.dryRun) {
|
||
|
|
console.log("\nDry-run mode enabled. No files will be removed.");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const ok = await confirmPrompt("Proceed to delete the listed paths?");
|
||
|
|
if (!ok) {
|
||
|
|
console.log("Aborted by user.");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const results = [];
|
||
|
|
for (const e of existing) {
|
||
|
|
try {
|
||
|
|
const res = await removePath(e.abs);
|
||
|
|
results.push({ target: e.rel, success: res.ok, info: res });
|
||
|
|
console.log(res.ok ? `Deleted: ${e.rel}` : `Failed: ${e.rel}`);
|
||
|
|
} catch (err) {
|
||
|
|
results.push({ target: e.rel, success: false, info: err });
|
||
|
|
console.log(`Failed: ${e.rel} — ${err?.message || err}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const deleted = results.filter((r) => r.success).length;
|
||
|
|
const failed = results.length - deleted;
|
||
|
|
console.log(`\nSummary: ${deleted} deleted, ${failed} failed.`);
|
||
|
|
if (failed > 0) {
|
||
|
|
console.log("Failed items:");
|
||
|
|
for (const f of results.filter((r) => !r.success))
|
||
|
|
console.log(" -", f.target);
|
||
|
|
}
|
||
|
|
console.log("Done.");
|
||
|
|
}
|
||
|
|
|
||
|
|
run().catch((err) => {
|
||
|
|
console.error("Unexpected error:", err);
|
||
|
|
process.exitCode = 1;
|
||
|
|
});
|