@kb-labs/devkit 1.0.0 → 1.2.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/.github/workflow-templates/deploy.yml +28 -0
- package/.github/workflow-templates/docker-build.yml +25 -0
- package/.github/workflows/deploy-reusable.yml +110 -0
- package/.github/workflows/docker-build-reusable.yml +110 -0
- package/README.md +186 -34
- package/bin/devkit-architecture.mjs +14 -32
- package/bin/devkit-build-order.mjs +2 -30
- package/bin/devkit-check-build-readiness.mjs +14 -24
- package/bin/devkit-check-commands.mjs +9 -25
- package/bin/devkit-check-deprecated.mjs +24 -39
- package/bin/devkit-check-duplicates.mjs +3 -34
- package/bin/devkit-check-exports.mjs +3 -34
- package/bin/devkit-check-imports.mjs +3 -34
- package/bin/devkit-check-paths.mjs +9 -32
- package/bin/devkit-check-structure.mjs +3 -34
- package/bin/devkit-check-types.mjs +8 -31
- package/bin/devkit-fix-deps.mjs +3 -33
- package/bin/devkit-health.mjs +13 -35
- package/bin/devkit-stats.mjs +3 -31
- package/bin/devkit-types-audit.mjs +13 -34
- package/bin/devkit-types-order.mjs +8 -29
- package/bin/devkit-validate-naming.mjs +9 -35
- package/bin/devkit-visualize.mjs +3 -34
- package/bin/lib/find-packages.mjs +77 -0
- package/eslint/node.js +3 -1
- package/eslint/plugin.js +51 -0
- package/eslint/react.js +2 -1
- package/package.json +29 -24
- package/sync/index.mjs +27 -3
- package/tsconfig/dist/tsconfig.tools.tsbuildinfo +1 -1
- package/tsconfig/node.json +2 -1
- package/tsup/bin.js +5 -0
- package/tsup/dual.js +5 -0
- package/tsup/node.js +41 -3
- package/tsup/react-lib.js +6 -1
- package/tsup/sdk.js +5 -0
|
@@ -24,6 +24,9 @@ import fs from 'node:fs';
|
|
|
24
24
|
import path from 'node:path';
|
|
25
25
|
import { fileURLToPath } from 'node:url';
|
|
26
26
|
|
|
27
|
+
// Shared package discovery — supports both flat and categorized layouts
|
|
28
|
+
import { findPackages as _findPackagePaths } from './lib/find-packages.mjs';
|
|
29
|
+
|
|
27
30
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
28
31
|
|
|
29
32
|
// ANSI colors
|
|
@@ -56,37 +59,13 @@ const options = {
|
|
|
56
59
|
};
|
|
57
60
|
|
|
58
61
|
/**
|
|
59
|
-
* Find all packages
|
|
62
|
+
* Find all packages (returns objects with path, dir)
|
|
60
63
|
*/
|
|
61
64
|
function findPackages(rootDir) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (!entry.isDirectory() || !entry.name.startsWith('kb-labs-')) {continue;}
|
|
67
|
-
|
|
68
|
-
const repoPath = path.join(rootDir, entry.name);
|
|
69
|
-
const packagesDir = path.join(repoPath, 'packages');
|
|
70
|
-
|
|
71
|
-
if (!fs.existsSync(packagesDir)) {continue;}
|
|
72
|
-
|
|
73
|
-
const packageDirs = fs.readdirSync(packagesDir, { withFileTypes: true });
|
|
74
|
-
|
|
75
|
-
for (const pkgDir of packageDirs) {
|
|
76
|
-
if (!pkgDir.isDirectory()) {continue;}
|
|
77
|
-
|
|
78
|
-
const packageJsonPath = path.join(packagesDir, pkgDir.name, 'package.json');
|
|
79
|
-
|
|
80
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
81
|
-
packages.push({
|
|
82
|
-
path: packageJsonPath,
|
|
83
|
-
dir: path.join(packagesDir, pkgDir.name),
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return packages;
|
|
65
|
+
return _findPackagePaths(rootDir).map((pkgPath) => ({
|
|
66
|
+
path: pkgPath,
|
|
67
|
+
dir: path.dirname(pkgPath),
|
|
68
|
+
}));
|
|
90
69
|
}
|
|
91
70
|
|
|
92
71
|
/**
|
|
@@ -13,6 +13,9 @@ import fs from 'node:fs';
|
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import { fileURLToPath } from 'node:url';
|
|
15
15
|
|
|
16
|
+
// Shared package discovery — supports both flat and categorized layouts
|
|
17
|
+
import { findPackages as _findPackagePaths } from './lib/find-packages.mjs';
|
|
18
|
+
|
|
16
19
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
20
|
|
|
18
21
|
// ANSI colors
|
|
@@ -93,41 +96,12 @@ function validatePackage(packageJsonPath, repoName) {
|
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
function findPackages(rootDir) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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;
|
|
99
|
+
return _findPackagePaths(rootDir).map((pkgPath) => {
|
|
100
|
+
const parts = pkgPath.split(path.sep);
|
|
101
|
+
const repoDir = parts.find((p) => p.startsWith('kb-labs-')) || 'unknown';
|
|
102
|
+
const repoName = extractRepoName(repoDir);
|
|
103
|
+
return { path: pkgPath, repoName, repoPath: repoDir };
|
|
104
|
+
}).filter((p) => p.repoName);
|
|
131
105
|
}
|
|
132
106
|
|
|
133
107
|
function main() {
|
package/bin/devkit-visualize.mjs
CHANGED
|
@@ -20,6 +20,9 @@ import fs from 'node:fs';
|
|
|
20
20
|
import path from 'node:path';
|
|
21
21
|
import { fileURLToPath } from 'node:url';
|
|
22
22
|
|
|
23
|
+
// Shared package discovery — supports both flat and categorized layouts
|
|
24
|
+
import { findPackages } from './lib/find-packages.mjs';
|
|
25
|
+
|
|
23
26
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
24
27
|
|
|
25
28
|
// ANSI colors
|
|
@@ -49,40 +52,6 @@ const options = {
|
|
|
49
52
|
all: !args.includes('--graph') && !args.includes('--stats') && !args.includes('--tree'),
|
|
50
53
|
};
|
|
51
54
|
|
|
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
55
|
/**
|
|
87
56
|
* Build dependency graph
|
|
88
57
|
*/
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared package discovery for KB Labs workspace.
|
|
3
|
+
*
|
|
4
|
+
* Supports both flat and categorized workspace layouts.
|
|
5
|
+
* All devkit tools should import this instead of inlining their own findPackages.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
|
|
11
|
+
const CATEGORIES = ['platform', 'plugins', 'infra', 'templates', 'installer', 'sites'];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Find all KB Labs packages in the workspace.
|
|
15
|
+
*
|
|
16
|
+
* Scans for kb-labs-* directories in:
|
|
17
|
+
* Scans root level (flat: kb-labs-*) and category level (platform/kb-labs-*, plugins/kb-labs-*, etc.).
|
|
18
|
+
* Also scans apps/ directories for app-style packages.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} rootDir - Workspace root directory
|
|
21
|
+
* @param {string} [filterPackage] - Optional package name filter (e.g., 'core-cli')
|
|
22
|
+
* @returns {string[]} Array of package.json file paths
|
|
23
|
+
*/
|
|
24
|
+
export function findPackages(rootDir, filterPackage) {
|
|
25
|
+
const packages = [];
|
|
26
|
+
|
|
27
|
+
// Collect all repo directories (both flat and categorized)
|
|
28
|
+
const repoDirs = [];
|
|
29
|
+
|
|
30
|
+
const rootEntries = fs.readdirSync(rootDir, { withFileTypes: true });
|
|
31
|
+
for (const entry of rootEntries) {
|
|
32
|
+
if (!entry.isDirectory()) continue;
|
|
33
|
+
|
|
34
|
+
if (entry.name.startsWith('kb-labs-')) {
|
|
35
|
+
// Flat layout: kb-labs-* at root
|
|
36
|
+
repoDirs.push(path.join(rootDir, entry.name));
|
|
37
|
+
} else if (CATEGORIES.includes(entry.name)) {
|
|
38
|
+
// Categorized layout: platform/kb-labs-*, plugins/kb-labs-*, etc.
|
|
39
|
+
const categoryPath = path.join(rootDir, entry.name);
|
|
40
|
+
try {
|
|
41
|
+
const categoryEntries = fs.readdirSync(categoryPath, { withFileTypes: true });
|
|
42
|
+
for (const catEntry of categoryEntries) {
|
|
43
|
+
if (catEntry.isDirectory() && catEntry.name.startsWith('kb-labs-')) {
|
|
44
|
+
repoDirs.push(path.join(categoryPath, catEntry.name));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
// Category dir not readable, skip
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Scan each repo for packages/ and apps/ subdirectories
|
|
54
|
+
for (const repoPath of repoDirs) {
|
|
55
|
+
for (const subdir of ['packages', 'apps']) {
|
|
56
|
+
const pkgsDir = path.join(repoPath, subdir);
|
|
57
|
+
if (!fs.existsSync(pkgsDir)) continue;
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const pkgDirs = fs.readdirSync(pkgsDir, { withFileTypes: true });
|
|
61
|
+
for (const pkgDir of pkgDirs) {
|
|
62
|
+
if (!pkgDir.isDirectory()) continue;
|
|
63
|
+
if (filterPackage && pkgDir.name !== filterPackage) continue;
|
|
64
|
+
|
|
65
|
+
const packageJsonPath = path.join(pkgsDir, pkgDir.name, 'package.json');
|
|
66
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
67
|
+
packages.push(packageJsonPath);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
// Dir not readable, skip
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return packages;
|
|
77
|
+
}
|
package/eslint/node.js
CHANGED
package/eslint/plugin.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strict ESLint preset for KB Labs plugins.
|
|
3
|
+
*
|
|
4
|
+
* Extends the base node.js preset with architectural boundary enforcement:
|
|
5
|
+
* plugins can only import from @kb-labs/sdk and their own internal packages.
|
|
6
|
+
*
|
|
7
|
+
* Usage in plugin's eslint.config.js:
|
|
8
|
+
* import pluginPreset from '@kb-labs/devkit/eslint/plugin.js';
|
|
9
|
+
* export default [...pluginPreset];
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import nodePreset from './node.js'
|
|
13
|
+
|
|
14
|
+
// Packages that plugins are allowed to import from
|
|
15
|
+
const ALLOWED_EXTERNAL = [
|
|
16
|
+
'@kb-labs/sdk',
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
// Platform internals that plugins must NOT import directly
|
|
20
|
+
const FORBIDDEN_PATTERNS = [
|
|
21
|
+
'@kb-labs/core-*',
|
|
22
|
+
'@kb-labs/cli-*',
|
|
23
|
+
'@kb-labs/shared-*',
|
|
24
|
+
'@kb-labs/plugin-*',
|
|
25
|
+
'@kb-labs/workflow-*',
|
|
26
|
+
'@kb-labs/rest-api-*',
|
|
27
|
+
'@kb-labs/studio-*',
|
|
28
|
+
'@kb-labs/adapters-*',
|
|
29
|
+
'@kb-labs/gateway-*',
|
|
30
|
+
'@kb-labs/host-agent-*',
|
|
31
|
+
'@kb-labs/state-*',
|
|
32
|
+
'@kb-labs/tenant',
|
|
33
|
+
'@kb-labs/perm-*',
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
export default [
|
|
37
|
+
...nodePreset,
|
|
38
|
+
|
|
39
|
+
{
|
|
40
|
+
rules: {
|
|
41
|
+
'no-restricted-imports': ['error', {
|
|
42
|
+
patterns: FORBIDDEN_PATTERNS.map(pattern => ({
|
|
43
|
+
group: [pattern],
|
|
44
|
+
message: `Plugins must depend only on @kb-labs/sdk. Direct imports from platform internals are not allowed. Re-export what you need through SDK.`,
|
|
45
|
+
})),
|
|
46
|
+
}],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
export { ALLOWED_EXTERNAL, FORBIDDEN_PATTERNS }
|
package/eslint/react.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kb-labs/devkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Shared developer toolkit for KB Labs projects: TS/ESLint/Prettier/Vitest/Tsup presets and reusable GitHub Actions.",
|
|
6
6
|
"files": [
|
|
@@ -65,8 +65,10 @@
|
|
|
65
65
|
"./eslint/": "./eslint/",
|
|
66
66
|
"./eslint/node.js": "./eslint/node.js",
|
|
67
67
|
"./eslint/react.js": "./eslint/react.js",
|
|
68
|
+
"./eslint/plugin.js": "./eslint/plugin.js",
|
|
68
69
|
"./eslint/node": "./eslint/node.js",
|
|
69
70
|
"./eslint/react": "./eslint/react.js",
|
|
71
|
+
"./eslint/plugin": "./eslint/plugin.js",
|
|
70
72
|
"./prettier/": "./prettier/",
|
|
71
73
|
"./prettier/index.json": "./prettier/index.json",
|
|
72
74
|
"./vitest/": "./vitest/",
|
|
@@ -101,6 +103,26 @@
|
|
|
101
103
|
"./tsup/external-sync": "./tsup/external-sync.mjs",
|
|
102
104
|
"./sync": "./sync/index.mjs"
|
|
103
105
|
},
|
|
106
|
+
"scripts": {
|
|
107
|
+
"clean": "rimraf dist",
|
|
108
|
+
"build": "echo \"@kb-labs/devkit: nothing to build\"",
|
|
109
|
+
"lint": "echo \"@kb-labs/devkit: lint skipped (plain JS/MJS, not TS project)\" && exit 0",
|
|
110
|
+
"lint:fix": "eslint . --fix --ignore-pattern 'dist/**'",
|
|
111
|
+
"test": "vitest run --passWithNoTests",
|
|
112
|
+
"test:watch": "vitest --passWithNoTests",
|
|
113
|
+
"paths": "node ./bin/devkit-paths.mjs",
|
|
114
|
+
"fixtures": "node scripts/fixtures.js –help",
|
|
115
|
+
"fixtures:check": "node scripts/fixtures.js –action=check",
|
|
116
|
+
"fixtures:lint": "node scripts/fixtures.js –action=lint",
|
|
117
|
+
"fixtures:test": "node scripts/fixtures.js –action=test",
|
|
118
|
+
"fixtures:build": "node scripts/fixtures.js –action=build",
|
|
119
|
+
"fixtures:bootstrap": "node scripts/fixtures.js –action=bootstrap",
|
|
120
|
+
"fixtures:clean": "node scripts/fixtures.js –action=clean",
|
|
121
|
+
"fixtures:ci": "node scripts/fixtures.js –action=check –since=${GITHUB_BASE_REF:-origin/main} –concurrency=3 || node scripts/fixtures.js –action=check",
|
|
122
|
+
"postinstall": "kb-devkit-tsup-external --generate || true",
|
|
123
|
+
"dev": "tsup --config tsup.config.ts --watch",
|
|
124
|
+
"type-check": "echo \"@kb-labs/devkit: type-check skipped (plain JS/MJS, not TS project)\" && exit 0"
|
|
125
|
+
},
|
|
104
126
|
"peerDependencies": {
|
|
105
127
|
"@playwright/test": "*",
|
|
106
128
|
"eslint": "^9.35.0",
|
|
@@ -135,11 +157,14 @@
|
|
|
135
157
|
"dependencies": {
|
|
136
158
|
"eslint-import-resolver-typescript": "^3.10.1",
|
|
137
159
|
"eslint-plugin-import": "^2.32.0",
|
|
160
|
+
"eslint-plugin-jsx-a11y": "^6.10.0",
|
|
161
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
138
162
|
"eslint-plugin-unused-imports": "^4.1.4",
|
|
139
163
|
"glob": "^11.0.0",
|
|
140
164
|
"yaml": "^2.8.0"
|
|
141
165
|
},
|
|
142
166
|
"devDependencies": {
|
|
167
|
+
"@kb-labs/devkit": "link:./",
|
|
143
168
|
"@types/node": "^24.3.3",
|
|
144
169
|
"@vitest/coverage-v8": "^3.2.4",
|
|
145
170
|
"eslint": "^9.35.0",
|
|
@@ -152,9 +177,9 @@
|
|
|
152
177
|
"tsup": "^8.5.0",
|
|
153
178
|
"typescript": "^5.9.2",
|
|
154
179
|
"typescript-eslint": "^8.44.0",
|
|
155
|
-
"vitest": "^3.2.4"
|
|
156
|
-
"@kb-labs/devkit": "1.0.0"
|
|
180
|
+
"vitest": "^3.2.4"
|
|
157
181
|
},
|
|
182
|
+
"packageManager": "pnpm@9.11.0+sha512.0a203ffaed5a3f63242cd064c8fb5892366c103e328079318f78062f24ea8c9d50bc6a47aa3567cabefd824d170e78fa2745ed1f16b132e16436146b7688f19b",
|
|
158
183
|
"engines": {
|
|
159
184
|
"node": ">=20.0.0",
|
|
160
185
|
"pnpm": ">=9.11.0"
|
|
@@ -162,25 +187,5 @@
|
|
|
162
187
|
"license": "MIT",
|
|
163
188
|
"publishConfig": {
|
|
164
189
|
"access": "public"
|
|
165
|
-
},
|
|
166
|
-
"scripts": {
|
|
167
|
-
"clean": "rimraf dist",
|
|
168
|
-
"build": "echo \"@kb-labs/devkit: nothing to build\"",
|
|
169
|
-
"lint": "eslint . --ignore-pattern 'dist/**'",
|
|
170
|
-
"lint:fix": "eslint . --fix --ignore-pattern 'dist/**'",
|
|
171
|
-
"test": "vitest run --passWithNoTests",
|
|
172
|
-
"test:watch": "vitest --passWithNoTests",
|
|
173
|
-
"paths": "node ./bin/devkit-paths.mjs",
|
|
174
|
-
"fixtures": "node scripts/fixtures.js –help",
|
|
175
|
-
"fixtures:check": "node scripts/fixtures.js –action=check",
|
|
176
|
-
"fixtures:lint": "node scripts/fixtures.js –action=lint",
|
|
177
|
-
"fixtures:test": "node scripts/fixtures.js –action=test",
|
|
178
|
-
"fixtures:build": "node scripts/fixtures.js –action=build",
|
|
179
|
-
"fixtures:bootstrap": "node scripts/fixtures.js –action=bootstrap",
|
|
180
|
-
"fixtures:clean": "node scripts/fixtures.js –action=clean",
|
|
181
|
-
"fixtures:ci": "node scripts/fixtures.js –action=check –since=${GITHUB_BASE_REF:-origin/main} –concurrency=3 || node scripts/fixtures.js –action=check",
|
|
182
|
-
"postinstall": "kb-devkit-tsup-external --generate || true",
|
|
183
|
-
"dev": "tsup --config tsup.config.ts --watch",
|
|
184
|
-
"type-check": "tsc --noEmit"
|
|
185
190
|
}
|
|
186
|
-
}
|
|
191
|
+
}
|
package/sync/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Public API: sync runner used by bin and by consumers via import('@kb-labs/devkit/sync')
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
|
-
import { cp, mkdir, readFile, readdir, writeFile, access } from 'node:fs/promises';
|
|
3
|
+
import { cp, mkdir, readFile, readdir, writeFile, access, chmod } from 'node:fs/promises';
|
|
4
4
|
import { dirname, join, resolve, relative } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import process from 'node:process';
|
|
@@ -57,7 +57,12 @@ const BASE_MAP = {
|
|
|
57
57
|
from: resolve(DEVKIT_ROOT, 'templates/configs'),
|
|
58
58
|
to: (root) => root, // Root of package
|
|
59
59
|
type: 'configs', // Special type for config drift checking
|
|
60
|
-
}
|
|
60
|
+
},
|
|
61
|
+
hooks: {
|
|
62
|
+
from: resolve(DEVKIT_ROOT, 'scripts/hooks'),
|
|
63
|
+
to: (root) => resolve(root, 'scripts/hooks'),
|
|
64
|
+
type: 'hooks', // Special type: also installs into .git/hooks/
|
|
65
|
+
},
|
|
61
66
|
};
|
|
62
67
|
|
|
63
68
|
function resolveFromDevkit(p) {
|
|
@@ -231,7 +236,7 @@ async function runCheck(root, effectiveMap, targets, { verbose, scope }) {
|
|
|
231
236
|
for (const key of targets) {
|
|
232
237
|
const { from, to, type } = effectiveMap[key];
|
|
233
238
|
const dst = to(root);
|
|
234
|
-
const res = await comparePaths(from, dst, type);
|
|
239
|
+
const res = await comparePaths(from, dst, type === 'hooks' ? 'dir' : type);
|
|
235
240
|
|
|
236
241
|
const changed = (res.diffs.length + res.onlySrc.length + (considerOnlyDst ? res.onlyDst.length : 0)) > 0;
|
|
237
242
|
const item = {
|
|
@@ -373,6 +378,7 @@ async function runSync(root, effectiveMap, targets, { force, verbose, dryRun })
|
|
|
373
378
|
return [sf, join(dst, rel)];
|
|
374
379
|
});
|
|
375
380
|
}
|
|
381
|
+
const isHooks = type === 'hooks';
|
|
376
382
|
const beforeState = await Promise.all(filePairs.map(async ([srcFile, dstFile]) => {
|
|
377
383
|
const existed = await exists(dstFile);
|
|
378
384
|
const hb = existed ? await sha256File(dstFile).catch(() => null) : null;
|
|
@@ -404,6 +410,24 @@ async function runSync(root, effectiveMap, targets, { force, verbose, dryRun })
|
|
|
404
410
|
details.push({ key, action: 'synced', from, dst, type });
|
|
405
411
|
log(`→ ${key}: ${created} created, ${updated} updated, ${keptF} kept`);
|
|
406
412
|
log(`synced ${key} -> ${dst}`);
|
|
413
|
+
|
|
414
|
+
// For hooks: also install into .git/hooks/ and chmod +x
|
|
415
|
+
if (isHooks) {
|
|
416
|
+
const gitHooksDir = resolve(root, '.git', 'hooks');
|
|
417
|
+
const gitDirExists = await exists(resolve(root, '.git'));
|
|
418
|
+
if (gitDirExists) {
|
|
419
|
+
await mkdir(gitHooksDir, { recursive: true });
|
|
420
|
+
for (const [, dstFile] of filePairs) {
|
|
421
|
+
const hookName = dstFile.slice(dst.length + 1);
|
|
422
|
+
const gitHookDst = join(gitHooksDir, hookName);
|
|
423
|
+
await cp(dstFile, gitHookDst, { force: true });
|
|
424
|
+
await chmod(gitHookDst, 0o755);
|
|
425
|
+
}
|
|
426
|
+
log(`hooks installed into .git/hooks/`);
|
|
427
|
+
} else {
|
|
428
|
+
warn(`skip .git/hooks install — no .git directory found at ${root}`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
407
431
|
}
|
|
408
432
|
const finishedAt = Date.now();
|
|
409
433
|
log('sync done', summary, `(force=${force}, dry-run=${!!dryRun})`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../../../kb-labs-sdk/tsup.config.ts","../../../kb-labs-sdk/vitest.config.ts","../../../kb-labs-sdk/packages/sdk/tsup.config.ts","../../../kb-labs-sdk/packages/sdk/dist/index.d.ts","../../../kb-labs-sdk/packages/sdk/dist/testing/index.d.ts","../../../kb-labs-sdk/packages/sdk/src/index.ts","../../../kb-labs-sdk/packages/sdk/src/__tests__/exports.snapshot.test.ts","../../../kb-labs-sdk/packages/sdk/src/command/index.ts","../../../kb-labs-sdk/packages/sdk/src/manifest/index.ts","../../../kb-labs-sdk/packages/sdk/src/test/create-test-context.ts","../../../kb-labs-sdk/packages/sdk/src/test/index.ts","../../../kb-labs-sdk/packages/sdk/src/testing/index.ts","../../../kb-labs-sdk/packages/sdk/src/utils/index.ts","../../../kb-labs-sdk/eslint.config.js","../../../kb-labs-sdk/packages/sdk/eslint.config.js","../../../kb-labs-sdk/packages/sdk/dist/index.js","../../../kb-labs-sdk/packages/sdk/dist/testing/index.js","../../../kb-labs-sdk/packages/sdk/
|
|
1
|
+
{"root":["../../../kb-labs-sdk/tsup.config.bin.ts","../../../kb-labs-sdk/tsup.config.cli.ts","../../../kb-labs-sdk/tsup.config.dual.ts","../../../kb-labs-sdk/tsup.config.ts","../../../kb-labs-sdk/vitest.config.ts","../../../kb-labs-sdk/packages/sdk/tsup.config.ts","../../../kb-labs-sdk/packages/sdk/dist/index.d.ts","../../../kb-labs-sdk/packages/sdk/dist/command/index.d.ts","../../../kb-labs-sdk/packages/sdk/dist/contracts/index.d.ts","../../../kb-labs-sdk/packages/sdk/dist/hooks/index.d.ts","../../../kb-labs-sdk/packages/sdk/dist/manifest/index.d.ts","../../../kb-labs-sdk/packages/sdk/dist/testing/index.d.ts","../../../kb-labs-sdk/packages/sdk/dist/types/index.d.ts","../../../kb-labs-sdk/packages/sdk/src/index.ts","../../../kb-labs-sdk/packages/sdk/src/__tests__/deep-behavior.test.ts","../../../kb-labs-sdk/packages/sdk/src/__tests__/entrypoint-contracts.test.ts","../../../kb-labs-sdk/packages/sdk/src/__tests__/exports.snapshot.test.ts","../../../kb-labs-sdk/packages/sdk/src/__type-tests__/sdk-typing.type-test.ts","../../../kb-labs-sdk/packages/sdk/src/command/index.ts","../../../kb-labs-sdk/packages/sdk/src/contracts/index.ts","../../../kb-labs-sdk/packages/sdk/src/hooks/index.ts","../../../kb-labs-sdk/packages/sdk/src/manifest/index.ts","../../../kb-labs-sdk/packages/sdk/src/test/create-test-context.ts","../../../kb-labs-sdk/packages/sdk/src/test/index.ts","../../../kb-labs-sdk/packages/sdk/src/testing/index.ts","../../../kb-labs-sdk/packages/sdk/src/types/index.ts","../../../kb-labs-sdk/packages/sdk/src/utils/index.ts","../../../kb-labs-sdk/eslint.config.js","../../../kb-labs-sdk/packages/sdk/eslint.config.js","../../../kb-labs-sdk/packages/sdk/dist/index.js","../../../kb-labs-sdk/packages/sdk/dist/command/index.js","../../../kb-labs-sdk/packages/sdk/dist/contracts/index.js","../../../kb-labs-sdk/packages/sdk/dist/hooks/index.js","../../../kb-labs-sdk/packages/sdk/dist/manifest/index.js","../../../kb-labs-sdk/packages/sdk/dist/testing/index.js","../../../kb-labs-sdk/packages/sdk/dist/types/index.js","../../../kb-labs-sdk/packages/sdk/scripts/check-api-removals.mjs","../../../kb-labs-sdk/packages/sdk/scripts/generate-export-glossary.mjs","../../../kb-labs-sdk/scripts/devkit-sync.mjs"],"errors":true,"version":"5.9.3"}
|
package/tsconfig/node.json
CHANGED
package/tsup/bin.js
CHANGED
package/tsup/dual.js
CHANGED
|
@@ -152,4 +152,9 @@ export default defineConfig({
|
|
|
152
152
|
...externalList, // Explicitly listed packages (workspace + local deps)
|
|
153
153
|
/^@kb-labs\//, // Force all @kb-labs packages to be external
|
|
154
154
|
],
|
|
155
|
+
ignoreWatch: [
|
|
156
|
+
'**/node_modules/**',
|
|
157
|
+
'**/dist/**',
|
|
158
|
+
'**/.git/**',
|
|
159
|
+
],
|
|
155
160
|
})
|
package/tsup/node.js
CHANGED
|
@@ -1,8 +1,40 @@
|
|
|
1
1
|
import { defineConfig } from 'tsup'
|
|
2
2
|
import { readTsupExternalSync } from './external-sync.mjs'
|
|
3
|
-
import { readFileSync } from 'node:fs'
|
|
3
|
+
import { readFileSync, existsSync } from 'node:fs'
|
|
4
4
|
import { join } from 'node:path'
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Derive tsup entry points from package.json exports field.
|
|
8
|
+
* Maps each export value like "./dist/foo.js" → "src/foo.ts" (or root "foo.ts").
|
|
9
|
+
* Falls back to ['src/index.ts'] if no exports or src file not found.
|
|
10
|
+
*/
|
|
11
|
+
function resolveEntryFromExports() {
|
|
12
|
+
try {
|
|
13
|
+
const pkgPath = join(process.cwd(), 'package.json')
|
|
14
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
|
|
15
|
+
const exportsMap = pkg.exports ?? {}
|
|
16
|
+
const srcFiles = new Set()
|
|
17
|
+
|
|
18
|
+
for (const condition of Object.values(exportsMap)) {
|
|
19
|
+
// condition can be a string or { import, types, require, ... }
|
|
20
|
+
const importPath = typeof condition === 'string' ? condition
|
|
21
|
+
: (condition.import ?? condition.default ?? null)
|
|
22
|
+
if (!importPath || typeof importPath !== 'string') continue
|
|
23
|
+
|
|
24
|
+
// "./dist/foo.js" → "foo", then try "src/foo.ts" or "foo.ts"
|
|
25
|
+
const base = importPath.replace(/^\.\/dist\//, '').replace(/\.js$/, '')
|
|
26
|
+
const candidates = [`src/${base}.ts`, `${base}.ts`]
|
|
27
|
+
for (const c of candidates) {
|
|
28
|
+
if (existsSync(join(process.cwd(), c))) { srcFiles.add(c); break }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return srcFiles.size > 0 ? Array.from(srcFiles) : ['src/index.ts']
|
|
33
|
+
} catch {
|
|
34
|
+
return ['src/index.ts']
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
6
38
|
function resolveExternalDependencies() {
|
|
7
39
|
try {
|
|
8
40
|
const pkgPath = join(process.cwd(), 'package.json')
|
|
@@ -32,11 +64,12 @@ function getExternal() {
|
|
|
32
64
|
return resolveExternalDependencies()
|
|
33
65
|
}
|
|
34
66
|
|
|
35
|
-
// Pre-compute external list once at module load time
|
|
67
|
+
// Pre-compute external list and entry once at module load time
|
|
36
68
|
const externalList = getExternal()
|
|
69
|
+
const entryFromExports = resolveEntryFromExports()
|
|
37
70
|
|
|
38
71
|
export default defineConfig({
|
|
39
|
-
entry:
|
|
72
|
+
entry: entryFromExports,
|
|
40
73
|
format: ['esm'],
|
|
41
74
|
target: 'es2022',
|
|
42
75
|
sourcemap: true,
|
|
@@ -48,6 +81,11 @@ export default defineConfig({
|
|
|
48
81
|
splitting: false,
|
|
49
82
|
skipNodeModulesBundle: true,
|
|
50
83
|
shims: false,
|
|
84
|
+
ignoreWatch: [
|
|
85
|
+
'**/node_modules/**',
|
|
86
|
+
'**/dist/**',
|
|
87
|
+
'**/.git/**',
|
|
88
|
+
],
|
|
51
89
|
// Mark all node_modules packages as external (including transitive deps)
|
|
52
90
|
// Use noExternal: [] to prevent bundling any node_modules packages
|
|
53
91
|
// This is more reliable than regex for ensuring transitive deps stay external
|
package/tsup/react-lib.js
CHANGED