bosia 0.2.2 ā 0.3.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 +39 -39
- package/package.json +56 -53
- package/src/ambient.d.ts +31 -0
- package/src/cli/add.ts +120 -114
- package/src/cli/build.ts +10 -10
- package/src/cli/create.ts +142 -137
- package/src/cli/dev.ts +8 -8
- package/src/cli/feat.ts +291 -132
- package/src/cli/index.ts +51 -42
- package/src/cli/registry.ts +136 -115
- package/src/cli/start.ts +17 -17
- package/src/cli/test.ts +25 -0
- package/src/core/build.ts +72 -56
- package/src/core/client/App.svelte +177 -153
- package/src/core/client/appState.svelte.ts +57 -0
- package/src/core/client/enhance.ts +112 -0
- package/src/core/client/hydrate.ts +97 -65
- package/src/core/client/prefetch.ts +101 -94
- package/src/core/client/router.svelte.ts +64 -51
- package/src/core/cookies.ts +70 -66
- package/src/core/cors.ts +44 -35
- package/src/core/csrf.ts +38 -38
- package/src/core/dedup.ts +17 -17
- package/src/core/dev.ts +165 -168
- package/src/core/env.ts +155 -128
- package/src/core/envCodegen.ts +73 -73
- package/src/core/errors.ts +48 -49
- package/src/core/hooks.ts +50 -50
- package/src/core/html.ts +192 -139
- package/src/core/matcher.ts +130 -121
- package/src/core/paths.ts +8 -10
- package/src/core/plugin.ts +113 -107
- package/src/core/prerender.ts +191 -118
- package/src/core/renderer.ts +359 -265
- package/src/core/routeFile.ts +140 -127
- package/src/core/routeTypes.ts +144 -83
- package/src/core/scanner.ts +125 -95
- package/src/core/server.ts +543 -370
- package/src/core/types.ts +25 -20
- package/src/lib/client.ts +12 -0
- package/src/lib/index.ts +8 -8
- package/src/lib/utils.ts +44 -30
- package/templates/default/.prettierignore +5 -0
- package/templates/default/.prettierrc.json +9 -0
- package/templates/default/README.md +5 -5
- package/templates/default/package.json +22 -18
- package/templates/default/src/app.css +80 -80
- package/templates/default/src/app.d.ts +3 -3
- package/templates/default/src/routes/+error.svelte +7 -10
- package/templates/default/src/routes/+layout.svelte +2 -2
- package/templates/default/src/routes/+page.svelte +31 -29
- package/templates/default/src/routes/about/+page.svelte +3 -3
- package/templates/default/tsconfig.json +20 -20
- package/templates/demo/.prettierignore +5 -0
- package/templates/demo/.prettierrc.json +9 -0
- package/templates/demo/README.md +9 -9
- package/templates/demo/package.json +22 -17
- package/templates/demo/src/app.css +80 -80
- package/templates/demo/src/app.d.ts +3 -3
- package/templates/demo/src/hooks.server.ts +9 -9
- package/templates/demo/src/routes/(public)/+layout.svelte +45 -23
- package/templates/demo/src/routes/(public)/+page.svelte +96 -67
- package/templates/demo/src/routes/(public)/about/+page.svelte +13 -25
- package/templates/demo/src/routes/(public)/all/[...catchall]/+page.svelte +24 -28
- package/templates/demo/src/routes/(public)/blog/+page.svelte +55 -46
- package/templates/demo/src/routes/(public)/blog/[slug]/+page.server.ts +36 -38
- package/templates/demo/src/routes/(public)/blog/[slug]/+page.svelte +60 -42
- package/templates/demo/src/routes/+error.svelte +10 -7
- package/templates/demo/src/routes/+layout.server.ts +4 -4
- package/templates/demo/src/routes/+layout.svelte +2 -2
- package/templates/demo/src/routes/actions-test/+page.server.ts +16 -16
- package/templates/demo/src/routes/actions-test/+page.svelte +49 -49
- package/templates/demo/src/routes/api/hello/+server.ts +25 -25
- package/templates/demo/tsconfig.json +20 -20
- package/templates/todo/.prettierignore +5 -0
- package/templates/todo/.prettierrc.json +9 -0
- package/templates/todo/README.md +9 -9
- package/templates/todo/package.json +22 -17
- package/templates/todo/src/app.css +80 -80
- package/templates/todo/src/app.d.ts +7 -7
- package/templates/todo/src/hooks.server.ts +9 -9
- package/templates/todo/src/routes/+error.svelte +10 -7
- package/templates/todo/src/routes/+layout.server.ts +4 -4
- package/templates/todo/src/routes/+layout.svelte +2 -2
- package/templates/todo/src/routes/+page.svelte +44 -44
- package/templates/todo/template.json +1 -1
- package/templates/todo/tsconfig.json +20 -20
package/src/cli/registry.ts
CHANGED
|
@@ -7,162 +7,183 @@ import { spawn } from "bun";
|
|
|
7
7
|
export const REGISTRY_URL = "https://raw.githubusercontent.com/bosapi/bosia/main/registry";
|
|
8
8
|
|
|
9
9
|
export interface InstallOptions {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
skipInstall?: boolean; // write deps to package.json instead of `bun add`
|
|
11
|
+
skipPrompts?: boolean; // auto-overwrite, no interactive prompts
|
|
12
|
+
cwd?: string; // override process.cwd() for file operations
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
// āāā Local registry resolution āāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
16
16
|
|
|
17
17
|
export function resolveLocalRegistry(): string {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
let dir = dirname(new URL(import.meta.url).pathname);
|
|
19
|
+
for (let i = 0; i < 10; i++) {
|
|
20
|
+
const candidate = join(dir, "registry");
|
|
21
|
+
if (existsSync(join(candidate, "index.json"))) return candidate;
|
|
22
|
+
const parent = dirname(dir);
|
|
23
|
+
if (parent === dir) break;
|
|
24
|
+
dir = parent;
|
|
25
|
+
}
|
|
26
|
+
throw new Error("Could not find local registry/ directory.");
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/** Resolve local registry, exiting with error message on failure. For CLI entry points. */
|
|
30
30
|
export function resolveLocalRegistryOrExit(): string {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
try {
|
|
32
|
+
return resolveLocalRegistry();
|
|
33
|
+
} catch {
|
|
34
|
+
console.error("ā Could not find local registry/ directory.");
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// āāā Registry file readers āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
40
40
|
|
|
41
41
|
/** Read and parse a JSON file from the registry (local or remote). */
|
|
42
42
|
export async function readRegistryJSON<T>(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
registryRoot: string | null,
|
|
44
|
+
category: string,
|
|
45
|
+
name: string,
|
|
46
|
+
file: string,
|
|
47
47
|
): Promise<T> {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
if (registryRoot) {
|
|
49
|
+
const path = join(registryRoot, category, name, file);
|
|
50
|
+
if (!existsSync(path)) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`"${file}" not found for ${category.slice(0, -1)} "${name}" in local registry`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
56
|
+
}
|
|
57
|
+
return fetchJSON<T>(`${REGISTRY_URL}/${category}/${name}/${file}`);
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
/** Read a text file from the registry (local or remote). */
|
|
59
61
|
export async function readRegistryFile(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
registryRoot: string | null,
|
|
63
|
+
category: string,
|
|
64
|
+
name: string,
|
|
65
|
+
file: string,
|
|
64
66
|
): Promise<string> {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
if (registryRoot) {
|
|
68
|
+
const path = join(registryRoot, category, name, file);
|
|
69
|
+
if (!existsSync(path)) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`File "${file}" not found for ${category.slice(0, -1)} "${name}" in local registry`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return readFileSync(path, "utf-8");
|
|
75
|
+
}
|
|
76
|
+
return fetchText(`${REGISTRY_URL}/${category}/${name}/${file}`);
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
// āāā package.json helpers āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
76
80
|
|
|
77
81
|
export interface PkgDeps {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
deps?: Record<string, string>;
|
|
83
|
+
devDeps?: Record<string, string>;
|
|
84
|
+
scripts?: Record<string, string>;
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
/**
|
|
84
88
|
* Merge dependencies and scripts into package.json in a single read/write.
|
|
85
89
|
* Returns the list of added keys, or empty arrays if nothing changed.
|
|
86
90
|
*/
|
|
87
|
-
export function mergePkgJson(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
91
|
+
export function mergePkgJson(
|
|
92
|
+
cwd: string,
|
|
93
|
+
changes: PkgDeps,
|
|
94
|
+
): { addedDeps: string[]; addedScripts: string[] } {
|
|
95
|
+
const pkgPath = join(cwd, "package.json");
|
|
96
|
+
if (!existsSync(pkgPath)) return { addedDeps: [], addedScripts: [] };
|
|
97
|
+
|
|
98
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
99
|
+
let changed = false;
|
|
100
|
+
const addedDeps: string[] = [];
|
|
101
|
+
const addedScripts: string[] = [];
|
|
102
|
+
|
|
103
|
+
if (changes.deps && Object.keys(changes.deps).length > 0) {
|
|
104
|
+
pkg.dependencies = pkg.dependencies ?? {};
|
|
105
|
+
for (const [name, ver] of Object.entries(changes.deps)) {
|
|
106
|
+
if (!pkg.dependencies[name]) {
|
|
107
|
+
pkg.dependencies[name] = ver;
|
|
108
|
+
addedDeps.push(name);
|
|
109
|
+
changed = true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (changes.devDeps && Object.keys(changes.devDeps).length > 0) {
|
|
115
|
+
pkg.devDependencies = pkg.devDependencies ?? {};
|
|
116
|
+
for (const [name, ver] of Object.entries(changes.devDeps)) {
|
|
117
|
+
if (!pkg.devDependencies[name]) {
|
|
118
|
+
pkg.devDependencies[name] = ver;
|
|
119
|
+
addedDeps.push(name);
|
|
120
|
+
changed = true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (changes.scripts && Object.keys(changes.scripts).length > 0) {
|
|
126
|
+
pkg.scripts = pkg.scripts ?? {};
|
|
127
|
+
for (const [key, val] of Object.entries(changes.scripts)) {
|
|
128
|
+
if (!pkg.scripts[key]) {
|
|
129
|
+
pkg.scripts[key] = val;
|
|
130
|
+
addedScripts.push(key);
|
|
131
|
+
changed = true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (changed) {
|
|
137
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return { addedDeps, addedScripts };
|
|
134
141
|
}
|
|
135
142
|
|
|
136
143
|
/** Run `bun add` for deps and optionally `bun add --dev` for devDeps. */
|
|
137
|
-
export async function bunAdd(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
144
|
+
export async function bunAdd(
|
|
145
|
+
cwd: string,
|
|
146
|
+
deps: Record<string, string>,
|
|
147
|
+
devDeps?: Record<string, string>,
|
|
148
|
+
): Promise<void> {
|
|
149
|
+
const packages = Object.entries(deps).map(([pkg, ver]) => (ver ? `${pkg}@${ver}` : pkg));
|
|
150
|
+
if (packages.length > 0) {
|
|
151
|
+
console.log(`\nš„ npm: ${packages.join(", ")}`);
|
|
152
|
+
const proc = spawn(["bun", "add", ...packages], {
|
|
153
|
+
stdout: "inherit",
|
|
154
|
+
stderr: "inherit",
|
|
155
|
+
cwd,
|
|
156
|
+
});
|
|
157
|
+
if ((await proc.exited) !== 0) {
|
|
158
|
+
console.warn(`ā ļø bun add failed for: ${packages.join(", ")}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const devPackages = Object.entries(devDeps ?? {}).map(([pkg, ver]) =>
|
|
162
|
+
ver ? `${pkg}@${ver}` : pkg,
|
|
163
|
+
);
|
|
164
|
+
if (devPackages.length > 0) {
|
|
165
|
+
console.log(`\nš„ npm (dev): ${devPackages.join(", ")}`);
|
|
166
|
+
const proc = spawn(["bun", "add", "--dev", ...devPackages], {
|
|
167
|
+
stdout: "inherit",
|
|
168
|
+
stderr: "inherit",
|
|
169
|
+
cwd,
|
|
170
|
+
});
|
|
171
|
+
if ((await proc.exited) !== 0) {
|
|
172
|
+
console.warn(`ā ļø bun add --dev failed for: ${devPackages.join(", ")}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
154
175
|
}
|
|
155
176
|
|
|
156
177
|
// āāā HTTP helpers āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
157
178
|
|
|
158
179
|
async function fetchJSON<T>(url: string): Promise<T> {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
180
|
+
const res = await fetch(url);
|
|
181
|
+
if (!res.ok) throw new Error(`Failed to fetch ${url} (${res.status})`);
|
|
182
|
+
return res.json() as Promise<T>;
|
|
162
183
|
}
|
|
163
184
|
|
|
164
185
|
async function fetchText(url: string): Promise<string> {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
186
|
+
const res = await fetch(url);
|
|
187
|
+
if (!res.ok) throw new Error(`Failed to fetch ${url} (${res.status})`);
|
|
188
|
+
return res.text();
|
|
168
189
|
}
|
package/src/cli/start.ts
CHANGED
|
@@ -3,24 +3,24 @@ import { loadEnv } from "../core/env.ts";
|
|
|
3
3
|
import { BOSIA_NODE_PATH } from "../core/paths.ts";
|
|
4
4
|
|
|
5
5
|
export async function runStart() {
|
|
6
|
-
|
|
6
|
+
loadEnv("production");
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
let serverEntry = "index.js";
|
|
9
|
+
try {
|
|
10
|
+
const manifest = await Bun.file("./dist/manifest.json").json();
|
|
11
|
+
serverEntry = manifest.serverEntry ?? "index.js";
|
|
12
|
+
} catch {}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
const proc = spawn(["bun", "run", `dist/server/${serverEntry}`], {
|
|
15
|
+
stdout: "inherit",
|
|
16
|
+
stderr: "inherit",
|
|
17
|
+
cwd: process.cwd(),
|
|
18
|
+
env: {
|
|
19
|
+
...process.env,
|
|
20
|
+
NODE_ENV: "production",
|
|
21
|
+
NODE_PATH: BOSIA_NODE_PATH,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
await proc.exited;
|
|
26
26
|
}
|
package/src/cli/test.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { spawn } from "bun";
|
|
2
|
+
import { loadEnv } from "../core/env.ts";
|
|
3
|
+
import { BOSIA_NODE_PATH } from "../core/paths.ts";
|
|
4
|
+
|
|
5
|
+
export async function runTest(args: string[]) {
|
|
6
|
+
loadEnv("test");
|
|
7
|
+
|
|
8
|
+
if (!process.env.BOSIA_ENV) process.env.BOSIA_ENV = "test";
|
|
9
|
+
if (!process.env.NODE_ENV) process.env.NODE_ENV = "test";
|
|
10
|
+
|
|
11
|
+
const proc = spawn(["bun", "test", ...args], {
|
|
12
|
+
stdout: "inherit",
|
|
13
|
+
stderr: "inherit",
|
|
14
|
+
cwd: process.cwd(),
|
|
15
|
+
env: {
|
|
16
|
+
...process.env,
|
|
17
|
+
BOSIA_ENV: process.env.BOSIA_ENV,
|
|
18
|
+
NODE_ENV: process.env.NODE_ENV,
|
|
19
|
+
NODE_PATH: BOSIA_NODE_PATH,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const code = await proc.exited;
|
|
24
|
+
process.exit(code);
|
|
25
|
+
}
|
package/src/core/build.ts
CHANGED
|
@@ -27,20 +27,24 @@ const envVars = loadEnv(envMode);
|
|
|
27
27
|
const classifiedEnv = classifyEnvVars(envVars);
|
|
28
28
|
|
|
29
29
|
// 0b. Clean all generated output first
|
|
30
|
-
try {
|
|
31
|
-
|
|
30
|
+
try {
|
|
31
|
+
rmSync("./dist", { recursive: true, force: true });
|
|
32
|
+
} catch {}
|
|
33
|
+
try {
|
|
34
|
+
rmSync("./.bosia", { recursive: true, force: true });
|
|
35
|
+
} catch {}
|
|
32
36
|
|
|
33
37
|
// 1. Scan routes
|
|
34
38
|
const manifest = scanRoutes();
|
|
35
39
|
console.log(`š Found ${manifest.pages.length} page route(s):`);
|
|
36
40
|
for (const r of manifest.pages) {
|
|
37
|
-
|
|
41
|
+
console.log(` ${r.pattern} ā ${r.page}${r.pageServer ? " (server)" : ""}`);
|
|
38
42
|
}
|
|
39
43
|
if (manifest.apis.length > 0) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
console.log(`š” Found ${manifest.apis.length} API route(s):`);
|
|
45
|
+
for (const r of manifest.apis) {
|
|
46
|
+
console.log(` ${r.pattern} ā ${r.server}`);
|
|
47
|
+
}
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
// 2. Generate .bosia/routes.ts (single file replaces all old code generators)
|
|
@@ -58,12 +62,19 @@ generateEnvModules(classifiedEnv);
|
|
|
58
62
|
// 3. Start Tailwind CSS (async ā runs concurrently with client+server builds)
|
|
59
63
|
const tailwindBin = resolveBosiaBin("tailwindcss");
|
|
60
64
|
const tailwindProc = Bun.spawn(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
[
|
|
66
|
+
tailwindBin,
|
|
67
|
+
"-i",
|
|
68
|
+
"./src/app.css",
|
|
69
|
+
"-o",
|
|
70
|
+
"./public/bosia-tw.css",
|
|
71
|
+
...(isProduction ? ["--minify"] : []),
|
|
72
|
+
],
|
|
73
|
+
{
|
|
74
|
+
cwd: process.cwd(),
|
|
75
|
+
env: { ...process.env, NODE_PATH: BOSIA_NODE_PATH },
|
|
76
|
+
stderr: "pipe",
|
|
77
|
+
},
|
|
67
78
|
);
|
|
68
79
|
const tailwindPromise = tailwindProc.exited;
|
|
69
80
|
|
|
@@ -74,85 +85,90 @@ const serverPlugin = makeBosiaPlugin("bun");
|
|
|
74
85
|
// Build-time defines: inline PUBLIC_STATIC_* and STATIC_* vars
|
|
75
86
|
const staticDefines: Record<string, string> = {};
|
|
76
87
|
for (const [key, value] of Object.entries(classifiedEnv.publicStatic)) {
|
|
77
|
-
|
|
88
|
+
staticDefines[`import.meta.env.${key}`] = JSON.stringify(value);
|
|
78
89
|
}
|
|
79
90
|
for (const [key, value] of Object.entries(classifiedEnv.privateStatic)) {
|
|
80
|
-
|
|
91
|
+
staticDefines[`import.meta.env.${key}`] = JSON.stringify(value);
|
|
81
92
|
}
|
|
82
93
|
|
|
83
94
|
// 5. Build Tailwind + client + server bundles in parallel
|
|
84
95
|
console.log("\nš¦ Building Tailwind + client + server...");
|
|
85
96
|
const clientPromise = Bun.build({
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
entrypoints: [join(CORE_DIR, "client", "hydrate.ts")],
|
|
98
|
+
outdir: "./dist/client",
|
|
99
|
+
target: "browser",
|
|
100
|
+
splitting: true,
|
|
101
|
+
naming: { chunk: "[name]-[hash].[ext]" },
|
|
102
|
+
minify: isProduction,
|
|
103
|
+
define: {
|
|
104
|
+
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV ?? "development"),
|
|
105
|
+
...staticDefines,
|
|
106
|
+
},
|
|
107
|
+
plugins: [clientPlugin, SveltePlugin()],
|
|
97
108
|
});
|
|
98
109
|
|
|
99
110
|
const serverPromise = Bun.build({
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
entrypoints: [join(CORE_DIR, "server.ts")],
|
|
112
|
+
outdir: "./dist/server",
|
|
113
|
+
target: "bun",
|
|
114
|
+
splitting: true,
|
|
115
|
+
naming: { entry: "index.[ext]", chunk: "[name]-[hash].[ext]" },
|
|
116
|
+
minify: isProduction,
|
|
117
|
+
external: ["elysia"],
|
|
118
|
+
plugins: [serverPlugin, SveltePlugin()],
|
|
108
119
|
});
|
|
109
120
|
|
|
110
121
|
const [tailwindExitCode, clientResult, serverResult] = await Promise.all([
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
122
|
+
tailwindPromise,
|
|
123
|
+
clientPromise,
|
|
124
|
+
serverPromise,
|
|
114
125
|
]);
|
|
115
126
|
|
|
116
127
|
if (tailwindExitCode !== 0) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
const stderr = await new Response(tailwindProc.stderr).text();
|
|
129
|
+
console.error("ā Tailwind CSS build failed:\n" + stderr);
|
|
130
|
+
process.exit(1);
|
|
120
131
|
}
|
|
121
132
|
console.log("ā
Tailwind CSS built: public/bosia-tw.css");
|
|
122
133
|
|
|
123
134
|
if (!clientResult.success) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
135
|
+
console.error("ā Client build failed:");
|
|
136
|
+
for (const msg of clientResult.logs) console.error(msg);
|
|
137
|
+
process.exit(1);
|
|
127
138
|
}
|
|
128
139
|
|
|
129
140
|
if (!serverResult.success) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
141
|
+
console.error("ā Server build failed:");
|
|
142
|
+
for (const msg of serverResult.logs) console.error(msg);
|
|
143
|
+
process.exit(1);
|
|
133
144
|
}
|
|
134
145
|
|
|
135
146
|
// 6. Collect output files for dist/manifest.json
|
|
136
147
|
const jsFiles: string[] = [];
|
|
137
148
|
const cssFiles: string[] = [];
|
|
138
149
|
for (const output of clientResult.outputs) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
150
|
+
const rel = relative("./dist/client", output.path);
|
|
151
|
+
if (output.path.endsWith(".js")) jsFiles.push(rel);
|
|
152
|
+
if (output.path.endsWith(".css")) cssFiles.push(rel);
|
|
142
153
|
}
|
|
143
154
|
|
|
144
155
|
// Entry is always "index.js" due to naming: { entry: "index.[ext]" }
|
|
145
|
-
const serverEntry =
|
|
146
|
-
|
|
147
|
-
|
|
156
|
+
const serverEntry =
|
|
157
|
+
serverResult.outputs
|
|
158
|
+
.find((o) => o.path.endsWith("index.js"))
|
|
159
|
+
?.path.split("/")
|
|
160
|
+
.pop() ?? "index.js";
|
|
148
161
|
|
|
149
162
|
// 8. Write dist/manifest.json
|
|
150
163
|
mkdirSync("./dist", { recursive: true });
|
|
151
164
|
const distManifest = {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
165
|
+
js: jsFiles,
|
|
166
|
+
css: cssFiles,
|
|
167
|
+
entry:
|
|
168
|
+
jsFiles.find((f) => f === "hydrate.js") ??
|
|
169
|
+
jsFiles.find((f) => f.startsWith("hydrate")) ??
|
|
170
|
+
"hydrate.js",
|
|
171
|
+
serverEntry,
|
|
156
172
|
};
|
|
157
173
|
writeFileSync("./dist/manifest.json", JSON.stringify(distManifest, null, 2));
|
|
158
174
|
console.log(`ā
Client bundle: ${jsFiles.join(", ")}`);
|