@analogjs/vite-plugin-nitro 3.0.0-alpha.2 → 3.0.0-alpha.21

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 +18 -10
  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 +100 -122
  7. package/src/lib/build-server.js.map +1 -1
  8. package/src/lib/build-sitemap.d.ts +9 -9
  9. package/src/lib/build-sitemap.js +130 -63
  10. package/src/lib/build-sitemap.js.map +1 -1
  11. package/src/lib/build-ssr.d.ts +3 -2
  12. package/src/lib/build-ssr.js +26 -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 +132 -106
  18. package/src/lib/plugins/dev-server-plugin.d.ts +3 -3
  19. package/src/lib/plugins/dev-server-plugin.js +91 -101
  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 +27 -62
  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 -97
  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 -84
  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 +1 -1
  32. package/src/lib/utils/node-web-bridge.js +50 -45
  33. package/src/lib/utils/node-web-bridge.js.map +1 -1
  34. package/src/lib/utils/register-dev-middleware.d.ts +12 -12
  35. package/src/lib/utils/register-dev-middleware.js +41 -44
  36. package/src/lib/utils/register-dev-middleware.js.map +1 -1
  37. package/src/lib/utils/renderers.d.ts +49 -38
  38. package/src/lib/utils/renderers.js +66 -53
  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 +741 -687
  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,62 +1,62 @@
1
- import type { NitroEventHandler } from 'nitro/types';
1
+ import type { NitroEventHandler } from "nitro/types";
2
2
  type GetHandlersArgs = {
3
- workspaceRoot: string;
4
- sourceRoot: string;
5
- rootDir: string;
6
- additionalPagesDirs?: string[];
7
- hasAPIDir?: boolean;
3
+ workspaceRoot: string;
4
+ sourceRoot: string;
5
+ rootDir: string;
6
+ additionalPagesDirs?: string[];
7
+ hasAPIDir?: boolean;
8
8
  };
