@kinetixui/cli 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +162 -20
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import pc4 from "picocolors";
5
+ import pc6 from "picocolors";
6
6
 
7
7
  // src/commands/add.ts
8
8
  import { existsSync as existsSync4 } from "fs";
@@ -19,6 +19,7 @@ import { existsSync } from "fs";
19
19
  import { readFile, writeFile } from "fs/promises";
20
20
  import path from "path";
21
21
  var CONFIG_FILE = "kinetixui.json";
22
+ var DEFAULT_REGISTRY = "https://kinetixui.com/r";
22
23
  var DEFAULT_CONFIG = {
23
24
  $schema: "https://kinetixui.com/schema/config.json",
24
25
  tailwind: { css: "app/globals.css" },
@@ -160,6 +161,17 @@ async function fetchRegistryItem(registryUrl, name) {
160
161
  }
161
162
  return await res.json();
162
163
  }
164
+ async function fetchComponentSpec(registryUrl, name) {
165
+ const base = assertRegistryUrl(registryUrl).replace(/\/$/, "");
166
+ const specsBase = base.endsWith("/r") ? `${base.slice(0, -2)}/specs` : `${base}/specs`;
167
+ const url = `${specsBase}/${assertComponentName(name)}.json`;
168
+ const res = await fetch(url);
169
+ if (res.status === 404) return null;
170
+ if (!res.ok) {
171
+ throw new Error(`Could not load the component spec for "${name}" (${res.status} at ${url}).`);
172
+ }
173
+ return await res.json();
174
+ }
163
175
  async function resolveTree(registryUrl, names) {
164
176
  const resolved = /* @__PURE__ */ new Map();
165
177
  const queue = [...names];
@@ -203,15 +215,88 @@ async function add(names, options) {
203
215
  console.log(pc.green("Done."));
204
216
  }
205
217
 
206
- // src/commands/init.ts
218
+ // src/commands/doctor.ts
207
219
  import { existsSync as existsSync5 } from "fs";
220
+ import { readdir } from "fs/promises";
208
221
  import path5 from "path";
209
222
  import pc2 from "picocolors";
223
+ var ICON = { pass: pc2.green("\u2714"), warn: pc2.yellow("!"), fail: pc2.red("\u2716") };
224
+ function report(level, message) {
225
+ console.log(ICON[level], message);
226
+ return level;
227
+ }
228
+ async function doctor() {
229
+ const cwd = process.cwd();
230
+ const levels = [];
231
+ let config = null;
232
+ if (!existsSync5(path5.join(cwd, CONFIG_FILE))) {
233
+ levels.push(report("fail", `No ${CONFIG_FILE} found \u2014 run "kinetixui init" first.`));
234
+ } else {
235
+ try {
236
+ config = await readConfig(cwd);
237
+ levels.push(report("pass", `${CONFIG_FILE} found and parses.`));
238
+ } catch {
239
+ levels.push(report("fail", `${CONFIG_FILE} exists but isn't valid JSON.`));
240
+ }
241
+ }
242
+ if (config) {
243
+ for (const alias of ["components", "ui", "lib"]) {
244
+ const dir = resolveAliasDir(cwd, config, alias);
245
+ if (existsSync5(dir)) {
246
+ levels.push(report("pass", `"${alias}" alias resolves to ${path5.relative(cwd, dir) || "."}`));
247
+ } else {
248
+ levels.push(report("warn", `"${alias}" alias points to ${path5.relative(cwd, dir) || "."}, which doesn't exist yet.`));
249
+ }
250
+ }
251
+ const cssPath = path5.join(cwd, config.srcDir ? "src" : "", config.tailwind.css);
252
+ if (existsSync5(cssPath)) {
253
+ levels.push(report("pass", `Tailwind CSS file found at ${path5.relative(cwd, cssPath)}`));
254
+ } else {
255
+ levels.push(report("warn", `Tailwind CSS file not found at ${path5.relative(cwd, cssPath)} \u2014 the token contract has nowhere to land.`));
256
+ }
257
+ }
258
+ let registryNames = null;
259
+ try {
260
+ const items = await fetchRegistryIndex(DEFAULT_REGISTRY);
261
+ registryNames = new Set(items.filter((item) => item.type === "registry:ui").map((item) => item.name));
262
+ levels.push(report("pass", `Registry reachable (${DEFAULT_REGISTRY}) \u2014 ${registryNames.size} component(s) listed.`));
263
+ } catch (err) {
264
+ levels.push(report("fail", `Registry unreachable at ${DEFAULT_REGISTRY}: ${err.message}`));
265
+ }
266
+ if (config && registryNames) {
267
+ const uiDir = resolveAliasDir(cwd, config, "ui");
268
+ if (existsSync5(uiDir)) {
269
+ const files = (await readdir(uiDir)).filter((f) => f.endsWith(".tsx"));
270
+ const unknown = files.filter((f) => !registryNames.has(f.replace(/\.tsx$/, "")));
271
+ if (unknown.length === 0) {
272
+ levels.push(report("pass", `Every .tsx file in ${path5.relative(cwd, uiDir)} matches a name in the registry.`));
273
+ } else {
274
+ levels.push(
275
+ report(
276
+ "warn",
277
+ `${unknown.length} file(s) in ${path5.relative(cwd, uiDir)} don't match any current registry name (hand-written, or removed from the registry since you added them): ${unknown.join(", ")}`
278
+ )
279
+ );
280
+ }
281
+ }
282
+ }
283
+ const passed = levels.filter((l) => l === "pass").length;
284
+ const warned = levels.filter((l) => l === "warn").length;
285
+ const failed = levels.filter((l) => l === "fail").length;
286
+ console.log();
287
+ console.log(`${passed} passed, ${warned} warned, ${failed} failed`);
288
+ if (failed > 0) process.exitCode = 1;
289
+ }
290
+
291
+ // src/commands/init.ts
292
+ import { existsSync as existsSync6 } from "fs";
293
+ import path6 from "path";
294
+ import pc3 from "picocolors";
210
295
  import prompts from "prompts";
211
296
  async function init(options) {
212
297
  const cwd = process.cwd();
213
- const configPath = path5.join(cwd, CONFIG_FILE);
214
- if (existsSync5(configPath) && !options.yes) {
298
+ const configPath = path6.join(cwd, CONFIG_FILE);
299
+ if (existsSync6(configPath) && !options.yes) {
215
300
  const { proceed } = await prompts({
216
301
  type: "confirm",
217
302
  name: "proceed",
@@ -223,7 +308,7 @@ async function init(options) {
223
308
  return;
224
309
  }
225
310
  }
226
- const srcDirDetected = existsSync5(path5.join(cwd, "src"));
311
+ const srcDirDetected = existsSync6(path6.join(cwd, "src"));
227
312
  const answers = options.yes ? {
228
313
  srcDir: srcDirDetected,
229
314
  css: DEFAULT_CONFIG.tailwind.css,
@@ -273,38 +358,80 @@ async function init(options) {
273
358
  }
274
359
  };
275
360
  await writeConfig(cwd, config);
276
- console.log(pc2.green("\u2714"), `Created ${CONFIG_FILE}`);
361
+ console.log(pc3.green("\u2714"), `Created ${CONFIG_FILE}`);
277
362
  const items = await resolveTree(options.registry, ["tokens"]);
278
363
  const { written, skipped } = await writeItems(cwd, config, items.values(), false);
279
- for (const file of written) console.log(pc2.green("\u2714"), `Wrote ${file}`);
364
+ for (const file of written) console.log(pc3.green("\u2714"), `Wrote ${file}`);
280
365
  for (const file of skipped) {
281
- console.log(pc2.yellow("!"), `${file} already exists \u2014 merge the token contract in by hand, or delete it and re-run init.`);
366
+ console.log(pc3.yellow("!"), `${file} already exists \u2014 merge the token contract in by hand, or delete it and re-run init.`);
367
+ }
368
+ console.log();
369
+ console.log(`Next: ${pc3.cyan("npx @kinetixui/cli add button")}`);
370
+ console.log(`See everything: ${pc3.cyan("npx @kinetixui/cli list")}`);
371
+ }
372
+
373
+ // src/commands/inspect.ts
374
+ import { existsSync as existsSync7 } from "fs";
375
+ import path7 from "path";
376
+ import pc4 from "picocolors";
377
+ async function inspect(name, options) {
378
+ const item = await fetchRegistryItem(options.registry, name);
379
+ console.log(`${pc4.bold(item.title ?? item.name)} ${pc4.dim(`(${item.name})`)}`);
380
+ if (item.description) console.log(item.description);
381
+ console.log();
382
+ console.log(`${pc4.dim("Type")} ${item.type}`);
383
+ const cwd = process.cwd();
384
+ const config = await readConfig(cwd);
385
+ if (config) {
386
+ const uiDir = resolveAliasDir(cwd, config, "ui");
387
+ const installed = item.files.some(
388
+ (file) => file.type === "registry:ui" && existsSync7(path7.join(uiDir, path7.basename(file.target)))
389
+ );
390
+ console.log(`${pc4.dim("Installed")} ${installed ? pc4.green("yes") : pc4.yellow("no")}`);
391
+ }
392
+ console.log(
393
+ `${pc4.dim("Depends on")} ${item.dependencies?.length ? item.dependencies.join(", ") : pc4.dim("(none)")}`
394
+ );
395
+ console.log(
396
+ `${pc4.dim("Registry")} ${item.registryDependencies?.length ? item.registryDependencies.join(", ") : pc4.dim("(none)")}`
397
+ );
398
+ console.log();
399
+ console.log(pc4.dim("Files:"));
400
+ for (const file of item.files) {
401
+ console.log(` ${file.path} ${pc4.dim("\u2192")} ${file.target}`);
402
+ }
403
+ const spec = await fetchComponentSpec(options.registry, name);
404
+ if (spec) {
405
+ console.log();
406
+ console.log(pc4.dim("Variants:"));
407
+ for (const [axis, options_] of Object.entries(spec.variants)) {
408
+ console.log(` ${pc4.dim(axis)} ${options_.join(", ")}`);
409
+ }
282
410
  }
283
411
  console.log();
284
- console.log(`Next: ${pc2.cyan("npx @kinetixui/cli add button")}`);
285
- console.log(`See everything: ${pc2.cyan("npx @kinetixui/cli list")}`);
412
+ console.log(`Add it: ${pc4.cyan(`npx @kinetixui/cli add ${item.name}`)}`);
286
413
  }
287
414
 
288
415
  // src/commands/list.ts
289
- import pc3 from "picocolors";
416
+ import pc5 from "picocolors";
290
417
  async function list(options) {
291
418
  const items = await fetchRegistryIndex(options.registry);
292
419
  const components = items.filter((item) => item.type === "registry:ui").sort((a, b) => a.name.localeCompare(b.name));
293
420
  console.log(`${components.length} component(s):`);
294
421
  console.log();
295
422
  for (const item of components) {
296
- const description = item.description ? pc3.dim(` \u2014 ${item.description}`) : "";
297
- console.log(` ${pc3.cyan(item.name)}${description}`);
423
+ const description = item.description ? pc5.dim(` \u2014 ${item.description}`) : "";
424
+ console.log(` ${pc5.cyan(item.name)}${description}`);
298
425
  }
299
426
  console.log();
300
- console.log(`Add one: ${pc3.cyan("npx @kinetixui/cli add <name>")}`);
301
- console.log(`Add everything: ${pc3.cyan("npx @kinetixui/cli add --all")}`);
427
+ console.log(`Add one: ${pc5.cyan("npx @kinetixui/cli add <name>")}`);
428
+ console.log(`Add everything: ${pc5.cyan("npx @kinetixui/cli add --all")}`);
302
429
  }
303
430
 
304
431
  // package.json
305
432
  var package_default = {
306
433
  name: "@kinetixui/cli",
307
- version: "0.11.0",
434
+ version: "0.13.0",
308
435
  description: "Add KinetixUI components and tokens to your project.",
309
436
  type: "module",
310
437
  license: "MIT",
@@ -342,14 +469,13 @@ var package_default = {
342
469
  };
343
470
 
344
471
  // src/index.ts
345
- var DEFAULT_REGISTRY = "https://kinetixui.com/r";
346
472
  var program = new Command();
347
473
  program.name("kinetixui").description("Add KinetixUI components and tokens to your project.").version(package_default.version);
348
474
  program.command("init").description("Set up kinetixui.json and pull in the token contract.").option("-y, --yes", "skip prompts and use defaults", false).option("-r, --registry <url>", "registry base URL", DEFAULT_REGISTRY).action(async (opts) => {
349
475
  try {
350
476
  await init({ registry: opts.registry, yes: opts.yes });
351
477
  } catch (err) {
352
- console.error(pc4.red("\u2716"), err.message);
478
+ console.error(pc6.red("\u2716"), err.message);
353
479
  process.exitCode = 1;
354
480
  }
355
481
  });
@@ -357,7 +483,7 @@ program.command("add").description("Add one or more components to your project."
357
483
  try {
358
484
  await add(components, { registry: opts.registry, overwrite: opts.overwrite, all: opts.all });
359
485
  } catch (err) {
360
- console.error(pc4.red("\u2716"), err.message);
486
+ console.error(pc6.red("\u2716"), err.message);
361
487
  process.exitCode = 1;
362
488
  }
363
489
  });
@@ -365,7 +491,23 @@ program.command("list").description("List every component available in the regis
365
491
  try {
366
492
  await list({ registry: opts.registry });
367
493
  } catch (err) {
368
- console.error(pc4.red("\u2716"), err.message);
494
+ console.error(pc6.red("\u2716"), err.message);
495
+ process.exitCode = 1;
496
+ }
497
+ });
498
+ program.command("inspect").description("Show everything the registry knows about one component.").argument("<name>", "component name, e.g. button").option("-r, --registry <url>", "registry base URL", DEFAULT_REGISTRY).action(async (name, opts) => {
499
+ try {
500
+ await inspect(name, { registry: opts.registry });
501
+ } catch (err) {
502
+ console.error(pc6.red("\u2716"), err.message);
503
+ process.exitCode = 1;
504
+ }
505
+ });
506
+ program.command("doctor").description("Check kinetixui.json, its aliases, and the registry connection.").action(async () => {
507
+ try {
508
+ await doctor();
509
+ } catch (err) {
510
+ console.error(pc6.red("\u2716"), err.message);
369
511
  process.exitCode = 1;
370
512
  }
371
513
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kinetixui/cli",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "Add KinetixUI components and tokens to your project.",
5
5
  "type": "module",
6
6
  "license": "MIT",