@beaket/ui 0.1.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/bin/cli.mjs ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from "child_process";
3
+ import { fileURLToPath } from "url";
4
+ import { dirname, join } from "path";
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ const script = join(__dirname, "..", "src", "index.ts");
8
+
9
+ spawnSync("npx", ["tsx", script, ...process.argv.slice(2)], {
10
+ stdio: "inherit",
11
+ shell: true,
12
+ });
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@beaket/ui",
3
+ "version": "0.1.0",
4
+ "description": "CLI for adding Beaket UI components to your project",
5
+ "type": "module",
6
+ "bin": {
7
+ "ui": "./bin/cli.mjs"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "src"
12
+ ],
13
+ "keywords": [
14
+ "react",
15
+ "components",
16
+ "ui",
17
+ "tailwindcss",
18
+ "cli"
19
+ ],
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/beaket/ui"
24
+ },
25
+ "dependencies": {
26
+ "commander": "13.1.0",
27
+ "fs-extra": "11.3.0",
28
+ "picocolors": "1.1.1",
29
+ "prompts": "2.4.2"
30
+ }
31
+ }
@@ -0,0 +1,58 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import pc from "picocolors";
4
+ import { getConfig } from "../utils/config.ts";
5
+ import { writeComponentFiles } from "../utils/files.ts";
6
+ import { fetchComponent, fetchRegistry } from "../utils/registry.ts";
7
+
8
+ export async function add(componentName: string) {
9
+ console.log();
10
+
11
+ // Read config
12
+ const config = await getConfig();
13
+ if (!config) {
14
+ console.log(pc.red("Error:"), "beaket.json not found.");
15
+ console.log("Run", pc.cyan("npx @beaket/ui init"), "first.");
16
+ process.exit(1);
17
+ }
18
+
19
+ // Fetch registry
20
+ console.log(`Adding ${pc.cyan(componentName)}...`);
21
+
22
+ const registry = await fetchRegistry();
23
+ const componentDef = registry.components.find((c) => c.name === componentName);
24
+
25
+ if (!componentDef) {
26
+ console.log(pc.red("Error:"), `Component "${componentName}" not found.`);
27
+ console.log();
28
+ console.log("Available components:");
29
+ registry.components.forEach((c) => {
30
+ console.log(` - ${c.name}`);
31
+ });
32
+ process.exit(1);
33
+ }
34
+
35
+ // Fetch component files
36
+ const files = await fetchComponent(componentDef);
37
+
38
+ // Write files
39
+ const componentsDir = path.join(process.cwd(), config.paths.components);
40
+ await writeComponentFiles(componentsDir, componentName, files, config);
41
+
42
+ console.log(pc.green("✓"), `Added ${componentName}`);
43
+ console.log();
44
+ console.log("Import it in your code:");
45
+ console.log(
46
+ pc.cyan(
47
+ ` import { ${pascalCase(componentName)} } from "${config.aliases.components}/${componentName}";`,
48
+ ),
49
+ );
50
+ console.log();
51
+ }
52
+
53
+ function pascalCase(str: string): string {
54
+ return str
55
+ .split("-")
56
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
57
+ .join("");
58
+ }
@@ -0,0 +1,134 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import pc from "picocolors";
4
+ import prompts from "prompts";
5
+ import { writeConfig, type BeaketConfig } from "../utils/config.ts";
6
+ import { installDependencies } from "../utils/files.ts";
7
+
8
+ const CSS_VARIABLES = `
9
+ /* Beaket UI Design System */
10
+ :root {
11
+ --branch: #1C1F24;
12
+ --ink: #0D0D0D;
13
+ --paper: #F8F8F8;
14
+ --steel: #6B6B6B;
15
+ --chrome: #C8C8C8;
16
+ --graphite: #1A1A1A;
17
+ --iron: #2D2D2D;
18
+ --slate: #404040;
19
+ --zinc: #525252;
20
+ --aluminum: #9E9E9E;
21
+ --silver: #DEDEDE;
22
+ --platinum: #F0F0F0;
23
+ --frost: #F5F5F5;
24
+ --signal-blue: #2B6CB0;
25
+ --signal-red: #D32F2F;
26
+ --signal-green: #137752;
27
+ --signal-amber: #A86800;
28
+ --signal-purple: #6F2DA8;
29
+ --signal-cyan: #1A6B7C;
30
+ }
31
+ `;
32
+
33
+ export async function init() {
34
+ console.log();
35
+ console.log(pc.bold("Initializing Beaket UI..."));
36
+ console.log();
37
+
38
+ const response = await prompts([
39
+ {
40
+ type: "text",
41
+ name: "componentsDir",
42
+ message: "Where would you like to install components?",
43
+ initial: "src/components",
44
+ },
45
+ {
46
+ type: "text",
47
+ name: "utilsDir",
48
+ message: "Where would you like to install utilities (cn, etc.)?",
49
+ initial: "src/lib",
50
+ },
51
+ {
52
+ type: "text",
53
+ name: "tailwindCss",
54
+ message: "Where is your Tailwind CSS file?",
55
+ initial: "src/styles.css",
56
+ },
57
+ {
58
+ type: "text",
59
+ name: "componentsAlias",
60
+ message: "Components import alias",
61
+ initial: "@/components",
62
+ },
63
+ {
64
+ type: "text",
65
+ name: "utilsAlias",
66
+ message: "Utils import alias",
67
+ initial: "@/lib",
68
+ },
69
+ ]);
70
+
71
+ if (!response.componentsDir) {
72
+ console.log(pc.red("Cancelled."));
73
+ process.exit(1);
74
+ }
75
+
76
+ const config: BeaketConfig = {
77
+ $schema: "https://beaket.dev/schema.json",
78
+ tailwind: {
79
+ css: response.tailwindCss,
80
+ },
81
+ aliases: {
82
+ components: response.componentsAlias,
83
+ utils: response.utilsAlias,
84
+ },
85
+ paths: {
86
+ components: response.componentsDir,
87
+ utils: response.utilsDir,
88
+ },
89
+ };
90
+
91
+ // Write beaket.json
92
+ await writeConfig(config);
93
+ console.log(pc.green("✓"), "Created beaket.json");
94
+
95
+ // Inject CSS variables into Tailwind CSS file
96
+ const cssPath = path.join(process.cwd(), response.tailwindCss);
97
+ if (await fs.pathExists(cssPath)) {
98
+ const cssContent = await fs.readFile(cssPath, "utf-8");
99
+ if (!cssContent.includes("Beaket UI Design System")) {
100
+ await fs.writeFile(cssPath, cssContent + CSS_VARIABLES);
101
+ console.log(pc.green("✓"), `Added CSS variables to ${response.tailwindCss}`);
102
+ }
103
+ } else {
104
+ console.log(pc.yellow("!"), `CSS file not found: ${response.tailwindCss}`);
105
+ }
106
+
107
+ // Create utils directory and cn function
108
+ const utilsDir = path.join(process.cwd(), response.utilsDir);
109
+ await fs.ensureDir(utilsDir);
110
+
111
+ const cnContent = `import { type ClassValue, clsx } from "clsx";
112
+ import { twMerge } from "tailwind-merge";
113
+
114
+ export function cn(...inputs: ClassValue[]) {
115
+ return twMerge(clsx(inputs));
116
+ }
117
+ `;
118
+
119
+ await fs.writeFile(path.join(utilsDir, "utils.ts"), cnContent);
120
+ console.log(pc.green("✓"), `Created ${response.utilsDir}/utils.ts`);
121
+
122
+ // Install dependencies
123
+ console.log();
124
+ console.log("Installing dependencies...");
125
+ await installDependencies(["clsx", "tailwind-merge"]);
126
+ console.log(pc.green("✓"), "Installed clsx, tailwind-merge");
127
+
128
+ console.log();
129
+ console.log(pc.green("Done!"), "Beaket UI is ready.");
130
+ console.log();
131
+ console.log("You can now add components:");
132
+ console.log(pc.cyan(" npx @beaket/ui add button"));
133
+ console.log();
134
+ }
package/src/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env -S npx tsx
2
+ import { Command } from "commander";
3
+ import { add } from "./commands/add.ts";
4
+ import { init } from "./commands/init.ts";
5
+
6
+ const program = new Command();
7
+
8
+ program
9
+ .name("@beaket/ui")
10
+ .description("CLI for adding Beaket UI components to your project")
11
+ .version("0.1.0");
12
+
13
+ program.command("init").description("Initialize Beaket UI in your project").action(init);
14
+
15
+ program
16
+ .command("add")
17
+ .description("Add a component to your project")
18
+ .argument("<component>", "Component name to add")
19
+ .action(add);
20
+
21
+ program.parse();
@@ -0,0 +1,35 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+
4
+ export interface BeaketConfig {
5
+ $schema?: string;
6
+ tailwind: {
7
+ css: string;
8
+ };
9
+ aliases: {
10
+ components: string;
11
+ utils: string;
12
+ };
13
+ paths: {
14
+ components: string;
15
+ utils: string;
16
+ };
17
+ }
18
+
19
+ const CONFIG_FILE = "beaket.json";
20
+
21
+ export async function getConfig(): Promise<BeaketConfig | null> {
22
+ const configPath = path.join(process.cwd(), CONFIG_FILE);
23
+
24
+ if (!(await fs.pathExists(configPath))) {
25
+ return null;
26
+ }
27
+
28
+ const content = await fs.readFile(configPath, "utf-8");
29
+ return JSON.parse(content) as BeaketConfig;
30
+ }
31
+
32
+ export async function writeConfig(config: BeaketConfig): Promise<void> {
33
+ const configPath = path.join(process.cwd(), CONFIG_FILE);
34
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2));
35
+ }
@@ -0,0 +1,66 @@
1
+ import { spawn } from "child_process";
2
+ import fs from "fs-extra";
3
+ import path from "path";
4
+ import type { BeaketConfig } from "./config.ts";
5
+ import type { ComponentFile } from "./registry.ts";
6
+
7
+ export async function writeComponentFiles(
8
+ baseDir: string,
9
+ componentName: string,
10
+ files: ComponentFile[],
11
+ config: BeaketConfig,
12
+ ): Promise<void> {
13
+ for (const file of files) {
14
+ // Transform file path: components/button/button.tsx -> button/button.tsx
15
+ const relativePath = file.path.replace(/^components\//, "");
16
+ const targetPath = path.join(baseDir, relativePath);
17
+
18
+ // Transform imports in content
19
+ let content = file.content;
20
+
21
+ // Replace @/lib/utils with user's utils alias
22
+ content = content.replace(/@\/lib\/utils/g, `${config.aliases.utils}/utils`);
23
+
24
+ // Replace @/components with user's components alias
25
+ content = content.replace(/@\/components/g, config.aliases.components);
26
+
27
+ await fs.ensureDir(path.dirname(targetPath));
28
+ await fs.writeFile(targetPath, content);
29
+ }
30
+ }
31
+
32
+ export async function installDependencies(deps: string[]): Promise<void> {
33
+ const packageManager = await detectPackageManager();
34
+ const installCmd = packageManager === "npm" ? "install" : "add";
35
+
36
+ return new Promise((resolve, reject) => {
37
+ const child = spawn(packageManager, [installCmd, ...deps], {
38
+ stdio: "inherit",
39
+ shell: true,
40
+ });
41
+
42
+ child.on("close", (code) => {
43
+ if (code === 0) {
44
+ resolve();
45
+ } else {
46
+ reject(new Error(`Failed to install dependencies (code ${code})`));
47
+ }
48
+ });
49
+ });
50
+ }
51
+
52
+ async function detectPackageManager(): Promise<"npm" | "pnpm" | "yarn" | "bun"> {
53
+ const cwd = process.cwd();
54
+
55
+ if (await fs.pathExists(path.join(cwd, "pnpm-lock.yaml"))) {
56
+ return "pnpm";
57
+ }
58
+ if (await fs.pathExists(path.join(cwd, "yarn.lock"))) {
59
+ return "yarn";
60
+ }
61
+ if (await fs.pathExists(path.join(cwd, "bun.lockb"))) {
62
+ return "bun";
63
+ }
64
+
65
+ return "npm";
66
+ }
@@ -0,0 +1,56 @@
1
+ export interface ComponentDefinition {
2
+ name: string;
3
+ description?: string;
4
+ dependencies: string[];
5
+ registryDependencies: string[];
6
+ files: string[];
7
+ }
8
+
9
+ export interface Registry {
10
+ $schema?: string;
11
+ name: string;
12
+ components: ComponentDefinition[];
13
+ }
14
+
15
+ export interface ComponentFile {
16
+ path: string;
17
+ content: string;
18
+ }
19
+
20
+ // GitHub raw URL base - update this to your repo
21
+ const GITHUB_RAW_BASE = "https://raw.githubusercontent.com/beaket/ui/main";
22
+
23
+ export async function fetchRegistry(): Promise<Registry> {
24
+ const url = `${GITHUB_RAW_BASE}/registry/registry.json`;
25
+
26
+ try {
27
+ const response = await fetch(url);
28
+ if (!response.ok) {
29
+ throw new Error(`HTTP ${response.status}`);
30
+ }
31
+ return (await response.json()) as Registry;
32
+ } catch (error) {
33
+ throw new Error(`Failed to fetch registry from ${url}. Make sure the repository is public.`);
34
+ }
35
+ }
36
+
37
+ export async function fetchComponent(componentDef: ComponentDefinition): Promise<ComponentFile[]> {
38
+ const files: ComponentFile[] = [];
39
+
40
+ for (const filePath of componentDef.files) {
41
+ const url = `${GITHUB_RAW_BASE}/src/${filePath}`;
42
+
43
+ try {
44
+ const response = await fetch(url);
45
+ if (!response.ok) {
46
+ throw new Error(`HTTP ${response.status}`);
47
+ }
48
+ const content = await response.text();
49
+ files.push({ path: filePath, content });
50
+ } catch (error) {
51
+ throw new Error(`Failed to fetch ${filePath} from ${url}`);
52
+ }
53
+ }
54
+
55
+ return files;
56
+ }