@orochibraru/svelte-smol 1.5.2 → 1.6.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.
@@ -0,0 +1,149 @@
1
+ import type { Adapter } from "@sveltejs/kit";
2
+ export interface AdapterOptions {
3
+ /**
4
+ * Output directory. Holds the compiled executable plus the `client/` and
5
+ * `prerendered/` folders it serves.
6
+ * @default "build"
7
+ */
8
+ out?: string;
9
+ /**
10
+ * Filename of the compiled executable, written into `out`. Ignored when
11
+ * {@link AdapterOptions.compile | `compile`} is `false` (the server bundle
12
+ * is always `index.js` then).
13
+ * @default "server"
14
+ */
15
+ name?: string;
16
+ /**
17
+ * Compile the server to a single standalone executable with
18
+ * `bun build --compile`. Turn this off to emit a plain `build/index.js`
19
+ * bundle instead, run with `bun run build/index.js`. Pure-JS dependencies
20
+ * are still bundled in; a native (`.node`) addon can't be, so it resolves
21
+ * from `node_modules` at runtime — which a `bun` process can do and a
22
+ * compiled binary can't. This is the only way to ship `sharp`, `sqlite3`
23
+ * and the like. The `healthcheck` binary is still compiled either way.
24
+ * @default true
25
+ */
26
+ compile?: boolean;
27
+ /**
28
+ * Cross-compilation target for `bun build --compile`, e.g.
29
+ * `"bun-linux-x64"`, `"bun-linux-arm64-musl"`, `"bun-darwin-arm64"`,
30
+ * `"bun-windows-x64"` (optional `-modern` / `-baseline` SIMD suffix, and
31
+ * `-musl` for Alpine). Bun downloads the matching runtime on first use.
32
+ * @default the host platform
33
+ */
34
+ target?: Bun.Build.CompileTarget;
35
+ /**
36
+ * Emit a V8 bytecode cache into the executable for faster cold starts, at
37
+ * the cost of a larger binary.
38
+ * @default false
39
+ */
40
+ bytecode?: boolean;
41
+ /**
42
+ * Minify the bundled server code before embedding it.
43
+ * @default false
44
+ */
45
+ minify?: boolean;
46
+ /**
47
+ * Embed a source map so server stack traces point at original code.
48
+ * @default false
49
+ */
50
+ sourcemap?: boolean;
51
+ /**
52
+ * Pre-compress client assets and prerendered pages to `.gz` / `.br`
53
+ * siblings. The handler serves them with the matching `Content-Encoding`
54
+ * when the request's `Accept-Encoding` allows.
55
+ * @default false
56
+ */
57
+ precompress?: boolean;
58
+ /**
59
+ * Compile a second tiny executable, `healthcheck`, alongside the server
60
+ * and expose a matching `GET` endpoint. The binary probes that endpoint
61
+ * over loopback (or the Unix socket) and exits `0` when healthy, `1`
62
+ * otherwise — ready to drop into a Docker `HEALTHCHECK`. Pass an object
63
+ * to change the endpoint path.
64
+ * @default true
65
+ */
66
+ healthcheck?: boolean | {
67
+ path?: string;
68
+ };
69
+ /**
70
+ * Prefix for this adapter's own runtime env vars (`PORT`, `HOST`,
71
+ * `ORIGIN`, ...).
72
+ * @default ""
73
+ */
74
+ envPrefix?: string;
75
+ /**
76
+ * Serve the app's static assets and prerendered pages from the
77
+ * executable. The `client/` and `prerendered/` folders must sit next to
78
+ * the binary (or point `ASSETS_DIR` at their parent). Turn off when a
79
+ * reverse proxy or CDN serves them instead.
80
+ * @default true
81
+ */
82
+ serveAssets?: boolean;
83
+ /**
84
+ * Extra options merged into the `Bun.serve()` call (`tls`, `reusePort`,
85
+ * `maxConnections`, a custom `error` handler, ...). Applied *before* this
86
+ * adapter's own required fields (`idleTimeout`, `maxRequestBodySize`,
87
+ * `fetch`, `hostname`/`port`/`unix`, `websocket`), so it can't be used to
88
+ * override request handling, only to add to it. Use the existing env vars
89
+ * (`IDLE_TIMEOUT`, `BODY_SIZE_LIMIT`, `HOST`/`PORT`, `SOCKET_PATH`,
90
+ * `SHUTDOWN_TIMEOUT`) to change those instead.
91
+ * @default {}
92
+ */
93
+ serveOptions?: Record<string, unknown>;
94
+ }
95
+ /**
96
+ * SvelteKit adapter that compiles the app into a single standalone executable
97
+ * with `bun build --compile`.
98
+ *
99
+ * `adapt()` writes the SvelteKit server and this package's entrypoint templates
100
+ * into a temp directory, then compiles them — `@sveltejs/kit` and every other
101
+ * pure-JS dependency bundled in — into one binary. The build must run under the
102
+ * Bun runtime (`bun --bun vite build`, or a `bunfig.toml` with `[run] bun =
103
+ * true`); loading the config alone works under Node too.
104
+ *
105
+ * With {@link AdapterOptions.compile | `compile: false`} it emits a plain
106
+ * `build/index.js` bundle instead (run with `bun`), the only way to ship a
107
+ * native (`.node`) addon.
108
+ *
109
+ * Output, all written to {@link AdapterOptions.out | `out`}:
110
+ *
111
+ * ```text
112
+ * build/
113
+ * ├── server the executable (rename with `name`)
114
+ * ├── client/ static assets, served by the executable
115
+ * └── prerendered/ prerendered pages, served by the executable
116
+ * ```
117
+ *
118
+ * The executable resolves `client/` and `prerendered/` from its own location
119
+ * (`dirname(process.execPath)`, overridable with the `ASSETS_DIR` env var), so
120
+ * it can run from any working directory. Deploy the whole `out` directory, or
121
+ * just the binary when {@link AdapterOptions.serveAssets | `serveAssets`} is
122
+ * off and a proxy/CDN serves the assets.
123
+ *
124
+ * Runtime configuration (`HOST`, `PORT`, `ORIGIN`, `BODY_SIZE_LIMIT`, …) is
125
+ * read from environment variables, optionally namespaced by
126
+ * {@link AdapterOptions.envPrefix | `envPrefix`}.
127
+ *
128
+ * @param options - see {@link AdapterOptions}
129
+ * @returns the configured SvelteKit {@link Adapter}
130
+ *
131
+ * @example
132
+ * ```js
133
+ * // svelte.config.js
134
+ * import adapter from "@orochibraru/svelte-smol";
135
+ *
136
+ * export default {
137
+ * kit: {
138
+ * adapter: adapter({
139
+ * // cross-compile for an Alpine container from any host
140
+ * target: "bun-linux-x64-musl",
141
+ * bytecode: true,
142
+ * }),
143
+ * },
144
+ * };
145
+ * ```
146
+ *
147
+ * @see {@link https://bun.com/docs/bundler/executables | Bun — Single-file executables}
148
+ */
149
+ export default function adapter(options?: AdapterOptions): Adapter;
package/dist/index.js ADDED
@@ -0,0 +1,181 @@
1
+ import { fileURLToPath } from "node:url";
2
+ // `node:url`, not `Bun.fileURLToPath`: this module is loaded at config-parse
3
+ // time by Node-based tooling too (svelte-check, the Svelte language server,
4
+ // `svelte-kit sync`), where `Bun` is undefined. Everything that actually needs
5
+ // the Bun runtime lives inside `adapt()`.
6
+ const templates = fileURLToPath(new URL("./templates", import.meta.url));
7
+ /**
8
+ * SvelteKit adapter that compiles the app into a single standalone executable
9
+ * with `bun build --compile`.
10
+ *
11
+ * `adapt()` writes the SvelteKit server and this package's entrypoint templates
12
+ * into a temp directory, then compiles them — `@sveltejs/kit` and every other
13
+ * pure-JS dependency bundled in — into one binary. The build must run under the
14
+ * Bun runtime (`bun --bun vite build`, or a `bunfig.toml` with `[run] bun =
15
+ * true`); loading the config alone works under Node too.
16
+ *
17
+ * With {@link AdapterOptions.compile | `compile: false`} it emits a plain
18
+ * `build/index.js` bundle instead (run with `bun`), the only way to ship a
19
+ * native (`.node`) addon.
20
+ *
21
+ * Output, all written to {@link AdapterOptions.out | `out`}:
22
+ *
23
+ * ```text
24
+ * build/
25
+ * ├── server the executable (rename with `name`)
26
+ * ├── client/ static assets, served by the executable
27
+ * └── prerendered/ prerendered pages, served by the executable
28
+ * ```
29
+ *
30
+ * The executable resolves `client/` and `prerendered/` from its own location
31
+ * (`dirname(process.execPath)`, overridable with the `ASSETS_DIR` env var), so
32
+ * it can run from any working directory. Deploy the whole `out` directory, or
33
+ * just the binary when {@link AdapterOptions.serveAssets | `serveAssets`} is
34
+ * off and a proxy/CDN serves the assets.
35
+ *
36
+ * Runtime configuration (`HOST`, `PORT`, `ORIGIN`, `BODY_SIZE_LIMIT`, …) is
37
+ * read from environment variables, optionally namespaced by
38
+ * {@link AdapterOptions.envPrefix | `envPrefix`}.
39
+ *
40
+ * @param options - see {@link AdapterOptions}
41
+ * @returns the configured SvelteKit {@link Adapter}
42
+ *
43
+ * @example
44
+ * ```js
45
+ * // svelte.config.js
46
+ * import adapter from "@orochibraru/svelte-smol";
47
+ *
48
+ * export default {
49
+ * kit: {
50
+ * adapter: adapter({
51
+ * // cross-compile for an Alpine container from any host
52
+ * target: "bun-linux-x64-musl",
53
+ * bytecode: true,
54
+ * }),
55
+ * },
56
+ * };
57
+ * ```
58
+ *
59
+ * @see {@link https://bun.com/docs/bundler/executables | Bun — Single-file executables}
60
+ */
61
+ export default function adapter(options = {}) {
62
+ const { out = "build", name = "server", compile = true, target, bytecode = false, minify = false, sourcemap = false, precompress = false, healthcheck = true, envPrefix = "", serveAssets = true, serveOptions = {}, } = options;
63
+ const healthcheckConfig = healthcheck === false
64
+ ? false
65
+ : {
66
+ path: (healthcheck === true ? undefined : healthcheck.path) ?? "/_health",
67
+ };
68
+ return {
69
+ name: "@orochibraru/svelte-smol",
70
+ supports: {
71
+ instrumentation: () => true,
72
+ read: () => true,
73
+ },
74
+ async adapt(builder) {
75
+ const { base } = builder.config.kit.paths;
76
+ const tmp = builder.getBuildDirectory("adapter-bun");
77
+ builder.rimraf(out);
78
+ builder.rimraf(tmp);
79
+ builder.mkdirp(`${out}/`);
80
+ builder.mkdirp(tmp);
81
+ builder.log.minor("Copying assets");
82
+ builder.writeClient(`${out}/client${base}`);
83
+ builder.writePrerendered(`${out}/prerendered${base}`);
84
+ if (precompress) {
85
+ builder.log.minor("Compressing assets");
86
+ await Promise.all([
87
+ builder.compress(`${out}/client`),
88
+ builder.compress(`${out}/prerendered`),
89
+ ]);
90
+ }
91
+ builder.log.minor("Building server");
92
+ builder.writeServer(`${tmp}/server`);
93
+ await Bun.write(`${tmp}/server/manifest.js`, [
94
+ `export const manifest = ${builder.generateManifest({ relativePath: "./" })};`,
95
+ `export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});`,
96
+ `export const base = ${JSON.stringify(base)};`,
97
+ ].join("\n\n"));
98
+ // Entry templates ship as raw `.ts` — `bun build --compile` runs them
99
+ // straight through. The virtual specifiers (`ENV`, `MANIFEST`,
100
+ // `SERVER`, `HANDLER`) and the `ENV_PREFIX` / `BUILD_OPTIONS` /
101
+ // `SERVE_OPTIONS` tokens are resolved by this raw word-boundary token
102
+ // swap over the copied source.
103
+ builder.log.minor("Copying entrypoint");
104
+ builder.copy(templates, tmp, {
105
+ replace: {
106
+ BUILD_OPTIONS: JSON.stringify({
107
+ serveAssets,
108
+ precompress,
109
+ healthcheck: healthcheckConfig,
110
+ compiled: compile,
111
+ }),
112
+ ENV: "./env.ts",
113
+ ENV_PREFIX: JSON.stringify(envPrefix),
114
+ HANDLER: "./handler.ts",
115
+ MANIFEST: "./server/manifest.js",
116
+ SERVE_OPTIONS: JSON.stringify(serveOptions),
117
+ SERVER: "./server/index.js",
118
+ },
119
+ });
120
+ const entry = `${tmp}/index.ts`;
121
+ if (builder.hasServerInstrumentationFile?.()) {
122
+ // Instrumentation (OpenTelemetry &c.) has to load before anything
123
+ // else in the bundle. A compiled binary has no post-build
124
+ // entrypoint to rewrite, so prepend the import to the compile
125
+ // entrypoint instead.
126
+ await Bun.write(entry, `import "./server/instrumentation.server.js";\n${await Bun.file(entry).text()}`);
127
+ }
128
+ const check = (result, label) => {
129
+ if (!result.success) {
130
+ for (const message of result.logs) {
131
+ builder.log.error(String(message));
132
+ }
133
+ throw new Error(`\`bun build\` failed for ${label}`);
134
+ }
135
+ };
136
+ const compileBinary = async (entrypoint, outName) => {
137
+ builder.log.minor(target ? `Compiling ${outName} (${target})` : `Compiling ${outName}`);
138
+ check(await Bun.build({
139
+ entrypoints: [entrypoint],
140
+ target: "bun",
141
+ minify,
142
+ bytecode,
143
+ sourcemap: sourcemap ? "linked" : "none",
144
+ compile: {
145
+ outfile: `${out}/${outName}`,
146
+ ...(target ? { target } : {}),
147
+ },
148
+ }), outName);
149
+ builder.log.success(`Compiled ${out}/${outName}`);
150
+ };
151
+ const bundleServer = async (entrypoint) => {
152
+ builder.log.minor("Bundling index.js");
153
+ // Same bundle as the compiled binary, just emitted as a file. Every
154
+ // pure-JS dependency is inlined; a native (`.node`) addon can't be,
155
+ // so its `require` stays in the output and resolves from
156
+ // `node_modules` at runtime — which is exactly what a plain `bun`
157
+ // process (unlike a compiled binary) can do. Ship `node_modules`
158
+ // for those; anything fully bundled needn't be installed.
159
+ check(await Bun.build({
160
+ entrypoints: [entrypoint],
161
+ target: "bun",
162
+ format: "esm",
163
+ minify,
164
+ sourcemap: sourcemap ? "linked" : "none",
165
+ outdir: out,
166
+ naming: "index.js",
167
+ }), "index.js");
168
+ builder.log.success(`Bundled ${out}/index.js`);
169
+ };
170
+ if (compile) {
171
+ await compileBinary(entry, name);
172
+ }
173
+ else {
174
+ await bundleServer(entry);
175
+ }
176
+ if (healthcheckConfig) {
177
+ await compileBinary(`${tmp}/healthcheck.ts`, "healthcheck");
178
+ }
179
+ },
180
+ };
181
+ }
@@ -166,7 +166,10 @@ const ssr = async (request: Request, bunServer: Bun.Server<undefined>) => {
166
166
  return asset;
167
167
  }
