fix: remove unused
This commit is contained in:
@@ -26,8 +26,7 @@
|
||||
"lint:fix": "biome check --write .",
|
||||
"lint:workspace": "pnpm dlx sherif@latest",
|
||||
"typecheck": "pnpm -r typecheck",
|
||||
"update-simple-git-hooks": "npx simple-git-hooks",
|
||||
"deps:unused": "node tooling/unused-deps.mjs"
|
||||
"update-simple-git-hooks": "npx simple-git-hooks"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"pre-push": "pnpm typecheck && pnpm test"
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { spawn } from 'node:child_process';
|
||||
import { promises as fs } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import url from 'node:url';
|
||||
|
||||
// Lazy import depcheck to avoid hard crash if not installed
|
||||
async function loadDepcheck() {
|
||||
try {
|
||||
const mod = await import('depcheck');
|
||||
return mod.default ?? mod;
|
||||
} catch (err) {
|
||||
console.error(
|
||||
'depcheck is not installed. Install it with: pnpm -w add -D depcheck',
|
||||
);
|
||||
process.exitCode = 1;
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(__dirname, '..');
|
||||
|
||||
const DEFAULT_IGNORE_DIRS = [
|
||||
'node_modules',
|
||||
'dist',
|
||||
'build',
|
||||
'.next',
|
||||
'.nuxt',
|
||||
'.svelte-kit',
|
||||
'.output',
|
||||
'.turbo',
|
||||
'coverage',
|
||||
'.vercel',
|
||||
'.cache',
|
||||
'.astro',
|
||||
'.pnpm',
|
||||
];
|
||||
|
||||
function parseArgs() {
|
||||
const args = new Set(process.argv.slice(2));
|
||||
return {
|
||||
json: args.has('--json') || args.has('-j'),
|
||||
};
|
||||
}
|
||||
|
||||
async function fileExists(p) {
|
||||
try {
|
||||
await fs.access(p);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function readWorkspacePatterns() {
|
||||
const workspaceFile = path.join(repoRoot, 'pnpm-workspace.yaml');
|
||||
if (!(await fileExists(workspaceFile))) return [];
|
||||
const content = await fs.readFile(workspaceFile, 'utf8');
|
||||
const patterns = [];
|
||||
for (const line of content.split(/\r?\n/)) {
|
||||
const m = line.match(/^\s*-\s*"?([^"#]+)"?\s*(?:#.*)?$/);
|
||||
if (m) {
|
||||
patterns.push(m[1].trim());
|
||||
}
|
||||
}
|
||||
return patterns.filter(Boolean);
|
||||
}
|
||||
|
||||
async function isPackageDir(dir) {
|
||||
const pkgPath = path.join(dir, 'package.json');
|
||||
return fileExists(pkgPath);
|
||||
}
|
||||
|
||||
async function listSubdirs(dir) {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
return entries
|
||||
.filter((e) => e.isDirectory())
|
||||
.map((e) => path.join(dir, e.name));
|
||||
}
|
||||
|
||||
async function findPackagesFromPattern(pattern) {
|
||||
// Normalize pattern relative to repo root
|
||||
const absolutePattern = path.resolve(repoRoot, pattern);
|
||||
if (pattern.endsWith('/**')) {
|
||||
const baseDir = absolutePattern.slice(0, -3); // remove /**
|
||||
return await findPackagesRecursively(baseDir);
|
||||
}
|
||||
if (pattern.endsWith('/*')) {
|
||||
const baseDir = absolutePattern.slice(0, -2); // remove /*
|
||||
return (await listSubdirs(baseDir)).filterAsync(isPackageDir);
|
||||
}
|
||||
// Direct path
|
||||
return (await isPackageDir(absolutePattern)) ? [absolutePattern] : [];
|
||||
}
|
||||
|
||||
async function findPackagesRecursively(startDir) {
|
||||
const results = [];
|
||||
async function walk(dir) {
|
||||
if (!(await fileExists(dir))) return;
|
||||
if (await isPackageDir(dir)) results.push(dir);
|
||||
const subdirs = await listSubdirs(dir);
|
||||
for (const sub of subdirs) {
|
||||
await walk(sub);
|
||||
}
|
||||
}
|
||||
await walk(startDir);
|
||||
return results;
|
||||
}
|
||||
|
||||
// Add filterAsync utility on arrays
|
||||
Object.defineProperty(Array.prototype, 'filterAsync', {
|
||||
value: async function (predicate) {
|
||||
const results = await Promise.all(this.map(predicate));
|
||||
return this.filter((_, i) => results[i]);
|
||||
},
|
||||
enumerable: false,
|
||||
});
|
||||
|
||||
async function discoverWorkspacePackageDirs() {
|
||||
const patterns = await readWorkspacePatterns();
|
||||
const discovered = new Set();
|
||||
for (const pattern of patterns) {
|
||||
const pkgs = await findPackagesFromPattern(pattern);
|
||||
for (const p of pkgs) discovered.add(path.resolve(p));
|
||||
}
|
||||
// Always include repo root as well
|
||||
discovered.add(repoRoot);
|
||||
return Array.from(discovered);
|
||||
}
|
||||
|
||||
async function runDepcheckOnDir(depcheck, dir) {
|
||||
const result = await depcheck(dir, {
|
||||
ignoreBinPackage: false,
|
||||
ignoreDirs: DEFAULT_IGNORE_DIRS,
|
||||
ignoreMatches: [],
|
||||
specials: [
|
||||
depcheck.special.eslint,
|
||||
depcheck.special.typescript,
|
||||
depcheck.special.webpack,
|
||||
depcheck.special.rollup,
|
||||
depcheck.special.babel,
|
||||
depcheck.special.postcss,
|
||||
],
|
||||
});
|
||||
return {
|
||||
dir,
|
||||
unused: {
|
||||
dependencies: result.dependencies ?? [],
|
||||
devDependencies: result.devDependencies ?? [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function relative(p) {
|
||||
return path.relative(repoRoot, p) || '.';
|
||||
}
|
||||
|
||||
function printHuman(results) {
|
||||
let any = false;
|
||||
for (const { dir, unused } of results) {
|
||||
if (unused.dependencies.length + unused.devDependencies.length === 0)
|
||||
continue;
|
||||
any = true;
|
||||
console.log(`\n${relative(dir)}:`);
|
||||
if (unused.dependencies.length) {
|
||||
console.log(` unused dependencies (${unused.dependencies.length}):`);
|
||||
for (const d of unused.dependencies) console.log(` - ${d}`);
|
||||
}
|
||||
if (unused.devDependencies.length) {
|
||||
console.log(
|
||||
` unused devDependencies (${unused.devDependencies.length}):`,
|
||||
);
|
||||
for (const d of unused.devDependencies) console.log(` - ${d}`);
|
||||
}
|
||||
}
|
||||
if (!any) console.log('No unused dependencies found.');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = parseArgs();
|
||||
const depcheck = await loadDepcheck();
|
||||
const packageDirs = await discoverWorkspacePackageDirs();
|
||||
const checks = await Promise.all(
|
||||
packageDirs.map((dir) => runDepcheckOnDir(depcheck, dir)),
|
||||
);
|
||||
if (args.json) {
|
||||
const compact = checks
|
||||
.map(({ dir, unused }) => ({ dir: relative(dir), ...unused }))
|
||||
.filter((r) => r.dependencies.length || r.devDependencies.length);
|
||||
console.log(JSON.stringify(compact, null, 2));
|
||||
} else {
|
||||
printHuman(checks);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
Reference in New Issue
Block a user