@kb-labs/devkit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursorrules +32 -0
- package/.github/CODEOWNERS +2 -0
- package/.github/actions/setup-node-pnpm/action.yml +47 -0
- package/.github/workflow-templates/ci.yml +13 -0
- package/.github/workflow-templates/drift-check.yml +10 -0
- package/.github/workflow-templates/profiles-validate.yml +16 -0
- package/.github/workflow-templates/release.yml +8 -0
- package/.github/workflows/ci-reusable.yml +131 -0
- package/.github/workflows/drift-check-reusable.yml +23 -0
- package/.github/workflows/fixtures.yml +74 -0
- package/.github/workflows/profiles-validate-reusable.yml +67 -0
- package/.github/workflows/release-reusable.yml +50 -0
- package/.vscode/settings.json +23 -0
- package/AGENTS.md +130 -0
- package/LICENSE +21 -0
- package/README.md +1542 -0
- package/agents/devkit-maintainer/context.globs +15 -0
- package/agents/devkit-maintainer/permissions.yml +17 -0
- package/agents/devkit-maintainer/prompt.md +28 -0
- package/agents/devkit-maintainer/runbook.md +31 -0
- package/agents/docs-crafter/prompt.md +24 -0
- package/agents/docs-crafter/runbook.md +18 -0
- package/agents/release-manager/context.globs +7 -0
- package/agents/release-manager/prompt.md +27 -0
- package/agents/release-manager/runbook.md +17 -0
- package/agents/test-generator/context.globs +7 -0
- package/agents/test-generator/prompt.md +27 -0
- package/agents/test-generator/runbook.md +18 -0
- package/bin/devkit-architecture.mjs +1225 -0
- package/bin/devkit-build-order.mjs +500 -0
- package/bin/devkit-check-build-readiness.mjs +222 -0
- package/bin/devkit-check-commands.mjs +394 -0
- package/bin/devkit-check-configs.mjs +461 -0
- package/bin/devkit-check-deprecated.mjs +532 -0
- package/bin/devkit-check-duplicates.mjs +431 -0
- package/bin/devkit-check-exports.mjs +561 -0
- package/bin/devkit-check-imports.mjs +712 -0
- package/bin/devkit-check-paths.mjs +670 -0
- package/bin/devkit-check-scripts.mjs +335 -0
- package/bin/devkit-check-structure.mjs +495 -0
- package/bin/devkit-check-types.mjs +450 -0
- package/bin/devkit-ci.mjs +261 -0
- package/bin/devkit-core-gate.mjs +225 -0
- package/bin/devkit-fix-deps.mjs +1169 -0
- package/bin/devkit-freshness.mjs +199 -0
- package/bin/devkit-health.mjs +489 -0
- package/bin/devkit-migrate-configs.mjs +370 -0
- package/bin/devkit-paths.mjs +192 -0
- package/bin/devkit-stats.mjs +453 -0
- package/bin/devkit-sync.mjs +12 -0
- package/bin/devkit-tsup-external.mjs +148 -0
- package/bin/devkit-types-audit.mjs +615 -0
- package/bin/devkit-types-order.mjs +637 -0
- package/bin/devkit-validate-naming.mjs +203 -0
- package/bin/devkit-visualize.mjs +452 -0
- package/bin/kb-devkit-qa-history.mjs +453 -0
- package/bin/kb-devkit-qa.mjs +915 -0
- package/eslint/node.js +134 -0
- package/eslint/react.js +260 -0
- package/package.json +186 -0
- package/prettier/index.json +10 -0
- package/sync/index.mjs +693 -0
- package/templates/configs/README.md +306 -0
- package/templates/configs/eslint.config.js +27 -0
- package/templates/configs/package.json.bin +25 -0
- package/templates/configs/package.json.lib +30 -0
- package/templates/configs/tsconfig.build.json +15 -0
- package/templates/configs/tsconfig.json +9 -0
- package/templates/configs/tsup.config.bin.ts +34 -0
- package/templates/configs/tsup.config.cli.ts +41 -0
- package/templates/configs/tsup.config.dual.ts +46 -0
- package/templates/configs/tsup.config.ts +36 -0
- package/templates/package-json/README.md +290 -0
- package/templates/package-json/package.json.bin.template +40 -0
- package/templates/package-json/package.json.template +45 -0
- package/tsconfig/base.json +21 -0
- package/tsconfig/cli.json +12 -0
- package/tsconfig/dist/__tests__/cache.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/cache.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/cache.spec.js +85 -0
- package/tsconfig/dist/__tests__/cache.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/fs-atomic.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/fs-atomic.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/fs-atomic.spec.js +153 -0
- package/tsconfig/dist/__tests__/fs-atomic.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/init-workspace.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/init-workspace.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/init-workspace.spec.js +99 -0
- package/tsconfig/dist/__tests__/init-workspace.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/kb-error.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/kb-error.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/kb-error.spec.js +190 -0
- package/tsconfig/dist/__tests__/kb-error.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/preset-lockfile.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/preset-lockfile.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/preset-lockfile.spec.js +142 -0
- package/tsconfig/dist/__tests__/preset-lockfile.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/product-config-profiles.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/product-config-profiles.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/product-config-profiles.spec.js +100 -0
- package/tsconfig/dist/__tests__/product-config-profiles.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/product-config.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/product-config.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/product-config.spec.js +298 -0
- package/tsconfig/dist/__tests__/product-config.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/runtime.spec.d.ts +2 -0
- package/tsconfig/dist/__tests__/runtime.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/runtime.spec.js +127 -0
- package/tsconfig/dist/__tests__/runtime.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/upsert-lockfile.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/upsert-lockfile.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/upsert-lockfile.spec.js +251 -0
- package/tsconfig/dist/__tests__/upsert-lockfile.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/validate-config.spec.d.ts +2 -0
- package/tsconfig/dist/__tests__/validate-config.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/validate-config.spec.js +14 -0
- package/tsconfig/dist/__tests__/validate-config.spec.js.map +1 -0
- package/tsconfig/dist/api/init-workspace.d.ts +10 -0
- package/tsconfig/dist/api/init-workspace.d.ts.map +1 -0
- package/tsconfig/dist/api/init-workspace.js +191 -0
- package/tsconfig/dist/api/init-workspace.js.map +1 -0
- package/tsconfig/dist/api/product-config.d.ts +21 -0
- package/tsconfig/dist/api/product-config.d.ts.map +1 -0
- package/tsconfig/dist/api/product-config.js +192 -0
- package/tsconfig/dist/api/product-config.js.map +1 -0
- package/tsconfig/dist/api/read-config.d.ts +22 -0
- package/tsconfig/dist/api/read-config.d.ts.map +1 -0
- package/tsconfig/dist/api/read-config.js +105 -0
- package/tsconfig/dist/api/read-config.js.map +1 -0
- package/tsconfig/dist/api/upsert-lockfile.d.ts +10 -0
- package/tsconfig/dist/api/upsert-lockfile.d.ts.map +1 -0
- package/tsconfig/dist/api/upsert-lockfile.js +63 -0
- package/tsconfig/dist/api/upsert-lockfile.js.map +1 -0
- package/tsconfig/dist/cache/fs-cache.d.ts +38 -0
- package/tsconfig/dist/cache/fs-cache.d.ts.map +1 -0
- package/tsconfig/dist/cache/fs-cache.js +142 -0
- package/tsconfig/dist/cache/fs-cache.js.map +1 -0
- package/tsconfig/dist/errors/kb-error.d.ts +32 -0
- package/tsconfig/dist/errors/kb-error.d.ts.map +1 -0
- package/tsconfig/dist/errors/kb-error.js +54 -0
- package/tsconfig/dist/errors/kb-error.js.map +1 -0
- package/tsconfig/dist/fs/__tests__/fs.spec.d.ts +2 -0
- package/tsconfig/dist/fs/__tests__/fs.spec.d.ts.map +1 -0
- package/tsconfig/dist/fs/__tests__/fs.spec.js +22 -0
- package/tsconfig/dist/fs/__tests__/fs.spec.js.map +1 -0
- package/tsconfig/dist/fs/fs.d.ts +6 -0
- package/tsconfig/dist/fs/fs.d.ts.map +1 -0
- package/tsconfig/dist/fs/fs.js +12 -0
- package/tsconfig/dist/fs/fs.js.map +1 -0
- package/tsconfig/dist/fs/index.d.ts +2 -0
- package/tsconfig/dist/fs/index.d.ts.map +1 -0
- package/tsconfig/dist/fs/index.js +2 -0
- package/tsconfig/dist/fs/index.js.map +1 -0
- package/tsconfig/dist/hash/config-hash.d.ts +17 -0
- package/tsconfig/dist/hash/config-hash.d.ts.map +1 -0
- package/tsconfig/dist/hash/config-hash.js +55 -0
- package/tsconfig/dist/hash/config-hash.js.map +1 -0
- package/tsconfig/dist/index.d.ts +5 -0
- package/tsconfig/dist/index.d.ts.map +1 -0
- package/tsconfig/dist/index.js +5 -0
- package/tsconfig/dist/index.js.map +1 -0
- package/tsconfig/dist/lockfile/lockfile.d.ts +54 -0
- package/tsconfig/dist/lockfile/lockfile.d.ts.map +1 -0
- package/tsconfig/dist/lockfile/lockfile.js +141 -0
- package/tsconfig/dist/lockfile/lockfile.js.map +1 -0
- package/tsconfig/dist/logging/__tests__/logger.spec.d.ts +2 -0
- package/tsconfig/dist/logging/__tests__/logger.spec.d.ts.map +1 -0
- package/tsconfig/dist/logging/__tests__/logger.spec.js +65 -0
- package/tsconfig/dist/logging/__tests__/logger.spec.js.map +1 -0
- package/tsconfig/dist/logging/__tests__/redaction.spec.d.ts +2 -0
- package/tsconfig/dist/logging/__tests__/redaction.spec.d.ts.map +1 -0
- package/tsconfig/dist/logging/__tests__/redaction.spec.js +34 -0
- package/tsconfig/dist/logging/__tests__/redaction.spec.js.map +1 -0
- package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.d.ts +2 -0
- package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.d.ts.map +1 -0
- package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.js +90 -0
- package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.js.map +1 -0
- package/tsconfig/dist/logging/index.d.ts +6 -0
- package/tsconfig/dist/logging/index.d.ts.map +1 -0
- package/tsconfig/dist/logging/index.js +6 -0
- package/tsconfig/dist/logging/index.js.map +1 -0
- package/tsconfig/dist/logging/logger.d.ts +9 -0
- package/tsconfig/dist/logging/logger.d.ts.map +1 -0
- package/tsconfig/dist/logging/logger.js +101 -0
- package/tsconfig/dist/logging/logger.js.map +1 -0
- package/tsconfig/dist/logging/redaction.d.ts +7 -0
- package/tsconfig/dist/logging/redaction.d.ts.map +1 -0
- package/tsconfig/dist/logging/redaction.js +23 -0
- package/tsconfig/dist/logging/redaction.js.map +1 -0
- package/tsconfig/dist/logging/sinks/json.d.ts +4 -0
- package/tsconfig/dist/logging/sinks/json.d.ts.map +1 -0
- package/tsconfig/dist/logging/sinks/json.js +23 -0
- package/tsconfig/dist/logging/sinks/json.js.map +1 -0
- package/tsconfig/dist/logging/sinks/stdout.d.ts +3 -0
- package/tsconfig/dist/logging/sinks/stdout.d.ts.map +1 -0
- package/tsconfig/dist/logging/sinks/stdout.js +24 -0
- package/tsconfig/dist/logging/sinks/stdout.js.map +1 -0
- package/tsconfig/dist/logging/types/index.d.ts +2 -0
- package/tsconfig/dist/logging/types/index.d.ts.map +1 -0
- package/tsconfig/dist/logging/types/index.js +2 -0
- package/tsconfig/dist/logging/types/index.js.map +1 -0
- package/tsconfig/dist/logging/types/types.d.ts +37 -0
- package/tsconfig/dist/logging/types/types.d.ts.map +1 -0
- package/tsconfig/dist/logging/types/types.js +2 -0
- package/tsconfig/dist/logging/types/types.js.map +1 -0
- package/tsconfig/dist/merge/layered-merge.d.ts +16 -0
- package/tsconfig/dist/merge/layered-merge.d.ts.map +1 -0
- package/tsconfig/dist/merge/layered-merge.js +97 -0
- package/tsconfig/dist/merge/layered-merge.js.map +1 -0
- package/tsconfig/dist/preset/resolve-preset.d.ts +29 -0
- package/tsconfig/dist/preset/resolve-preset.d.ts.map +1 -0
- package/tsconfig/dist/preset/resolve-preset.js +104 -0
- package/tsconfig/dist/preset/resolve-preset.js.map +1 -0
- package/tsconfig/dist/repo/__tests__/repo.spec.d.ts +2 -0
- package/tsconfig/dist/repo/__tests__/repo.spec.d.ts.map +1 -0
- package/tsconfig/dist/repo/__tests__/repo.spec.js +25 -0
- package/tsconfig/dist/repo/__tests__/repo.spec.js.map +1 -0
- package/tsconfig/dist/repo/index.d.ts +2 -0
- package/tsconfig/dist/repo/index.d.ts.map +1 -0
- package/tsconfig/dist/repo/index.js +2 -0
- package/tsconfig/dist/repo/index.js.map +1 -0
- package/tsconfig/dist/repo/repo.d.ts +6 -0
- package/tsconfig/dist/repo/repo.d.ts.map +1 -0
- package/tsconfig/dist/repo/repo.js +25 -0
- package/tsconfig/dist/repo/repo.js.map +1 -0
- package/tsconfig/dist/runtime/index.d.ts +2 -0
- package/tsconfig/dist/runtime/index.d.ts.map +1 -0
- package/tsconfig/dist/runtime/index.js +2 -0
- package/tsconfig/dist/runtime/index.js.map +1 -0
- package/tsconfig/dist/runtime/runtime.d.ts +46 -0
- package/tsconfig/dist/runtime/runtime.d.ts.map +1 -0
- package/tsconfig/dist/runtime/runtime.js +126 -0
- package/tsconfig/dist/runtime/runtime.js.map +1 -0
- package/tsconfig/dist/tsconfig.tools.tsbuildinfo +1 -0
- package/tsconfig/dist/tsconfig.tsbuildinfo +1 -0
- package/tsconfig/dist/types/index.d.ts +2 -0
- package/tsconfig/dist/types/index.d.ts.map +1 -0
- package/tsconfig/dist/types/index.js +2 -0
- package/tsconfig/dist/types/index.js.map +1 -0
- package/tsconfig/dist/types/init.d.ts +34 -0
- package/tsconfig/dist/types/init.d.ts.map +1 -0
- package/tsconfig/dist/types/init.js +6 -0
- package/tsconfig/dist/types/init.js.map +1 -0
- package/tsconfig/dist/types/preset.d.ts +27 -0
- package/tsconfig/dist/types/preset.d.ts.map +1 -0
- package/tsconfig/dist/types/preset.js +6 -0
- package/tsconfig/dist/types/preset.js.map +1 -0
- package/tsconfig/dist/types/types.d.ts +6 -0
- package/tsconfig/dist/types/types.d.ts.map +1 -0
- package/tsconfig/dist/types/types.js +2 -0
- package/tsconfig/dist/types/types.js.map +1 -0
- package/tsconfig/dist/utils/__tests__/env.spec.d.ts +2 -0
- package/tsconfig/dist/utils/__tests__/env.spec.d.ts.map +1 -0
- package/tsconfig/dist/utils/__tests__/env.spec.js +33 -0
- package/tsconfig/dist/utils/__tests__/env.spec.js.map +1 -0
- package/tsconfig/dist/utils/env.d.ts +7 -0
- package/tsconfig/dist/utils/env.d.ts.map +1 -0
- package/tsconfig/dist/utils/env.js +25 -0
- package/tsconfig/dist/utils/env.js.map +1 -0
- package/tsconfig/dist/utils/fs-atomic.d.ts +15 -0
- package/tsconfig/dist/utils/fs-atomic.d.ts.map +1 -0
- package/tsconfig/dist/utils/fs-atomic.js +45 -0
- package/tsconfig/dist/utils/fs-atomic.js.map +1 -0
- package/tsconfig/dist/utils/index.d.ts +3 -0
- package/tsconfig/dist/utils/index.d.ts.map +1 -0
- package/tsconfig/dist/utils/index.js +3 -0
- package/tsconfig/dist/utils/index.js.map +1 -0
- package/tsconfig/dist/utils/paths.d.ts +21 -0
- package/tsconfig/dist/utils/paths.d.ts.map +1 -0
- package/tsconfig/dist/utils/paths.js +32 -0
- package/tsconfig/dist/utils/paths.js.map +1 -0
- package/tsconfig/dist/utils/product-normalize.d.ts +27 -0
- package/tsconfig/dist/utils/product-normalize.d.ts.map +1 -0
- package/tsconfig/dist/utils/product-normalize.js +45 -0
- package/tsconfig/dist/utils/product-normalize.js.map +1 -0
- package/tsconfig/dist/validation/validate-config.d.ts +7 -0
- package/tsconfig/dist/validation/validate-config.d.ts.map +1 -0
- package/tsconfig/dist/validation/validate-config.js +22 -0
- package/tsconfig/dist/validation/validate-config.js.map +1 -0
- package/tsconfig/lib.json +13 -0
- package/tsconfig/node.json +12 -0
- package/tsconfig/react-app.json +8 -0
- package/tsconfig/react-lib.json +8 -0
- package/tsconfig/test.json +15 -0
- package/tsup/bin.js +172 -0
- package/tsup/dual.js +155 -0
- package/tsup/external-sync.mjs +41 -0
- package/tsup/external.mjs +109 -0
- package/tsup/node.js +65 -0
- package/tsup/react-lib.js +18 -0
- package/tsup/sdk.js +118 -0
- package/vite/react-app.js +50 -0
- package/vitest/node.js +37 -0
- package/vitest/react.js +39 -0
- package/vitest/vitest-setup.ts +9 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @kb-labs/devkit - Naming Convention Validator
|
|
5
|
+
*
|
|
6
|
+
* Validates that all packages follow the Pyramid Rule:
|
|
7
|
+
* @kb-labs/{repo}-{package}
|
|
8
|
+
*
|
|
9
|
+
* Folder name MUST match {repo}-{package}
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
|
|
18
|
+
// ANSI colors
|
|
19
|
+
const colors = {
|
|
20
|
+
reset: '\x1b[0m',
|
|
21
|
+
red: '\x1b[31m',
|
|
22
|
+
green: '\x1b[32m',
|
|
23
|
+
yellow: '\x1b[33m',
|
|
24
|
+
blue: '\x1b[34m',
|
|
25
|
+
gray: '\x1b[90m',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function log(message, color = 'reset') {
|
|
29
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function extractRepoName(repoPath) {
|
|
33
|
+
// kb-labs-core ā core
|
|
34
|
+
// kb-labs-cli ā cli
|
|
35
|
+
const match = repoPath.match(/kb-labs-([^/]+)/);
|
|
36
|
+
return match ? match[1] : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function validatePackage(packageJsonPath, repoName) {
|
|
40
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
41
|
+
const packageName = packageJson.name;
|
|
42
|
+
|
|
43
|
+
if (!packageName || !packageName.startsWith('@kb-labs/')) {
|
|
44
|
+
return { valid: true, skip: true, reason: 'Not a @kb-labs package' };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const folderPath = path.dirname(packageJsonPath);
|
|
48
|
+
const folderName = path.basename(folderPath);
|
|
49
|
+
|
|
50
|
+
// Extract package name without @kb-labs/ prefix
|
|
51
|
+
const shortName = packageName.replace('@kb-labs/', '');
|
|
52
|
+
|
|
53
|
+
// Expected pattern: {repo}-{package}
|
|
54
|
+
const expectedPrefix = `${repoName}-`;
|
|
55
|
+
|
|
56
|
+
// Check 1: Package name should start with repo prefix
|
|
57
|
+
if (!shortName.startsWith(expectedPrefix)) {
|
|
58
|
+
return {
|
|
59
|
+
valid: false,
|
|
60
|
+
packageName,
|
|
61
|
+
folderName,
|
|
62
|
+
expectedPackageName: `@kb-labs/${expectedPrefix}${folderName.replace(new RegExp(`^${repoName}-`), '')}`,
|
|
63
|
+
issue: `Package name missing repo prefix`,
|
|
64
|
+
suggestion: `Rename to @kb-labs/${expectedPrefix}${folderName.replace(new RegExp(`^${repoName}-`), '')}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check 2: Folder name should match package name
|
|
69
|
+
if (folderName !== shortName) {
|
|
70
|
+
return {
|
|
71
|
+
valid: false,
|
|
72
|
+
packageName,
|
|
73
|
+
folderName,
|
|
74
|
+
expectedFolderName: shortName,
|
|
75
|
+
issue: 'Folder name does not match package name',
|
|
76
|
+
suggestion: `Rename folder to ${shortName}`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Check 3: Folder name should have repo prefix
|
|
81
|
+
if (!folderName.startsWith(expectedPrefix)) {
|
|
82
|
+
return {
|
|
83
|
+
valid: false,
|
|
84
|
+
packageName,
|
|
85
|
+
folderName,
|
|
86
|
+
expectedFolderName: `${expectedPrefix}${folderName}`,
|
|
87
|
+
issue: 'Folder name missing repo prefix',
|
|
88
|
+
suggestion: `Rename folder to ${expectedPrefix}${folderName}`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { valid: true, packageName, folderName };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function findPackages(rootDir) {
|
|
96
|
+
const packages = [];
|
|
97
|
+
|
|
98
|
+
// Find all kb-labs-* directories
|
|
99
|
+
const entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
|
100
|
+
|
|
101
|
+
for (const entry of entries) {
|
|
102
|
+
if (!entry.isDirectory() || !entry.name.startsWith('kb-labs-')) {continue;}
|
|
103
|
+
|
|
104
|
+
const repoPath = path.join(rootDir, entry.name);
|
|
105
|
+
const packagesDir = path.join(repoPath, 'packages');
|
|
106
|
+
|
|
107
|
+
if (!fs.existsSync(packagesDir)) {continue;}
|
|
108
|
+
|
|
109
|
+
const repoName = extractRepoName(entry.name);
|
|
110
|
+
if (!repoName) {continue;}
|
|
111
|
+
|
|
112
|
+
// Find all package.json files in packages/
|
|
113
|
+
const packageDirs = fs.readdirSync(packagesDir, { withFileTypes: true });
|
|
114
|
+
|
|
115
|
+
for (const pkgDir of packageDirs) {
|
|
116
|
+
if (!pkgDir.isDirectory()) {continue;}
|
|
117
|
+
|
|
118
|
+
const packageJsonPath = path.join(packagesDir, pkgDir.name, 'package.json');
|
|
119
|
+
|
|
120
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
121
|
+
packages.push({
|
|
122
|
+
path: packageJsonPath,
|
|
123
|
+
repoName,
|
|
124
|
+
repoPath: entry.name,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return packages;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function main() {
|
|
134
|
+
const rootDir = process.cwd();
|
|
135
|
+
|
|
136
|
+
log('\nš¦ KB Labs Naming Convention Validator\n', 'blue');
|
|
137
|
+
log('Checking Pyramid Rule: @kb-labs/{repo}-{package}\n', 'gray');
|
|
138
|
+
|
|
139
|
+
const packages = findPackages(rootDir);
|
|
140
|
+
|
|
141
|
+
if (packages.length === 0) {
|
|
142
|
+
log('ā ļø No KB Labs packages found in current directory', 'yellow');
|
|
143
|
+
log(' Run this command from the monorepo root containing kb-labs-* folders\n', 'gray');
|
|
144
|
+
process.exit(0);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
log(`Found ${packages.length} packages to validate\n`, 'gray');
|
|
148
|
+
|
|
149
|
+
const violations = [];
|
|
150
|
+
const valid = [];
|
|
151
|
+
|
|
152
|
+
for (const pkg of packages) {
|
|
153
|
+
const result = validatePackage(pkg.path, pkg.repoName);
|
|
154
|
+
|
|
155
|
+
if (result.skip) {continue;}
|
|
156
|
+
|
|
157
|
+
if (result.valid) {
|
|
158
|
+
valid.push({ ...result, repo: pkg.repoPath });
|
|
159
|
+
} else {
|
|
160
|
+
violations.push({ ...result, repo: pkg.repoPath, path: pkg.path });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Print valid packages
|
|
165
|
+
if (valid.length > 0) {
|
|
166
|
+
log('ā
Valid packages:', 'green');
|
|
167
|
+
for (const v of valid) {
|
|
168
|
+
log(` ${v.folderName.padEnd(30)} ā ${v.packageName}`, 'gray');
|
|
169
|
+
}
|
|
170
|
+
log('');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Print violations
|
|
174
|
+
if (violations.length > 0) {
|
|
175
|
+
log(`ā Found ${violations.length} naming convention violation(s):\n`, 'red');
|
|
176
|
+
|
|
177
|
+
for (let i = 0; i < violations.length; i++) {
|
|
178
|
+
const v = violations[i];
|
|
179
|
+
log(`${i + 1}. ${v.repo}/packages/${v.folderName}/`, 'yellow');
|
|
180
|
+
log(` Issue: ${v.issue}`, 'red');
|
|
181
|
+
log(` Current package name: ${v.packageName}`, 'gray');
|
|
182
|
+
log(` Current folder name: ${v.folderName}`, 'gray');
|
|
183
|
+
|
|
184
|
+
if (v.expectedPackageName) {
|
|
185
|
+
log(` Expected package: ${v.expectedPackageName}`, 'green');
|
|
186
|
+
}
|
|
187
|
+
if (v.expectedFolderName) {
|
|
188
|
+
log(` Expected folder: ${v.expectedFolderName}`, 'green');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
log(` Suggestion: ${v.suggestion}`, 'blue');
|
|
192
|
+
log('');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
log('š See docs/naming-convention.md for the full guide\n', 'gray');
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
log(`ā
All ${valid.length} packages follow the Pyramid Rule!\n`, 'green');
|
|
200
|
+
process.exit(0);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
main();
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @kb-labs/devkit - Dependency Visualizer
|
|
5
|
+
*
|
|
6
|
+
* Generates visualizations:
|
|
7
|
+
* 1. Dependency graph (who depends on whom)
|
|
8
|
+
* 2. Package size metrics
|
|
9
|
+
* 3. Dependency tree (hierarchical view)
|
|
10
|
+
* 4. Module coupling analysis
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* kb-devkit-visualize # Show all visualizations
|
|
14
|
+
* kb-devkit-visualize --graph # Show dependency graph
|
|
15
|
+
* kb-devkit-visualize --package cli-core # Show dependencies of specific package
|
|
16
|
+
* kb-devkit-visualize --stats # Show package statistics
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import fs from 'node:fs';
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
|
|
23
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
|
|
25
|
+
// ANSI colors
|
|
26
|
+
const colors = {
|
|
27
|
+
reset: '\x1b[0m',
|
|
28
|
+
red: '\x1b[31m',
|
|
29
|
+
green: '\x1b[32m',
|
|
30
|
+
yellow: '\x1b[33m',
|
|
31
|
+
blue: '\x1b[34m',
|
|
32
|
+
magenta: '\x1b[35m',
|
|
33
|
+
cyan: '\x1b[36m',
|
|
34
|
+
gray: '\x1b[90m',
|
|
35
|
+
bold: '\x1b[1m',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function log(message, color = 'reset') {
|
|
39
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Parse CLI arguments
|
|
43
|
+
const args = process.argv.slice(2);
|
|
44
|
+
const options = {
|
|
45
|
+
package: args.find((arg) => arg.startsWith('--package='))?.split('=')[1],
|
|
46
|
+
graph: args.includes('--graph'),
|
|
47
|
+
stats: args.includes('--stats'),
|
|
48
|
+
tree: args.includes('--tree'),
|
|
49
|
+
all: !args.includes('--graph') && !args.includes('--stats') && !args.includes('--tree'),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Find all packages in monorepo
|
|
54
|
+
*/
|
|
55
|
+
function findPackages(rootDir, filterPackage) {
|
|
56
|
+
const packages = [];
|
|
57
|
+
const entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
|
58
|
+
|
|
59
|
+
for (const entry of entries) {
|
|
60
|
+
if (!entry.isDirectory() || !entry.name.startsWith('kb-labs-')) {continue;}
|
|
61
|
+
|
|
62
|
+
const repoPath = path.join(rootDir, entry.name);
|
|
63
|
+
const packagesDir = path.join(repoPath, 'packages');
|
|
64
|
+
|
|
65
|
+
if (!fs.existsSync(packagesDir)) {continue;}
|
|
66
|
+
|
|
67
|
+
const packageDirs = fs.readdirSync(packagesDir, { withFileTypes: true });
|
|
68
|
+
|
|
69
|
+
for (const pkgDir of packageDirs) {
|
|
70
|
+
if (!pkgDir.isDirectory()) {continue;}
|
|
71
|
+
|
|
72
|
+
// Filter by package name if specified
|
|
73
|
+
if (filterPackage && pkgDir.name !== filterPackage) {continue;}
|
|
74
|
+
|
|
75
|
+
const packageJsonPath = path.join(packagesDir, pkgDir.name, 'package.json');
|
|
76
|
+
|
|
77
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
78
|
+
packages.push(packageJsonPath);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return packages;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Build dependency graph
|
|
88
|
+
*/
|
|
89
|
+
function buildDependencyGraph(packages) {
|
|
90
|
+
const graph = new Map(); // package -> {dependencies, dependents, size}
|
|
91
|
+
const packageInfo = new Map(); // package -> {path, packageJson}
|
|
92
|
+
|
|
93
|
+
// First pass: collect all packages
|
|
94
|
+
for (const packagePath of packages) {
|
|
95
|
+
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));
|
|
96
|
+
const packageName = packageJson.name;
|
|
97
|
+
|
|
98
|
+
if (!packageName || !packageName.startsWith('@kb-labs/')) {continue;}
|
|
99
|
+
|
|
100
|
+
const packageDir = path.dirname(packagePath);
|
|
101
|
+
const size = calculatePackageSize(packageDir);
|
|
102
|
+
|
|
103
|
+
graph.set(packageName, {
|
|
104
|
+
dependencies: [],
|
|
105
|
+
dependents: [],
|
|
106
|
+
size,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
packageInfo.set(packageName, {
|
|
110
|
+
path: packagePath,
|
|
111
|
+
packageJson,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Second pass: build dependency relationships
|
|
116
|
+
for (const [packageName, info] of packageInfo.entries()) {
|
|
117
|
+
const allDeps = {
|
|
118
|
+
...info.packageJson.dependencies,
|
|
119
|
+
...info.packageJson.devDependencies,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
for (const dep of Object.keys(allDeps)) {
|
|
123
|
+
if (dep.startsWith('@kb-labs/') && graph.has(dep)) {
|
|
124
|
+
graph.get(packageName).dependencies.push(dep);
|
|
125
|
+
graph.get(dep).dependents.push(packageName);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return { graph, packageInfo };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Calculate package size (count of files)
|
|
135
|
+
*/
|
|
136
|
+
function calculatePackageSize(packageDir) {
|
|
137
|
+
const srcDir = path.join(packageDir, 'src');
|
|
138
|
+
|
|
139
|
+
if (!fs.existsSync(srcDir)) {
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let fileCount = 0;
|
|
144
|
+
let lineCount = 0;
|
|
145
|
+
|
|
146
|
+
function walk(dir) {
|
|
147
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
148
|
+
|
|
149
|
+
for (const entry of entries) {
|
|
150
|
+
const fullPath = path.join(dir, entry.name);
|
|
151
|
+
|
|
152
|
+
if (entry.isDirectory()) {
|
|
153
|
+
walk(fullPath);
|
|
154
|
+
} else if (entry.isFile() && /\.(ts|tsx|js|jsx)$/.test(entry.name)) {
|
|
155
|
+
fileCount++;
|
|
156
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
157
|
+
lineCount += content.split('\n').length;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
walk(srcDir);
|
|
163
|
+
|
|
164
|
+
return { fileCount, lineCount };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Render dependency graph
|
|
169
|
+
*/
|
|
170
|
+
function renderDependencyGraph(graph, packageInfo, filterPackage) {
|
|
171
|
+
log('\nš Dependency Graph:\n', 'blue');
|
|
172
|
+
|
|
173
|
+
const packages = Array.from(graph.keys());
|
|
174
|
+
|
|
175
|
+
if (filterPackage) {
|
|
176
|
+
// Show dependencies of specific package
|
|
177
|
+
const fullPackageName = packages.find((p) => p.includes(filterPackage));
|
|
178
|
+
|
|
179
|
+
if (!fullPackageName) {
|
|
180
|
+
log(` Package "${filterPackage}" not found\n`, 'red');
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const { dependencies, dependents } = graph.get(fullPackageName);
|
|
185
|
+
|
|
186
|
+
log(`Package: ${fullPackageName}\n`, 'cyan');
|
|
187
|
+
|
|
188
|
+
if (dependencies.length > 0) {
|
|
189
|
+
log(' Dependencies (packages this depends on):', 'yellow');
|
|
190
|
+
for (const dep of dependencies) {
|
|
191
|
+
log(` āā ${dep}`, 'gray');
|
|
192
|
+
}
|
|
193
|
+
log('');
|
|
194
|
+
} else {
|
|
195
|
+
log(' No dependencies', 'gray');
|
|
196
|
+
log('');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (dependents.length > 0) {
|
|
200
|
+
log(' Dependents (packages that depend on this):', 'green');
|
|
201
|
+
for (const dependent of dependents) {
|
|
202
|
+
log(` āā ${dependent}`, 'gray');
|
|
203
|
+
}
|
|
204
|
+
log('');
|
|
205
|
+
} else {
|
|
206
|
+
log(' No dependents', 'gray');
|
|
207
|
+
log('');
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
// Show full graph overview
|
|
211
|
+
log('Packages by dependency count:\n', 'gray');
|
|
212
|
+
|
|
213
|
+
const byDependencyCount = packages
|
|
214
|
+
.map((p) => ({
|
|
215
|
+
name: p,
|
|
216
|
+
dependencies: graph.get(p).dependencies.length,
|
|
217
|
+
dependents: graph.get(p).dependents.length,
|
|
218
|
+
}))
|
|
219
|
+
.sort((a, b) => b.dependencies - a.dependencies);
|
|
220
|
+
|
|
221
|
+
for (let i = 0; i < Math.min(byDependencyCount.length, 20); i++) {
|
|
222
|
+
const pkg = byDependencyCount[i];
|
|
223
|
+
const depBar = 'ā'.repeat(Math.min(pkg.dependencies, 30));
|
|
224
|
+
const deptBar = 'ā'.repeat(Math.min(pkg.dependents, 30));
|
|
225
|
+
|
|
226
|
+
log(`${(i + 1).toString().padStart(3)}. ${pkg.name}`, 'cyan');
|
|
227
|
+
log(` Deps: ${colors.yellow}${depBar}${colors.reset} ${pkg.dependencies}`, 'gray');
|
|
228
|
+
log(` Used: ${colors.green}${deptBar}${colors.reset} ${pkg.dependents}`, 'gray');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (byDependencyCount.length > 20) {
|
|
232
|
+
log(`\n ... and ${byDependencyCount.length - 20} more packages`, 'gray');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
log('');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Render package statistics
|
|
241
|
+
*/
|
|
242
|
+
function renderPackageStats(graph, packageInfo) {
|
|
243
|
+
log('\nš Package Statistics:\n', 'blue');
|
|
244
|
+
|
|
245
|
+
const packages = Array.from(graph.keys());
|
|
246
|
+
const stats = packages.map((p) => ({
|
|
247
|
+
name: p,
|
|
248
|
+
...graph.get(p),
|
|
249
|
+
repo: p.split('/')[1].split('-')[0], // Extract repo name (core, cli, etc.)
|
|
250
|
+
}));
|
|
251
|
+
|
|
252
|
+
// Group by repository
|
|
253
|
+
const byRepo = new Map();
|
|
254
|
+
for (const pkg of stats) {
|
|
255
|
+
if (!byRepo.has(pkg.repo)) {
|
|
256
|
+
byRepo.set(pkg.repo, []);
|
|
257
|
+
}
|
|
258
|
+
byRepo.get(pkg.repo).push(pkg);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
log('By Repository:\n', 'gray');
|
|
262
|
+
|
|
263
|
+
const repoStats = Array.from(byRepo.entries()).map(([repo, packages]) => ({
|
|
264
|
+
repo,
|
|
265
|
+
count: packages.length,
|
|
266
|
+
totalFiles: packages.reduce((sum, p) => sum + (p.size.fileCount || 0), 0),
|
|
267
|
+
totalLines: packages.reduce((sum, p) => sum + (p.size.lineCount || 0), 0),
|
|
268
|
+
}));
|
|
269
|
+
|
|
270
|
+
repoStats.sort((a, b) => b.count - a.count);
|
|
271
|
+
|
|
272
|
+
for (const stat of repoStats) {
|
|
273
|
+
log(` ${stat.repo.padEnd(15)} ${stat.count.toString().padStart(3)} packages`, 'cyan');
|
|
274
|
+
log(` ${''.padEnd(15)} ${stat.totalFiles.toString().padStart(3)} files, ${stat.totalLines.toLocaleString().padStart(7)} lines`, 'gray');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
log('');
|
|
278
|
+
|
|
279
|
+
// Overall stats
|
|
280
|
+
const totalPackages = packages.length;
|
|
281
|
+
const totalDeps = stats.reduce((sum, p) => sum + p.dependencies.length, 0);
|
|
282
|
+
const avgDeps = (totalDeps / totalPackages).toFixed(1);
|
|
283
|
+
const maxDeps = Math.max(...stats.map((p) => p.dependencies.length));
|
|
284
|
+
const totalFiles = stats.reduce((sum, p) => sum + (p.size.fileCount || 0), 0);
|
|
285
|
+
const totalLines = stats.reduce((sum, p) => sum + (p.size.lineCount || 0), 0);
|
|
286
|
+
|
|
287
|
+
log('Overall:', 'gray');
|
|
288
|
+
log(` Total packages: ${totalPackages}`, 'cyan');
|
|
289
|
+
log(` Total dependencies: ${totalDeps}`, 'yellow');
|
|
290
|
+
log(` Average deps/pkg: ${avgDeps}`, 'yellow');
|
|
291
|
+
log(` Max dependencies: ${maxDeps}`, 'yellow');
|
|
292
|
+
log(` Total files: ${totalFiles}`, 'green');
|
|
293
|
+
log(` Total lines: ${totalLines.toLocaleString()}`, 'green');
|
|
294
|
+
log('');
|
|
295
|
+
|
|
296
|
+
// Most depended-on packages
|
|
297
|
+
const mostUsed = stats
|
|
298
|
+
.filter((p) => p.dependents.length > 0)
|
|
299
|
+
.sort((a, b) => b.dependents.length - a.dependents.length)
|
|
300
|
+
.slice(0, 10);
|
|
301
|
+
|
|
302
|
+
if (mostUsed.length > 0) {
|
|
303
|
+
log('Most Depended-On Packages:\n', 'gray');
|
|
304
|
+
|
|
305
|
+
for (let i = 0; i < mostUsed.length; i++) {
|
|
306
|
+
const pkg = mostUsed[i];
|
|
307
|
+
const bar = 'ā'.repeat(Math.min(pkg.dependents.length, 40));
|
|
308
|
+
log(` ${(i + 1).toString().padStart(2)}. ${pkg.name}`, 'cyan');
|
|
309
|
+
log(` ${colors.green}${bar}${colors.reset} ${pkg.dependents.length} dependents`, 'gray');
|
|
310
|
+
}
|
|
311
|
+
log('');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Largest packages
|
|
315
|
+
const largest = stats
|
|
316
|
+
.filter((p) => p.size.lineCount > 0)
|
|
317
|
+
.sort((a, b) => b.size.lineCount - a.size.lineCount)
|
|
318
|
+
.slice(0, 10);
|
|
319
|
+
|
|
320
|
+
if (largest.length > 0) {
|
|
321
|
+
log('Largest Packages (by lines of code):\n', 'gray');
|
|
322
|
+
|
|
323
|
+
for (let i = 0; i < largest.length; i++) {
|
|
324
|
+
const pkg = largest[i];
|
|
325
|
+
log(` ${(i + 1).toString().padStart(2)}. ${pkg.name}`, 'cyan');
|
|
326
|
+
log(` ${pkg.size.lineCount.toLocaleString()} lines, ${pkg.size.fileCount} files`, 'gray');
|
|
327
|
+
}
|
|
328
|
+
log('');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Leaf packages (no dependents)
|
|
332
|
+
const leaves = stats.filter((p) => p.dependents.length === 0);
|
|
333
|
+
|
|
334
|
+
if (leaves.length > 0) {
|
|
335
|
+
log(`Leaf Packages (${leaves.length} packages with no dependents):\n`, 'gray');
|
|
336
|
+
|
|
337
|
+
for (let i = 0; i < Math.min(leaves.length, 10); i++) {
|
|
338
|
+
const pkg = leaves[i];
|
|
339
|
+
log(` ${(i + 1).toString().padStart(2)}. ${pkg.name}`, 'yellow');
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (leaves.length > 10) {
|
|
343
|
+
log(` ... and ${leaves.length - 10} more`, 'gray');
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
log('');
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Render dependency tree
|
|
352
|
+
*/
|
|
353
|
+
function renderDependencyTree(graph, packageInfo, filterPackage) {
|
|
354
|
+
log('\nš³ Dependency Tree:\n', 'blue');
|
|
355
|
+
|
|
356
|
+
const packages = Array.from(graph.keys());
|
|
357
|
+
|
|
358
|
+
if (!filterPackage) {
|
|
359
|
+
log(' Specify --package=<name> to see dependency tree\n', 'gray');
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const fullPackageName = packages.find((p) => p.includes(filterPackage));
|
|
364
|
+
|
|
365
|
+
if (!fullPackageName) {
|
|
366
|
+
log(` Package "${filterPackage}" not found\n`, 'red');
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const visited = new Set();
|
|
371
|
+
|
|
372
|
+
function printTree(packageName, depth = 0, prefix = '', isLast = true) {
|
|
373
|
+
if (visited.has(packageName)) {
|
|
374
|
+
log(`${prefix}${isLast ? 'āā' : 'āā'} ${packageName} ${colors.gray}(circular)${colors.reset}`, 'yellow');
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
visited.add(packageName);
|
|
379
|
+
|
|
380
|
+
const symbol = isLast ? 'āā' : 'āā';
|
|
381
|
+
const color = depth === 0 ? 'cyan' : 'gray';
|
|
382
|
+
log(`${prefix}${depth > 0 ? symbol : ''} ${packageName}`, color);
|
|
383
|
+
|
|
384
|
+
const { dependencies } = graph.get(packageName);
|
|
385
|
+
|
|
386
|
+
for (let i = 0; i < dependencies.length; i++) {
|
|
387
|
+
const dep = dependencies[i];
|
|
388
|
+
const isLastDep = i === dependencies.length - 1;
|
|
389
|
+
const newPrefix = depth === 0 ? ' ' : prefix + (isLast ? ' ' : 'ā ');
|
|
390
|
+
|
|
391
|
+
printTree(dep, depth + 1, newPrefix, isLastDep);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
visited.delete(packageName);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
printTree(fullPackageName);
|
|
398
|
+
|
|
399
|
+
log('');
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Main function
|
|
404
|
+
*/
|
|
405
|
+
function main() {
|
|
406
|
+
const rootDir = process.cwd();
|
|
407
|
+
|
|
408
|
+
log('\nšØ KB Labs Visualizer\n', 'blue');
|
|
409
|
+
|
|
410
|
+
if (options.package) {
|
|
411
|
+
log(`Analyzing package: ${options.package}\n`, 'gray');
|
|
412
|
+
} else {
|
|
413
|
+
log('Analyzing all packages...\n', 'gray');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const packages = findPackages(rootDir, options.package);
|
|
417
|
+
|
|
418
|
+
if (packages.length === 0) {
|
|
419
|
+
log('ā ļø No KB Labs packages found', 'yellow');
|
|
420
|
+
log(' Run this command from the monorepo root\n', 'gray');
|
|
421
|
+
process.exit(0);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
log(`Found ${packages.length} package(s)\n`, 'gray');
|
|
425
|
+
|
|
426
|
+
// Build dependency graph
|
|
427
|
+
const { graph, packageInfo } = buildDependencyGraph(packages);
|
|
428
|
+
|
|
429
|
+
// Render visualizations
|
|
430
|
+
if (options.graph || options.all) {
|
|
431
|
+
renderDependencyGraph(graph, packageInfo, options.package);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (options.stats || options.all) {
|
|
435
|
+
renderPackageStats(graph, packageInfo);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (options.tree) {
|
|
439
|
+
renderDependencyTree(graph, packageInfo, options.package);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
log('š” Tips:', 'blue');
|
|
443
|
+
log(' ⢠Use --graph to see dependency relationships', 'gray');
|
|
444
|
+
log(' ⢠Use --stats to see package statistics', 'gray');
|
|
445
|
+
log(' ⢠Use --tree --package=<name> to see dependency tree', 'gray');
|
|
446
|
+
log(' ⢠Use --package=<name> to filter by specific package', 'gray');
|
|
447
|
+
log('');
|
|
448
|
+
|
|
449
|
+
process.exit(0);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
main();
|