9
9
  /**
10
- * Discovers and generates Nitro event handlers for server-side page routes.
11
- *
12
- * This function:
13
- * 1. Discovers all .server.ts files in the app/pages directory and additional pages directories
14
- * 2. Converts file paths to route patterns using Angular-style route syntax
15
- * 3. Generates Nitro event handlers with proper route mapping and lazy loading
16
- * 4. Handles dynamic route parameters and catch-all routes
17
- *
18
- * @param workspaceRoot The workspace root directory path
19
- * @param sourceRoot The source directory path (e.g., 'src')
20
- * @param rootDir The project root directory relative to workspace
21
- * @param additionalPagesDirs Optional array of additional pages directories to scan
22
- * @param hasAPIDir Whether the project has an API directory (affects route prefixing)
23
- * @returns Array of NitroEventHandler objects with handler paths and route patterns
24
- *
25
- * Example usage:
26
- * const handlers = getPageHandlers({
27
- * workspaceRoot: '/workspace',
28
- * sourceRoot: 'src',
29
- * rootDir: 'apps/my-app',
30
- * additionalPagesDirs: ['/libs/shared/pages'],
31
- * hasAPIDir: true
32
- * });
33
- *
34
- * Sample discovered file paths:
35
- * - /workspace/apps/my-app/src/app/pages/index.server.ts
36
- * - /workspace/apps/my-app/src/app/pages/users/[id].server.ts
37
- * - /workspace/apps/my-app/src/app/pages/products/[...slug].server.ts
38
- * - /workspace/apps/my-app/src/app/pages/(auth)/login.server.ts
39
- *
40
- * Route transformation examples:
41
- * - index.server.ts → /_analog/pages/index
42
- * - users/[id].server.ts → /_analog/pages/users/:id
43
- * - products/[...slug].server.ts → /_analog/pages/products/**:slug
44
- * - (auth)/login.server.ts → /_analog/pages/-auth-/login
45
- *
46
- * tinyglobby vs fast-glob comparison:
47
- * - Both support the same glob patterns for file discovery
48
- * - Both are efficient for finding server-side page files
49
- * - tinyglobby is now used instead of fast-glob
50
- * - tinyglobby provides similar functionality with smaller bundle size
51
- * - tinyglobby's globSync returns absolute paths when absolute: true is set
52
- *
53
- * Route transformation rules:
54
- * 1. Removes .server.ts extension
55
- * 2. Converts [param] to :param for dynamic routes
56
- * 3. Converts [...param] to **:param for catch-all routes
57
- * 4. Converts (group) to -group- for route groups
58
- * 5. Converts dots to forward slashes
59
- * 6. Prefixes with /_analog/pages and optionally /api
60
- */
61
- export declare function getPageHandlers({ workspaceRoot, sourceRoot, rootDir, additionalPagesDirs, hasAPIDir, }: GetHandlersArgs): NitroEventHandler[];
10
+ * Discovers and generates Nitro event handlers for server-side page routes.
11
+ *
12
+ * This function:
13
+ * 1. Discovers all .server.ts files in the app/pages directory and additional pages directories
14
+ * 2. Converts file paths to route patterns using Angular-style route syntax
15
+ * 3. Generates Nitro event handlers with proper route mapping and lazy loading
16
+ * 4. Handles dynamic route parameters and catch-all routes
17
+ *
18
+ * @param workspaceRoot The workspace root directory path
19
+ * @param sourceRoot The source directory path (e.g., 'src')
20
+ * @param rootDir The project root directory relative to workspace
21
+ * @param additionalPagesDirs Optional array of additional pages directories to scan
22
+ * @param hasAPIDir Whether the project has an API directory (affects route prefixing)
23
+ * @returns Array of NitroEventHandler objects with handler paths and route patterns
24
+ *
25
+ * Example usage:
26
+ * const handlers = getPageHandlers({
27
+ * workspaceRoot: '/workspace',
28
+ * sourceRoot: 'src',
29
+ * rootDir: 'apps/my-app',
30
+ * additionalPagesDirs: ['/libs/shared/pages'],
31
+ * hasAPIDir: true
32
+ * });
33
+ *
34
+ * Sample discovered file paths:
35
+ * - /workspace/apps/my-app/src/app/pages/index.server.ts
36
+ * - /workspace/apps/my-app/src/app/pages/users/[id].server.ts
37
+ * - /workspace/apps/my-app/src/app/pages/products/[...slug].server.ts
38
+ * - /workspace/apps/my-app/src/app/pages/(auth)/login.server.ts
39
+ *
40
+ * Route transformation examples:
41
+ * - index.server.ts → /_analog/pages/index
42
+ * - users/[id].server.ts → /_analog/pages/users/:id
43
+ * - products/[...slug].server.ts → /_analog/pages/products/**:slug
44
+ * - (auth)/login.server.ts → /_analog/pages/-auth-/login
45
+ *
46
+ * tinyglobby vs fast-glob comparison:
47
+ * - Both support the same glob patterns for file discovery
48
+ * - Both are efficient for finding server-side page files
49
+ * - tinyglobby is now used instead of fast-glob
50
+ * - tinyglobby provides similar functionality with smaller bundle size
51
+ * - tinyglobby's globSync returns absolute paths when absolute: true is set
52
+ *
53
+ * Route transformation rules:
54
+ * 1. Removes .server.ts extension
55
+ * 2. Converts [param] to :param for dynamic routes
56
+ * 3. Converts [...param] to **:param for catch-all routes
57
+ * 4. Converts (group) to -group- for route groups
58
+ * 5. Converts dots to forward slashes
59
+ * 6. Prefixes with /_analog/pages and optionally /api
60
+ */
61
+ export declare function getPageHandlers({ workspaceRoot, sourceRoot, rootDir, additionalPagesDirs, hasAPIDir }: GetHandlersArgs): NitroEventHandler[];
62
62
  export {};
