@analogjs/vite-plugin-nitro 2.4.0-beta.9 → 3.0.0-alpha.10

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 (50) hide show
  1. package/package.json +6 -8
  2. package/src/index.d.ts +9 -9
  3. package/src/index.js +6 -2
  4. package/src/index.js.map +1 -1
  5. package/src/lib/build-server.d.ts +3 -2
  6. package/src/lib/build-server.js +107 -53
  7. package/src/lib/build-server.js.map +1 -1
  8. package/src/lib/build-sitemap.d.ts +6 -6
  9. package/src/lib/build-sitemap.js +48 -60
  10. package/src/lib/build-sitemap.js.map +1 -1
  11. package/src/lib/build-ssr.d.ts +2 -2
  12. package/src/lib/build-ssr.js +16 -18
  13. package/src/lib/build-ssr.js.map +1 -1
  14. package/src/lib/hooks/post-rendering-hook.d.ts +1 -1
  15. package/src/lib/hooks/post-rendering-hook.js +10 -6
  16. package/src/lib/hooks/post-rendering-hook.js.map +1 -1
  17. package/src/lib/options.d.ts +114 -106
  18. package/src/lib/plugins/dev-server-plugin.d.ts +3 -3
  19. package/src/lib/plugins/dev-server-plugin.js +91 -99
  20. package/src/lib/plugins/dev-server-plugin.js.map +1 -1
  21. package/src/lib/plugins/page-endpoints.d.ts +5 -5
  22. package/src/lib/plugins/page-endpoints.js +39 -51
  23. package/src/lib/plugins/page-endpoints.js.map +1 -1
  24. package/src/lib/utils/get-content-files.d.ts +54 -54
  25. package/src/lib/utils/get-content-files.js +88 -100
  26. package/src/lib/utils/get-content-files.js.map +1 -1
  27. package/src/lib/utils/get-page-handlers.d.ts +58 -58
  28. package/src/lib/utils/get-page-handlers.js +70 -82
  29. package/src/lib/utils/get-page-handlers.js.map +1 -1
  30. package/src/lib/utils/load-esm.d.ts +18 -18
  31. package/src/lib/utils/node-web-bridge.d.ts +3 -0
  32. package/src/lib/utils/node-web-bridge.js +55 -0
  33. package/src/lib/utils/node-web-bridge.js.map +1 -0
  34. package/src/lib/utils/register-dev-middleware.d.ts +12 -40
  35. package/src/lib/utils/register-dev-middleware.js +41 -65
  36. package/src/lib/utils/register-dev-middleware.js.map +1 -1
  37. package/src/lib/utils/renderers.d.ts +53 -3
  38. package/src/lib/utils/renderers.js +112 -27
  39. package/src/lib/utils/renderers.js.map +1 -1
  40. package/src/lib/utils/rolldown.d.ts +2 -0
  41. package/src/lib/utils/rolldown.js +12 -0
  42. package/src/lib/utils/rolldown.js.map +1 -0
  43. package/src/lib/vite-plugin-nitro.d.ts +3 -3
  44. package/src/lib/vite-plugin-nitro.js +505 -450
  45. package/src/lib/vite-plugin-nitro.js.map +1 -1
  46. package/README.md +0 -125
  47. package/src/lib/options.js +0 -2
  48. package/src/lib/options.js.map +0 -1
  49. package/src/lib/utils/load-esm.js +0 -23
  50. package/src/lib/utils/load-esm.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"get-page-handlers.js","sourceRoot":"","sources":["../../../../../../packages/vite-plugin-nitro/src/lib/utils/get-page-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAY,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,OAAO,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAUrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,aAAa,EACb,UAAU,EACV,OAAO,EACP,mBAAmB,EACnB,SAAS,GACO;IAChB,+DAA+D;IAC/D,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC;IAE5D,4FAA4F;IAC5F,iGAAiG;IACjG,MAAM,aAAa,GAAa,QAAQ,CACtC;QACE,GAAG,IAAI,IAAI,UAAU,2BAA2B;QAChD,GAAG,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC,GAAG,CAChC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,aAAa,GAAG,GAAG,iBAAiB,CACjD;KACF,EACD,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAC9B,CAAC;IAEF,4DAA4D;IAC5D,MAAM,QAAQ,GAAwB,aAAa,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE;QACvE,sEAAsE;QACtE,MAAM,KAAK,GAAG,YAAY;aACvB,OAAO,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC,kCAAkC;aACrE,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,8BAA8B;aAC3D,OAAO,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,oDAAoD;aACvF,OAAO,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC,gCAAgC;aACpE,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,4CAA4C;aAC7E,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,6CAA6C;aAC1E,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,kCAAkC;QAE1D,8EAA8E;QAC9E,OAAO;YACL,OAAO,EAAE,YAAY;YACrB,KAAK,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,EAAE;YACnD,IAAI,EAAE,IAAI;SACX,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"get-page-handlers.js","names":[],"sources":["../../../../../../packages/vite-plugin-nitro/src/lib/utils/get-page-handlers.ts"],"sourcesContent":["import { resolve, relative } from 'node:path';\nimport { globSync } from 'tinyglobby';\n\nimport type { NitroEventHandler } from 'nitro/types';\nimport { normalizePath } from 'vite';\n\ntype GetHandlersArgs = {\n workspaceRoot: string;\n sourceRoot: string;\n rootDir: string;\n additionalPagesDirs?: string[];\n hasAPIDir?: boolean;\n};\n\n/**\n * Discovers and generates Nitro event handlers for server-side page routes.\n *\n * This function:\n * 1. Discovers all .server.ts files in the app/pages directory and additional pages directories\n * 2. Converts file paths to route patterns using Angular-style route syntax\n * 3. Generates Nitro event handlers with proper route mapping and lazy loading\n * 4. Handles dynamic route parameters and catch-all routes\n *\n * @param workspaceRoot The workspace root directory path\n * @param sourceRoot The source directory path (e.g., 'src')\n * @param rootDir The project root directory relative to workspace\n * @param additionalPagesDirs Optional array of additional pages directories to scan\n * @param hasAPIDir Whether the project has an API directory (affects route prefixing)\n * @returns Array of NitroEventHandler objects with handler paths and route patterns\n *\n * Example usage:\n * const handlers = getPageHandlers({\n * workspaceRoot: '/workspace',\n * sourceRoot: 'src',\n * rootDir: 'apps/my-app',\n * additionalPagesDirs: ['/libs/shared/pages'],\n * hasAPIDir: true\n * });\n *\n * Sample discovered file paths:\n * - /workspace/apps/my-app/src/app/pages/index.server.ts\n * - /workspace/apps/my-app/src/app/pages/users/[id].server.ts\n * - /workspace/apps/my-app/src/app/pages/products/[...slug].server.ts\n * - /workspace/apps/my-app/src/app/pages/(auth)/login.server.ts\n *\n * Route transformation examples:\n * - index.server.ts → /_analog/pages/index\n * - users/[id].server.ts → /_analog/pages/users/:id\n * - products/[...slug].server.ts → /_analog/pages/products/**:slug\n * - (auth)/login.server.ts → /_analog/pages/-auth-/login\n *\n * tinyglobby vs fast-glob comparison:\n * - Both support the same glob patterns for file discovery\n * - Both are efficient for finding server-side page files\n * - tinyglobby is now used instead of fast-glob\n * - tinyglobby provides similar functionality with smaller bundle size\n * - tinyglobby's globSync returns absolute paths when absolute: true is set\n *\n * Route transformation rules:\n * 1. Removes .server.ts extension\n * 2. Converts [param] to :param for dynamic routes\n * 3. Converts [...param] to **:param for catch-all routes\n * 4. Converts (group) to -group- for route groups\n * 5. Converts dots to forward slashes\n * 6. Prefixes with /_analog/pages and optionally /api\n */\nexport function getPageHandlers({\n workspaceRoot,\n sourceRoot,\n rootDir,\n additionalPagesDirs,\n hasAPIDir,\n}: GetHandlersArgs): NitroEventHandler[] {\n // Normalize the project root path for consistent path handling\n const root = normalizePath(resolve(workspaceRoot, rootDir));\n\n // Discover all .server.ts files in the app/pages directory and additional pages directories\n // Pattern: looks for any .server.ts files in app/pages/**/*.server.ts and additional directories\n const endpointFiles: string[] = globSync(\n [\n `${root}/${sourceRoot}/app/pages/**/*.server.ts`,\n ...(additionalPagesDirs || []).map(\n (dir) => `${workspaceRoot}${dir}/**/*.server.ts`,\n ),\n ],\n { dot: true, absolute: true },\n );\n\n // Transform each discovered file into a Nitro event handler\n const handlers: NitroEventHandler[] = endpointFiles.map((endpointFile) => {\n // Normalize the endpoint file path for consistent path handling\n const normalized = normalizePath(endpointFile);\n // Transform the normalized path into a route pattern\n const route = normalized\n .replace(/^(.*?)\\/pages/, '/pages')\n .replace(/\\.server\\.ts$/, '') // Remove .server.ts extension\n .replace(/\\[\\.{3}(.+)\\]/g, '**:$1') // Convert [...param] to **:param (catch-all routes)\n .replace(/\\[\\.{3}(\\w+)\\]/g, '**:$1') // Alternative catch-all pattern\n .replace(/\\/\\((.*?)\\)$/, '/-$1-') // Convert (group) to -group- (route groups)\n .replace(/\\[(\\w+)\\]/g, ':$1') // Convert [param] to :param (dynamic routes)\n .replace(/\\./g, '/'); // Convert dots to forward slashes\n\n // Return Nitro event handler with absolute handler path and transformed route\n return {\n handler: endpointFile,\n route: `${hasAPIDir ? '/api' : ''}/_analog${route}`,\n lazy: true,\n };\n });\n\n return handlers;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkEA,SAAgB,gBAAgB,EAC9B,eACA,YACA,SACA,qBACA,aACuC;AAsCvC,QAhCgC,SAC9B,CACE,GANS,cAAc,QAAQ,eAAe,QAAQ,CAAC,CAM/C,GAAG,WAAW,4BACtB,IAAI,uBAAuB,EAAE,EAAE,KAC5B,QAAQ,GAAG,gBAAgB,IAAI,iBACjC,CACF,EACD;EAAE,KAAK;EAAM,UAAU;EAAM,CAC9B,CAGmD,KAAK,iBAAiB;EAIxE,MAAM,QAFa,cAAc,aAAa,CAG3C,QAAQ,iBAAiB,SAAS,CAClC,QAAQ,iBAAiB,GAAG,CAC5B,QAAQ,kBAAkB,QAAQ,CAClC,QAAQ,mBAAmB,QAAQ,CACnC,QAAQ,gBAAgB,QAAQ,CAChC,QAAQ,cAAc,MAAM,CAC5B,QAAQ,OAAO,IAAI;AAGtB,SAAO;GACL,SAAS;GACT,OAAO,GAAG,YAAY,SAAS,GAAG,UAAU;GAC5C,MAAM;GACP;GACD"}
@@ -1,21 +1,21 @@
1
1
  /**
2
- * @license
3
- * Copyright Google LLC All Rights Reserved.
4
- *
5
- * Use of this source code is governed by an MIT-style license that can be
6
- * found in the LICENSE file at https://angular.dev/license
7
- */
8
- import { URL } from 'node:url';
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.dev/license
7
+ */
8
+ import { URL } from "node:url";
9
9
  /**
10
- * This uses a dynamic import to load a module which may be ESM.
11
- * CommonJS code can load ESM code via a dynamic import. Unfortunately, TypeScript
12
- * will currently, unconditionally downlevel dynamic import into a require call.
13
- * require calls cannot load ESM code and will result in a runtime error. To workaround
14
- * this, a Function constructor is used to prevent TypeScript from changing the dynamic import.
15
- * Once TypeScript provides support for keeping the dynamic import this workaround can
16
- * be dropped.
17
- *
18
- * @param modulePath The path of the module to load.
19
- * @returns A Promise that resolves to the dynamically imported module.
20
- */
10
+ * This uses a dynamic import to load a module which may be ESM.
11
+ * CommonJS code can load ESM code via a dynamic import. Unfortunately, TypeScript
12
+ * will currently, unconditionally downlevel dynamic import into a require call.
13
+ * require calls cannot load ESM code and will result in a runtime error. To workaround
14
+ * this, a Function constructor is used to prevent TypeScript from changing the dynamic import.
15
+ * Once TypeScript provides support for keeping the dynamic import this workaround can
16
+ * be dropped.
17
+ *
18
+ * @param modulePath The path of the module to load.
19
+ * @returns A Promise that resolves to the dynamically imported module.
20
+ */
21
21
  export declare function loadEsmModule<T>(modulePath: string | URL): Promise<T>;
@@ -0,0 +1,3 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ export declare function toWebRequest(req: IncomingMessage): Request;
3
+ export declare function writeWebResponseToNode(res: ServerResponse, response: Response): Promise<void>;
@@ -0,0 +1,55 @@
1
+ import { Readable } from "node:stream";
2
+ import { pipeline } from "node:stream/promises";
3
+ //#region packages/vite-plugin-nitro/src/lib/utils/node-web-bridge.ts
4
+ function toWebHeaders(headers) {
5
+ return Object.entries(headers).reduce((acc, [key, value]) => {
6
+ if (value) acc.set(key, Array.isArray(value) ? value.join(", ") : value);
7
+ return acc;
8
+ }, new Headers());
9
+ }
10
+ function toWebRequest(req) {
11
+ const protocol = "http";
12
+ const host = req.headers.host || "localhost";
13
+ const url = new URL(req.url || "/", `${protocol}://${host}`);
14
+ const body = req.method && !["GET", "HEAD"].includes(req.method) ? Readable.toWeb(req) : void 0;
15
+ return new Request(url, {
16
+ method: req.method,
17
+ headers: toWebHeaders(req.headers),
18
+ body,
19
+ duplex: body ? "half" : void 0
20
+ });
21
+ }
22
+ function isClientDisconnectError(error, res) {
23
+ if (!(error instanceof Error)) return false;
24
+ const hasDisconnectCode = "code" in error && typeof error.code === "string" && [
25
+ "ERR_STREAM_PREMATURE_CLOSE",
26
+ "ERR_INVALID_STATE",
27
+ "ECONNRESET",
28
+ "EPIPE"
29
+ ].includes(error.code);
30
+ const hasDisconnectMessage = /closed or destroyed stream/i.test(error.message);
31
+ return (res.destroyed || res.writableEnded) && (hasDisconnectCode || hasDisconnectMessage);
32
+ }
33
+ async function writeWebResponseToNode(res, response) {
34
+ res.statusCode = response.status;
35
+ res.statusMessage = response.statusText;
36
+ const setCookies = "getSetCookie" in response.headers && typeof response.headers.getSetCookie === "function" ? response.headers.getSetCookie() : [];
37
+ if (setCookies.length > 0) res.setHeader("set-cookie", setCookies);
38
+ response.headers.forEach((value, key) => {
39
+ if (key !== "set-cookie") res.setHeader(key, value);
40
+ });
41
+ if (!response.body) {
42
+ res.end();
43
+ return;
44
+ }
45
+ try {
46
+ await pipeline(Readable.fromWeb(response.body), res);
47
+ } catch (error) {
48
+ if (isClientDisconnectError(error, res)) return;
49
+ throw error;
50
+ }
51
+ }
52
+ //#endregion
53
+ export { toWebRequest, writeWebResponseToNode };
54
+
55
+ //# sourceMappingURL=node-web-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-web-bridge.js","names":[],"sources":["../../../../../../packages/vite-plugin-nitro/src/lib/utils/node-web-bridge.ts"],"sourcesContent":["import type {\n IncomingHttpHeaders,\n IncomingMessage,\n ServerResponse,\n} from 'node:http';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\n\nfunction toWebHeaders(headers: IncomingHttpHeaders) {\n return Object.entries(headers).reduce((acc, [key, value]) => {\n if (value) {\n acc.set(key, Array.isArray(value) ? value.join(', ') : value);\n }\n\n return acc;\n }, new Headers());\n}\n\nexport function toWebRequest(req: IncomingMessage): Request {\n const protocol = 'http';\n const host = req.headers.host || 'localhost';\n const url = new URL(req.url || '/', `${protocol}://${host}`);\n const body =\n req.method && !['GET', 'HEAD'].includes(req.method)\n ? (Readable.toWeb(req) as ReadableStream<Uint8Array>)\n : undefined;\n\n return new Request(url, {\n method: req.method,\n headers: toWebHeaders(req.headers),\n body,\n // @ts-expect-error duplex is required for streaming request bodies in Node.js\n duplex: body ? 'half' : undefined,\n });\n}\n\nfunction isClientDisconnectError(error: unknown, res: ServerResponse): boolean {\n if (!(error instanceof Error)) {\n return false;\n }\n\n const hasDisconnectCode =\n 'code' in error &&\n typeof error.code === 'string' &&\n [\n 'ERR_STREAM_PREMATURE_CLOSE',\n 'ERR_INVALID_STATE',\n 'ECONNRESET',\n 'EPIPE',\n ].includes(error.code);\n\n const hasDisconnectMessage = /closed or destroyed stream/i.test(\n error.message,\n );\n\n return (\n (res.destroyed || res.writableEnded) &&\n (hasDisconnectCode || hasDisconnectMessage)\n );\n}\n\nexport async function writeWebResponseToNode(\n res: ServerResponse,\n response: Response,\n): Promise<void> {\n res.statusCode = response.status;\n res.statusMessage = response.statusText;\n\n const setCookies =\n 'getSetCookie' in response.headers &&\n typeof response.headers.getSetCookie === 'function'\n ? response.headers.getSetCookie()\n : [];\n\n if (setCookies.length > 0) {\n res.setHeader('set-cookie', setCookies);\n }\n\n response.headers.forEach((value, key) => {\n if (key !== 'set-cookie') {\n res.setHeader(key, value);\n }\n });\n\n if (!response.body) {\n res.end();\n return;\n }\n\n // The Web ReadableStream and Node.js stream/web ReadableStream types\n // are structurally identical at runtime but TypeScript treats them as\n // distinct nominal types. The double-cast bridges this gap safely.\n try {\n await pipeline(\n Readable.fromWeb(\n response.body as unknown as import('node:stream/web').ReadableStream,\n ),\n res,\n );\n } catch (error) {\n // Long-lived dev responses such as SSE can be interrupted by a browser\n // refresh or HMR-triggered reconnect. Those closed-stream cases are\n // expected and should not surface as noisy server errors.\n if (isClientDisconnectError(error, res)) {\n return;\n }\n\n throw error;\n }\n}\n"],"mappings":";;;AAQA,SAAS,aAAa,SAA8B;AAClD,QAAO,OAAO,QAAQ,QAAQ,CAAC,QAAQ,KAAK,CAAC,KAAK,WAAW;AAC3D,MAAI,MACF,KAAI,IAAI,KAAK,MAAM,QAAQ,MAAM,GAAG,MAAM,KAAK,KAAK,GAAG,MAAM;AAG/D,SAAO;IACN,IAAI,SAAS,CAAC;;AAGnB,SAAgB,aAAa,KAA+B;CAC1D,MAAM,WAAW;CACjB,MAAM,OAAO,IAAI,QAAQ,QAAQ;CACjC,MAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,GAAG,SAAS,KAAK,OAAO;CAC5D,MAAM,OACJ,IAAI,UAAU,CAAC,CAAC,OAAO,OAAO,CAAC,SAAS,IAAI,OAAO,GAC9C,SAAS,MAAM,IAAI,GACpB,KAAA;AAEN,QAAO,IAAI,QAAQ,KAAK;EACtB,QAAQ,IAAI;EACZ,SAAS,aAAa,IAAI,QAAQ;EAClC;EAEA,QAAQ,OAAO,SAAS,KAAA;EACzB,CAAC;;AAGJ,SAAS,wBAAwB,OAAgB,KAA8B;AAC7E,KAAI,EAAE,iBAAiB,OACrB,QAAO;CAGT,MAAM,oBACJ,UAAU,SACV,OAAO,MAAM,SAAS,YACtB;EACE;EACA;EACA;EACA;EACD,CAAC,SAAS,MAAM,KAAK;CAExB,MAAM,uBAAuB,8BAA8B,KACzD,MAAM,QACP;AAED,SACG,IAAI,aAAa,IAAI,mBACrB,qBAAqB;;AAI1B,eAAsB,uBACpB,KACA,UACe;AACf,KAAI,aAAa,SAAS;AAC1B,KAAI,gBAAgB,SAAS;CAE7B,MAAM,aACJ,kBAAkB,SAAS,WAC3B,OAAO,SAAS,QAAQ,iBAAiB,aACrC,SAAS,QAAQ,cAAc,GAC/B,EAAE;AAER,KAAI,WAAW,SAAS,EACtB,KAAI,UAAU,cAAc,WAAW;AAGzC,UAAS,QAAQ,SAAS,OAAO,QAAQ;AACvC,MAAI,QAAQ,aACV,KAAI,UAAU,KAAK,MAAM;GAE3B;AAEF,KAAI,CAAC,SAAS,MAAM;AAClB,MAAI,KAAK;AACT;;AAMF,KAAI;AACF,QAAM,SACJ,SAAS,QACP,SAAS,KACV,EACD,IACD;UACM,OAAO;AAId,MAAI,wBAAwB,OAAO,IAAI,CACrC;AAGF,QAAM"}
@@ -1,42 +1,14 @@
1
- import { ViteDevServer } from 'vite';
1
+ import { ViteDevServer } from "vite";
2
2
  /**
3
- * Registers development server middleware by discovering and loading middleware files.
4
- *
5
- * This function:
6
- * 1. Discovers all TypeScript middleware files in the server/middleware directory
7
- * 2. Dynamically loads each middleware module using Vite's SSR module loader
8
- * 3. Registers each middleware handler with the Vite development server
9
- * 4. Handles middleware execution flow and error handling
10
- *
11
- * @param root The project root directory path
12
- * @param sourceRoot The source directory path (e.g., 'src')
13
- * @param viteServer The Vite development server instance
14
- *
15
- * Example usage:
16
- * await registerDevServerMiddleware('/workspace/my-app', 'src', viteServer);
17
- *
18
- * Sample middleware file paths that would be discovered:
19
- * - /workspace/my-app/src/server/middleware/auth.ts
20
- * - /workspace/my-app/src/server/middleware/cors.ts
21
- * - /workspace/my-app/src/server/middleware/logging.ts
22
- * - /workspace/my-app/src/server/middleware/validation.ts
23
- *
24
- * tinyglobby vs fast-glob comparison:
25
- * - Both support the same glob patterns for file discovery
26
- * - Both are efficient for finding middleware files
27
- * - tinyglobby is now used instead of fast-glob
28
- * - tinyglobby provides similar functionality with smaller bundle size
29
- * - tinyglobby's globSync returns absolute paths when absolute: true is set
30
- *
31
- * globSync options explained:
32
- * - dot: true - Includes files/directories that start with a dot (e.g., .env.middleware)
33
- * - absolute: true - Returns absolute file paths instead of relative paths
34
- *
35
- * Middleware execution flow:
36
- * 1. Request comes to Vite dev server
37
- * 2. Each registered middleware is executed in order
38
- * 3. If middleware returns a result, request processing stops
39
- * 4. If middleware returns no result, next middleware is called
40
- * 5. If no middleware handles the request, it continues to normal Vite processing
41
- */
3
+ * Registers development server middleware by discovering and loading middleware files.
4
+ *
5
+ * Each discovered h3 middleware module is loaded through Vite's SSR loader,
6
+ * wrapped in a temporary H3 app, then bridged back into Vite's Connect stack.
7
+ * If the middleware does not write a response, control falls through to the
8
+ * next Vite middleware.
9
+ *
10
+ * @param root The project root directory path
11
+ * @param sourceRoot The source directory path (e.g., 'src')
12
+ * @param viteServer The Vite development server instance
13
+ */
42
14
  export declare function registerDevServerMiddleware(root: string, sourceRoot: string, viteServer: ViteDevServer): Promise<void>;
@@ -1,68 +1,44 @@
1
- import { createEvent } from 'h3';
2
- import { globSync } from 'tinyglobby';
1
+ import { toWebRequest, writeWebResponseToNode } from "./node-web-bridge.js";
2
+ import { globSync } from "tinyglobby";
3
+ import { H3 } from "nitro/h3";
4
+ //#region packages/vite-plugin-nitro/src/lib/utils/register-dev-middleware.ts
5
+ var PASSTHROUGH_HEADER = "x-analog-passthrough";
3
6
  /**
4
- * Registers development server middleware by discovering and loading middleware files.
5
- *
6
- * This function:
7
- * 1. Discovers all TypeScript middleware files in the server/middleware directory
8
- * 2. Dynamically loads each middleware module using Vite's SSR module loader
9
- * 3. Registers each middleware handler with the Vite development server
10
- * 4. Handles middleware execution flow and error handling
11
- *
12
- * @param root The project root directory path
13
- * @param sourceRoot The source directory path (e.g., 'src')
14
- * @param viteServer The Vite development server instance
15
- *
16
- * Example usage:
17
- * await registerDevServerMiddleware('/workspace/my-app', 'src', viteServer);
18
- *
19
- * Sample middleware file paths that would be discovered:
20
- * - /workspace/my-app/src/server/middleware/auth.ts
21
- * - /workspace/my-app/src/server/middleware/cors.ts
22
- * - /workspace/my-app/src/server/middleware/logging.ts
23
- * - /workspace/my-app/src/server/middleware/validation.ts
24
- *
25
- * tinyglobby vs fast-glob comparison:
26
- * - Both support the same glob patterns for file discovery
27
- * - Both are efficient for finding middleware files
28
- * - tinyglobby is now used instead of fast-glob
29
- * - tinyglobby provides similar functionality with smaller bundle size
30
- * - tinyglobby's globSync returns absolute paths when absolute: true is set
31
- *
32
- * globSync options explained:
33
- * - dot: true - Includes files/directories that start with a dot (e.g., .env.middleware)
34
- * - absolute: true - Returns absolute file paths instead of relative paths
35
- *
36
- * Middleware execution flow:
37
- * 1. Request comes to Vite dev server
38
- * 2. Each registered middleware is executed in order
39
- * 3. If middleware returns a result, request processing stops
40
- * 4. If middleware returns no result, next middleware is called
41
- * 5. If no middleware handles the request, it continues to normal Vite processing
42
- */
43
- export async function registerDevServerMiddleware(root, sourceRoot, viteServer) {
44
- // Discover all TypeScript middleware files in the server/middleware directory
45
- // Pattern: looks for any .ts files in server/middleware/**/*.ts
46
- const middlewareFiles = globSync([`${root}/${sourceRoot}/server/middleware/**/*.ts`], {
47
- dot: true,
48
- absolute: true,
49
- });
50
- // Register each discovered middleware file with the Vite dev server
51
- middlewareFiles.forEach((file) => {
52
- viteServer.middlewares.use(async (req, res, next) => {
53
- // Dynamically load the middleware module using Vite's SSR module loader
54
- // This allows for hot module replacement during development
55
- const middlewareHandler = await viteServer
56
- .ssrLoadModule(file)
57
- .then((m) => m.default);
58
- // Execute the middleware handler with the request/response event
59
- const result = await middlewareHandler(createEvent(req, res));
60
- // If middleware doesn't return a result, continue to next middleware
61
- // If middleware returns a result, stop processing (middleware handled the request)
62
- if (!result) {
63
- next();
64
- }
65
- });
66
- });
7
+ * Registers development server middleware by discovering and loading middleware files.
8
+ *
9
+ * Each discovered h3 middleware module is loaded through Vite's SSR loader,
10
+ * wrapped in a temporary H3 app, then bridged back into Vite's Connect stack.
11
+ * If the middleware does not write a response, control falls through to the
12
+ * next Vite middleware.
13
+ *
14
+ * @param root The project root directory path
15
+ * @param sourceRoot The source directory path (e.g., 'src')
16
+ * @param viteServer The Vite development server instance
17
+ */
18
+ async function registerDevServerMiddleware(root, sourceRoot, viteServer) {
19
+ globSync([`${root}/${sourceRoot}/server/middleware/**/*.ts`], {
20
+ dot: true,
21
+ absolute: true
22
+ }).forEach((file) => {
23
+ const app = new H3();
24
+ app.use(async (event) => {
25
+ return (await viteServer.ssrLoadModule(file).then((m) => m.default))(event);
26
+ });
27
+ app.use(() => new Response(null, {
28
+ status: 204,
29
+ headers: { [PASSTHROUGH_HEADER]: "1" }
30
+ }));
31
+ viteServer.middlewares.use(async (req, res, next) => {
32
+ const response = await app.fetch(toWebRequest(req));
33
+ if (response.headers.get(PASSTHROUGH_HEADER) === "1") {
34
+ next();
35
+ return;
36
+ }
37
+ await writeWebResponseToNode(res, response);
38
+ });
39
+ });
67
40
  }
41
+ //#endregion
42
+ export { registerDevServerMiddleware };
43
+
68
44
  //# sourceMappingURL=register-dev-middleware.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"register-dev-middleware.js","sourceRoot":"","sources":["../../../../../../packages/vite-plugin-nitro/src/lib/utils/register-dev-middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,WAAW,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,IAAY,EACZ,UAAkB,EAClB,UAAyB;IAEzB,8EAA8E;IAC9E,gEAAgE;IAChE,MAAM,eAAe,GAAG,QAAQ,CAC9B,CAAC,GAAG,IAAI,IAAI,UAAU,4BAA4B,CAAC,EACnD;QACE,GAAG,EAAE,IAAI;QACT,QAAQ,EAAE,IAAI;KACf,CACF,CAAC;IAEF,oEAAoE;IACpE,eAAe,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC/B,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YAClD,wEAAwE;YACxE,4DAA4D;YAC5D,MAAM,iBAAiB,GAAiB,MAAM,UAAU;iBACrD,aAAa,CAAC,IAAI,CAAC;iBACnB,IAAI,CAAC,CAAC,CAAU,EAAE,EAAE,CAAE,CAA+B,CAAC,OAAO,CAAC,CAAC;YAElE,iEAAiE;YACjE,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;YAE9D,qEAAqE;YACrE,mFAAmF;YACnF,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,IAAI,EAAE,CAAC;YACT,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"register-dev-middleware.js","names":[],"sources":["../../../../../../packages/vite-plugin-nitro/src/lib/utils/register-dev-middleware.ts"],"sourcesContent":["import { ViteDevServer } from 'vite';\nimport { EventHandler, H3 } from 'nitro/h3';\nimport { globSync } from 'tinyglobby';\n\nimport { toWebRequest, writeWebResponseToNode } from './node-web-bridge.js';\n\nconst PASSTHROUGH_HEADER = 'x-analog-passthrough';\n\n/**\n * Registers development server middleware by discovering and loading middleware files.\n *\n * Each discovered h3 middleware module is loaded through Vite's SSR loader,\n * wrapped in a temporary H3 app, then bridged back into Vite's Connect stack.\n * If the middleware does not write a response, control falls through to the\n * next Vite middleware.\n *\n * @param root The project root directory path\n * @param sourceRoot The source directory path (e.g., 'src')\n * @param viteServer The Vite development server instance\n */\nexport async function registerDevServerMiddleware(\n root: string,\n sourceRoot: string,\n viteServer: ViteDevServer,\n): Promise<void> {\n const middlewareFiles = globSync(\n [`${root}/${sourceRoot}/server/middleware/**/*.ts`],\n {\n dot: true,\n absolute: true,\n },\n );\n\n middlewareFiles.forEach((file) => {\n // Create the H3 app once per middleware file (not per request).\n // The dynamic handler inside still loads the module fresh each request\n // via ssrLoadModule, preserving HMR.\n const app = new H3();\n app.use(async (event) => {\n const handler: EventHandler = await viteServer\n .ssrLoadModule(file)\n .then((m: unknown) => (m as { default: EventHandler }).default);\n return handler(event);\n });\n // Sentinel catch-all: when the middleware returns undefined (does not\n // handle the request), h3 does not emit its default 404 — instead we\n // detect the passthrough header and let the Connect stack continue.\n app.use(\n () =>\n new Response(null, {\n status: 204,\n headers: { [PASSTHROUGH_HEADER]: '1' },\n }),\n );\n\n viteServer.middlewares.use(async (req, res, next) => {\n const response = await app.fetch(toWebRequest(req));\n\n if (response.headers.get(PASSTHROUGH_HEADER) === '1') {\n next();\n return;\n }\n\n await writeWebResponseToNode(res, response);\n });\n });\n}\n"],"mappings":";;;;AAMA,IAAM,qBAAqB;;;;;;;;;;;;;AAc3B,eAAsB,4BACpB,MACA,YACA,YACe;AACS,UACtB,CAAC,GAAG,KAAK,GAAG,WAAW,4BAA4B,EACnD;EACE,KAAK;EACL,UAAU;EACX,CACF,CAEe,SAAS,SAAS;EAIhC,MAAM,MAAM,IAAI,IAAI;AACpB,MAAI,IAAI,OAAO,UAAU;AAIvB,WAH8B,MAAM,WACjC,cAAc,KAAK,CACnB,MAAM,MAAgB,EAAgC,QAAQ,EAClD,MAAM;IACrB;AAIF,MAAI,UAEA,IAAI,SAAS,MAAM;GACjB,QAAQ;GACR,SAAS,GAAG,qBAAqB,KAAK;GACvC,CAAC,CACL;AAED,aAAW,YAAY,IAAI,OAAO,KAAK,KAAK,SAAS;GACnD,MAAM,WAAW,MAAM,IAAI,MAAM,aAAa,IAAI,CAAC;AAEnD,OAAI,SAAS,QAAQ,IAAI,mBAAmB,KAAK,KAAK;AACpD,UAAM;AACN;;AAGF,SAAM,uBAAuB,KAAK,SAAS;IAC3C;GACF"}
@@ -1,3 +1,53 @@
1
- export declare const ssrRenderer = "\nimport { eventHandler, getResponseHeader } from 'h3';\n// @ts-ignore\nimport renderer from '#analog/ssr';\n// @ts-ignore\nimport template from '#analog/index';\n\nexport default eventHandler(async (event) => {\n const noSSR = getResponseHeader(event, 'x-analog-no-ssr');\n\n if (noSSR === 'true') {\n return template;\n }\n\n const html = await renderer(event.node.req.url, template, {\n req: event.node.req,\n res: event.node.res,\n });\n\n return html;\n});";
2
- export declare const clientRenderer = "\nimport { eventHandler } from 'h3';\n\n// @ts-ignore\nimport template from '#analog/index';\n\nexport default eventHandler(async () => {\n return template;\n});\n";
3
- export declare const apiMiddleware = "\nimport { eventHandler, proxyRequest } from 'h3';\nimport { useRuntimeConfig } from '#imports';\n\nexport default eventHandler(async (event) => {\n const prefix = useRuntimeConfig().prefix;\n const apiPrefix = `${prefix}/${useRuntimeConfig().apiPrefix}`;\n\n if (event.node.req.url?.startsWith(apiPrefix)) {\n const reqUrl = event.node.req.url?.replace(apiPrefix, '');\n\n if (\n event.node.req.method === 'GET' &&\n // in the case of XML routes, we want to proxy the request so that nitro gets the correct headers\n // and can render the XML correctly as a static asset\n !event.node.req.url?.endsWith('.xml')\n ) {\n return $fetch(reqUrl, { headers: event.node.req.headers });\n }\n\n return proxyRequest(event, reqUrl, {\n // @ts-ignore\n fetch: $fetch.native,\n });\n }\n});";
1
+ /**
2
+ * Code snippet emitted into virtual modules to create a request-scoped
3
+ * fetch using ofetch's `createFetch` + h3's `fetchWithEvent`.
4
+ *
5
+ * Shared between the SSR renderer and page-endpoint virtual modules so
6
+ * the fetch-wiring logic stays in sync.
7
+ *
8
+ * The emitted variable is named `serverFetch` — callers should reference it
9
+ * by that name.
10
+ */
11
+ export declare const SERVER_FETCH_FACTORY_SNIPPET = "\n const serverFetch = createFetch({\n fetch: (resource, init) => {\n const url = resource instanceof Request ? resource.url : resource.toString();\n return fetchWithEvent(event, url, init);\n }\n });";
12
+ /**
13
+ * SSR renderer virtual module content.
14
+ *
15
+ * This code runs inside Nitro's server runtime (Node.js context) where
16
+ * event.node is always populated. In h3 v2, event.node is typed as optional,
17
+ * so we use h3's first-class event properties (event.path, event.method) where
18
+ * possible and apply optional chaining when accessing the Node.js context for
19
+ * the Angular renderer which requires raw req/res objects.
20
+ *
21
+ * h3 v2 idiomatic APIs used:
22
+ * - defineHandler (replaces defineEventHandler / eventHandler)
23
+ * - event.path (replaces event.node.req.url)
24
+ * - getResponseHeader compat shim (still available in h3 v2)
25
+ */
26
+ export declare function ssrRenderer(): string;
27
+ /**
28
+ * Client-only renderer virtual module content.
29
+ *
30
+ * Used when SSR is disabled — simply serves the static index.html template
31
+ * for every route, letting the client-side Angular router handle navigation.
32
+ */
33
+ export declare function clientRenderer(): string;
34
+ /**
35
+ * API middleware virtual module content.
36
+ *
37
+ * Intercepts requests matching the configured API prefix and either:
38
+ * - Uses event-bound internal forwarding for GET requests (except .xml routes)
39
+ * - Uses request proxying for all other methods to forward the full request
40
+ *
41
+ * h3 v2 idiomatic APIs used:
42
+ * - defineHandler (replaces defineEventHandler / eventHandler)
43
+ * - event.path (replaces event.node.req.url)
44
+ * - event.method (replaces event.node.req.method)
45
+ * - proxyRequest is retained internally because it preserves Nitro route
46
+ * matching for event-bound server requests during SSR/prerender
47
+ * - Object.fromEntries(event.req.headers.entries()) replaces direct event.node.req.headers access
48
+ *
49
+ * `fetchWithEvent` keeps the active event context while forwarding to a
50
+ * rewritten path, which avoids falling through to the HTML renderer when
51
+ * SSR code makes relative API requests.
52
+ */
53
+ export declare const apiMiddleware = "\nimport { defineHandler, fetchWithEvent, proxyRequest } from 'nitro/h3';\nimport { useRuntimeConfig } from 'nitro/runtime-config';\n\nexport default defineHandler(async (event) => {\n const prefix = useRuntimeConfig().prefix;\n const apiPrefix = `${prefix}/${useRuntimeConfig().apiPrefix}`;\n\n if (event.path?.startsWith(apiPrefix)) {\n const reqUrl = event.path?.replace(apiPrefix, '');\n\n if (\n event.method === 'GET' &&\n // in the case of XML routes, we want to proxy the request so that nitro gets the correct headers\n // and can render the XML correctly as a static asset\n !event.path?.endsWith('.xml')\n ) {\n return fetchWithEvent(event, reqUrl, {\n headers: Object.fromEntries(event.req.headers.entries()),\n });\n }\n\n return proxyRequest(event, reqUrl);\n }\n});";
@@ -1,58 +1,143 @@
1
- export const ssrRenderer = `
2
- import { eventHandler, getResponseHeader } from 'h3';
1
+ //#region packages/vite-plugin-nitro/src/lib/utils/renderers.ts
2
+ /**
3
+ * Code snippet emitted into virtual modules to create a request-scoped
4
+ * fetch using ofetch's `createFetch` + h3's `fetchWithEvent`.
5
+ *
6
+ * Shared between the SSR renderer and page-endpoint virtual modules so
7
+ * the fetch-wiring logic stays in sync.
8
+ *
9
+ * The emitted variable is named `serverFetch` — callers should reference it
10
+ * by that name.
11
+ */
12
+ var SERVER_FETCH_FACTORY_SNIPPET = `
13
+ const serverFetch = createFetch({
14
+ fetch: (resource, init) => {
15
+ const url = resource instanceof Request ? resource.url : resource.toString();
16
+ return fetchWithEvent(event, url, init);
17
+ }
18
+ });`;
19
+ /**
20
+ * SSR renderer virtual module content.
21
+ *
22
+ * This code runs inside Nitro's server runtime (Node.js context) where
23
+ * event.node is always populated. In h3 v2, event.node is typed as optional,
24
+ * so we use h3's first-class event properties (event.path, event.method) where
25
+ * possible and apply optional chaining when accessing the Node.js context for
26
+ * the Angular renderer which requires raw req/res objects.
27
+ *
28
+ * h3 v2 idiomatic APIs used:
29
+ * - defineHandler (replaces defineEventHandler / eventHandler)
30
+ * - event.path (replaces event.node.req.url)
31
+ * - getResponseHeader compat shim (still available in h3 v2)
32
+ */
33
+ function ssrRenderer() {
34
+ return `
35
+ import { createFetch } from 'ofetch';
36
+ import { defineHandler, fetchWithEvent } from 'nitro/h3';
3
37
  // @ts-ignore
4
38
  import renderer from '#analog/ssr';
5
- // @ts-ignore
6
39
  import template from '#analog/index';
7
40
 
8
- export default eventHandler(async (event) => {
9
- const noSSR = getResponseHeader(event, 'x-analog-no-ssr');
41
+ const normalizeHtmlRequestUrl = (url) =>
42
+ url.replace(/\\/index\\.html(?=$|[?#])/, '/');
43
+
44
+ export default defineHandler(async (event) => {
45
+ event.res.headers.set('content-type', 'text/html; charset=utf-8');
46
+ const noSSR = event.res.headers.get('x-analog-no-ssr');
47
+ const requestPath = normalizeHtmlRequestUrl(event.path);
10
48
 
11
49
  if (noSSR === 'true') {
12
50
  return template;
13
51
  }
14
52
 
15
- const html = await renderer(event.node.req.url, template, {
16
- req: event.node.req,
17
- res: event.node.res,
18
- });
53
+ // event.path is the canonical h3 v2 way to access the request URL.
54
+ // event.node?.req and event.node?.res are needed by the Angular SSR renderer
55
+ // which operates on raw Node.js request/response objects.
56
+ // During prerendering (Nitro v3 fetch-based pipeline), event.node is undefined.
57
+ // The Angular renderer requires a req object with at least { headers, url },
58
+ // so we provide a minimal stub to avoid runtime errors in prerender context.
59
+ const req = event.node?.req
60
+ ? {
61
+ ...event.node.req,
62
+ url: requestPath,
63
+ originalUrl: requestPath,
64
+ }
65
+ : {
66
+ headers: { host: 'localhost' },
67
+ url: requestPath,
68
+ originalUrl: requestPath,
69
+ connection: {},
70
+ };
71
+ const res = event.node?.res;
72
+ ${SERVER_FETCH_FACTORY_SNIPPET}
73
+
74
+ const html = await renderer(requestPath, template, { req, res, fetch: serverFetch });
19
75
 
20
76
  return html;
21
77
  });`;
22
- export const clientRenderer = `
23
- import { eventHandler } from 'h3';
24
-
25
- // @ts-ignore
78
+ }
79
+ /**
80
+ * Client-only renderer virtual module content.
81
+ *
82
+ * Used when SSR is disabled — simply serves the static index.html template
83
+ * for every route, letting the client-side Angular router handle navigation.
84
+ */
85
+ function clientRenderer() {
86
+ return `
87
+ import { defineHandler } from 'nitro/h3';
26
88
  import template from '#analog/index';
27
89
 
28
- export default eventHandler(async () => {
90
+ export default defineHandler(async (event) => {
91
+ event.res.headers.set('content-type', 'text/html; charset=utf-8');
29
92
  return template;
30
93
  });
31
94
  `;
32
- export const apiMiddleware = `
33
- import { eventHandler, proxyRequest } from 'h3';
34
- import { useRuntimeConfig } from '#imports';
95
+ }
96
+ /**
97
+ * API middleware virtual module content.
98
+ *
99
+ * Intercepts requests matching the configured API prefix and either:
100
+ * - Uses event-bound internal forwarding for GET requests (except .xml routes)
101
+ * - Uses request proxying for all other methods to forward the full request
102
+ *
103
+ * h3 v2 idiomatic APIs used:
104
+ * - defineHandler (replaces defineEventHandler / eventHandler)
105
+ * - event.path (replaces event.node.req.url)
106
+ * - event.method (replaces event.node.req.method)
107
+ * - proxyRequest is retained internally because it preserves Nitro route
108
+ * matching for event-bound server requests during SSR/prerender
109
+ * - Object.fromEntries(event.req.headers.entries()) replaces direct event.node.req.headers access
110
+ *
111
+ * `fetchWithEvent` keeps the active event context while forwarding to a
112
+ * rewritten path, which avoids falling through to the HTML renderer when
113
+ * SSR code makes relative API requests.
114
+ */
115
+ var apiMiddleware = `
116
+ import { defineHandler, fetchWithEvent, proxyRequest } from 'nitro/h3';
117
+ import { useRuntimeConfig } from 'nitro/runtime-config';
35
118
 
36
- export default eventHandler(async (event) => {
119
+ export default defineHandler(async (event) => {
37
120
  const prefix = useRuntimeConfig().prefix;
38
121
  const apiPrefix = \`\${prefix}/\${useRuntimeConfig().apiPrefix}\`;
39
122
 
40
- if (event.node.req.url?.startsWith(apiPrefix)) {
41
- const reqUrl = event.node.req.url?.replace(apiPrefix, '');
123
+ if (event.path?.startsWith(apiPrefix)) {
124
+ const reqUrl = event.path?.replace(apiPrefix, '');
42
125
 
43
126
  if (
44
- event.node.req.method === 'GET' &&
127
+ event.method === 'GET' &&
45
128
  // in the case of XML routes, we want to proxy the request so that nitro gets the correct headers
46
129
  // and can render the XML correctly as a static asset
47
- !event.node.req.url?.endsWith('.xml')
130
+ !event.path?.endsWith('.xml')
48
131
  ) {
49
- return $fetch(reqUrl, { headers: event.node.req.headers });
132
+ return fetchWithEvent(event, reqUrl, {
133
+ headers: Object.fromEntries(event.req.headers.entries()),
134
+ });
50
135
  }
51
136
 
52
- return proxyRequest(event, reqUrl, {
53
- // @ts-ignore
54
- fetch: $fetch.native,
55
- });
137
+ return proxyRequest(event, reqUrl);
56
138
  }
57
139
  });`;
140
+ //#endregion
141
+ export { SERVER_FETCH_FACTORY_SNIPPET, apiMiddleware, clientRenderer, ssrRenderer };
142
+
58
143
  //# sourceMappingURL=renderers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"renderers.js","sourceRoot":"","sources":["../../../../../../packages/vite-plugin-nitro/src/lib/utils/renderers.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;IAoBvB,CAAC;AAEL,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;CAS7B,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;IAyBzB,CAAC"}
1
+ {"version":3,"file":"renderers.js","names":[],"sources":["../../../../../../packages/vite-plugin-nitro/src/lib/utils/renderers.ts"],"sourcesContent":["/**\n * Code snippet emitted into virtual modules to create a request-scoped\n * fetch using ofetch's `createFetch` + h3's `fetchWithEvent`.\n *\n * Shared between the SSR renderer and page-endpoint virtual modules so\n * the fetch-wiring logic stays in sync.\n *\n * The emitted variable is named `serverFetch` — callers should reference it\n * by that name.\n */\nexport const SERVER_FETCH_FACTORY_SNIPPET = `\n const serverFetch = createFetch({\n fetch: (resource, init) => {\n const url = resource instanceof Request ? resource.url : resource.toString();\n return fetchWithEvent(event, url, init);\n }\n });`;\n\n/**\n * SSR renderer virtual module content.\n *\n * This code runs inside Nitro's server runtime (Node.js context) where\n * event.node is always populated. In h3 v2, event.node is typed as optional,\n * so we use h3's first-class event properties (event.path, event.method) where\n * possible and apply optional chaining when accessing the Node.js context for\n * the Angular renderer which requires raw req/res objects.\n *\n * h3 v2 idiomatic APIs used:\n * - defineHandler (replaces defineEventHandler / eventHandler)\n * - event.path (replaces event.node.req.url)\n * - getResponseHeader compat shim (still available in h3 v2)\n */\nexport function ssrRenderer() {\n return `\nimport { createFetch } from 'ofetch';\nimport { defineHandler, fetchWithEvent } from 'nitro/h3';\n// @ts-ignore\nimport renderer from '#analog/ssr';\nimport template from '#analog/index';\n\nconst normalizeHtmlRequestUrl = (url) =>\n url.replace(/\\\\/index\\\\.html(?=$|[?#])/, '/');\n\nexport default defineHandler(async (event) => {\n event.res.headers.set('content-type', 'text/html; charset=utf-8');\n const noSSR = event.res.headers.get('x-analog-no-ssr');\n const requestPath = normalizeHtmlRequestUrl(event.path);\n\n if (noSSR === 'true') {\n return template;\n }\n\n // event.path is the canonical h3 v2 way to access the request URL.\n // event.node?.req and event.node?.res are needed by the Angular SSR renderer\n // which operates on raw Node.js request/response objects.\n // During prerendering (Nitro v3 fetch-based pipeline), event.node is undefined.\n // The Angular renderer requires a req object with at least { headers, url },\n // so we provide a minimal stub to avoid runtime errors in prerender context.\n const req = event.node?.req\n ? {\n ...event.node.req,\n url: requestPath,\n originalUrl: requestPath,\n }\n : {\n headers: { host: 'localhost' },\n url: requestPath,\n originalUrl: requestPath,\n connection: {},\n };\n const res = event.node?.res;\n${SERVER_FETCH_FACTORY_SNIPPET}\n\n const html = await renderer(requestPath, template, { req, res, fetch: serverFetch });\n\n return html;\n});`;\n}\n\n/**\n * Client-only renderer virtual module content.\n *\n * Used when SSR is disabled — simply serves the static index.html template\n * for every route, letting the client-side Angular router handle navigation.\n */\nexport function clientRenderer() {\n return `\nimport { defineHandler } from 'nitro/h3';\nimport template from '#analog/index';\n\nexport default defineHandler(async (event) => {\n event.res.headers.set('content-type', 'text/html; charset=utf-8');\n return template;\n});\n`;\n}\n\n/**\n * API middleware virtual module content.\n *\n * Intercepts requests matching the configured API prefix and either:\n * - Uses event-bound internal forwarding for GET requests (except .xml routes)\n * - Uses request proxying for all other methods to forward the full request\n *\n * h3 v2 idiomatic APIs used:\n * - defineHandler (replaces defineEventHandler / eventHandler)\n * - event.path (replaces event.node.req.url)\n * - event.method (replaces event.node.req.method)\n * - proxyRequest is retained internally because it preserves Nitro route\n * matching for event-bound server requests during SSR/prerender\n * - Object.fromEntries(event.req.headers.entries()) replaces direct event.node.req.headers access\n *\n * `fetchWithEvent` keeps the active event context while forwarding to a\n * rewritten path, which avoids falling through to the HTML renderer when\n * SSR code makes relative API requests.\n */\nexport const apiMiddleware = `\nimport { defineHandler, fetchWithEvent, proxyRequest } from 'nitro/h3';\nimport { useRuntimeConfig } from 'nitro/runtime-config';\n\nexport default defineHandler(async (event) => {\n const prefix = useRuntimeConfig().prefix;\n const apiPrefix = \\`\\${prefix}/\\${useRuntimeConfig().apiPrefix}\\`;\n\n if (event.path?.startsWith(apiPrefix)) {\n const reqUrl = event.path?.replace(apiPrefix, '');\n\n if (\n event.method === 'GET' &&\n // in the case of XML routes, we want to proxy the request so that nitro gets the correct headers\n // and can render the XML correctly as a static asset\n !event.path?.endsWith('.xml')\n ) {\n return fetchWithEvent(event, reqUrl, {\n headers: Object.fromEntries(event.req.headers.entries()),\n });\n }\n\n return proxyRequest(event, reqUrl);\n }\n});`;\n"],"mappings":";;;;;;;;;;;AAUA,IAAa,+BAA+B;;;;;;;;;;;;;;;;;;;;;AAsB5C,SAAgB,cAAc;AAC5B,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsCP,6BAA6B;;;;;;;;;;;;;AAc/B,SAAgB,iBAAiB;AAC/B,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BT,IAAa,gBAAgB"}
@@ -0,0 +1,2 @@
1
+ export declare function isRolldown(): boolean;
2
+ export declare function getBundleOptionsKey(): "rolldownOptions" | "rollupOptions";
@@ -0,0 +1,12 @@
1
+ import * as vite from "vite";
2
+ //#region packages/vite-plugin-nitro/src/lib/utils/rolldown.ts
3
+ function isRolldown() {
4
+ return !!vite.rolldownVersion;
5
+ }
6
+ function getBundleOptionsKey() {
7
+ return isRolldown() ? "rolldownOptions" : "rollupOptions";
8
+ }
9
+ //#endregion
10
+ export { getBundleOptionsKey, isRolldown };
11
+
12
+ //# sourceMappingURL=rolldown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rolldown.js","names":[],"sources":["../../../../../../packages/vite-plugin-nitro/src/lib/utils/rolldown.ts"],"sourcesContent":["import * as vite from 'vite';\n\nexport function isRolldown(): boolean {\n return !!vite.rolldownVersion;\n}\n\nexport function getBundleOptionsKey(): 'rolldownOptions' | 'rollupOptions' {\n return isRolldown() ? 'rolldownOptions' : 'rollupOptions';\n}\n"],"mappings":";;AAEA,SAAgB,aAAsB;AACpC,QAAO,CAAC,CAAC,KAAK;;AAGhB,SAAgB,sBAA2D;AACzE,QAAO,YAAY,GAAG,oBAAoB"}
@@ -1,4 +1,4 @@
1
- import { NitroConfig } from 'nitropack';
2
- import type { Plugin } from 'vite';
3
- import { Options } from './options.js';
1
+ import type { NitroConfig } from "nitro/types";
2
+ import type { Plugin } from "vite";
3
+ import { Options } from "./options.js";
4
4
  export declare function nitro(options?: Options, nitroOptions?: NitroConfig): Plugin[];