@tschk/moonshine-deploy-bun 0.3.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/README.md +16 -0
- package/package.json +31 -0
- package/src/index.ts +131 -0
package/README.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# `@tschk/moonshine-deploy-bun`
|
|
2
|
+
|
|
3
|
+
Bun deployment adapter for Moonshine.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { bunAdapter, createBunServer } from "@tschk/moonshine-deploy-bun";
|
|
7
|
+
|
|
8
|
+
await bunAdapter.build(manifest, ".moonshine");
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- Target runtime: `bun`.
|
|
12
|
+
- Capabilities: `streaming`, `islands`, `revalidation`.
|
|
13
|
+
- Output: `server.ts` using `Bun.serve`, static files from the build directory.
|
|
14
|
+
|
|
15
|
+
See [docs/DEPLOYMENT.md](../../docs/DEPLOYMENT.md) and
|
|
16
|
+
[docs/MANIFEST.md](../../docs/MANIFEST.md).
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tschk/moonshine-deploy-bun",
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"license": "ISC",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.ts",
|
|
7
|
+
"types": "./src/index.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/index.ts"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"format": "bunx prettier --check .",
|
|
13
|
+
"lint": "bunx oxlint .",
|
|
14
|
+
"typecheck": "bunx tsc --noEmit",
|
|
15
|
+
"test": "bun test"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@tschk/moonshine-framework": "^0.3.1",
|
|
19
|
+
"@tschk/moonshine-server": "^0.3.1",
|
|
20
|
+
"@tschk/moonshine-compiler": "^0.3.1",
|
|
21
|
+
"@tschk/moonshine-react": "^0.3.1"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/bun": "1.3.14",
|
|
25
|
+
"@tschk/moonshine-adapter-conformance": "^0.3.1",
|
|
26
|
+
"typescript": "7.0.2"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"src"
|
|
30
|
+
]
|
|
31
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { copyFile, mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, isAbsolute, resolve } from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
type DeploymentAdapter,
|
|
5
|
+
type MoonshineManifest,
|
|
6
|
+
} from "@tschk/moonshine-framework";
|
|
7
|
+
import { tryServeStatic } from "@tschk/moonshine-server";
|
|
8
|
+
import {
|
|
9
|
+
type Harness,
|
|
10
|
+
type HarnessFactory,
|
|
11
|
+
} from "@tschk/moonshine-adapter-conformance";
|
|
12
|
+
|
|
13
|
+
export type BunServerOptions = {
|
|
14
|
+
fetch: (request: Request) => Promise<Response>;
|
|
15
|
+
port?: number;
|
|
16
|
+
staticDir?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type BunServer = {
|
|
20
|
+
url: URL;
|
|
21
|
+
port: number;
|
|
22
|
+
stop: (force?: boolean) => Promise<void>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function createBunServer(options: BunServerOptions): BunServer {
|
|
26
|
+
const fetch = async (request: Request): Promise<Response> => {
|
|
27
|
+
const url = new URL(request.url);
|
|
28
|
+
const pathname = url.pathname.replace(/\/+$/, "") || "/";
|
|
29
|
+
if (
|
|
30
|
+
options.staticDir &&
|
|
31
|
+
(request.method === "GET" || request.method === "HEAD") &&
|
|
32
|
+
pathname !== "/"
|
|
33
|
+
) {
|
|
34
|
+
const res = await tryServeStatic(options.staticDir, pathname);
|
|
35
|
+
if (res) return res;
|
|
36
|
+
}
|
|
37
|
+
return options.fetch(request);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return Bun.serve({
|
|
41
|
+
port: options.port ?? 0,
|
|
42
|
+
fetch,
|
|
43
|
+
}) as BunServer;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const bunHarness: HarnessFactory = (fetch, options) => {
|
|
47
|
+
const server = createBunServer({
|
|
48
|
+
fetch,
|
|
49
|
+
port: 0,
|
|
50
|
+
staticDir: options.staticDir,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
start: async () => server.url,
|
|
55
|
+
stop: async () => {
|
|
56
|
+
await server.stop(true);
|
|
57
|
+
},
|
|
58
|
+
} as Harness;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
async function copyAsset(
|
|
62
|
+
asset: MoonshineManifest["assets"][number],
|
|
63
|
+
outDir: string,
|
|
64
|
+
): Promise<string> {
|
|
65
|
+
const source = isAbsolute(asset.file)
|
|
66
|
+
? asset.file
|
|
67
|
+
: resolve(outDir, asset.file);
|
|
68
|
+
const target = resolve(
|
|
69
|
+
outDir,
|
|
70
|
+
(asset.path ?? asset.file).replace(/^\/+/, ""),
|
|
71
|
+
);
|
|
72
|
+
await mkdir(dirname(target), { recursive: true });
|
|
73
|
+
if (source !== target) {
|
|
74
|
+
await copyFile(source, target);
|
|
75
|
+
}
|
|
76
|
+
return (asset.path ?? asset.file).replace(/^\/+/, "");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const bunAdapter: DeploymentAdapter = {
|
|
80
|
+
name: "bun",
|
|
81
|
+
runtimes: ["bun"],
|
|
82
|
+
capabilities: ["streaming", "islands", "revalidation"],
|
|
83
|
+
build: async (manifest, outDir) => {
|
|
84
|
+
await mkdir(outDir, { recursive: true });
|
|
85
|
+
const assets: MoonshineManifest["assets"] = [];
|
|
86
|
+
for (const asset of manifest.assets) {
|
|
87
|
+
const file = await copyAsset(asset, outDir);
|
|
88
|
+
assets.push({ ...asset, file });
|
|
89
|
+
}
|
|
90
|
+
const builtManifest: MoonshineManifest = { ...manifest, assets };
|
|
91
|
+
await writeFile(
|
|
92
|
+
resolve(outDir, "manifest.json"),
|
|
93
|
+
JSON.stringify(builtManifest, null, 2) + "\n",
|
|
94
|
+
);
|
|
95
|
+
const entry = `import { createRequestHandler } from "@tschk/moonshine-server";
|
|
96
|
+
import { reactRenderer } from "@tschk/moonshine-react";
|
|
97
|
+
import { createBunServer } from "@tschk/moonshine-deploy-bun";
|
|
98
|
+
import { resolve } from "node:path";
|
|
99
|
+
import manifest from "./manifest.json" with { type: "json" };
|
|
100
|
+
import { modules } from "./dist/server.js";
|
|
101
|
+
|
|
102
|
+
const projectDir = resolve(import.meta.dir, "..");
|
|
103
|
+
const abs = (p) => (p ? resolve(projectDir, p) : undefined);
|
|
104
|
+
const resolvedManifest = {
|
|
105
|
+
...manifest,
|
|
106
|
+
routes: manifest.routes.map((r) => ({
|
|
107
|
+
...r,
|
|
108
|
+
file: resolve(projectDir, r.file),
|
|
109
|
+
dataFile: abs(r.dataFile),
|
|
110
|
+
layouts: r.layouts?.map(abs).filter(Boolean),
|
|
111
|
+
middleware: r.middleware?.map(abs).filter(Boolean),
|
|
112
|
+
errorBoundary: abs(r.errorBoundary),
|
|
113
|
+
})),
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const fetch = createRequestHandler({
|
|
117
|
+
manifest: resolvedManifest,
|
|
118
|
+
modules,
|
|
119
|
+
renderer: reactRenderer,
|
|
120
|
+
staticDir: import.meta.dir,
|
|
121
|
+
});
|
|
122
|
+
const server = createBunServer({
|
|
123
|
+
fetch,
|
|
124
|
+
port: Number(process.env.PORT) || 0,
|
|
125
|
+
staticDir: import.meta.dir,
|
|
126
|
+
});
|
|
127
|
+
console.log(server.url.origin);
|
|
128
|
+
`;
|
|
129
|
+
await writeFile(resolve(outDir, "server.ts"), entry);
|
|
130
|
+
},
|
|
131
|
+
};
|