@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 +12 -0
- package/package.json +31 -0
- package/src/commands/add.ts +58 -0
- package/src/commands/init.ts +134 -0
- package/src/index.ts +21 -0
- package/src/utils/config.ts +35 -0
- package/src/utils/files.ts +66 -0
- package/src/utils/registry.ts +56 -0
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
|
+
}
|