feat: dashboard v2, esm, upgrades (#211)
* esm * wip * wip * wip * wip * wip * wip * subscription notice * wip * wip * wip * fix envs * fix: update docker build * fix * esm/types * delete dashboard :D * add patches to dockerfiles * update packages + catalogs + ts * wip * remove native libs * ts * improvements * fix redirects and fetching session * try fix favicon * fixes * fix * order and resize reportds within a dashboard * improvements * wip * added userjot to dashboard * fix * add op * wip * different cache key * improve date picker * fix table * event details loading * redo onboarding completely * fix login * fix * fix * extend session, billing and improve bars * fix * reduce price on 10M
This commit is contained in:
committed by
GitHub
parent
436e81ecc9
commit
81a7e5d62e
@@ -13,8 +13,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openpanel/tsconfig": "workspace:*",
|
||||
"@types/node": "20.14.8",
|
||||
"@types/node": "catalog:",
|
||||
"@types/semver": "^7.5.4",
|
||||
"typescript": "^5.2.2"
|
||||
"typescript": "catalog:"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
import arg from 'arg';
|
||||
import type { ReleaseType } from 'semver';
|
||||
import semver, { RELEASE_TYPES } from 'semver';
|
||||
|
||||
@@ -10,14 +10,16 @@
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"allowUnreachableCode": true
|
||||
"allowUnreachableCode": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"verbatimModuleSyntax": true
|
||||
},
|
||||
"exclude": ["node_modules", "build", "dist"]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "@openpanel/tsconfig",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"files": ["base.json", "sdk.json", "tsup.config.json"],
|
||||
"devDependencies": {
|
||||
|
||||
201
tooling/unused-deps.mjs
Normal file
201
tooling/unused-deps.mjs
Normal file
@@ -0,0 +1,201 @@
|
||||
#!/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