cus-base-ui 0.2.1 → 0.2.2
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 +7 -7
- package/dist/ui-cli.js +232 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,26 +8,26 @@ Bạn không cần cài đặt gì cả, chỉ cần đứng tại thư mục d
|
|
|
8
8
|
|
|
9
9
|
### 1. Khởi tạo dự án (Lần đầu)
|
|
10
10
|
```bash
|
|
11
|
-
npx base-
|
|
11
|
+
npx cus-base-ui init
|
|
12
12
|
```
|
|
13
13
|
Lệnh này sẽ tự động cài các gói cần thiết (`clsx`, `tailwind-merge`) và tạo file `src/lib/utils/cn.ts`.
|
|
14
14
|
|
|
15
15
|
### 2. Thêm Component
|
|
16
16
|
```bash
|
|
17
|
-
npx base-
|
|
17
|
+
npx cus-base-ui add button input switch
|
|
18
18
|
```
|
|
19
19
|
*Lưu ý: CLI sẽ tự động nhận diện và tải thêm các component phụ thuộc nếu cần.*
|
|
20
20
|
|
|
21
21
|
### 3. Cấu hình Tailwind
|
|
22
22
|
Chạy lệnh sau để nhận hướng dẫn copy-paste cấu hình theme:
|
|
23
23
|
```bash
|
|
24
|
-
npx base-
|
|
24
|
+
npx cus-base-ui tailwind
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
### 4. Liệt kê Component
|
|
29
29
|
```bash
|
|
30
|
-
npx base-
|
|
30
|
+
npx cus-base-ui list
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
## 🛠 Cách để tự bạn quản lý và phát hành
|
|
@@ -44,12 +44,12 @@ npm run build:cli
|
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
### 3. Phát hành lên NPM
|
|
47
|
-
Để mọi người có thể gõ `npx base-
|
|
47
|
+
Để mọi người có thể gõ `npx cus-base-ui`, bạn cần đưa nó lên NPM:
|
|
48
48
|
1. Đăng nhập: `npm login`
|
|
49
|
-
2. Phát hành: `npm publish` (Nếu tên `base-
|
|
49
|
+
2. Phát hành: `npm publish` (Nếu tên `cus-base-ui` đã bị trùng trên NPM, hãy đổi tên trong `package.json`).
|
|
50
50
|
|
|
51
51
|
---
|
|
52
52
|
|
|
53
53
|
## 📂 Cơ chế hoạt động
|
|
54
|
-
- **Local Mode**: Nếu bạn chạy `npx cus-ui add --local`, nó sẽ tìm file `registry.json` ngay tại thư mục hiện tại.
|
|
54
|
+
- **Local Mode**: Nếu bạn chạy `npx cus-base-ui add --local`, nó sẽ tìm file `registry.json` ngay tại thư mục hiện tại.
|
|
55
55
|
- **Remote Mode (Mặc định)**: CLI sẽ tải dữ liệu trực tiếp từ: `https://raw.githubusercontent.com/huy14032003/ui-component/main/registry.json`.
|
package/dist/ui-cli.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
|
|
25
|
+
// scripts/ui-cli.ts
|
|
26
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
27
|
+
var import_path = __toESM(require("path"), 1);
|
|
28
|
+
var import_child_process = require("child_process");
|
|
29
|
+
var REGISTRY_LOCAL = "./registry.json";
|
|
30
|
+
var REGISTRY_REMOTE = "https://raw.githubusercontent.com/huy14032003/ui-component/main/registry.json";
|
|
31
|
+
var log = (msg) => console.log(`[CUS-BASE-UI] ${msg}`);
|
|
32
|
+
var warn = (msg) => console.warn(`[CUS-BASE-UI] WARN: ${msg}`);
|
|
33
|
+
var error = (msg) => console.error(`[CUS-BASE-UI] ERROR: ${msg}`);
|
|
34
|
+
var getTargetProjectDir = () => process.cwd();
|
|
35
|
+
var validateRegistry = (data) => {
|
|
36
|
+
if (!data || typeof data !== "object") return false;
|
|
37
|
+
const reg = data;
|
|
38
|
+
return "components" in reg && typeof reg.components === "object" && reg.components !== null;
|
|
39
|
+
};
|
|
40
|
+
var getRegistry = async (isLocal) => {
|
|
41
|
+
if (isLocal && import_fs.default.existsSync(REGISTRY_LOCAL)) {
|
|
42
|
+
log("Using local registry...");
|
|
43
|
+
try {
|
|
44
|
+
const data = JSON.parse(import_fs.default.readFileSync(REGISTRY_LOCAL, "utf-8"));
|
|
45
|
+
if (!validateRegistry(data)) {
|
|
46
|
+
error('Invalid local registry format \u2014 missing "components" field.');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
return data;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
error(`Failed to parse local registry: ${err instanceof Error ? err.message : err}`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
log("Fetching registry from remote...");
|
|
56
|
+
try {
|
|
57
|
+
const response = await fetch(REGISTRY_REMOTE);
|
|
58
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
59
|
+
const data = await response.json();
|
|
60
|
+
if (!validateRegistry(data)) {
|
|
61
|
+
error('Invalid remote registry format \u2014 missing "components" field.');
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
return data;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
67
|
+
error(`Cannot fetch registry: ${message}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var installNpmPackages = (packages, cwd) => {
|
|
72
|
+
if (packages.length === 0) return;
|
|
73
|
+
const pkgJsonPath = import_path.default.join(cwd, "package.json");
|
|
74
|
+
let toInstall = packages;
|
|
75
|
+
if (import_fs.default.existsSync(pkgJsonPath)) {
|
|
76
|
+
const pkg = JSON.parse(import_fs.default.readFileSync(pkgJsonPath, "utf-8"));
|
|
77
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
78
|
+
toInstall = packages.filter((p) => !allDeps[p]);
|
|
79
|
+
}
|
|
80
|
+
if (toInstall.length === 0) return;
|
|
81
|
+
log(`Installing: ${toInstall.join(", ")}...`);
|
|
82
|
+
try {
|
|
83
|
+
(0, import_child_process.execSync)(`npm install ${toInstall.join(" ")} --save`, { stdio: "inherit", cwd });
|
|
84
|
+
} catch (err) {
|
|
85
|
+
error(`Failed to install packages: ${toInstall.join(", ")}. ${err instanceof Error ? err.message : ""}`);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
var ensureCore = (registry, cwd) => {
|
|
89
|
+
const core = registry.core;
|
|
90
|
+
if (!core) return;
|
|
91
|
+
installNpmPackages(core.dependencies, cwd);
|
|
92
|
+
for (const file of core.files) {
|
|
93
|
+
const targetPath = import_path.default.join(cwd, file.path);
|
|
94
|
+
const targetDir = import_path.default.dirname(targetPath);
|
|
95
|
+
if (!import_fs.default.existsSync(targetDir)) {
|
|
96
|
+
import_fs.default.mkdirSync(targetDir, { recursive: true });
|
|
97
|
+
}
|
|
98
|
+
if (!import_fs.default.existsSync(targetPath)) {
|
|
99
|
+
import_fs.default.writeFileSync(targetPath, file.content);
|
|
100
|
+
log(`Created core file: ${file.path}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
var addComponent = (name, registry, cwd, options, added = /* @__PURE__ */ new Set()) => {
|
|
105
|
+
if (added.has(name)) return;
|
|
106
|
+
added.add(name);
|
|
107
|
+
const component = registry.components[name];
|
|
108
|
+
if (!component) {
|
|
109
|
+
error(`Component "${name}" not found. Run 'list' to see available components.`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
log(`Adding: ${name}...`);
|
|
113
|
+
ensureCore(registry, cwd);
|
|
114
|
+
installNpmPackages(component.dependencies, cwd);
|
|
115
|
+
if (component.internalDependencies) {
|
|
116
|
+
for (const dep of component.internalDependencies) {
|
|
117
|
+
if (registry.components[dep]) {
|
|
118
|
+
addComponent(dep, registry, cwd, options, added);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
for (const file of component.files) {
|
|
123
|
+
const targetPath = import_path.default.join(cwd, file.path);
|
|
124
|
+
const targetDir = import_path.default.dirname(targetPath);
|
|
125
|
+
if (!import_fs.default.existsSync(targetDir)) {
|
|
126
|
+
import_fs.default.mkdirSync(targetDir, { recursive: true });
|
|
127
|
+
}
|
|
128
|
+
if (import_fs.default.existsSync(targetPath) && !options.force) {
|
|
129
|
+
warn(`Skipped (exists): ${file.path} \u2014 use --force to overwrite`);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
import_fs.default.writeFileSync(targetPath, file.content);
|
|
133
|
+
log(`Created: ${file.path}`);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
var removeComponent = (name, registry, cwd) => {
|
|
137
|
+
const component = registry.components[name];
|
|
138
|
+
if (!component) {
|
|
139
|
+
error(`Component "${name}" not found.`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
log(`Removing: ${name}...`);
|
|
143
|
+
for (const file of component.files) {
|
|
144
|
+
const targetPath = import_path.default.join(cwd, file.path);
|
|
145
|
+
if (import_fs.default.existsSync(targetPath)) {
|
|
146
|
+
import_fs.default.unlinkSync(targetPath);
|
|
147
|
+
log(`Deleted: ${file.path}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (const file of component.files) {
|
|
151
|
+
const targetDir = import_path.default.dirname(import_path.default.join(cwd, file.path));
|
|
152
|
+
try {
|
|
153
|
+
if (import_fs.default.existsSync(targetDir) && import_fs.default.readdirSync(targetDir).length === 0) {
|
|
154
|
+
import_fs.default.rmdirSync(targetDir);
|
|
155
|
+
log(`Removed empty dir: ${import_path.default.relative(cwd, targetDir)}`);
|
|
156
|
+
}
|
|
157
|
+
} catch (err) {
|
|
158
|
+
warn(`Could not remove directory: ${err instanceof Error ? err.message : err}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
var main = async () => {
|
|
163
|
+
const args = process.argv.slice(2);
|
|
164
|
+
const isLocal = args.includes("--local");
|
|
165
|
+
const isForce = args.includes("--force");
|
|
166
|
+
const filteredArgs = args.filter((a) => !a.startsWith("--"));
|
|
167
|
+
const command = filteredArgs[0];
|
|
168
|
+
const componentNames = filteredArgs.slice(1);
|
|
169
|
+
const cwd = getTargetProjectDir();
|
|
170
|
+
const registry = await getRegistry(isLocal);
|
|
171
|
+
switch (command) {
|
|
172
|
+
case "add": {
|
|
173
|
+
if (componentNames.length === 0) {
|
|
174
|
+
error("Usage: npx cus-base-ui add <component-name> [--force]");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
for (const name of componentNames) {
|
|
178
|
+
addComponent(name, registry, cwd, { force: isForce });
|
|
179
|
+
}
|
|
180
|
+
log("Done!");
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
case "remove": {
|
|
184
|
+
if (componentNames.length === 0) {
|
|
185
|
+
error("Usage: npx cus-base-ui remove <component-name>");
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
for (const name of componentNames) {
|
|
189
|
+
removeComponent(name, registry, cwd);
|
|
190
|
+
}
|
|
191
|
+
log("Done!");
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
case "list": {
|
|
195
|
+
const components = Object.keys(registry.components).sort();
|
|
196
|
+
log(`Available components (${components.length}):`);
|
|
197
|
+
for (const k of components) {
|
|
198
|
+
const deps = registry.components[k].internalDependencies;
|
|
199
|
+
const depStr = deps?.length ? ` (requires: ${deps.join(", ")})` : "";
|
|
200
|
+
console.log(` - ${k}${depStr}`);
|
|
201
|
+
}
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
case "init": {
|
|
205
|
+
ensureCore(registry, cwd);
|
|
206
|
+
log("Initialization complete.");
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
case "tailwind": {
|
|
210
|
+
console.log("\n--- Copy to tailwind.config.ts / tailwind.config.js ---\n");
|
|
211
|
+
console.log("// See README_CLI.md for full theme config");
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
default: {
|
|
215
|
+
console.log(`
|
|
216
|
+
cus-base-ui \u2014 UI Component CLI
|
|
217
|
+
|
|
218
|
+
Commands:
|
|
219
|
+
init Initialize project (install core deps + files)
|
|
220
|
+
add <name> [--force] Add component(s) to your project
|
|
221
|
+
remove <name> Remove component(s) from your project
|
|
222
|
+
list List all available components
|
|
223
|
+
tailwind Show Tailwind config instructions
|
|
224
|
+
|
|
225
|
+
Options:
|
|
226
|
+
--local Use local registry.json instead of remote
|
|
227
|
+
--force Overwrite existing files when adding
|
|
228
|
+
`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cus-base-ui",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cus-base-ui": "./dist/ui-cli.js"
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"scripts": {
|
|
16
16
|
"dev": "vite",
|
|
17
17
|
"build": "tsc -b && vite build",
|
|
18
|
-
"build:cli": "npx -y esbuild scripts/ui-cli.ts --bundle --platform=node --outfile=dist/ui-cli.js --format=
|
|
18
|
+
"build:cli": "npx -y esbuild scripts/ui-cli.ts --bundle --platform=node --outfile=dist/ui-cli.js --format=cjs --packages=external",
|
|
19
19
|
"lint": "eslint .",
|
|
20
20
|
"preview": "vite preview",
|
|
21
21
|
"test": "vitest",
|