168
168
 
169
- const baseOrigin = origin || get_origin(request.headers);
169
+ const baseOrigin =
170
+ same_host_remote_origin(request, url) ||
171
+ origin ||
172
+ get_origin(request.headers);
170
173
  const path = request.url.slice(request.url.split("/", 3).join("/").length);
171
174
  const newRequest = new Request(baseOrigin + path, request);
172
175
 
@@ -233,3 +236,24 @@ function get_origin(headers: Headers) {
233
236
 
234
237
  return port ? `${protocol}://${host}:${port}` : `${protocol}://${host}`;
235
238
  }
239
+
240
+ // Remote function calls are CSRF-checked against the request origin. When the
241
+ // browser's Origin header matches the Host, use it instead of ORIGIN.
242
+ function same_host_remote_origin(request: Request, url: URL) {
243
+ const request_origin = request.headers.get("origin");
244
+ if (
245
+ !request_origin ||
246
+ !url.pathname.startsWith(`${base}/${manifest.appDir}/remote/`)
247
+ ) {
248
+ return null;
249
+ }
250
+ try {
251
+ const parsed = new URL(request_origin);
252
+ const host =
253
+ (host_header && request.headers.get(host_header)) ||
254
+ request.headers.get("host");
255
+ return parsed.host === host ? parsed.origin : null;
256
+ } catch {
257
+ return null;
258
+ }
259
+ }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@orochibraru/svelte-smol",
3
- "version": "1.5.2",
3
+ "version": "1.6.1",
4
4
  "type": "module",
