@kinetixui/cli 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +200 -20
- 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
|
|
5
|
+
import pc7 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/
|
|
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 =
|
|
214
|
-
if (
|
|
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 =
|
|
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,110 @@ async function init(options) {
|
|
|
273
358
|
}
|
|
274
359
|
};
|
|
275
360
|
await writeConfig(cwd, config);
|
|
276
|
-
console.log(
|
|
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(
|
|
364
|
+
for (const file of written) console.log(pc3.green("\u2714"), `Wrote ${file}`);
|
|
280
365
|
for (const file of skipped) {
|
|
281
|
-
console.log(
|
|
366
|
+
console.log(pc3.yellow("!"), `${file} already exists \u2014 merge the token contract in by hand, or delete it and re-run init.`);
|
|
282
367
|
}
|
|
283
368
|
console.log();
|
|
284
|
-
console.log(`Next: ${
|
|
285
|
-
console.log(`See everything: ${
|
|
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
|
+
}
|
|
410
|
+
}
|
|
411
|
+
console.log();
|
|
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
|
|
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 ?
|
|
297
|
-
console.log(` ${
|
|
423
|
+
const description = item.description ? pc5.dim(` \u2014 ${item.description}`) : "";
|
|
424
|
+
console.log(` ${pc5.cyan(item.name)}${description}`);
|
|
425
|
+
}
|
|
426
|
+
console.log();
|
|
427
|
+
console.log(`Add one: ${pc5.cyan("npx @kinetixui/cli add <name>")}`);
|
|
428
|
+
console.log(`Add everything: ${pc5.cyan("npx @kinetixui/cli add --all")}`);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/commands/parity.ts
|
|
432
|
+
import pc6 from "picocolors";
|
|
433
|
+
var PLATFORMS = ["React", "SwiftUI", "Compose", "Flutter"];
|
|
434
|
+
var PLATFORM_ABBR = {
|
|
435
|
+
React: "RE",
|
|
436
|
+
SwiftUI: "SW",
|
|
437
|
+
Compose: "JC",
|
|
438
|
+
Flutter: "FL"
|
|
439
|
+
};
|
|
440
|
+
async function parity(names, options) {
|
|
441
|
+
const items = await fetchRegistryIndex(options.registry);
|
|
442
|
+
const components = items.filter((item) => item.type === "registry:ui").sort((a, b) => a.name.localeCompare(b.name));
|
|
443
|
+
const rows = names.length ? names.map((name) => {
|
|
444
|
+
const item = components.find((c) => c.name === name);
|
|
445
|
+
if (!item) throw new Error(`"${name}" isn't in the registry. Run "kinetixui list" to see what's available.`);
|
|
446
|
+
return item;
|
|
447
|
+
}) : components;
|
|
448
|
+
const nameWidth = Math.max(9, ...rows.map((r) => r.name.length));
|
|
449
|
+
const header = `${"COMPONENT".padEnd(nameWidth)} ${PLATFORMS.map((p) => PLATFORM_ABBR[p]).join(" ")} STATUS`;
|
|
450
|
+
console.log(pc6.dim(header));
|
|
451
|
+
for (const item of rows) {
|
|
452
|
+
const on = new Set(item.platforms ?? PLATFORMS);
|
|
453
|
+
const marks = PLATFORMS.map((p) => on.has(p) ? pc6.green("\u2713 ") : pc6.dim("\u2014 "));
|
|
454
|
+
const status = item.status ? pc6.yellow(item.status) : "";
|
|
455
|
+
console.log(`${item.name.padEnd(nameWidth)} ${marks.join(" ")} ${status}`);
|
|
298
456
|
}
|
|
299
457
|
console.log();
|
|
300
|
-
console.log(
|
|
301
|
-
console.log(`Add everything: ${pc3.cyan("npx @kinetixui/cli add --all")}`);
|
|
458
|
+
console.log(`${rows.length} of ${components.length} component(s) shown.`);
|
|
302
459
|
}
|
|
303
460
|
|
|
304
461
|
// package.json
|
|
305
462
|
var package_default = {
|
|
306
463
|
name: "@kinetixui/cli",
|
|
307
|
-
version: "0.
|
|
464
|
+
version: "0.14.0",
|
|
308
465
|
description: "Add KinetixUI components and tokens to your project.",
|
|
309
466
|
type: "module",
|
|
310
467
|
license: "MIT",
|
|
@@ -342,14 +499,13 @@ var package_default = {
|
|
|
342
499
|
};
|
|
343
500
|
|
|
344
501
|
// src/index.ts
|
|
345
|
-
var DEFAULT_REGISTRY = "https://kinetixui.com/r";
|
|
346
502
|
var program = new Command();
|
|
347
503
|
program.name("kinetixui").description("Add KinetixUI components and tokens to your project.").version(package_default.version);
|
|
348
504
|
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
505
|
try {
|
|
350
506
|
await init({ registry: opts.registry, yes: opts.yes });
|
|
351
507
|
} catch (err) {
|
|
352
|
-
console.error(
|
|
508
|
+
console.error(pc7.red("\u2716"), err.message);
|
|
353
509
|
process.exitCode = 1;
|
|
354
510
|
}
|
|
355
511
|
});
|
|
@@ -357,7 +513,7 @@ program.command("add").description("Add one or more components to your project."
|
|
|
357
513
|
try {
|
|
358
514
|
await add(components, { registry: opts.registry, overwrite: opts.overwrite, all: opts.all });
|
|
359
515
|
} catch (err) {
|
|
360
|
-
console.error(
|
|
516
|
+
console.error(pc7.red("\u2716"), err.message);
|
|
361
517
|
process.exitCode = 1;
|
|
362
518
|
}
|
|
363
519
|
});
|
|
@@ -365,7 +521,31 @@ program.command("list").description("List every component available in the regis
|
|
|
365
521
|
try {
|
|
366
522
|
await list({ registry: opts.registry });
|
|
367
523
|
} catch (err) {
|
|
368
|
-
console.error(
|
|
524
|
+
console.error(pc7.red("\u2716"), err.message);
|
|
525
|
+
process.exitCode = 1;
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
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) => {
|
|
529
|
+
try {
|
|
530
|
+
await inspect(name, { registry: opts.registry });
|
|
531
|
+
} catch (err) {
|
|
532
|
+
console.error(pc7.red("\u2716"), err.message);
|
|
533
|
+
process.exitCode = 1;
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
program.command("parity").description("Show which platforms carry each component (or one, by name).").argument("[components...]", "component names, e.g. button card \u2014 omit for the full matrix").option("-r, --registry <url>", "registry base URL", DEFAULT_REGISTRY).action(async (components, opts) => {
|
|
537
|
+
try {
|
|
538
|
+
await parity(components, { registry: opts.registry });
|
|
539
|
+
} catch (err) {
|
|
540
|
+
console.error(pc7.red("\u2716"), err.message);
|
|
541
|
+
process.exitCode = 1;
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
program.command("doctor").description("Check kinetixui.json, its aliases, and the registry connection.").action(async () => {
|
|
545
|
+
try {
|
|
546
|
+
await doctor();
|
|
547
|
+
} catch (err) {
|
|
548
|
+
console.error(pc7.red("\u2716"), err.message);
|
|
369
549
|
process.exitCode = 1;
|
|
370
550
|
}
|
|
371
551
|
});
|