bosia 0.7.2 → 0.7.3
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/create.ts +107 -12
- package/templates/shop/template.json +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bosia",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
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": [
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"scripts": {
|
|
44
44
|
"check": "tsc --noEmit && prettier --check .",
|
|
45
45
|
"check:templates": "bun scripts/check-templates.ts",
|
|
46
|
+
"build:templates": "bun scripts/build-prebuilt-templates.ts",
|
|
46
47
|
"test": "bun test",
|
|
47
48
|
"test:watch": "bun test --watch"
|
|
48
49
|
},
|
package/src/cli/create.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolve, join, basename, relative } from "path";
|
|
2
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
3
|
+
import { tmpdir } from "os";
|
|
3
4
|
import { spawn } from "bun";
|
|
4
5
|
import * as p from "@clack/prompts";
|
|
5
6
|
import { installFeature, initFeatRegistry, resolveLocalRegistry } from "./feat.ts";
|
|
@@ -63,6 +64,26 @@ export async function runCreate(name: string | undefined, args: string[] = []) {
|
|
|
63
64
|
|
|
64
65
|
console.log(`\n⬡ Creating Bosia project: ${basename(targetDir)} (template: ${template})\n`);
|
|
65
66
|
|
|
67
|
+
const templateConfigPath = join(templateDir, "template.json");
|
|
68
|
+
const config = existsSync(templateConfigPath)
|
|
69
|
+
? JSON.parse(readFileSync(templateConfigPath, "utf-8"))
|
|
70
|
+
: null;
|
|
71
|
+
|
|
72
|
+
// Fast path: heavy templates marked `"prebuilt": true` ship a baked,
|
|
73
|
+
// version-locked artifact on GitHub Releases. One download + extract beats
|
|
74
|
+
// 150+ serial registry fetches. Skipped for `--local` (dev flow) and falls
|
|
75
|
+
// back to the registry path if the artifact is missing (offline / no asset yet).
|
|
76
|
+
// `BOSIA_BUILDING_PREBUILT` is set by the artifact generator so it scaffolds
|
|
77
|
+
// via the registry instead of trying to download the asset it's producing.
|
|
78
|
+
if (config?.prebuilt === true && !isLocal && !process.env.BOSIA_BUILDING_PREBUILT) {
|
|
79
|
+
const ok = await scaffoldFromPrebuilt(template, targetDir, name);
|
|
80
|
+
if (ok) {
|
|
81
|
+
await finishCreate(targetDir, name, templateDir, skipInstall);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
console.log("⚠️ Prebuilt artifact unavailable — installing from registry instead.\n");
|
|
85
|
+
}
|
|
86
|
+
|
|
66
87
|
copyDir(templateDir, targetDir, name, isLocal);
|
|
67
88
|
|
|
68
89
|
if (existsSync(join(targetDir, ".env.example"))) {
|
|
@@ -70,9 +91,7 @@ export async function runCreate(name: string | undefined, args: string[] = []) {
|
|
|
70
91
|
}
|
|
71
92
|
|
|
72
93
|
// Install template features from registry
|
|
73
|
-
|
|
74
|
-
if (existsSync(templateConfigPath)) {
|
|
75
|
-
const config = JSON.parse(readFileSync(templateConfigPath, "utf-8"));
|
|
94
|
+
if (config) {
|
|
76
95
|
if (config.features?.length) {
|
|
77
96
|
let localRegistry: string | null = null;
|
|
78
97
|
try {
|
|
@@ -96,15 +115,29 @@ export async function runCreate(name: string | undefined, args: string[] = []) {
|
|
|
96
115
|
}
|
|
97
116
|
}
|
|
98
117
|
|
|
99
|
-
|
|
118
|
+
await finishCreate(targetDir, name, templateDir, skipInstall);
|
|
119
|
+
}
|
|
100
120
|
|
|
101
|
-
|
|
102
|
-
|
|
121
|
+
// ─── Shared finish: optional `bun install` + printed instructions ──────────
|
|
122
|
+
async function finishCreate(
|
|
123
|
+
targetDir: string,
|
|
124
|
+
name: string,
|
|
125
|
+
templateDir: string,
|
|
126
|
+
skipInstall: boolean,
|
|
127
|
+
) {
|
|
128
|
+
const printInstructions = () => {
|
|
103
129
|
const instPath = join(templateDir, "instructions.txt");
|
|
104
130
|
if (existsSync(instPath)) {
|
|
105
131
|
const instructions = readFileSync(instPath, "utf-8").trimEnd();
|
|
106
132
|
if (instructions) console.log(instructions);
|
|
107
133
|
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
console.log(`\n✅ Project created at ${targetDir}\n`);
|
|
137
|
+
|
|
138
|
+
if (skipInstall) {
|
|
139
|
+
console.log(`Skipped \`bun install\` (--no-install).\n\ncd ${name} && bun install\n`);
|
|
140
|
+
printInstructions();
|
|
108
141
|
return;
|
|
109
142
|
}
|
|
110
143
|
|
|
@@ -119,14 +152,76 @@ export async function runCreate(name: string | undefined, args: string[] = []) {
|
|
|
119
152
|
console.warn("⚠️ bun install failed — run it manually.");
|
|
120
153
|
} else {
|
|
121
154
|
console.log(`\n🎉 Ready!\n\ncd ${name}`);
|
|
155
|
+
printInstructions();
|
|
156
|
+
console.log(`bun x bosia dev\n`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
122
159
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
160
|
+
// ─── Prebuilt artifact fast path ──────────────────────────────────────────
|
|
161
|
+
// Downloads the version-locked `<template>.tar.gz` GitHub Release asset,
|
|
162
|
+
// extracts it into targetDir, then substitutes the `{{PROJECT_NAME}}`
|
|
163
|
+
// placeholder baked into the artifact. Returns false (caller falls back to the
|
|
164
|
+
// registry path) on any failure: 404, offline, or a corrupt archive.
|
|
165
|
+
async function scaffoldFromPrebuilt(
|
|
166
|
+
template: string,
|
|
167
|
+
targetDir: string,
|
|
168
|
+
name: string,
|
|
169
|
+
): Promise<boolean> {
|
|
170
|
+
const url = `https://github.com/bosapi/bosia/releases/download/v${BOSIA_VERSION}/${template}.tar.gz`;
|
|
171
|
+
const tmpTar = join(tmpdir(), `bosia-${template}-${Date.now()}.tar.gz`);
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
console.log(`⬇️ Downloading prebuilt template…`);
|
|
175
|
+
const res = await fetch(url);
|
|
176
|
+
if (!res.ok) return false;
|
|
177
|
+
writeFileSync(tmpTar, Buffer.from(await res.arrayBuffer()));
|
|
178
|
+
} catch {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
mkdirSync(targetDir, { recursive: true });
|
|
184
|
+
const tar = spawn(["tar", "-xzf", tmpTar, "-C", targetDir], {
|
|
185
|
+
stdout: "inherit",
|
|
186
|
+
stderr: "inherit",
|
|
187
|
+
});
|
|
188
|
+
if ((await tar.exited) !== 0) return false;
|
|
189
|
+
} catch {
|
|
190
|
+
return false;
|
|
191
|
+
} finally {
|
|
192
|
+
try {
|
|
193
|
+
unlinkSync(tmpTar);
|
|
194
|
+
} catch {
|
|
195
|
+
// best-effort cleanup
|
|
127
196
|
}
|
|
197
|
+
}
|
|
128
198
|
|
|
129
|
-
|
|
199
|
+
substitutePlaceholder(targetDir, name);
|
|
200
|
+
|
|
201
|
+
// Artifact bakes `.env` already, but restore from `.env.example` if missing.
|
|
202
|
+
const envPath = join(targetDir, ".env");
|
|
203
|
+
const envExample = join(targetDir, ".env.example");
|
|
204
|
+
if (!existsSync(envPath) && existsSync(envExample)) {
|
|
205
|
+
writeFileSync(envPath, readFileSync(envExample, "utf-8"));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Replace the `{{PROJECT_NAME}}` placeholder (baked at generate-time) across
|
|
212
|
+
// every extracted file. Mirrors copyDir's utf-8 assumption — templates carry
|
|
213
|
+
// no binary files.
|
|
214
|
+
function substitutePlaceholder(dir: string, name: string) {
|
|
215
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
216
|
+
const full = join(dir, entry.name);
|
|
217
|
+
if (entry.isDirectory()) {
|
|
218
|
+
substitutePlaceholder(full, name);
|
|
219
|
+
} else if (entry.isFile()) {
|
|
220
|
+
const content = readFileSync(full, "utf-8");
|
|
221
|
+
if (content.includes("{{PROJECT_NAME}}")) {
|
|
222
|
+
writeFileSync(full, content.replaceAll("{{PROJECT_NAME}}", name), "utf-8");
|
|
223
|
+
}
|
|
224
|
+
}
|
|
130
225
|
}
|
|
131
226
|
}
|
|
132
227
|
|