@moonsjs/vite 0.0.0-stage → 0.1.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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +9 -2
  3. package/dist/index-8xq8fcgq.js +222 -0
  4. package/dist/index-8xq8fcgq.js.map +14 -0
  5. package/dist/index-fg6nqart.js +776 -0
  6. package/dist/index-fg6nqart.js.map +10 -0
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +424 -0
  10. package/dist/index.js.map +11 -0
  11. package/dist/leak-check.d.ts +14 -0
  12. package/dist/leak-check.d.ts.map +1 -0
  13. package/dist/manifest.d.ts +21 -0
  14. package/dist/manifest.d.ts.map +1 -0
  15. package/dist/plugin.d.ts +13 -0
  16. package/dist/plugin.d.ts.map +1 -0
  17. package/dist/server/dev.d.ts +9 -0
  18. package/dist/server/dev.d.ts.map +1 -0
  19. package/dist/server/handler.d.ts +18 -0
  20. package/dist/server/handler.d.ts.map +1 -0
  21. package/dist/server/index.d.ts +13 -0
  22. package/dist/server/index.d.ts.map +1 -0
  23. package/dist/server/index.js +311 -0
  24. package/dist/server/index.js.map +13 -0
  25. package/dist/server/node-server.d.ts +17 -0
  26. package/dist/server/node-server.d.ts.map +1 -0
  27. package/dist/server/node.d.ts +6 -0
  28. package/dist/server/node.d.ts.map +1 -0
  29. package/dist/server/shell.d.ts +18 -0
  30. package/dist/server/shell.d.ts.map +1 -0
  31. package/dist/server/shutdown.d.ts +17 -0
  32. package/dist/server/shutdown.d.ts.map +1 -0
  33. package/dist/server/test-server.d.ts +21 -0
  34. package/dist/server/test-server.d.ts.map +1 -0
  35. package/dist/transform.d.ts +101 -0
  36. package/dist/transform.d.ts.map +1 -0
  37. package/dist/transform.js +34 -0
  38. package/dist/transform.js.map +9 -0
  39. package/dist/virtual-modules.d.ts +12 -0
  40. package/dist/virtual-modules.d.ts.map +1 -0
  41. package/package.json +51 -4
  42. package/src/ctx-fallback.d.ts +9 -0
  43. package/src/index.ts +2 -0
  44. package/src/leak-check.ts +36 -0
  45. package/src/manifest.ts +79 -0
  46. package/src/plugin.ts +479 -0
  47. package/src/server/dev.ts +54 -0
  48. package/src/server/handler.ts +249 -0
  49. package/src/server/index.ts +12 -0
  50. package/src/server/node-server.ts +115 -0
  51. package/src/server/node.ts +61 -0
  52. package/src/server/shell.ts +77 -0
  53. package/src/server/shutdown.ts +64 -0
  54. package/src/server/test-server.ts +44 -0
  55. package/src/transform.ts +1073 -0
  56. package/src/virtual-modules.ts +23 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 RamonymousDev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,10 @@
