bosia 0.1.0 → 0.1.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/package.json +2 -1
- package/src/cli/add.ts +77 -9
- package/src/cli/create.ts +14 -19
- package/src/cli/index.ts +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bosia",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A fast, batteries-included fullstack framework — SSR · Svelte 5 Runes · Bun · ElysiaJS. File-based routing inspired by SvelteKit. No Node.js, no Vite, no adapters.",
|
|
6
6
|
"keywords": [
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"typescript": "^5"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
+
"@clack/prompts": "^1.1.0",
|
|
47
48
|
"@tailwindcss/cli": "^4.2.1",
|
|
48
49
|
"bun-plugin-svelte": "^0.0.6",
|
|
49
50
|
"clsx": "^2.1.1",
|
package/src/cli/add.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { join, dirname } from "path";
|
|
2
|
-
import { mkdirSync, writeFileSync } from "fs";
|
|
2
|
+
import { mkdirSync, writeFileSync, readFileSync, existsSync } from "fs";
|
|
3
3
|
import { spawn } from "bun";
|
|
4
4
|
|
|
5
5
|
// ─── bosia add <component> ────────────────────────────────
|
|
6
|
-
// Fetches a component from the GitHub registry
|
|
7
|
-
//
|
|
6
|
+
// Fetches a component from the GitHub registry (or local registry
|
|
7
|
+
// with --local) and copies it into src/lib/components/ui/<name>/.
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const REMOTE_BASE = "https://raw.githubusercontent.com/bosapi/bosia/main/registry";
|
|
10
10
|
|
|
11
11
|
interface ComponentMeta {
|
|
12
12
|
name: string;
|
|
@@ -19,11 +19,22 @@ interface ComponentMeta {
|
|
|
19
19
|
// Track already-installed components within a session to avoid re-running deps
|
|
20
20
|
const installed = new Set<string>();
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
// Resolved once in runAdd, used by addComponent
|
|
23
|
+
let registryRoot: string | null = null;
|
|
24
|
+
|
|
25
|
+
export async function runAdd(name: string | undefined, flags: string[] = []) {
|
|
23
26
|
if (!name) {
|
|
24
|
-
console.error("❌ Please provide a component name.\n Usage: bosia add <component>");
|
|
27
|
+
console.error("❌ Please provide a component name.\n Usage: bosia add <component> [--local]");
|
|
25
28
|
process.exit(1);
|
|
26
29
|
}
|
|
30
|
+
|
|
31
|
+
if (flags.includes("--local")) {
|
|
32
|
+
// Walk up from this file to find the repo's registry/ directory
|
|
33
|
+
registryRoot = resolveLocalRegistry();
|
|
34
|
+
console.log(`⬡ Using local registry: ${registryRoot}\n`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
ensureUtils();
|
|
27
38
|
await addComponent(name, true);
|
|
28
39
|
}
|
|
29
40
|
|
|
@@ -33,19 +44,19 @@ export async function addComponent(name: string, root = false) {
|
|
|
33
44
|
|
|
34
45
|
console.log(root ? `⬡ Installing component: ${name}\n` : ` 📦 Dependency: ${name}`);
|
|
35
46
|
|
|
36
|
-
const meta = await
|
|
47
|
+
const meta = await readMeta(name);
|
|
37
48
|
|
|
38
49
|
// Install component dependencies first (recursive)
|
|
39
50
|
for (const dep of meta.dependencies) {
|
|
40
51
|
await addComponent(dep, false);
|
|
41
52
|
}
|
|
42
53
|
|
|
43
|
-
// Download component files into src/lib/components/ui/<name>/
|
|
54
|
+
// Download/copy component files into src/lib/components/ui/<name>/
|
|
44
55
|
const destDir = join(process.cwd(), "src", "lib", "components", "ui", name);
|
|
45
56
|
mkdirSync(destDir, { recursive: true });
|
|
46
57
|
|
|
47
58
|
for (const file of meta.files) {
|
|
48
|
-
const content = await
|
|
59
|
+
const content = await readFile(name, file);
|
|
49
60
|
const dest = join(destDir, file);
|
|
50
61
|
mkdirSync(dirname(dest), { recursive: true });
|
|
51
62
|
writeFileSync(dest, content, "utf-8");
|
|
@@ -70,6 +81,63 @@ export async function addComponent(name: string, root = false) {
|
|
|
70
81
|
if (root) console.log(`\n✅ ${name} installed at src/lib/components/ui/${name}/`);
|
|
71
82
|
}
|
|
72
83
|
|
|
84
|
+
// ─── Ensure $lib/utils.ts exists ─────────────────────────────
|
|
85
|
+
|
|
86
|
+
const UTILS_CONTENT = `import { clsx, type ClassValue } from "clsx";
|
|
87
|
+
import { twMerge } from "tailwind-merge";
|
|
88
|
+
|
|
89
|
+
export function cn(...inputs: ClassValue[]) {
|
|
90
|
+
return twMerge(clsx(inputs));
|
|
91
|
+
}
|
|
92
|
+
`;
|
|
93
|
+
|
|
94
|
+
function ensureUtils() {
|
|
95
|
+
const utilsPath = join(process.cwd(), "src", "lib", "utils.ts");
|
|
96
|
+
if (!existsSync(utilsPath)) {
|
|
97
|
+
mkdirSync(dirname(utilsPath), { recursive: true });
|
|
98
|
+
writeFileSync(utilsPath, UTILS_CONTENT, "utf-8");
|
|
99
|
+
console.log(" ✍️ src/lib/utils.ts (cn utility)\n");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ─── Registry resolvers ──────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
function resolveLocalRegistry(): string {
|
|
106
|
+
// Walk up from this file's directory to find registry/
|
|
107
|
+
let dir = dirname(new URL(import.meta.url).pathname);
|
|
108
|
+
for (let i = 0; i < 10; i++) {
|
|
109
|
+
const candidate = join(dir, "registry");
|
|
110
|
+
if (existsSync(join(candidate, "index.json"))) return candidate;
|
|
111
|
+
const parent = dirname(dir);
|
|
112
|
+
if (parent === dir) break;
|
|
113
|
+
dir = parent;
|
|
114
|
+
}
|
|
115
|
+
console.error("❌ Could not find local registry/ directory.");
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function readMeta(name: string): Promise<ComponentMeta> {
|
|
120
|
+
if (registryRoot) {
|
|
121
|
+
const path = join(registryRoot, "components", name, "meta.json");
|
|
122
|
+
if (!existsSync(path)) {
|
|
123
|
+
throw new Error(`Component "${name}" not found in local registry`);
|
|
124
|
+
}
|
|
125
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
126
|
+
}
|
|
127
|
+
return fetchJSON<ComponentMeta>(`${REMOTE_BASE}/components/${name}/meta.json`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function readFile(name: string, file: string): Promise<string> {
|
|
131
|
+
if (registryRoot) {
|
|
132
|
+
const path = join(registryRoot, "components", name, file);
|
|
133
|
+
if (!existsSync(path)) {
|
|
134
|
+
throw new Error(`File "${file}" not found for component "${name}" in local registry`);
|
|
135
|
+
}
|
|
136
|
+
return readFileSync(path, "utf-8");
|
|
137
|
+
}
|
|
138
|
+
return fetchText(`${REMOTE_BASE}/components/${name}/${file}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
73
141
|
async function fetchJSON<T>(url: string): Promise<T> {
|
|
74
142
|
const res = await fetch(url);
|
|
75
143
|
if (!res.ok) throw new Error(`Failed to fetch ${url} (${res.status})`);
|
package/src/cli/create.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolve, join, basename } from "path";
|
|
2
|
-
import { existsSync, mkdirSync, readdirSync,
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "fs";
|
|
3
3
|
import { spawn } from "bun";
|
|
4
|
-
import * as
|
|
4
|
+
import * as p from "@clack/prompts";
|
|
5
5
|
|
|
6
6
|
// ─── bosia create <name> [--template <name>] ──────────────
|
|
7
7
|
|
|
@@ -79,26 +79,21 @@ async function promptTemplate(): Promise<string> {
|
|
|
79
79
|
|
|
80
80
|
if (templates.length === 1) return templates[0];
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
const selected = await p.select({
|
|
83
|
+
message: "Which template?",
|
|
84
|
+
options: templates.map((t) => ({
|
|
85
|
+
value: t,
|
|
86
|
+
label: t,
|
|
87
|
+
hint: TEMPLATE_DESCRIPTIONS[t],
|
|
88
|
+
})),
|
|
87
89
|
});
|
|
88
|
-
console.log();
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
91
|
+
if (p.isCancel(selected)) {
|
|
92
|
+
p.cancel("Operation cancelled.");
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|
|
94
95
|
|
|
95
|
-
return
|
|
96
|
-
rl.question(` Template name (default): `, (answer) => {
|
|
97
|
-
rl.close();
|
|
98
|
-
const trimmed = answer.trim();
|
|
99
|
-
resolvePromise(trimmed || "default");
|
|
100
|
-
});
|
|
101
|
-
});
|
|
96
|
+
return selected as string;
|
|
102
97
|
}
|
|
103
98
|
|
|
104
99
|
function copyDir(src: string, dest: string, projectName: string) {
|
package/src/cli/index.ts
CHANGED
|
@@ -33,7 +33,9 @@ async function main() {
|
|
|
33
33
|
}
|
|
34
34
|
case "add": {
|
|
35
35
|
const { runAdd } = await import("./add.ts");
|
|
36
|
-
|
|
36
|
+
const addName = args.find((a) => !a.startsWith("--"));
|
|
37
|
+
const addFlags = args.filter((a) => a.startsWith("--"));
|
|
38
|
+
await runAdd(addName, addFlags);
|
|
37
39
|
break;
|
|
38
40
|
}
|
|
39
41
|
case "feat": {
|