5
- "main": "./index.ts",
5
+ "main": "./dist/index.js",
6
6
  "keywords": [
7
7
  "bun",
8
8
  "svelte",
@@ -10,19 +10,20 @@
10
10
  "adapter",
11
11
  "typescript"
12
12
  ],
13
- "module": "./index.ts",
14
- "types": "./index.ts",
13
+ "module": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
15
  "author": {
16
16
  "email": "orochibraru@gmail.com",
17
17
  "name": "orochibraru"
18
18
  },
19
19
  "exports": {
20
- ".": "./index.ts"
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "default": "./dist/index.js"
23
+ }
21
24
  },
22
25
  "files": [
23
- "index.ts",
24
- "internal.d.ts",
25
- "templates"
26
+ "dist"
26
27
  ],
27
28
  "repository": {
28
29
  "type": "git",
@@ -32,6 +33,8 @@
32
33
  "access": "public"
33
34
  },
34
35
  "scripts": {
36
+ "build": "rm -rf dist && tsc -p tsconfig.build.json && cp -R templates dist/templates",
37
+ "prepack": "bun run build",
35
38
  "typecheck": "tsc --noEmit -p tsconfig.json",
36
39
  "test": "bun test index.test.ts",
37
40
  "test:integration": "bun test test/integration.test.ts",
package/index.ts DELETED
@@ -1,319 +0,0 @@
1
- import { fileURLToPath } from "node:url";
2
- import type { Adapter, Builder } from "@sveltejs/kit";
3
-
4
- export interface AdapterOptions {
5
- /**
6
- * Output directory. Holds the compiled executable plus the `client/` and
7
- * `prerendered/` folders it serves.
8
- * @default "build"
9
- */
10
- out?: string;
11
- /**
12
- * Filename of the compiled executable, written into `out`. Ignored when
13
- * {@link AdapterOptions.compile | `compile`} is `false` (the server bundle
14
- * is always `index.js` then).
15
- * @default "server"
16
- */
17
- name?: string;
18
- /**
19
- * Compile the server to a single standalone executable with
20
- * `bun build --compile`. Turn this off to emit a plain `build/index.js`
21
- * bundle instead, run with `bun run build/index.js`. Pure-JS dependencies
22
- * are still bundled in; a native (`.node`) addon can't be, so it resolves
23
- * from `node_modules` at runtime — which a `bun` process can do and a
24
- * compiled binary can't. This is the only way to ship `sharp`, `sqlite3`
25
- * and the like. The `healthcheck` binary is still compiled either way.
26
- * @default true
27
- */
28
- compile?: boolean;
29
- /**
30
- * Cross-compilation target for `bun build --compile`, e.g.
31
- * `"bun-linux-x64"`, `"bun-linux-arm64-musl"`, `"bun-darwin-arm64"`,
32
- * `"bun-windows-x64"` (optional `-modern` / `-baseline` SIMD suffix, and
33
- * `-musl` for Alpine). Bun downloads the matching runtime on first use.
34
- * @default the host platform
35
- */
36
- target?: Bun.Build.CompileTarget;
37
- /**
38
- * Emit a V8 bytecode cache into the executable for faster cold starts, at
39
- * the cost of a larger binary.
40
- * @default false
41
- */
42
- bytecode?: boolean;
43
- /**
44
- * Minify the bundled server code before embedding it.
45
- * @default false
46
- */
47
- minify?: boolean;
48
- /**
49
- * Embed a source map so server stack traces point at original code.
50
- * @default false
51
- */
52
- sourcemap?: boolean;
53
- /**
54
- * Pre-compress client assets and prerendered pages to `.gz` / `.br`
55
- * siblings. The handler serves them with the matching `Content-Encoding`
56
- * when the request's `Accept-Encoding` allows.
57
- * @default false
58
- */
59
- precompress?: boolean;
60
- /**
61
- * Compile a second tiny executable, `healthcheck`, alongside the server
62
- * and expose a matching `GET` endpoint. The binary probes that endpoint
63
- * over loopback (or the Unix socket) and exits `0` when healthy, `1`
64
- * otherwise — ready to drop into a Docker `HEALTHCHECK`. Pass an object
65
- * to change the endpoint path.
66
- * @default true
67
- */
68
- healthcheck?: boolean | { path?: string };
69
- /**
70
- * Prefix for this adapter's own runtime env vars (`PORT`, `HOST`,
71
- * `ORIGIN`, ...).
72
- * @default ""
73
- */
74
- envPrefix?: string;
75
- /**
76
- * Serve the app's static assets and prerendered pages from the
77
- * executable. The `client/` and `prerendered/` folders must sit next to
78
- * the binary (or point `ASSETS_DIR` at their parent). Turn off when a
79
- * reverse proxy or CDN serves them instead.
80
- * @default true
81
- */
82
- serveAssets?: boolean;
83
- /**
84
- * Extra options merged into the `Bun.serve()` call (`tls`, `reusePort`,
85
- * `maxConnections`, a custom `error` handler, ...). Applied *before* this
86
- * adapter's own required fields (`idleTimeout`, `maxRequestBodySize`,
87
- * `fetch`, `hostname`/`port`/`unix`, `websocket`), so it can't be used to
88
- * override request handling, only to add to it. Use the existing env vars
89
- * (`IDLE_TIMEOUT`, `BODY_SIZE_LIMIT`, `HOST`/`PORT`, `SOCKET_PATH`,
90
- * `SHUTDOWN_TIMEOUT`) to change those instead.
91
- * @default {}
92
- */
93
- serveOptions?: Record<string, unknown>;
94
- }
95
-
96
- // `node:url`, not `Bun.fileURLToPath`: this module is loaded at config-parse
97
- // time by Node-based tooling too (svelte-check, the Svelte language server,
98
- // `svelte-kit sync`), where `Bun` is undefined. Everything that actually needs
99
- // the Bun runtime lives inside `adapt()`.
100
- const templates = fileURLToPath(new URL("./templates", import.meta.url));
101
-
102
- /**
103
- * SvelteKit adapter that compiles the app into a single standalone executable
104
- * with `bun build --compile`.
105
- *
106
- * `adapt()` writes the SvelteKit server and this package's entrypoint templates
107
- * into a temp directory, then compiles them — `@sveltejs/kit` and every other
108
- * pure-JS dependency bundled in — into one binary. The build must run under the
109
- * Bun runtime (`bun --bun vite build`, or a `bunfig.toml` with `[run] bun =
110
- * true`); loading the config alone works under Node too.
111
- *
112
- * With {@link AdapterOptions.compile | `compile: false`} it emits a plain
113
- * `build/index.js` bundle instead (run with `bun`), the only way to ship a
114
- * native (`.node`) addon.
115
- *
116
- * Output, all written to {@link AdapterOptions.out | `out`}:
117
- *
118
- * ```text
119
- * build/
120
- * ├── server the executable (rename with `name`)
121
- * ├── client/ static assets, served by the executable
122
- * └── prerendered/ prerendered pages, served by the executable
123
- * ```
124
- *
125
- * The executable resolves `client/` and `prerendered/` from its own location
126
- * (`dirname(process.execPath)`, overridable with the `ASSETS_DIR` env var), so
127
- * it can run from any working directory. Deploy the whole `out` directory, or
128
- * just the binary when {@link AdapterOptions.serveAssets | `serveAssets`} is
129
- * off and a proxy/CDN serves the assets.
130
- *
131
- * Runtime configuration (`HOST`, `PORT`, `ORIGIN`, `BODY_SIZE_LIMIT`, …) is
132
- * read from environment variables, optionally namespaced by
133
- * {@link AdapterOptions.envPrefix | `envPrefix`}.
134
- *
135
- * @param options - see {@link AdapterOptions}
136
- * @returns the configured SvelteKit {@link Adapter}
137
- *
138
- * @example
139
- * ```js
140
- * // svelte.config.js
141
- * import adapter from "@orochibraru/svelte-smol";
142
- *
143
- * export default {
144
- * kit: {
145
- * adapter: adapter({
146
- * // cross-compile for an Alpine container from any host
147
- * target: "bun-linux-x64-musl",
148
- * bytecode: true,
149
- * }),
150
- * },
151
- * };
152
- * ```
153
- *
154
- * @see {@link https://bun.com/docs/bundler/executables | Bun — Single-file executables}
155
- */
156
- export default function adapter(options: AdapterOptions = {}): Adapter {
157
- const {
158
- out = "build",
159
- name = "server",
160
- compile = true,
161
- target,
162
- bytecode = false,
163
- minify = false,
164
- sourcemap = false,
165
- precompress = false,
166
- healthcheck = true,
167
- envPrefix = "",
168
- serveAssets = true,
169
- serveOptions = {},
170
- } = options;
171
-
172
- const healthcheckConfig =
173
- healthcheck === false
174
- ? false
175
- : {
176
- path:
177
- (healthcheck === true ? undefined : healthcheck.path) ?? "/_health",
178
- };
179
-
180
- return {
181
- name: "@orochibraru/svelte-smol",
182
- supports: {
183
- instrumentation: () => true,
184
- read: () => true,
185
- },
186
- async adapt(builder: Builder) {
187
- const { base } = builder.config.kit.paths;
188
- const tmp = builder.getBuildDirectory("adapter-bun");
189
-
190
- builder.rimraf(out);
191
- builder.rimraf(tmp);
192
- builder.mkdirp(`${out}/`);
193
- builder.mkdirp(tmp);
194
-
195
- builder.log.minor("Copying assets");
196
- builder.writeClient(`${out}/client${base}`);
197
- builder.writePrerendered(`${out}/prerendered${base}`);
198
-
199
- if (precompress) {
200
- builder.log.minor("Compressing assets");
201
- await Promise.all([
202
- builder.compress(`${out}/client`),
203
- builder.compress(`${out}/prerendered`),
204
- ]);
205
- }
206
-
207
- builder.log.minor("Building server");
208
- builder.writeServer(`${tmp}/server`);
209
- await Bun.write(
210
- `${tmp}/server/manifest.js`,
211
- [
212
- `export const manifest = ${builder.generateManifest({ relativePath: "./" })};`,
213
- `export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});`,
214
- `export const base = ${JSON.stringify(base)};`,
215
- ].join("\n\n"),
216
- );
217
-
218
- // Entry templates ship as raw `.ts` — `bun build --compile` runs them
219
- // straight through. The virtual specifiers (`ENV`, `MANIFEST`,
220
- // `SERVER`, `HANDLER`) and the `ENV_PREFIX` / `BUILD_OPTIONS` /
221
- // `SERVE_OPTIONS` tokens are resolved by this raw word-boundary token
222
- // swap over the copied source.
223
- builder.log.minor("Copying entrypoint");
224
- builder.copy(templates, tmp, {
225
- replace: {
226
- BUILD_OPTIONS: JSON.stringify({
227
- serveAssets,
228
- precompress,
229
- healthcheck: healthcheckConfig,
230
- compiled: compile,
231
- }),
232
- ENV: "./env.ts",
233
- ENV_PREFIX: JSON.stringify(envPrefix),
234
- HANDLER: "./handler.ts",
235
- MANIFEST: "./server/manifest.js",
236
- SERVE_OPTIONS: JSON.stringify(serveOptions),
237
- SERVER: "./server/index.js",
238
- },
239
- });
240
-
241
- const entry = `${tmp}/index.ts`;
242
- if (builder.hasServerInstrumentationFile?.()) {
243
- // Instrumentation (OpenTelemetry &c.) has to load before anything
244
- // else in the bundle. A compiled binary has no post-build
245
- // entrypoint to rewrite, so prepend the import to the compile
246
- // entrypoint instead.
247
- await Bun.write(
248
- entry,
249
- `import "./server/instrumentation.server.js";\n${await Bun.file(entry).text()}`,
250
- );
251
- }
252
-
253
- const check = (
254
- result: Awaited<ReturnType<typeof Bun.build>>,
255
- label: string,
256
- ) => {
257
- if (!result.success) {
258
- for (const message of result.logs) {
259
- builder.log.error(String(message));
260
- }
261
- throw new Error(`\`bun build\` failed for ${label}`);
262
- }
263
- };
264
-
265
- const compileBinary = async (entrypoint: string, outName: string) => {
266
- builder.log.minor(
267
- target ? `Compiling ${outName} (${target})` : `Compiling ${outName}`,
268
- );
269
- check(
270
- await Bun.build({
271
- entrypoints: [entrypoint],
272
- target: "bun",
273
- minify,
274
- bytecode,
275
- sourcemap: sourcemap ? "linked" : "none",
276
- compile: {
277
- outfile: `${out}/${outName}`,
278
- ...(target ? { target } : {}),
279
- },
280
- }),
281
- outName,
282
- );
283
- builder.log.success(`Compiled ${out}/${outName}`);
284
- };
285
-
286
- const bundleServer = async (entrypoint: string) => {
287
- builder.log.minor("Bundling index.js");
288
- // Same bundle as the compiled binary, just emitted as a file. Every
289
- // pure-JS dependency is inlined; a native (`.node`) addon can't be,
290
- // so its `require` stays in the output and resolves from
291
- // `node_modules` at runtime — which is exactly what a plain `bun`
292
- // process (unlike a compiled binary) can do. Ship `node_modules`
293
- // for those; anything fully bundled needn't be installed.
294
- check(
295
- await Bun.build({
296
- entrypoints: [entrypoint],
297
- target: "bun",
298
- format: "esm",
299
- minify,
300
- sourcemap: sourcemap ? "linked" : "none",
301
- outdir: out,
302
- naming: "index.js",
303
- }),
304
- "index.js",
305
- );
306
- builder.log.success(`Bundled ${out}/index.js`);
307
- };
308
-
309
- if (compile) {
310
- await compileBinary(entry, name);
311
- } else {
312
- await bundleServer(entry);
313
- }
314
- if (healthcheckConfig) {
315
- await compileBinary(`${tmp}/healthcheck.ts`, "healthcheck");
316
- }
317
- },
318
- };
319
- }
package/internal.d.ts DELETED
@@ -1,43 +0,0 @@
1
- // Ambient declarations for the virtual specifiers/globals `templates/*.ts`
2
- // reference. None of these are real modules or runtime globals at
3
- // type-check time, `index.ts`'s adapt() resolves them via a raw text
4
- // replacement over the compiled output (see its `builder.copy(..., {
5
- // replace })` call), so this file exists purely so `templates/*.ts`
6
- // typechecks against the shape that replacement actually produces.
7
-
8
- declare module "ENV" {
9
- export function env(name: string, fallback: string): string;
10
- export function env(name: string, fallback: false): string | false;
11
- export function env(name: string, fallback?: undefined): string | undefined;
12
- }
13
-
14
- declare module "HANDLER" {
15
- export function getHandler(): {
16
- fetch: (
17
- request: Request,
18
- server: Bun.Server<undefined>,
19
- ) => Response | Promise<Response>;
20
- websocket: Bun.WebSocketHandler<undefined> | undefined;
21
- };
22
- }
23
-
24
- declare module "MANIFEST" {
25
- import type { SSRManifest } from "@sveltejs/kit";
26
-
27
- export const base: string;
28
- export const manifest: SSRManifest;
29
- export const prerendered: Set<string>;
30
- }
31
-
32
- declare module "SERVER" {
33
- export { Server } from "@sveltejs/kit";
34
- }
35
-
36
- declare const ENV_PREFIX: string;
37
- declare const BUILD_OPTIONS: {
38
- serveAssets: boolean;
39
- precompress: boolean;
40
- healthcheck: false | { path: string };
41
- compiled: boolean;
42
- };
43
- declare const SERVE_OPTIONS: Record<string, unknown>;
File without changes
File without changes
File without changes