@r0hitsharma/uikit-cli 0.12.0-rohit-fork-ci.1
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/README.md +286 -0
- package/dist/cli.js +225 -0
- package/dist/cli.sh +10 -0
- package/dist/command-executor.js +41 -0
- package/dist/commands/doctor.js +518 -0
- package/dist/commands/format.js +34 -0
- package/dist/commands/link.js +252 -0
- package/dist/commands/lint.js +43 -0
- package/dist/commands/register.js +35 -0
- package/dist/commands/unlink.js +68 -0
- package/dist/fs-utils.js +56 -0
- package/dist/link-validator.js +80 -0
- package/dist/logger.js +43 -0
- package/dist/package-discovery.js +218 -0
- package/dist/shell-utils.js +9 -0
- package/dist/tool-binaries.js +16 -0
- package/dist/types.js +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
/**
|
|
3
|
+
* Package and workspace discovery for monorepos
|
|
4
|
+
*/
|
|
5
|
+
export class PackageDiscovery {
|
|
6
|
+
fs;
|
|
7
|
+
constructor(fs) {
|
|
8
|
+
this.fs = fs;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Find the consumer root by walking up from `startDir`. Prefers a monorepo
|
|
12
|
+
* root (a `package.json` with a `workspaces` field); if none exists anywhere
|
|
13
|
+
* up the tree, falls back to the nearest single package that depends on an
|
|
14
|
+
* `@r0hitsharma/*` package — so link/unlink work for a plain
|
|
15
|
+
* non-workspaces consumer, not just a monorepo.
|
|
16
|
+
*/
|
|
17
|
+
findConsumerRoot(startDir) {
|
|
18
|
+
let current = startDir;
|
|
19
|
+
let singlePackageRoot = null;
|
|
20
|
+
while (true) {
|
|
21
|
+
const pkgPath = path.join(current, 'package.json');
|
|
22
|
+
if (this.fs.exists(pkgPath)) {
|
|
23
|
+
try {
|
|
24
|
+
const pkg = this.fs.readJson(pkgPath);
|
|
25
|
+
// A workspaces root always wins (unchanged monorepo behavior).
|
|
26
|
+
if (pkg.workspaces) {
|
|
27
|
+
return current;
|
|
28
|
+
}
|
|
29
|
+
// Otherwise remember the nearest package that consumes uikit, as a
|
|
30
|
+
// fallback if no workspaces root turns up above it.
|
|
31
|
+
if (singlePackageRoot === null && this.dependsOnUikit(pkg)) {
|
|
32
|
+
singlePackageRoot = current;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Continue searching if parse fails
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const parent = path.dirname(current);
|
|
40
|
+
if (parent === current) {
|
|
41
|
+
if (singlePackageRoot !== null) {
|
|
42
|
+
return singlePackageRoot;
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`Could not find a consumer root.\n` +
|
|
45
|
+
`Searched from: ${startDir}\n` +
|
|
46
|
+
`Found no package.json with a "workspaces" field, and none depending ` +
|
|
47
|
+
`on an @r0hitsharma/* package.\n` +
|
|
48
|
+
`Run from inside your consumer project (a monorepo root or a single ` +
|
|
49
|
+
`package that installs uikit).`);
|
|
50
|
+
}
|
|
51
|
+
current = parent;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
dependsOnUikit(pkg) {
|
|
55
|
+
const fields = [
|
|
56
|
+
pkg.dependencies,
|
|
57
|
+
pkg.devDependencies,
|
|
58
|
+
pkg.peerDependencies,
|
|
59
|
+
pkg.optionalDependencies,
|
|
60
|
+
];
|
|
61
|
+
return fields.some((field) => field &&
|
|
62
|
+
Object.keys(field).some((name) => name.startsWith('@r0hitsharma/')));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Find uikit root by walking up looking for valid uikit monorepo structure
|
|
66
|
+
*/
|
|
67
|
+
findUIKitRoot(startDir) {
|
|
68
|
+
let current = startDir;
|
|
69
|
+
while (true) {
|
|
70
|
+
if (this.isValidUIKitRoot(current)) {
|
|
71
|
+
return current;
|
|
72
|
+
}
|
|
73
|
+
const parent = path.dirname(current);
|
|
74
|
+
if (parent === current) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
current = parent;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Find uikit root from consumer by looking for sibling "uikit" directory
|
|
82
|
+
*/
|
|
83
|
+
findUIKitRootFromConsumer(consumerRoot) {
|
|
84
|
+
const candidateNames = ['uikit'];
|
|
85
|
+
let current = consumerRoot;
|
|
86
|
+
while (true) {
|
|
87
|
+
for (const candidateName of candidateNames) {
|
|
88
|
+
const candidate = path.join(current, candidateName);
|
|
89
|
+
if (this.isValidUIKitRoot(candidate)) {
|
|
90
|
+
return candidate;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const parent = path.dirname(current);
|
|
94
|
+
if (parent === current) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
current = parent;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Check if directory is a valid uikit root (has design-system package)
|
|
102
|
+
*/
|
|
103
|
+
isValidUIKitRoot(rootDir) {
|
|
104
|
+
if (!this.fs.exists(rootDir)) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const workspaces = this.loadWorkspaces(rootDir);
|
|
109
|
+
return workspaces.some((ws) => ws.name === '@r0hitsharma/design-system');
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Load all workspaces from a monorepo root
|
|
117
|
+
*/
|
|
118
|
+
loadWorkspaces(rootDir) {
|
|
119
|
+
const patterns = this.getWorkspacePatterns(rootDir);
|
|
120
|
+
const workspaces = [];
|
|
121
|
+
if (process.env.UIKIT_DEBUG) {
|
|
122
|
+
console.log('[DEBUG loadWorkspaces] rootDir:', rootDir, 'patterns:', patterns);
|
|
123
|
+
}
|
|
124
|
+
for (const pattern of patterns) {
|
|
125
|
+
const resolved = this.resolveWorkspacePattern(rootDir, pattern);
|
|
126
|
+
if (process.env.UIKIT_DEBUG) {
|
|
127
|
+
console.log('[DEBUG loadWorkspaces] pattern:', pattern, 'resolved to:', resolved);
|
|
128
|
+
}
|
|
129
|
+
for (const dir of resolved) {
|
|
130
|
+
const ws = this.readPackageAsWorkspace(rootDir, dir);
|
|
131
|
+
if (ws) {
|
|
132
|
+
workspaces.push(ws);
|
|
133
|
+
}
|
|
134
|
+
else if (process.env.UIKIT_DEBUG) {
|
|
135
|
+
console.log('[DEBUG loadWorkspaces] Skipping (no/invalid package.json):', dir);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return workspaces.filter((ws) => Boolean(ws.name));
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Load consumer workspaces, falling back to the root package itself when the
|
|
143
|
+
* consumer is a single, non-workspaces package (`location: ''`). This is what
|
|
144
|
+
* lets link/unlink target a plain consumer, not only a monorepo.
|
|
145
|
+
*/
|
|
146
|
+
loadConsumerWorkspaces(rootDir) {
|
|
147
|
+
const workspaces = this.loadWorkspaces(rootDir);
|
|
148
|
+
if (workspaces.length > 0) {
|
|
149
|
+
return workspaces;
|
|
150
|
+
}
|
|
151
|
+
const root = this.readPackageAsWorkspace(rootDir, rootDir);
|
|
152
|
+
return root && root.name ? [root] : [];
|
|
153
|
+
}
|
|
154
|
+
/** Read a directory's package.json into a WorkspaceInfo, or null if absent/invalid. */
|
|
155
|
+
readPackageAsWorkspace(rootDir, dir) {
|
|
156
|
+
const pkgPath = path.join(dir, 'package.json');
|
|
157
|
+
if (!this.fs.exists(pkgPath)) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
const pkg = this.fs.readJson(pkgPath);
|
|
162
|
+
return {
|
|
163
|
+
name: pkg.name ?? null,
|
|
164
|
+
location: path.relative(rootDir, dir),
|
|
165
|
+
path: dir,
|
|
166
|
+
dependencies: pkg.dependencies ?? {},
|
|
167
|
+
devDependencies: pkg.devDependencies ?? {},
|
|
168
|
+
peerDependencies: pkg.peerDependencies ?? {},
|
|
169
|
+
optionalDependencies: pkg.optionalDependencies ?? {},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
getWorkspacePatterns(rootDir) {
|
|
177
|
+
const pkgPath = path.join(rootDir, 'package.json');
|
|
178
|
+
if (!this.fs.exists(pkgPath)) {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
const pkg = this.fs.readJson(pkgPath);
|
|
183
|
+
if (Array.isArray(pkg.workspaces)) {
|
|
184
|
+
return pkg.workspaces;
|
|
185
|
+
}
|
|
186
|
+
if (pkg.workspaces && Array.isArray(pkg.workspaces.packages)) {
|
|
187
|
+
return pkg.workspaces.packages;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
// Return empty if parse fails
|
|
192
|
+
}
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
resolveWorkspacePattern(rootDir, pattern) {
|
|
196
|
+
// Handle simple glob patterns like "packages/*"
|
|
197
|
+
if (pattern.includes('*')) {
|
|
198
|
+
// Only support "dir/*" pattern for now
|
|
199
|
+
if (pattern.endsWith('/*')) {
|
|
200
|
+
const baseDir = pattern.slice(0, -2);
|
|
201
|
+
const basePath = path.join(rootDir, baseDir);
|
|
202
|
+
if (!this.fs.exists(basePath) || !this.fs.isDirectory(basePath)) {
|
|
203
|
+
return [];
|
|
204
|
+
}
|
|
205
|
+
const entries = this.fs.readDir(basePath);
|
|
206
|
+
return entries
|
|
207
|
+
.filter((entry) => !entry.startsWith('.') && entry !== 'node_modules')
|
|
208
|
+
.map((entry) => path.join(basePath, entry))
|
|
209
|
+
.filter((p) => this.fs.isDirectory(p));
|
|
210
|
+
}
|
|
211
|
+
// Unsupported pattern
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
// Static pattern, return as-is if it's a directory
|
|
215
|
+
const fullPath = path.join(rootDir, pattern);
|
|
216
|
+
return this.fs.isDirectory(fullPath) ? [fullPath] : [];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const requireFromCli = createRequire(import.meta.url);
|
|
4
|
+
/**
|
|
5
|
+
* Resolve a CLI binary from uikit-cli dependencies so consumers
|
|
6
|
+
* don't need tool packages installed in their own workspace.
|
|
7
|
+
*/
|
|
8
|
+
export function resolveCliBinary(packageName, binaryPath) {
|
|
9
|
+
try {
|
|
10
|
+
const packageJsonPath = requireFromCli.resolve(`${packageName}/package.json`);
|
|
11
|
+
return path.join(path.dirname(packageJsonPath), binaryPath);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
throw new Error(`Could not resolve ${packageName} binary from @r0hitsharma/uikit-cli dependencies.`);
|
|
15
|
+
}
|
|
16
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@r0hitsharma/uikit-cli",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"description": "CLI for managing local uikit package linking in consumer workspaces",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "tsc -b && npm run wrapper",
|
|
7
|
+
"wrapper": "./scripts/create-wrapper.sh",
|
|
8
|
+
"clean": "rm -rf dist",
|
|
9
|
+
"type:check": "tsc -p tsconfig.json --noEmit",
|
|
10
|
+
"lint": "oxlint -c oxlint.config.ts --max-warnings=0 src",
|
|
11
|
+
"lint:fix": "oxlint -c oxlint.config.ts --fix src",
|
|
12
|
+
"format": "oxfmt -c oxfmt.config.ts --write src",
|
|
13
|
+
"format:check": "oxfmt -c oxfmt.config.ts --check src",
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"prepare": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"bin": {
|
|
18
|
+
"uikit-cli": "./dist/cli.sh"
|
|
19
|
+
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"oxfmt": "0.67.0",
|
|
25
|
+
"oxlint": "1.82.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@r0hitsharma/oxfmt-config": "*",
|
|
29
|
+
"@r0hitsharma/tsconfig": "*",
|
|
30
|
+
"@types/node": "24.13.3",
|
|
31
|
+
"vitest": "^4.1.9"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"version": "0.12.0-rohit-fork-ci.1",
|
|
37
|
+
"repository": {
|
|
38
|
+
"url": "https://github.com/r0hitsharma/uikit"
|
|
39
|
+
}
|
|
40
|
+
}
|