@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,712 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @kb-labs/devkit - Import Checker
|
|
5
|
+
*
|
|
6
|
+
* Checks for:
|
|
7
|
+
* 1. Broken imports (files that don't exist)
|
|
8
|
+
* 2. Unused dependencies in package.json
|
|
9
|
+
* 3. Imports from node_modules that should be workspace imports
|
|
10
|
+
* 4. Circular dependencies between packages
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* kb-devkit-check-imports # Check all packages
|
|
14
|
+
* kb-devkit-check-imports --package cli-core # Check specific package
|
|
15
|
+
* kb-devkit-check-imports --fix # Auto-fix unused deps
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
|
|
22
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Parse .devkitignore file
|
|
26
|
+
* Returns rules grouped by type
|
|
27
|
+
*/
|
|
28
|
+
function parseDevkitIgnore(rootDir) {
|
|
29
|
+
const ignorePath = path.join(rootDir, '.devkitignore');
|
|
30
|
+
const rules = {
|
|
31
|
+
'missing-dep': [],
|
|
32
|
+
'unused-dep': [],
|
|
33
|
+
'broken-import': [],
|
|
34
|
+
'circular-dep': [],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
if (!fs.existsSync(ignorePath)) {
|
|
38
|
+
return rules;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const content = fs.readFileSync(ignorePath, 'utf-8');
|
|
42
|
+
const lines = content.split('\n');
|
|
43
|
+
|
|
44
|
+
for (const line of lines) {
|
|
45
|
+
const trimmed = line.trim();
|
|
46
|
+
|
|
47
|
+
// Skip empty lines and comments
|
|
48
|
+
if (!trimmed || trimmed.startsWith('#')) {continue;}
|
|
49
|
+
|
|
50
|
+
// Parse rule: "rule-type: pattern # reason"
|
|
51
|
+
const match = trimmed.match(/^(missing-dep|unused-dep|broken-import|circular-dep):\s*(.+?)(?:\s*#\s*(.*))?$/);
|
|
52
|
+
if (!match) {continue;}
|
|
53
|
+
|
|
54
|
+
const [, ruleType, pattern, reason] = match;
|
|
55
|
+
|
|
56
|
+
// Parse pattern: "package -> target"
|
|
57
|
+
// Simpler regex: match @scope/package or package, then ->, then target
|
|
58
|
+
const patternMatch = pattern.trim().match(/^(@[\w-]+\/[\w-]+|[\w-]+)\s*->\s*(.+)$/);
|
|
59
|
+
if (!patternMatch) {continue;}
|
|
60
|
+
|
|
61
|
+
const [, packageName, target] = patternMatch;
|
|
62
|
+
|
|
63
|
+
rules[ruleType].push({
|
|
64
|
+
package: packageName.trim(),
|
|
65
|
+
target: target.trim(),
|
|
66
|
+
reason: reason?.trim() || 'No reason provided',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return rules;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check if an issue should be ignored based on rules
|
|
75
|
+
*/
|
|
76
|
+
function shouldIgnore(rules, ruleType, packageName, target) {
|
|
77
|
+
const typeRules = rules[ruleType] || [];
|
|
78
|
+
|
|
79
|
+
for (const rule of typeRules) {
|
|
80
|
+
if (rule.package !== packageName) {continue;}
|
|
81
|
+
|
|
82
|
+
// Handle wildcard patterns
|
|
83
|
+
if (rule.target.includes('*')) {
|
|
84
|
+
const regex = new RegExp('^' + rule.target.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
|
|
85
|
+
if (regex.test(target)) {
|
|
86
|
+
return rule;
|
|
87
|
+
}
|
|
88
|
+
} else if (rule.target === target) {
|
|
89
|
+
return rule;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ANSI colors
|
|
97
|
+
const colors = {
|
|
98
|
+
reset: '\x1b[0m',
|
|
99
|
+
red: '\x1b[31m',
|
|
100
|
+
green: '\x1b[32m',
|
|
101
|
+
yellow: '\x1b[33m',
|
|
102
|
+
blue: '\x1b[34m',
|
|
103
|
+
magenta: '\x1b[35m',
|
|
104
|
+
cyan: '\x1b[36m',
|
|
105
|
+
gray: '\x1b[90m',
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
function log(message, color = 'reset') {
|
|
109
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Parse CLI arguments
|
|
113
|
+
const args = process.argv.slice(2);
|
|
114
|
+
const options = {
|
|
115
|
+
package: args.find((arg) => arg.startsWith('--package='))?.split('=')[1],
|
|
116
|
+
fix: args.includes('--fix'),
|
|
117
|
+
verbose: args.includes('--verbose') || args.includes('-v'),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Extract imports from TypeScript/JavaScript file
|
|
122
|
+
*/
|
|
123
|
+
function extractImports(filePath, content) {
|
|
124
|
+
const imports = [];
|
|
125
|
+
|
|
126
|
+
// Match: import ... from 'module'
|
|
127
|
+
// Match: import('module')
|
|
128
|
+
// Match: require('module')
|
|
129
|
+
const patterns = [
|
|
130
|
+
/import\s+(?:[\w*{}\n\r\t, ]+\s+from\s+)?['"]([^'"]+)['"]/g,
|
|
131
|
+
/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
132
|
+
/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
for (const pattern of patterns) {
|
|
136
|
+
let match;
|
|
137
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
138
|
+
const importPath = match[1];
|
|
139
|
+
imports.push({
|
|
140
|
+
path: importPath,
|
|
141
|
+
line: content.substring(0, match.index).split('\n').length,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return imports;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Check if import path is a local file import
|
|
151
|
+
*/
|
|
152
|
+
function isLocalImport(importPath) {
|
|
153
|
+
return importPath.startsWith('.') || importPath.startsWith('/');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Check if import path is a node built-in
|
|
158
|
+
*/
|
|
159
|
+
function isNodeBuiltin(importPath) {
|
|
160
|
+
// If it starts with node:, it's definitely a builtin
|
|
161
|
+
if (importPath.startsWith('node:')) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const builtins = [
|
|
166
|
+
'assert', 'buffer', 'child_process', 'cluster', 'crypto', 'dgram',
|
|
167
|
+
'dns', 'domain', 'events', 'fs', 'http', 'https', 'net', 'os',
|
|
168
|
+
'path', 'punycode', 'querystring', 'readline', 'repl', 'stream',
|
|
169
|
+
'string_decoder', 'timers', 'tls', 'tty', 'url', 'util', 'v8',
|
|
170
|
+
'vm', 'zlib', 'module', 'inspector', 'perf_hooks', 'worker_threads',
|
|
171
|
+
'async_hooks', 'trace_events', 'diagnostics_channel',
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
// Also check for subpaths like fs/promises
|
|
175
|
+
const baseModule = importPath.split('/')[0];
|
|
176
|
+
return builtins.includes(baseModule);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Resolve import path to actual file
|
|
181
|
+
*/
|
|
182
|
+
function resolveImport(importPath, sourceFile, packageRoot) {
|
|
183
|
+
if (!isLocalImport(importPath)) {
|
|
184
|
+
return null; // External dependency
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const sourceDir = path.dirname(sourceFile);
|
|
188
|
+
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json'];
|
|
189
|
+
|
|
190
|
+
// Try exact path
|
|
191
|
+
const resolved = path.resolve(sourceDir, importPath);
|
|
192
|
+
|
|
193
|
+
// Try with extensions
|
|
194
|
+
for (const ext of extensions) {
|
|
195
|
+
const withExt = resolved + ext;
|
|
196
|
+
if (fs.existsSync(withExt) && fs.statSync(withExt).isFile()) {
|
|
197
|
+
return withExt;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Try as directory with index file
|
|
202
|
+
if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
|
|
203
|
+
for (const ext of extensions) {
|
|
204
|
+
const indexFile = path.join(resolved, `index${ext}`);
|
|
205
|
+
if (fs.existsSync(indexFile)) {
|
|
206
|
+
return indexFile;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return null; // Could not resolve
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Find all source files in package
|
|
216
|
+
*/
|
|
217
|
+
function findSourceFiles(packageDir) {
|
|
218
|
+
const files = [];
|
|
219
|
+
const srcDir = path.join(packageDir, 'src');
|
|
220
|
+
|
|
221
|
+
if (!fs.existsSync(srcDir)) {
|
|
222
|
+
return files;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function walk(dir) {
|
|
226
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
227
|
+
|
|
228
|
+
for (const entry of entries) {
|
|
229
|
+
const fullPath = path.join(dir, entry.name);
|
|
230
|
+
|
|
231
|
+
if (entry.isDirectory()) {
|
|
232
|
+
walk(fullPath);
|
|
233
|
+
} else if (entry.isFile() && /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(entry.name)) {
|
|
234
|
+
files.push(fullPath);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
walk(srcDir);
|
|
240
|
+
return files;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Check package for import issues
|
|
245
|
+
*/
|
|
246
|
+
function checkPackage(packageJsonPath) {
|
|
247
|
+
const packageDir = path.dirname(packageJsonPath);
|
|
248
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
249
|
+
const packageName = packageJson.name;
|
|
250
|
+
|
|
251
|
+
if (!packageName || !packageName.startsWith('@kb-labs/')) {
|
|
252
|
+
return null; // Skip non-KB Labs packages
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const issues = {
|
|
256
|
+
brokenImports: [],
|
|
257
|
+
unusedDeps: [],
|
|
258
|
+
missingWorkspaceDeps: [],
|
|
259
|
+
missingNpmDeps: [],
|
|
260
|
+
usedDeps: new Set(),
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const dependencies = {
|
|
264
|
+
...packageJson.dependencies,
|
|
265
|
+
...packageJson.devDependencies,
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const sourceFiles = findSourceFiles(packageDir);
|
|
269
|
+
|
|
270
|
+
if (sourceFiles.length === 0) {
|
|
271
|
+
return null; // No source files
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Check each source file
|
|
275
|
+
for (const sourceFile of sourceFiles) {
|
|
276
|
+
const content = fs.readFileSync(sourceFile, 'utf-8');
|
|
277
|
+
const imports = extractImports(sourceFile, content);
|
|
278
|
+
|
|
279
|
+
for (const imp of imports) {
|
|
280
|
+
const { path: importPath, line } = imp;
|
|
281
|
+
|
|
282
|
+
// Check local imports
|
|
283
|
+
if (isLocalImport(importPath)) {
|
|
284
|
+
const resolved = resolveImport(importPath, sourceFile, packageDir);
|
|
285
|
+
|
|
286
|
+
if (!resolved) {
|
|
287
|
+
issues.brokenImports.push({
|
|
288
|
+
file: path.relative(packageDir, sourceFile),
|
|
289
|
+
line,
|
|
290
|
+
import: importPath,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// Check external dependencies
|
|
295
|
+
else if (!isNodeBuiltin(importPath)) {
|
|
296
|
+
// Extract package name (handle scoped packages and subpaths)
|
|
297
|
+
const match = importPath.match(/^(@[^/]+\/[^/]+|[^/]+)/);
|
|
298
|
+
const depName = match ? match[1] : importPath;
|
|
299
|
+
|
|
300
|
+
issues.usedDeps.add(depName);
|
|
301
|
+
|
|
302
|
+
// Check if it's a workspace package that should be in dependencies
|
|
303
|
+
if (depName.startsWith('@kb-labs/')) {
|
|
304
|
+
if (!dependencies[depName]) {
|
|
305
|
+
issues.missingWorkspaceDeps.push({
|
|
306
|
+
file: path.relative(packageDir, sourceFile),
|
|
307
|
+
line,
|
|
308
|
+
package: depName,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// Check if it's a regular npm package that should be in dependencies
|
|
313
|
+
else {
|
|
314
|
+
if (!dependencies[depName]) {
|
|
315
|
+
issues.missingNpmDeps.push({
|
|
316
|
+
file: path.relative(packageDir, sourceFile),
|
|
317
|
+
line,
|
|
318
|
+
package: depName,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Find unused dependencies
|
|
327
|
+
const allDeps = Object.keys(dependencies);
|
|
328
|
+
for (const dep of allDeps) {
|
|
329
|
+
// Skip type-only packages and build tools
|
|
330
|
+
if (
|
|
331
|
+
dep.startsWith('@types/') ||
|
|
332
|
+
dep === 'typescript' ||
|
|
333
|
+
dep === 'tsup' ||
|
|
334
|
+
dep === 'vitest' ||
|
|
335
|
+
dep === 'rimraf' ||
|
|
336
|
+
dep === 'eslint' ||
|
|
337
|
+
dep === 'prettier' ||
|
|
338
|
+
dep === '@kb-labs/devkit' ||
|
|
339
|
+
dep.startsWith('eslint-') ||
|
|
340
|
+
dep.startsWith('@vitest/') ||
|
|
341
|
+
dep.startsWith('@testing-library/')
|
|
342
|
+
) {
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (!issues.usedDeps.has(dep)) {
|
|
347
|
+
issues.unusedDeps.push(dep);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
packageName,
|
|
353
|
+
packageDir,
|
|
354
|
+
packageJsonPath,
|
|
355
|
+
...issues,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Find all packages in monorepo
|
|
361
|
+
*/
|
|
362
|
+
function findPackages(rootDir, filterPackage) {
|
|
363
|
+
const packages = [];
|
|
364
|
+
const entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
|
365
|
+
|
|
366
|
+
for (const entry of entries) {
|
|
367
|
+
if (!entry.isDirectory() || !entry.name.startsWith('kb-labs-')) {continue;}
|
|
368
|
+
|
|
369
|
+
const repoPath = path.join(rootDir, entry.name);
|
|
370
|
+
const packagesDir = path.join(repoPath, 'packages');
|
|
371
|
+
|
|
372
|
+
if (!fs.existsSync(packagesDir)) {continue;}
|
|
373
|
+
|
|
374
|
+
const packageDirs = fs.readdirSync(packagesDir, { withFileTypes: true });
|
|
375
|
+
|
|
376
|
+
for (const pkgDir of packageDirs) {
|
|
377
|
+
if (!pkgDir.isDirectory()) {continue;}
|
|
378
|
+
|
|
379
|
+
// Filter by package name if specified
|
|
380
|
+
if (filterPackage && pkgDir.name !== filterPackage) {continue;}
|
|
381
|
+
|
|
382
|
+
const packageJsonPath = path.join(packagesDir, pkgDir.name, 'package.json');
|
|
383
|
+
|
|
384
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
385
|
+
packages.push(packageJsonPath);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return packages;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Detect circular dependencies between packages
|
|
395
|
+
*/
|
|
396
|
+
function detectCircularDeps(allResults) {
|
|
397
|
+
const graph = new Map();
|
|
398
|
+
const packagePaths = new Map();
|
|
399
|
+
|
|
400
|
+
// Build dependency graph
|
|
401
|
+
for (const result of allResults) {
|
|
402
|
+
if (!result) {continue;}
|
|
403
|
+
|
|
404
|
+
const deps = [];
|
|
405
|
+
const packageJson = JSON.parse(fs.readFileSync(result.packageJsonPath, 'utf-8'));
|
|
406
|
+
const allDeps = {
|
|
407
|
+
...packageJson.dependencies,
|
|
408
|
+
...packageJson.devDependencies,
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
for (const dep of Object.keys(allDeps)) {
|
|
412
|
+
if (dep.startsWith('@kb-labs/')) {
|
|
413
|
+
deps.push(dep);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
graph.set(result.packageName, deps);
|
|
418
|
+
packagePaths.set(result.packageName, result.packageDir);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Find cycles using DFS
|
|
422
|
+
const cycles = [];
|
|
423
|
+
const visited = new Set();
|
|
424
|
+
const recStack = new Set();
|
|
425
|
+
|
|
426
|
+
function dfs(node, path = []) {
|
|
427
|
+
if (recStack.has(node)) {
|
|
428
|
+
// Found a cycle
|
|
429
|
+
const cycleStart = path.indexOf(node);
|
|
430
|
+
const cycle = [...path.slice(cycleStart), node];
|
|
431
|
+
cycles.push(cycle);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (visited.has(node)) {return;}
|
|
436
|
+
|
|
437
|
+
visited.add(node);
|
|
438
|
+
recStack.add(node);
|
|
439
|
+
path.push(node);
|
|
440
|
+
|
|
441
|
+
const neighbors = graph.get(node) || [];
|
|
442
|
+
for (const neighbor of neighbors) {
|
|
443
|
+
if (graph.has(neighbor)) {
|
|
444
|
+
dfs(neighbor, [...path]);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
recStack.delete(node);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
for (const node of graph.keys()) {
|
|
452
|
+
dfs(node);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Remove duplicate cycles
|
|
456
|
+
const uniqueCycles = [];
|
|
457
|
+
const seen = new Set();
|
|
458
|
+
|
|
459
|
+
for (const cycle of cycles) {
|
|
460
|
+
const normalized = [...cycle].sort().join('ā');
|
|
461
|
+
if (!seen.has(normalized)) {
|
|
462
|
+
seen.add(normalized);
|
|
463
|
+
uniqueCycles.push(cycle);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return uniqueCycles;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Main function
|
|
472
|
+
*/
|
|
473
|
+
function main() {
|
|
474
|
+
const rootDir = process.cwd();
|
|
475
|
+
|
|
476
|
+
log('\nš KB Labs Import Checker\n', 'blue');
|
|
477
|
+
|
|
478
|
+
// Load ignore rules
|
|
479
|
+
const ignoreRules = parseDevkitIgnore(rootDir);
|
|
480
|
+
const hasIgnoreFile = fs.existsSync(path.join(rootDir, '.devkitignore'));
|
|
481
|
+
const totalIgnoreRules = Object.values(ignoreRules).reduce((sum, arr) => sum + arr.length, 0);
|
|
482
|
+
|
|
483
|
+
if (hasIgnoreFile && totalIgnoreRules > 0) {
|
|
484
|
+
log(`Loaded ${totalIgnoreRules} ignore rule(s) from .devkitignore\n`, 'gray');
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (options.package) {
|
|
488
|
+
log(`Checking package: ${options.package}\n`, 'gray');
|
|
489
|
+
} else {
|
|
490
|
+
log('Checking all packages...\n', 'gray');
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const packages = findPackages(rootDir, options.package);
|
|
494
|
+
|
|
495
|
+
if (packages.length === 0) {
|
|
496
|
+
log('ā ļø No KB Labs packages found', 'yellow');
|
|
497
|
+
log(' Run this command from the monorepo root\n', 'gray');
|
|
498
|
+
process.exit(0);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
log(`Found ${packages.length} package(s) to check\n`, 'gray');
|
|
502
|
+
|
|
503
|
+
const results = [];
|
|
504
|
+
let totalIssues = 0;
|
|
505
|
+
|
|
506
|
+
// Check each package
|
|
507
|
+
for (const packagePath of packages) {
|
|
508
|
+
const result = checkPackage(packagePath);
|
|
509
|
+
if (result) {
|
|
510
|
+
results.push(result);
|
|
511
|
+
|
|
512
|
+
const issueCount =
|
|
513
|
+
result.brokenImports.length +
|
|
514
|
+
result.unusedDeps.length +
|
|
515
|
+
result.missingWorkspaceDeps.length;
|
|
516
|
+
|
|
517
|
+
if (issueCount > 0) {
|
|
518
|
+
totalIssues += issueCount;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Detect circular dependencies
|
|
524
|
+
const cycles = detectCircularDeps(results);
|
|
525
|
+
|
|
526
|
+
// Track ignored items for summary
|
|
527
|
+
let ignoredBroken = 0;
|
|
528
|
+
let ignoredMissing = 0;
|
|
529
|
+
let ignoredUnused = 0;
|
|
530
|
+
|
|
531
|
+
// Print results
|
|
532
|
+
let hasIssues = false;
|
|
533
|
+
|
|
534
|
+
for (const result of results) {
|
|
535
|
+
// Filter issues based on ignore rules
|
|
536
|
+
const filteredBroken = result.brokenImports.filter((issue) => {
|
|
537
|
+
const ignored = shouldIgnore(ignoreRules, 'broken-import', result.packageName, issue.import);
|
|
538
|
+
if (ignored) {ignoredBroken++;}
|
|
539
|
+
return !ignored;
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
const uniqueMissingDeps = [...new Set(result.missingWorkspaceDeps.map((d) => d.package))];
|
|
543
|
+
const filteredMissingDeps = uniqueMissingDeps.filter((dep) => {
|
|
544
|
+
const ignored = shouldIgnore(ignoreRules, 'missing-dep', result.packageName, dep);
|
|
545
|
+
if (ignored) {ignoredMissing++;}
|
|
546
|
+
return !ignored;
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
const uniqueMissingNpmDeps = [...new Set(result.missingNpmDeps.map((d) => d.package))];
|
|
550
|
+
const filteredMissingNpmDeps = uniqueMissingNpmDeps.filter((dep) => {
|
|
551
|
+
const ignored = shouldIgnore(ignoreRules, 'missing-dep', result.packageName, dep);
|
|
552
|
+
if (ignored) {ignoredMissing++;}
|
|
553
|
+
return !ignored;
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
const filteredUnused = result.unusedDeps.filter((dep) => {
|
|
557
|
+
const ignored = shouldIgnore(ignoreRules, 'unused-dep', result.packageName, dep);
|
|
558
|
+
if (ignored) {ignoredUnused++;}
|
|
559
|
+
return !ignored;
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
const issueCount =
|
|
563
|
+
filteredBroken.length +
|
|
564
|
+
filteredMissingDeps.length +
|
|
565
|
+
filteredMissingNpmDeps.length +
|
|
566
|
+
filteredUnused.length;
|
|
567
|
+
|
|
568
|
+
if (issueCount === 0 && !options.verbose) {continue;}
|
|
569
|
+
|
|
570
|
+
hasIssues = hasIssues || issueCount > 0;
|
|
571
|
+
|
|
572
|
+
if (issueCount > 0) {
|
|
573
|
+
log(`\nā ${result.packageName}`, 'red');
|
|
574
|
+
log(` ${path.relative(rootDir, result.packageDir)}`, 'gray');
|
|
575
|
+
} else if (options.verbose) {
|
|
576
|
+
log(`\nā
${result.packageName}`, 'green');
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Broken imports
|
|
580
|
+
if (filteredBroken.length > 0) {
|
|
581
|
+
log(`\n š“ Broken imports (${filteredBroken.length}):`, 'red');
|
|
582
|
+
for (const issue of filteredBroken) {
|
|
583
|
+
log(` ${issue.file}:${issue.line}`, 'yellow');
|
|
584
|
+
log(` āā Cannot resolve: ${issue.import}`, 'gray');
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Missing workspace dependencies
|
|
589
|
+
if (filteredMissingDeps.length > 0) {
|
|
590
|
+
log(`\n š” Missing workspace dependencies (${filteredMissingDeps.length}):`, 'yellow');
|
|
591
|
+
for (const dep of filteredMissingDeps) {
|
|
592
|
+
const usages = result.missingWorkspaceDeps.filter((d) => d.package === dep);
|
|
593
|
+
log(` ${dep}`, 'cyan');
|
|
594
|
+
log(` āā Used in ${usages.length} file(s)`, 'gray');
|
|
595
|
+
if (options.verbose) {
|
|
596
|
+
for (const usage of usages.slice(0, 3)) {
|
|
597
|
+
log(` - ${usage.file}:${usage.line}`, 'gray');
|
|
598
|
+
}
|
|
599
|
+
if (usages.length > 3) {
|
|
600
|
+
log(` ... and ${usages.length - 3} more`, 'gray');
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Missing npm dependencies
|
|
607
|
+
if (filteredMissingNpmDeps.length > 0) {
|
|
608
|
+
log(`\n š” Missing npm dependencies (${filteredMissingNpmDeps.length}):`, 'yellow');
|
|
609
|
+
for (const dep of filteredMissingNpmDeps) {
|
|
610
|
+
const usages = result.missingNpmDeps.filter((d) => d.package === dep);
|
|
611
|
+
log(` ${dep}`, 'cyan');
|
|
612
|
+
log(` āā Used in ${usages.length} file(s)`, 'gray');
|
|
613
|
+
if (options.verbose) {
|
|
614
|
+
for (const usage of usages.slice(0, 3)) {
|
|
615
|
+
log(` - ${usage.file}:${usage.line}`, 'gray');
|
|
616
|
+
}
|
|
617
|
+
if (usages.length > 3) {
|
|
618
|
+
log(` ... and ${usages.length - 3} more`, 'gray');
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Unused dependencies
|
|
625
|
+
if (filteredUnused.length > 0) {
|
|
626
|
+
log(`\n š Unused dependencies (${filteredUnused.length}):`, 'yellow');
|
|
627
|
+
for (const dep of filteredUnused) {
|
|
628
|
+
log(` ${dep}`, 'gray');
|
|
629
|
+
}
|
|
630
|
+
if (options.fix) {
|
|
631
|
+
log(` š” Run with --fix to remove unused dependencies`, 'blue');
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Print circular dependencies
|
|
637
|
+
if (cycles.length > 0) {
|
|
638
|
+
hasIssues = true;
|
|
639
|
+
log(`\n\nš Circular Dependencies (${cycles.length}):\n`, 'magenta');
|
|
640
|
+
|
|
641
|
+
for (let i = 0; i < cycles.length; i++) {
|
|
642
|
+
const cycle = cycles[i];
|
|
643
|
+
log(`${i + 1}. ${cycle.join(' ā ')}`, 'yellow');
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
log('\n ā ļø Circular dependencies can cause build and runtime issues', 'gray');
|
|
647
|
+
log(' š” Consider refactoring to extract shared code into a new package\n', 'gray');
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Summary
|
|
651
|
+
log('\n' + 'ā'.repeat(60) + '\n', 'gray');
|
|
652
|
+
|
|
653
|
+
if (!hasIssues && cycles.length === 0) {
|
|
654
|
+
log('ā
No import issues found!\n', 'green');
|
|
655
|
+
if (ignoredBroken + ignoredMissing + ignoredUnused > 0) {
|
|
656
|
+
log(` ā¹ļø ${ignoredBroken + ignoredMissing + ignoredUnused} issue(s) ignored via .devkitignore\n`, 'gray');
|
|
657
|
+
}
|
|
658
|
+
process.exit(0);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
log('š Summary:\n', 'blue');
|
|
662
|
+
|
|
663
|
+
// Calculate filtered counts
|
|
664
|
+
let brokenCount = 0;
|
|
665
|
+
let missingCount = 0;
|
|
666
|
+
let unusedCount = 0;
|
|
667
|
+
|
|
668
|
+
for (const result of results) {
|
|
669
|
+
brokenCount += result.brokenImports.filter(
|
|
670
|
+
(issue) => !shouldIgnore(ignoreRules, 'broken-import', result.packageName, issue.import)
|
|
671
|
+
).length;
|
|
672
|
+
|
|
673
|
+
const uniqueDeps = [...new Set(result.missingWorkspaceDeps.map((d) => d.package))];
|
|
674
|
+
missingCount += uniqueDeps.filter(
|
|
675
|
+
(dep) => !shouldIgnore(ignoreRules, 'missing-dep', result.packageName, dep)
|
|
676
|
+
).length;
|
|
677
|
+
|
|
678
|
+
unusedCount += result.unusedDeps.filter(
|
|
679
|
+
(dep) => !shouldIgnore(ignoreRules, 'unused-dep', result.packageName, dep)
|
|
680
|
+
).length;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (brokenCount > 0) {
|
|
684
|
+
log(` š“ ${brokenCount} broken import(s)`, 'red');
|
|
685
|
+
}
|
|
686
|
+
if (missingCount > 0) {
|
|
687
|
+
log(` š” ${missingCount} missing workspace dep(s)`, 'yellow');
|
|
688
|
+
}
|
|
689
|
+
if (unusedCount > 0) {
|
|
690
|
+
log(` š ${unusedCount} unused dependency(ies)`, 'yellow');
|
|
691
|
+
}
|
|
692
|
+
if (cycles.length > 0) {
|
|
693
|
+
log(` š ${cycles.length} circular dependency cycle(s)`, 'magenta');
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// Show ignored count
|
|
697
|
+
const totalIgnored = ignoredBroken + ignoredMissing + ignoredUnused;
|
|
698
|
+
if (totalIgnored > 0) {
|
|
699
|
+
log(`\n ā¹ļø ${totalIgnored} issue(s) ignored via .devkitignore`, 'gray');
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
log('\nš” Tips:', 'blue');
|
|
703
|
+
log(' ⢠Use --verbose to see all packages (including clean ones)', 'gray');
|
|
704
|
+
log(' ⢠Use --package=<name> to check a specific package', 'gray');
|
|
705
|
+
log(' ⢠Use --fix to auto-remove unused dependencies (coming soon)', 'gray');
|
|
706
|
+
log(' ⢠Add rules to .devkitignore to suppress known issues', 'gray');
|
|
707
|
+
log('');
|
|
708
|
+
|
|
709
|
+
process.exit(1);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
main();
|