@archon-research/uikit-cli 0.3.0-rohit-improve-cli.2 → 0.3.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/dist/cli.js CHANGED
@@ -1,490 +1,198 @@
1
1
  #!/usr/bin/env node
2
- import { execSync } from 'node:child_process';
3
- import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, symlinkSync, } from 'node:fs';
4
- import path from 'node:path';
5
2
  import { fileURLToPath } from 'node:url';
6
- const OXLINT_VERSION = '1.62.0';
7
- const OXFMT_VERSION = '0.47.0';
8
- function shellEscape(value) {
9
- return `'${value.replace(/'/g, `'"'"'`)}'`;
10
- }
11
- function run(command, cwd) {
12
- console.log(`> (${cwd}) ${command}`);
13
- execSync(command, { stdio: 'inherit', cwd });
14
- }
15
- function tryRun(command, cwd, quiet = false) {
16
- if (!quiet) {
17
- console.log(`> (${cwd}) ${command}`);
18
- }
19
- try {
20
- execSync(command, {
21
- stdio: quiet ? 'ignore' : 'inherit',
22
- cwd,
23
- });
24
- return true;
25
- }
26
- catch {
27
- return false;
28
- }
29
- }
30
- function removeWorkspaceShadowInstall(consumerRoot, workspace, packageName) {
31
- const packagePath = path.join(consumerRoot, workspace, 'node_modules', packageName);
32
- if (!existsSync(packagePath)) {
33
- return;
34
- }
35
- try {
36
- const stats = lstatSync(packagePath);
37
- if (stats.isSymbolicLink()) {
38
- return;
39
- }
40
- // Remove workspace-local regular install so Node resolves to linked root package.
41
- rmSync(packagePath, { recursive: true, force: true });
42
- console.log(`Removed shadow install at ${workspace}/node_modules/${packageName} to preserve local links.`);
43
- }
44
- catch {
45
- // Best effort cleanup; linking may still succeed via root resolution.
46
- }
47
- }
48
- function ensureLinkedPath(consumerRoot, packageName, expectedTarget) {
49
- const packagePath = path.join(consumerRoot, 'node_modules', packageName);
50
- try {
51
- const stats = lstatSync(packagePath);
52
- if (stats.isSymbolicLink() && realpathSync(packagePath) === expectedTarget) {
53
- return;
54
- }
55
- rmSync(packagePath, { recursive: true, force: true });
56
- }
57
- catch {
58
- // Path may not exist yet; continue and create symlink.
59
- }
60
- mkdirSync(path.dirname(packagePath), { recursive: true });
61
- symlinkSync(expectedTarget, packagePath, 'dir');
62
- }
63
- function clearWorkspaceViteCache(consumerRoot, workspace) {
64
- const viteCachePath = path.join(consumerRoot, workspace, 'node_modules', '.vite');
65
- if (!existsSync(viteCachePath)) {
66
- return;
67
- }
68
- try {
69
- rmSync(viteCachePath, { recursive: true, force: true });
70
- console.log(`Cleared Vite cache at ${workspace}/node_modules/.vite.`);
71
- }
72
- catch {
73
- // Best-effort cache cleanup; linking still succeeds without this.
74
- }
75
- }
76
- function readJson(filePath) {
77
- const content = readFileSync(filePath, 'utf8');
78
- return JSON.parse(content);
79
- }
80
- function getWorkspacePatterns(rootDir) {
81
- const pkg = readJson(path.join(rootDir, 'package.json'));
82
- const workspaces = pkg.workspaces;
83
- if (Array.isArray(workspaces)) {
84
- return workspaces;
85
- }
86
- if (workspaces && Array.isArray(workspaces.packages)) {
87
- return workspaces.packages;
88
- }
89
- return [];
90
- }
91
- function resolveWorkspacePattern(rootDir, pattern) {
92
- if (pattern.endsWith('/*')) {
93
- const base = pattern.slice(0, -2);
94
- const absBase = path.join(rootDir, base);
95
- return readdirSync(absBase, { withFileTypes: true })
96
- .filter((entry) => entry.isDirectory())
97
- .map((entry) => path.join(base, entry.name));
98
- }
99
- return [pattern];
100
- }
101
- function loadWorkspaces(rootDir) {
102
- const patterns = getWorkspacePatterns(rootDir);
103
- const locations = patterns.flatMap((pattern) => resolveWorkspacePattern(rootDir, pattern));
104
- return locations
105
- .map((location) => {
106
- const pkgPath = path.join(rootDir, location, 'package.json');
107
- const pkg = readJson(pkgPath);
108
- return {
109
- ...pkg,
110
- location,
111
- path: path.join(rootDir, location),
112
- };
113
- })
114
- .filter((ws) => Boolean(ws.name));
115
- }
116
- function findConsumerRoot(startDir) {
117
- let current = startDir;
118
- while (true) {
119
- const pkgPath = path.join(current, 'package.json');
120
- try {
121
- const content = readFileSync(pkgPath, 'utf8');
122
- const pkg = JSON.parse(content);
123
- if (pkg.workspaces) {
124
- return current;
125
- }
126
- }
127
- catch {
128
- // File not found or parse error, continue up.
129
- }
130
- const parent = path.dirname(current);
131
- if (parent === current) {
132
- throw new Error('Could not find consumer workspace root (no package.json with workspaces field)');
133
- }
134
- current = parent;
135
- }
136
- }
137
- function findUIKitRoot(startDir) {
138
- let current = startDir;
139
- while (true) {
140
- if (isValidUIKitRoot(current)) {
141
- return current;
142
- }
143
- const parent = path.dirname(current);
144
- if (parent === current) {
145
- return null;
146
- }
147
- current = parent;
148
- }
149
- }
150
- function isValidUIKitRoot(rootDir) {
151
- try {
152
- const pkgPath = path.join(rootDir, 'package.json');
153
- const pkg = readJson(pkgPath);
154
- if (!pkg.workspaces) {
155
- return false;
156
- }
157
- const workspaces = loadWorkspaces(rootDir);
158
- return workspaces.some((ws) => ws.name === '@archon-research/design-system');
159
- }
160
- catch {
161
- return false;
162
- }
163
- }
164
- function findUIKitRootFromConsumer(consumerRoot) {
165
- const candidateNames = ['uikit'];
166
- let current = consumerRoot;
167
- while (true) {
168
- for (const candidateName of candidateNames) {
169
- const candidate = path.join(current, candidateName);
170
- if (isValidUIKitRoot(candidate)) {
171
- return candidate;
172
- }
173
- }
174
- const parent = path.dirname(current);
175
- if (parent === current) {
176
- return null;
177
- }
178
- current = parent;
179
- }
180
- }
181
- function resolveUIKitRoot(explicitUIKitRoot, consumerRoot) {
182
- if (explicitUIKitRoot) {
183
- const resolved = path.resolve(process.cwd(), explicitUIKitRoot);
184
- if (!isValidUIKitRoot(resolved)) {
185
- throw new Error(`Invalid uikit root: ${resolved}. Expected a workspace root containing @archon-research/design-system.`);
186
- }
187
- return resolved;
188
- }
189
- const scriptRelativeRoot = path.resolve(scriptDir, '../../..');
190
- if (isValidUIKitRoot(scriptRelativeRoot)) {
191
- return scriptRelativeRoot;
192
- }
193
- if (consumerRoot) {
194
- const discovered = findUIKitRootFromConsumer(consumerRoot);
195
- if (discovered) {
196
- return discovered;
197
- }
198
- }
199
- const fromCwd = findUIKitRoot(process.cwd());
200
- if (fromCwd) {
201
- return fromCwd;
202
- }
203
- throw new Error('Could not locate local uikit workspace automatically. Run from a consumer workspace near your uikit checkout, or pass --uikit-root / set UIKIT_ROOT.');
204
- }
3
+ import path from 'node:path';
4
+ import { RealFileSystem } from './fs-utils.js';
5
+ import { NpmCommandExecutor } from './command-executor.js';
6
+ import { ConsoleLogger } from './logger.js';
7
+ import { PackageDiscovery } from './package-discovery.js';
8
+ import { LinkValidator } from './link-validator.js';
9
+ import { RegisterCommand } from './commands/register.js';
10
+ import { LinkCommand } from './commands/link.js';
11
+ import { UnlinkCommand } from './commands/unlink.js';
12
+ import { LintCommand } from './commands/lint.js';
13
+ import { FormatCommand } from './commands/format.js';
14
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
15
+ /**
16
+ * Parse command line arguments
17
+ */
205
18
  function parseArgs(argv) {
206
19
  const args = argv.slice(2);
207
- const command = args[0];
208
- let mode;
209
- if (!command || command === 'link') {
210
- mode = 'link';
211
- }
212
- else if (command === 'unlink') {
213
- mode = 'unlink';
214
- }
215
- else if (command === 'register') {
216
- mode = 'register';
217
- }
218
- else if (command === 'lint') {
219
- mode = 'lint';
220
- }
221
- else if (command === 'format') {
222
- mode = 'format';
223
- }
224
- else {
225
- throw new Error(`Unknown command: ${command}. Expected one of: link, unlink, register, lint, format.`);
226
- }
227
- const commandArgs = args.slice(1);
228
- let consumerRoot = null;
229
- let uikitRoot = process.env.UIKIT_ROOT ?? null;
230
- for (let i = 1; i < args.length; i += 1) {
20
+ if (args.length === 0) {
21
+ throw new Error('Usage: uikit-cli <register|link|unlink|lint|format> [--verify] [--debug] [--uikit-root <path>] [args...]');
22
+ }
23
+ const mode = args[0];
24
+ const validModes = ['lint', 'format', 'register', 'link', 'unlink'];
25
+ if (!validModes.includes(mode)) {
26
+ throw new Error(`Unknown command: ${mode}`);
27
+ }
28
+ let uikitRoot;
29
+ let verify = false;
30
+ let debug = false;
31
+ const commandArgs = [];
32
+ let i = 1;
33
+ while (i < args.length) {
231
34
  const arg = args[i];
232
- if (arg === '--consumer-root' && args[i + 1]) {
233
- consumerRoot = path.resolve(process.cwd(), args[i + 1]);
35
+ if (arg === '--uikit-root' && i + 1 < args.length) {
36
+ uikitRoot = args[i + 1];
37
+ i += 2;
38
+ }
39
+ else if (arg === '--verify') {
40
+ verify = true;
234
41
  i += 1;
235
- continue;
236
42
  }
237
- if (arg === '--uikit-root' && args[i + 1]) {
238
- uikitRoot = args[i + 1];
43
+ else if (arg === '--debug') {
44
+ debug = true;
239
45
  i += 1;
240
46
  }
241
- }
242
- if (mode !== 'register' &&
243
- mode !== 'lint' &&
244
- mode !== 'format' &&
245
- !consumerRoot) {
246
- consumerRoot = findConsumerRoot(process.cwd());
247
- }
248
- if (mode === 'lint' || mode === 'format') {
249
- return {
250
- mode,
251
- consumerRoot: null,
252
- uikitRoot: '',
253
- commandArgs,
254
- };
255
- }
256
- return {
257
- mode,
258
- consumerRoot,
259
- uikitRoot: resolveUIKitRoot(uikitRoot, consumerRoot),
260
- commandArgs,
261
- };
262
- }
263
- function runLint(commandArgs) {
264
- const forwarded = commandArgs.map(shellEscape).join(' ');
265
- run(`npm exec --yes --package oxlint@${OXLINT_VERSION} -- oxlint ${forwarded}`.trim(), process.cwd());
266
- }
267
- function hasConfigFlag(args) {
268
- for (let i = 0; i < args.length; i += 1) {
269
- const arg = args[i];
270
- if (arg === '-c' || arg === '--config' || arg.startsWith('--config=')) {
271
- return true;
47
+ else {
48
+ commandArgs.push(arg);
49
+ i += 1;
272
50
  }
273
51
  }
274
- return false;
275
- }
276
- function runFormat(commandArgs) {
277
- const args = [...commandArgs];
278
- const defaultConfig = './.oxfmtrc.ts';
279
- if (!hasConfigFlag(args) && existsSync(path.join(process.cwd(), '.oxfmtrc.ts'))) {
280
- args.unshift(defaultConfig);
281
- args.unshift('-c');
52
+ // Check for UIKIT_DEBUG environment variable
53
+ if (process.env.UIKIT_DEBUG === '1') {
54
+ debug = true;
282
55
  }
283
- const forwarded = args.map(shellEscape).join(' ');
284
- run(`npm exec --yes --package oxfmt@${OXFMT_VERSION} -- oxfmt ${forwarded}`.trim(), process.cwd());
285
- }
286
- function registerLocalPackages(uikitRoot, supportedNames) {
287
- const uikitWorkspaces = loadWorkspaces(uikitRoot);
288
- const uikitPackages = uikitWorkspaces.filter((ws) => String(ws.name ?? '').startsWith('@archon-research/') &&
289
- !ws.private &&
290
- (!supportedNames || supportedNames.has(String(ws.name))));
291
- if (uikitPackages.length === 0) {
292
- console.log('No public @archon-research packages found in this uikit workspace.');
293
- return;
56
+ // Resolve uikit root
57
+ const fs = new RealFileSystem();
58
+ const discovery = new PackageDiscovery(fs);
59
+ if (debug) {
60
+ console.log('[DEBUG parseArgs] Resolving uikit root...');
294
61
  }
295
- for (const pkg of uikitPackages) {
296
- if (!pkg.name) {
297
- continue;
62
+ if (!uikitRoot) {
63
+ // Try relative to script location
64
+ const relativeRoot = path.resolve(scriptDir, '../../../..');
65
+ if (debug) {
66
+ console.log('[DEBUG parseArgs] Checking relative root:', relativeRoot);
298
67
  }
299
- // Ensure CLI bin target exists before linking globally.
300
- if (pkg.name === '@archon-research/uikit-cli' &&
301
- !existsSync(path.join(pkg.path, 'dist', 'cli.js'))) {
302
- run('npm run build', pkg.path);
303
- }
304
- run('npm link', pkg.path);
305
- }
306
- console.log('\nRegistered local uikit packages for downstream consumers.');
307
- }
308
- function linkCliIntoConsumer(consumerRoot) {
309
- run('npm link "@archon-research/uikit-cli" --package-lock=false --save=false', consumerRoot);
310
- }
311
- function collectWorkspaceRequirements(workspaces, supportedNames) {
312
- const neededByWorkspace = new Map();
313
- for (const ws of workspaces) {
314
- const fields = [
315
- ws.dependencies ?? {},
316
- ws.devDependencies ?? {},
317
- ws.optionalDependencies ?? {},
318
- ws.peerDependencies ?? {},
319
- ];
320
- const needed = new Set();
321
- for (const depField of fields) {
322
- for (const depName of Object.keys(depField)) {
323
- if (supportedNames.has(depName)) {
324
- needed.add(depName);
325
- }
68
+ if (discovery.isValidUIKitRoot(relativeRoot)) {
69
+ uikitRoot = relativeRoot;
70
+ if (debug) {
71
+ console.log('[DEBUG parseArgs] Found valid uikit root at relative location');
326
72
  }
327
73
  }
328
- if (needed.size > 0) {
329
- neededByWorkspace.set(ws.location, [...needed]);
330
- }
331
- }
332
- return neededByWorkspace;
333
- }
334
- function linkLocalPackages(consumerRoot, neededByWorkspace, dirByName) {
335
- if (neededByWorkspace.size === 0) {
336
- console.log('No local uikit packages referenced by this consumer workspaces.');
337
- return;
338
74
  }
339
- const allNames = new Set();
340
- for (const names of neededByWorkspace.values()) {
341
- for (const name of names) {
342
- allNames.add(name);
343
- }
344
- }
345
- const rootPackageArgs = [...allNames].map((name) => `"${name}"`).join(' ');
346
- if (rootPackageArgs) {
347
- run(`npm link ${rootPackageArgs} --package-lock=false --save=false`, consumerRoot);
348
- }
349
- // npm can materialize regular installs in some workspace setups; enforce root links.
350
- for (const name of allNames) {
351
- const target = dirByName.get(name);
352
- if (!target) {
353
- continue;
354
- }
355
- ensureLinkedPath(consumerRoot, name, target);
356
- }
357
- for (const [workspace, names] of neededByWorkspace.entries()) {
358
- const packageArgs = names.map((name) => `"${name}"`).join(' ');
359
- if (!packageArgs) {
360
- continue;
361
- }
362
- run(`npm link ${packageArgs} --workspace "${workspace}" --package-lock=false --save=false`, consumerRoot);
363
- // npm --workspace link can leave a regular install in some workspace layouts.
364
- // Removing workspace shadow installs keeps resolution aligned to linked root packages.
365
- for (const name of names) {
366
- removeWorkspaceShadowInstall(consumerRoot, workspace, name);
367
- }
368
- clearWorkspaceViteCache(consumerRoot, workspace);
369
- }
370
- }
371
- function unlinkLocalPackages(consumerRoot, neededByWorkspace) {
372
- if (neededByWorkspace.size === 0) {
373
- console.log('No local uikit packages referenced by this consumer workspaces.');
374
- return;
375
- }
376
- const allNames = new Set();
377
- for (const names of neededByWorkspace.values()) {
378
- for (const name of names) {
379
- allNames.add(name);
380
- }
381
- }
382
- for (const name of allNames) {
383
- const ok = tryRun(`npm unlink "${name}" --package-lock=false --save=false`, consumerRoot);
384
- if (!ok) {
385
- console.warn(`Unable to unlink ${name} at root; continuing.`);
386
- }
387
- }
388
- for (const [workspace, names] of neededByWorkspace.entries()) {
389
- const workspaceDir = path.join(consumerRoot, workspace);
390
- for (const name of names) {
391
- const ok = tryRun(`npm unlink "${name}" --workspace "${workspace}" --package-lock=false --save=false`, consumerRoot);
392
- if (!ok) {
393
- console.warn(`Unable to unlink ${name} in ${workspace}; continuing with restore flow.`);
394
- }
395
- // Fallback for cases where --workspace unlink does not clear workspace-local links.
396
- const fallbackOk = tryRun(`npm unlink "${name}" --package-lock=false --save=false`, workspaceDir, true);
397
- if (!fallbackOk) {
398
- console.warn(`Unable to unlink ${name} directly in ${workspace}; continuing with restore flow.`);
75
+ if (!uikitRoot && mode !== 'lint' && mode !== 'format') {
76
+ // Try to find from consumer
77
+ try {
78
+ const tempConsumerRoot = discovery.findConsumerRoot(process.cwd());
79
+ const foundRoot = discovery.findUIKitRootFromConsumer(tempConsumerRoot);
80
+ if (foundRoot) {
81
+ uikitRoot = foundRoot;
399
82
  }
400
83
  }
401
- clearWorkspaceViteCache(consumerRoot, workspace);
402
- }
403
- }
404
- function areRegistryPackagesReady(consumerRoot, neededByWorkspace) {
405
- for (const [workspace, names] of neededByWorkspace.entries()) {
406
- for (const name of names) {
407
- const ok = tryRun(`npm_config_min_release_age=0 npm ls ${name} --depth=0 --workspace "${workspace}"`, consumerRoot, true);
408
- if (!ok) {
409
- return false;
410
- }
84
+ catch {
85
+ // Will throw error below if needed
411
86
  }
412
87
  }
413
- return true;
414
- }
415
- function arePackagesPublished(consumerRoot, neededByWorkspace) {
416
- const uniqueNames = new Set();
417
- for (const names of neededByWorkspace.values()) {
418
- for (const name of names) {
419
- uniqueNames.add(name);
88
+ if (!uikitRoot && mode !== 'lint' && mode !== 'format') {
89
+ // Try walking up from cwd
90
+ const foundRoot = discovery.findUIKitRoot(process.cwd());
91
+ if (foundRoot) {
92
+ uikitRoot = foundRoot;
420
93
  }
421
94
  }
422
- for (const name of uniqueNames) {
423
- const ok = tryRun(`npm view ${name} version --json`, consumerRoot, true);
424
- if (!ok) {
425
- return false;
426
- }
95
+ if (!uikitRoot && mode !== 'lint' && mode !== 'format') {
96
+ throw new Error('Could not find uikit root directory.\n' +
97
+ 'Tried:\n' +
98
+ ' - Relative to script location\n' +
99
+ ' - Sibling to consumer root\n' +
100
+ ' - Walking up from cwd\n' +
101
+ 'Use --uikit-root to specify manually.');
102
+ }
103
+ let consumerRoot = null;
104
+ if (mode === 'link' || mode === 'unlink') {
105
+ consumerRoot = discovery.findConsumerRoot(process.cwd());
427
106
  }
428
- return true;
107
+ return {
108
+ mode,
109
+ consumerRoot,
110
+ uikitRoot: uikitRoot ?? '',
111
+ commandArgs,
112
+ verify,
113
+ debug,
114
+ };
429
115
  }
430
- const scriptDir = path.dirname(fileURLToPath(import.meta.url));
116
+ /**
117
+ * Ensure CLI binary is built before registering
118
+ */
119
+ function ensureCliBinaryBuilt(uikitRoot, executor) {
120
+ const cliPackagePath = path.join(uikitRoot, 'packages/uikit-cli');
121
+ const distPath = path.join(cliPackagePath, 'dist/cli.js');
122
+ const fs = new RealFileSystem();
123
+ if (!fs.exists(distPath)) {
124
+ console.log('Building CLI binary...');
125
+ executor.exec('npm run build', { cwd: cliPackagePath });
126
+ }
127
+ }
128
+ /**
129
+ * Link CLI into consumer for convenience
130
+ */
131
+ function linkCliIntoConsumer(consumerRoot, executor) {
132
+ executor.exec('npm link "@archon-research/uikit-cli" --package-lock=false --save=false --no-workspaces', { cwd: consumerRoot });
133
+ }
134
+ /**
135
+ * Main entry point
136
+ */
431
137
  try {
432
- const { mode, consumerRoot, uikitRoot, commandArgs } = parseArgs(process.argv);
138
+ const parsed = parseArgs(process.argv);
139
+ const { mode, consumerRoot, uikitRoot, commandArgs, verify, debug } = parsed;
140
+ if (debug) {
141
+ console.log('[DEBUG] Parsed args:', { mode, consumerRoot, uikitRoot, verify });
142
+ }
143
+ // Initialize dependencies
144
+ const fs = new RealFileSystem();
145
+ const executor = new NpmCommandExecutor(debug);
146
+ const logger = new ConsoleLogger(debug);
147
+ const discovery = new PackageDiscovery(fs);
148
+ const validator = new LinkValidator(fs, logger);
149
+ // Handle lint/format commands
433
150
  if (mode === 'lint') {
434
- runLint(commandArgs);
151
+ const lintCmd = new LintCommand(executor);
152
+ lintCmd.execute(commandArgs);
435
153
  process.exit(0);
436
154
  }
437
155
  if (mode === 'format') {
438
- runFormat(commandArgs);
156
+ const formatCmd = new FormatCommand(executor, fs);
157
+ formatCmd.execute(commandArgs);
439
158
  process.exit(0);
440
159
  }
160
+ // Handle register command
441
161
  if (mode === 'register') {
442
- registerLocalPackages(uikitRoot);
162
+ ensureCliBinaryBuilt(uikitRoot, executor);
163
+ const registerCmd = new RegisterCommand(discovery, executor, logger);
164
+ registerCmd.execute(uikitRoot);
165
+ if (verify) {
166
+ logger.info('\\nVerifying registration...');
167
+ // Could add verification logic here
168
+ logger.info('✓ Registration verified');
169
+ }
443
170
  process.exit(0);
444
171
  }
172
+ // Handle link/unlink commands (require consumerRoot)
445
173
  if (!consumerRoot) {
446
174
  throw new Error('Consumer root is required for link/unlink operations.');
447
175
  }
448
- const uikitWorkspaces = loadWorkspaces(uikitRoot);
449
- const consumerWorkspaces = loadWorkspaces(consumerRoot);
450
- const uikitPackages = uikitWorkspaces.filter((ws) => String(ws.name ?? '').startsWith('@archon-research/'));
451
- const dirByName = new Map(uikitPackages.map((pkg) => [pkg.name ?? '', pkg.path]));
452
- dirByName.delete('');
453
- const supportedNames = new Set(dirByName.keys());
454
- const neededByWorkspace = collectWorkspaceRequirements(consumerWorkspaces, supportedNames);
455
- registerLocalPackages(uikitRoot, supportedNames);
456
- linkCliIntoConsumer(consumerRoot);
176
+ // Register packages and link CLI before link/unlink
177
+ ensureCliBinaryBuilt(uikitRoot, executor);
178
+ const registerCmd = new RegisterCommand(discovery, executor, logger);
179
+ registerCmd.execute(uikitRoot);
180
+ linkCliIntoConsumer(consumerRoot, executor);
457
181
  if (mode === 'link') {
458
- linkLocalPackages(consumerRoot, neededByWorkspace, dirByName);
459
- console.log('\nLinked local uikit packages into consumer workspaces.');
182
+ const linkCmd = new LinkCommand(discovery, executor, validator, fs, logger);
183
+ linkCmd.execute(consumerRoot, uikitRoot, verify);
460
184
  process.exit(0);
461
185
  }
462
- if (!arePackagesPublished(consumerRoot, neededByWorkspace)) {
463
- console.warn('\nRegistry packages are not published yet; keeping local uikit links in place.');
464
- linkLocalPackages(consumerRoot, neededByWorkspace, dirByName);
465
- console.log('\nConsumer remains on local uikit links.');
186
+ if (mode === 'unlink') {
187
+ const unlinkCmd = new UnlinkCommand(executor, logger);
188
+ unlinkCmd.execute(consumerRoot, uikitRoot, discovery);
466
189
  process.exit(0);
467
190
  }
468
- unlinkLocalPackages(consumerRoot, neededByWorkspace);
469
- let rootInstallOk = tryRun('npm_config_min_release_age=0 npm install', consumerRoot);
470
- let installOk = true;
471
- for (const workspace of neededByWorkspace.keys()) {
472
- installOk =
473
- tryRun(`npm_config_min_release_age=0 npm install --workspace "${workspace}"`, consumerRoot) &&
474
- installOk;
475
- }
476
- if (!rootInstallOk ||
477
- !installOk ||
478
- !areRegistryPackagesReady(consumerRoot, neededByWorkspace)) {
479
- console.warn('\nRegistry packages are not fully resolvable; falling back to local uikit linking.');
480
- linkLocalPackages(consumerRoot, neededByWorkspace, dirByName);
481
- }
482
- // Keep consumer on the local CLI implementation so repeated link/unlink stays stable.
483
- linkCliIntoConsumer(consumerRoot);
484
- console.log('\nUnlinked local uikit packages and restored consumer dependencies.');
485
191
  }
486
192
  catch (error) {
487
- const message = error instanceof Error ? error.message : String(error);
488
- console.error(`Error: ${message}`);
193
+ if (process.env.UIKIT_DEBUG) {
194
+ console.error('Full error:', error);
195
+ }
196
+ console.error(error instanceof Error ? error.message : String(error));
489
197
  process.exit(1);
490
198
  }
package/dist/cli.sh ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env sh
2
+ # Use --preserve-symlinks to fix ES module resolution with npm link
3
+ # Follow symlink to find the actual script location
4
+ if [ -L "$0" ]; then
5
+ SCRIPT="$(readlink -f "$0" 2>/dev/null || readlink "$0")"
6
+ else
7
+ SCRIPT="$0"
8
+ fi
9
+ SCRIPT_DIR="$(cd "$(dirname "$SCRIPT")" && pwd)"
10
+ exec node --preserve-symlinks "$SCRIPT_DIR/cli.js" "$@"