ui8kit 1.1.0 → 1.3.5

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/index.js CHANGED
@@ -2,15 +2,14 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import chalk7 from "chalk";
5
+ import chalk12 from "chalk";
6
6
 
7
7
  // src/commands/add.ts
8
- import chalk3 from "chalk";
9
- import ora from "ora";
10
- import path3 from "path";
11
- import fs4 from "fs-extra";
12
- import { execa } from "execa";
8
+ import ora4 from "ora";
9
+ import path8 from "path";
10
+ import fs7 from "fs-extra";
13
11
  import fetch3 from "node-fetch";
12
+ import prompts3 from "prompts";
14
13
 
15
14
  // src/registry/api.ts
16
15
  import fetch from "node-fetch";
@@ -87,6 +86,7 @@ var SCHEMA_CONFIG = {
87
86
  schema: "JSON Schema URL",
88
87
  framework: "Target framework",
89
88
  typescript: "Whether the project uses TypeScript",
89
+ globalCss: "Path to global styles entry file",
90
90
  aliases: "Path aliases for imports in UI8Kit structure",
91
91
  registry: "Default component registry",
92
92
  componentsDir: "Directory where utility components will be installed",
@@ -153,285 +153,294 @@ var configSchema = z.object({
153
153
  $schema: z.string().optional(),
154
154
  framework: z.literal(SCHEMA_CONFIG.supportedFrameworks[0]),
155
155
  typescript: z.boolean().default(true),
156
+ globalCss: z.string().default("src/index.css"),
156
157
  aliases: z.record(z.string(), z.string()).default(SCHEMA_CONFIG.defaultAliases),
157
158
  registry: z.string().default(SCHEMA_CONFIG.defaultRegistry),
158
159
  componentsDir: z.string().default(SCHEMA_CONFIG.defaultDirectories.components),
159
160
  libDir: z.string().default(SCHEMA_CONFIG.defaultDirectories.lib)
160
161
  });
161
162
 
162
- // src/registry/api.ts
163
- var registryCache = /* @__PURE__ */ new Map();
164
- function isUrl(path7) {
165
- try {
166
- new URL(path7);
167
- return true;
168
- } catch {
169
- return false;
170
- }
171
- }
172
- function getRegistryCache(registryType) {
173
- if (!registryCache.has(registryType)) {
174
- registryCache.set(registryType, {
175
- workingCDN: null,
176
- registryIndex: null
177
- });
163
+ // src/utils/logger.ts
164
+ import chalk from "chalk";
165
+ import ora from "ora";
166
+ var verboseEnabled = false;
167
+ function output(level, message, ...args) {
168
+ const prefix = (() => {
169
+ switch (level) {
170
+ case "info":
171
+ return chalk.blue("\u2139");
172
+ case "success":
173
+ return chalk.green("\u2705");
174
+ case "warn":
175
+ return chalk.yellow("\u26A0\uFE0F");
176
+ case "error":
177
+ return chalk.red("\u274C");
178
+ case "debug":
179
+ return chalk.gray("\u{1F41E}");
180
+ default:
181
+ return "";
182
+ }
183
+ })();
184
+ if (level === "debug" && !verboseEnabled) {
185
+ return;
178
186
  }
179
- return registryCache.get(registryType);
187
+ console.log(`${prefix} ${message}`, ...args);
180
188
  }
181
- async function findWorkingCDN(registryType) {
182
- const cache = getRegistryCache(registryType);
183
- if (cache.workingCDN) {
184
- return cache.workingCDN;
189
+ var logger = {
190
+ setVerbose(enabled) {
191
+ verboseEnabled = enabled;
192
+ },
193
+ info(message, ...args) {
194
+ output("info", message, ...args);
195
+ },
196
+ success(message, ...args) {
197
+ output("success", message, ...args);
198
+ },
199
+ warn(message, ...args) {
200
+ output("warn", message, ...args);
201
+ },
202
+ error(message, ...args) {
203
+ output("error", message, ...args);
204
+ },
205
+ debug(message, ...args) {
206
+ output("debug", message, ...args);
207
+ },
208
+ spinner(text) {
209
+ return ora(text).start();
185
210
  }
186
- const cdnUrls = getCdnUrls(registryType);
187
- for (const baseUrl of cdnUrls) {
188
- try {
189
- console.log(`\u{1F50D} Testing ${registryType} CDN: ${baseUrl}`);
190
- const response = await fetch(`${baseUrl}/index.json`);
191
- if (response.ok) {
192
- cache.workingCDN = baseUrl;
193
- console.log(`\u2705 Using ${registryType} CDN: ${baseUrl}`);
194
- return baseUrl;
195
- }
196
- } catch (error) {
197
- console.log(`\u274C ${registryType} CDN failed: ${baseUrl}`);
198
- }
211
+ };
212
+
213
+ // src/utils/cache.ts
214
+ import fs from "fs-extra";
215
+ import os from "os";
216
+ import path from "path";
217
+ var DEFAULT_TTL_MS = 36e5;
218
+ function normalizeKey(key) {
219
+ return key.trim().replace(/^\/+/, "").replace(/\\/g, "/");
220
+ }
221
+ function cacheFilePath(key) {
222
+ const cacheDir = getCacheDir();
223
+ const normalized = normalizeKey(key);
224
+ const normalizedWithJson = normalized.endsWith(".json") ? normalized : `${normalized}.json`;
225
+ const dataPath = path.join(cacheDir, normalizedWithJson);
226
+ return { dataPath, metaPath: `${dataPath}.meta.json` };
227
+ }
228
+ function getCacheDir() {
229
+ return path.join(os.homedir(), ".ui8kit", "cache");
230
+ }
231
+ function parseTimestamp(timestamp) {
232
+ if (typeof timestamp !== "number") {
233
+ return null;
199
234
  }
200
- throw new Error(`No working ${registryType} CDN found`);
235
+ return Number.isFinite(timestamp) ? timestamp : null;
201
236
  }
202
- async function fetchFromWorkingCDN(path7, registryType) {
203
- const baseUrl = await findWorkingCDN(registryType);
204
- const url = `${baseUrl}/${path7}`;
205
- console.log(`\u{1F3AF} Fetching from ${registryType}: ${url}`);
206
- const response = await fetch(url);
207
- if (!response.ok) {
208
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
237
+ async function getCachedJson(key, options = {}) {
238
+ const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
239
+ if (options.noCache) {
240
+ return null;
209
241
  }
210
- return await response.json();
211
- }
212
- async function getRegistryIndex(registryType) {
213
- const cache = getRegistryCache(registryType);
214
- if (cache.registryIndex) {
215
- return cache.registryIndex;
242
+ const { dataPath, metaPath } = cacheFilePath(key);
243
+ if (!await fs.pathExists(dataPath) || !await fs.pathExists(metaPath)) {
244
+ return null;
216
245
  }
217
- console.log(`\u{1F310} Fetching ${registryType} registry index`);
218
- cache.registryIndex = await fetchFromWorkingCDN("index.json", registryType);
219
- return cache.registryIndex;
220
- }
221
- async function getComponentByType(name, registryType) {
222
246
  try {
223
- const index = await getRegistryIndex(registryType);
224
- const componentInfo = index.components?.find((c) => c.name === name);
225
- if (!componentInfo) {
226
- console.log(`\u274C Component ${name} not found in ${registryType} registry`);
247
+ const meta = await fs.readJson(metaPath);
248
+ const lastFetched = parseTimestamp(meta?.lastFetched);
249
+ const metaTtl = parseTimestamp(meta?.ttl) ?? ttlMs;
250
+ if (!lastFetched) {
227
251
  return null;
228
252
  }
229
- const folder = componentInfo.type === "registry:variants" ? "components/variants" : TYPE_TO_FOLDER[componentInfo.type];
230
- if (!folder) {
231
- console.log(`\u274C Unknown component type: ${componentInfo.type}`);
253
+ const now = Date.now();
254
+ if (now - lastFetched > metaTtl) {
232
255
  return null;
233
256
  }
234
- console.log(`\u{1F3AF} Loading ${name} from /${folder}/ (type: ${componentInfo.type})`);
235
- const data = await fetchFromWorkingCDN(`${folder}/${name}.json`, registryType);
236
- return componentSchema.parse(data);
237
- } catch (error) {
238
- console.log(`\u274C Failed to get component by type: ${error.message}`);
239
- return null;
240
- }
241
- }
242
- async function getComponent(name, registryType = SCHEMA_CONFIG.defaultRegistryType) {
243
- try {
244
- if (isUrl(name)) {
245
- return await fetchFromUrl(name);
246
- }
247
- return await getComponentByType(name, registryType);
248
- } catch (error) {
249
- console.error(`\u274C Failed to fetch ${name} from ${registryType}:`, error.message);
257
+ return await fs.readJson(dataPath);
258
+ } catch {
250
259
  return null;
251
260
  }
252
261
  }
253
- async function fetchFromUrl(url) {
254
- console.log(`\u{1F310} Fetching component from: ${url}`);
255
- const response = await fetch(url);
256
- if (!response.ok) {
257
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
258
- }
259
- const data = await response.json();
260
- return componentSchema.parse(data);
262
+ async function setCachedJson(key, data, options = {}) {
263
+ const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
264
+ const { dataPath, metaPath } = cacheFilePath(key);
265
+ await fs.ensureDir(path.dirname(dataPath));
266
+ await fs.writeJson(dataPath, data, { spaces: 2 });
267
+ await fs.writeJson(metaPath, {
268
+ lastFetched: Date.now(),
269
+ ttl: ttlMs
270
+ });
261
271
  }
262
- async function getAllComponents(registryType = SCHEMA_CONFIG.defaultRegistryType) {
263
- try {
264
- console.log(`\u{1F310} Fetching all ${registryType} components using optimized approach`);
265
- const indexData = await getRegistryIndex(registryType);
266
- const components = [];
267
- if (indexData.components && Array.isArray(indexData.components)) {
268
- for (const componentInfo of indexData.components) {
269
- const component = await getComponent(componentInfo.name, registryType);
270
- if (component) {
271
- components.push(component);
272
- }
273
- }
274
- }
275
- return components;
276
- } catch (error) {
277
- console.error(`\u274C Failed to fetch all ${registryType} components:`, error.message);
278
- return [];
272
+ async function clearCache() {
273
+ const cacheDir = getCacheDir();
274
+ if (await fs.pathExists(cacheDir)) {
275
+ await fs.remove(cacheDir);
279
276
  }
280
277
  }
281
278
 
282
- // src/registry/retry-api.ts
283
- import fetch2 from "node-fetch";
284
- var MAX_RETRIES = 3;
285
- var RETRY_DELAY = 2e3;
286
- var registryCache2 = /* @__PURE__ */ new Map();
287
- function isUrl2(path7) {
279
+ // src/registry/api.ts
280
+ var REGISTRY_INDEX_CACHE_TTL_MS = 36e5;
281
+ var registryCache = /* @__PURE__ */ new Map();
282
+ var DEFAULT_TIMEOUT_MS = 1e4;
283
+ var DEFAULT_MAX_RETRIES = 1;
284
+ var RETRY_DELAY_MS = 1200;
285
+ function isUrl(path13) {
288
286
  try {
289
- new URL(path7);
287
+ new URL(path13);
290
288
  return true;
291
289
  } catch {
292
290
  return false;
293
291
  }
294
292
  }
295
- function getRegistryCache2(registryType) {
296
- if (!registryCache2.has(registryType)) {
297
- registryCache2.set(registryType, {
293
+ function getRegistryCache(registryType) {
294
+ if (!registryCache.has(registryType)) {
295
+ registryCache.set(registryType, {
298
296
  workingCDN: null,
299
297
  registryIndex: null
300
298
  });
301
299
  }
302
- return registryCache2.get(registryType);
300
+ return registryCache.get(registryType);
303
301
  }
304
- async function checkInternetConnection() {
302
+ async function fetchJsonWithTimeout(url, timeoutMs) {
303
+ const controller = new AbortController();
304
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
305
305
  try {
306
- console.log("\u{1F310} Checking internet connection...");
307
- const controller = new AbortController();
308
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
309
- const response = await fetch2("https://www.google.com", {
310
- method: "HEAD",
311
- signal: controller.signal
312
- });
306
+ const response = await fetch(url, { signal: controller.signal });
307
+ if (!response.ok) {
308
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
309
+ }
310
+ return await response.json();
311
+ } finally {
313
312
  clearTimeout(timeoutId);
314
- return response.ok;
315
- } catch (error) {
316
- console.log("\u274C No internet connection detected");
317
- return false;
318
313
  }
319
314
  }
320
- async function fetchWithRetry(url, retries = MAX_RETRIES) {
321
- for (let attempt = 1; attempt <= retries; attempt++) {
315
+ async function fetchJsonWithRetry(url, maxRetries, timeoutMs) {
316
+ let attempt = 0;
317
+ let lastError;
318
+ while (attempt < maxRetries) {
319
+ attempt += 1;
322
320
  try {
323
- console.log(`\u{1F504} Attempt ${attempt}/${retries}: ${url}`);
324
- const controller = new AbortController();
325
- const timeoutId = setTimeout(() => controller.abort(), 1e4);
326
- const response = await fetch2(url, {
327
- signal: controller.signal
328
- });
329
- clearTimeout(timeoutId);
330
- if (response.ok) {
331
- return await response.json();
332
- } else if (response.status === 404) {
333
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
334
- } else {
335
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
336
- }
321
+ return await fetchJsonWithTimeout(url, timeoutMs);
337
322
  } catch (error) {
338
- console.log(`\u274C Attempt ${attempt} failed: ${error.message}`);
339
- if (attempt === retries) {
340
- throw error;
323
+ lastError = error;
324
+ if (attempt >= maxRetries) {
325
+ break;
341
326
  }
342
- console.log(`\u23F3 Waiting ${RETRY_DELAY / 1e3}s before retry...`);
343
- await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY));
327
+ await new Promise((resolve3) => setTimeout(resolve3, RETRY_DELAY_MS));
344
328
  }
345
329
  }
330
+ throw lastError instanceof Error ? lastError : new Error("Request failed");
346
331
  }
347
- async function findWorkingCDNWithRetry(registryType) {
348
- const cache = getRegistryCache2(registryType);
349
- if (cache.workingCDN) {
350
- return cache.workingCDN;
351
- }
352
- if (!await checkInternetConnection()) {
353
- throw new Error("No internet connection available");
354
- }
332
+ async function fetchFromRegistryPath(requestPath, registryType, options = {}) {
333
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
334
+ const maxRetries = Math.max(1, options.maxRetries ?? DEFAULT_MAX_RETRIES);
335
+ const cache = getRegistryCache(registryType);
355
336
  const cdnUrls = getCdnUrls(registryType);
356
- for (const baseUrl of cdnUrls) {
337
+ const orderedUrls = cache.workingCDN ? [cache.workingCDN, ...cdnUrls.filter((url) => url !== cache.workingCDN)] : [...cdnUrls];
338
+ let lastError;
339
+ for (const baseUrl of orderedUrls) {
357
340
  try {
358
- console.log(`\u{1F50D} Testing ${registryType} CDN with retry: ${baseUrl}`);
359
- await fetchWithRetry(`${baseUrl}/index.json`, 2);
341
+ const data = await fetchJsonWithRetry(`${baseUrl}/${requestPath}`, maxRetries, timeoutMs);
342
+ if (cache.workingCDN !== baseUrl) {
343
+ cache.registryIndex = null;
344
+ }
360
345
  cache.workingCDN = baseUrl;
361
- console.log(`\u2705 Using ${registryType} CDN: ${baseUrl}`);
362
- return baseUrl;
346
+ return data;
363
347
  } catch (error) {
364
- console.log(`\u274C ${registryType} CDN failed: ${baseUrl}`);
348
+ lastError = error;
349
+ if (cache.workingCDN === baseUrl) {
350
+ cache.workingCDN = null;
351
+ }
365
352
  }
366
353
  }
367
- throw new Error(`No working ${registryType} CDN found`);
354
+ throw lastError instanceof Error ? lastError : new Error(`No working ${registryType} CDN found`);
368
355
  }
369
- async function fetchFromWorkingCDNWithRetry(path7, registryType) {
370
- const baseUrl = await findWorkingCDNWithRetry(registryType);
371
- const url = `${baseUrl}/${path7}`;
372
- console.log(`\u{1F3AF} Fetching from ${registryType} with retry: ${url}`);
373
- return await fetchWithRetry(url);
374
- }
375
- async function getRegistryIndexWithRetry(registryType) {
376
- const cache = getRegistryCache2(registryType);
377
- if (cache.registryIndex) {
378
- return cache.registryIndex;
356
+ async function getRegistryIndex(registryType, options = {}) {
357
+ if (cacheStateHasIndex(registryType, options)) {
358
+ return getCachedInMemory(registryType);
359
+ }
360
+ const cached = await getCachedJson(
361
+ `${registryType}/index.json`,
362
+ { noCache: options.noCache, ttlMs: REGISTRY_INDEX_CACHE_TTL_MS }
363
+ );
364
+ if (cached) {
365
+ setCachedInMemory(registryType, cached);
366
+ return cached;
379
367
  }
380
- console.log(`\u{1F310} Fetching ${registryType} registry index with retry`);
381
- cache.registryIndex = await fetchFromWorkingCDNWithRetry("index.json", registryType);
368
+ const cache = getRegistryCache(registryType);
369
+ cache.registryIndex = await fetchFromRegistryPath("index.json", registryType, options);
370
+ await setCachedJson(
371
+ `${registryType}/index.json`,
372
+ cache.registryIndex,
373
+ { ttlMs: REGISTRY_INDEX_CACHE_TTL_MS }
374
+ );
382
375
  return cache.registryIndex;
383
376
  }
384
- async function getComponentByTypeWithRetry(name, registryType) {
377
+ async function getComponentByType(name, registryType, options = {}) {
378
+ const normalizedName = name.toLowerCase();
379
+ const cachedComponent = await getCachedJson(
380
+ `${registryType}/components/${normalizedName}.json`,
381
+ { noCache: options.noCache, ttlMs: REGISTRY_INDEX_CACHE_TTL_MS }
382
+ );
383
+ if (cachedComponent) {
384
+ try {
385
+ return componentSchema.parse(cachedComponent);
386
+ } catch (error) {
387
+ logger.debug(`Invalid cached component for ${name}, refetching`);
388
+ }
389
+ }
385
390
  try {
386
- const index = await getRegistryIndexWithRetry(registryType);
387
- const componentInfo = index.components?.find((c) => c.name === name);
391
+ const index = await getRegistryIndex(registryType, options);
392
+ const excludeTypes = options.excludeTypes ?? [];
393
+ const componentInfo = index.components?.find(
394
+ (c) => typeof c?.name === "string" && c.name.toLowerCase() === normalizedName && !excludeTypes.includes(c.type)
395
+ );
388
396
  if (!componentInfo) {
389
- console.log(`\u274C Component ${name} not found in ${registryType} registry`);
397
+ logger.debug(`Component ${name} not found in ${registryType} registry`);
390
398
  return null;
391
399
  }
392
- const folder = TYPE_TO_FOLDER[componentInfo.type];
400
+ const folder = componentInfo.type === "registry:variants" ? "components/variants" : TYPE_TO_FOLDER[componentInfo.type];
393
401
  if (!folder) {
394
- console.log(`\u274C Unknown component type: ${componentInfo.type}`);
402
+ logger.debug(`Unknown component type: ${componentInfo.type}`);
395
403
  return null;
396
404
  }
397
- console.log(`\u{1F3AF} Loading ${name} from /${registryType}/${folder}/ (type: ${componentInfo.type})`);
398
- const data = await fetchFromWorkingCDNWithRetry(`${folder}/${name}.json`, registryType);
405
+ logger.debug(`Loading ${name} from /${folder}/ (type: ${componentInfo.type})`);
406
+ const data = await fetchFromRegistryPath(`${folder}/${name}.json`, registryType, options);
407
+ await setCachedJson(`${registryType}/components/${normalizedName}.json`, data, { ttlMs: REGISTRY_INDEX_CACHE_TTL_MS });
399
408
  return componentSchema.parse(data);
400
409
  } catch (error) {
401
- console.log(`\u274C Failed to get component by type: ${error.message}`);
410
+ logger.debug(`Failed to get component by type: ${error.message}`);
402
411
  return null;
403
412
  }
404
413
  }
405
- async function getComponentWithRetry(name, registryType = SCHEMA_CONFIG.defaultRegistryType) {
414
+ async function getComponent(name, registryType = SCHEMA_CONFIG.defaultRegistryType, options = {}) {
406
415
  try {
407
- if (isUrl2(name)) {
408
- return await fetchFromUrlWithRetry(name);
409
- }
410
- if (!await checkInternetConnection()) {
411
- throw new Error("No internet connection available");
416
+ if (isUrl(name)) {
417
+ return await fetchFromUrl(name, options);
412
418
  }
413
- return await getComponentByTypeWithRetry(name, registryType);
419
+ return await getComponentByType(name, registryType, options);
414
420
  } catch (error) {
415
- console.error(`\u274C Failed to fetch ${name} from ${registryType}:`, error.message);
421
+ logger.debug(`Failed to fetch ${name} from ${registryType}: ${error.message}`);
416
422
  return null;
417
423
  }
418
424
  }
419
- async function fetchFromUrlWithRetry(url) {
420
- console.log(`\u{1F310} Fetching component from URL with retry: ${url}`);
421
- const data = await fetchWithRetry(url);
425
+ async function fetchFromUrl(url, options = {}) {
426
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
427
+ const maxRetries = Math.max(1, options.maxRetries ?? DEFAULT_MAX_RETRIES);
428
+ logger.debug(`Fetching component from: ${url}`);
429
+ const data = await fetchJsonWithRetry(url, maxRetries, timeoutMs);
422
430
  return componentSchema.parse(data);
423
431
  }
424
- async function getAllComponentsWithRetry(registryType = SCHEMA_CONFIG.defaultRegistryType) {
432
+ async function getAllComponents(registryType = SCHEMA_CONFIG.defaultRegistryType, options = {}) {
425
433
  try {
426
- console.log(`\u{1F310} Fetching all ${registryType} components with retry logic`);
427
- if (!await checkInternetConnection()) {
428
- throw new Error("No internet connection available");
429
- }
430
- const indexData = await getRegistryIndexWithRetry(registryType);
434
+ logger.debug(`Fetching all ${registryType} components using optimized approach`);
435
+ const indexData = await getRegistryIndex(registryType, options);
431
436
  const components = [];
437
+ const excludeTypes = options.excludeTypes ?? [];
432
438
  if (indexData.components && Array.isArray(indexData.components)) {
433
439
  for (const componentInfo of indexData.components) {
434
- const component = await getComponentWithRetry(componentInfo.name, registryType);
440
+ if (excludeTypes.includes(componentInfo.type)) {
441
+ continue;
442
+ }
443
+ const component = await getComponent(componentInfo.name, registryType, options);
435
444
  if (component) {
436
445
  components.push(component);
437
446
  }
@@ -439,14 +448,37 @@ async function getAllComponentsWithRetry(registryType = SCHEMA_CONFIG.defaultReg
439
448
  }
440
449
  return components;
441
450
  } catch (error) {
442
- console.error(`\u274C Failed to fetch all ${registryType} components:`, error.message);
451
+ logger.debug(`Failed to fetch all ${registryType} components: ${error.message}`);
443
452
  return [];
444
453
  }
445
454
  }
455
+ function cacheStateHasIndex(registryType, options) {
456
+ if (options.noCache) {
457
+ return false;
458
+ }
459
+ const cache = getRegistryCache(registryType);
460
+ return Boolean(cache.registryIndex);
461
+ }
462
+ function getCachedInMemory(registryType) {
463
+ return getRegistryCache(registryType).registryIndex;
464
+ }
465
+ function setCachedInMemory(registryType, index) {
466
+ const cache = getRegistryCache(registryType);
467
+ cache.registryIndex = index;
468
+ }
469
+ function resetCache(registryType) {
470
+ if (registryType) {
471
+ registryCache.delete(registryType);
472
+ logger.debug(`Cache reset for ${registryType} - will rediscover working CDN`);
473
+ } else {
474
+ registryCache.clear();
475
+ logger.debug(`All registry caches reset - will rediscover working CDNs`);
476
+ }
477
+ }
446
478
 
447
479
  // src/utils/project.ts
448
- import fs from "fs-extra";
449
- import path from "path";
480
+ import fs2 from "fs-extra";
481
+ import path2 from "path";
450
482
  var MODERN_CONFIG_NAME = "ui8kit.config.json";
451
483
  async function isViteProject() {
452
484
  const viteConfigFiles = [
@@ -456,134 +488,71 @@ async function isViteProject() {
456
488
  "vite.config.mjs"
457
489
  ];
458
490
  for (const file of viteConfigFiles) {
459
- if (await fs.pathExists(file)) {
491
+ if (await fs2.pathExists(file)) {
460
492
  return true;
461
493
  }
462
494
  }
463
495
  return false;
464
496
  }
465
497
  async function hasReact() {
466
- const packageJsonPath = path.join(process.cwd(), "package.json");
467
- if (!await fs.pathExists(packageJsonPath)) {
498
+ const packageJsonPath = path2.join(process.cwd(), "package.json");
499
+ if (!await fs2.pathExists(packageJsonPath)) {
468
500
  return false;
469
501
  }
470
- const packageJson = await fs.readJson(packageJsonPath);
502
+ const packageJson = await fs2.readJson(packageJsonPath);
471
503
  const deps = {
472
504
  ...packageJson.dependencies,
473
505
  ...packageJson.devDependencies
474
506
  };
475
507
  return "react" in deps;
476
508
  }
477
- async function findConfig(_registryType) {
509
+ async function findConfig(registryType) {
510
+ const rootConfig = await getConfig();
511
+ if (rootConfig)
512
+ return rootConfig;
478
513
  const srcConfig = await getConfig("./src");
479
514
  if (srcConfig)
480
515
  return srcConfig;
481
- if (_registryType) {
482
- const registryConfig = await getConfig(`./${_registryType}`);
516
+ if (registryType) {
517
+ const registryConfig = await getConfig(`./${registryType}`);
483
518
  if (registryConfig)
484
519
  return registryConfig;
485
520
  }
486
- return await getConfig();
521
+ return null;
487
522
  }
488
523
  async function getConfig(registryPath) {
489
- const baseDir = registryPath ? path.join(process.cwd(), registryPath) : process.cwd();
490
- const configPath = path.join(baseDir, MODERN_CONFIG_NAME);
491
- if (!await fs.pathExists(configPath)) {
524
+ const baseDir = registryPath ? path2.join(process.cwd(), registryPath) : process.cwd();
525
+ const configPath = path2.join(baseDir, MODERN_CONFIG_NAME);
526
+ if (!await fs2.pathExists(configPath)) {
492
527
  return null;
493
528
  }
494
529
  try {
495
- const config = await fs.readJson(configPath);
530
+ const config = await fs2.readJson(configPath);
496
531
  return configSchema.parse(config);
497
532
  } catch (error) {
498
533
  console.error("\u274C Invalid ui8kit.config.json:", error.message);
499
534
  return null;
500
535
  }
501
536
  }
502
- async function saveConfig(config, registryPath) {
503
- const configPath = registryPath ? path.join(process.cwd(), registryPath, MODERN_CONFIG_NAME) : path.join(process.cwd(), MODERN_CONFIG_NAME);
504
- await fs.ensureDir(path.dirname(configPath));
505
- await fs.writeJson(configPath, config, { spaces: 2 });
537
+ async function saveConfig(config) {
538
+ const configPath = path2.join(process.cwd(), MODERN_CONFIG_NAME);
539
+ await fs2.ensureDir(path2.dirname(configPath));
540
+ await fs2.writeJson(configPath, config, { spaces: 2 });
506
541
  }
507
542
  async function ensureDir(dirPath) {
508
- await fs.ensureDir(dirPath);
543
+ await fs2.ensureDir(dirPath);
509
544
  }
510
545
 
511
546
  // src/utils/registry-validator.ts
512
- import fs2 from "fs-extra";
513
- import chalk from "chalk";
514
- async function validateComponentInstallation(components, registryType) {
515
- return { isValid: true };
516
- }
517
- function handleValidationError(result) {
518
- console.error(chalk.red("\u274C Registry Validation Error:"));
519
- console.error(chalk.red(result.message));
520
- if (result.missingComponents && result.missingComponents.length > 0) {
521
- console.log(chalk.yellow("\n\u{1F4A1} Suggestion:"));
522
- console.log(`Install missing components first: ${chalk.cyan(`npx ui8kit add ${result.missingComponents.join(" ")}`)}
523
- `);
524
- }
525
- process.exit(1);
526
- }
547
+ import fs6 from "fs-extra";
548
+ import path6 from "path";
549
+ import chalk6 from "chalk";
550
+ import prompts2 from "prompts";
527
551
 
528
- // src/utils/dependency-checker.ts
529
- import fs3 from "fs-extra";
530
- import path2 from "path";
531
- import chalk2 from "chalk";
532
- async function checkProjectDependencies(requiredDeps) {
533
- const packageJsonPath = path2.join(process.cwd(), "package.json");
534
- if (!await fs3.pathExists(packageJsonPath)) {
535
- return {
536
- installed: [],
537
- missing: requiredDeps,
538
- workspace: []
539
- };
540
- }
541
- try {
542
- const packageJson = await fs3.readJson(packageJsonPath);
543
- const allDeps = {
544
- ...packageJson.dependencies,
545
- ...packageJson.devDependencies
546
- };
547
- const installed = [];
548
- const missing = [];
549
- const workspace = [];
550
- for (const dep of requiredDeps) {
551
- const version = allDeps[dep];
552
- if (!version) {
553
- missing.push(dep);
554
- } else if (version.startsWith("workspace:")) {
555
- workspace.push(dep);
556
- } else {
557
- installed.push(dep);
558
- }
559
- }
560
- return { installed, missing, workspace };
561
- } catch (error) {
562
- return {
563
- installed: [],
564
- missing: requiredDeps,
565
- workspace: []
566
- };
567
- }
568
- }
569
- function showDependencyStatus(deps) {
570
- if (deps.installed.length > 0) {
571
- console.log(chalk2.green(` \u2705 Already installed: ${deps.installed.join(", ")}`));
572
- }
573
- if (deps.workspace.length > 0) {
574
- console.log(chalk2.blue(` \u{1F517} Workspace dependencies: ${deps.workspace.join(", ")}`));
575
- }
576
- if (deps.missing.length > 0) {
577
- console.log(chalk2.yellow(` \u{1F4E6} Will install: ${deps.missing.join(", ")}`));
578
- }
579
- }
580
- async function filterMissingDependencies(dependencies) {
581
- const status = await checkProjectDependencies(dependencies);
582
- return status.missing;
583
- }
584
- function isWorkspaceError(errorMessage) {
585
- return errorMessage.includes("EUNSUPPORTEDPROTOCOL") || errorMessage.includes("workspace:") || errorMessage.includes('Unsupported URL Type "workspace:"');
586
- }
552
+ // src/commands/init.ts
553
+ import chalk5 from "chalk";
554
+ import prompts from "prompts";
555
+ import ora3 from "ora";
587
556
 
588
557
  // src/utils/cli-messages.ts
589
558
  var CLI_MESSAGES = {
@@ -599,14 +568,17 @@ var CLI_MESSAGES = {
599
568
  noCDNFound: (registryType) => `No working ${registryType} CDN found`,
600
569
  componentNotFound: (name, registryType) => `Component "${name}" not found in ${registryType} registry`,
601
570
  unknownComponentType: (type) => `Unknown component type: ${type}`,
602
- fileNotFound: (path7) => `File not found: ${path7}`,
571
+ fileNotFound: (path13) => `File not found: ${path13}`,
603
572
  invalidConfig: "Invalid ui8kit.config.json:",
604
573
  failedToInstall: (name, registryType) => `Failed to install ${name} from ${registryType}`,
605
574
  failedToFetch: (registryType) => `Failed to fetch components from ${registryType}`,
606
575
  failedToFetchComponent: (name, registryType) => `Failed to fetch ${name} from ${registryType}:`,
607
- failedToAnalyzeDeps: (path7) => `Warning: Could not analyze dependencies for ${path7}:`,
576
+ failedToAnalyzeDeps: (path13) => `Warning: Could not analyze dependencies for ${path13}:`,
608
577
  dependenciesFailed: "Failed to install dependencies",
609
- couldNotInstallDeps: (name) => `Warning: Could not install some dependencies for ${name}`
578
+ couldNotInstallDeps: (name) => `Warning: Could not install some dependencies for ${name}`,
579
+ noLocalInstall: "No installed local components were found in this project.",
580
+ invalidRegistryForDiff: "Could not load registry data for diff check.",
581
+ cacheClearFailed: "Could not clear cache."
610
582
  },
611
583
  // Success messages
612
584
  success: {
@@ -619,14 +591,16 @@ var CLI_MESSAGES = {
619
591
  registryBuilt: "Registry built successfully!",
620
592
  schemasGenerated: "Schema files generated successfully!",
621
593
  registryGenerated: (registryName) => `${registryName} registry generated successfully!`,
622
- depsAvailable: "All dependencies already available"
594
+ depsAvailable: "All dependencies already available",
595
+ cacheCleared: "UI8Kit cache cleared successfully."
623
596
  },
624
597
  // Info messages
625
598
  info: {
599
+ tryListComponents: (registryType) => `Run "npx ui8kit@latest add --all --registry ${registryType}" to list available components`,
626
600
  initializing: (registryName) => `Initializing UI8Kit in your project (${registryName} registry)...`,
627
601
  installing: (registryName) => `Installing from ${registryName} registry...`,
628
602
  installingAll: (registryName) => `Installing all available components from ${registryName} registry...`,
629
- retryEnabled: "Retry mode enabled - using enhanced connection logic",
603
+ retryEnabled: "Aggressive retry mode enabled (3 attempts per request)",
630
604
  fetchingComponentList: (registryType) => `Fetching component list from ${registryType}...`,
631
605
  fetchingRegistry: (registryType) => `Fetching ${registryType} registry index`,
632
606
  scanningComponents: (registryName) => `Scanning ${registryName} components...`,
@@ -641,12 +615,16 @@ var CLI_MESSAGES = {
641
615
  using: (registryType, baseUrl) => `Using ${registryType} CDN: ${baseUrl}`,
642
616
  loading: (name, registryType, folder, type) => `Loading ${name} from /${registryType}/${folder}/ (type: ${type})`,
643
617
  fetching: (registryType, url) => `Fetching from ${registryType}: ${url}`,
618
+ listingComponents: "Listing available components",
644
619
  fetchingUrl: "Fetching component from:",
645
- fetchingUrlWithRetry: "Fetching component from URL with retry:"
620
+ fetchingUrlWithRetry: "Fetching component from URL with retry:",
621
+ checkingForUpdates: "Checking for component updates...",
622
+ localDiffSummary: "Local components compared with registry."
646
623
  },
647
624
  // Prompts
648
625
  prompts: {
649
- typescript: "Are you using TypeScript?",
626
+ globalCss: "Where is your global CSS file?",
627
+ aliasComponents: "Configure import aliases?",
650
628
  overwrite: (registryName) => `UI8Kit is already initialized for ${registryName} registry. Overwrite configuration?`
651
629
  },
652
630
  // Examples and help text
@@ -694,235 +672,82 @@ var CLI_MESSAGES = {
694
672
  }
695
673
  };
696
674
 
697
- // src/commands/add.ts
698
- async function addCommand(components, options) {
699
- const registryType = resolveRegistryType(options.registry);
700
- if (options.all || components.includes("all")) {
701
- return await addAllComponents(options, registryType);
702
- }
703
- if (components.length === 0) {
704
- console.error(chalk3.red(`\u274C ${CLI_MESSAGES.errors.noComponentsSpecified}`));
705
- CLI_MESSAGES.examples.add.forEach((example) => console.log(example));
706
- process.exit(1);
707
- }
708
- const validation = await validateComponentInstallation(components, registryType);
709
- if (!validation.isValid) {
710
- handleValidationError(validation);
711
- }
712
- const config = await findConfig(registryType);
713
- if (!config) {
714
- console.error(chalk3.red(`\u274C ${CLI_MESSAGES.errors.notInitialized}`));
715
- console.log(`Run 'npx ui8kit@latest init' first.`);
716
- console.log(`For ${registryType} registry, run: npx ui8kit@latest init --registry ${registryType}`);
717
- process.exit(1);
718
- }
719
- const getComponentFn = options.retry ? getComponentWithRetry : getComponent;
720
- if (options.retry) {
721
- console.log(chalk3.blue(`\u{1F504} ${CLI_MESSAGES.info.retryEnabled}`));
722
- }
723
- console.log(chalk3.blue(`\u{1F4E6} ${CLI_MESSAGES.info.installing(registryType)}`));
724
- const results = await processComponents(components, registryType, config, getComponentFn, options);
725
- displayInstallationSummary(registryType, results);
726
- }
727
- async function addAllComponents(options, registryType) {
728
- console.log(chalk3.blue(`\u{1F680} ${CLI_MESSAGES.info.installingAll(registryType)}`));
729
- const config = await findConfig(registryType);
730
- if (!config) {
731
- console.error(chalk3.red(`\u274C ${CLI_MESSAGES.errors.notInitialized}`));
732
- console.log(`Run 'npx ui8kit@latest init' first.`);
733
- console.log(`For ${registryType} registry, run: npx ui8kit@latest init --registry ${registryType}`);
734
- process.exit(1);
735
- }
736
- const getAllComponentsFn = options.retry ? getAllComponentsWithRetry : getAllComponents;
737
- if (options.retry) {
738
- console.log(chalk3.blue(`\u{1F504} ${CLI_MESSAGES.info.retryEnabled}`));
675
+ // src/utils/package-manager.ts
676
+ import chalk3 from "chalk";
677
+ import ora2 from "ora";
678
+ import path4 from "path";
679
+ import fs4 from "fs-extra";
680
+ import { execa } from "execa";
681
+
682
+ // src/utils/dependency-checker.ts
683
+ import fs3 from "fs-extra";
684
+ import path3 from "path";
685
+ import chalk2 from "chalk";
686
+ async function checkProjectDependencies(requiredDeps) {
687
+ const packageJsonPath = path3.join(process.cwd(), "package.json");
688
+ if (!await fs3.pathExists(packageJsonPath)) {
689
+ return {
690
+ installed: [],
691
+ missing: requiredDeps,
692
+ workspace: []
693
+ };
739
694
  }
740
- const spinner = ora(CLI_MESSAGES.info.fetchingComponentList(registryType)).start();
741
695
  try {
742
- const allComponents = await getAllComponentsFn(registryType);
743
- if (allComponents.length === 0) {
744
- spinner.fail(`No components found in ${registryType} registry`);
745
- console.log(chalk3.yellow(`
746
- \u26A0\uFE0F ${registryType} ${CLI_MESSAGES.errors.registryTempUnavailable}`));
747
- console.log("Try these alternatives:");
748
- CLI_MESSAGES.examples.troubleshooting.forEach((alt) => console.log(` \u2022 ${alt}`));
749
- return;
750
- }
751
- spinner.succeed(CLI_MESSAGES.status.foundComponents(allComponents.length, registryType));
752
- if (options.dryRun) {
753
- console.log(chalk3.blue(`
754
- \u{1F4CB} ${CLI_MESSAGES.status.wouldInstallFrom(registryType)}`));
755
- allComponents.forEach((comp) => {
756
- console.log(` - ${comp.name} (${comp.type})`);
757
- });
758
- return;
759
- }
760
- const results = await processComponents(
761
- allComponents.map((c) => c.name),
762
- registryType,
763
- config,
764
- options.retry ? getComponentWithRetry : getComponent,
765
- options,
766
- allComponents
767
- );
768
- await installComponentsIndex(registryType, config);
769
- displayInstallationSummary(registryType, results);
770
- } catch (error) {
771
- spinner.fail(CLI_MESSAGES.errors.failedToFetch(registryType));
772
- console.error(chalk3.red("\u274C Error:"), error.message);
773
- console.log(chalk3.yellow(`
774
- \u26A0\uFE0F ${registryType} ${CLI_MESSAGES.errors.registryTempUnavailable}`));
775
- console.log("Try these alternatives:");
776
- CLI_MESSAGES.examples.troubleshooting.forEach((alt) => console.log(` \u2022 ${alt}`));
777
- process.exit(1);
778
- }
779
- }
780
- async function processComponents(componentNames, registryType, config, getComponentFn, options, preloadedComponents) {
781
- const results = [];
782
- const componentMap = new Map(preloadedComponents?.map((c) => [c.name, c]));
783
- for (const componentName of componentNames) {
784
- const spinner = ora(CLI_MESSAGES.status.installing(componentName, registryType)).start();
785
- try {
786
- let component = componentMap?.get(componentName);
787
- if (!component) {
788
- component = await getComponentFn(componentName, registryType);
789
- }
790
- if (!component) {
791
- throw new Error(CLI_MESSAGES.errors.componentNotFound(componentName, registryType));
792
- }
793
- if (options.dryRun) {
794
- spinner.succeed(CLI_MESSAGES.status.wouldInstall(component.name, registryType));
795
- console.log(` Type: ${component.type}`);
796
- console.log(` Files: ${component.files.length}`);
797
- console.log(` Dependencies: ${component.dependencies.join(", ") || "none"}`);
798
- if (component.dependencies.length > 0) {
799
- const depStatus = await checkProjectDependencies(component.dependencies);
800
- showDependencyStatus(depStatus);
801
- }
802
- continue;
803
- }
804
- await installComponentFiles(component, config, options.force);
805
- if (component.dependencies.length > 0) {
806
- try {
807
- await installDependencies(component.dependencies);
808
- } catch (error) {
809
- console.log(chalk3.yellow(` \u26A0\uFE0F ${CLI_MESSAGES.errors.couldNotInstallDeps(component.name)}`));
810
- console.log(chalk3.yellow(` Dependencies: ${component.dependencies.join(", ")}`));
811
- console.log(chalk3.yellow(` Please install them manually if needed`));
812
- }
696
+ const packageJson = await fs3.readJson(packageJsonPath);
697
+ const allDeps = {
698
+ ...packageJson.dependencies,
699
+ ...packageJson.devDependencies
700
+ };
701
+ const installed = [];
702
+ const missing = [];
703
+ const workspace = [];
704
+ for (const dep of requiredDeps) {
705
+ const version = allDeps[dep];
706
+ if (!version) {
707
+ missing.push(dep);
708
+ } else if (version.startsWith("workspace:")) {
709
+ workspace.push(dep);
710
+ } else {
711
+ installed.push(dep);
813
712
  }
814
- spinner.succeed(CLI_MESSAGES.status.installing(component.name, registryType));
815
- results.push({ name: component.name, status: "success" });
816
- } catch (error) {
817
- spinner.fail(CLI_MESSAGES.errors.failedToInstall(componentName, registryType));
818
- console.error(chalk3.red(` Error: ${error.message}`));
819
- results.push({
820
- name: componentName,
821
- status: "error",
822
- error: error.message
823
- });
824
713
  }
825
- }
826
- return results;
827
- }
828
- function displayInstallationSummary(registryType, results) {
829
- const successful = results.filter((r) => r.status === "success");
830
- const failed = results.filter((r) => r.status === "error");
831
- console.log(chalk3.blue("\n\u{1F4CA} Installation Summary:"));
832
- console.log(` Registry: ${registryType}`);
833
- console.log(` \u2705 Successful: ${successful.length}`);
834
- console.log(` \u274C Failed: ${failed.length}`);
835
- if (successful.length > 0) {
836
- console.log(chalk3.green(`
837
- \u{1F389} ${CLI_MESSAGES.success.componentsInstalled}`));
838
- console.log("You can now import and use them in your project.");
839
- }
840
- if (failed.length > 0) {
841
- process.exit(1);
842
- }
843
- }
844
- async function installComponentFiles(component, config, force = false) {
845
- for (const file of component.files) {
846
- const fileName = path3.basename(file.path);
847
- const target = file.target || inferTargetFromType(component.type);
848
- const installDir = resolveInstallDir(target, config);
849
- const targetPath = path3.join(process.cwd(), installDir, fileName);
850
- if (!force && await fs4.pathExists(targetPath)) {
851
- console.log(` \u26A0\uFE0F ${CLI_MESSAGES.status.skipped(fileName)}`);
852
- continue;
853
- }
854
- await fs4.ensureDir(path3.dirname(targetPath));
855
- await fs4.writeFile(targetPath, file.content, "utf-8");
856
- }
857
- }
858
- function inferTargetFromType(componentType) {
859
- switch (componentType) {
860
- case "registry:ui":
861
- return "ui";
862
- case "registry:composite":
863
- return "components";
864
- case "registry:block":
865
- return "blocks";
866
- case "registry:component":
867
- return "components";
868
- case "registry:layout":
869
- return "layouts";
870
- case "registry:lib":
871
- return "lib";
872
- case "registry:variants":
873
- return "variants";
874
- default:
875
- return "components";
714
+ return { installed, missing, workspace };
715
+ } catch (error) {
716
+ return {
717
+ installed: [],
718
+ missing: requiredDeps,
719
+ workspace: []
720
+ };
876
721
  }
877
722
  }
878
- function resolveInstallDir(target, config) {
879
- const normalizedTarget = target.replace(/\\/g, "/").replace(/^\/?src\//i, "");
880
- if (normalizedTarget === "lib") {
881
- return normalizeDir(config.libDir || SCHEMA_CONFIG.defaultDirectories.lib);
882
- }
883
- if (normalizedTarget === "variants") {
884
- return normalizeDir(SCHEMA_CONFIG.defaultDirectories.variants);
723
+ function showDependencyStatus(deps) {
724
+ if (deps.installed.length > 0) {
725
+ console.log(chalk2.green(` \u2705 Already installed: ${deps.installed.join(", ")}`));
885
726
  }
886
- const baseComponentsDir = normalizeDir(config.componentsDir || SCHEMA_CONFIG.defaultDirectories.components);
887
- if (normalizedTarget.includes("/")) {
888
- const parentRoot = baseComponentsDir.replace(/[/\\]components$/i, "") || "src";
889
- return path3.join(parentRoot, normalizedTarget).replace(/\\/g, "/");
727
+ if (deps.workspace.length > 0) {
728
+ console.log(chalk2.blue(` \u{1F517} Workspace dependencies: ${deps.workspace.join(", ")}`));
890
729
  }
891
- if (normalizedTarget === "ui")
892
- return path3.join(baseComponentsDir, "ui").replace(/\\/g, "/");
893
- if (normalizedTarget === "components")
894
- return baseComponentsDir;
895
- switch (normalizedTarget) {
896
- case "blocks":
897
- return normalizeDir(SCHEMA_CONFIG.defaultDirectories.blocks);
898
- case "layouts":
899
- return normalizeDir(SCHEMA_CONFIG.defaultDirectories.layouts);
900
- default:
901
- return baseComponentsDir;
730
+ if (deps.missing.length > 0) {
731
+ console.log(chalk2.yellow(` \u{1F4E6} Will install: ${deps.missing.join(", ")}`));
902
732
  }
903
733
  }
904
- function normalizeDir(dir) {
905
- return dir.replace(/^\.\//, "").replace(/\\/g, "/");
734
+ async function filterMissingDependencies(dependencies) {
735
+ const status = await checkProjectDependencies(dependencies);
736
+ return status.missing;
906
737
  }
907
- function resolveRegistryType(registryInput) {
908
- if (!registryInput) {
909
- return SCHEMA_CONFIG.defaultRegistryType;
910
- }
911
- if (SCHEMA_CONFIG.registryTypes.includes(registryInput)) {
912
- return registryInput;
913
- }
914
- console.warn(chalk3.yellow(`\u26A0\uFE0F Unknown registry type: ${registryInput}`));
915
- console.log(`Available registries: ${SCHEMA_CONFIG.registryTypes.join(", ")}`);
916
- console.log(`Using default: ${SCHEMA_CONFIG.defaultRegistryType}`);
917
- return SCHEMA_CONFIG.defaultRegistryType;
738
+ function isWorkspaceError(errorMessage) {
739
+ return errorMessage.includes("EUNSUPPORTEDPROTOCOL") || errorMessage.includes("workspace:") || errorMessage.includes('Unsupported URL Type "workspace:"');
918
740
  }
919
- async function installDependencies(dependencies) {
920
- const spinner = ora(CLI_MESSAGES.status.installing("dependencies", "")).start();
741
+
742
+ // src/utils/package-manager.ts
743
+ async function installDependencies(dependencies, options = {}) {
744
+ const useSpinner = options.useSpinner ?? true;
745
+ const spinner = useSpinner ? ora2(options.spinnerText ?? CLI_MESSAGES.status.installing("dependencies", "")).start() : null;
921
746
  try {
922
747
  const depStatus = await checkProjectDependencies(dependencies);
923
748
  const missingDependencies = await filterMissingDependencies(dependencies);
924
749
  if (missingDependencies.length === 0) {
925
- spinner.succeed(CLI_MESSAGES.success.depsAvailable);
750
+ spinner?.succeed(CLI_MESSAGES.success.depsAvailable);
926
751
  if (depStatus.workspace.length > 0) {
927
752
  console.log(chalk3.blue(` \u{1F517} Using workspace dependencies: ${depStatus.workspace.join(", ")}`));
928
753
  }
@@ -935,15 +760,15 @@ async function installDependencies(dependencies) {
935
760
  cwd: process.cwd(),
936
761
  stdio: "pipe"
937
762
  });
938
- spinner.succeed(CLI_MESSAGES.success.depsInstalled);
763
+ spinner?.succeed(CLI_MESSAGES.success.depsInstalled);
939
764
  } catch (error) {
940
- spinner.fail(CLI_MESSAGES.errors.dependenciesFailed);
765
+ spinner?.fail(CLI_MESSAGES.errors.dependenciesFailed);
941
766
  const errorMessage = error.stderr || error.message;
942
767
  if (isWorkspaceError(errorMessage)) {
943
768
  console.log(chalk3.yellow(`
944
769
  \u{1F4A1} ${CLI_MESSAGES.info.workspaceDepsDetected}`));
945
770
  const results = await installDependenciesIndividually(dependencies);
946
- if (results.some((r) => r.success)) {
771
+ if (results.some((result) => result.success)) {
947
772
  console.log(chalk3.green("\u2705 Some dependencies installed successfully"));
948
773
  return;
949
774
  }
@@ -964,7 +789,7 @@ async function installDependenciesIndividually(dependencies) {
964
789
  });
965
790
  console.log(chalk3.green(` \u2705 Installed ${dep}`));
966
791
  results.push({ dep, success: true });
967
- } catch (error) {
792
+ } catch {
968
793
  console.log(chalk3.yellow(` \u26A0\uFE0F Skipped ${dep} (may already be available)`));
969
794
  results.push({ dep, success: false });
970
795
  }
@@ -974,14 +799,14 @@ async function installDependenciesIndividually(dependencies) {
974
799
  async function detectPackageManager() {
975
800
  let dir = process.cwd();
976
801
  while (true) {
977
- if (await fs4.pathExists(path3.join(dir, "bun.lock")) || await fs4.pathExists(path3.join(dir, "bun.lockb"))) {
802
+ if (await fs4.pathExists(path4.join(dir, "bun.lock")) || await fs4.pathExists(path4.join(dir, "bun.lockb"))) {
978
803
  return "bun";
979
804
  }
980
- if (await fs4.pathExists(path3.join(dir, "pnpm-lock.yaml")))
805
+ if (await fs4.pathExists(path4.join(dir, "pnpm-lock.yaml")))
981
806
  return "pnpm";
982
- if (await fs4.pathExists(path3.join(dir, "yarn.lock")))
807
+ if (await fs4.pathExists(path4.join(dir, "yarn.lock")))
983
808
  return "yarn";
984
- const packageJsonPath = path3.join(dir, "package.json");
809
+ const packageJsonPath = path4.join(dir, "package.json");
985
810
  if (await fs4.pathExists(packageJsonPath)) {
986
811
  try {
987
812
  const packageJson = await fs4.readJson(packageJsonPath);
@@ -997,66 +822,107 @@ async function detectPackageManager() {
997
822
  } catch {
998
823
  }
999
824
  }
1000
- const parent = path3.dirname(dir);
825
+ const parent = path4.dirname(dir);
1001
826
  if (parent === dir)
1002
827
  break;
1003
828
  dir = parent;
1004
829
  }
1005
830
  return "npm";
1006
831
  }
1007
- async function installComponentsIndex(registryType, config) {
1008
- const spinner = ora("Installing components index...").start();
1009
- try {
1010
- const cdnUrls = SCHEMA_CONFIG.cdnBaseUrls;
1011
- for (const baseUrl of cdnUrls) {
1012
- try {
1013
- const url = `${baseUrl}/components/index.json`;
1014
- const response = await fetch3(url);
1015
- if (response.ok) {
1016
- const component = await response.json();
1017
- for (const file of component.files) {
1018
- const fileName = path3.basename(file.path);
1019
- const targetDir = config.componentsDir;
1020
- const targetPath = path3.join(process.cwd(), targetDir, fileName);
1021
- await fs4.ensureDir(path3.dirname(targetPath));
1022
- await fs4.writeFile(targetPath, file.content || "", "utf-8");
1023
- }
1024
- spinner.succeed("Installed components index");
1025
- return;
1026
- }
1027
- } catch {
1028
- continue;
1029
- }
832
+
833
+ // src/commands/init.ts
834
+ import path5 from "path";
835
+ import fs5 from "fs-extra";
836
+ import fetch2 from "node-fetch";
837
+
838
+ // src/utils/errors.ts
839
+ import chalk4 from "chalk";
840
+ var Ui8kitError = class extends Error {
841
+ suggestion;
842
+ constructor(message, suggestion) {
843
+ super(message);
844
+ this.name = this.constructor.name;
845
+ this.suggestion = suggestion;
846
+ }
847
+ };
848
+ var ConfigNotFoundError = class extends Ui8kitError {
849
+ constructor(registry) {
850
+ super(
851
+ `ui8kit config not found for registry "${registry}".`,
852
+ `Run: npx ui8kit@latest init --registry ${registry}`
853
+ );
854
+ }
855
+ };
856
+ function handleError(error) {
857
+ if (error instanceof Ui8kitError) {
858
+ if (error.message) {
859
+ console.error(chalk4.red(error.message));
1030
860
  }
1031
- spinner.info("Components index not found in registry (optional)");
1032
- } catch (error) {
1033
- spinner.fail("Could not install components index");
861
+ if (error.suggestion) {
862
+ console.log(chalk4.yellow(`\u{1F4A1} ${error.suggestion}`));
863
+ }
864
+ process.exit(1);
1034
865
  }
866
+ if (isZodError(error)) {
867
+ console.error(chalk4.red("\u274C Configuration validation error:"));
868
+ error.errors.forEach((issue) => {
869
+ const path13 = issue.path.join(".") || "root";
870
+ console.log(chalk4.yellow(` - ${path13}: ${issue.message}`));
871
+ });
872
+ process.exit(1);
873
+ }
874
+ console.error(chalk4.red("\u274C Unexpected error:"));
875
+ console.error(chalk4.red(error.message ?? String(error)));
876
+ process.exit(1);
877
+ }
878
+ function isZodError(error) {
879
+ return Boolean(error && typeof error === "object" && "issues" in error);
1035
880
  }
1036
881
 
1037
882
  // src/commands/init.ts
1038
- import chalk4 from "chalk";
1039
- import prompts from "prompts";
1040
- import ora2 from "ora";
1041
- import path4 from "path";
1042
- import fs5 from "fs-extra";
1043
- import fetch4 from "node-fetch";
883
+ function buildInitConfig(options) {
884
+ const registryName = options.registry || SCHEMA_CONFIG.defaultRegistryType;
885
+ const aliases = SCHEMA_CONFIG.defaultAliases;
886
+ const globalCss = options.globalCss || "src/index.css";
887
+ const aliasComponents = options.aliasComponents?.trim() || "@/components";
888
+ if (options.yes) {
889
+ return {
890
+ $schema: `${SCHEMA_CONFIG.baseUrl}.json`,
891
+ framework: "vite-react",
892
+ typescript: true,
893
+ globalCss,
894
+ aliases,
895
+ registry: SCHEMA_CONFIG.defaultRegistry,
896
+ componentsDir: SCHEMA_CONFIG.defaultDirectories.components,
897
+ libDir: SCHEMA_CONFIG.defaultDirectories.lib
898
+ };
899
+ }
900
+ return {
901
+ $schema: `${SCHEMA_CONFIG.baseUrl}.json`,
902
+ framework: "vite-react",
903
+ typescript: true,
904
+ globalCss,
905
+ aliases: { ...aliases, "@/components": aliasComponents },
906
+ registry: SCHEMA_CONFIG.defaultRegistry,
907
+ componentsDir: SCHEMA_CONFIG.defaultDirectories.components,
908
+ libDir: SCHEMA_CONFIG.defaultDirectories.lib
909
+ };
910
+ }
1044
911
  async function initCommand(options) {
1045
912
  const registryName = options.registry || SCHEMA_CONFIG.defaultRegistryType;
1046
- const registryPath = `./${registryName}`;
1047
- console.log(chalk4.blue(`\u{1F680} ${CLI_MESSAGES.info.initializing(registryName)}`));
913
+ logger.info(CLI_MESSAGES.info.initializing(registryName));
1048
914
  const viteDetected = await isViteProject();
1049
915
  if (!viteDetected) {
1050
- console.error(chalk4.red(`\u274C ${CLI_MESSAGES.errors.notViteProject}`));
916
+ console.error(chalk5.red(`\u274C ${CLI_MESSAGES.errors.notViteProject}`));
1051
917
  console.log("Please run this command in a Vite project directory.");
1052
918
  process.exit(1);
1053
919
  }
1054
920
  if (!await hasReact()) {
1055
- console.error(chalk4.red(`\u274C ${CLI_MESSAGES.errors.reactNotInstalled}`));
921
+ console.error(chalk5.red(`\u274C ${CLI_MESSAGES.errors.reactNotInstalled}`));
1056
922
  console.log("Please install React first: npm install react react-dom");
1057
923
  process.exit(1);
1058
924
  }
1059
- const existingConfig = await getConfig("./src");
925
+ const existingConfig = await findConfig(registryName);
1060
926
  if (existingConfig && !options.yes) {
1061
927
  const { overwrite } = await prompts({
1062
928
  type: "confirm",
@@ -1065,68 +931,67 @@ async function initCommand(options) {
1065
931
  initial: false
1066
932
  });
1067
933
  if (!overwrite) {
1068
- console.log(chalk4.yellow(`\u26A0\uFE0F ${CLI_MESSAGES.info.installationCancelled}`));
934
+ logger.warn(CLI_MESSAGES.info.installationCancelled);
1069
935
  return;
1070
936
  }
1071
937
  }
1072
- const aliases = SCHEMA_CONFIG.defaultAliases;
1073
938
  let config;
1074
939
  if (options.yes) {
1075
- config = {
1076
- $schema: `${SCHEMA_CONFIG.baseUrl}.json`,
1077
- framework: "vite-react",
1078
- typescript: true,
1079
- aliases,
1080
- registry: SCHEMA_CONFIG.defaultRegistry,
1081
- componentsDir: SCHEMA_CONFIG.defaultDirectories.components,
1082
- libDir: SCHEMA_CONFIG.defaultDirectories.lib
1083
- };
940
+ config = buildInitConfig({ yes: true, registry: registryName });
1084
941
  } else {
1085
942
  const responses = await prompts([
1086
943
  {
1087
- type: "confirm",
1088
- name: "typescript",
1089
- message: CLI_MESSAGES.prompts.typescript,
1090
- initial: true
944
+ type: "text",
945
+ name: "globalCss",
946
+ message: CLI_MESSAGES.prompts.globalCss,
947
+ initial: "src/index.css"
948
+ },
949
+ {
950
+ type: "text",
951
+ name: "aliasComponents",
952
+ message: CLI_MESSAGES.prompts.aliasComponents,
953
+ initial: "@/components"
1091
954
  }
1092
955
  ]);
1093
- config = {
1094
- $schema: `${SCHEMA_CONFIG.baseUrl}.json`,
1095
- framework: "vite-react",
1096
- typescript: responses.typescript,
1097
- aliases,
1098
- registry: SCHEMA_CONFIG.defaultRegistry,
1099
- componentsDir: SCHEMA_CONFIG.defaultDirectories.components,
1100
- libDir: SCHEMA_CONFIG.defaultDirectories.lib
1101
- };
956
+ const aliasComponents = responses.aliasComponents?.trim() || "@/components";
957
+ const globalCss = responses.globalCss || "src/index.css";
958
+ config = buildInitConfig({
959
+ yes: false,
960
+ registry: registryName,
961
+ globalCss,
962
+ aliasComponents
963
+ });
1102
964
  }
1103
- const spinner = ora2(CLI_MESSAGES.info.initializing(registryName)).start();
965
+ const spinner = ora3(CLI_MESSAGES.info.initializing(registryName)).start();
1104
966
  try {
1105
- await saveConfig(config, "./src");
967
+ await saveConfig(config);
1106
968
  await ensureDir(config.libDir);
1107
969
  await ensureDir(config.componentsDir);
1108
- await ensureDir(path4.join(config.componentsDir, "ui"));
970
+ await ensureDir(path5.join(config.componentsDir, "ui"));
1109
971
  await ensureDir(SCHEMA_CONFIG.defaultDirectories.blocks);
1110
972
  await ensureDir(SCHEMA_CONFIG.defaultDirectories.layouts);
1111
973
  await ensureDir(SCHEMA_CONFIG.defaultDirectories.variants);
1112
974
  spinner.text = "Installing core utilities and variants...";
1113
975
  await installCoreFiles(registryName, config, spinner);
976
+ spinner.text = "Installing core dependencies...";
977
+ await installDependencies(["clsx", "tailwind-merge"], {
978
+ useSpinner: false
979
+ });
1114
980
  spinner.succeed(CLI_MESSAGES.success.initialized(registryName));
1115
- console.log(chalk4.green(`
1116
- \u2705 ${CLI_MESSAGES.success.setupComplete(registryName)}`));
981
+ logger.success(`
982
+ \u2705 ${CLI_MESSAGES.success.setupComplete(registryName)}`);
1117
983
  console.log("\nDirectories created:");
1118
- console.log(` ${chalk4.cyan("src/lib/")} - Utils, helpers, functions`);
1119
- console.log(` ${chalk4.cyan("src/variants/")} - CVA variant configurations`);
1120
- console.log(` ${chalk4.cyan("src/components/ui/")} - UI components`);
1121
- console.log(` ${chalk4.cyan("src/components/")} - Complex components`);
1122
- console.log(` ${chalk4.cyan("src/layouts/")} - Page layouts and structures`);
1123
- console.log(` ${chalk4.cyan("src/blocks/")} - Component blocks`);
984
+ console.log(` ${chalk5.cyan("src/lib/")} - Utils, helpers, functions`);
985
+ console.log(` ${chalk5.cyan("src/variants/")} - CVA variant configurations`);
986
+ console.log(` ${chalk5.cyan("src/components/ui/")} - UI components`);
987
+ console.log(` ${chalk5.cyan("src/components/")} - Complex components`);
988
+ console.log(` ${chalk5.cyan("src/layouts/")} - Page layouts and structures`);
989
+ console.log(` ${chalk5.cyan("src/blocks/")} - Component blocks`);
1124
990
  console.log("\nNext steps:");
1125
- CLI_MESSAGES.examples.init.forEach((example) => console.log(` ${chalk4.cyan(example)}`));
991
+ CLI_MESSAGES.examples.init.forEach((example) => console.log(` ${chalk5.cyan(example)}`));
1126
992
  } catch (error) {
1127
993
  spinner.fail(CLI_MESSAGES.errors.buildFailed);
1128
- console.error(chalk4.red("\u274C Error:"), error.message);
1129
- process.exit(1);
994
+ handleError(error);
1130
995
  }
1131
996
  }
1132
997
  async function installCoreFiles(registryType, config, spinner) {
@@ -1135,7 +1000,7 @@ async function installCoreFiles(registryType, config, spinner) {
1135
1000
  for (const baseUrl of cdnUrls) {
1136
1001
  try {
1137
1002
  const indexUrl = `${baseUrl}/index.json`;
1138
- const response = await fetch4(indexUrl);
1003
+ const response = await fetch2(indexUrl);
1139
1004
  if (response.ok) {
1140
1005
  registryIndex = await response.json();
1141
1006
  break;
@@ -1149,87 +1014,796 @@ async function installCoreFiles(registryType, config, spinner) {
1149
1014
  await createUtilsFile(config.libDir, config.typescript);
1150
1015
  return;
1151
1016
  }
1152
- const variantItems = registryIndex.components.filter((c) => c.type === "registry:variants");
1153
- const libItems = registryIndex.components.filter((c) => c.type === "registry:lib");
1154
- for (const item of libItems) {
1155
- spinner.text = `Installing ${item.name}...`;
1156
- await installComponentFromRegistry(item.name, "registry:lib", cdnUrls, config);
1017
+ const variantItems = registryIndex.components.filter((c) => c.type === "registry:variants");
1018
+ const libItems = registryIndex.components.filter((c) => c.type === "registry:lib");
1019
+ for (const item of libItems) {
1020
+ spinner.text = `Installing ${item.name}...`;
1021
+ await installComponentFromRegistry(item.name, "registry:lib", cdnUrls, config);
1022
+ }
1023
+ for (const item of variantItems) {
1024
+ spinner.text = `Installing variant: ${item.name}...`;
1025
+ await installComponentFromRegistry(item.name, "registry:variants", cdnUrls, config);
1026
+ }
1027
+ spinner.text = "Syncing variants index...";
1028
+ const variantsIndexStatus = await installVariantsIndex(cdnUrls);
1029
+ if (variantsIndexStatus === "updated") {
1030
+ spinner.text = "Updated variants/index.ts from CDN";
1031
+ } else if (variantsIndexStatus === "created") {
1032
+ spinner.text = "Created variants/index.ts from CDN";
1033
+ } else if (variantsIndexStatus === "unchanged") {
1034
+ spinner.text = "variants/index.ts is up to date";
1035
+ } else {
1036
+ spinner.text = "variants/index.ts not found in registry (skipped)";
1037
+ }
1038
+ spinner.text = `\u2705 Installed ${libItems.length} utilities and ${variantItems.length} variants`;
1039
+ }
1040
+ async function installComponentFromRegistry(name, type, cdnUrls, config) {
1041
+ const folder = type === "registry:lib" ? "lib" : type === "registry:variants" ? "components/variants" : "components/ui";
1042
+ for (const baseUrl of cdnUrls) {
1043
+ try {
1044
+ const url = `${baseUrl}/${folder}/${name}.json`;
1045
+ const response = await fetch2(url);
1046
+ if (response.ok) {
1047
+ const component = await response.json();
1048
+ for (const file of component.files) {
1049
+ const fileName = path5.basename(file.path);
1050
+ let targetDir;
1051
+ if (type === "registry:lib") {
1052
+ targetDir = config.libDir;
1053
+ } else if (type === "registry:variants") {
1054
+ targetDir = SCHEMA_CONFIG.defaultDirectories.variants;
1055
+ } else {
1056
+ targetDir = path5.join(config.componentsDir, "ui");
1057
+ }
1058
+ const targetPath = path5.join(process.cwd(), targetDir, fileName);
1059
+ await fs5.ensureDir(path5.dirname(targetPath));
1060
+ await fs5.writeFile(targetPath, file.content || "", "utf-8");
1061
+ }
1062
+ return;
1063
+ }
1064
+ } catch {
1065
+ continue;
1066
+ }
1067
+ }
1068
+ }
1069
+ async function installVariantsIndex(cdnUrls) {
1070
+ for (const baseUrl of cdnUrls) {
1071
+ try {
1072
+ const url = `${baseUrl}/components/variants/index.json`;
1073
+ const response = await fetch2(url);
1074
+ if (response.ok) {
1075
+ const component = await response.json();
1076
+ for (const file of component.files) {
1077
+ const fileName = path5.basename(file.path);
1078
+ if (!fileName.startsWith("index.")) {
1079
+ continue;
1080
+ }
1081
+ const targetDir = SCHEMA_CONFIG.defaultDirectories.variants;
1082
+ const targetPath = path5.join(process.cwd(), targetDir, fileName);
1083
+ const incomingContent = file.content || "";
1084
+ const exists = await fs5.pathExists(targetPath);
1085
+ if (exists) {
1086
+ const currentContent = await fs5.readFile(targetPath, "utf-8");
1087
+ if (currentContent === incomingContent) {
1088
+ return "unchanged";
1089
+ }
1090
+ }
1091
+ await fs5.ensureDir(path5.dirname(targetPath));
1092
+ await fs5.writeFile(targetPath, incomingContent, "utf-8");
1093
+ return exists ? "updated" : "created";
1094
+ }
1095
+ }
1096
+ } catch {
1097
+ continue;
1098
+ }
1099
+ }
1100
+ return "skipped";
1101
+ }
1102
+ async function createUtilsFile(libDir, typescript) {
1103
+ const utilsContent = `import { type ClassValue, clsx } from "clsx"
1104
+ import { twMerge } from "tailwind-merge"
1105
+
1106
+ export function cn(...inputs: ClassValue[]) {
1107
+ return twMerge(clsx(inputs))
1108
+ }`;
1109
+ const fileName = typescript ? "utils.ts" : "utils.js";
1110
+ const filePath = path5.join(process.cwd(), libDir, fileName);
1111
+ await fs5.writeFile(filePath, utilsContent, "utf-8");
1112
+ }
1113
+
1114
+ // src/utils/registry-validator.ts
1115
+ async function validateComponentInstallation(components, registryType) {
1116
+ const packageJsonPath = path6.join(process.cwd(), "package.json");
1117
+ if (!await fs6.pathExists(packageJsonPath)) {
1118
+ return {
1119
+ isValid: false,
1120
+ message: "No package.json found in the current directory. Run this command from your project root."
1121
+ };
1122
+ }
1123
+ const nodeMajorVersion = Number.parseInt(process.versions.node.split(".")[0] ?? "0", 10);
1124
+ if (Number.isNaN(nodeMajorVersion) || nodeMajorVersion < 18) {
1125
+ return {
1126
+ isValid: false,
1127
+ message: `Node.js 18+ is required. Current version: ${process.versions.node}`
1128
+ };
1129
+ }
1130
+ const existingConfig = await findConfig(registryType);
1131
+ if (!existingConfig) {
1132
+ const { runInit } = await prompts2({
1133
+ type: "confirm",
1134
+ name: "runInit",
1135
+ message: "ui8kit.config.json not found. Run init now?",
1136
+ initial: true
1137
+ });
1138
+ if (runInit) {
1139
+ await initCommand({ registry: registryType });
1140
+ const configAfterInit = await findConfig(registryType);
1141
+ if (configAfterInit) {
1142
+ return { isValid: true };
1143
+ }
1144
+ }
1145
+ return {
1146
+ isValid: false,
1147
+ message: `ui8kit is not initialized. Run: npx ui8kit@latest init --registry ${registryType}`
1148
+ };
1149
+ }
1150
+ return { isValid: true };
1151
+ }
1152
+ function handleValidationError(result) {
1153
+ console.error(chalk6.red("\u274C Registry Validation Error:"));
1154
+ console.error(chalk6.red(result.message));
1155
+ if (result.missingComponents && result.missingComponents.length > 0) {
1156
+ console.log(chalk6.yellow("\n\u{1F4A1} Suggestion:"));
1157
+ console.log(`Install missing components first: ${chalk6.cyan(`npx ui8kit add ${result.missingComponents.join(" ")}`)}
1158
+ `);
1159
+ }
1160
+ process.exit(1);
1161
+ }
1162
+
1163
+ // src/utils/dependency-resolver.ts
1164
+ async function resolveRegistryTree(componentNames, registryType, getComponent2) {
1165
+ const componentsByName = /* @__PURE__ */ new Map();
1166
+ const state = /* @__PURE__ */ new Map();
1167
+ const visitStack = [];
1168
+ const normalized = componentNames.map((name) => name.toLowerCase());
1169
+ const ensureComponent = async (name) => {
1170
+ const key = name.toLowerCase();
1171
+ if (state.get(key) === "done") {
1172
+ return componentsByName.get(key) ?? null;
1173
+ }
1174
+ if (state.get(key) === "visiting") {
1175
+ const cycle = visitStack.includes(key) ? [...visitStack, key].join(" -> ") : key;
1176
+ logger.warn(`Circular registry dependency detected: ${cycle}`);
1177
+ return componentsByName.get(key) ?? null;
1178
+ }
1179
+ const component = await getComponent2(name, registryType);
1180
+ if (!component) {
1181
+ logger.warn(`Component ${name} not found in ${registryType} registry, skipping`);
1182
+ state.set(key, "done");
1183
+ return null;
1184
+ }
1185
+ componentsByName.set(key, component);
1186
+ state.set(key, "visiting");
1187
+ visitStack.push(key);
1188
+ for (const dependency of component.registryDependencies ?? []) {
1189
+ await ensureComponent(dependency);
1190
+ }
1191
+ visitStack.pop();
1192
+ state.set(key, "done");
1193
+ return component;
1194
+ };
1195
+ for (const name of normalized) {
1196
+ await ensureComponent(name);
1197
+ }
1198
+ const graph = /* @__PURE__ */ new Map();
1199
+ const inDegree = /* @__PURE__ */ new Map();
1200
+ for (const component of componentsByName.values()) {
1201
+ const name = component.name.toLowerCase();
1202
+ if (!graph.has(name)) {
1203
+ graph.set(name, /* @__PURE__ */ new Set());
1204
+ inDegree.set(name, 0);
1205
+ }
1206
+ }
1207
+ for (const component of componentsByName.values()) {
1208
+ const from = component.name.toLowerCase();
1209
+ const deps = component.registryDependencies ?? [];
1210
+ for (const dep of deps) {
1211
+ const to = dep.toLowerCase();
1212
+ if (!componentsByName.has(to)) {
1213
+ continue;
1214
+ }
1215
+ const targets = graph.get(to);
1216
+ if (targets) {
1217
+ targets.add(from);
1218
+ } else {
1219
+ graph.set(to, /* @__PURE__ */ new Set([from]));
1220
+ }
1221
+ inDegree.set(from, (inDegree.get(from) ?? 0) + 1);
1222
+ }
1223
+ }
1224
+ const queue = [];
1225
+ inDegree.forEach((value, name) => {
1226
+ if (value === 0) {
1227
+ queue.push(name);
1228
+ }
1229
+ });
1230
+ const orderedKeys = [];
1231
+ while (queue.length > 0) {
1232
+ const current = queue.shift();
1233
+ if (!current)
1234
+ continue;
1235
+ orderedKeys.push(current);
1236
+ const targets = graph.get(current) || /* @__PURE__ */ new Set();
1237
+ for (const next of targets) {
1238
+ const degree = (inDegree.get(next) ?? 1) - 1;
1239
+ inDegree.set(next, degree);
1240
+ if (degree === 0) {
1241
+ queue.push(next);
1242
+ }
1243
+ }
1244
+ }
1245
+ for (const key of componentsByName.keys()) {
1246
+ if (!orderedKeys.includes(key)) {
1247
+ logger.warn(`Unresolved dependency cycle detected for ${key}, appending in current order`);
1248
+ orderedKeys.push(key);
1249
+ }
1250
+ }
1251
+ return orderedKeys.map((key) => componentsByName.get(key)).filter((component) => Boolean(component));
1252
+ }
1253
+
1254
+ // src/utils/diff-utils.ts
1255
+ import { createTwoFilesPatch } from "diff";
1256
+ function buildUnifiedDiff(oldFilePath, newFilePath, oldContent, newContent) {
1257
+ return createTwoFilesPatch(
1258
+ oldFilePath,
1259
+ newFilePath,
1260
+ oldContent,
1261
+ newContent,
1262
+ "local",
1263
+ "registry",
1264
+ { context: 3 }
1265
+ );
1266
+ }
1267
+ function hasDiff(oldContent, newContent) {
1268
+ return oldContent !== newContent;
1269
+ }
1270
+ function formatDiffPreview(diff, maxLines = 80) {
1271
+ const lines = diff.split("\n");
1272
+ if (lines.length <= maxLines) {
1273
+ return diff;
1274
+ }
1275
+ return `${lines.slice(0, maxLines).join("\n")}
1276
+ ...`;
1277
+ }
1278
+
1279
+ // src/utils/transform.ts
1280
+ import path7 from "path";
1281
+ import ts from "typescript";
1282
+ var IMPORT_NODE_KIND = ts.SyntaxKind.ImportDeclaration;
1283
+ function normalizeAliasKey(alias) {
1284
+ return alias.replace(/\\/g, "/").replace(/\/+$/, "");
1285
+ }
1286
+ function toPosix(value) {
1287
+ return value.replace(/\\/g, "/");
1288
+ }
1289
+ function normalizeAliasTarget(alias) {
1290
+ return toPosix(alias).replace(/\/+$/, "");
1291
+ }
1292
+ function normalizeDefaultAliases() {
1293
+ const map = /* @__PURE__ */ new Map();
1294
+ for (const [alias, target] of Object.entries(SCHEMA_CONFIG.defaultAliases)) {
1295
+ map.set(normalizeAliasKey(alias), normalizeAliasTarget(target));
1296
+ }
1297
+ return map;
1298
+ }
1299
+ function normalizeConfiguredAliases(aliasMap) {
1300
+ const normalized = /* @__PURE__ */ new Map();
1301
+ for (const [alias, target] of Object.entries(aliasMap)) {
1302
+ normalized.set(normalizeAliasKey(alias), normalizeAliasTarget(target));
1303
+ }
1304
+ return normalized;
1305
+ }
1306
+ function pickAliasForImport(importPath, configuredAliases) {
1307
+ const pathValue = toPosix(importPath);
1308
+ if (!pathValue.startsWith("@/")) {
1309
+ return void 0;
1310
+ }
1311
+ const trimmed = pathValue.slice(2);
1312
+ const [root] = trimmed.split("/");
1313
+ const rootAlias = `@/${root}`;
1314
+ const directAlias = Array.from(configuredAliases.keys()).filter((alias) => pathValue === alias || pathValue.startsWith(`${alias}/`)).sort((a, b) => b.length - a.length)[0];
1315
+ if (directAlias) {
1316
+ const aliasValue = configuredAliases.get(directAlias);
1317
+ if (!aliasValue || !aliasValue.startsWith("@/")) {
1318
+ return void 0;
1319
+ }
1320
+ const remainder = pathValue.slice(directAlias.length).replace(/^\/+/, "");
1321
+ if (!remainder) {
1322
+ return aliasValue;
1323
+ }
1324
+ const remainderParts = remainder.split("/");
1325
+ const targetParts = normalizeAliasKey(aliasValue).replace(/^@\//, "").split("/");
1326
+ const aliasTail = targetParts[targetParts.length - 1];
1327
+ const normalizedRemainder = remainderParts[0] === aliasTail ? remainderParts.slice(1).join("/") : remainder;
1328
+ return normalizedRemainder ? `${aliasValue}/${normalizedRemainder}` : aliasValue;
1329
+ }
1330
+ const defaultAliasCandidates = Array.from(normalizeDefaultAliases().keys()).filter((alias) => pathValue === alias || pathValue.startsWith(`${alias}/`)).sort((a, b) => b.length - a.length);
1331
+ for (const defaultAlias of defaultAliasCandidates) {
1332
+ const defaultParts = normalizeAliasKey(defaultAlias).replace(/^@\//, "").split("/");
1333
+ const remainderFromDefault = trimmed.split("/").slice(defaultParts.length);
1334
+ if (remainderFromDefault.length === 0) {
1335
+ continue;
1336
+ }
1337
+ const candidateAliasTail = remainderFromDefault[0];
1338
+ const candidateAlias = `@/${candidateAliasTail}`;
1339
+ if (configuredAliases.has(candidateAlias)) {
1340
+ const remainderPath = remainderFromDefault.slice(1).join("/");
1341
+ return remainderPath ? `${candidateAlias}/${remainderPath}` : candidateAlias;
1342
+ }
1343
+ }
1344
+ return void 0;
1345
+ }
1346
+ function rewriteModuleSpecifier(specifierText, configuredAliases) {
1347
+ if (!specifierText.startsWith("@/")) {
1348
+ return specifierText;
1349
+ }
1350
+ const aliasesMap = normalizeConfiguredAliases(configuredAliases);
1351
+ const rewrittenRemainder = pickAliasForImport(specifierText, aliasesMap);
1352
+ if (!rewrittenRemainder || rewrittenRemainder === normalizeAliasKey(specifierText)) {
1353
+ return specifierText;
1354
+ }
1355
+ if (rewrittenRemainder) {
1356
+ return rewrittenRemainder;
1357
+ }
1358
+ return specifierText;
1359
+ }
1360
+ function transformImports(content, aliases) {
1361
+ const sourceFile = ts.createSourceFile("component.tsx", content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
1362
+ const importSpans = [];
1363
+ const configuredAliases = normalizeConfiguredAliases(aliases);
1364
+ function visit(node) {
1365
+ if (node.kind === IMPORT_NODE_KIND && ts.isImportDeclaration(node)) {
1366
+ const moduleSpecifier = node.moduleSpecifier;
1367
+ if (ts.isStringLiteral(moduleSpecifier)) {
1368
+ const value = moduleSpecifier.text;
1369
+ const rewritten = rewriteModuleSpecifier(value, Object.fromEntries(configuredAliases));
1370
+ if (rewritten !== value) {
1371
+ importSpans.push({
1372
+ start: moduleSpecifier.getStart(sourceFile),
1373
+ end: moduleSpecifier.getEnd(),
1374
+ replacement: `"${rewritten}"`
1375
+ });
1376
+ }
1377
+ }
1378
+ }
1379
+ ts.forEachChild(node, visit);
1380
+ }
1381
+ ts.forEachChild(sourceFile, visit);
1382
+ if (importSpans.length === 0) {
1383
+ return content;
1384
+ }
1385
+ importSpans.sort((a, b) => b.start - a.start);
1386
+ let transformed = content;
1387
+ for (const span of importSpans) {
1388
+ transformed = `${transformed.slice(0, span.start)}${span.replacement}${transformed.slice(span.end)}`;
1389
+ }
1390
+ return transformed;
1391
+ }
1392
+ function transformCleanup(content) {
1393
+ return content.replace(/\r\n/g, "\n").replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
1394
+ }
1395
+ function applyTransforms(content, aliases) {
1396
+ const withImports = transformImports(content, aliases);
1397
+ return transformCleanup(withImports);
1398
+ }
1399
+ function shouldTransformFile(fileName) {
1400
+ return [".ts", ".tsx"].includes(path7.extname(fileName));
1401
+ }
1402
+
1403
+ // src/commands/add.ts
1404
+ var ADD_EXCLUDED_COMPONENT_TYPES = ["registry:variants", "registry:lib"];
1405
+ async function addCommand(components, options) {
1406
+ const registryType = resolveRegistryType(options.registry);
1407
+ const requestOptions = {
1408
+ excludeTypes: ADD_EXCLUDED_COMPONENT_TYPES,
1409
+ maxRetries: options.retry ? 3 : 1,
1410
+ noCache: options.cache === false
1411
+ };
1412
+ try {
1413
+ if (options.all || components.includes("all")) {
1414
+ await addAllComponents(options, registryType, requestOptions);
1415
+ return;
1416
+ }
1417
+ const selectedComponents = components.length > 0 ? components : await pickComponentsFromPrompt(registryType, requestOptions);
1418
+ if (selectedComponents.length === 0) {
1419
+ logger.warn(CLI_MESSAGES.errors.noComponentsSpecified);
1420
+ return;
1421
+ }
1422
+ const validation = await validateComponentInstallation(selectedComponents, registryType);
1423
+ if (!validation.isValid) {
1424
+ handleValidationError(validation);
1425
+ }
1426
+ const config = await findConfig(registryType);
1427
+ if (!config) {
1428
+ throw new ConfigNotFoundError(registryType);
1429
+ }
1430
+ if (options.retry) {
1431
+ logger.info(CLI_MESSAGES.info.retryEnabled);
1432
+ }
1433
+ logger.info(CLI_MESSAGES.info.installing(registryType));
1434
+ const getComponentFn = (name, type) => getComponent(name, type, requestOptions);
1435
+ const results = await installRequestedComponents(
1436
+ selectedComponents,
1437
+ registryType,
1438
+ config,
1439
+ getComponentFn,
1440
+ options
1441
+ );
1442
+ displayInstallationSummary(registryType, results);
1443
+ } catch (error) {
1444
+ handleError(error);
1445
+ }
1446
+ }
1447
+ async function addAllComponents(options, registryType, requestOptions) {
1448
+ logger.info(CLI_MESSAGES.info.installingAll(registryType));
1449
+ const validation = await validateComponentInstallation([], registryType);
1450
+ if (!validation.isValid) {
1451
+ handleValidationError(validation);
1452
+ }
1453
+ const config = await findConfig(registryType);
1454
+ if (!config) {
1455
+ throw new ConfigNotFoundError(registryType);
1456
+ }
1457
+ const getAllComponentsFn = (type) => getAllComponents(type, requestOptions);
1458
+ if (options.retry) {
1459
+ logger.info(CLI_MESSAGES.info.retryEnabled);
1460
+ }
1461
+ const spinner = ora4(CLI_MESSAGES.info.fetchingComponentList(registryType)).start();
1462
+ try {
1463
+ const allComponents = await getAllComponentsFn(registryType);
1464
+ if (allComponents.length === 0) {
1465
+ spinner.fail(`No components found in ${registryType} registry`);
1466
+ logger.warn(`
1467
+ \u26A0\uFE0F ${registryType} ${CLI_MESSAGES.errors.registryTempUnavailable}`);
1468
+ console.log("Try these alternatives:");
1469
+ CLI_MESSAGES.examples.troubleshooting.forEach((alt) => console.log(` \u2022 ${alt}`));
1470
+ return;
1471
+ }
1472
+ spinner.succeed(CLI_MESSAGES.status.foundComponents(allComponents.length, registryType));
1473
+ if (options.dryRun) {
1474
+ await installRequestedComponents(
1475
+ allComponents.map((c) => c.name),
1476
+ registryType,
1477
+ config,
1478
+ (name, type) => getComponent(name, type, requestOptions),
1479
+ options,
1480
+ allComponents
1481
+ );
1482
+ return;
1483
+ }
1484
+ const results = await installRequestedComponents(
1485
+ allComponents.map((c) => c.name),
1486
+ registryType,
1487
+ config,
1488
+ (name, type) => getComponent(name, type, requestOptions),
1489
+ options,
1490
+ allComponents
1491
+ );
1492
+ await installComponentsIndex(registryType, config);
1493
+ displayInstallationSummary(registryType, results);
1494
+ } catch (error) {
1495
+ spinner.fail(CLI_MESSAGES.errors.failedToFetch(registryType));
1496
+ logger.error(`Error: ${error.message}`);
1497
+ logger.warn(`
1498
+ \u26A0\uFE0F ${registryType} ${CLI_MESSAGES.errors.registryTempUnavailable}`);
1499
+ console.log("Try these alternatives:");
1500
+ CLI_MESSAGES.examples.troubleshooting.forEach((alt) => console.log(` \u2022 ${alt}`));
1501
+ process.exit(1);
1502
+ }
1503
+ }
1504
+ async function processComponents(componentNames, registryType, config, getComponentFn, options, preloadedComponents, totalCount) {
1505
+ const results = [];
1506
+ const componentMap = new Map(preloadedComponents?.map((c) => [c.name.toLowerCase(), c]));
1507
+ const total = totalCount ?? componentNames.length;
1508
+ if (total > 1) {
1509
+ logger.info(`Installing ${total} components...`);
1510
+ }
1511
+ for (let i = 0; i < componentNames.length; i += 1) {
1512
+ const componentName = componentNames[i];
1513
+ const position = `${i + 1}/${total}`;
1514
+ const spinner = ora4(`[${position}] ${CLI_MESSAGES.status.installing(componentName, registryType)}`).start();
1515
+ try {
1516
+ const lookupName = componentName.toLowerCase();
1517
+ let component = componentMap?.get(lookupName) ?? null;
1518
+ if (!component) {
1519
+ component = await getComponentFn(componentName, registryType);
1520
+ }
1521
+ if (!component) {
1522
+ throw new Error(CLI_MESSAGES.errors.componentNotFound(componentName, registryType));
1523
+ }
1524
+ if (options.dryRun) {
1525
+ spinner.succeed(`[${position}] ${CLI_MESSAGES.status.wouldInstall(component.name, registryType)}`);
1526
+ logger.info(` Type: ${component.type}`);
1527
+ if (component.registryDependencies && component.registryDependencies.length > 0) {
1528
+ logger.info(` Registry deps: ${component.registryDependencies.join(" -> ")}`);
1529
+ }
1530
+ logger.info(` Files: ${component.files.length}`);
1531
+ logger.info(` Dependencies: ${component.dependencies.join(", ") || "none"}`);
1532
+ for (const file of component.files) {
1533
+ const fileName = path8.basename(file.path);
1534
+ const target = file.target || inferTargetFromType(component.type);
1535
+ const installDir = resolveInstallDir(target, config);
1536
+ const targetPath = path8.join(process.cwd(), installDir, fileName);
1537
+ const exists = await fs7.pathExists(targetPath);
1538
+ const status = exists ? "overwrite" : "create";
1539
+ logger.info(` ${status}: ${targetPath}`);
1540
+ if (exists) {
1541
+ const currentContent = await fs7.readFile(targetPath, "utf-8");
1542
+ const transformedIncoming = shouldTransformFile(fileName) ? applyTransforms(file.content, config.aliases) : file.content;
1543
+ const changed = hasDiff(currentContent, transformedIncoming);
1544
+ if (changed) {
1545
+ const patch = buildUnifiedDiff(targetPath, `${component.name}/${fileName}`, currentContent, transformedIncoming);
1546
+ console.log(formatDiffPreview(patch, 40));
1547
+ }
1548
+ }
1549
+ }
1550
+ if (component.dependencies.length > 0) {
1551
+ const depStatus = await checkProjectDependencies(component.dependencies);
1552
+ showDependencyStatus(depStatus);
1553
+ }
1554
+ continue;
1555
+ }
1556
+ await installComponentFiles(component, config, options.force);
1557
+ if (component.dependencies.length > 0) {
1558
+ try {
1559
+ await installDependencies(component.dependencies);
1560
+ } catch (error) {
1561
+ logger.warn(CLI_MESSAGES.errors.couldNotInstallDeps(component.name));
1562
+ logger.warn(` Dependencies: ${component.dependencies.join(", ")}`);
1563
+ logger.warn(" Please install them manually if needed");
1564
+ }
1565
+ }
1566
+ spinner.succeed(`[${position}] ${CLI_MESSAGES.status.installing(component.name, registryType)}`);
1567
+ results.push({ name: component.name, status: "success" });
1568
+ } catch (error) {
1569
+ spinner.fail(`[${position}] ${CLI_MESSAGES.errors.failedToInstall(componentName, registryType)}`);
1570
+ logger.error(` Error: ${error.message}`);
1571
+ results.push({
1572
+ name: componentName,
1573
+ status: "error",
1574
+ error: error.message
1575
+ });
1576
+ }
1577
+ }
1578
+ return results;
1579
+ }
1580
+ async function pickComponentsFromPrompt(registryType, requestOptions) {
1581
+ const allComponents = await getAllComponents(registryType, requestOptions);
1582
+ if (allComponents.length === 0) {
1583
+ logger.warn(`No components found in ${registryType} registry`);
1584
+ return [];
1585
+ }
1586
+ const sorted = allComponents.filter((component) => !ADD_EXCLUDED_COMPONENT_TYPES.includes(component.type)).sort((a, b) => {
1587
+ if (a.type === b.type) {
1588
+ return a.name.localeCompare(b.name);
1589
+ }
1590
+ return a.type.localeCompare(b.type);
1591
+ });
1592
+ const grouped = /* @__PURE__ */ new Map();
1593
+ for (const component of sorted) {
1594
+ const group = grouped.get(component.type) ?? [];
1595
+ group.push(component);
1596
+ if (!grouped.has(component.type)) {
1597
+ grouped.set(component.type, group);
1598
+ }
1599
+ }
1600
+ const choices = [];
1601
+ for (const [type, components] of grouped) {
1602
+ choices.push({
1603
+ title: `
1604
+ ${type}`,
1605
+ value: "__separator__",
1606
+ description: "",
1607
+ disabled: true
1608
+ });
1609
+ for (const component of components) {
1610
+ choices.push({
1611
+ title: component.name,
1612
+ value: component.name,
1613
+ description: component.description || component.type
1614
+ });
1615
+ }
1616
+ }
1617
+ if (choices.length === 0) {
1618
+ logger.warn(`No selectable components found in ${registryType} registry`);
1619
+ return [];
1620
+ }
1621
+ const { selected } = await prompts3({
1622
+ type: "multiselect",
1623
+ name: "selected",
1624
+ message: "Which components would you like to add?",
1625
+ instructions: false,
1626
+ choices,
1627
+ hint: "Space to select, Enter to confirm"
1628
+ });
1629
+ return selected || [];
1630
+ }
1631
+ async function installRequestedComponents(componentNames, registryType, config, getComponentFn, options, preloadedComponents = []) {
1632
+ const componentMap = /* @__PURE__ */ new Map();
1633
+ for (const component of preloadedComponents) {
1634
+ componentMap.set(component.name.toLowerCase(), component);
1635
+ }
1636
+ const resolverGetComponent = async (name, type) => {
1637
+ const normalized = name.toLowerCase();
1638
+ const cached = componentMap.get(normalized);
1639
+ if (cached) {
1640
+ return cached;
1641
+ }
1642
+ const component = await getComponentFn(name, type);
1643
+ if (!component) {
1644
+ return null;
1645
+ }
1646
+ componentMap.set(component.name.toLowerCase(), component);
1647
+ return component;
1648
+ };
1649
+ const orderedComponents = await resolveRegistryTree(
1650
+ componentNames,
1651
+ registryType,
1652
+ (name, type) => resolverGetComponent(name, type)
1653
+ );
1654
+ if (options.dryRun && orderedComponents.length > 0) {
1655
+ logger.info("\n\u{1F4E6} Resolved registry dependency tree:");
1656
+ orderedComponents.forEach((component, index) => {
1657
+ console.log(` ${index + 1}. ${component.name}`);
1658
+ });
1659
+ }
1660
+ const orderedNames = new Set(orderedComponents.map((component) => component.name.toLowerCase()));
1661
+ const normalizedRequested = Array.from(new Set(componentNames.map((name) => name.toLowerCase())));
1662
+ const missingRequested = normalizedRequested.filter((name) => !orderedNames.has(name));
1663
+ const missingResults = missingRequested.map((name) => ({
1664
+ name,
1665
+ status: "error",
1666
+ error: `Component "${name}" was not found in ${registryType} registry`
1667
+ }));
1668
+ const processingResults = await processComponents(
1669
+ orderedComponents.map((component) => component.name),
1670
+ registryType,
1671
+ config,
1672
+ resolverGetComponent,
1673
+ options,
1674
+ orderedComponents,
1675
+ orderedComponents.length
1676
+ );
1677
+ return [...missingResults, ...processingResults];
1678
+ }
1679
+ function displayInstallationSummary(registryType, results) {
1680
+ const successful = results.filter((r) => r.status === "success");
1681
+ const failed = results.filter((r) => r.status === "error");
1682
+ logger.info("\n\u{1F4CA} Installation Summary:");
1683
+ console.log(` Registry: ${registryType}`);
1684
+ console.log(` \u2705 Successful: ${successful.length}`);
1685
+ console.log(` \u274C Failed: ${failed.length}`);
1686
+ if (successful.length > 0) {
1687
+ logger.success(`
1688
+ \u{1F389} ${CLI_MESSAGES.success.componentsInstalled}`);
1689
+ console.log("You can now import and use them in your project.");
1690
+ }
1691
+ if (failed.length > 0) {
1692
+ process.exit(1);
1693
+ }
1694
+ }
1695
+ async function installComponentFiles(component, config, force = false) {
1696
+ for (const file of component.files) {
1697
+ const fileName = path8.basename(file.path);
1698
+ const target = file.target || inferTargetFromType(component.type);
1699
+ const installDir = resolveInstallDir(target, config);
1700
+ const targetPath = path8.join(process.cwd(), installDir, fileName);
1701
+ if (!force && await fs7.pathExists(targetPath)) {
1702
+ console.log(` \u26A0\uFE0F ${CLI_MESSAGES.status.skipped(fileName)}`);
1703
+ continue;
1704
+ }
1705
+ await fs7.ensureDir(path8.dirname(targetPath));
1706
+ const preparedContent = shouldTransformFile(fileName) ? applyTransforms(file.content, config.aliases) : file.content;
1707
+ await fs7.writeFile(targetPath, preparedContent, "utf-8");
1708
+ }
1709
+ }
1710
+ function inferTargetFromType(componentType) {
1711
+ switch (componentType) {
1712
+ case "registry:ui":
1713
+ return "ui";
1714
+ case "registry:composite":
1715
+ return "components";
1716
+ case "registry:block":
1717
+ return "blocks";
1718
+ case "registry:component":
1719
+ return "components";
1720
+ case "registry:layout":
1721
+ return "layouts";
1722
+ case "registry:lib":
1723
+ return "lib";
1724
+ case "registry:variants":
1725
+ return "variants";
1726
+ default:
1727
+ return "components";
1728
+ }
1729
+ }
1730
+ function resolveInstallDir(target, config) {
1731
+ const normalizedTarget = target.replace(/\\/g, "/").replace(/^\/?src\//i, "");
1732
+ if (normalizedTarget === "lib") {
1733
+ return normalizeDir(config.libDir || SCHEMA_CONFIG.defaultDirectories.lib);
1734
+ }
1735
+ if (normalizedTarget === "variants") {
1736
+ return normalizeDir(SCHEMA_CONFIG.defaultDirectories.variants);
1737
+ }
1738
+ const baseComponentsDir = normalizeDir(config.componentsDir || SCHEMA_CONFIG.defaultDirectories.components);
1739
+ if (normalizedTarget.includes("/")) {
1740
+ const parentRoot = baseComponentsDir.replace(/[/\\]components$/i, "") || "src";
1741
+ return path8.join(parentRoot, normalizedTarget).replace(/\\/g, "/");
1742
+ }
1743
+ if (normalizedTarget === "ui")
1744
+ return path8.join(baseComponentsDir, "ui").replace(/\\/g, "/");
1745
+ if (normalizedTarget === "components")
1746
+ return baseComponentsDir;
1747
+ switch (normalizedTarget) {
1748
+ case "blocks":
1749
+ return normalizeDir(SCHEMA_CONFIG.defaultDirectories.blocks);
1750
+ case "layouts":
1751
+ return normalizeDir(SCHEMA_CONFIG.defaultDirectories.layouts);
1752
+ default:
1753
+ return baseComponentsDir;
1754
+ }
1755
+ }
1756
+ function normalizeDir(dir) {
1757
+ return dir.replace(/^\.\//, "").replace(/\\/g, "/");
1758
+ }
1759
+ function resolveRegistryType(registryInput) {
1760
+ if (!registryInput) {
1761
+ return SCHEMA_CONFIG.defaultRegistryType;
1157
1762
  }
1158
- for (const item of variantItems) {
1159
- spinner.text = `Installing variant: ${item.name}...`;
1160
- await installComponentFromRegistry(item.name, "registry:variants", cdnUrls, config);
1763
+ if (SCHEMA_CONFIG.registryTypes.includes(registryInput)) {
1764
+ return registryInput;
1161
1765
  }
1162
- spinner.text = `Installing variants index...`;
1163
- await installVariantsIndex(cdnUrls, config);
1164
- spinner.text = `\u2705 Installed ${libItems.length} utilities and ${variantItems.length} variants`;
1766
+ logger.warn(`\u26A0\uFE0F Unknown registry type: ${registryInput}`);
1767
+ console.log(`Available registries: ${SCHEMA_CONFIG.registryTypes.join(", ")}`);
1768
+ console.log(`Using default: ${SCHEMA_CONFIG.defaultRegistryType}`);
1769
+ return SCHEMA_CONFIG.defaultRegistryType;
1165
1770
  }
1166
- async function installComponentFromRegistry(name, type, cdnUrls, config) {
1167
- const folder = type === "registry:lib" ? "lib" : type === "registry:variants" ? "components/variants" : "components/ui";
1168
- for (const baseUrl of cdnUrls) {
1169
- try {
1170
- const url = `${baseUrl}/${folder}/${name}.json`;
1171
- const response = await fetch4(url);
1172
- if (response.ok) {
1173
- const component = await response.json();
1174
- for (const file of component.files) {
1175
- const fileName = path4.basename(file.path);
1176
- let targetDir;
1177
- if (type === "registry:lib") {
1178
- targetDir = config.libDir;
1179
- } else if (type === "registry:variants") {
1180
- targetDir = SCHEMA_CONFIG.defaultDirectories.variants;
1181
- } else {
1182
- targetDir = path4.join(config.componentsDir, "ui");
1771
+ async function installComponentsIndex(registryType, config) {
1772
+ const spinner = ora4("Installing components index...").start();
1773
+ try {
1774
+ const cdnUrls = SCHEMA_CONFIG.cdnBaseUrls;
1775
+ for (const baseUrl of cdnUrls) {
1776
+ try {
1777
+ const url = `${baseUrl}/components/index.json`;
1778
+ const response = await fetch3(url);
1779
+ if (response.ok) {
1780
+ const component = await response.json();
1781
+ for (const file of component.files) {
1782
+ const fileName = path8.basename(file.path);
1783
+ const targetDir = config.componentsDir;
1784
+ const targetPath = path8.join(process.cwd(), targetDir, fileName);
1785
+ await fs7.ensureDir(path8.dirname(targetPath));
1786
+ await fs7.writeFile(targetPath, file.content || "", "utf-8");
1183
1787
  }
1184
- const targetPath = path4.join(process.cwd(), targetDir, fileName);
1185
- await fs5.ensureDir(path4.dirname(targetPath));
1186
- await fs5.writeFile(targetPath, file.content || "", "utf-8");
1187
- }
1188
- return;
1189
- }
1190
- } catch {
1191
- continue;
1192
- }
1193
- }
1194
- }
1195
- async function installVariantsIndex(cdnUrls, config) {
1196
- for (const baseUrl of cdnUrls) {
1197
- try {
1198
- const url = `${baseUrl}/components/variants/index.json`;
1199
- const response = await fetch4(url);
1200
- if (response.ok) {
1201
- const component = await response.json();
1202
- for (const file of component.files) {
1203
- const fileName = path4.basename(file.path);
1204
- const targetDir = SCHEMA_CONFIG.defaultDirectories.variants;
1205
- const targetPath = path4.join(process.cwd(), targetDir, fileName);
1206
- await fs5.ensureDir(path4.dirname(targetPath));
1207
- await fs5.writeFile(targetPath, file.content || "", "utf-8");
1788
+ spinner.succeed("Installed components index");
1789
+ return;
1208
1790
  }
1209
- return;
1791
+ } catch {
1792
+ continue;
1210
1793
  }
1211
- } catch {
1212
- continue;
1213
1794
  }
1795
+ spinner.info("Components index not found in registry (optional)");
1796
+ } catch (error) {
1797
+ spinner.fail("Could not install components index");
1214
1798
  }
1215
1799
  }
1216
- async function createUtilsFile(libDir, typescript) {
1217
- const utilsContent = `import { type ClassValue, clsx } from "clsx"
1218
- import { twMerge } from "tailwind-merge"
1219
-
1220
- export function cn(...inputs: ClassValue[]) {
1221
- return twMerge(clsx(inputs))
1222
- }`;
1223
- const fileName = typescript ? "utils.ts" : "utils.js";
1224
- const filePath = path4.join(process.cwd(), libDir, fileName);
1225
- await fs5.writeFile(filePath, utilsContent, "utf-8");
1226
- }
1227
1800
 
1228
1801
  // src/commands/build.ts
1229
- import fs6 from "fs-extra";
1230
- import path5 from "path";
1231
- import chalk5 from "chalk";
1232
- import ora3 from "ora";
1802
+ import fs8 from "fs-extra";
1803
+ import path9 from "path";
1804
+ import chalk7 from "chalk";
1805
+ import ora5 from "ora";
1806
+ import * as ts2 from "typescript";
1233
1807
 
1234
1808
  // src/registry/build-schema.ts
1235
1809
  import { z as z2 } from "zod";
@@ -1323,6 +1897,11 @@ function generateConfigSchema() {
1323
1897
  "default": true,
1324
1898
  "description": SCHEMA_CONFIG.fieldDescriptions.typescript
1325
1899
  },
1900
+ "globalCss": {
1901
+ "type": "string",
1902
+ "default": "src/index.css",
1903
+ "description": SCHEMA_CONFIG.fieldDescriptions.globalCss
1904
+ },
1326
1905
  "aliases": {
1327
1906
  "type": "object",
1328
1907
  "additionalProperties": {
@@ -1450,44 +2029,46 @@ function generateRegistryItemSchema() {
1450
2029
  // src/commands/build.ts
1451
2030
  async function buildCommand(registryPath = "./src/registry.json", options = {}) {
1452
2031
  const buildOptions = {
1453
- cwd: path5.resolve(options.cwd || process.cwd()),
1454
- registryFile: path5.resolve(registryPath),
1455
- outputDir: path5.resolve(options.output || "./packages/registry/r")
2032
+ cwd: path9.resolve(options.cwd || process.cwd()),
2033
+ registryFile: path9.resolve(registryPath),
2034
+ outputDir: path9.resolve(options.output || "./packages/registry/r")
1456
2035
  };
1457
- console.log(chalk5.blue(CLI_MESSAGES.info.building));
2036
+ console.log(chalk7.blue(CLI_MESSAGES.info.building));
1458
2037
  try {
1459
- const registryContent = await fs6.readFile(buildOptions.registryFile, "utf-8");
2038
+ await clearCache();
2039
+ resetCache();
2040
+ const registryContent = await fs8.readFile(buildOptions.registryFile, "utf-8");
1460
2041
  const registryData = JSON.parse(registryContent);
1461
2042
  const registry = registrySchema.parse(registryData);
1462
- await fs6.ensureDir(buildOptions.outputDir);
2043
+ await ensureVariantsIndexItem(registry, buildOptions.cwd);
2044
+ await fs8.ensureDir(buildOptions.outputDir);
1463
2045
  await generateSchemaFiles(buildOptions.outputDir);
1464
- const spinner = ora3(CLI_MESSAGES.info.processingComponents).start();
2046
+ const spinner = ora5(CLI_MESSAGES.info.processingComponents).start();
1465
2047
  for (const item of registry.items) {
1466
2048
  spinner.text = `Building ${item.name}...`;
1467
2049
  item.$schema = "https://ui.buildy.tw/schema/registry-item.json";
1468
2050
  for (const file of item.files) {
1469
- const filePath = path5.resolve(buildOptions.cwd, file.path);
1470
- if (await fs6.pathExists(filePath)) {
1471
- file.content = await fs6.readFile(filePath, "utf-8");
2051
+ const filePath = path9.resolve(buildOptions.cwd, file.path);
2052
+ if (await fs8.pathExists(filePath)) {
2053
+ file.content = await fs8.readFile(filePath, "utf-8");
1472
2054
  } else {
1473
2055
  throw new Error(CLI_MESSAGES.errors.fileNotFound(file.path));
1474
2056
  }
1475
2057
  }
1476
2058
  const validatedItem = registryItemSchema.parse(item);
1477
2059
  const typeDir = getOutputDir(validatedItem.type);
1478
- const outputPath = path5.join(buildOptions.outputDir, typeDir);
1479
- await fs6.ensureDir(outputPath);
1480
- const outputFile = path5.join(outputPath, `${validatedItem.name}.json`);
1481
- await fs6.writeFile(outputFile, JSON.stringify(validatedItem, null, 2));
2060
+ const outputPath = path9.join(buildOptions.outputDir, typeDir);
2061
+ await fs8.ensureDir(outputPath);
2062
+ const outputFile = path9.join(outputPath, `${validatedItem.name}.json`);
2063
+ await fs8.writeFile(outputFile, JSON.stringify(validatedItem, null, 2));
1482
2064
  }
1483
2065
  spinner.succeed(CLI_MESSAGES.status.builtComponents(registry.items.length));
1484
2066
  await createIndexFile(registry, buildOptions.outputDir);
1485
- console.log(chalk5.green(`\u2705 ${CLI_MESSAGES.success.registryBuilt}`));
2067
+ console.log(chalk7.green(`\u2705 ${CLI_MESSAGES.success.registryBuilt}`));
1486
2068
  console.log(`Output: ${buildOptions.outputDir}`);
1487
- console.log(chalk5.green(`\u2705 ${CLI_MESSAGES.success.schemasGenerated}`));
2069
+ console.log(chalk7.green(`\u2705 ${CLI_MESSAGES.success.schemasGenerated}`));
1488
2070
  } catch (error) {
1489
- console.error(chalk5.red(`\u274C ${CLI_MESSAGES.errors.buildFailed}`), error.message);
1490
- process.exit(1);
2071
+ handleError(error);
1491
2072
  }
1492
2073
  }
1493
2074
  var BUILD_OUTPUT_FOLDERS = {
@@ -1517,39 +2098,103 @@ async function createIndexFile(registry, outputDir) {
1517
2098
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
1518
2099
  registry: registry?.registry || SCHEMA_CONFIG.defaultRegistryType
1519
2100
  };
1520
- await fs6.writeFile(
1521
- path5.join(outputDir, "index.json"),
2101
+ await fs8.writeFile(
2102
+ path9.join(outputDir, "index.json"),
1522
2103
  JSON.stringify(index, null, 2)
1523
2104
  );
1524
2105
  }
1525
2106
  async function generateSchemaFiles(outputDir) {
1526
- const registryBaseDir = path5.dirname(outputDir);
1527
- const schemaDir = path5.join(registryBaseDir, "schema");
1528
- await fs6.ensureDir(schemaDir);
2107
+ const registryBaseDir = path9.dirname(outputDir);
2108
+ const schemaDir = path9.join(registryBaseDir, "schema");
2109
+ await fs8.ensureDir(schemaDir);
1529
2110
  const configSchemaJson = generateConfigSchema();
1530
2111
  const registrySchemaJson = generateRegistrySchema();
1531
2112
  const registryItemSchemaJson = generateRegistryItemSchema();
1532
- await fs6.writeFile(
1533
- path5.join(registryBaseDir, "schema.json"),
2113
+ await fs8.writeFile(
2114
+ path9.join(registryBaseDir, "schema.json"),
1534
2115
  JSON.stringify(configSchemaJson, null, 2)
1535
2116
  );
1536
- await fs6.writeFile(
1537
- path5.join(schemaDir, "registry.json"),
2117
+ await fs8.writeFile(
2118
+ path9.join(schemaDir, "registry.json"),
1538
2119
  JSON.stringify(registrySchemaJson, null, 2)
1539
2120
  );
1540
- await fs6.writeFile(
1541
- path5.join(schemaDir, "registry-item.json"),
2121
+ await fs8.writeFile(
2122
+ path9.join(schemaDir, "registry-item.json"),
1542
2123
  JSON.stringify(registryItemSchemaJson, null, 2)
1543
2124
  );
1544
2125
  }
2126
+ async function ensureVariantsIndexItem(registry, cwd) {
2127
+ const indexSourcePath = path9.join(cwd, "src/variants/index.ts");
2128
+ if (!await fs8.pathExists(indexSourcePath)) {
2129
+ return;
2130
+ }
2131
+ const sourceContent = await fs8.readFile(indexSourcePath, "utf-8");
2132
+ const dependencies = extractFileDependencies(sourceContent);
2133
+ const exportedModules = extractExportedModules(sourceContent);
2134
+ const indexItem = {
2135
+ type: "registry:variants",
2136
+ name: "index",
2137
+ description: exportedModules.length > 0 ? `Variant exports: ${exportedModules.join(", ")}` : "Variants export index",
2138
+ dependencies,
2139
+ devDependencies: [],
2140
+ files: [
2141
+ {
2142
+ path: path9.relative(cwd, indexSourcePath).replace(/\\/g, "/")
2143
+ }
2144
+ ]
2145
+ };
2146
+ const items = Array.isArray(registry.items) ? registry.items : [];
2147
+ const existingIndexIdx = items.findIndex(
2148
+ (item) => item && item.type === indexItem.type && item.name === indexItem.name
2149
+ );
2150
+ if (existingIndexIdx >= 0) {
2151
+ items[existingIndexIdx] = {
2152
+ ...items[existingIndexIdx],
2153
+ ...indexItem
2154
+ };
2155
+ } else {
2156
+ items.push(indexItem);
2157
+ }
2158
+ }
2159
+ function extractExportedModules(content) {
2160
+ const exports = /* @__PURE__ */ new Set();
2161
+ const starExportRegex = /export\s+\*\s+from\s+['"]\.\/([^'"]+)['"]/g;
2162
+ const namedExportRegex = /export\s+\{[^}]+\}\s+from\s+['"]\.\/([^'"]+)['"]/g;
2163
+ let match;
2164
+ while ((match = starExportRegex.exec(content)) !== null) {
2165
+ exports.add(match[1]);
2166
+ }
2167
+ while ((match = namedExportRegex.exec(content)) !== null) {
2168
+ exports.add(match[1]);
2169
+ }
2170
+ return [...exports];
2171
+ }
2172
+ function extractFileDependencies(content) {
2173
+ const dependencies = /* @__PURE__ */ new Set();
2174
+ const sourceFile = ts2.createSourceFile("index.ts", content, ts2.ScriptTarget.Latest, true);
2175
+ function visit(node) {
2176
+ if (ts2.isImportDeclaration(node)) {
2177
+ const moduleSpecifier = node.moduleSpecifier;
2178
+ if (ts2.isStringLiteral(moduleSpecifier)) {
2179
+ const moduleName = moduleSpecifier.text;
2180
+ if (isExternalDependency(moduleName)) {
2181
+ dependencies.add(moduleName);
2182
+ }
2183
+ }
2184
+ }
2185
+ ts2.forEachChild(node, visit);
2186
+ }
2187
+ visit(sourceFile);
2188
+ return [...dependencies];
2189
+ }
1545
2190
 
1546
2191
  // src/commands/scan.ts
1547
- import fs7 from "fs-extra";
1548
- import path6 from "path";
1549
- import chalk6 from "chalk";
1550
- import ora4 from "ora";
2192
+ import fs9 from "fs-extra";
2193
+ import path10 from "path";
2194
+ import chalk8 from "chalk";
2195
+ import ora6 from "ora";
1551
2196
  import { glob } from "glob";
1552
- import * as ts from "typescript";
2197
+ import * as ts3 from "typescript";
1553
2198
  var DEV_PATTERNS = [
1554
2199
  "@types/",
1555
2200
  "eslint",
@@ -1574,28 +2219,28 @@ async function scanCommand(options = {}) {
1574
2219
  const registryName = options.registry || SCHEMA_CONFIG.defaultRegistryType;
1575
2220
  const registryPath = `./${registryName}`;
1576
2221
  const scanOptions = {
1577
- cwd: path6.resolve(options.cwd || process.cwd()),
1578
- registry: path6.resolve(registryPath),
1579
- outputFile: path6.resolve(options.output || "./src/registry.json"),
1580
- sourceDir: path6.resolve(options.source || "./src")
2222
+ cwd: path10.resolve(options.cwd || process.cwd()),
2223
+ registry: path10.resolve(registryPath),
2224
+ outputFile: path10.resolve(options.output || "./src/registry.json"),
2225
+ sourceDir: path10.resolve(options.source || "./src")
1581
2226
  };
1582
- console.log(chalk6.blue(`\u{1F50D} ${CLI_MESSAGES.info.scanningComponents(registryName)}`));
2227
+ console.log(chalk8.blue(`\u{1F50D} ${CLI_MESSAGES.info.scanningComponents(registryName)}`));
1583
2228
  try {
1584
- const spinner = ora4(CLI_MESSAGES.info.scanningDirectories).start();
1585
- const componentsDir = path6.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.components));
1586
- const uiDir = path6.join(componentsDir, "ui");
1587
- const blocksDir = path6.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.blocks));
1588
- const layoutsDir = path6.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.layouts));
1589
- const libDir = path6.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.lib));
1590
- const variantsDir = path6.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.variants));
2229
+ const spinner = ora6(CLI_MESSAGES.info.scanningDirectories).start();
2230
+ const componentsDir = path10.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.components));
2231
+ const uiDir = path10.join(componentsDir, "ui");
2232
+ const blocksDir = path10.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.blocks));
2233
+ const layoutsDir = path10.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.layouts));
2234
+ const libDir = path10.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.lib));
2235
+ const variantsDir = path10.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.variants));
1591
2236
  const uiComponents = await scanDirectory(uiDir, "registry:ui");
1592
2237
  const compositeComponents = await scanDirectoryFlat(componentsDir, "registry:composite", ["index.ts"]);
1593
2238
  const variantComponents = await scanDirectory(variantsDir, "registry:variants", ["index.ts"]);
1594
2239
  const blockComponents = await scanDirectory(blocksDir, "registry:block");
1595
2240
  const layoutComponents = await scanDirectory(layoutsDir, "registry:layout");
1596
2241
  const libComponents = await scanDirectory(libDir, "registry:lib");
1597
- const variantsIndexItem = await scanSingleFile(path6.join(variantsDir, "index.ts"), "registry:variants");
1598
- const componentsIndexItem = await scanSingleFile(path6.join(componentsDir, "index.ts"), "registry:composite");
2242
+ const variantsIndexItem = await scanSingleFile(path10.join(variantsDir, "index.ts"), "registry:variants");
2243
+ const componentsIndexItem = await scanSingleFile(path10.join(componentsDir, "index.ts"), "registry:composite");
1599
2244
  const allComponentsRaw = [
1600
2245
  ...uiComponents,
1601
2246
  ...compositeComponents,
@@ -1631,16 +2276,16 @@ async function scanCommand(options = {}) {
1631
2276
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
1632
2277
  registry: registryName
1633
2278
  };
1634
- await fs7.ensureDir(path6.dirname(scanOptions.outputFile));
1635
- await fs7.writeFile(scanOptions.outputFile, JSON.stringify(registry, null, 2));
2279
+ await fs9.ensureDir(path10.dirname(scanOptions.outputFile));
2280
+ await fs9.writeFile(scanOptions.outputFile, JSON.stringify(registry, null, 2));
1636
2281
  spinner.succeed(CLI_MESSAGES.status.scannedComponents(allComponents.length));
1637
- console.log(chalk6.green(`\u2705 ${CLI_MESSAGES.success.registryGenerated(registryName)}`));
2282
+ console.log(chalk8.green(`\u2705 ${CLI_MESSAGES.success.registryGenerated(registryName)}`));
1638
2283
  console.log(`Output: ${scanOptions.outputFile}`);
1639
2284
  const summary = allComponents.reduce((acc, comp) => {
1640
2285
  acc[comp.type] = (acc[comp.type] || 0) + 1;
1641
2286
  return acc;
1642
2287
  }, {});
1643
- console.log(chalk6.blue("\n\u{1F4CA} Component Summary:"));
2288
+ console.log(chalk8.blue("\n\u{1F4CA} Component Summary:"));
1644
2289
  Object.entries(summary).forEach(([type, count]) => {
1645
2290
  console.log(` ${type}: ${count}`);
1646
2291
  });
@@ -1650,30 +2295,29 @@ async function scanCommand(options = {}) {
1650
2295
  comp.dependencies.forEach((dep) => allDeps.add(dep));
1651
2296
  comp.devDependencies.forEach((dep) => allDevDeps.add(dep));
1652
2297
  });
1653
- console.log(chalk6.blue("\n\u{1F4E6} Dependencies Summary:"));
2298
+ console.log(chalk8.blue("\n\u{1F4E6} Dependencies Summary:"));
1654
2299
  console.log(` Dependencies: ${allDeps.size} unique (${Array.from(allDeps).join(", ") || "none"})`);
1655
2300
  console.log(` DevDependencies: ${allDevDeps.size} unique (${Array.from(allDevDeps).join(", ") || "none"})`);
1656
2301
  } catch (error) {
1657
- console.error(chalk6.red(`\u274C ${CLI_MESSAGES.errors.scanFailed}`), error.message);
1658
- process.exit(1);
2302
+ handleError(error);
1659
2303
  }
1660
2304
  }
1661
2305
  async function scanDirectory(dirPath, type, ignorePatterns = []) {
1662
- if (!await fs7.pathExists(dirPath)) {
2306
+ if (!await fs9.pathExists(dirPath)) {
1663
2307
  return [];
1664
2308
  }
1665
2309
  const components = [];
1666
- const pattern = path6.join(dirPath, "**/*.{ts,tsx,js,jsx}").replace(/\\/g, "/");
2310
+ const pattern = path10.join(dirPath, "**/*.{ts,tsx,js,jsx}").replace(/\\/g, "/");
1667
2311
  const ignore = ignorePatterns.map((p) => p.replace(/\\/g, "/"));
1668
2312
  const files = await glob(pattern, { windowsPathsNoEscape: true, ignore });
1669
2313
  for (const filePath of files) {
1670
- const relativePath = path6.relative(process.cwd(), filePath).replace(/\\/g, "/");
1671
- const fileName = path6.basename(filePath, path6.extname(filePath));
2314
+ const relativePath = path10.relative(process.cwd(), filePath).replace(/\\/g, "/");
2315
+ const fileName = path10.basename(filePath, path10.extname(filePath));
1672
2316
  if (fileName === "index" || fileName.startsWith("_")) {
1673
2317
  continue;
1674
2318
  }
1675
2319
  try {
1676
- const content = await fs7.readFile(filePath, "utf-8");
2320
+ const content = await fs9.readFile(filePath, "utf-8");
1677
2321
  const description = extractDescription(content);
1678
2322
  if (!hasValidExports(content)) {
1679
2323
  continue;
@@ -1696,20 +2340,20 @@ async function scanDirectory(dirPath, type, ignorePatterns = []) {
1696
2340
  return components;
1697
2341
  }
1698
2342
  async function scanDirectoryFlat(dirPath, type, ignoreFiles = []) {
1699
- if (!await fs7.pathExists(dirPath)) {
2343
+ if (!await fs9.pathExists(dirPath)) {
1700
2344
  return [];
1701
2345
  }
1702
2346
  const components = [];
1703
- const pattern = path6.join(dirPath, "*.{ts,tsx,js,jsx}").replace(/\\/g, "/");
2347
+ const pattern = path10.join(dirPath, "*.{ts,tsx,js,jsx}").replace(/\\/g, "/");
1704
2348
  const files = await glob(pattern, { windowsPathsNoEscape: true });
1705
2349
  for (const filePath of files) {
1706
- const relativePath = path6.relative(process.cwd(), filePath).replace(/\\/g, "/");
1707
- const fileName = path6.basename(filePath, path6.extname(filePath));
1708
- if (ignoreFiles.includes(fileName + path6.extname(filePath)) || fileName.startsWith("_")) {
2350
+ const relativePath = path10.relative(process.cwd(), filePath).replace(/\\/g, "/");
2351
+ const fileName = path10.basename(filePath, path10.extname(filePath));
2352
+ if (ignoreFiles.includes(fileName + path10.extname(filePath)) || fileName.startsWith("_")) {
1709
2353
  continue;
1710
2354
  }
1711
2355
  try {
1712
- const content = await fs7.readFile(filePath, "utf-8");
2356
+ const content = await fs9.readFile(filePath, "utf-8");
1713
2357
  const description = extractDescription(content);
1714
2358
  if (!hasValidExports(content)) {
1715
2359
  continue;
@@ -1732,17 +2376,17 @@ async function scanDirectoryFlat(dirPath, type, ignoreFiles = []) {
1732
2376
  return components;
1733
2377
  }
1734
2378
  async function scanSingleFile(filePath, type) {
1735
- if (!await fs7.pathExists(filePath)) {
2379
+ if (!await fs9.pathExists(filePath)) {
1736
2380
  return null;
1737
2381
  }
1738
2382
  try {
1739
- const content = await fs7.readFile(filePath, "utf-8");
2383
+ const content = await fs9.readFile(filePath, "utf-8");
1740
2384
  const description = extractDescription(content);
1741
2385
  if (!hasValidExports(content)) {
1742
2386
  return null;
1743
2387
  }
1744
- const relativePath = path6.relative(process.cwd(), filePath).replace(/\\/g, "/");
1745
- const fileName = path6.basename(filePath, path6.extname(filePath));
2388
+ const relativePath = path10.relative(process.cwd(), filePath).replace(/\\/g, "/");
2389
+ const fileName = path10.basename(filePath, path10.extname(filePath));
1746
2390
  return {
1747
2391
  name: fileName,
1748
2392
  type,
@@ -1771,7 +2415,17 @@ function extractDescription(content) {
1771
2415
  return "";
1772
2416
  }
1773
2417
  function hasValidExports(content) {
1774
- return /export\s+(default\s+)?(function|const|class|interface|type)/m.test(content) || /export\s*\{/.test(content);
2418
+ const sourceFile = ts3.createSourceFile("index.ts", content, ts3.ScriptTarget.Latest, true);
2419
+ let hasExports = false;
2420
+ function visit(node) {
2421
+ if (ts3.isExportDeclaration(node) || ts3.isExportAssignment(node) || hasExportModifier(node)) {
2422
+ hasExports = true;
2423
+ return;
2424
+ }
2425
+ ts3.forEachChild(node, visit);
2426
+ }
2427
+ visit(sourceFile);
2428
+ return hasExports;
1775
2429
  }
1776
2430
  async function analyzeComponentDependencies(files, cwd) {
1777
2431
  const allDependencies = /* @__PURE__ */ new Set();
@@ -1779,12 +2433,12 @@ async function analyzeComponentDependencies(files, cwd) {
1779
2433
  let description;
1780
2434
  for (const file of files) {
1781
2435
  try {
1782
- const filePath = path6.resolve(cwd, file.path);
1783
- const content = await fs7.readFile(filePath, "utf-8");
1784
- const sourceFile = ts.createSourceFile(
2436
+ const filePath = path10.resolve(cwd, file.path);
2437
+ const content = await fs9.readFile(filePath, "utf-8");
2438
+ const sourceFile = ts3.createSourceFile(
1785
2439
  file.path,
1786
2440
  content,
1787
- ts.ScriptTarget.Latest,
2441
+ ts3.ScriptTarget.Latest,
1788
2442
  true
1789
2443
  );
1790
2444
  const analysis = analyzeAST(sourceFile);
@@ -1809,9 +2463,9 @@ function analyzeAST(sourceFile) {
1809
2463
  let description;
1810
2464
  let hasExports = false;
1811
2465
  function visit(node) {
1812
- if (ts.isImportDeclaration(node)) {
2466
+ if (ts3.isImportDeclaration(node)) {
1813
2467
  const moduleSpecifier = node.moduleSpecifier;
1814
- if (ts.isStringLiteral(moduleSpecifier)) {
2468
+ if (ts3.isStringLiteral(moduleSpecifier)) {
1815
2469
  const moduleName = moduleSpecifier.text;
1816
2470
  if (isExternalDependency(moduleName)) {
1817
2471
  if (isDevDependency(moduleName)) {
@@ -1822,9 +2476,9 @@ function analyzeAST(sourceFile) {
1822
2476
  }
1823
2477
  }
1824
2478
  }
1825
- if (ts.isExportDeclaration(node)) {
2479
+ if (ts3.isExportDeclaration(node)) {
1826
2480
  hasExports = true;
1827
- } else if (ts.isExportAssignment(node)) {
2481
+ } else if (ts3.isExportAssignment(node)) {
1828
2482
  hasExports = true;
1829
2483
  } else if (hasExportModifier(node)) {
1830
2484
  hasExports = true;
@@ -1833,7 +2487,7 @@ function analyzeAST(sourceFile) {
1833
2487
  if (jsDocComment && !description) {
1834
2488
  description = jsDocComment;
1835
2489
  }
1836
- ts.forEachChild(node, visit);
2490
+ ts3.forEachChild(node, visit);
1837
2491
  }
1838
2492
  visit(sourceFile);
1839
2493
  return {
@@ -1849,16 +2503,16 @@ function isDevDependency(moduleName) {
1849
2503
  function hasExportModifier(node) {
1850
2504
  if ("modifiers" in node && node.modifiers) {
1851
2505
  return node.modifiers.some(
1852
- (mod) => mod.kind === ts.SyntaxKind.ExportKeyword
2506
+ (mod) => mod.kind === ts3.SyntaxKind.ExportKeyword
1853
2507
  );
1854
2508
  }
1855
2509
  return false;
1856
2510
  }
1857
2511
  function getJSDocComment(node) {
1858
2512
  try {
1859
- const jsDocTags = ts.getJSDocCommentsAndTags(node);
2513
+ const jsDocTags = ts3.getJSDocCommentsAndTags(node);
1860
2514
  for (const tag of jsDocTags) {
1861
- if (ts.isJSDoc(tag) && tag.comment) {
2515
+ if (ts3.isJSDoc(tag) && tag.comment) {
1862
2516
  if (typeof tag.comment === "string") {
1863
2517
  return tag.comment.trim();
1864
2518
  } else if (Array.isArray(tag.comment)) {
@@ -1878,19 +2532,427 @@ function normalizeDir2(dir) {
1878
2532
  return dir.replace(/^\.\//, "").replace(/\\/g, "/");
1879
2533
  }
1880
2534
 
2535
+ // src/commands/list.ts
2536
+ import chalk9 from "chalk";
2537
+ var LIST_EXCLUDED_COMPONENT_TYPES = ["registry:variants", "registry:lib"];
2538
+ async function listCommand(options = {}) {
2539
+ const registryType = resolveRegistryType2(options.registry);
2540
+ const requestOptions = {
2541
+ excludeTypes: LIST_EXCLUDED_COMPONENT_TYPES,
2542
+ noCache: options.cache === false
2543
+ };
2544
+ try {
2545
+ const components = await getAllComponents(registryType, requestOptions);
2546
+ if (options.json) {
2547
+ console.log(JSON.stringify(components, null, 2));
2548
+ return;
2549
+ }
2550
+ const byType = /* @__PURE__ */ new Map();
2551
+ for (const component of components) {
2552
+ const group = byType.get(component.type) ?? [];
2553
+ group.push(component);
2554
+ byType.set(component.type, group);
2555
+ }
2556
+ const sortedGroups = Array.from(byType.entries()).sort((a, b) => a[0].localeCompare(b[0]));
2557
+ if (sortedGroups.length === 0) {
2558
+ logger.warn(CLI_MESSAGES.errors.registryTempUnavailable);
2559
+ return;
2560
+ }
2561
+ logger.info(CLI_MESSAGES.info.listingComponents);
2562
+ for (const [type, group] of sortedGroups) {
2563
+ const entries = group.sort((a, b) => a.name.localeCompare(b.name));
2564
+ console.log(chalk9.cyan(`${type} (${entries.length} components)`));
2565
+ for (const component of entries) {
2566
+ const description = component.description ? chalk9.dim(component.description) : "";
2567
+ console.log(chalk9.white(` ${component.name.padEnd(14)}`) + description);
2568
+ }
2569
+ console.log("");
2570
+ }
2571
+ } catch (error) {
2572
+ logger.error(error.message);
2573
+ }
2574
+ }
2575
+ function resolveRegistryType2(registryInput) {
2576
+ if (!registryInput) {
2577
+ return SCHEMA_CONFIG.defaultRegistryType;
2578
+ }
2579
+ if (SCHEMA_CONFIG.registryTypes.includes(registryInput)) {
2580
+ return registryInput;
2581
+ }
2582
+ logger.warn(`\u26A0\uFE0F Unknown registry type: ${registryInput}`);
2583
+ logger.warn(`Available registries: ${SCHEMA_CONFIG.registryTypes.join(", ")}`);
2584
+ return SCHEMA_CONFIG.defaultRegistryType;
2585
+ }
2586
+
2587
+ // src/commands/diff.ts
2588
+ import fs10 from "fs-extra";
2589
+ import path11 from "path";
2590
+ import { glob as glob2 } from "glob";
2591
+ import chalk10 from "chalk";
2592
+ async function diffCommand(componentName, options = {}) {
2593
+ try {
2594
+ const registryType = resolveRegistryType3(options.registry);
2595
+ const config = await findConfig(registryType);
2596
+ const defaultConfig = config ?? {
2597
+ framework: SCHEMA_CONFIG.supportedFrameworks[0],
2598
+ typescript: true,
2599
+ globalCss: "src/index.css",
2600
+ aliases: SCHEMA_CONFIG.defaultAliases,
2601
+ registry: SCHEMA_CONFIG.defaultRegistry,
2602
+ componentsDir: SCHEMA_CONFIG.defaultDirectories.components,
2603
+ libDir: SCHEMA_CONFIG.defaultDirectories.lib
2604
+ };
2605
+ const installed = await scanLocalComponents(defaultConfig);
2606
+ if (installed.length === 0) {
2607
+ logger.warn(CLI_MESSAGES.errors.noLocalInstall);
2608
+ return;
2609
+ }
2610
+ const registryComponents = await getAllComponents(registryType, { noCache: options.cache === false });
2611
+ const registryIndex = new Map(registryComponents.map((item) => [item.name.toLowerCase(), item]));
2612
+ const targets = componentName ? installed.filter((item) => item.name.toLowerCase() === componentName.toLowerCase()) : installed;
2613
+ if (componentName && targets.length === 0) {
2614
+ logger.warn(`Component "${componentName}" not found in local project structure`);
2615
+ return;
2616
+ }
2617
+ const results = [];
2618
+ logger.info(CLI_MESSAGES.info.checkingForUpdates);
2619
+ for (const item of targets) {
2620
+ const remoteComponent = registryIndex.get(item.name.toLowerCase());
2621
+ if (!remoteComponent) {
2622
+ results.push({
2623
+ component: item.name,
2624
+ type: "unknown",
2625
+ status: "missing-remote",
2626
+ files: [{ path: item.filePath, changed: false }]
2627
+ });
2628
+ continue;
2629
+ }
2630
+ const fileSummary = await compareComponentFiles(item, remoteComponent, defaultConfig);
2631
+ const hasChanges = fileSummary.some((file) => file.changed);
2632
+ results.push({
2633
+ component: item.name,
2634
+ type: remoteComponent.type,
2635
+ status: hasChanges ? "update" : "up-to-date",
2636
+ files: fileSummary
2637
+ });
2638
+ }
2639
+ if (options.json) {
2640
+ console.log(JSON.stringify(results, null, 2));
2641
+ return;
2642
+ }
2643
+ const updates = results.filter((item) => item.status === "update").length;
2644
+ const upToDate = results.filter((item) => item.status === "up-to-date").length;
2645
+ for (const result of results) {
2646
+ if (result.status === "missing-remote") {
2647
+ logger.warn(`
2648
+ \u26A0\uFE0F ${result.component}: not found in registry`);
2649
+ continue;
2650
+ }
2651
+ const statusTitle = result.status === "update" ? `${chalk10.yellow("UPDATE")}` : chalk10.green("UP-TO-DATE");
2652
+ const title = `${statusTitle} ${result.component} (${result.type})`;
2653
+ logger.info(title);
2654
+ for (const file of result.files) {
2655
+ console.log(` ${chalk10.white(file.path)}`);
2656
+ if (file.changed && file.diff) {
2657
+ const preview = formatDiffPreview(file.diff, 120);
2658
+ console.log(colorDiff(preview));
2659
+ } else {
2660
+ console.log(chalk10.dim(" No changes"));
2661
+ }
2662
+ }
2663
+ }
2664
+ console.log(
2665
+ `
2666
+ ${CLI_MESSAGES.info.localDiffSummary} ${chalk10.yellow(updates)} component(s) have updates, ${chalk10.green(upToDate)} up to date`
2667
+ );
2668
+ if (updates > 0) {
2669
+ console.log('Run "ui8kit add <component> --force" to update.');
2670
+ }
2671
+ } catch (error) {
2672
+ handleError(error);
2673
+ }
2674
+ }
2675
+ async function compareComponentFiles(installed, remote, config) {
2676
+ const localContent = await fs10.readFile(installed.filePath, "utf-8");
2677
+ const remoteCandidate = remote.files.find((file) => {
2678
+ const candidateName = path11.basename(file.path);
2679
+ return candidateName.toLowerCase() === path11.basename(installed.filePath).toLowerCase();
2680
+ });
2681
+ if (!remoteCandidate) {
2682
+ return [{ path: installed.filePath, changed: false }];
2683
+ }
2684
+ const remoteContent = applyTransforms(remoteCandidate.content, config.aliases);
2685
+ const changed = hasDiff(localContent, remoteContent);
2686
+ return changed ? [{
2687
+ path: installed.filePath,
2688
+ changed: true,
2689
+ diff: buildUnifiedDiff(installed.filePath, `${remote.name}/${path11.basename(installed.filePath)}`, localContent, remoteContent)
2690
+ }] : [{ path: installed.filePath, changed: false }];
2691
+ }
2692
+ async function scanLocalComponents(config) {
2693
+ const componentsDir = path11.resolve(process.cwd(), config.componentsDir || SCHEMA_CONFIG.defaultDirectories.components);
2694
+ const componentsUiDir = path11.join(componentsDir, "ui");
2695
+ const blocksDir = path11.resolve(process.cwd(), SCHEMA_CONFIG.defaultDirectories.blocks);
2696
+ const layoutsDir = path11.resolve(process.cwd(), SCHEMA_CONFIG.defaultDirectories.layouts);
2697
+ const directories = [componentsUiDir, componentsDir, blocksDir, layoutsDir];
2698
+ const entries = [];
2699
+ const patterns = directories.map((dir) => path11.join(dir, "*.{ts,tsx}").replace(/\\/g, "/"));
2700
+ for (const pattern of patterns) {
2701
+ const baseDir = path11.dirname(pattern);
2702
+ if (!await fs10.pathExists(baseDir)) {
2703
+ continue;
2704
+ }
2705
+ const filePaths = await glob2(pattern, { windowsPathsNoEscape: true });
2706
+ for (const filePath of filePaths) {
2707
+ const fileName = path11.basename(filePath);
2708
+ if (fileName === "index.tsx" || fileName === "index.ts") {
2709
+ continue;
2710
+ }
2711
+ entries.push({
2712
+ name: path11.parse(fileName).name.toLowerCase(),
2713
+ filePath: path11.resolve(process.cwd(), filePath)
2714
+ });
2715
+ }
2716
+ }
2717
+ const uniqueByName = /* @__PURE__ */ new Map();
2718
+ for (const entry of entries) {
2719
+ if (!uniqueByName.has(entry.name)) {
2720
+ uniqueByName.set(entry.name, entry);
2721
+ }
2722
+ }
2723
+ return Array.from(uniqueByName.values());
2724
+ }
2725
+ function colorDiff(value) {
2726
+ return value.split("\n").map((line) => {
2727
+ if (line.startsWith("+")) {
2728
+ return chalk10.green(line);
2729
+ }
2730
+ if (line.startsWith("-")) {
2731
+ return chalk10.red(line);
2732
+ }
2733
+ return line;
2734
+ }).join("\n");
2735
+ }
2736
+ function resolveRegistryType3(registryInput) {
2737
+ if (!registryInput) {
2738
+ return SCHEMA_CONFIG.defaultRegistryType;
2739
+ }
2740
+ if (SCHEMA_CONFIG.registryTypes.includes(registryInput)) {
2741
+ return registryInput;
2742
+ }
2743
+ logger.warn(`\u26A0\uFE0F Unknown registry type: ${registryInput}`);
2744
+ logger.warn(`Available registries: ${SCHEMA_CONFIG.registryTypes.join(", ")}`);
2745
+ return SCHEMA_CONFIG.defaultRegistryType;
2746
+ }
2747
+
2748
+ // src/commands/cache.ts
2749
+ async function cacheClearCommand() {
2750
+ try {
2751
+ await clearCache();
2752
+ logger.success(`${CLI_MESSAGES.success.cacheCleared} (${getCacheDir()})`);
2753
+ } catch (error) {
2754
+ handleError(error);
2755
+ }
2756
+ }
2757
+
2758
+ // src/commands/info.ts
2759
+ import fs11 from "fs-extra";
2760
+ import os2 from "os";
2761
+ import path12 from "path";
2762
+ import fetch4 from "node-fetch";
2763
+ import chalk11 from "chalk";
2764
+
2765
+ // src/utils/cli-version.ts
2766
+ import { existsSync, readFileSync } from "node:fs";
2767
+ import { dirname, resolve } from "node:path";
2768
+ import { fileURLToPath } from "node:url";
2769
+ var __dirname = dirname(fileURLToPath(import.meta.url));
2770
+ function findPackageJsonPath() {
2771
+ const roots = [process.argv[1], __dirname];
2772
+ for (const rawRoot of roots) {
2773
+ if (!rawRoot) {
2774
+ continue;
2775
+ }
2776
+ let current = rawRoot.endsWith(".js") ? dirname(rawRoot) : rawRoot;
2777
+ for (let i = 0; i < 8; i += 1) {
2778
+ const candidate = resolve(current, "package.json");
2779
+ if (existsSync(candidate)) {
2780
+ return candidate;
2781
+ }
2782
+ const parent = dirname(current);
2783
+ if (parent === current) {
2784
+ break;
2785
+ }
2786
+ current = parent;
2787
+ }
2788
+ }
2789
+ return null;
2790
+ }
2791
+ function getCliVersion() {
2792
+ const packageJsonPath = findPackageJsonPath();
2793
+ if (!packageJsonPath) {
2794
+ return "0.0.0";
2795
+ }
2796
+ try {
2797
+ const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
2798
+ return pkg.version ?? "0.0.0";
2799
+ } catch {
2800
+ return "0.0.0";
2801
+ }
2802
+ }
2803
+
2804
+ // src/commands/info.ts
2805
+ async function infoCommand(options = {}) {
2806
+ const version = getCliVersion();
2807
+ const pm = await detectPackageManager();
2808
+ const cwd = process.cwd();
2809
+ const configStatus = await readConfigStatus();
2810
+ const cache = await readCacheStatus();
2811
+ const cdn = await checkPrimaryCdn();
2812
+ if (options.json) {
2813
+ console.log(JSON.stringify({
2814
+ version,
2815
+ node: process.version,
2816
+ os: `${os2.platform()} ${os2.arch()}`,
2817
+ packageManager: pm,
2818
+ cwd,
2819
+ config: configStatus.config,
2820
+ configFound: configStatus.found,
2821
+ cache,
2822
+ cdn,
2823
+ registry: SCHEMA_CONFIG.defaultRegistry
2824
+ }, null, 2));
2825
+ return;
2826
+ }
2827
+ console.log(`ui8kit v${version}`);
2828
+ console.log(`Node ${process.version}`);
2829
+ console.log(`OS ${os2.platform()} ${os2.arch()}`);
2830
+ console.log(`PM ${pm}`);
2831
+ console.log(`CWD ${cwd}`);
2832
+ console.log("");
2833
+ if (configStatus.found) {
2834
+ console.log(chalk11.green(`Config ${configStatus.path} (found)`));
2835
+ const config = configStatus.config;
2836
+ console.log(` framework ${config.framework}`);
2837
+ console.log(` typescript ${config.typescript}`);
2838
+ console.log(` globalCss ${config.globalCss}`);
2839
+ console.log(` componentsDir ${config.componentsDir}`);
2840
+ console.log(` libDir ${config.libDir}`);
2841
+ } else {
2842
+ console.log(chalk11.yellow("Config not found"));
2843
+ }
2844
+ console.log("");
2845
+ console.log(`Registry ${SCHEMA_CONFIG.defaultRegistry}`);
2846
+ console.log(`CDN ${cdn.url} (${cdn.ok ? "ok" : "failed"})`);
2847
+ console.log(`Cache ${cache.path} (${cache.items} items, ${cache.mb} MB)`);
2848
+ }
2849
+ async function readConfigStatus() {
2850
+ const candidatePaths = [
2851
+ path12.join(process.cwd(), "ui8kit.config.json"),
2852
+ path12.join(process.cwd(), "src", "ui8kit.config.json"),
2853
+ path12.join(process.cwd(), SCHEMA_CONFIG.defaultRegistryType, "ui8kit.config.json")
2854
+ ];
2855
+ for (const configPath of candidatePaths) {
2856
+ if (await fs11.pathExists(configPath)) {
2857
+ try {
2858
+ const config = await fs11.readJson(configPath);
2859
+ return {
2860
+ found: true,
2861
+ path: `./${path12.relative(process.cwd(), configPath).replace(/\\/g, "/")}`,
2862
+ config: {
2863
+ framework: config.framework ?? "unknown",
2864
+ typescript: config.typescript ?? false,
2865
+ globalCss: config.globalCss ?? "src/index.css",
2866
+ componentsDir: config.componentsDir ?? SCHEMA_CONFIG.defaultDirectories.components,
2867
+ libDir: config.libDir ?? SCHEMA_CONFIG.defaultDirectories.lib
2868
+ }
2869
+ };
2870
+ } catch {
2871
+ continue;
2872
+ }
2873
+ }
2874
+ }
2875
+ return {
2876
+ found: false,
2877
+ path: null,
2878
+ config: null
2879
+ };
2880
+ }
2881
+ async function readCacheStatus() {
2882
+ const cachePath = getCacheDir();
2883
+ let items = 0;
2884
+ let bytes = 0;
2885
+ if (await fs11.pathExists(cachePath)) {
2886
+ const result = await countCacheFiles(cachePath);
2887
+ items = result.count;
2888
+ bytes = result.bytes;
2889
+ }
2890
+ return {
2891
+ path: cachePath.replace(/\\/g, "/"),
2892
+ items,
2893
+ mb: `${(bytes / (1024 * 1024)).toFixed(1)}`
2894
+ };
2895
+ }
2896
+ async function countCacheFiles(dirPath) {
2897
+ let count = 0;
2898
+ let size = 0;
2899
+ const entries = await fs11.readdir(dirPath, { withFileTypes: true });
2900
+ for (const entry of entries) {
2901
+ const fullPath = path12.join(dirPath, entry.name);
2902
+ if (entry.isDirectory()) {
2903
+ const nested = await countCacheFiles(fullPath);
2904
+ count += nested.count;
2905
+ size += nested.bytes;
2906
+ continue;
2907
+ }
2908
+ count += 1;
2909
+ const stat = await fs11.stat(fullPath);
2910
+ size += stat.size;
2911
+ }
2912
+ return { count, bytes: size };
2913
+ }
2914
+ async function checkPrimaryCdn() {
2915
+ const url = SCHEMA_CONFIG.cdnBaseUrls[0];
2916
+ try {
2917
+ const response = await fetch4(`${url}/index.json`, { method: "HEAD" });
2918
+ if (response.status >= 200 && response.status < 400) {
2919
+ return { url, ok: true };
2920
+ }
2921
+ } catch {
2922
+ }
2923
+ return { url, ok: false };
2924
+ }
2925
+
1881
2926
  // src/index.ts
2927
+ import { resolve as resolve2 } from "node:path";
1882
2928
  var program = new Command();
1883
- program.name("ui8kit").description("A CLI for adding UI components to your Vite React projects (UI8Kit registry)").version("0.1.0");
2929
+ program.option("-c, --cwd <dir>", "Working directory", process.cwd()).option("-v, --verbose", "Enable verbose output").option("--no-cache", "Bypass registry cache").name("ui8kit").description("A CLI for adding UI components to your Vite React projects (UI8Kit registry)").version(getCliVersion());
2930
+ program.command("list").description("List available components in registry").option("-r, --registry <type>", "Registry type: ui", "ui").option("--json", "Output raw JSON").action((options) => listCommand(options));
2931
+ program.command("diff").description("Show local vs registry differences").argument("[component]", "Component name").option("-r, --registry <type>", "Registry type: ui", "ui").option("--json", "Output diff in machine-readable JSON").action((component, options) => diffCommand(component, options));
2932
+ program.command("cache").description("Manage local cache").command("clear").description("Clear registry cache").action(cacheClearCommand);
2933
+ program.command("info").description("Show environment and config diagnostics").option("--json", "Output diagnostics as JSON").action(infoCommand);
1884
2934
  program.command("init").description("Initialize UI8Kit structure in your project").option("-y, --yes", "Skip prompts and use defaults").option("-r, --registry <type>", "Registry type: ui", "ui").action(initCommand);
1885
- program.command("add").description("Add components to your project from the registry").argument("[components...]", "Components to add").option("-a, --all", "Install all available components").option("-f, --force", "Overwrite existing files").option("-r, --registry <type>", "Registry type: ui", "ui").option("--dry-run", "Show what would be installed without installing").option("--retry", "Enable retry logic for unreliable connections").action(addCommand);
1886
- program.command("scan").description("Scan and generate registry from existing components").option("-r, --registry <type|path>", "Registry type (ui) or custom path", "ui").option("-o, --output <file>", "Output registry file").option("-s, --source <dir>", "Source directory to scan").option("--cwd <dir>", "Working directory").action(async (options) => {
2935
+ program.command("add").description("Add components to your project from the registry").argument("[components...]", "Components to add").option("-a, --all", "Install all available components").option("-f, --force", "Overwrite existing files").option("-r, --registry <type>", "Registry type: ui", "ui").option("--dry-run", "Show what would be installed without installing").option("--retry", "Aggressive retry mode (3 attempts per CDN request)").action(addCommand);
2936
+ program.command("scan").description("Scan and generate registry from existing components").option("-r, --registry <type|path>", "Registry type (ui) or custom path", "ui").option("-o, --output <file>", "Output registry file").option("-s, --source <dir>", "Source directory to scan").action(async (options) => {
1887
2937
  await scanCommand(options);
1888
2938
  });
1889
- program.command("build").description("Build components registry").argument("[registry]", "Path to registry.json file", "./src/registry.json").option("-o, --output <path>", "Output directory", "./packages/registry/r").option("-c, --cwd <cwd>", "Working directory", process.cwd()).action(buildCommand);
2939
+ program.command("build").description("Build components registry").argument("[registry]", "Path to registry.json file", "./src/registry.json").option("-o, --output <path>", "Output directory", "./packages/registry/r").action(buildCommand);
1890
2940
  program.on("command:*", () => {
1891
- console.error(chalk7.red(`Invalid command: ${program.args.join(" ")}`));
2941
+ console.error(chalk12.red(`Invalid command: ${program.args.join(" ")}`));
1892
2942
  console.log("See --help for a list of available commands.");
1893
2943
  process.exit(1);
1894
2944
  });
2945
+ program.hook("preAction", (_, actionCommand) => {
2946
+ const actionOptions = actionCommand?.opts?.();
2947
+ const globalOptions = program.opts();
2948
+ const verbose = globalOptions.verbose || actionOptions?.verbose;
2949
+ const cwd = actionOptions?.cwd || globalOptions.cwd;
2950
+ if (verbose) {
2951
+ logger.setVerbose(true);
2952
+ }
2953
+ if (cwd && resolve2(process.cwd()) !== resolve2(cwd)) {
2954
+ process.chdir(cwd);
2955
+ }
2956
+ });
1895
2957
  program.parse();
1896
2958
  //# sourceMappingURL=index.js.map