@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,561 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @kb-labs/devkit - Export Checker
|
|
5
|
+
*
|
|
6
|
+
* Checks for:
|
|
7
|
+
* 1. Unused exports (exported but never imported by other packages)
|
|
8
|
+
* 2. Dead code in public APIs (exports without consumers)
|
|
9
|
+
* 3. Missing barrel exports (files not exported from index.ts)
|
|
10
|
+
* 4. Inconsistent exports (package.json exports vs actual files)
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* kb-devkit-check-exports # Check all packages
|
|
14
|
+
* kb-devkit-check-exports --package cli-core # Check specific package
|
|
15
|
+
* kb-devkit-check-exports --strict # Include internal-only exports
|
|
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
|
+
// ANSI colors
|
|
25
|
+
const colors = {
|
|
26
|
+
reset: '\x1b[0m',
|
|
27
|
+
red: '\x1b[31m',
|
|
28
|
+
green: '\x1b[32m',
|
|
29
|
+
yellow: '\x1b[33m',
|
|
30
|
+
blue: '\x1b[34m',
|
|
31
|
+
magenta: '\x1b[35m',
|
|
32
|
+
cyan: '\x1b[36m',
|
|
33
|
+
gray: '\x1b[90m',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function log(message, color = 'reset') {
|
|
37
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Parse CLI arguments
|
|
41
|
+
const args = process.argv.slice(2);
|
|
42
|
+
const options = {
|
|
43
|
+
package: args.find((arg) => arg.startsWith('--package='))?.split('=')[1],
|
|
44
|
+
strict: args.includes('--strict'),
|
|
45
|
+
verbose: args.includes('--verbose') || args.includes('-v'),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Extract exports from TypeScript/JavaScript file
|
|
50
|
+
*/
|
|
51
|
+
function extractExports(filePath, content) {
|
|
52
|
+
const exports = [];
|
|
53
|
+
|
|
54
|
+
// Match: export const/let/var/function/class/interface/type/enum name
|
|
55
|
+
// Match: export { name1, name2 }
|
|
56
|
+
// Match: export * from 'module'
|
|
57
|
+
const patterns = [
|
|
58
|
+
// export const foo = ...
|
|
59
|
+
/export\s+(?:const|let|var)\s+(\w+)/g,
|
|
60
|
+
// export function foo() {}
|
|
61
|
+
/export\s+function\s+(\w+)/g,
|
|
62
|
+
// export class Foo {}
|
|
63
|
+
/export\s+class\s+(\w+)/g,
|
|
64
|
+
// export interface Foo {}
|
|
65
|
+
/export\s+interface\s+(\w+)/g,
|
|
66
|
+
// export type Foo = ...
|
|
67
|
+
/export\s+type\s+(\w+)/g,
|
|
68
|
+
// export enum Foo {}
|
|
69
|
+
/export\s+enum\s+(\w+)/g,
|
|
70
|
+
// export { foo, bar }
|
|
71
|
+
/export\s*{([^}]+)}/g,
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
for (const pattern of patterns) {
|
|
75
|
+
let match;
|
|
76
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
77
|
+
const exportName = match[1];
|
|
78
|
+
const line = content.substring(0, match.index).split('\n').length;
|
|
79
|
+
|
|
80
|
+
// Handle export { foo, bar } - multiple exports
|
|
81
|
+
if (exportName.includes(',')) {
|
|
82
|
+
const names = exportName
|
|
83
|
+
.split(',')
|
|
84
|
+
.map((n) => n.trim())
|
|
85
|
+
.filter((n) => n && !n.includes('as')); // Skip "as" renames for now
|
|
86
|
+
|
|
87
|
+
for (const name of names) {
|
|
88
|
+
exports.push({ name, line, type: 'named' });
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
exports.push({ name: exportName, line, type: 'named' });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Check for default export
|
|
97
|
+
if (/export\s+default/.test(content)) {
|
|
98
|
+
const line = content.substring(0, content.indexOf('export default')).split('\n').length;
|
|
99
|
+
exports.push({ name: 'default', line, type: 'default' });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check for re-exports (export * from 'module')
|
|
103
|
+
const reExportPattern = /export\s+\*\s+from\s+['"]([^'"]+)['"]/g;
|
|
104
|
+
let reExportMatch;
|
|
105
|
+
while ((reExportMatch = reExportPattern.exec(content)) !== null) {
|
|
106
|
+
const line = content.substring(0, reExportMatch.index).split('\n').length;
|
|
107
|
+
exports.push({
|
|
108
|
+
name: `* from "${reExportMatch[1]}"`,
|
|
109
|
+
line,
|
|
110
|
+
type: 're-export',
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return exports;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Extract imports from TypeScript/JavaScript file
|
|
119
|
+
*/
|
|
120
|
+
function extractImports(filePath, content) {
|
|
121
|
+
const imports = [];
|
|
122
|
+
|
|
123
|
+
// Match: import { name1, name2 } from 'module'
|
|
124
|
+
// Match: import name from 'module'
|
|
125
|
+
const namedImportPattern = /import\s+{([^}]+)}\s+from\s+['"]([^'"]+)['"]/g;
|
|
126
|
+
const defaultImportPattern = /import\s+(\w+)\s+from\s+['"]([^'"]+)['"]/g;
|
|
127
|
+
|
|
128
|
+
let match;
|
|
129
|
+
|
|
130
|
+
// Named imports
|
|
131
|
+
while ((match = namedImportPattern.exec(content)) !== null) {
|
|
132
|
+
const names = match[1]
|
|
133
|
+
.split(',')
|
|
134
|
+
.map((n) => n.trim())
|
|
135
|
+
.filter((n) => n && !n.includes('as')); // Skip "as" renames
|
|
136
|
+
|
|
137
|
+
const module = match[2];
|
|
138
|
+
|
|
139
|
+
for (const name of names) {
|
|
140
|
+
imports.push({ name, module, type: 'named' });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Default imports
|
|
145
|
+
while ((match = defaultImportPattern.exec(content)) !== null) {
|
|
146
|
+
const name = match[1];
|
|
147
|
+
const module = match[2];
|
|
148
|
+
|
|
149
|
+
// Skip if this is actually a named import (has { after import)
|
|
150
|
+
if (!content.substring(match.index, match.index + 50).includes('{')) {
|
|
151
|
+
imports.push({ name, module, type: 'default' });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return imports;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Find all source files in package
|
|
160
|
+
*/
|
|
161
|
+
function findSourceFiles(packageDir) {
|
|
162
|
+
const files = [];
|
|
163
|
+
const srcDir = path.join(packageDir, 'src');
|
|
164
|
+
|
|
165
|
+
if (!fs.existsSync(srcDir)) {
|
|
166
|
+
return files;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function walk(dir) {
|
|
170
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
171
|
+
|
|
172
|
+
for (const entry of entries) {
|
|
173
|
+
const fullPath = path.join(dir, entry.name);
|
|
174
|
+
|
|
175
|
+
if (entry.isDirectory()) {
|
|
176
|
+
walk(fullPath);
|
|
177
|
+
} else if (entry.isFile() && /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(entry.name)) {
|
|
178
|
+
files.push(fullPath);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
walk(srcDir);
|
|
184
|
+
return files;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Check if export is used in package.json exports field
|
|
189
|
+
*/
|
|
190
|
+
function isUsedInPackageExports(exportName, packageJson) {
|
|
191
|
+
if (!packageJson.exports) {return false;}
|
|
192
|
+
|
|
193
|
+
const exportsStr = JSON.stringify(packageJson.exports);
|
|
194
|
+
return exportsStr.includes(exportName);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Check package for export issues
|
|
199
|
+
*/
|
|
200
|
+
function checkPackage(packageJsonPath, allPackages) {
|
|
201
|
+
const packageDir = path.dirname(packageJsonPath);
|
|
202
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
203
|
+
const packageName = packageJson.name;
|
|
204
|
+
|
|
205
|
+
if (!packageName || !packageName.startsWith('@kb-labs/')) {
|
|
206
|
+
return null; // Skip non-KB Labs packages
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const issues = {
|
|
210
|
+
unusedExports: [],
|
|
211
|
+
missingBarrelExports: [],
|
|
212
|
+
inconsistentExports: [],
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const sourceFiles = findSourceFiles(packageDir);
|
|
216
|
+
|
|
217
|
+
if (sourceFiles.length === 0) {
|
|
218
|
+
return null; // No source files
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Step 1: Collect all exports from all files
|
|
222
|
+
const allExports = new Map(); // file -> exports[]
|
|
223
|
+
|
|
224
|
+
for (const sourceFile of sourceFiles) {
|
|
225
|
+
const content = fs.readFileSync(sourceFile, 'utf-8');
|
|
226
|
+
const exports = extractExports(sourceFile, content);
|
|
227
|
+
|
|
228
|
+
if (exports.length > 0) {
|
|
229
|
+
allExports.set(sourceFile, exports);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Step 2: Collect all imports from other packages
|
|
234
|
+
const externalImports = new Map(); // packageName -> imports[]
|
|
235
|
+
|
|
236
|
+
for (const otherPackagePath of allPackages) {
|
|
237
|
+
if (otherPackagePath === packageJsonPath) {continue;} // Skip self
|
|
238
|
+
|
|
239
|
+
const otherPackageDir = path.dirname(otherPackagePath);
|
|
240
|
+
const otherSourceFiles = findSourceFiles(otherPackageDir);
|
|
241
|
+
|
|
242
|
+
for (const otherSourceFile of otherSourceFiles) {
|
|
243
|
+
const content = fs.readFileSync(otherSourceFile, 'utf-8');
|
|
244
|
+
const imports = extractImports(otherSourceFile, content);
|
|
245
|
+
|
|
246
|
+
for (const imp of imports) {
|
|
247
|
+
// Check if importing from our package
|
|
248
|
+
if (imp.module === packageName || imp.module.startsWith(`${packageName}/`)) {
|
|
249
|
+
if (!externalImports.has(imp.module)) {
|
|
250
|
+
externalImports.set(imp.module, []);
|
|
251
|
+
}
|
|
252
|
+
externalImports.get(imp.module).push(imp.name);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Step 3: Check for unused exports
|
|
259
|
+
const indexFile = sourceFiles.find((f) => /\/index\.(ts|tsx|js|jsx|mjs|cjs)$/.test(f));
|
|
260
|
+
const indexExports = indexFile ? allExports.get(indexFile) || [] : [];
|
|
261
|
+
const importedNames = new Set();
|
|
262
|
+
|
|
263
|
+
for (const imports of externalImports.values()) {
|
|
264
|
+
for (const name of imports) {
|
|
265
|
+
importedNames.add(name);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
for (const [file, exports] of allExports.entries()) {
|
|
270
|
+
const relativeFile = path.relative(packageDir, file);
|
|
271
|
+
|
|
272
|
+
// Skip test files
|
|
273
|
+
if (relativeFile.includes('.test.') || relativeFile.includes('.spec.')) {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
for (const exp of exports) {
|
|
278
|
+
// Skip re-exports and default exports (harder to track)
|
|
279
|
+
if (exp.type === 're-export' || exp.type === 'default') {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Skip if used in package.json exports
|
|
284
|
+
if (isUsedInPackageExports(exp.name, packageJson)) {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Check if exported from index (public API)
|
|
289
|
+
const isInIndex = indexExports.some((e) => e.name === exp.name);
|
|
290
|
+
|
|
291
|
+
// If not exported from index and not imported externally
|
|
292
|
+
if (!isInIndex && !importedNames.has(exp.name)) {
|
|
293
|
+
// In strict mode, report all unused exports
|
|
294
|
+
// In normal mode, only report exports from index.ts
|
|
295
|
+
if (options.strict || file === indexFile) {
|
|
296
|
+
issues.unusedExports.push({
|
|
297
|
+
file: relativeFile,
|
|
298
|
+
line: exp.line,
|
|
299
|
+
name: exp.name,
|
|
300
|
+
type: exp.type,
|
|
301
|
+
isPublic: file === indexFile,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Step 4: Check for missing barrel exports (files not exported from index.ts)
|
|
309
|
+
if (indexFile && sourceFiles.length > 1) {
|
|
310
|
+
const indexContent = fs.readFileSync(indexFile, 'utf-8');
|
|
311
|
+
|
|
312
|
+
for (const file of sourceFiles) {
|
|
313
|
+
if (file === indexFile) {continue;}
|
|
314
|
+
|
|
315
|
+
// Skip test files, internal files
|
|
316
|
+
const relativeFile = path.relative(path.dirname(indexFile), file);
|
|
317
|
+
if (
|
|
318
|
+
relativeFile.includes('.test.') ||
|
|
319
|
+
relativeFile.includes('.spec.') ||
|
|
320
|
+
relativeFile.includes('internal/') ||
|
|
321
|
+
relativeFile.includes('__tests__/') ||
|
|
322
|
+
relativeFile.includes('__mocks__/')
|
|
323
|
+
) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Check if file is re-exported from index
|
|
328
|
+
const fileExports = allExports.get(file) || [];
|
|
329
|
+
if (fileExports.length === 0) {continue;}
|
|
330
|
+
|
|
331
|
+
const fileBasename = path.basename(file).replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, '');
|
|
332
|
+
const isReExported =
|
|
333
|
+
indexContent.includes(`from './${relativeFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, '')}`) ||
|
|
334
|
+
indexContent.includes(`from "./${relativeFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, '')}`) ||
|
|
335
|
+
indexContent.includes(`from './${fileBasename}'`) ||
|
|
336
|
+
indexContent.includes(`from "./${fileBasename}"`);
|
|
337
|
+
|
|
338
|
+
if (!isReExported && !options.strict) {
|
|
339
|
+
// Only report in strict mode for now (too noisy otherwise)
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (!isReExported) {
|
|
344
|
+
issues.missingBarrelExports.push({
|
|
345
|
+
file: relativeFile,
|
|
346
|
+
exportsCount: fileExports.length,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Step 5: Check package.json exports consistency
|
|
353
|
+
if (packageJson.exports && typeof packageJson.exports === 'object') {
|
|
354
|
+
for (const [exportPath, exportConfig] of Object.entries(packageJson.exports)) {
|
|
355
|
+
if (exportPath === '.') {continue;} // Main export
|
|
356
|
+
|
|
357
|
+
// Get the actual file path
|
|
358
|
+
const actualPath =
|
|
359
|
+
typeof exportConfig === 'string'
|
|
360
|
+
? exportConfig
|
|
361
|
+
: exportConfig.import || exportConfig.require || exportConfig.default;
|
|
362
|
+
|
|
363
|
+
if (!actualPath || typeof actualPath !== 'string') {continue;}
|
|
364
|
+
|
|
365
|
+
// Check if file exists
|
|
366
|
+
const fullPath = path.join(packageDir, actualPath);
|
|
367
|
+
if (!fs.existsSync(fullPath)) {
|
|
368
|
+
issues.inconsistentExports.push({
|
|
369
|
+
exportPath,
|
|
370
|
+
actualPath,
|
|
371
|
+
issue: 'File does not exist',
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
packageName,
|
|
379
|
+
packageDir,
|
|
380
|
+
packageJsonPath,
|
|
381
|
+
...issues,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Find all packages in monorepo
|
|
387
|
+
*/
|
|
388
|
+
function findPackages(rootDir, filterPackage) {
|
|
389
|
+
const packages = [];
|
|
390
|
+
const entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
|
391
|
+
|
|
392
|
+
for (const entry of entries) {
|
|
393
|
+
if (!entry.isDirectory() || !entry.name.startsWith('kb-labs-')) {continue;}
|
|
394
|
+
|
|
395
|
+
const repoPath = path.join(rootDir, entry.name);
|
|
396
|
+
const packagesDir = path.join(repoPath, 'packages');
|
|
397
|
+
|
|
398
|
+
if (!fs.existsSync(packagesDir)) {continue;}
|
|
399
|
+
|
|
400
|
+
const packageDirs = fs.readdirSync(packagesDir, { withFileTypes: true });
|
|
401
|
+
|
|
402
|
+
for (const pkgDir of packageDirs) {
|
|
403
|
+
if (!pkgDir.isDirectory()) {continue;}
|
|
404
|
+
|
|
405
|
+
// Filter by package name if specified
|
|
406
|
+
if (filterPackage && pkgDir.name !== filterPackage) {continue;}
|
|
407
|
+
|
|
408
|
+
const packageJsonPath = path.join(packagesDir, pkgDir.name, 'package.json');
|
|
409
|
+
|
|
410
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
411
|
+
packages.push(packageJsonPath);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return packages;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Main function
|
|
421
|
+
*/
|
|
422
|
+
function main() {
|
|
423
|
+
const rootDir = process.cwd();
|
|
424
|
+
|
|
425
|
+
log('\nš¤ KB Labs Export Checker\n', 'blue');
|
|
426
|
+
|
|
427
|
+
if (options.package) {
|
|
428
|
+
log(`Checking package: ${options.package}\n`, 'gray');
|
|
429
|
+
} else {
|
|
430
|
+
log('Checking all packages...\n', 'gray');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (options.strict) {
|
|
434
|
+
log('ā ļø Strict mode enabled (includes internal exports)\n', 'yellow');
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const packages = findPackages(rootDir, options.package);
|
|
438
|
+
|
|
439
|
+
if (packages.length === 0) {
|
|
440
|
+
log('ā ļø No KB Labs packages found', 'yellow');
|
|
441
|
+
log(' Run this command from the monorepo root\n', 'gray');
|
|
442
|
+
process.exit(0);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
log(`Found ${packages.length} package(s) to check\n`, 'gray');
|
|
446
|
+
|
|
447
|
+
const results = [];
|
|
448
|
+
|
|
449
|
+
// Check each package
|
|
450
|
+
for (const packagePath of packages) {
|
|
451
|
+
const result = checkPackage(packagePath, packages);
|
|
452
|
+
if (result) {
|
|
453
|
+
results.push(result);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Print results
|
|
458
|
+
let hasIssues = false;
|
|
459
|
+
|
|
460
|
+
for (const result of results) {
|
|
461
|
+
const issueCount =
|
|
462
|
+
result.unusedExports.length +
|
|
463
|
+
result.missingBarrelExports.length +
|
|
464
|
+
result.inconsistentExports.length;
|
|
465
|
+
|
|
466
|
+
if (issueCount === 0 && !options.verbose) {continue;}
|
|
467
|
+
|
|
468
|
+
hasIssues = issueCount > 0;
|
|
469
|
+
|
|
470
|
+
if (issueCount > 0) {
|
|
471
|
+
log(`\nā ${result.packageName}`, 'red');
|
|
472
|
+
log(` ${path.relative(rootDir, result.packageDir)}`, 'gray');
|
|
473
|
+
} else if (options.verbose) {
|
|
474
|
+
log(`\nā
${result.packageName}`, 'green');
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Unused exports
|
|
478
|
+
if (result.unusedExports.length > 0) {
|
|
479
|
+
log(`\n š Unused exports (${result.unusedExports.length}):`, 'yellow');
|
|
480
|
+
|
|
481
|
+
// Group by file
|
|
482
|
+
const byFile = new Map();
|
|
483
|
+
for (const exp of result.unusedExports) {
|
|
484
|
+
if (!byFile.has(exp.file)) {
|
|
485
|
+
byFile.set(exp.file, []);
|
|
486
|
+
}
|
|
487
|
+
byFile.get(exp.file).push(exp);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
for (const [file, exports] of byFile.entries()) {
|
|
491
|
+
log(` ${file}`, 'cyan');
|
|
492
|
+
for (const exp of exports) {
|
|
493
|
+
const visibility = exp.isPublic ? '(public API)' : '(internal)';
|
|
494
|
+
log(` āā ${exp.name} ${visibility}`, 'gray');
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
log(`\n š” These exports are never imported by other packages`, 'blue');
|
|
499
|
+
log(` š” Consider removing them to reduce API surface`, 'blue');
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Missing barrel exports
|
|
503
|
+
if (result.missingBarrelExports.length > 0 && options.strict) {
|
|
504
|
+
log(`\n š” Files not exported from index.ts (${result.missingBarrelExports.length}):`, 'yellow');
|
|
505
|
+
|
|
506
|
+
for (const file of result.missingBarrelExports) {
|
|
507
|
+
log(` ${file.file}`, 'gray');
|
|
508
|
+
log(` āā ${file.exportsCount} export(s)`, 'gray');
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
log(`\n š” These files have exports but aren't re-exported from index.ts`, 'blue');
|
|
512
|
+
log(` š” Consider adding them to the barrel export or marking as internal`, 'blue');
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Inconsistent exports
|
|
516
|
+
if (result.inconsistentExports.length > 0) {
|
|
517
|
+
log(`\n š“ Inconsistent package.json exports (${result.inconsistentExports.length}):`, 'red');
|
|
518
|
+
|
|
519
|
+
for (const exp of result.inconsistentExports) {
|
|
520
|
+
log(` "${exp.exportPath}" ā ${exp.actualPath}`, 'yellow');
|
|
521
|
+
log(` āā ${exp.issue}`, 'gray');
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
log(`\n š” Update package.json exports field to match actual files`, 'blue');
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Summary
|
|
529
|
+
log('\n' + 'ā'.repeat(60) + '\n', 'gray');
|
|
530
|
+
|
|
531
|
+
if (!hasIssues) {
|
|
532
|
+
log('ā
No export issues found!\n', 'green');
|
|
533
|
+
process.exit(0);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
log('š Summary:\n', 'blue');
|
|
537
|
+
|
|
538
|
+
const unusedCount = results.reduce((sum, r) => sum + r.unusedExports.length, 0);
|
|
539
|
+
const missingCount = results.reduce((sum, r) => sum + r.missingBarrelExports.length, 0);
|
|
540
|
+
const inconsistentCount = results.reduce((sum, r) => sum + r.inconsistentExports.length, 0);
|
|
541
|
+
|
|
542
|
+
if (unusedCount > 0) {
|
|
543
|
+
log(` š ${unusedCount} unused export(s)`, 'yellow');
|
|
544
|
+
}
|
|
545
|
+
if (missingCount > 0 && options.strict) {
|
|
546
|
+
log(` š” ${missingCount} file(s) not in barrel exports`, 'yellow');
|
|
547
|
+
}
|
|
548
|
+
if (inconsistentCount > 0) {
|
|
549
|
+
log(` š“ ${inconsistentCount} inconsistent package.json export(s)`, 'red');
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
log('\nš” Tips:', 'blue');
|
|
553
|
+
log(' ⢠Use --verbose to see all packages (including clean ones)', 'gray');
|
|
554
|
+
log(' ⢠Use --package=<name> to check a specific package', 'gray');
|
|
555
|
+
log(' ⢠Use --strict to include internal exports (more thorough)', 'gray');
|
|
556
|
+
log('');
|
|
557
|
+
|
|
558
|
+
process.exit(1);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
main();
|