@ramonyx/bundler 0.1.1 → 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 +1 -1
- package/dist/assets.d.ts +76 -0
- package/dist/assets.js +197 -0
- package/dist/assets.js.map +1 -0
- package/dist/build.d.ts +26 -6
- package/dist/build.js +101 -8
- package/dist/build.js.map +1 -1
- package/dist/dev.d.ts +6 -0
- package/dist/dev.js +122 -2
- package/dist/dev.js.map +1 -1
- package/dist/devtools.d.ts +43 -0
- package/dist/devtools.js +146 -0
- package/dist/devtools.js.map +1 -0
- package/dist/edge.d.ts +28 -0
- package/dist/edge.js +85 -0
- package/dist/edge.js.map +1 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/manifest.d.ts +35 -0
- package/dist/manifest.js +29 -0
- package/dist/manifest.js.map +1 -0
- package/dist/plugin.d.ts +35 -0
- package/dist/plugin.js +46 -0
- package/dist/plugin.js.map +1 -0
- package/dist/prerender.d.ts +49 -0
- package/dist/prerender.js +104 -0
- package/dist/prerender.js.map +1 -0
- package/dist/server-only.d.ts +6 -0
- package/dist/server-only.js +69 -0
- package/dist/server-only.js.map +1 -0
- package/package.json +3 -3
package/dist/dev.js
CHANGED
|
@@ -1,10 +1,70 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { extname, join, relative } from "node:path";
|
|
3
4
|
import { Readable } from "node:stream";
|
|
4
5
|
import { pipeline } from "node:stream/promises";
|
|
5
6
|
import { generateClientEntry, loadPageRoutes, RouteManifestError, } from "@ramonyx/compiler";
|
|
6
7
|
import { createServer } from "vite";
|
|
8
|
+
import { createDevtoolsCollector, devtoolsPanelHtml } from "./devtools.js";
|
|
9
|
+
import { readAssetManifest } from "./manifest.js";
|
|
10
|
+
import { serverOnlyPlugin } from "./server-only.js";
|
|
7
11
|
const ENTRY_FILE = "client-entry.ts";
|
|
12
|
+
const DEV_ASSET_TYPES = {
|
|
13
|
+
".svg": "image/svg+xml",
|
|
14
|
+
".png": "image/png",
|
|
15
|
+
".jpg": "image/jpeg",
|
|
16
|
+
".jpeg": "image/jpeg",
|
|
17
|
+
".gif": "image/gif",
|
|
18
|
+
".webp": "image/webp",
|
|
19
|
+
".avif": "image/avif",
|
|
20
|
+
".ico": "image/x-icon",
|
|
21
|
+
".woff": "font/woff",
|
|
22
|
+
".woff2": "font/woff2",
|
|
23
|
+
".ttf": "font/ttf",
|
|
24
|
+
".otf": "font/otf",
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Dev: sajikan `src/assets/**` apa adanya di `/assets/*` (tanpa hash) — paritas dengan
|
|
28
|
+
* resolver aset dev (`createAssetResolver(..., { fallbackToPath: true })`, ADR-0069).
|
|
29
|
+
*/
|
|
30
|
+
function assetsDevPlugin(appDir) {
|
|
31
|
+
return {
|
|
32
|
+
name: "ramonyx-assets-dev",
|
|
33
|
+
configureServer(server) {
|
|
34
|
+
server.middlewares.use((req, res, next) => {
|
|
35
|
+
const url = req.url ?? "";
|
|
36
|
+
if (!url.startsWith("/assets/")) {
|
|
37
|
+
next();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
let rel;
|
|
41
|
+
try {
|
|
42
|
+
rel = decodeURIComponent(url.slice("/assets/".length).split("?")[0] ?? "");
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
next();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (rel.length === 0 || rel.includes("..")) {
|
|
49
|
+
next();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const file = join(appDir, "src", "assets", rel);
|
|
53
|
+
if (!existsSync(file)) {
|
|
54
|
+
next();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
void readFile(file)
|
|
58
|
+
.then((data) => {
|
|
59
|
+
res.setHeader("content-type", DEV_ASSET_TYPES[extname(file).toLowerCase()] ?? "application/octet-stream");
|
|
60
|
+
res.setHeader("cache-control", "no-cache");
|
|
61
|
+
res.end(data);
|
|
62
|
+
})
|
|
63
|
+
.catch(() => next());
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
8
68
|
/** Path yang dimiliki Vite (jangan diproksikan ke SSR). */
|
|
9
69
|
function isViteRequest(url) {
|
|
10
70
|
const path = url.split("?")[0] ?? "";
|
|
@@ -82,6 +142,33 @@ export async function createDevServer(options) {
|
|
|
82
142
|
const entryDir = join(appDir, ".ramonyx");
|
|
83
143
|
const entryFile = join(entryDir, ENTRY_FILE);
|
|
84
144
|
const routesDir = join(appDir, "src", "routes");
|
|
145
|
+
const devtools = createDevtoolsCollector();
|
|
146
|
+
const clientDir = join(appDir, "dist", "client");
|
|
147
|
+
/** Ukuran bundel/aset dari manifest build (bila sudah pernah dibangun). */
|
|
148
|
+
const refreshAssets = async () => {
|
|
149
|
+
const manifest = await readAssetManifest(clientDir);
|
|
150
|
+
if (manifest === null) {
|
|
151
|
+
devtools.setAssets([]);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const names = new Set([manifest.client, ...manifest.files]);
|
|
155
|
+
const entries = [];
|
|
156
|
+
for (const name of names) {
|
|
157
|
+
try {
|
|
158
|
+
const info = await stat(join(clientDir, name));
|
|
159
|
+
entries.push({ name, bytes: info.size });
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// berkas hilang (mis. dibersihkan) — lewati
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (manifest.assets !== undefined) {
|
|
166
|
+
for (const entry of Object.values(manifest.assets)) {
|
|
167
|
+
entries.push({ name: entry.file, bytes: entry.bytes });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
devtools.setAssets(entries);
|
|
171
|
+
};
|
|
85
172
|
const vite = await createServer({
|
|
86
173
|
configFile: false,
|
|
87
174
|
root: appDir,
|
|
@@ -90,6 +177,34 @@ export async function createDevServer(options) {
|
|
|
90
177
|
esbuild: { jsx: "automatic" },
|
|
91
178
|
server: { port: options.port ?? 5173 },
|
|
92
179
|
plugins: [
|
|
180
|
+
...(options.plugins ?? []),
|
|
181
|
+
serverOnlyPlugin(),
|
|
182
|
+
assetsDevPlugin(appDir),
|
|
183
|
+
{
|
|
184
|
+
name: "ramonyx-devtools",
|
|
185
|
+
configureServer(server) {
|
|
186
|
+
server.middlewares.use((req, res, next) => {
|
|
187
|
+
const path = (req.url ?? "").split("?")[0] ?? "";
|
|
188
|
+
if (path === "/__ramonyx/devtools") {
|
|
189
|
+
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
190
|
+
res.setHeader("cache-control", "no-store");
|
|
191
|
+
res.end(devtoolsPanelHtml());
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (path === "/__ramonyx/data") {
|
|
195
|
+
void refreshAssets()
|
|
196
|
+
.catch(() => undefined)
|
|
197
|
+
.then(() => {
|
|
198
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
199
|
+
res.setHeader("cache-control", "no-store");
|
|
200
|
+
res.end(JSON.stringify(devtools.snapshot()));
|
|
201
|
+
});
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
next();
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
},
|
|
93
208
|
{
|
|
94
209
|
name: "ramonyx-ssr-proxy",
|
|
95
210
|
configureServer(server) {
|
|
@@ -114,6 +229,10 @@ export async function createDevServer(options) {
|
|
|
114
229
|
};
|
|
115
230
|
const regenerateEntry = async () => {
|
|
116
231
|
const pages = await loadPageRoutes({ dir: routesDir, importer });
|
|
232
|
+
devtools.setRoutes(pages.map((page) => ({
|
|
233
|
+
path: page.path,
|
|
234
|
+
...(page.pageFile !== undefined ? { file: relative(appDir, page.pageFile) } : {}),
|
|
235
|
+
})));
|
|
117
236
|
await mkdir(entryDir, { recursive: true });
|
|
118
237
|
await writeFile(entryFile, generateClientEntry(pages, { from: entryDir }));
|
|
119
238
|
};
|
|
@@ -135,6 +254,7 @@ export async function createDevServer(options) {
|
|
|
135
254
|
url: `http://localhost:${port}`,
|
|
136
255
|
importer,
|
|
137
256
|
target: options.target,
|
|
257
|
+
devtools,
|
|
138
258
|
close: () => vite.close(),
|
|
139
259
|
};
|
|
140
260
|
}
|
package/dist/dev.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../../packages/bundler/src/dev.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport type { ServerResponse } from \"node:http\";\nimport { join, relative } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport {\n generateClientEntry,\n loadPageRoutes,\n type ModuleImporter,\n RouteManifestError,\n} from \"@ramonyx/compiler\";\nimport { type Connect, createServer } from \"vite\";\n\nexport interface DevServerOptions {\n readonly appDir: string;\n /** URL server SSR yang diproksikan (mis. \"http://localhost:3000\"). */\n readonly target: string;\n readonly port?: number;\n}\n\nexport interface DevServer {\n readonly url: string;\n /** Importer untuk compiler: modul mengikuti module graph Vite (invalidasi saat file berubah). */\n readonly importer: ModuleImporter;\n readonly target: string;\n close(): Promise<void>;\n}\n\nconst ENTRY_FILE = \"client-entry.ts\";\n\n/** Path yang dimiliki Vite (jangan diproksikan ke SSR). */\nfunction isViteRequest(url: string): boolean {\n const path = url.split(\"?\")[0] ?? \"\";\n return (\n path.startsWith(\"/@\") ||\n path.startsWith(\"/node_modules/\") ||\n path.startsWith(\"/.ramonyx/\") ||\n path.startsWith(\"/src/\") ||\n path === \"/favicon.ico\"\n );\n}\n\nconst HOP_BY_HOP = new Set([\n \"connection\",\n \"keep-alive\",\n \"transfer-encoding\",\n \"content-length\",\n \"content-encoding\",\n]);\n\n/**\n * Proxy manual ke SSR (bukan `server.proxy` Vite): request non-Vite diteruskan dengan `fetch`,\n * respons dipipe kembali apa adanya. Body request non-GET/HEAD dialirkan via `Readable.toWeb`.\n */\nasync function proxyToSsr(\n target: string,\n req: Connect.IncomingMessage,\n res: ServerResponse,\n next: Connect.NextFunction,\n): Promise<void> {\n try {\n const url = new URL(req.url ?? \"/\", target);\n const headers = new Headers();\n for (const [name, value] of Object.entries(req.headers)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(name, item);\n }\n } else {\n headers.set(name, value);\n }\n }\n\n const method = req.method ?? \"GET\";\n const init: RequestInit & { duplex?: \"half\" } = {\n method,\n headers,\n redirect: \"manual\",\n };\n if (method !== \"GET\" && method !== \"HEAD\") {\n init.body = Readable.toWeb(req) as unknown as BodyInit;\n init.duplex = \"half\";\n }\n\n const response = await fetch(url, init);\n res.statusCode = response.status;\n response.headers.forEach((value, name) => {\n if (name !== \"set-cookie\" && !HOP_BY_HOP.has(name)) {\n res.setHeader(name, value);\n }\n });\n const cookies = response.headers.getSetCookie();\n if (cookies.length > 0) {\n res.setHeader(\"set-cookie\", cookies);\n }\n\n if (response.body === null) {\n res.end();\n return;\n }\n await pipeline(Readable.fromWeb(response.body as never), res);\n } catch (error) {\n next(error);\n }\n}\n\n/**\n * Dev server: Vite (transform + HMR) sebagai front, proxy ke server SSR.\n * Modul halaman/manifest dimuat lewat `vite.ssrLoadModule` agar SSR ikut segar saat file berubah\n * (tanpa restart); perubahan di `src/routes` meregenerasi entry client + full-reload browser.\n */\nexport async function createDevServer(options: DevServerOptions): Promise<DevServer> {\n const appDir = options.appDir;\n const entryDir = join(appDir, \".ramonyx\");\n const entryFile = join(entryDir, ENTRY_FILE);\n const routesDir = join(appDir, \"src\", \"routes\");\n\n const vite = await createServer({\n configFile: false,\n root: appDir,\n appType: \"custom\",\n logLevel: \"info\",\n esbuild: { jsx: \"automatic\" },\n server: { port: options.port ?? 5173 },\n plugins: [\n {\n name: \"ramonyx-ssr-proxy\",\n configureServer(server) {\n server.middlewares.use((req, res, next) => {\n if (req.url === undefined || isViteRequest(req.url)) {\n next();\n return;\n }\n void proxyToSsr(options.target, req, res, next);\n });\n },\n },\n ],\n });\n\n const importer: ModuleImporter = async (absolute, label) => {\n try {\n return (await vite.ssrLoadModule(absolute)) as Record<string, unknown>;\n } catch (error) {\n throw new RouteManifestError(\n `[dev] gagal memuat ${label} (${relative(appDir, absolute) || absolute}) via Vite: ${\n error instanceof Error ? error.message : String(error)\n }`,\n { cause: error },\n );\n }\n };\n\n const regenerateEntry = async (): Promise<void> => {\n const pages = await loadPageRoutes({ dir: routesDir, importer });\n await mkdir(entryDir, { recursive: true });\n await writeFile(entryFile, generateClientEntry(pages, { from: entryDir }));\n };\n await regenerateEntry();\n\n vite.watcher.on(\"all\", (_event, file) => {\n if (!file.startsWith(routesDir)) {\n return;\n }\n void regenerateEntry()\n .catch(() => undefined)\n .finally(() => {\n vite.ws.send({ type: \"full-reload\" });\n });\n });\n\n await vite.listen();\n const address = vite.httpServer?.address();\n const port =\n typeof address === \"object\" && address !== null ? address.port : (options.port ?? 5173);\n\n return {\n url: `http://localhost:${port}`,\n importer,\n target: options.target,\n close: () => vite.close(),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../../packages/bundler/src/dev.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEpE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EACL,mBAAmB,EACnB,cAAc,EAEd,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAgB,YAAY,EAAkC,MAAM,MAAM,CAAC;AAClF,OAAO,EAAE,uBAAuB,EAA0B,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACnG,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAqBpD,MAAM,UAAU,GAAG,iBAAiB,CAAC;AAErC,MAAM,eAAe,GAA2B;IAC9C,MAAM,EAAE,eAAe;IACvB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,YAAY;IACrB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,cAAc;IACtB,OAAO,EAAE,WAAW;IACpB,QAAQ,EAAE,YAAY;IACtB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,UAAU;CACnB,CAAC;AAEF;;;GAGG;AACH,SAAS,eAAe,CAAC,MAAc;IACrC,OAAO;QACL,IAAI,EAAE,oBAAoB;QAC1B,eAAe,CAAC,MAAM;YACpB,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;gBACxC,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC;gBAC1B,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;oBAChC,IAAI,EAAE,CAAC;oBACP,OAAO;gBACT,CAAC;gBACD,IAAI,GAAW,CAAC;gBAChB,IAAI,CAAC;oBACH,GAAG,GAAG,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;gBAC7E,CAAC;gBAAC,MAAM,CAAC;oBACP,IAAI,EAAE,CAAC;oBACP,OAAO;gBACT,CAAC;gBACD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC3C,IAAI,EAAE,CAAC;oBACP,OAAO;gBACT,CAAC;gBACD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;gBAChD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBACtB,IAAI,EAAE,CAAC;oBACP,OAAO;gBACT,CAAC;gBACD,KAAK,QAAQ,CAAC,IAAI,CAAC;qBAChB,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;oBACb,GAAG,CAAC,SAAS,CACX,cAAc,EACd,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,0BAA0B,CAC3E,CAAC;oBACF,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;oBAC3C,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC,CAAC;qBACD,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;YACzB,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED,2DAA2D;AAC3D,SAAS,aAAa,CAAC,GAAW;IAChC,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACrC,OAAO,CACL,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QACrB,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC;QACjC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;QAC7B,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QACxB,IAAI,KAAK,cAAc,CACxB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,YAAY;IACZ,YAAY;IACZ,mBAAmB;IACnB,gBAAgB;IAChB,kBAAkB;CACnB,CAAC,CAAC;AAEH;;;GAGG;AACH,KAAK,UAAU,UAAU,CACvB,MAAc,EACd,GAA4B,EAC5B,GAAmB,EACnB,IAA0B;IAE1B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,MAAM,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACxD,IAAI,KAAK,KAAK,SAAS;gBAAE,SAAS;YAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACzB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBAC7B,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC;QACnC,MAAM,IAAI,GAAsC;YAC9C,MAAM;YACN,OAAO;YACP,QAAQ,EAAE,QAAQ;SACnB,CAAC;QACF,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YAC1C,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAwB,CAAC;YACvD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACvB,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACxC,GAAG,CAAC,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC;QACjC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;YACvC,IAAI,IAAI,KAAK,YAAY,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnD,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;QAChD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACvC,CAAC;QAED,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YAC3B,GAAG,CAAC,GAAG,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QACD,MAAM,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAa,CAAC,EAAE,GAAG,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,KAAK,CAAC,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAyB;IAC7D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAEhD,MAAM,QAAQ,GAAG,uBAAuB,EAAE,CAAC;IAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEjD,2EAA2E;IAC3E,MAAM,aAAa,GAAG,KAAK,IAAmB,EAAE;QAC9C,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,SAAS,CAAC,CAAC;QACpD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YACvB,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,GAAG,CAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACpE,MAAM,OAAO,GAAsC,EAAE,CAAC;QACtD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;gBAC/C,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAC3C,CAAC;YAAC,MAAM,CAAC;gBACP,4CAA4C;YAC9C,CAAC;QACH,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAClC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACnD,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;QACD,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC;QAC9B,UAAU,EAAE,KAAK;QACjB,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,QAAQ;QACjB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE;QAC7B,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE;QACtC,OAAO,EAAE;YACP,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;YAC1B,gBAAgB,EAAE;YAClB,eAAe,CAAC,MAAM,CAAC;YACvB;gBACE,IAAI,EAAE,kBAAkB;gBACxB,eAAe,CAAC,MAAM;oBACpB,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;wBACxC,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;wBACjD,IAAI,IAAI,KAAK,qBAAqB,EAAE,CAAC;4BACnC,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC;4BAC1D,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;4BAC3C,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC,CAAC;4BAC7B,OAAO;wBACT,CAAC;wBACD,IAAI,IAAI,KAAK,iBAAiB,EAAE,CAAC;4BAC/B,KAAK,aAAa,EAAE;iCACjB,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;iCACtB,IAAI,CAAC,GAAG,EAAE;gCACT,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,iCAAiC,CAAC,CAAC;gCACjE,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;gCAC3C,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;4BAC/C,CAAC,CAAC,CAAC;4BACL,OAAO;wBACT,CAAC;wBACD,IAAI,EAAE,CAAC;oBACT,CAAC,CAAC,CAAC;gBACL,CAAC;aACF;YACD;gBACE,IAAI,EAAE,mBAAmB;gBACzB,eAAe,CAAC,MAAM;oBACpB,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;wBACxC,IAAI,GAAG,CAAC,GAAG,KAAK,SAAS,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;4BACpD,IAAI,EAAE,CAAC;4BACP,OAAO;wBACT,CAAC;wBACD,KAAK,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;oBAClD,CAAC,CAAC,CAAC;gBACL,CAAC;aACF;SACF;KACF,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAmB,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;QACzD,IAAI,CAAC;YACH,OAAO,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAA4B,CAAC;QACzE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,kBAAkB,CAC1B,sBAAsB,KAAK,KAAK,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,QAAQ,eACpE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CACvD,EAAE,EACF,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,eAAe,GAAG,KAAK,IAAmB,EAAE;QAChD,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;QACjE,QAAQ,CAAC,SAAS,CAChB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACnB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,CAAC,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClF,CAAC,CAAC,CACJ,CAAC;QACF,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,MAAM,SAAS,CAAC,SAAS,EAAE,mBAAmB,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC7E,CAAC,CAAC;IACF,MAAM,eAAe,EAAE,CAAC;IAExB,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE;QACtC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAChC,OAAO;QACT,CAAC;QACD,KAAK,eAAe,EAAE;aACnB,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;aACtB,OAAO,CAAC,GAAG,EAAE;YACZ,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;IACpB,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC;IAC3C,MAAM,IAAI,GACR,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;IAE1F,OAAO;QACL,GAAG,EAAE,oBAAoB,IAAI,EAAE;QAC/B,QAAQ;QACR,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,QAAQ;QACR,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE;KAC1B,CAAC;AACJ,CAAC","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { mkdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport type { ServerResponse } from \"node:http\";\nimport { extname, join, relative } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport {\n generateClientEntry,\n loadPageRoutes,\n type ModuleImporter,\n RouteManifestError,\n} from \"@ramonyx/compiler\";\nimport { type Connect, createServer, type Plugin, type PluginOption } from \"vite\";\nimport { createDevtoolsCollector, type DevtoolsCollector, devtoolsPanelHtml } from \"./devtools.ts\";\nimport { readAssetManifest } from \"./manifest.ts\";\nimport { serverOnlyPlugin } from \"./server-only.ts\";\n\nexport interface DevServerOptions {\n readonly appDir: string;\n /** URL server SSR yang diproksikan (mis. \"http://localhost:3000\"). */\n readonly target: string;\n readonly port?: number;\n /** Plugin Vite tambahan (dari plugin Ramonyx — ADR-0067). */\n readonly plugins?: readonly PluginOption[];\n}\n\nexport interface DevServer {\n readonly url: string;\n /** Importer untuk compiler: modul mengikuti module graph Vite (invalidasi saat file berubah). */\n readonly importer: ModuleImporter;\n readonly target: string;\n /** Kolektor panel devtools (ADR-0077) — pasang `hooks`/`middleware`-nya ke handler aplikasi. */\n readonly devtools: DevtoolsCollector;\n close(): Promise<void>;\n}\n\nconst ENTRY_FILE = \"client-entry.ts\";\n\nconst DEV_ASSET_TYPES: Record<string, string> = {\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".avif\": \"image/avif\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".ttf\": \"font/ttf\",\n \".otf\": \"font/otf\",\n};\n\n/**\n * Dev: sajikan `src/assets/**` apa adanya di `/assets/*` (tanpa hash) — paritas dengan\n * resolver aset dev (`createAssetResolver(..., { fallbackToPath: true })`, ADR-0069).\n */\nfunction assetsDevPlugin(appDir: string): Plugin {\n return {\n name: \"ramonyx-assets-dev\",\n configureServer(server) {\n server.middlewares.use((req, res, next) => {\n const url = req.url ?? \"\";\n if (!url.startsWith(\"/assets/\")) {\n next();\n return;\n }\n let rel: string;\n try {\n rel = decodeURIComponent(url.slice(\"/assets/\".length).split(\"?\")[0] ?? \"\");\n } catch {\n next();\n return;\n }\n if (rel.length === 0 || rel.includes(\"..\")) {\n next();\n return;\n }\n const file = join(appDir, \"src\", \"assets\", rel);\n if (!existsSync(file)) {\n next();\n return;\n }\n void readFile(file)\n .then((data) => {\n res.setHeader(\n \"content-type\",\n DEV_ASSET_TYPES[extname(file).toLowerCase()] ?? \"application/octet-stream\",\n );\n res.setHeader(\"cache-control\", \"no-cache\");\n res.end(data);\n })\n .catch(() => next());\n });\n },\n };\n}\n\n/** Path yang dimiliki Vite (jangan diproksikan ke SSR). */\nfunction isViteRequest(url: string): boolean {\n const path = url.split(\"?\")[0] ?? \"\";\n return (\n path.startsWith(\"/@\") ||\n path.startsWith(\"/node_modules/\") ||\n path.startsWith(\"/.ramonyx/\") ||\n path.startsWith(\"/src/\") ||\n path === \"/favicon.ico\"\n );\n}\n\nconst HOP_BY_HOP = new Set([\n \"connection\",\n \"keep-alive\",\n \"transfer-encoding\",\n \"content-length\",\n \"content-encoding\",\n]);\n\n/**\n * Proxy manual ke SSR (bukan `server.proxy` Vite): request non-Vite diteruskan dengan `fetch`,\n * respons dipipe kembali apa adanya. Body request non-GET/HEAD dialirkan via `Readable.toWeb`.\n */\nasync function proxyToSsr(\n target: string,\n req: Connect.IncomingMessage,\n res: ServerResponse,\n next: Connect.NextFunction,\n): Promise<void> {\n try {\n const url = new URL(req.url ?? \"/\", target);\n const headers = new Headers();\n for (const [name, value] of Object.entries(req.headers)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(name, item);\n }\n } else {\n headers.set(name, value);\n }\n }\n\n const method = req.method ?? \"GET\";\n const init: RequestInit & { duplex?: \"half\" } = {\n method,\n headers,\n redirect: \"manual\",\n };\n if (method !== \"GET\" && method !== \"HEAD\") {\n init.body = Readable.toWeb(req) as unknown as BodyInit;\n init.duplex = \"half\";\n }\n\n const response = await fetch(url, init);\n res.statusCode = response.status;\n response.headers.forEach((value, name) => {\n if (name !== \"set-cookie\" && !HOP_BY_HOP.has(name)) {\n res.setHeader(name, value);\n }\n });\n const cookies = response.headers.getSetCookie();\n if (cookies.length > 0) {\n res.setHeader(\"set-cookie\", cookies);\n }\n\n if (response.body === null) {\n res.end();\n return;\n }\n await pipeline(Readable.fromWeb(response.body as never), res);\n } catch (error) {\n next(error);\n }\n}\n\n/**\n * Dev server: Vite (transform + HMR) sebagai front, proxy ke server SSR.\n * Modul halaman/manifest dimuat lewat `vite.ssrLoadModule` agar SSR ikut segar saat file berubah\n * (tanpa restart); perubahan di `src/routes` meregenerasi entry client + full-reload browser.\n */\nexport async function createDevServer(options: DevServerOptions): Promise<DevServer> {\n const appDir = options.appDir;\n const entryDir = join(appDir, \".ramonyx\");\n const entryFile = join(entryDir, ENTRY_FILE);\n const routesDir = join(appDir, \"src\", \"routes\");\n\n const devtools = createDevtoolsCollector();\n const clientDir = join(appDir, \"dist\", \"client\");\n\n /** Ukuran bundel/aset dari manifest build (bila sudah pernah dibangun). */\n const refreshAssets = async (): Promise<void> => {\n const manifest = await readAssetManifest(clientDir);\n if (manifest === null) {\n devtools.setAssets([]);\n return;\n }\n const names = new Set<string>([manifest.client, ...manifest.files]);\n const entries: { name: string; bytes: number }[] = [];\n for (const name of names) {\n try {\n const info = await stat(join(clientDir, name));\n entries.push({ name, bytes: info.size });\n } catch {\n // berkas hilang (mis. dibersihkan) — lewati\n }\n }\n if (manifest.assets !== undefined) {\n for (const entry of Object.values(manifest.assets)) {\n entries.push({ name: entry.file, bytes: entry.bytes });\n }\n }\n devtools.setAssets(entries);\n };\n\n const vite = await createServer({\n configFile: false,\n root: appDir,\n appType: \"custom\",\n logLevel: \"info\",\n esbuild: { jsx: \"automatic\" },\n server: { port: options.port ?? 5173 },\n plugins: [\n ...(options.plugins ?? []),\n serverOnlyPlugin(),\n assetsDevPlugin(appDir),\n {\n name: \"ramonyx-devtools\",\n configureServer(server) {\n server.middlewares.use((req, res, next) => {\n const path = (req.url ?? \"\").split(\"?\")[0] ?? \"\";\n if (path === \"/__ramonyx/devtools\") {\n res.setHeader(\"content-type\", \"text/html; charset=utf-8\");\n res.setHeader(\"cache-control\", \"no-store\");\n res.end(devtoolsPanelHtml());\n return;\n }\n if (path === \"/__ramonyx/data\") {\n void refreshAssets()\n .catch(() => undefined)\n .then(() => {\n res.setHeader(\"content-type\", \"application/json; charset=utf-8\");\n res.setHeader(\"cache-control\", \"no-store\");\n res.end(JSON.stringify(devtools.snapshot()));\n });\n return;\n }\n next();\n });\n },\n },\n {\n name: \"ramonyx-ssr-proxy\",\n configureServer(server) {\n server.middlewares.use((req, res, next) => {\n if (req.url === undefined || isViteRequest(req.url)) {\n next();\n return;\n }\n void proxyToSsr(options.target, req, res, next);\n });\n },\n },\n ],\n });\n\n const importer: ModuleImporter = async (absolute, label) => {\n try {\n return (await vite.ssrLoadModule(absolute)) as Record<string, unknown>;\n } catch (error) {\n throw new RouteManifestError(\n `[dev] gagal memuat ${label} (${relative(appDir, absolute) || absolute}) via Vite: ${\n error instanceof Error ? error.message : String(error)\n }`,\n { cause: error },\n );\n }\n };\n\n const regenerateEntry = async (): Promise<void> => {\n const pages = await loadPageRoutes({ dir: routesDir, importer });\n devtools.setRoutes(\n pages.map((page) => ({\n path: page.path,\n ...(page.pageFile !== undefined ? { file: relative(appDir, page.pageFile) } : {}),\n })),\n );\n await mkdir(entryDir, { recursive: true });\n await writeFile(entryFile, generateClientEntry(pages, { from: entryDir }));\n };\n await regenerateEntry();\n\n vite.watcher.on(\"all\", (_event, file) => {\n if (!file.startsWith(routesDir)) {\n return;\n }\n void regenerateEntry()\n .catch(() => undefined)\n .finally(() => {\n vite.ws.send({ type: \"full-reload\" });\n });\n });\n\n await vite.listen();\n const address = vite.httpServer?.address();\n const port =\n typeof address === \"object\" && address !== null ? address.port : (options.port ?? 5173);\n\n return {\n url: `http://localhost:${port}`,\n importer,\n target: options.target,\n devtools,\n close: () => vite.close(),\n };\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AppHooks, Middleware } from "@ramonyx/core";
|
|
2
|
+
export interface DevtoolsRoute {
|
|
3
|
+
readonly path: string;
|
|
4
|
+
readonly file?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface DevtoolsAsset {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly bytes: number;
|
|
9
|
+
}
|
|
10
|
+
export interface DevtoolsRequest {
|
|
11
|
+
readonly method: string;
|
|
12
|
+
readonly path: string;
|
|
13
|
+
status?: number;
|
|
14
|
+
ms?: number;
|
|
15
|
+
loaderMs?: number;
|
|
16
|
+
renderMs?: number;
|
|
17
|
+
cached?: boolean;
|
|
18
|
+
/** `payload` = HTML memuat payload hydration; `none` = tidak; undefined = bukan HTML. */
|
|
19
|
+
hydration?: "payload" | "none";
|
|
20
|
+
}
|
|
21
|
+
export interface DevtoolsSnapshot {
|
|
22
|
+
readonly generatedAt: string;
|
|
23
|
+
readonly routes: readonly DevtoolsRoute[];
|
|
24
|
+
readonly assets: readonly DevtoolsAsset[];
|
|
25
|
+
readonly requests: readonly DevtoolsRequest[];
|
|
26
|
+
}
|
|
27
|
+
export interface DevtoolsCollector {
|
|
28
|
+
/** Pasang ke `createHandler`/`createPageRoutes` (gabung `mergeHooks`). */
|
|
29
|
+
readonly hooks: AppHooks;
|
|
30
|
+
/** Bungkus handler (paling luar) agar semua request tercatat. */
|
|
31
|
+
readonly middleware: Middleware;
|
|
32
|
+
setRoutes(routes: readonly DevtoolsRoute[]): void;
|
|
33
|
+
setAssets(assets: readonly DevtoolsAsset[]): void;
|
|
34
|
+
snapshot(): DevtoolsSnapshot;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Kolektor devtools. Catatan kejujuran: korelasi loader/render → request memakai
|
|
38
|
+
* "request terbuka terakhir dengan path sama" (event hook tidak membawa identitas request);
|
|
39
|
+
* cukup untuk dev berkonkurensi rendah, didokumentasikan sebagai aproksimasi.
|
|
40
|
+
*/
|
|
41
|
+
export declare function createDevtoolsCollector(): DevtoolsCollector;
|
|
42
|
+
/** Halaman panel (HTML mandiri, tanpa build) — polling `/__ramonyx/data`. */
|
|
43
|
+
export declare function devtoolsPanelHtml(): string;
|
package/dist/devtools.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// Panel devtools (ADR-0077): kolektor data dev-only — rute, timing request/loader/render,
|
|
2
|
+
// status payload hydration, ukuran bundel/aset. Ditenagai primitif yang sudah ada
|
|
3
|
+
// (`AppHooks` ADR-0065 + middleware ADR-0067). **Tidak pernah aktif di produksi** — hanya
|
|
4
|
+
// `createDevServer` yang membuatnya.
|
|
5
|
+
const MAX_REQUESTS = 50;
|
|
6
|
+
function round2(value) {
|
|
7
|
+
return Math.round(value * 100) / 100;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Kolektor devtools. Catatan kejujuran: korelasi loader/render → request memakai
|
|
11
|
+
* "request terbuka terakhir dengan path sama" (event hook tidak membawa identitas request);
|
|
12
|
+
* cukup untuk dev berkonkurensi rendah, didokumentasikan sebagai aproksimasi.
|
|
13
|
+
*/
|
|
14
|
+
export function createDevtoolsCollector() {
|
|
15
|
+
const requests = [];
|
|
16
|
+
let routes = [];
|
|
17
|
+
let assets = [];
|
|
18
|
+
const catat = (request) => {
|
|
19
|
+
requests.push(request);
|
|
20
|
+
if (requests.length > MAX_REQUESTS) {
|
|
21
|
+
requests.shift();
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const terakhirDenganPath = (path) => {
|
|
25
|
+
for (let index = requests.length - 1; index >= 0; index -= 1) {
|
|
26
|
+
const record = requests[index];
|
|
27
|
+
if (record !== undefined && record.path === path && record.status === undefined) {
|
|
28
|
+
return record;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
};
|
|
33
|
+
const middleware = async (context, next) => {
|
|
34
|
+
const mulai = performance.now();
|
|
35
|
+
const record = {
|
|
36
|
+
method: context.request.method,
|
|
37
|
+
path: new URL(context.request.url).pathname,
|
|
38
|
+
};
|
|
39
|
+
catat(record);
|
|
40
|
+
const response = await next();
|
|
41
|
+
record.status = response.status;
|
|
42
|
+
record.ms = round2(performance.now() - mulai);
|
|
43
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
44
|
+
if (contentType.includes("text/html")) {
|
|
45
|
+
try {
|
|
46
|
+
const html = await response.clone().text();
|
|
47
|
+
record.hydration = html.includes('id="ramonyx-payload"') ? "payload" : "none";
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// body tidak terbaca (streaming diputus) — biarkan undefined
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return response;
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
middleware,
|
|
57
|
+
hooks: {
|
|
58
|
+
onLoaderEnd: (event) => {
|
|
59
|
+
const record = terakhirDenganPath(event.path);
|
|
60
|
+
if (record !== undefined) {
|
|
61
|
+
record.loaderMs = round2(event.durationMs);
|
|
62
|
+
record.cached = event.cached;
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
onRenderEnd: (event) => {
|
|
66
|
+
const record = terakhirDenganPath(event.path);
|
|
67
|
+
if (record !== undefined) {
|
|
68
|
+
record.renderMs = round2(event.durationMs);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
setRoutes(next) {
|
|
73
|
+
routes = [...next];
|
|
74
|
+
},
|
|
75
|
+
setAssets(next) {
|
|
76
|
+
assets = [...next];
|
|
77
|
+
},
|
|
78
|
+
snapshot() {
|
|
79
|
+
return {
|
|
80
|
+
generatedAt: new Date().toISOString(),
|
|
81
|
+
routes,
|
|
82
|
+
assets,
|
|
83
|
+
requests: [...requests].reverse(),
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** Halaman panel (HTML mandiri, tanpa build) — polling `/__ramonyx/data`. */
|
|
89
|
+
export function devtoolsPanelHtml() {
|
|
90
|
+
return `<!doctype html>
|
|
91
|
+
<html lang="id">
|
|
92
|
+
<head>
|
|
93
|
+
<meta charset="utf-8" />
|
|
94
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
95
|
+
<title>Ramonyx Devtools</title>
|
|
96
|
+
<style>
|
|
97
|
+
:root { --bg:#0f1115; --fg:#e6e8ee; --redup:#9aa2b1; --aksen:#7cc4ff; --garis:#262b36; }
|
|
98
|
+
* { box-sizing: border-box; }
|
|
99
|
+
body { margin:0; background:var(--bg); color:var(--fg); font:14px/1.5 ui-sans-serif, system-ui, sans-serif; }
|
|
100
|
+
.shell { max-width: 1100px; margin: 0 auto; padding: 20px; }
|
|
101
|
+
h1 { font-size: 20px; margin: 0 0 4px; } h2 { font-size: 14px; color: var(--redup); text-transform: uppercase; letter-spacing: .08em; margin: 24px 0 8px; }
|
|
102
|
+
table { border-collapse: collapse; width: 100%; font-size: 13px; }
|
|
103
|
+
th, td { border: 1px solid var(--garis); padding: 5px 8px; text-align: left; }
|
|
104
|
+
th { color: var(--redup); font-weight: 500; }
|
|
105
|
+
code { font-family: ui-monospace, monospace; color: var(--aksen); }
|
|
106
|
+
.ok { color: #7ddc9a; } .bad { color: #ff8080; } .dim { color: var(--redup); }
|
|
107
|
+
</style>
|
|
108
|
+
</head>
|
|
109
|
+
<body>
|
|
110
|
+
<div class="shell">
|
|
111
|
+
<h1>Ramonyx Devtools <span class="dim">(dev-only)</span></h1>
|
|
112
|
+
<p class="dim">Data dari <code>/__ramonyx/data</code> — rute, timing request/loader/render, hydration, ukuran bundel. Diperbarui tiap 2 dtk.</p>
|
|
113
|
+
<h2>Bundel & aset</h2>
|
|
114
|
+
<table id="aset"><thead><tr><th>Nama</th><th>Ukuran</th></tr></thead><tbody></tbody></table>
|
|
115
|
+
<h2>Rute</h2>
|
|
116
|
+
<table id="rute"><thead><tr><th>Path</th><th>Berkas</th></tr></thead><tbody></tbody></table>
|
|
117
|
+
<h2>Request terakhir</h2>
|
|
118
|
+
<table id="req"><thead><tr><th>Method</th><th>Path</th><th>Status</th><th>Total</th><th>Loader</th><th>Render</th><th>Cache</th><th>Hydration</th></tr></thead><tbody></tbody></table>
|
|
119
|
+
</div>
|
|
120
|
+
<script>
|
|
121
|
+
const fmt = (n) => (typeof n === "number" ? n + " ms" : "–");
|
|
122
|
+
const kb = (n) => (n / 1024).toFixed(1) + " KB";
|
|
123
|
+
async function segarkan() {
|
|
124
|
+
try {
|
|
125
|
+
const data = await (await fetch("/__ramonyx/data", { cache: "no-store" })).json();
|
|
126
|
+
document.querySelector("#aset tbody").innerHTML = data.assets
|
|
127
|
+
.map((a) => "<tr><td><code>" + a.name + "</code></td><td>" + kb(a.bytes) + "</td></tr>")
|
|
128
|
+
.join("");
|
|
129
|
+
document.querySelector("#rute tbody").innerHTML = data.routes
|
|
130
|
+
.map((r) => "<tr><td><code>" + r.path + "</code></td><td class='dim'>" + (r.file ?? "–") + "</td></tr>")
|
|
131
|
+
.join("");
|
|
132
|
+
document.querySelector("#req tbody").innerHTML = data.requests
|
|
133
|
+
.map((r) => "<tr><td>" + r.method + "</td><td><code>" + r.path + "</code></td><td class='" + ((r.status ?? 0) >= 400 ? "bad" : "ok") + "'>" + (r.status ?? "…") + "</td><td>" + fmt(r.ms) + "</td><td>" + fmt(r.loaderMs) + "</td><td>" + fmt(r.renderMs) + "</td><td>" + (r.cached === true ? "hit" : r.cached === false ? "miss" : "–") + "</td><td>" + (r.hydration === "payload" ? "payload ✓" : r.hydration === "none" ? "tidak" : "–") + "</td></tr>")
|
|
134
|
+
.join("");
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error("devtools: gagal memuat data", error);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
segarkan();
|
|
140
|
+
setInterval(segarkan, 2000);
|
|
141
|
+
</script>
|
|
142
|
+
</body>
|
|
143
|
+
</html>
|
|
144
|
+
`;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=devtools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devtools.js","sourceRoot":"","sources":["../../../packages/bundler/src/devtools.ts"],"names":[],"mappings":"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","sourcesContent":["// Panel devtools (ADR-0077): kolektor data dev-only — rute, timing request/loader/render,\n// status payload hydration, ukuran bundel/aset. Ditenagai primitif yang sudah ada\n// (`AppHooks` ADR-0065 + middleware ADR-0067). **Tidak pernah aktif di produksi** — hanya\n// `createDevServer` yang membuatnya.\n\nimport type { AppHooks, Middleware } from \"@ramonyx/core\";\n\nexport interface DevtoolsRoute {\n readonly path: string;\n readonly file?: string;\n}\n\nexport interface DevtoolsAsset {\n readonly name: string;\n readonly bytes: number;\n}\n\nexport interface DevtoolsRequest {\n readonly method: string;\n readonly path: string;\n status?: number;\n ms?: number;\n loaderMs?: number;\n renderMs?: number;\n cached?: boolean;\n /** `payload` = HTML memuat payload hydration; `none` = tidak; undefined = bukan HTML. */\n hydration?: \"payload\" | \"none\";\n}\n\nexport interface DevtoolsSnapshot {\n readonly generatedAt: string;\n readonly routes: readonly DevtoolsRoute[];\n readonly assets: readonly DevtoolsAsset[];\n readonly requests: readonly DevtoolsRequest[];\n}\n\nexport interface DevtoolsCollector {\n /** Pasang ke `createHandler`/`createPageRoutes` (gabung `mergeHooks`). */\n readonly hooks: AppHooks;\n /** Bungkus handler (paling luar) agar semua request tercatat. */\n readonly middleware: Middleware;\n setRoutes(routes: readonly DevtoolsRoute[]): void;\n setAssets(assets: readonly DevtoolsAsset[]): void;\n snapshot(): DevtoolsSnapshot;\n}\n\nconst MAX_REQUESTS = 50;\n\nfunction round2(value: number): number {\n return Math.round(value * 100) / 100;\n}\n\n/**\n * Kolektor devtools. Catatan kejujuran: korelasi loader/render → request memakai\n * \"request terbuka terakhir dengan path sama\" (event hook tidak membawa identitas request);\n * cukup untuk dev berkonkurensi rendah, didokumentasikan sebagai aproksimasi.\n */\nexport function createDevtoolsCollector(): DevtoolsCollector {\n const requests: DevtoolsRequest[] = [];\n let routes: readonly DevtoolsRoute[] = [];\n let assets: readonly DevtoolsAsset[] = [];\n\n const catat = (request: DevtoolsRequest): void => {\n requests.push(request);\n if (requests.length > MAX_REQUESTS) {\n requests.shift();\n }\n };\n\n const terakhirDenganPath = (path: string): DevtoolsRequest | undefined => {\n for (let index = requests.length - 1; index >= 0; index -= 1) {\n const record = requests[index];\n if (record !== undefined && record.path === path && record.status === undefined) {\n return record;\n }\n }\n return undefined;\n };\n\n const middleware: Middleware = async (context, next) => {\n const mulai = performance.now();\n const record: DevtoolsRequest = {\n method: context.request.method,\n path: new URL(context.request.url).pathname,\n };\n catat(record);\n\n const response = await next();\n record.status = response.status;\n record.ms = round2(performance.now() - mulai);\n\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"text/html\")) {\n try {\n const html = await response.clone().text();\n record.hydration = html.includes('id=\"ramonyx-payload\"') ? \"payload\" : \"none\";\n } catch {\n // body tidak terbaca (streaming diputus) — biarkan undefined\n }\n }\n return response;\n };\n\n return {\n middleware,\n hooks: {\n onLoaderEnd: (event) => {\n const record = terakhirDenganPath(event.path);\n if (record !== undefined) {\n record.loaderMs = round2(event.durationMs);\n record.cached = event.cached;\n }\n },\n onRenderEnd: (event) => {\n const record = terakhirDenganPath(event.path);\n if (record !== undefined) {\n record.renderMs = round2(event.durationMs);\n }\n },\n },\n setRoutes(next) {\n routes = [...next];\n },\n setAssets(next) {\n assets = [...next];\n },\n snapshot() {\n return {\n generatedAt: new Date().toISOString(),\n routes,\n assets,\n requests: [...requests].reverse(),\n };\n },\n };\n}\n\n/** Halaman panel (HTML mandiri, tanpa build) — polling `/__ramonyx/data`. */\nexport function devtoolsPanelHtml(): string {\n return `<!doctype html>\n<html lang=\"id\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n<title>Ramonyx Devtools</title>\n<style>\n :root { --bg:#0f1115; --fg:#e6e8ee; --redup:#9aa2b1; --aksen:#7cc4ff; --garis:#262b36; }\n * { box-sizing: border-box; }\n body { margin:0; background:var(--bg); color:var(--fg); font:14px/1.5 ui-sans-serif, system-ui, sans-serif; }\n .shell { max-width: 1100px; margin: 0 auto; padding: 20px; }\n h1 { font-size: 20px; margin: 0 0 4px; } h2 { font-size: 14px; color: var(--redup); text-transform: uppercase; letter-spacing: .08em; margin: 24px 0 8px; }\n table { border-collapse: collapse; width: 100%; font-size: 13px; }\n th, td { border: 1px solid var(--garis); padding: 5px 8px; text-align: left; }\n th { color: var(--redup); font-weight: 500; }\n code { font-family: ui-monospace, monospace; color: var(--aksen); }\n .ok { color: #7ddc9a; } .bad { color: #ff8080; } .dim { color: var(--redup); }\n</style>\n</head>\n<body>\n<div class=\"shell\">\n <h1>Ramonyx Devtools <span class=\"dim\">(dev-only)</span></h1>\n <p class=\"dim\">Data dari <code>/__ramonyx/data</code> — rute, timing request/loader/render, hydration, ukuran bundel. Diperbarui tiap 2 dtk.</p>\n <h2>Bundel & aset</h2>\n <table id=\"aset\"><thead><tr><th>Nama</th><th>Ukuran</th></tr></thead><tbody></tbody></table>\n <h2>Rute</h2>\n <table id=\"rute\"><thead><tr><th>Path</th><th>Berkas</th></tr></thead><tbody></tbody></table>\n <h2>Request terakhir</h2>\n <table id=\"req\"><thead><tr><th>Method</th><th>Path</th><th>Status</th><th>Total</th><th>Loader</th><th>Render</th><th>Cache</th><th>Hydration</th></tr></thead><tbody></tbody></table>\n</div>\n<script>\nconst fmt = (n) => (typeof n === \"number\" ? n + \" ms\" : \"–\");\nconst kb = (n) => (n / 1024).toFixed(1) + \" KB\";\nasync function segarkan() {\n try {\n const data = await (await fetch(\"/__ramonyx/data\", { cache: \"no-store\" })).json();\n document.querySelector(\"#aset tbody\").innerHTML = data.assets\n .map((a) => \"<tr><td><code>\" + a.name + \"</code></td><td>\" + kb(a.bytes) + \"</td></tr>\")\n .join(\"\");\n document.querySelector(\"#rute tbody\").innerHTML = data.routes\n .map((r) => \"<tr><td><code>\" + r.path + \"</code></td><td class='dim'>\" + (r.file ?? \"–\") + \"</td></tr>\")\n .join(\"\");\n document.querySelector(\"#req tbody\").innerHTML = data.requests\n .map((r) => \"<tr><td>\" + r.method + \"</td><td><code>\" + r.path + \"</code></td><td class='\" + ((r.status ?? 0) >= 400 ? \"bad\" : \"ok\") + \"'>\" + (r.status ?? \"…\") + \"</td><td>\" + fmt(r.ms) + \"</td><td>\" + fmt(r.loaderMs) + \"</td><td>\" + fmt(r.renderMs) + \"</td><td>\" + (r.cached === true ? \"hit\" : r.cached === false ? \"miss\" : \"–\") + \"</td><td>\" + (r.hydration === \"payload\" ? \"payload ✓\" : r.hydration === \"none\" ? \"tidak\" : \"–\") + \"</td></tr>\")\n .join(\"\");\n } catch (error) {\n console.error(\"devtools: gagal memuat data\", error);\n }\n}\nsegarkan();\nsetInterval(segarkan, 2000);\n</script>\n</body>\n</html>\n`;\n}\n"]}
|
package/dist/edge.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type PluginOption } from "vite";
|
|
2
|
+
export interface BuildEdgeOptions {
|
|
3
|
+
readonly appDir: string;
|
|
4
|
+
/** Relatif terhadap appDir; default "dist/edge". */
|
|
5
|
+
readonly outDir?: string;
|
|
6
|
+
/** URL bundle klien (default: manifest `dist/client`, fallback `/assets/client.js`). */
|
|
7
|
+
readonly clientScript?: string;
|
|
8
|
+
/** Aktifkan SSR streaming di artefak; default true (renderer React mendukung). */
|
|
9
|
+
readonly streaming?: boolean;
|
|
10
|
+
readonly minify?: boolean;
|
|
11
|
+
/** Plugin Vite tambahan (harus edge-safe — ADR-0067). */
|
|
12
|
+
readonly plugins?: readonly PluginOption[];
|
|
13
|
+
}
|
|
14
|
+
export interface BuildEdgeResult {
|
|
15
|
+
readonly outDir: string;
|
|
16
|
+
readonly entryFile: string;
|
|
17
|
+
readonly outputFile: string;
|
|
18
|
+
readonly bytes: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Membangun artefak edge dari aplikasi Ramonyx.
|
|
22
|
+
*
|
|
23
|
+
* Manifest rute dibangun saat build (impor statis hasil codegen) sehingga artefak tidak
|
|
24
|
+
* menyentuh filesystem saat runtime; seluruh dependency dibundel (`ssr.noExternal`),
|
|
25
|
+
* `process.env.NODE_ENV` di-define sebagai `"production"`, dan hasilnya diperiksa:
|
|
26
|
+
* impor `node:*` yang lolos = error (runtime edge tidak menyediakannya).
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildEdge(options: BuildEdgeOptions): Promise<BuildEdgeResult>;
|
package/dist/edge.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Build target edge/serverless (ADR-0058): bundel SSR satu berkas dari entry yang digenerate
|
|
2
|
+
// (impor statis, tanpa `node:fs`) + guard agar tidak ada API Node yang lolos.
|
|
3
|
+
//
|
|
4
|
+
// Hasil: `<outDir>/index.js` — ESM dengan `export default { fetch(request) }`,
|
|
5
|
+
// kontrak Cloudflare Workers / Vercel Edge / Deno Deploy / runtime fetch standar.
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
8
|
+
import { join, resolve } from "node:path";
|
|
9
|
+
import { generateServerEntry, listApiRoutes, loadPageRoutes } from "@ramonyx/compiler";
|
|
10
|
+
import { errorHint } from "@ramonyx/core";
|
|
11
|
+
import { build } from "vite";
|
|
12
|
+
import { assetInfoMap } from "./assets.js";
|
|
13
|
+
import { readClientScript } from "./build.js";
|
|
14
|
+
import { readAssetManifest } from "./manifest.js";
|
|
15
|
+
const NODE_IMPORT = /["'](node:[a-z0-9_/-]+)["']/g;
|
|
16
|
+
/**
|
|
17
|
+
* Membangun artefak edge dari aplikasi Ramonyx.
|
|
18
|
+
*
|
|
19
|
+
* Manifest rute dibangun saat build (impor statis hasil codegen) sehingga artefak tidak
|
|
20
|
+
* menyentuh filesystem saat runtime; seluruh dependency dibundel (`ssr.noExternal`),
|
|
21
|
+
* `process.env.NODE_ENV` di-define sebagai `"production"`, dan hasilnya diperiksa:
|
|
22
|
+
* impor `node:*` yang lolos = error (runtime edge tidak menyediakannya).
|
|
23
|
+
*/
|
|
24
|
+
export async function buildEdge(options) {
|
|
25
|
+
// Path absolut: entry Vite diresolusi relatif terhadap `root`, path relatif akan tergandakan.
|
|
26
|
+
const appDir = resolve(options.appDir);
|
|
27
|
+
const routesDir = join(appDir, "src/routes");
|
|
28
|
+
const outDir = resolve(appDir, options.outDir ?? "dist/edge");
|
|
29
|
+
const entryDir = join(appDir, ".ramonyx");
|
|
30
|
+
const entryFile = join(entryDir, "edge-entry.ts");
|
|
31
|
+
const [pages, apiRoutes] = await Promise.all([
|
|
32
|
+
loadPageRoutes({ dir: routesDir }),
|
|
33
|
+
listApiRoutes({ dir: routesDir }),
|
|
34
|
+
]);
|
|
35
|
+
const middlewareCandidate = join(routesDir, "middleware.ts");
|
|
36
|
+
const middlewareFile = existsSync(middlewareCandidate) ? middlewareCandidate : undefined;
|
|
37
|
+
const clientDir = join(appDir, "dist/client");
|
|
38
|
+
const clientScript = options.clientScript ?? (await readClientScript(clientDir));
|
|
39
|
+
// Peta aset di-inline (artefak edge tanpa fs — ADR-0069): URL + dimensi + varian (ADR-0079).
|
|
40
|
+
const manifest = await readAssetManifest(clientDir);
|
|
41
|
+
const assets = assetInfoMap(manifest);
|
|
42
|
+
await mkdir(entryDir, { recursive: true });
|
|
43
|
+
await writeFile(entryFile, generateServerEntry({
|
|
44
|
+
from: entryDir,
|
|
45
|
+
pages,
|
|
46
|
+
apiRoutes,
|
|
47
|
+
middlewareFile,
|
|
48
|
+
clientScript,
|
|
49
|
+
streaming: options.streaming ?? true,
|
|
50
|
+
assets,
|
|
51
|
+
}));
|
|
52
|
+
await build({
|
|
53
|
+
configFile: false,
|
|
54
|
+
root: appDir,
|
|
55
|
+
logLevel: "warn",
|
|
56
|
+
esbuild: { jsx: "automatic" },
|
|
57
|
+
plugins: [...(options.plugins ?? [])],
|
|
58
|
+
// `*.server.ts` tetap nyata di edge (server) — plugin stub hanya untuk bundle klien.
|
|
59
|
+
// Runtime edge tidak punya `process` — define agar React memakai build produksi.
|
|
60
|
+
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
61
|
+
ssr: { noExternal: true },
|
|
62
|
+
build: {
|
|
63
|
+
outDir,
|
|
64
|
+
emptyOutDir: true,
|
|
65
|
+
target: "es2022",
|
|
66
|
+
minify: options.minify ?? true,
|
|
67
|
+
ssr: entryFile,
|
|
68
|
+
rollupOptions: {
|
|
69
|
+
output: { entryFileNames: "index.js", format: "esm", codeSplitting: false },
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
const outputFile = join(outDir, "index.js");
|
|
74
|
+
const bundle = await readFile(outputFile, "utf8");
|
|
75
|
+
const nodeImports = [
|
|
76
|
+
...new Set([...bundle.matchAll(NODE_IMPORT)].map((match) => match[1] ?? "")),
|
|
77
|
+
];
|
|
78
|
+
if (nodeImports.length > 0) {
|
|
79
|
+
throw new Error(`bundel edge memuat API Node (${nodeImports.join(", ")}) — periksa middleware/kode aplikasi ` +
|
|
80
|
+
`yang memakai \`node:*\` (mis. \`guardedFetch\` dari @ramonyx/security/ssrf). ${errorHint("RMX-EDGE-1")}`);
|
|
81
|
+
}
|
|
82
|
+
const info = await stat(outputFile);
|
|
83
|
+
return { outDir, entryFile, outputFile, bytes: info.size };
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=edge.js.map
|
package/dist/edge.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge.js","sourceRoot":"","sources":["../../../packages/bundler/src/edge.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,8EAA8E;AAC9E,EAAE;AACF,+EAA+E;AAC/E,kFAAkF;AAElF,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACvF,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAqB,MAAM,MAAM,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAsBlD,MAAM,WAAW,GAAG,8BAA8B,CAAC;AAEnD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAyB;IACvD,8FAA8F;IAC9F,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,WAAW,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;IAElD,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC3C,cAAc,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;QAClC,aAAa,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;KAClC,CAAC,CAAC;IAEH,MAAM,mBAAmB,GAAG,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IAC7D,MAAM,cAAc,GAAG,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,SAAS,CAAC;IACzF,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC9C,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,CAAC,MAAM,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;IAEjF,6FAA6F;IAC7F,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IAEtC,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,SAAS,CACb,SAAS,EACT,mBAAmB,CAAC;QAClB,IAAI,EAAE,QAAQ;QACd,KAAK;QACL,SAAS;QACT,cAAc;QACd,YAAY;QACZ,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI;QACpC,MAAM;KACP,CAAC,CACH,CAAC;IAEF,MAAM,KAAK,CAAC;QACV,UAAU,EAAE,KAAK;QACjB,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE;QAC7B,OAAO,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QACrC,qFAAqF;QACrF,iFAAiF;QACjF,MAAM,EAAE,EAAE,sBAAsB,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE;QAChE,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;QACzB,KAAK,EAAE;YACL,MAAM;YACN,WAAW,EAAE,IAAI;YACjB,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI;YAC9B,GAAG,EAAE,SAAS;YACd,aAAa,EAAE;gBACb,MAAM,EAAE,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE;aAC5E;SACF;KACF,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG;QAClB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;KAC7E,CAAC;IACF,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,gCAAgC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,uCAAuC;YAC3F,gFAAgF,SAAS,CAAC,YAAY,CAAC,EAAE,CAC5G,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC;IACpC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;AAC7D,CAAC","sourcesContent":["// Build target edge/serverless (ADR-0058): bundel SSR satu berkas dari entry yang digenerate\n// (impor statis, tanpa `node:fs`) + guard agar tidak ada API Node yang lolos.\n//\n// Hasil: `<outDir>/index.js` — ESM dengan `export default { fetch(request) }`,\n// kontrak Cloudflare Workers / Vercel Edge / Deno Deploy / runtime fetch standar.\n\nimport { existsSync } from \"node:fs\";\nimport { mkdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { generateServerEntry, listApiRoutes, loadPageRoutes } from \"@ramonyx/compiler\";\nimport { errorHint } from \"@ramonyx/core\";\nimport { build, type PluginOption } from \"vite\";\nimport { assetInfoMap } from \"./assets.ts\";\nimport { readClientScript } from \"./build.ts\";\nimport { readAssetManifest } from \"./manifest.ts\";\n\nexport interface BuildEdgeOptions {\n readonly appDir: string;\n /** Relatif terhadap appDir; default \"dist/edge\". */\n readonly outDir?: string;\n /** URL bundle klien (default: manifest `dist/client`, fallback `/assets/client.js`). */\n readonly clientScript?: string;\n /** Aktifkan SSR streaming di artefak; default true (renderer React mendukung). */\n readonly streaming?: boolean;\n readonly minify?: boolean;\n /** Plugin Vite tambahan (harus edge-safe — ADR-0067). */\n readonly plugins?: readonly PluginOption[];\n}\n\nexport interface BuildEdgeResult {\n readonly outDir: string;\n readonly entryFile: string;\n readonly outputFile: string;\n readonly bytes: number;\n}\n\nconst NODE_IMPORT = /[\"'](node:[a-z0-9_/-]+)[\"']/g;\n\n/**\n * Membangun artefak edge dari aplikasi Ramonyx.\n *\n * Manifest rute dibangun saat build (impor statis hasil codegen) sehingga artefak tidak\n * menyentuh filesystem saat runtime; seluruh dependency dibundel (`ssr.noExternal`),\n * `process.env.NODE_ENV` di-define sebagai `\"production\"`, dan hasilnya diperiksa:\n * impor `node:*` yang lolos = error (runtime edge tidak menyediakannya).\n */\nexport async function buildEdge(options: BuildEdgeOptions): Promise<BuildEdgeResult> {\n // Path absolut: entry Vite diresolusi relatif terhadap `root`, path relatif akan tergandakan.\n const appDir = resolve(options.appDir);\n const routesDir = join(appDir, \"src/routes\");\n const outDir = resolve(appDir, options.outDir ?? \"dist/edge\");\n const entryDir = join(appDir, \".ramonyx\");\n const entryFile = join(entryDir, \"edge-entry.ts\");\n\n const [pages, apiRoutes] = await Promise.all([\n loadPageRoutes({ dir: routesDir }),\n listApiRoutes({ dir: routesDir }),\n ]);\n\n const middlewareCandidate = join(routesDir, \"middleware.ts\");\n const middlewareFile = existsSync(middlewareCandidate) ? middlewareCandidate : undefined;\n const clientDir = join(appDir, \"dist/client\");\n const clientScript = options.clientScript ?? (await readClientScript(clientDir));\n\n // Peta aset di-inline (artefak edge tanpa fs — ADR-0069): URL + dimensi + varian (ADR-0079).\n const manifest = await readAssetManifest(clientDir);\n const assets = assetInfoMap(manifest);\n\n await mkdir(entryDir, { recursive: true });\n await writeFile(\n entryFile,\n generateServerEntry({\n from: entryDir,\n pages,\n apiRoutes,\n middlewareFile,\n clientScript,\n streaming: options.streaming ?? true,\n assets,\n }),\n );\n\n await build({\n configFile: false,\n root: appDir,\n logLevel: \"warn\",\n esbuild: { jsx: \"automatic\" },\n plugins: [...(options.plugins ?? [])],\n // `*.server.ts` tetap nyata di edge (server) — plugin stub hanya untuk bundle klien.\n // Runtime edge tidak punya `process` — define agar React memakai build produksi.\n define: { \"process.env.NODE_ENV\": JSON.stringify(\"production\") },\n ssr: { noExternal: true },\n build: {\n outDir,\n emptyOutDir: true,\n target: \"es2022\",\n minify: options.minify ?? true,\n ssr: entryFile,\n rollupOptions: {\n output: { entryFileNames: \"index.js\", format: \"esm\", codeSplitting: false },\n },\n },\n });\n\n const outputFile = join(outDir, \"index.js\");\n const bundle = await readFile(outputFile, \"utf8\");\n const nodeImports = [\n ...new Set([...bundle.matchAll(NODE_IMPORT)].map((match) => match[1] ?? \"\")),\n ];\n if (nodeImports.length > 0) {\n throw new Error(\n `bundel edge memuat API Node (${nodeImports.join(\", \")}) — periksa middleware/kode aplikasi ` +\n `yang memakai \\`node:*\\` (mis. \\`guardedFetch\\` dari @ramonyx/security/ssrf). ${errorHint(\"RMX-EDGE-1\")}`,\n );\n }\n\n const info = await stat(outputFile);\n return { outDir, entryFile, outputFile, bytes: info.size };\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
export type { AssetProcessor, AssetProcessorInput, AssetProcessorOutput, AssetProcessorResult, AssetUrlOptions, CollectAssetsOptions, CollectAssetsResult, CreateAssetResolverOptions, } from "./assets.js";
|
|
2
|
+
export { assetInfoMap, assetsDirExists, assetUrl, collectAssets, createAssetResolver, DEFAULT_ASSETS_DIR, fontPreloadTag, } from "./assets.js";
|
|
1
3
|
export type { BuildClientOptions, BuildClientResult } from "./build.js";
|
|
2
|
-
export { buildClient } from "./build.js";
|
|
4
|
+
export { buildClient, readClientScript } from "./build.js";
|
|
3
5
|
export type { DevServer, DevServerOptions } from "./dev.js";
|
|
4
6
|
export { createDevServer } from "./dev.js";
|
|
7
|
+
export type { BuildEdgeOptions, BuildEdgeResult } from "./edge.js";
|
|
8
|
+
export { buildEdge } from "./edge.js";
|
|
9
|
+
export type { AssetEntry, AssetManifest, AssetMap } from "./manifest.js";
|
|
10
|
+
export { CLIENT_MANIFEST_FILE, readAssetManifest, writeAssetManifest } from "./manifest.js";
|
|
11
|
+
export type { LoadedPlugins, PluginContext, RamonyxPlugin } from "./plugin.js";
|
|
12
|
+
export { definePlugin, loadPlugins } from "./plugin.js";
|
|
13
|
+
export type { PrerenderOptions, PrerenderResult, PrerenderRouteInfo, } from "./prerender.js";
|
|
14
|
+
export { prerenderRoutes } from "./prerender.js";
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { assetInfoMap, assetsDirExists, assetUrl, collectAssets, createAssetResolver, DEFAULT_ASSETS_DIR, fontPreloadTag, } from "./assets.js";
|
|
2
|
+
export { buildClient, readClientScript } from "./build.js";
|
|
2
3
|
export { createDevServer } from "./dev.js";
|
|
4
|
+
export { buildEdge } from "./edge.js";
|
|
5
|
+
export { CLIENT_MANIFEST_FILE, readAssetManifest, writeAssetManifest } from "./manifest.js";
|
|
6
|
+
export { definePlugin, loadPlugins } from "./plugin.js";
|
|
7
|
+
export { prerenderRoutes } from "./prerender.js";
|
|
3
8
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../packages/bundler/src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../packages/bundler/src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,GACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE3D,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAE5F,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAMxD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["export type {\n AssetProcessor,\n AssetProcessorInput,\n AssetProcessorOutput,\n AssetProcessorResult,\n AssetUrlOptions,\n CollectAssetsOptions,\n CollectAssetsResult,\n CreateAssetResolverOptions,\n} from \"./assets.ts\";\nexport {\n assetInfoMap,\n assetsDirExists,\n assetUrl,\n collectAssets,\n createAssetResolver,\n DEFAULT_ASSETS_DIR,\n fontPreloadTag,\n} from \"./assets.ts\";\nexport type { BuildClientOptions, BuildClientResult } from \"./build.ts\";\nexport { buildClient, readClientScript } from \"./build.ts\";\nexport type { DevServer, DevServerOptions } from \"./dev.ts\";\nexport { createDevServer } from \"./dev.ts\";\nexport type { BuildEdgeOptions, BuildEdgeResult } from \"./edge.ts\";\nexport { buildEdge } from \"./edge.ts\";\nexport type { AssetEntry, AssetManifest, AssetMap } from \"./manifest.ts\";\nexport { CLIENT_MANIFEST_FILE, readAssetManifest, writeAssetManifest } from \"./manifest.ts\";\nexport type { LoadedPlugins, PluginContext, RamonyxPlugin } from \"./plugin.ts\";\nexport { definePlugin, loadPlugins } from \"./plugin.ts\";\nexport type {\n PrerenderOptions,\n PrerenderResult,\n PrerenderRouteInfo,\n} from \"./prerender.ts\";\nexport { prerenderRoutes } from \"./prerender.ts\";\n"]}
|