@@ -1,87 +1,73 @@
1
- import { resolve } from 'node:path';
2
- import { globSync } from 'tinyglobby';
3
- import { normalizePath } from 'vite';
1
+ import { normalizePath } from "vite";
2
+ import { resolve } from "node:path";
3
+ import { globSync } from "tinyglobby";
4
+ //#region packages/vite-plugin-nitro/src/lib/utils/get-page-handlers.ts
4
5
  /**
5
- * Discovers and generates Nitro event handlers for server-side page routes.
6
- *
7
- * This function:
8
- * 1. Discovers all .server.ts files in the app/pages directory and additional pages directories
9
- * 2. Converts file paths to route patterns using Angular-style route syntax
10
- * 3. Generates Nitro event handlers with proper route mapping and lazy loading
11
- * 4. Handles dynamic route parameters and catch-all routes
12
- *
13
- * @param workspaceRoot The workspace root directory path
14
- * @param sourceRoot The source directory path (e.g., 'src')
15
- * @param rootDir The project root directory relative to workspace
16
- * @param additionalPagesDirs Optional array of additional pages directories to scan
17
- * @param hasAPIDir Whether the project has an API directory (affects route prefixing)
18
- * @returns Array of NitroEventHandler objects with handler paths and route patterns
19
- *
20
- * Example usage:
21
- * const handlers = getPageHandlers({
22
- * workspaceRoot: '/workspace',
23
- * sourceRoot: 'src',
24
- * rootDir: 'apps/my-app',
25
- * additionalPagesDirs: ['/libs/shared/pages'],
26
- * hasAPIDir: true
27
- * });
28
- *
29
- * Sample discovered file paths:
30
- * - /workspace/apps/my-app/src/app/pages/index.server.ts
31
- * - /workspace/apps/my-app/src/app/pages/users/[id].server.ts
32
- * - /workspace/apps/my-app/src/app/pages/products/[...slug].server.ts
33
- * - /workspace/apps/my-app/src/app/pages/(auth)/login.server.ts
34
- *
35
- * Route transformation examples:
36
- * - index.server.ts → /_analog/pages/index
37
- * - users/[id].server.ts → /_analog/pages/users/:id
38
- * - products/[...slug].server.ts → /_analog/pages/products/**:slug
39
- * - (auth)/login.server.ts → /_analog/pages/-auth-/login
40
- *
41
- * tinyglobby vs fast-glob comparison:
42
- * - Both support the same glob patterns for file discovery
43
- * - Both are efficient for finding server-side page files
44
- * - tinyglobby is now used instead of fast-glob
45
- * - tinyglobby provides similar functionality with smaller bundle size
46
- * - tinyglobby's globSync returns absolute paths when absolute: true is set
47
- *
48
- * Route transformation rules:
49
- * 1. Removes .server.ts extension
50
- * 2. Converts [param] to :param for dynamic routes
51
- * 3. Converts [...param] to **:param for catch-all routes
52
- * 4. Converts (group) to -group- for route groups
53
- * 5. Converts dots to forward slashes
54
- * 6. Prefixes with /_analog/pages and optionally /api
55
- */
56
- export function getPageHandlers({ workspaceRoot, sourceRoot, rootDir, additionalPagesDirs, hasAPIDir, }) {
57
- // Normalize the project root path for consistent path handling
58
- const root = normalizePath(resolve(workspaceRoot, rootDir));
59
- // Discover all .server.ts files in the app/pages directory and additional pages directories
60
- // Pattern: looks for any .server.ts files in app/pages/**/*.server.ts and additional directories
61
- const endpointFiles = globSync([
62
- `${root}/${sourceRoot}/app/pages/**/*.server.ts`,
63
- ...(additionalPagesDirs || []).map((dir) => `${workspaceRoot}${dir}/**/*.server.ts`),
64
- ], { dot: true, absolute: true });
65
- // Transform each discovered file into a Nitro event handler
66
- const handlers = endpointFiles.map((endpointFile) => {
67
- // Normalize the endpoint file path for consistent path handling
68
- const normalized = normalizePath(endpointFile);
69
- // Transform the normalized path into a route pattern
70
- const route = normalized
71
- .replace(/^(.*?)\/pages/, '/pages')
72
- .replace(/\.server\.ts$/, '') // Remove .server.ts extension
73
- .replace(/\[\.{3}(.+)\]/g, '**:$1') // Convert [...param] to **:param (catch-all routes)
74
- .replace(/\[\.{3}(\w+)\]/g, '**:$1') // Alternative catch-all pattern
75
- .replace(/\/\((.*?)\)$/, '/-$1-') // Convert (group) to -group- (route groups)
76
- .replace(/\[(\w+)\]/g, ':$1') // Convert [param] to :param (dynamic routes)
77
- .replace(/\./g, '/'); // Convert dots to forward slashes
78
- // Return Nitro event handler with absolute handler path and transformed route
79
- return {
80
- handler: endpointFile,
81
- route: `${hasAPIDir ? '/api' : ''}/_analog${route}`,
82
- lazy: true,
83
- };
84
- });
85
- return handlers;
6
+ * Discovers and generates Nitro event handlers for server-side page routes.
7
+ *
8
+ * This function:
9
+ * 1. Discovers all .server.ts files in the app/pages directory and additional pages directories
10
+ * 2. Converts file paths to route patterns using Angular-style route syntax
11
+ * 3. Generates Nitro event handlers with proper route mapping and lazy loading
12
+ * 4. Handles dynamic route parameters and catch-all routes
13
+ *
14
+ * @param workspaceRoot The workspace root directory path
15
+ * @param sourceRoot The source directory path (e.g., 'src')
16
+ * @param rootDir The project root directory relative to workspace
17
+ * @param additionalPagesDirs Optional array of additional pages directories to scan
18
+ * @param hasAPIDir Whether the project has an API directory (affects route prefixing)
19
+ * @returns Array of NitroEventHandler objects with handler paths and route patterns
20
+ *
21
+ * Example usage:
22
+ * const handlers = getPageHandlers({
23
+ * workspaceRoot: '/workspace',
24
+ * sourceRoot: 'src',
25
+ * rootDir: 'apps/my-app',
26
+ * additionalPagesDirs: ['/libs/shared/pages'],
27
+ * hasAPIDir: true
28
+ * });
29
+ *
30
+ * Sample discovered file paths:
31
+ * - /workspace/apps/my-app/src/app/pages/index.server.ts
32
+ * - /workspace/apps/my-app/src/app/pages/users/[id].server.ts
33
+ * - /workspace/apps/my-app/src/app/pages/products/[...slug].server.ts
34
+ * - /workspace/apps/my-app/src/app/pages/(auth)/login.server.ts
35
+ *
36
+ * Route transformation examples:
37
+ * - index.server.ts → /_analog/pages/index
38
+ * - users/[id].server.ts → /_analog/pages/users/:id
39
+ * - products/[...slug].server.ts → /_analog/pages/products/**:slug
40
+ * - (auth)/login.server.ts → /_analog/pages/-auth-/login
41
+ *
42
+ * tinyglobby vs fast-glob comparison:
43
+ * - Both support the same glob patterns for file discovery
44
+ * - Both are efficient for finding server-side page files
45
+ * - tinyglobby is now used instead of fast-glob
46
+ * - tinyglobby provides similar functionality with smaller bundle size
47
+ * - tinyglobby's globSync returns absolute paths when absolute: true is set
48
+ *
49
+ * Route transformation rules:
50
+ * 1. Removes .server.ts extension
51
+ * 2. Converts [param] to :param for dynamic routes
52
+ * 3. Converts [...param] to **:param for catch-all routes
53
+ * 4. Converts (group) to -group- for route groups
54
+ * 5. Converts dots to forward slashes
55
+ * 6. Prefixes with /_analog/pages and optionally /api
56
+ */
57
+ function getPageHandlers({ workspaceRoot, sourceRoot, rootDir, additionalPagesDirs, hasAPIDir }) {
58
+ return globSync([`${normalizePath(resolve(workspaceRoot, rootDir))}/${sourceRoot}/app/pages/**/*.server.ts`, ...(additionalPagesDirs || []).map((dir) => `${workspaceRoot}${dir}/**/*.server.ts`)], {
59
+ dot: true,
60
+ absolute: true
61
+ }).map((endpointFile) => {
62
+ const route = normalizePath(endpointFile).replace(/^(.*?)\/pages/, "/pages").replace(/\.server\.ts$/, "").replace(/\[\.{3}(.+)\]/g, "**:$1").replace(/\[\.{3}(\w+)\]/g, "**:$1").replace(/\/\((.*?)\)$/, "/-$1-").replace(/\[(\w+)\]/g, ":$1").replace(/\./g, "/");
63
+ return {
64
+ handler: endpointFile,
65
+ route: `${hasAPIDir ? "/api" : ""}/_analog${route}`,
66
+ lazy: true
67
+ };
68
+ });
86
69
  }
70
+ //#endregion
71
+ export { getPageHandlers };
72
+
87
73
  //# sourceMappingURL=get-page-handlers.js.map
@@ -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,gEAAgE;QAChE,MAAM,UAAU,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;QAC/C,qDAAqD;QACrD,MAAM,KAAK,GAAG,UAAU;aACrB,OAAO,CAAC,eAAe,EAAE,QAAQ,CAAC;aAClC,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":["../../../../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>;
@@ -1,3 +1,3 @@
1
- import type { IncomingMessage, ServerResponse } from 'node:http';
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  export declare function toWebRequest(req: IncomingMessage): Request;
3
3
  export declare function writeWebResponseToNode(res: ServerResponse, response: Response): Promise<void>;
@@ -1,50 +1,55 @@
1
- import { Readable } from 'node:stream';
2
- import { pipeline } from 'node:stream/promises';
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
3
4
  function toWebHeaders(headers) {
4
- return Object.entries(headers).reduce((acc, [key, value]) => {
5
- if (value) {
6
- acc.set(key, Array.isArray(value) ? value.join(', ') : value);
7
- }
8
- return acc;
9
- }, new Headers());
5
+ return Object.entries(headers).reduce((acc, [key, value]) => {
6
+ if (value && !key.startsWith(":")) acc.set(key, Array.isArray(value) ? value.join(", ") : value);
7
+ return acc;
8
+ }, new Headers());
10
9
  }
11
- export function toWebRequest(req) {
12
- const protocol = 'http';
13
- const host = req.headers.host || 'localhost';
14
- const url = new URL(req.url || '/', `${protocol}://${host}`);
15
- const body = req.method && !['GET', 'HEAD'].includes(req.method)
16
- ? Readable.toWeb(req)
17
- : undefined;
18
- return new Request(url, {
19
- method: req.method,
20
- headers: toWebHeaders(req.headers),
21
- body,
22
- // @ts-expect-error duplex is required for streaming request bodies in Node.js
23
- duplex: body ? 'half' : undefined,
24
- });
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
+ });
25
21
  }
