@tokiui/cli 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +126 -20
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -189,20 +189,34 @@ var initCommand = new import_commander.Command("init").description("Initialize t
189
189
  spinner.start("Setting up global CSS");
190
190
  const globalsAbsPath = import_path2.default.join(cwd, answers.globalsPath);
191
191
  await import_fs_extra2.default.ensureDir(import_path2.default.dirname(globalsAbsPath));
192
- let css = import_fs_extra2.default.existsSync(globalsAbsPath) ? await import_fs_extra2.default.readFile(globalsAbsPath, "utf-8") : "";
193
- const prepend = [];
194
- if (!css.includes('@import "tailwindcss"') && !css.includes("@import 'tailwindcss'")) {
195
- prepend.push('@import "tailwindcss";');
196
- }
197
- if (!css.includes("@tokiui/ui/styles.css")) {
198
- prepend.push('@import "@tokiui/ui/styles.css";');
199
- }
200
- if (prepend.length > 0) {
201
- css = prepend.join("\n") + "\n\n" + css;
202
- await import_fs_extra2.default.writeFile(globalsAbsPath, css);
203
- spinner.succeed(`globals.css updated (${answers.globalsPath})`);
204
- } else {
192
+ const TOKIUI_CSS = '@import "tailwindcss";\n@import "@tokiui/ui/styles.css";\n';
193
+ const existingCss = import_fs_extra2.default.existsSync(globalsAbsPath) ? await import_fs_extra2.default.readFile(globalsAbsPath, "utf-8") : "";
194
+ if (existingCss.includes("@tokiui/ui/styles.css")) {
205
195
  spinner.succeed("globals.css already configured");
196
+ } else if (!existingCss.trim()) {
197
+ await import_fs_extra2.default.writeFile(globalsAbsPath, TOKIUI_CSS);
198
+ spinner.succeed(`globals.css created (${answers.globalsPath})`);
199
+ } else {
200
+ const isStarterTheme = /@media\s*\(\s*prefers-color-scheme\s*:\s*dark\s*\)/.test(existingCss) || /@theme\s+inline/.test(existingCss);
201
+ if (isStarterTheme) {
202
+ await import_fs_extra2.default.writeFile(globalsAbsPath + ".bak", existingCss);
203
+ await import_fs_extra2.default.writeFile(globalsAbsPath, TOKIUI_CSS);
204
+ spinner.succeed("globals.css set up \u2014 tokiui now owns the theme");
205
+ console.log(
206
+ import_kleur.default.dim(` Replaced the starter theme (saved a copy to ${import_path2.default.basename(globalsAbsPath)}.bak).`)
207
+ );
208
+ } else {
209
+ const lead = [];
210
+ if (!existingCss.includes('@import "tailwindcss"') && !existingCss.includes("@import 'tailwindcss'")) {
211
+ lead.push('@import "tailwindcss";');
212
+ }
213
+ lead.push('@import "@tokiui/ui/styles.css";');
214
+ await import_fs_extra2.default.writeFile(globalsAbsPath, lead.join("\n") + "\n\n" + existingCss);
215
+ spinner.succeed(`globals.css updated (${answers.globalsPath})`);
216
+ console.log(
217
+ import_kleur.default.dim(" Note: remove any :root / @theme / prefers-color-scheme rules that conflict with tokiui tokens.")
218
+ );
219
+ }
206
220
  }
207
221
  spinner.start("Checking TypeScript path aliases");
208
222
  const tsconfigPath = import_path2.default.join(cwd, "tsconfig.json");
@@ -266,11 +280,12 @@ function resolveRegistryRef() {
266
280
  }
267
281
  return envRef;
268
282
  }
269
- return `cli-v${"0.2.1"}`;
283
+ return `cli-v${"0.3.1"}`;
270
284
  }
271
285
  var REGISTRY_REF = resolveRegistryRef();
272
286
  var REGISTRY_BASE = `${ALLOWED_FETCH_ORIGIN}${REGISTRY_REF}/packages/registry`;
273
287
  var SOURCE_BASE = `${ALLOWED_FETCH_ORIGIN}${REGISTRY_REF}/packages/ui/src`;