1
- # Temporary Holding Version
1
+ # @moonsjs/vite
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Plugin Vite + runtime server untuk framework [**Moons**](https://github.com/RamonymousDev/Moons):
4
+ transform zero-trust `*.server.ts` (§12), virtual module, dev SSR, build client+server,
5
+ graceful shutdown, dan health endpoint.
6
+
7
+ - Dokumentasi API: [docs/api/vite.md](https://github.com/RamonymousDev/Moons/blob/main/docs/api/vite.md)
8
+ - Kenapa zero-trust: [docs/manifest.md](https://github.com/RamonymousDev/Moons/blob/main/docs/manifest.md)
9
+
10
+ MIT.
@@ -0,0 +1,222 @@
1
+ // src/server/shell.ts
2
+ import { MoonsError } from "@moonsjs/core";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { join, resolve } from "node:path";
5
+ var ROOT_PLACEHOLDER2 = '<div id="root"></div>';
6
+ var DEFAULT_TEMPLATE2 = [
7
+ "<!doctype html>",
8
+ '<html lang="id">',
9
+ "<head>",
10
+ ' <meta charset="utf-8" />',
11
+ ' <meta name="viewport" content="width=device-width, initial-scale=1" />',
12
+ " <title>Moons</title>",
13
+ "</head>",
14
+ "<body>",
15
+ ` ${ROOT_PLACEHOLDER2}`,
16
+ ' <script type="module" src="/src/client/main.tsx"></script>',
17
+ "</body>",
18
+ "</html>",
19
+ ""
20
+ ].join(`
21
+ `);
22
+ function injectShell2(template, markup, payload) {
23
+ if (!template.includes(ROOT_PLACEHOLDER2)) {
24
+ throw new MoonsError({
25
+ code: "E_INTERNAL",
26
+ message: `shell HTML tidak memuat \`${ROOT_PLACEHOLDER2}\``,
27
+ details: "index.html aplikasi wajib memuat elemen root tersebut agar SSR bisa meng-inject markup.",
28
+ suggestions: [
29
+ `Tambahkan ${ROOT_PLACEHOLDER2} ke index.html`,
30
+ "Atau hapus index.html untuk memakai shell bawaan"
31
+ ]
32
+ });
33
+ }
34
+ const dataScript = `<script id="__moons_data" type="application/x-moons">${payload}</script>`;
35
+ return template.replace(ROOT_PLACEHOLDER2, `<div id="root">${markup}</div>${dataScript}`);
36
+ }
37
+ function createDevShell2(server, options) {
38
+ return async (markup, payload, url) => {
39
+ const template = existsSync(options.templatePath) ? readFileSync(options.templatePath, "utf8") : DEFAULT_TEMPLATE2;
40
+ const html = injectShell2(template, markup, payload);
41
+ return await server.transformIndexHtml(url, html);
42
+ };
43
+ }
44
+ function createFileShell2(options = {}) {
45
+ let cached = null;
46
+ return async (markup, payload) => {
47
+ if (cached === null) {
48
+ const clientDir = resolve(options.clientDir ?? process.cwd(), "dist/client");
49
+ const file = join(clientDir, "index.html");
50
+ if (!existsSync(file)) {
51
+ throw new MoonsError({
52
+ code: "E_INTERNAL",
53
+ file,
54
+ message: "`dist/client/index.html` tidak ditemukan",
55
+ details: "Build client harus dijalankan sebelum server produksi dinyalakan (PRD §18).",
56
+ suggestions: ["Jalankan `bun run build` lebih dulu"]
57
+ });
58
+ }
59
+ cached = readFileSync(file, "utf8");
60
+ }
61
+ return injectShell2(cached, markup, payload);
62
+ };
63
+ }
64
+
65
+ // src/server/node.ts
66
+ var MAX_BODY_BYTES = 32 * 1024 * 1024;
67
+ async function toWebRequest2(req) {
68
+ const host = req.headers.host ?? "localhost";
69
+ const url = `http://${host}${req.url ?? "/"}`;
70
+ const method = req.method ?? "GET";
71
+ const headers = new Headers;
72
+ for (const [key, value] of Object.entries(req.headers)) {
73
+ if (value === undefined || key === "content-length") {
74
+ continue;
75
+ }
76
+ if (Array.isArray(value)) {
77
+ for (const item of value) {
78
+ headers.append(key, item);
79
+ }
80
+ } else {
81
+ headers.set(key, value);
82
+ }
83
+ }
84
+ if (method === "GET" || method === "HEAD") {
85
+ return new Request(url, { method, headers });
86
+ }
87
+ const body = await readBody(req);
88
+ return new Request(url, { method, headers, body: new Uint8Array(body) });
89
+ }
90
+ function readBody(req) {
91
+ return new Promise((resolvePromise, reject) => {
92
+ const chunks = [];
93
+ let size = 0;
94
+ req.on("data", (chunk) => {
95
+ size += chunk.length;
96
+ if (size > MAX_BODY_BYTES) {
97
+ req.destroy();
98
+ reject(new Error("request body melebihi batas transport"));
99
+ return;
100
+ }
101
+ chunks.push(chunk);
102
+ });
103
+ req.on("end", () => resolvePromise(Buffer.concat(chunks)));
104
+ req.on("error", (error) => reject(error));
105
+ });
106
+ }
107
+ async function sendNodeResponse2(res, response) {
108
+ res.statusCode = response.status;
109
+ response.headers.forEach((value, key) => {
110
+ res.setHeader(key, value);
111
+ });
112
+ const body = Buffer.from(await response.arrayBuffer());
113
+ res.end(body);
114
+ }
115
+
116
+ // src/virtual-modules.ts
117
+ var VIRTUAL_MODULES = {
118
+ routes: "virtual:moons/routes",
119
+ config: "virtual:moons/config",
120
+ clientEntry: "virtual:moons/client-entry",
121
+ serverEntry: "virtual:moons/server-entry"
122
+ };
123
+ function isVirtualModuleId(id) {
124
+ const clean = id.startsWith("\x00") ? id.slice(1) : id;
125
+ return Object.values(VIRTUAL_MODULES).includes(clean);
126
+ }
127
+ function fromNullPrefixed(id) {
128
+ const clean = id.startsWith("\x00") ? id.slice(1) : id;
129
+ return isVirtualModuleId(clean) ? clean : null;
130
+ }
131
+
132
+ // src/server/dev.ts
133
+ function installDevMiddleware2(server, options) {
134
+ server.middlewares.use((req, res, next) => {
135
+ if (!isHtmlRequest(req)) {
136
+ next();
137
+ return;
138
+ }
139
+ handleRequest(server, options)(req, res).catch((error) => {
140
+ next(error);
141
+ });
142
+ });
143
+ }
144
+ function handleRequest(server, options) {
145
+ return async (req, res) => {
146
+ const entry = await server.ssrLoadModule(VIRTUAL_MODULES.serverEntry);
147
+ const handler = entry.createDevHandler(createDevShell2(server, options));
148
+ const response = await handler(await toWebRequest2(req));
149
+ await sendNodeResponse2(res, response);
150
+ };
151
+ }
152
+ function isHtmlRequest(req) {
153
+ if (req.method !== "GET" && req.method !== "HEAD") {
154
+ return false;
155
+ }
156
+ const path = new URL(req.url ?? "/", "http://localhost").pathname;
157
+ if (path.startsWith("/@") || path.startsWith("/node_modules/") || path.startsWith("/__")) {
158
+ return false;
159
+ }
160
+ const lastSegment = path.split("/").pop() ?? "";
161
+ if (lastSegment.includes(".")) {
162
+ return false;
163
+ }
164
+ const accept = req.headers.accept ?? "";
165
+ return accept.includes("text/html") || accept.includes("*/*");
166
+ }
167
+
168
+ // src/manifest.ts
169
+ import { findLayouts, routeKeyFromFile } from "@moonsjs/core";
170
+ import { relative, sep } from "node:path";
171
+ var NOT_FOUND_ROUTE_KEY = "__moons/404";
172
+ var ERROR_ROUTE_KEY = "__moons/error";
173
+ function buildClientManifest(routes, routesDir) {
174
+ const manifest = {};
175
+ for (const route of routes) {
176
+ if (route.kind !== "page") {
177
+ continue;
178
+ }
179
+ const key = routeKeyFromFile(routesDir, route.file);
180
+ manifest[key] = {
181
+ page: key,
182
+ layouts: findLayouts(route, routes).map((layout) => routeKeyFromFile(routesDir, layout.file))
183
+ };
184
+ }
185
+ const notFound = routes.find((route) => route.kind === "404");
186
+ if (notFound !== undefined) {
187
+ manifest[NOT_FOUND_ROUTE_KEY] = { page: routeKeyFromFile(routesDir, notFound.file), layouts: [] };
188
+ }
189
+ const errorRoute = routes.find((route) => route.kind === "error");
190
+ if (errorRoute !== undefined) {
191
+ manifest[ERROR_ROUTE_KEY] = { page: routeKeyFromFile(routesDir, errorRoute.file), layouts: [] };
192
+ }
193
+ return manifest;
194
+ }
195
+ function renderRoutesModule(routes, routesDir, projectRoot) {
196
+ const manifest = buildClientManifest(routes, routesDir);
197
+ const loadEntries = routes.map((route) => {
198
+ const key = routeKeyFromFile(routesDir, route.file);
199
+ return ` ${JSON.stringify(key)}: () => import(${JSON.stringify(rootSpecifier(projectRoot, route.file))}),`;
200
+ });
201
+ return [
202
+ "// Generated by @moons. Jangan diubah manual.",
203
+ `export const routes = ${JSON.stringify(routes, null, 2)};`,
204
+ "",
205
+ `export const clientManifest = ${JSON.stringify(manifest, null, 2)};`,
206
+ "",
207
+ "export const loadRoute = {",
208
+ ...loadEntries,
209
+ "};",
210
+ ""
211
+ ].join(`
212
+ `);
213
+ }
214
+ function rootSpecifier(projectRoot, file) {
215
+ let rel = relative(projectRoot, file);
216
+ rel = sep === "/" ? rel : rel.split(sep).join("/");
217
+ return rel.startsWith("/") ? rel : `/${rel}`;
218
+ }
219
+
220
+ export { NOT_FOUND_ROUTE_KEY, ERROR_ROUTE_KEY, renderRoutesModule, rootSpecifier, VIRTUAL_MODULES, fromNullPrefixed, ROOT_PLACEHOLDER2, DEFAULT_TEMPLATE2, injectShell2, createDevShell2, createFileShell2, toWebRequest2, sendNodeResponse2, installDevMiddleware2 };
221
+
222
+ //# debugId=47C888724E7211AE64756E2164756E21
@@ -0,0 +1,14 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/server/shell.ts", "../src/server/node.ts", "../src/virtual-modules.ts", "../src/server/dev.ts", "../src/manifest.ts"],
4
+ "sourcesContent": [
5
+ "import { MoonsError } from \"@moonsjs/core\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type { ViteDevServer } from \"vite\";\n\nexport type Shell = (markup: string, payload: string, url: string) => string | Promise<string>;\n\nexport const ROOT_PLACEHOLDER = '<div id=\"root\"></div>';\n\nexport const DEFAULT_TEMPLATE = [\n \"<!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>Moons</title>\",\n \"</head>\",\n \"<body>\",\n ` ${ROOT_PLACEHOLDER}`,\n ' <script type=\"module\" src=\"/src/client/main.tsx\"></script>',\n \"</body>\",\n \"</html>\",\n \"\",\n].join(\"\\n\");\n\n/** Inject markup SSR + payload devalue ke shell HTML (PRD §8.2, §17). */\nexport function injectShell(template: string, markup: string, payload: string): string {\n if (!template.includes(ROOT_PLACEHOLDER)) {\n throw new MoonsError({\n code: \"E_INTERNAL\",\n message: `shell HTML tidak memuat \\`${ROOT_PLACEHOLDER}\\``,\n details: \"index.html aplikasi wajib memuat elemen root tersebut agar SSR bisa meng-inject markup.\",\n suggestions: [\n `Tambahkan ${ROOT_PLACEHOLDER} ke index.html`,\n \"Atau hapus index.html untuk memakai shell bawaan\",\n ],\n });\n }\n const dataScript = `<script id=\"__moons_data\" type=\"application/x-moons\">${payload}</script>`;\n return template.replace(ROOT_PLACEHOLDER, `<div id=\"root\">${markup}</div>${dataScript}`);\n}\n\n/**\n * Shell untuk dev: baca `index.html` dari disk setiap request (selalu segar)\n * lalu lewatkan ke Vite `transformIndexHtml` (inject `/@vite/client`, dsb).\n */\nexport function createDevShell(server: ViteDevServer, options: { templatePath: string }): Shell {\n return async (markup, payload, url) => {\n const template = existsSync(options.templatePath)\n ? readFileSync(options.templatePath, \"utf8\")\n : DEFAULT_TEMPLATE;\n const html = injectShell(template, markup, payload);\n return await server.transformIndexHtml(url, html);\n };\n}\n\n/** Shell untuk produksi: baca `dist/client/index.html` sekali dari cwd. */\nexport function createFileShell(options: { clientDir?: string } = {}): Shell {\n let cached: string | null = null;\n return async (markup, payload) => {\n if (cached === null) {\n const clientDir = resolve(options.clientDir ?? process.cwd(), \"dist/client\");\n const file = join(clientDir, \"index.html\");\n if (!existsSync(file)) {\n throw new MoonsError({\n code: \"E_INTERNAL\",\n file,\n message: \"`dist/client/index.html` tidak ditemukan\",\n details: \"Build client harus dijalankan sebelum server produksi dinyalakan (PRD §18).\",\n suggestions: [\"Jalankan `bun run build` lebih dulu\"],\n });\n }\n cached = readFileSync(file, \"utf8\");\n }\n return injectShell(cached, markup, payload);\n };\n}\n",
6
+ "import type { IncomingMessage, ServerResponse } from \"node:http\";\n\nconst MAX_BODY_BYTES = 32 * 1024 * 1024; // batas defensif transport; limit RPC §7.3 tetap berlaku\n\n/** Konversi request Node → Web `Request` (body diteruskan untuk non-GET/HEAD, §7.3). */\nexport async function toWebRequest(req: IncomingMessage): Promise<Request> {\n const host = req.headers.host ?? \"localhost\";\n const url = `http://${host}${req.url ?? \"/\"}`;\n const method = req.method ?? \"GET\";\n\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (value === undefined || key === \"content-length\") {\n // content-length tidak disalin; fetch menghitung ulang dari body.\n continue;\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(key, item);\n }\n } else {\n headers.set(key, value);\n }\n }\n\n if (method === \"GET\" || method === \"HEAD\") {\n return new Request(url, { method, headers });\n }\n\n const body = await readBody(req);\n return new Request(url, { method, headers, body: new Uint8Array(body) });\n}\n\nfunction readBody(req: IncomingMessage): Promise<Buffer> {\n return new Promise((resolvePromise, reject) => {\n const chunks: Buffer[] = [];\n let size = 0;\n\n req.on(\"data\", (chunk: Buffer) => {\n size += chunk.length;\n if (size > MAX_BODY_BYTES) {\n req.destroy();\n reject(new Error(\"request body melebihi batas transport\"));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", () => resolvePromise(Buffer.concat(chunks)));\n req.on(\"error\", (error) => reject(error));\n });\n}\n\n/** Tulis Web `Response` ke `ServerResponse` Node. */\nexport async function sendNodeResponse(res: ServerResponse, response: Response): Promise<void> {\n res.statusCode = response.status;\n response.headers.forEach((value, key) => {\n res.setHeader(key, value);\n });\n const body = Buffer.from(await response.arrayBuffer());\n res.end(body);\n}\n",
7
+ "/** Nama virtual module PRD §9.4. Semua di-resolve ke id `\\0`-prefix. */\nexport const VIRTUAL_MODULES = {\n routes: \"virtual:moons/routes\",\n config: \"virtual:moons/config\",\n clientEntry: \"virtual:moons/client-entry\",\n serverEntry: \"virtual:moons/server-entry\",\n} as const;\n\nexport type VirtualModuleName = (typeof VIRTUAL_MODULES)[keyof typeof VIRTUAL_MODULES];\n\nexport function isVirtualModuleId(id: string): id is VirtualModuleName {\n const clean = id.startsWith(\"\\0\") ? id.slice(1) : id;\n return (Object.values(VIRTUAL_MODULES) as string[]).includes(clean);\n}\n\nexport function toNullPrefixed(id: VirtualModuleName): `\\0${string}` {\n return `\\0${id}`;\n}\n\nexport function fromNullPrefixed(id: string): VirtualModuleName | null {\n const clean = id.startsWith(\"\\0\") ? id.slice(1) : id;\n return isVirtualModuleId(clean) ? (clean as VirtualModuleName) : null;\n}\n",
8
+ "import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { ViteDevServer } from \"vite\";\nimport { VIRTUAL_MODULES } from \"../virtual-modules\";\nimport type { Shell } from \"./shell\";\nimport { createDevShell } from \"./shell\";\nimport { sendNodeResponse, toWebRequest } from \"./node\";\n\ntype DevServerEntry = {\n createDevHandler: (shell: Shell) => (request: Request) => Promise<Response>;\n};\n\n/**\n * Middleware SSR dev: dipasang sebelum middleware internal Vite, hanya\n * menangani request HTML (modul/aset tetap dilayani Vite).\n */\nexport function installDevMiddleware(\n server: ViteDevServer,\n options: { templatePath: string },\n): void {\n server.middlewares.use((req, res, next) => {\n if (!isHtmlRequest(req)) {\n next();\n return;\n }\n void handleRequest(server, options)(req, res).catch((error: unknown) => {\n next(error as Error);\n });\n });\n}\n\nfunction handleRequest(server: ViteDevServer, options: { templatePath: string }) {\n return async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n const entry = (await server.ssrLoadModule(VIRTUAL_MODULES.serverEntry)) as DevServerEntry;\n const handler = entry.createDevHandler(createDevShell(server, options));\n const response = await handler(await toWebRequest(req));\n await sendNodeResponse(res, response);\n };\n}\n\nfunction isHtmlRequest(req: IncomingMessage): boolean {\n if (req.method !== \"GET\" && req.method !== \"HEAD\") {\n return false;\n }\n const path = new URL(req.url ?? \"/\", \"http://localhost\").pathname;\n if (path.startsWith(\"/@\") || path.startsWith(\"/node_modules/\") || path.startsWith(\"/__\")) {\n return false;\n }\n const lastSegment = path.split(\"/\").pop() ?? \"\";\n if (lastSegment.includes(\".\")) {\n return false;\n }\n const accept = req.headers.accept ?? \"\";\n return accept.includes(\"text/html\") || accept.includes(\"*/*\");\n}\n",
9
+ "import { findLayouts, routeKeyFromFile, type RouteDefinition } from \"@moonsjs/core\";\nimport { relative, sep } from \"node:path\";\n\nexport interface ClientManifestEntry {\n page: string;\n layouts: string[];\n}\n\n/** Key manifest untuk halaman kustom `_404` dan `_error` (§11.2). */\nexport const NOT_FOUND_ROUTE_KEY = \"__moons/404\";\nexport const ERROR_ROUTE_KEY = \"__moons/error\";\n\n/**\n * Manifest route untuk client (PRD §8.2): key = key route file\n * (routeKeyFromFile), nilai = page + layout chain dari root ke leaf.\n */\nexport function buildClientManifest(\n routes: RouteDefinition[],\n routesDir: string,\n): Record<string, ClientManifestEntry> {\n const manifest: Record<string, ClientManifestEntry> = {};\n for (const route of routes) {\n if (route.kind !== \"page\") {\n continue;\n }\n const key = routeKeyFromFile(routesDir, route.file);\n manifest[key] = {\n page: key,\n layouts: findLayouts(route, routes).map((layout) => routeKeyFromFile(routesDir, layout.file)),\n };\n }\n\n const notFound = routes.find((route) => route.kind === \"404\");\n if (notFound !== undefined) {\n manifest[NOT_FOUND_ROUTE_KEY] = { page: routeKeyFromFile(routesDir, notFound.file), layouts: [] };\n }\n const errorRoute = routes.find((route) => route.kind === \"error\");\n if (errorRoute !== undefined) {\n manifest[ERROR_ROUTE_KEY] = { page: routeKeyFromFile(routesDir, errorRoute.file), layouts: [] };\n }\n\n return manifest;\n}\n\n/**\n * Isi `virtual:moons/routes`: daftar RouteDefinition, manifest client, dan\n * peta import() dinamis per file route (specifier root-relative).\n */\nexport function renderRoutesModule(\n routes: RouteDefinition[],\n routesDir: string,\n projectRoot: string,\n): string {\n const manifest = buildClientManifest(routes, routesDir);\n\n const loadEntries = routes.map((route) => {\n const key = routeKeyFromFile(routesDir, route.file);\n return ` ${JSON.stringify(key)}: () => import(${JSON.stringify(rootSpecifier(projectRoot, route.file))}),`;\n });\n\n return [\n \"// Generated by @moons. Jangan diubah manual.\",\n `export const routes = ${JSON.stringify(routes, null, 2)};`,\n \"\",\n `export const clientManifest = ${JSON.stringify(manifest, null, 2)};`,\n \"\",\n \"export const loadRoute = {\",\n ...loadEntries,\n \"};\",\n \"\",\n ].join(\"\\n\");\n}\n\n/** Path file absolut project → specifier root-relative yang bisa di-resolve Vite. */\nexport function rootSpecifier(projectRoot: string, file: string): string {\n let rel = relative(projectRoot, file);\n rel = sep === \"/\" ? rel : rel.split(sep).join(\"/\");\n return rel.startsWith(\"/\") ? rel : `/${rel}`;\n}\n"
10
+ ],
11
+ "mappings": "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",
12
+ "debugId": "47C888724E7211AE64756E2164756E21",
13
+ "names": []
14
+ }