26
- export async function writeWebResponseToNode(res, response) {
27
- res.statusCode = response.status;
28
- res.statusMessage = response.statusText;
29
- const setCookies = 'getSetCookie' in response.headers &&
30
- typeof response.headers.getSetCookie === 'function'
31
- ? response.headers.getSetCookie()
32
- : [];
33
- if (setCookies.length > 0) {
34
- res.setHeader('set-cookie', setCookies);
35
- }
36
- response.headers.forEach((value, key) => {
37
- if (key !== 'set-cookie') {
38
- res.setHeader(key, value);
39
- }
40
- });
41
- if (!response.body) {
42
- res.end();
43
- return;
44
- }
45
- // The Web ReadableStream and Node.js stream/web ReadableStream types
46
- // are structurally identical at runtime but TypeScript treats them as
47
- // distinct nominal types. The double-cast bridges this gap safely.
48
- await pipeline(Readable.fromWeb(response.body), res);
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);
49
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
+
50
55
  //# sourceMappingURL=node-web-bridge.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"node-web-bridge.js","sourceRoot":"","sources":["../../../../../../packages/vite-plugin-nitro/src/lib/utils/node-web-bridge.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,SAAS,YAAY,CAAC,OAA4B;IAChD,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QAC1D,IAAI,KAAK,EAAE,CAAC;YACV,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAChE,CAAC;QAED,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,IAAI,OAAO,EAAE,CAAC,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAoB;IAC/C,MAAM,QAAQ,GAAG,MAAM,CAAC;IACxB,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,GAAG,QAAQ,MAAM,IAAI,EAAE,CAAC,CAAC;IAC7D,MAAM,IAAI,GACR,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC;QACjD,CAAC,CAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAgC;QACrD,CAAC,CAAC,SAAS,CAAC;IAEhB,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE;QACtB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC;QAClC,IAAI;QACJ,8EAA8E;QAC9E,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;KAClC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,GAAmB,EACnB,QAAkB;IAElB,GAAG,CAAC,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC;IACjC,GAAG,CAAC,aAAa,GAAG,QAAQ,CAAC,UAAU,CAAC;IAExC,MAAM,UAAU,GACd,cAAc,IAAI,QAAQ,CAAC,OAAO;QAClC,OAAO,QAAQ,CAAC,OAAO,CAAC,YAAY,KAAK,UAAU;QACjD,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE;QACjC,CAAC,CAAC,EAAE,CAAC;IAET,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAC1C,CAAC;IAED,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtC,IAAI,GAAG,KAAK,YAAY,EAAE,CAAC;YACzB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnB,GAAG,CAAC,GAAG,EAAE,CAAC;QACV,OAAO;IACT,CAAC;IAED,qEAAqE;IACrE,sEAAsE;IACtE,mEAAmE;IACnE,MAAM,QAAQ,CACZ,QAAQ,CAAC,OAAO,CACd,QAAQ,CAAC,IAA2D,CACrE,EACD,GAAG,CACJ,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"node-web-bridge.js","names":[],"sources":["../../../../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 && !key.startsWith(':')) {\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,SAAS,CAAC,IAAI,WAAW,IAAI,CAC/B,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,14 +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
