travel to another computer

This commit is contained in:
Lucas Tettamanti
2026-01-10 12:39:32 -03:00
parent ce96df9e30
commit 2d01972619
7 changed files with 1200 additions and 96 deletions

100
scripts/scan-unused.mjs Normal file
View File

@@ -0,0 +1,100 @@
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
/**
* Scan simple de archivos JS alcanzables desde un entrypoint,
* siguiendo imports estáticos: `import ... from "./x.js"` y `await import("./x.js")`.
*
* OJO: no entiende requires dinámicos ni construcciones complejas.
* Útil para detectar archivos claramente no usados.
*/
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const root = path.resolve(__dirname, "..");
const entry = path.join(root, "index.js");
const srcDir = path.join(root, "src");
function listJsFiles(dir) {
const out = [];
const stack = [dir];
while (stack.length) {
const cur = stack.pop();
const items = fs.readdirSync(cur, { withFileTypes: true });
for (const it of items) {
const p = path.join(cur, it.name);
if (it.isDirectory()) stack.push(p);
else if (it.isFile() && p.endsWith(".js")) out.push(p);
}
}
return out;
}
function readText(p) {
return fs.readFileSync(p, "utf8");
}
function resolveLocalImport(fromFile, spec) {
if (!spec.startsWith(".")) return null;
const base = path.resolve(path.dirname(fromFile), spec);
const candidates = [];
if (base.endsWith(".js")) candidates.push(base);
else {
candidates.push(`${base}.js`);
candidates.push(path.join(base, "index.js"));
}
for (const c of candidates) {
if (fs.existsSync(c) && fs.statSync(c).isFile()) return c;
}
return null;
}
function extractImports(code) {
const out = new Set();
// static import ... from "x"
for (const m of code.matchAll(/import\s+[^;]*?\s+from\s+["']([^"']+)["']/g)) {
out.add(m[1]);
}
// side-effect import "x"
for (const m of code.matchAll(/import\s+["']([^"']+)["']/g)) {
out.add(m[1]);
}
// dynamic import("x") or await import("x")
for (const m of code.matchAll(/import\(\s*["']([^"']+)["']\s*\)/g)) {
out.add(m[1]);
}
return [...out];
}
const allSrc = listJsFiles(srcDir);
const all = [entry, ...allSrc];
const reachable = new Set();
const queue = [entry];
while (queue.length) {
const f = queue.shift();
if (reachable.has(f)) continue;
reachable.add(f);
let code = "";
try {
code = readText(f);
} catch {
continue;
}
const specs = extractImports(code);
for (const s of specs) {
const resolved = resolveLocalImport(f, s);
if (resolved && !reachable.has(resolved)) queue.push(resolved);
}
}
const unused = allSrc
.filter((f) => !reachable.has(f))
.map((f) => path.relative(root, f).replace(/\\/g, "/"))
.sort();
console.log(JSON.stringify({ entry: path.relative(root, entry), reachable_count: reachable.size, total_src: allSrc.length, unused }, null, 2));