@glass-ui-kit/cli 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/README.md +63 -0
- package/dist/index.js +318 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# @glass-ui-kit/cli
|
|
2
|
+
|
|
3
|
+
The official CLI for Glass UI. Add premium glassmorphism components to your React projects in seconds.
|
|
4
|
+
|
|
5
|
+
## init
|
|
6
|
+
|
|
7
|
+
Use the `init` command to initialize the project configuration.
|
|
8
|
+
|
|
9
|
+
The `init` command:
|
|
10
|
+
|
|
11
|
+
1. Creates a `glass.config.json` file.
|
|
12
|
+
2. Generates the `cn` utility at `src/lib/utils.ts`.
|
|
13
|
+
3. Installs necessary dependencies (`clsx`, `tailwind-merge`).
|
|
14
|
+
4. Injects Glass Physics variables into your global CSS.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @glass-ui-kit/cli init
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## add
|
|
22
|
+
|
|
23
|
+
Use the `add` command to add components to your project.
|
|
24
|
+
The `add` command downloads the component source code and places it directly into your project.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx @glass-ui-kit/cli add [component]
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Example
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx @glass-ui-kit/cli add card
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
You can also run the command without any arguments to view a list of all available components:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx @glass-ui-kit/cli add
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Documentation
|
|
46
|
+
|
|
47
|
+
Visit [https://ui-glass.vercel.app](https://ui-glass.vercel.app) to view the registry and documentation.
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
Licensed under the [MIT license](https://github.com/jntellez/glass-ui/blob/master/LICENSE).
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
### Keywords
|
|
56
|
+
|
|
57
|
+
components
|
|
58
|
+
ui
|
|
59
|
+
glassmorphism
|
|
60
|
+
tailwind
|
|
61
|
+
design-system
|
|
62
|
+
react
|
|
63
|
+
cli
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command as Command3 } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/commands/init.ts
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
import path2 from "path";
|
|
10
|
+
|
|
11
|
+
// src/utils/filesystem.ts
|
|
12
|
+
import path from "path";
|
|
13
|
+
import { existsSync, mkdirSync } from "fs";
|
|
14
|
+
async function writeFile(filePath, content) {
|
|
15
|
+
const absolutePath = path.resolve(process.cwd(), filePath);
|
|
16
|
+
const dir = path.dirname(absolutePath);
|
|
17
|
+
if (!existsSync(dir)) {
|
|
18
|
+
mkdirSync(dir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
await Bun.write(absolutePath, content);
|
|
21
|
+
return absolutePath;
|
|
22
|
+
}
|
|
23
|
+
async function readFile(filePath) {
|
|
24
|
+
const absolutePath = path.resolve(process.cwd(), filePath);
|
|
25
|
+
const file = Bun.file(absolutePath);
|
|
26
|
+
if (!await file.exists()) {
|
|
27
|
+
throw new Error(`File not found: ${filePath}`);
|
|
28
|
+
}
|
|
29
|
+
return await file.text();
|
|
30
|
+
}
|
|
31
|
+
function exists(filePath) {
|
|
32
|
+
return existsSync(path.resolve(process.cwd(), filePath));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/utils/get-project-info.ts
|
|
36
|
+
async function getPackageManager() {
|
|
37
|
+
if (exists("bun.lockb")) return "bun";
|
|
38
|
+
if (exists("pnpm-lock.yaml")) return "pnpm";
|
|
39
|
+
if (exists("yarn.lock")) return "yarn";
|
|
40
|
+
return "npm";
|
|
41
|
+
}
|
|
42
|
+
async function getFramework() {
|
|
43
|
+
if (!exists("package.json")) return "unknown";
|
|
44
|
+
const pkg = await Bun.file("package.json").json();
|
|
45
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
46
|
+
if (deps["next"]) return "next";
|
|
47
|
+
if (deps["astro"]) return "astro";
|
|
48
|
+
if (deps["vite"]) return "vite";
|
|
49
|
+
return "unknown";
|
|
50
|
+
}
|
|
51
|
+
function getCssPath(framework) {
|
|
52
|
+
const paths = {
|
|
53
|
+
next: ["app/globals.css", "src/app/globals.css", "styles/globals.css"],
|
|
54
|
+
vite: ["src/index.css", "src/main.css", "src/style.css"],
|
|
55
|
+
astro: ["src/styles/global.css", "src/global.css"],
|
|
56
|
+
unknown: ["src/index.css", "styles.css"]
|
|
57
|
+
};
|
|
58
|
+
for (const p of paths[framework] || []) {
|
|
59
|
+
if (exists(p)) return p;
|
|
60
|
+
}
|
|
61
|
+
return paths[framework]?.[0] || null;
|
|
62
|
+
}
|
|
63
|
+
async function installDependencies(deps, pm) {
|
|
64
|
+
const installCmd = pm === "npm" ? "install" : "add";
|
|
65
|
+
const args = [installCmd, ...deps];
|
|
66
|
+
const proc = Bun.spawn([pm, ...args], {
|
|
67
|
+
stdout: "inherit",
|
|
68
|
+
stderr: "inherit"
|
|
69
|
+
});
|
|
70
|
+
await proc.exited;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/utils/templates.ts
|
|
74
|
+
var UTILS_CN = `import { type ClassValue, clsx } from "clsx";
|
|
75
|
+
import { twMerge } from "tailwind-merge";
|
|
76
|
+
|
|
77
|
+
export function cn(...inputs: ClassValue[]) {
|
|
78
|
+
return twMerge(clsx(inputs));
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
81
|
+
var GLASS_TOKENS = `
|
|
82
|
+
@layer base {
|
|
83
|
+
:root {
|
|
84
|
+
/* --- GLASS UI TOKENS (Light) --- */
|
|
85
|
+
--glass-surface: rgba(255, 255, 255, 0.4);
|
|
86
|
+
--glass-border: rgba(255, 255, 255, 0.6);
|
|
87
|
+
--glass-highlight: rgba(255, 255, 255, 0.5);
|
|
88
|
+
--glass-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15);
|
|
89
|
+
--glass-blur: 12px;
|
|
90
|
+
--glass-saturation: 110%;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.dark {
|
|
94
|
+
/* --- GLASS UI TOKENS (Dark) --- */
|
|
95
|
+
--glass-surface: rgba(0, 0, 0, 0.3);
|
|
96
|
+
--glass-border: rgba(255, 255, 255, 0.08);
|
|
97
|
+
--glass-highlight: rgba(255, 255, 255, 0.03);
|
|
98
|
+
--glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.3);
|
|
99
|
+
--glass-blur: 12px;
|
|
100
|
+
--glass-saturation: 100%;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
|
|
105
|
+
// src/commands/init.ts
|
|
106
|
+
var init = new Command().name("init").description("Initialize configuration and dependencies").option("-y, --yes", "Skip confirmation prompt", false).action(async (opts) => {
|
|
107
|
+
try {
|
|
108
|
+
console.log(chalk.bold("\nInitializing Glass UI..."));
|
|
109
|
+
const framework = await getFramework();
|
|
110
|
+
const pm = await getPackageManager();
|
|
111
|
+
const cwd = process.cwd();
|
|
112
|
+
const configPath = "glass.config.json";
|
|
113
|
+
let cssPath = getCssPath(framework);
|
|
114
|
+
if (!cssPath) {
|
|
115
|
+
cssPath = "src/index.css";
|
|
116
|
+
}
|
|
117
|
+
const utilsPath = path2.join(cwd, "src/lib/utils.ts");
|
|
118
|
+
if (!exists(configPath)) {
|
|
119
|
+
await writeFile(
|
|
120
|
+
configPath,
|
|
121
|
+
JSON.stringify(
|
|
122
|
+
{
|
|
123
|
+
framework,
|
|
124
|
+
style: "default",
|
|
125
|
+
css: cssPath,
|
|
126
|
+
aliases: { components: "@/components/ui", utils: "@/lib/utils" }
|
|
127
|
+
},
|
|
128
|
+
null,
|
|
129
|
+
2
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
console.log(chalk.green(" Created glass.config.json"));
|
|
133
|
+
} else {
|
|
134
|
+
console.log(chalk.gray(" glass.config.json already exists."));
|
|
135
|
+
}
|
|
136
|
+
if (!exists(utilsPath)) {
|
|
137
|
+
await writeFile(utilsPath, UTILS_CN);
|
|
138
|
+
console.log(chalk.green(" Created src/lib/utils.ts"));
|
|
139
|
+
} else {
|
|
140
|
+
console.log(chalk.gray(" src/lib/utils.ts already exists."));
|
|
141
|
+
}
|
|
142
|
+
let cssContent = "";
|
|
143
|
+
try {
|
|
144
|
+
if (exists(cssPath)) {
|
|
145
|
+
cssContent = await readFile(cssPath);
|
|
146
|
+
} else {
|
|
147
|
+
console.log(chalk.yellow(` Creating new CSS file at ${cssPath}`));
|
|
148
|
+
}
|
|
149
|
+
} catch (e) {
|
|
150
|
+
}
|
|
151
|
+
if (!cssContent.includes("--glass-surface")) {
|
|
152
|
+
const newCssContent = `${GLASS_TOKENS}
|
|
153
|
+
${cssContent}`;
|
|
154
|
+
await writeFile(cssPath, newCssContent);
|
|
155
|
+
console.log(chalk.green(` Updated ${cssPath} with glass tokens`));
|
|
156
|
+
} else {
|
|
157
|
+
console.log(chalk.gray(` Tokens already present in ${cssPath}`));
|
|
158
|
+
}
|
|
159
|
+
console.log(
|
|
160
|
+
chalk.cyan(` Installing dependencies (clsx, tailwind-merge)...`)
|
|
161
|
+
);
|
|
162
|
+
await installDependencies(["clsx", "tailwind-merge"], pm);
|
|
163
|
+
console.log(chalk.bold.green("\nSetup complete."));
|
|
164
|
+
console.log(`Try adding a component:
|
|
165
|
+
`);
|
|
166
|
+
console.log(chalk.cyan(` npx @glass-ui-kit/cli@latest add card`));
|
|
167
|
+
console.log("");
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error(chalk.red("\nInitialization failed:"));
|
|
170
|
+
if (error instanceof Error) {
|
|
171
|
+
console.error(chalk.gray(error.message));
|
|
172
|
+
} else {
|
|
173
|
+
console.error(chalk.gray(String(error)));
|
|
174
|
+
}
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// src/commands/add.ts
|
|
180
|
+
import chalk2 from "chalk";
|
|
181
|
+
import { Command as Command2 } from "commander";
|
|
182
|
+
import path4 from "path";
|
|
183
|
+
|
|
184
|
+
// src/utils/registry.ts
|
|
185
|
+
import path3 from "path";
|
|
186
|
+
import os from "os";
|
|
187
|
+
import fs from "fs/promises";
|
|
188
|
+
import {
|
|
189
|
+
registryIndexSchema
|
|
190
|
+
} from "@glass-ui-kit/schema";
|
|
191
|
+
var DEFAULT_REGISTRY_URL = "https://ui-glass.vercel.app/registry.json";
|
|
192
|
+
var CACHE_DIR = path3.join(os.homedir(), ".glass-ui");
|
|
193
|
+
var CACHE_FILE = path3.join(CACHE_DIR, "registry.json");
|
|
194
|
+
var CACHE_TTL = 1e3 * 60 * 60 * 24;
|
|
195
|
+
function getRegistryUrl() {
|
|
196
|
+
return process.env.GLASS_UI_REGISTRY_URL || DEFAULT_REGISTRY_URL;
|
|
197
|
+
}
|
|
198
|
+
async function readCache() {
|
|
199
|
+
try {
|
|
200
|
+
const fileExists = await fs.stat(CACHE_FILE).then(() => true).catch(() => false);
|
|
201
|
+
if (!fileExists) return null;
|
|
202
|
+
const raw = await fs.readFile(CACHE_FILE, "utf-8");
|
|
203
|
+
const cache = JSON.parse(raw);
|
|
204
|
+
const now = Date.now();
|
|
205
|
+
if (now - cache.lastUpdated > CACHE_TTL) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
return cache.data;
|
|
209
|
+
} catch {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function writeCache(data) {
|
|
214
|
+
try {
|
|
215
|
+
await fs.mkdir(CACHE_DIR, { recursive: true });
|
|
216
|
+
const cache = {
|
|
217
|
+
lastUpdated: Date.now(),
|
|
218
|
+
data
|
|
219
|
+
};
|
|
220
|
+
await fs.writeFile(CACHE_FILE, JSON.stringify(cache), "utf-8");
|
|
221
|
+
} catch {
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
async function fetchRegistry() {
|
|
225
|
+
const cachedRegistry = await readCache();
|
|
226
|
+
if (cachedRegistry) {
|
|
227
|
+
return cachedRegistry;
|
|
228
|
+
}
|
|
229
|
+
const url = getRegistryUrl();
|
|
230
|
+
let response;
|
|
231
|
+
try {
|
|
232
|
+
response = await fetch(url);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
"Network error: Unable to connect to registry. Check your internet connection."
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
if (!response.ok) {
|
|
239
|
+
throw new Error(
|
|
240
|
+
`Registry unavailable. URL: ${url} (Status: ${response.status})`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
let data;
|
|
244
|
+
try {
|
|
245
|
+
data = await response.json();
|
|
246
|
+
} catch {
|
|
247
|
+
throw new Error("Invalid response: Registry returned non-JSON data.");
|
|
248
|
+
}
|
|
249
|
+
const parsed = registryIndexSchema.safeParse(data);
|
|
250
|
+
if (!parsed.success) {
|
|
251
|
+
console.error("Debug: Schema validation errors:", parsed.error.flatten());
|
|
252
|
+
throw new Error(
|
|
253
|
+
"Incompatible registry version. Your CLI might be outdated. Please try updating @glass-ui-kit/cli."
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
await writeCache(parsed.data);
|
|
257
|
+
return parsed.data;
|
|
258
|
+
}
|
|
259
|
+
function getItem(registry, name) {
|
|
260
|
+
return registry.find((item) => item.name === name);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// src/commands/add.ts
|
|
264
|
+
var add = new Command2().name("add").description("Add a component to your project").argument("<component>", "The component to add").action(async (componentName) => {
|
|
265
|
+
try {
|
|
266
|
+
if (!exists("glass.config.json")) {
|
|
267
|
+
console.error(chalk2.red("Configuration file not found."));
|
|
268
|
+
console.log(chalk2.gray("Please run the init command first:"));
|
|
269
|
+
console.log(chalk2.cyan(" npx @glass-ui-kit/cli@latest init"));
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
const config = JSON.parse(await readFile("glass.config.json"));
|
|
273
|
+
const pm = await getPackageManager();
|
|
274
|
+
console.log(chalk2.bold(`Fetching component: ${componentName}...`));
|
|
275
|
+
const registry = await fetchRegistry();
|
|
276
|
+
const item = getItem(registry, componentName);
|
|
277
|
+
if (!item) {
|
|
278
|
+
console.error(
|
|
279
|
+
chalk2.red(`Component '${componentName}' not found in registry.`)
|
|
280
|
+
);
|
|
281
|
+
console.log(chalk2.gray("Available components:"));
|
|
282
|
+
console.log(chalk2.gray(` ${registry.map((i) => i.name).join(", ")}`));
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
const targetDirAlias = config.aliases.components || "@/components/ui";
|
|
286
|
+
const targetDir = targetDirAlias.replace(/^@\//, "./src/");
|
|
287
|
+
for (const file of item.files) {
|
|
288
|
+
const fileName = path4.basename(file.path);
|
|
289
|
+
const filePath = path4.join(targetDir, fileName);
|
|
290
|
+
if (!file.content) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
await writeFile(filePath, file.content);
|
|
294
|
+
console.log(chalk2.green(` Created ${filePath}`));
|
|
295
|
+
}
|
|
296
|
+
if (item.dependencies?.length) {
|
|
297
|
+
console.log(chalk2.cyan(` Installing dependencies...`));
|
|
298
|
+
await installDependencies(item.dependencies, pm);
|
|
299
|
+
}
|
|
300
|
+
console.log(chalk2.bold.green(`
|
|
301
|
+
Done.`));
|
|
302
|
+
} catch (error) {
|
|
303
|
+
console.error(chalk2.red("\nOperation failed:"));
|
|
304
|
+
if (error instanceof Error) {
|
|
305
|
+
console.error(chalk2.gray(` ${error.message}`));
|
|
306
|
+
} else {
|
|
307
|
+
console.error(chalk2.gray(" An unknown error occurred."));
|
|
308
|
+
}
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// src/index.ts
|
|
314
|
+
var program = new Command3();
|
|
315
|
+
program.name("glass-ui").description("The Glass UI CLI - Add glassmorphism components to your app").version("0.0.1");
|
|
316
|
+
program.addCommand(init);
|
|
317
|
+
program.addCommand(add);
|
|
318
|
+
program.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@glass-ui-kit/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The official CLI for Glass UI. Add glassmorphism components to your React projects in seconds.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"glassmorphism",
|
|
7
|
+
"design-system",
|
|
8
|
+
"react",
|
|
9
|
+
"cli",
|
|
10
|
+
"ui",
|
|
11
|
+
"tailwind"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/jntellez/glass-ui#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/jntellez/glass-ui/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/jntellez/glass-ui.git",
|
|
20
|
+
"directory": "packages/cli"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "Glass UI Team",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"bin": {
|
|
26
|
+
"glass-ui": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"chalk": "^5.3.0",
|
|
34
|
+
"commander": "^11.1.0",
|
|
35
|
+
"node-fetch": "^3.3.2",
|
|
36
|
+
"zod": "^3.22.4",
|
|
37
|
+
"@glass-ui-kit/schema": "0.0.1"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^20.11.0",
|
|
41
|
+
"bun-types": "latest",
|
|
42
|
+
"tsup": "^8.0.1",
|
|
43
|
+
"typescript": "^5.3.3",
|
|
44
|
+
"@glass-ui-kit/tsconfig": "0.0.1"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsup src/index.ts --format esm --clean",
|
|
51
|
+
"dev": "bun run ./src/index.ts",
|
|
52
|
+
"lint": "eslint src"
|
|
53
|
+
}
|
|
54
|
+
}
|