- * 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
- */
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
+ */
14
14
  export declare function registerDevServerMiddleware(root: string, sourceRoot: string, viteServer: ViteDevServer): Promise<void>;
@@ -1,47 +1,44 @@
1
- import { H3 } from 'nitro/h3';
2
- import { globSync } from 'tinyglobby';
3
- import { toWebRequest, writeWebResponseToNode } from './node-web-bridge.js';
4
- const PASSTHROUGH_HEADER = 'x-analog-passthrough';
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";
5
6
  /**
6
- * Registers development server middleware by discovering and loading middleware files.
7
- *
8
- * Each discovered h3 middleware module is loaded through Vite's SSR loader,
9
- * wrapped in a temporary H3 app, then bridged back into Vite's Connect stack.
10
- * If the middleware does not write a response, control falls through to the
11
- * next Vite middleware.
12
- *
13
- * @param root The project root directory path
14
- * @param sourceRoot The source directory path (e.g., 'src')
15
- * @param viteServer The Vite development server instance
16
- */
17
- export async function registerDevServerMiddleware(root, sourceRoot, viteServer) {
18
- const middlewareFiles = globSync([`${root}/${sourceRoot}/server/middleware/**/*.ts`], {
19
- dot: true,
20
- absolute: true,
21
- });
22
- middlewareFiles.forEach((file) => {
23
- viteServer.middlewares.use(async (req, res, next) => {
24
- const middlewareHandler = await viteServer
25
- .ssrLoadModule(file)
26
- .then((m) => m.default);
27
- // Bridge h3 event handler using H3.fetch() (web-first API).
28
- // A sentinel catch-all is appended so that when the middleware
29
- // returns undefined (does not handle the request), h3 does not
30
- // emit its default 404 — instead we detect the passthrough
31
- // header and let the Connect stack continue.
32
- const app = new H3();
33
- app.use(middlewareHandler);
34
- app.use(() => new Response(null, {
35
- status: 204,
36
- headers: { [PASSTHROUGH_HEADER]: '1' },
37
- }));
38
- const response = await app.fetch(toWebRequest(req));
39
- if (response.headers.get(PASSTHROUGH_HEADER) === '1') {
40
- next();
41
- return;
42
- }
43
- await writeWebResponseToNode(res, response);
44
- });
45
- });
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
+ });
46
40
  }
41
+ //#endregion
42
+ export { registerDevServerMiddleware };
43
+
47
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,EAAE,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE5E,MAAM,kBAAkB,GAAG,sBAAsB,CAAC;AAElD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,IAAY,EACZ,UAAkB,EAClB,UAAyB;IAEzB,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,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,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,4DAA4D;YAC5D,+DAA+D;YAC/D,+DAA+D;YAC/D,2DAA2D;YAC3D,6CAA6C;YAC7C,MAAM,GAAG,GAAG,IAAI,EAAE,EAAE,CAAC;YACrB,GAAG,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;YAC3B,GAAG,CAAC,GAAG,CACL,GAAG,EAAE,CACH,IAAI,QAAQ,CAAC,IAAI,EAAE;gBACjB,MAAM,EAAE,GAAG;gBACX,OAAO,EAAE,EAAE,CAAC,kBAAkB,CAAC,EAAE,GAAG,EAAE;aACvC,CAAC,CACL,CAAC;YAEF,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;YAEpD,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,KAAK,GAAG,EAAE,CAAC;gBACrD,IAAI,EAAE,CAAC;gBACP,OAAO;YACT,CAAC;YAED,MAAM,sBAAsB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"register-dev-middleware.js","names":[],"sources":["../../../../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"}