288
+ var BLOCK_BASE = `${ALLOWED_FETCH_ORIGIN}${REGISTRY_REF}/apps/docs/src/registry/blocks`;
274
289
  async function safeFetch(url) {
275
290
  if (!url.startsWith(ALLOWED_FETCH_ORIGIN)) {
276
291
  throw new Error(
@@ -358,6 +373,31 @@ async function fetchComponentSource(name, file) {
358
373
  if (!res.ok) throw new Error(`Failed to fetch source for ${name}/${file}`);
359
374
  return res.text();
360
375
  }
376
+ function assertRegistryBlock(data) {
377
+ const obj = data;
378
+ const valid = typeof data === "object" && data !== null && typeof obj.name === "string" && Array.isArray(obj.files) && Array.isArray(obj.dependencies) && Array.isArray(obj.registryDependencies) && obj.files.every((f) => typeof f === "string") && obj.dependencies.every((d) => typeof d === "string") && obj.registryDependencies.every((d) => typeof d === "string");
379
+ if (!valid) {
380
+ throw new Error(
381
+ `Received an invalid block definition for "${String(obj.name ?? "unknown")}" from the registry.`
382
+ );
383
+ }
384
+ }
385
+ async function fetchBlock(name) {
386
+ assertSafeComponentName(name);
387
+ const res = await safeFetch(`${BLOCK_BASE}/${name}/block.json`);
388
+ if (res.status === 404) return null;
389
+ if (!res.ok) throw new Error(`Failed to fetch block "${name}": ${res.statusText}`);
390
+ const data = await res.json();
391
+ assertRegistryBlock(data);
392
+ return data;
393
+ }
394
+ async function fetchBlockSource(name, file) {
395
+ assertSafeComponentName(name);
396
+ assertSafeFilePath(file);
397
+ const res = await safeFetch(`${BLOCK_BASE}/${name}/${file}`);
398
+ if (!res.ok) throw new Error(`Failed to fetch source for block ${name}/${file}`);
399
+ return res.text();
400
+ }
361
401
 
362
402
  // src/utils/transforms.ts
363
403
  function transformImports(source, libDir) {
@@ -370,14 +410,14 @@ var addCommand = new import_commander2.Command("add").description("Add a compone
370
410
  const config = getConfig(cwd);
371
411
  if (!config) {
372
412
  console.log(
373
- import_kleur2.default.red("No tokiui.json found. Run `npx tokiui init` first.")
413
+ import_kleur2.default.red("No tokiui.json found. Run `npx @tokiui/cli init` first.")
374
414
  );
375
415
  process.exit(1);
376
416
  }
377
417
  const libAlias = config.libAlias ?? `@/${config.libDir}`;
378
418
  let componentName = componentArg;
379
419
  if (!componentName) {
380
- const spinner = (0, import_ora2.default)("Fetching component list...").start();
420
+ const spinner = (0, import_ora2.default)("Fetching registry...").start();
381
421
  const index = await fetchRegistryIndex().catch(() => {
382
422
  spinner.fail("Failed to fetch registry");
383
423
  process.exit(1);
@@ -386,8 +426,11 @@ var addCommand = new import_commander2.Command("add").description("Add a compone
386
426
  const answer = await (0, import_prompts2.default)({
387
427
  type: "multiselect",
388
428
  name: "components",
389
- message: "Which components would you like to add?",
390
- choices: index.components.map((c) => ({ title: c.label, value: c.name })),
429
+ message: "Which components or blocks would you like to add?",
430
+ choices: [
431
+ ...index.components.map((c) => ({ title: c.label, value: c.name })),
432
+ ...(index.blocks ?? []).map((b) => ({ title: `${b.label} \xB7 block`, value: b.name }))
433
+ ],
391
434
  min: 1
392
435
  });
393
436
  if (!answer.components?.length) {
@@ -396,12 +439,20 @@ var addCommand = new import_commander2.Command("add").description("Add a compone
396
439
  }
397
440
  const visited = /* @__PURE__ */ new Set();
398
441
  for (const name of answer.components) {
399
- await installComponent(name, cwd, config.componentsDir, libAlias, visited);
442
+ await install(name, cwd, config.componentsDir, libAlias, visited);
400
443
  }
401
444
  return;
402
445
  }
403
- await installComponent(componentName, cwd, config.componentsDir, libAlias, /* @__PURE__ */ new Set());
446
+ await install(componentName, cwd, config.componentsDir, libAlias, /* @__PURE__ */ new Set());
404
447
  });
448
+ async function install(name, cwd, componentsDir, libAlias, visited) {
449
+ const block = await fetchBlock(name).catch(() => null);
450
+ if (block) {
451
+ await installBlock(block, cwd, componentsDir, libAlias, visited);
452
+ return;
453
+ }
454
+ await installComponent(name, cwd, componentsDir, libAlias, visited);
455
+ }
405
456
  async function installComponent(name, cwd, componentsDir, libAlias, visited) {
406
457
  if (visited.has(name)) return;
407
458
  visited.add(name);
@@ -457,6 +508,61 @@ async function installComponent(name, cwd, componentsDir, libAlias, visited) {
457
508
  }
458
509
  spinner.succeed(`Added ${import_kleur2.default.bold(name)}`);
459
510
  }
511
+ async function installBlock(block, cwd, componentsDir, libAlias, visited) {
512
+ const spinner = (0, import_ora2.default)(`Adding block ${block.name}...`).start();
513
+ if (block.registryDependencies.length > 0) {
514
+ spinner.stop();
515
+ for (const dep of block.registryDependencies) {
516
+ await installComponent(dep, cwd, componentsDir, libAlias, visited);
517
+ }
518
+ spinner.start(`Adding block ${block.name}...`);
519
+ }
520
+ const blockDir = import_path3.default.join(import_path3.default.dirname(componentsDir), "blocks", block.name);
521
+ for (const file of block.files) {
522
+ try {
523
+ assertSafeFilePath(file);
524
+ } catch (err) {
525
+ spinner.fail(`Security: ${err instanceof Error ? err.message : String(err)}`);
526
+ process.exit(1);
527
+ }
528
+ const source = await fetchBlockSource(block.name, file);
529
+ const transformed = transformImports(source, libAlias);
530
+ const destPath = import_path3.default.join(cwd, blockDir, file);
531
+ if (import_fs_extra3.default.existsSync(destPath)) {
532
+ spinner.stop();
533
+ const { overwrite } = await (0, import_prompts2.default)({
534
+ type: "confirm",
535
+ name: "overwrite",
536
+ message: `${import_path3.default.join(blockDir, file)} already exists. Overwrite?`,
537
+ initial: false
538
+ });
539
+ if (!overwrite) {
540
+ console.log(import_kleur2.default.dim(`Skipped ${file}`));
541
+ spinner.start(`Adding block ${block.name}...`);
542
+ continue;
543
+ }
544
+ spinner.start(`Adding block ${block.name}...`);
545
+ }
546
+ await import_fs_extra3.default.ensureDir(import_path3.default.dirname(destPath));
547
+ await import_fs_extra3.default.writeFile(destPath, transformed);
548
+ }
549
+ if (block.dependencies.length > 0) {
550
+ for (const dep of block.dependencies) {
551
+ try {
552
+ assertSafeDependency(dep);
553
+ } catch (err) {
554
+ spinner.fail(`Security: ${err instanceof Error ? err.message : String(err)}`);
555
+ process.exit(1);
556
+ }
557
+ }
558
+ console.log(import_kleur2.default.dim(` Installing: ${block.dependencies.join(", ")}`));
559
+ const pm = detectPackageManager(cwd);
560
+ const installCmd = pm === "npm" ? "install" : "add";
561
+ await (0, import_execa2.execa)(pm, [installCmd, ...block.dependencies], { cwd });
562
+ }
563
+ spinner.succeed(`Added block ${import_kleur2.default.bold(block.name)} \u2192 ${blockDir}/`);
564
+ console.log(import_kleur2.default.dim(` Import ${import_path3.default.join(blockDir, "page.tsx")} into a route to use it.`));
565
+ }
460
566
 
461
567
  // src/commands/theme.ts
462
568
  var import_commander3 = require("commander");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokiui/cli",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "CLI for adding tokiui components to your project",
5
5
  "license": "MIT",
6
6
  "author": "Topher Gacad",