fix: remove unused
This commit is contained in:
@@ -26,8 +26,7 @@
|
|||||||
"lint:fix": "biome check --write .",
|
"lint:fix": "biome check --write .",
|
||||||
"lint:workspace": "pnpm dlx sherif@latest",
|
"lint:workspace": "pnpm dlx sherif@latest",
|
||||||
"typecheck": "pnpm -r typecheck",
|
"typecheck": "pnpm -r typecheck",
|
||||||
"update-simple-git-hooks": "npx simple-git-hooks",
|
"update-simple-git-hooks": "npx simple-git-hooks"
|
||||||
"deps:unused": "node tooling/unused-deps.mjs"
|
|
||||||
},
|
},
|
||||||
"simple-git-hooks": {
|
"simple-git-hooks": {
|
||||||
"pre-push": "pnpm typecheck && pnpm test"
|
"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