bosia 0.5.7 → 0.5.8
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bosia",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
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": [
|
package/src/core/build.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { scanRoutes } from "./scanner.ts";
|
|
|
5
5
|
import { generateRoutesFile } from "./routeFile.ts";
|
|
6
6
|
import { generateRouteTypes, ensureRootDirs } from "./routeTypes.ts";
|
|
7
7
|
import { makeBosiaPlugin } from "./plugin.ts";
|
|
8
|
-
import { makeBosiaSvelteCompiler } from "./svelteCompiler.ts";
|
|
8
|
+
import { makeBosiaSvelteCompiler, svelteMapCache } from "./svelteCompiler.ts";
|
|
9
9
|
import { prerenderStaticRoutes, generateStaticSite } from "./prerender.ts";
|
|
10
10
|
import { loadEnv, classifyEnvVars } from "./env.ts";
|
|
11
11
|
import { generateEnvModules } from "./envCodegen.ts";
|
|
@@ -187,6 +187,18 @@ if (!serverResult.success) {
|
|
|
187
187
|
process.exit(1);
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
// Persist the per-file Svelte compile maps so the inspector's runtime stack
|
|
191
|
+
// resolver can chain bundle-map → svelte-map to land on original source. We
|
|
192
|
+
// cannot chain at bundle time because Bun's bundle maps reference intermediate
|
|
193
|
+
// JS positions that are mostly absent from Svelte's sparse compile map —
|
|
194
|
+
// post-build remapping nukes mappings. Instead we keep both maps separate and
|
|
195
|
+
// do a two-stage lookup with `bias` interpolation in the resolver.
|
|
196
|
+
if (!isProduction && svelteMapCache.size > 0) {
|
|
197
|
+
const entries: Record<string, unknown> = {};
|
|
198
|
+
for (const [k, v] of svelteMapCache) entries[k] = v;
|
|
199
|
+
writeFileSync(`${OUT_DIR}/svelte-maps.json`, JSON.stringify(entries));
|
|
200
|
+
}
|
|
201
|
+
|
|
190
202
|
// 6. Collect output files for dist/manifest.json
|
|
191
203
|
const jsFiles: string[] = [];
|
|
192
204
|
const cssFiles: string[] = [];
|
|
@@ -2,6 +2,7 @@ import { parse, compile } from "svelte/compiler";
|
|
|
2
2
|
import MagicString from "magic-string";
|
|
3
3
|
import { basename, relative } from "node:path";
|
|
4
4
|
import type { BunPlugin } from "bun";
|
|
5
|
+
import { svelteMapCache } from "../../svelteCompiler.ts";
|
|
5
6
|
|
|
6
7
|
const VIRTUAL_NS = "bosia-inspector-css";
|
|
7
8
|
|
|
@@ -87,6 +88,21 @@ export interface InspectorBunPluginOptions {
|
|
|
87
88
|
dev: boolean;
|
|
88
89
|
}
|
|
89
90
|
|
|
91
|
+
// Svelte 5 dev compile emits named `function get()` / `function set($$value)`
|
|
92
|
+
// expressions inside `$.bind_*` calls (for nicer `$inspect` stack traces). Bun's
|
|
93
|
+
// bundler destructures `import * as $ from "svelte/internal/client"` into named
|
|
94
|
+
// imports, so `$.get(search)` becomes plain `get(search)` — which collides with
|
|
95
|
+
// the wrapping function name and recurses into itself → RangeError. Prod compile
|
|
96
|
+
// uses anonymous arrow functions and is unaffected.
|
|
97
|
+
//
|
|
98
|
+
// Rename to `$$g` / `$$s` (3 chars — length-preserving so cached svelte source
|
|
99
|
+
// map columns stay accurate). These names aren't present in svelte/internal/client.
|
|
100
|
+
function fixBindShadow(code: string): string {
|
|
101
|
+
return code
|
|
102
|
+
.replace(/\bfunction get\(\)/g, () => "function $$g()")
|
|
103
|
+
.replace(/\bfunction set\(\$\$value\)/g, () => "function $$s($$value)");
|
|
104
|
+
}
|
|
105
|
+
|
|
90
106
|
const fnv = (s: string): string => {
|
|
91
107
|
let h = 2166136261;
|
|
92
108
|
for (let i = 0; i < s.length; i++) {
|
|
@@ -120,7 +136,29 @@ export function createInspectorBunPlugin(opts: InspectorBunPluginOptions): BunPl
|
|
|
120
136
|
cssHash: ({ css }) => `svelte-${fnv(css)}`,
|
|
121
137
|
});
|
|
122
138
|
|
|
123
|
-
|
|
139
|
+
// Store Svelte's compile-step map. Bun's bundler doesn't chain
|
|
140
|
+
// onLoad sourcemaps, so the final bundle map resolves stack frames
|
|
141
|
+
// to the post-svelte-compile JS intermediate (`$.next()` /
|
|
142
|
+
// `$.append()` calls) using the .svelte filename — line numbers
|
|
143
|
+
// land past EOF. The inspector's runtime resolver chases bundle
|
|
144
|
+
// map → this svelte map (with bias-interpolated lookup) to refine
|
|
145
|
+
// to original source. `injectLocs` only shifts column offsets
|
|
146
|
+
// inside HTML open tags, never script-block line numbers, so we
|
|
147
|
+
// accept the small column drift to skip a second map chain.
|
|
148
|
+
//
|
|
149
|
+
// Only cache for the client target — server (Bun) and client builds
|
|
150
|
+
// share `svelteMapCache` keyed by abs path, but their compile output
|
|
151
|
+
// line numbers differ. The resolver translates browser-side stack
|
|
152
|
+
// frames (delivered via SSE), which run client code.
|
|
153
|
+
if (dev && generate === "client" && result.js.map) {
|
|
154
|
+
const m =
|
|
155
|
+
typeof result.js.map === "string"
|
|
156
|
+
? JSON.parse(result.js.map)
|
|
157
|
+
: result.js.map;
|
|
158
|
+
svelteMapCache.set(args.path, m);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let js = dev ? fixBindShadow(result.js.code) : result.js.code;
|
|
124
162
|
// Skip empty CSS — multiple +page.svelte routes with no <style> would
|
|
125
163
|
// otherwise each emit an empty CSS chunk, all hashing to the same
|
|
126
164
|
// content → Bun fails with "Multiple files share the same output path".
|
|
@@ -1,10 +1,37 @@
|
|
|
1
|
-
import { TraceMap, originalPositionFor } from "@jridgewell/trace-mapping";
|
|
1
|
+
import { TraceMap, originalPositionFor, GREATEST_LOWER_BOUND } from "@jridgewell/trace-mapping";
|
|
2
2
|
import { readFileSync, existsSync } from "node:fs";
|
|
3
3
|
import { dirname, resolve as pathResolve } from "node:path";
|
|
4
4
|
import { OUT_DIR } from "../../paths.ts";
|
|
5
5
|
|
|
6
6
|
const cache = new Map<string, TraceMap | null>();
|
|
7
7
|
|
|
8
|
+
// Per-`.svelte` (absolute path) compile maps written by the build step. The
|
|
9
|
+
// bundle map only resolves a stack frame to the post-svelte-compile JS
|
|
10
|
+
// position labeled with the .svelte filename; a second lookup against this
|
|
11
|
+
// map (with `bias: GREATEST_LOWER_BOUND` to interpolate sparse mappings)
|
|
12
|
+
// translates that intermediate position to original source line/col.
|
|
13
|
+
let svelteMaps: Map<string, TraceMap> | null = null;
|
|
14
|
+
let svelteMapsLoaded = false;
|
|
15
|
+
|
|
16
|
+
function loadSvelteMaps(): Map<string, TraceMap> | null {
|
|
17
|
+
if (svelteMapsLoaded) return svelteMaps;
|
|
18
|
+
svelteMapsLoaded = true;
|
|
19
|
+
try {
|
|
20
|
+
const p = pathResolve(process.cwd(), OUT_DIR, "svelte-maps.json");
|
|
21
|
+
if (!existsSync(p)) return null;
|
|
22
|
+
const raw = JSON.parse(readFileSync(p, "utf8")) as Record<string, unknown>;
|
|
23
|
+
svelteMaps = new Map();
|
|
24
|
+
for (const [absPath, m] of Object.entries(raw)) {
|
|
25
|
+
try {
|
|
26
|
+
svelteMaps.set(absPath, new TraceMap(m as never));
|
|
27
|
+
} catch {}
|
|
28
|
+
}
|
|
29
|
+
return svelteMaps;
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
8
35
|
function loadMap(mapPath: string): TraceMap | null {
|
|
9
36
|
if (cache.has(mapPath)) return cache.get(mapPath)!;
|
|
10
37
|
try {
|
|
@@ -57,6 +84,39 @@ export function resolveFrame(
|
|
|
57
84
|
const pos = originalPositionFor(tm, { line, column: col });
|
|
58
85
|
if (!pos.source || pos.line == null) return null;
|
|
59
86
|
const abs = pathResolve(dirname(mp), pos.source);
|
|
87
|
+
|
|
88
|
+
// Bundle map points at the post-svelte-compile JS position labeled with the
|
|
89
|
+
// .svelte filename. Refine by chasing through the cached svelte compile map
|
|
90
|
+
// to the real source position. Svelte's map is sparse — a given line may
|
|
91
|
+
// only carry mappings starting at some column. Try the exact column first,
|
|
92
|
+
// then fall back to the rightmost mapping on the same line, so we never lose
|
|
93
|
+
// a frame just because the bundle's reported column lands in a gap.
|
|
94
|
+
if (abs.endsWith(".svelte") || abs.endsWith(".svelte.ts") || abs.endsWith(".svelte.js")) {
|
|
95
|
+
const maps = loadSvelteMaps();
|
|
96
|
+
const svelteMap = maps?.get(abs);
|
|
97
|
+
if (svelteMap) {
|
|
98
|
+
let refined = originalPositionFor(svelteMap, {
|
|
99
|
+
line: pos.line,
|
|
100
|
+
column: pos.column ?? 0,
|
|
101
|
+
bias: GREATEST_LOWER_BOUND,
|
|
102
|
+
});
|
|
103
|
+
if (!refined.source) {
|
|
104
|
+
refined = originalPositionFor(svelteMap, {
|
|
105
|
+
line: pos.line,
|
|
106
|
+
column: Number.MAX_SAFE_INTEGER,
|
|
107
|
+
bias: GREATEST_LOWER_BOUND,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
if (refined.source && refined.line != null) {
|
|
111
|
+
const refinedAbs = pathResolve(dirname(abs), refined.source);
|
|
112
|
+
const rel = refinedAbs.startsWith(process.cwd() + "/")
|
|
113
|
+
? refinedAbs.slice(process.cwd().length + 1)
|
|
114
|
+
: refinedAbs;
|
|
115
|
+
return { file: rel, line: refined.line, col: refined.column ?? 1 };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
60
120
|
const rel = abs.startsWith(process.cwd() + "/") ? abs.slice(process.cwd().length + 1) : abs;
|
|
61
121
|
return { file: rel, line: pos.line, col: pos.column ?? 1 };
|
|
62
122
|
}
|
|
@@ -3,6 +3,29 @@ import type { BunPlugin } from "bun";
|
|
|
3
3
|
|
|
4
4
|
const svelteHash = (s: string) => Bun.hash(s, 5381).toString(36);
|
|
5
5
|
|
|
6
|
+
// Bun's bundler does not chain sourcemaps from `onLoad` results, so the final
|
|
7
|
+
// bundle map points at the compiled svelte output (e.g. `$.next()`) using the
|
|
8
|
+
// .svelte filename — runtime stacks resolve to nonsense line numbers past EOF.
|
|
9
|
+
// We capture each per-file svelte compile map here, keyed by absolute source
|
|
10
|
+
// path; `remapBundleSourcemaps()` reads these after `Bun.build` and rewrites
|
|
11
|
+
// the output `.map` files to chain back to original source positions.
|
|
12
|
+
export const svelteMapCache = new Map<string, unknown>();
|
|
13
|
+
|
|
14
|
+
// Svelte 5 dev compile emits named `function get()` / `function set($$value)`
|
|
15
|
+
// expressions inside `$.bind_*` calls (for nicer `$inspect` stack traces). Bun's
|
|
16
|
+
// bundler destructures `import * as $ from "svelte/internal/client"` into named
|
|
17
|
+
// imports, so `$.get(search)` becomes plain `get(search)` — which collides with
|
|
18
|
+
// the wrapping function name and recurses into itself → RangeError. Prod compile
|
|
19
|
+
// uses anonymous arrow functions and is unaffected.
|
|
20
|
+
//
|
|
21
|
+
// Rename to `$$g` / `$$s` (3 chars — length-preserving so cached svelte source
|
|
22
|
+
// map columns stay accurate). These names aren't present in svelte/internal/client.
|
|
23
|
+
function fixBindShadow(code: string): string {
|
|
24
|
+
return code
|
|
25
|
+
.replace(/\bfunction get\(\)/g, () => "function $$g()")
|
|
26
|
+
.replace(/\bfunction set\(\$\$value\)/g, () => "function $$s($$value)");
|
|
27
|
+
}
|
|
28
|
+
|
|
6
29
|
export function makeBosiaSvelteCompiler(target: "browser" | "bun"): BunPlugin {
|
|
7
30
|
const generate = target === "browser" ? "client" : "server";
|
|
8
31
|
const dev = process.env.NODE_ENV !== "production";
|
|
@@ -25,7 +48,19 @@ export function makeBosiaSvelteCompiler(target: "browser" | "bun"): BunPlugin {
|
|
|
25
48
|
cssHash: ({ css }) => `svelte-${svelteHash(css)}`,
|
|
26
49
|
filename: args.path,
|
|
27
50
|
});
|
|
28
|
-
|
|
51
|
+
// Only the client target's map is useful to the inspector's runtime
|
|
52
|
+
// resolver — browser-side stack frames are what we need to translate.
|
|
53
|
+
// Server (Bun) compile output has different line numbers and would
|
|
54
|
+
// clobber the client entry under the same cache key.
|
|
55
|
+
if (dev && target === "browser" && result.js.map) {
|
|
56
|
+
const m =
|
|
57
|
+
typeof result.js.map === "string"
|
|
58
|
+
? JSON.parse(result.js.map)
|
|
59
|
+
: result.js.map;
|
|
60
|
+
svelteMapCache.set(args.path, m);
|
|
61
|
+
}
|
|
62
|
+
const contents = dev ? fixBindShadow(result.js.code) : result.js.code;
|
|
63
|
+
return { contents, loader: "ts" };
|
|
29
64
|
});
|
|
30
65
|
|
|
31
66
|
build.onLoad({ filter: /\.svelte\.[tj]s$/ }, async (args) => {
|
|
@@ -38,6 +73,13 @@ export function makeBosiaSvelteCompiler(target: "browser" | "bun"): BunPlugin {
|
|
|
38
73
|
dev,
|
|
39
74
|
filename: args.path,
|
|
40
75
|
});
|
|
76
|
+
if (dev && target === "browser" && result.js.map) {
|
|
77
|
+
const m =
|
|
78
|
+
typeof result.js.map === "string"
|
|
79
|
+
? JSON.parse(result.js.map)
|
|
80
|
+
: result.js.map;
|
|
81
|
+
svelteMapCache.set(args.path, m);
|
|
82
|
+
}
|
|
41
83
|
return { contents: result.js.code, loader: "js" };
|
|
42
84
|
});
|
|
43
85
|
},
|