@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.
- package/dist/index.js +126 -20
- 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
|
-
|
|
193
|
-
const
|
|
194
|
-
if (
|
|
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.
|
|
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
|
|
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:
|
|
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
|
|
442
|
+
await install(name, cwd, config.componentsDir, libAlias, visited);
|
|
400
443
|
}
|
|
401
444
|
return;
|
|
402
445
|
}
|
|
403
|
-
await
|
|
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");
|