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

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 +14 -9
  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,713 +1,767 @@
1
- import { build, createDevServer, createNitro } from 'nitro/builder';
2
- import * as vite from 'vite';
3
- import { mergeConfig, normalizePath } from 'vite';
4
- import { relative, resolve } from 'node:path';
5
- import { pathToFileURL } from 'node:url';
6
- import { existsSync, readFileSync } from 'node:fs';
7
- import { buildServer } from './build-server.js';
8
- import { buildSSRApp } from './build-ssr.js';
9
- import { pageEndpointsPlugin } from './plugins/page-endpoints.js';
10
- import { getPageHandlers } from './utils/get-page-handlers.js';
11
- import { buildSitemap } from './build-sitemap.js';
12
- import { devServerPlugin } from './plugins/dev-server-plugin.js';
13
- import { toWebRequest, writeWebResponseToNode, } from './utils/node-web-bridge.js';
14
- import { getMatchingContentFilesWithFrontMatter } from './utils/get-content-files.js';
15
- import { ssrRenderer, clientRenderer, apiMiddleware, } from './utils/renderers.js';
16
- let clientOutputPath = '';
17
- let rendererIndexEntry = '';
1
+ import { buildServer, isVercelPreset } from "./build-server.js";
2
+ import { getBundleOptionsKey, isRolldown } from "./utils/rolldown.js";
3
+ import { buildClientApp, buildSSRApp } from "./build-ssr.js";
4
+ import { apiMiddleware, clientRenderer, ssrRenderer } from "./utils/renderers.js";
5
+ import { pageEndpointsPlugin } from "./plugins/page-endpoints.js";
6
+ import { getPageHandlers } from "./utils/get-page-handlers.js";
7
+ import { buildSitemap } from "./build-sitemap.js";
8
+ import { toWebRequest, writeWebResponseToNode } from "./utils/node-web-bridge.js";
9
+ import { devServerPlugin } from "./plugins/dev-server-plugin.js";
10
+ import { getMatchingContentFilesWithFrontMatter } from "./utils/get-content-files.js";
11
+ import { build, createDevServer, createNitro } from "nitro/builder";
12
+ import { mergeConfig, normalizePath } from "vite";
13
+ import { relative, resolve } from "node:path";
14
+ import { pathToFileURL } from "node:url";
15
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
16
+ //#region packages/vite-plugin-nitro/src/lib/vite-plugin-nitro.ts
18
17
  function createNitroMiddlewareHandler(handler) {
19
- return {
20
- route: '/**',
21
- handler,
22
- middleware: true,
23
- };
18
+ return {
19
+ route: "/**",
20
+ handler,
21
+ middleware: true
22
+ };
24
23
  }
25
24
  /**
26
- * Creates a `rollup:before` hook that marks specified packages as external
27
- * in Nitro's bundler config (applied to both the server build and the
28
- * prerender build).
29
- *
30
- * ## Subpath matching (Rolldown compatibility)
31
- *
32
- * When `bundlerConfig.external` is an **array**, Rollup automatically
33
- * prefix-matches entries — `'rxjs'` in the array will also externalise
34
- * `'rxjs/operators'`, `'rxjs/internal/Observable'`, etc.
35
- *
36
- * Rolldown (the default bundler in Nitro v3) does **not** do this. It
37
- * treats array entries as exact strings. To keep behaviour consistent
38
- * across both bundlers, the **function** branch already needed explicit
39
- * subpath matching. We now use the same `isExternal` helper for all
40
- * branches so that `'rxjs'` reliably matches `'rxjs/operators'`
41
- * regardless of whether the existing `external` value is a function,
42
- * array, or absent.
43
- *
44
- * Without this, the Nitro prerender build fails on Windows CI with:
45
- *
46
- * [RESOLVE_ERROR] Could not resolve 'rxjs/operators'
47
- */
25
+ * Creates a `rollup:before` hook that marks specified packages as external
26
+ * in Nitro's bundler config (applied to both the server build and the
27
+ * prerender build).
28
+ *
29
+ * ## Subpath matching (Rolldown compatibility)
30
+ *
31
+ * When `bundlerConfig.external` is an **array**, Rollup automatically
32
+ * prefix-matches entries — `'rxjs'` in the array will also externalise
33
+ * `'rxjs/operators'`, `'rxjs/internal/Observable'`, etc.
34
+ *
35
+ * Rolldown (the default bundler in Nitro v3) does **not** do this. It
36
+ * treats array entries as exact strings. To keep behaviour consistent
37
+ * across both bundlers, the **function** branch already needed explicit
38
+ * subpath matching. We now use the same `isExternal` helper for all
39
+ * branches so that `'rxjs'` reliably matches `'rxjs/operators'`
40
+ * regardless of whether the existing `external` value is a function,
41
+ * array, or absent.
42
+ *
43
+ * Without this, the Nitro prerender build fails on Windows CI with:
44
+ *
45
+ * [RESOLVE_ERROR] Could not resolve 'rxjs/operators'
46
+ */
48
47
  function createRollupBeforeHook(externalEntries) {
49
- const isExternal = (source) => externalEntries.some((entry) => source === entry || source.startsWith(entry + '/'));
50
- return (_nitro, bundlerConfig) => {
51
- sanitizeNitroBundlerConfig(_nitro, bundlerConfig);
52
- if (externalEntries.length === 0) {
53
- return;
54
- }
55
- const existing = bundlerConfig.external;
56
- if (!existing) {
57
- bundlerConfig.external = externalEntries;
58
- }
59
- else if (typeof existing === 'function') {
60
- bundlerConfig.external = (source, importer, isResolved) => existing(source, importer, isResolved) || isExternal(source);
61
- }
62
- else if (Array.isArray(existing)) {
63
- bundlerConfig.external = [...existing, ...externalEntries];
64
- }
65
- else {
66
- bundlerConfig.external = [existing, ...externalEntries];
67
- }
68
- };
48
+ const isExternal = (source) => externalEntries.some((entry) => source === entry || source.startsWith(entry + "/"));
49
+ return (_nitro, bundlerConfig) => {
50
+ sanitizeNitroBundlerConfig(_nitro, bundlerConfig);
51
+ if (externalEntries.length === 0) return;
52
+ const existing = bundlerConfig.external;
53
+ if (!existing) bundlerConfig.external = externalEntries;
54
+ else if (typeof existing === "function") bundlerConfig.external = (source, importer, isResolved) => existing(source, importer, isResolved) || isExternal(source);
55
+ else if (Array.isArray(existing)) bundlerConfig.external = [...existing, ...externalEntries];
56
+ else bundlerConfig.external = [existing, ...externalEntries];
57
+ };
69
58
  }
70
59
  function appendNoExternals(noExternals, ...entries) {
71
- if (!noExternals) {
72
- return entries;
73
- }
74
- return Array.isArray(noExternals)
75
- ? [...noExternals, ...entries]
76
- : noExternals;
60
+ if (!noExternals) return entries;
61
+ return Array.isArray(noExternals) ? [...noExternals, ...entries] : noExternals;
77
62
  }
78
63
  /**
79
- * Patches Nitro's internal Rollup/Rolldown bundler config to work around
80
- * incompatibilities in the Nitro v3 alpha series.
81
- *
82
- * Called from the `rollup:before` hook, this function runs against the *final*
83
- * bundler config that Nitro assembles for its server/prerender builds — it
84
- * does NOT touch the normal Vite client or SSR environment configs.
85
- *
86
- * Each workaround is narrowly scoped and safe to remove once the corresponding
87
- * upstream Nitro issue is resolved.
88
- */
64
+ * Patches Nitro's internal Rollup/Rolldown bundler config to work around
65
+ * incompatibilities in the Nitro v3 alpha series.
66
+ *
67
+ * Called from the `rollup:before` hook, this function runs against the *final*
68
+ * bundler config that Nitro assembles for its server/prerender builds — it
69
+ * does NOT touch the normal Vite client or SSR environment configs.
70
+ *
71
+ * Each workaround is narrowly scoped and safe to remove once the corresponding
72
+ * upstream Nitro issue is resolved.
73
+ */
89
74
  function sanitizeNitroBundlerConfig(_nitro, bundlerConfig) {
90
- const output = bundlerConfig['output'];
91
- if (!output || Array.isArray(output) || typeof output !== 'object') {
92
- return;
93
- }
94
- // ── 1. Remove invalid `output.codeSplitting` ────────────────────────
95
- //
96
- // Nitro 3.0.1-alpha.2 adds `output.codeSplitting` to its internal bundler
97
- // config, but Rolldown rejects it as an unknown key:
98
- //
99
- // Warning: Invalid output options (1 issue found)
100
- // - For the "codeSplitting". Invalid key: Expected never but received "codeSplitting".
101
- //
102
- // Analog never sets this option. Removing it restores default bundler
103
- // behavior without changing any Analog semantics.
104
- if ('codeSplitting' in output) {
105
- delete output['codeSplitting'];
106
- }
107
- // ── 2. Remove invalid `output.manualChunks` ─────────────────────────
108
- //
109
- // Nitro's default config enables manual chunking for node_modules. Under
110
- // Nitro v3 alpha + Rollup 4.59 this crashes during the prerender rebundle:
111
- //
112
- // Cannot read properties of undefined (reading 'included')
113
- //
114
- // A single server bundle is acceptable for Analog's use case, so we strip
115
- // `manualChunks` until the upstream bug is fixed.
116
- if ('manualChunks' in output) {
117
- delete output['manualChunks'];
118
- }
119
- // ── 3. Escape route params in `output.chunkFileNames` ───────────────
120
- //
121
- // Nitro's `getChunkName()` derives chunk filenames from route patterns,
122
- // using its internal `routeToFsPath()` helper to convert route params
123
- // (`:productId` → `[productId]`) and catch-alls (`**` → `[...]`).
124
- //
125
- // Rollup/Rolldown interprets *any* `[token]` in the string returned by a
126
- // `chunkFileNames` function as a placeholder. Only a handful are valid —
127
- // `[name]`, `[hash]`, `[format]`, `[ext]` — so route-derived tokens like
128
- // `[productId]` or `[...]` trigger a build error:
129
- //
130
- // "[productId]" is not a valid placeholder in the "output.chunkFileNames" pattern.
131
- //
132
- // We wrap the original function to replace non-standard `[token]` patterns
133
- // with `_token_`, preserving the intended filename while avoiding the
134
- // placeholder validation error.
135
- //
136
- // Example: `_routes/products/[productId].mjs` → `_routes/products/_productId_.mjs`
137
- const VALID_ROLLUP_PLACEHOLDER = /^\[(?:name|hash|format|ext)\]$/;
138
- const chunkFileNames = output['chunkFileNames'];
139
- if (typeof chunkFileNames === 'function') {
140
- const originalFn = chunkFileNames;
141
- output['chunkFileNames'] = (...args) => {
142
- const result = originalFn(...args);
143
- if (typeof result !== 'string')
144
- return result;
145
- return result.replace(/\[[^\]]+\]/g, (match) => VALID_ROLLUP_PLACEHOLDER.test(match)
146
- ? match
147
- : `_${match.slice(1, -1)}_`);
148
- };
149
- }
75
+ const output = bundlerConfig["output"];
76
+ if (!output || Array.isArray(output) || typeof output !== "object") return;
77
+ if ("codeSplitting" in output) delete output["codeSplitting"];
78
+ if ("manualChunks" in output) delete output["manualChunks"];
79
+ const VALID_ROLLUP_PLACEHOLDER = /^\[(?:name|hash|format|ext)\]$/;
80
+ const chunkFileNames = output["chunkFileNames"];
81
+ if (typeof chunkFileNames === "function") {
82
+ const originalFn = chunkFileNames;
83
+ output["chunkFileNames"] = (...args) => {
84
+ const result = originalFn(...args);
85
+ if (typeof result !== "string") return result;
86
+ return result.replace(/\[[^\]]+\]/g, (match) => VALID_ROLLUP_PLACEHOLDER.test(match) ? match : `_${match.slice(1, -1)}_`);
87
+ };
88
+ }
150
89
  }
151
- function resolveClientOutputPath(workspaceRoot, rootDir, configuredOutDir, ssrBuild) {
152
- if (clientOutputPath) {
153
- return clientOutputPath;
154
- }
155
- if (!ssrBuild) {
156
- return resolve(workspaceRoot, rootDir, configuredOutDir || 'dist/client');
157
- }
158
- // SSR builds write server assets to dist/<app>/ssr, but the renderer template
159
- // still needs the client index.html emitted to dist/<app>/client.
160
- return resolve(workspaceRoot, 'dist', rootDir, 'client');
90
+ function resolveClientOutputPath(cachedPath, workspaceRoot, rootDir, configuredOutDir) {
91
+ if (cachedPath) {
92
+ debugLog("resolveClientOutputPath using cached path", () => ({
93
+ cachedPath,
94
+ workspaceRoot,
95
+ rootDir,
96
+ configuredOutDir
97
+ }));
98
+ return cachedPath;
99
+ }
100
+ if (configuredOutDir) {
101
+ const resolvedPath = normalizePath(resolve(workspaceRoot, rootDir, configuredOutDir));
102
+ debugLog("resolveClientOutputPath using configured build.outDir", () => ({
103
+ workspaceRoot,
104
+ rootDir,
105
+ configuredOutDir,
106
+ resolvedPath
107
+ }));
108
+ return resolvedPath;
109
+ }
110
+ const resolvedPath = normalizePath(resolve(workspaceRoot, "dist", rootDir, "client"));
111
+ debugLog("resolveClientOutputPath using default dist client path", () => ({
112
+ workspaceRoot,
113
+ rootDir,
114
+ configuredOutDir,
115
+ resolvedPath
116
+ }));
117
+ return resolvedPath;
118
+ }
119
+ function getEnvironmentBuildOutDir(environment) {
120
+ if (!environment || typeof environment !== "object") return;
121
+ const environmentConfig = environment;
122
+ return environmentConfig.config?.build?.outDir ?? environmentConfig.build?.outDir;
123
+ }
124
+ function resolveBuiltClientOutputPath(cachedPath, workspaceRoot, rootDir, configuredOutDir, environment) {
125
+ const environmentOutDir = getEnvironmentBuildOutDir(environment);
126
+ if (environmentOutDir) {
127
+ const resolvedPath = normalizePath(resolve(workspaceRoot, rootDir, environmentOutDir));
128
+ debugLog("resolveBuiltClientOutputPath using environment outDir", () => ({
129
+ cachedPath,
130
+ workspaceRoot,
131
+ rootDir,
132
+ configuredOutDir,
133
+ environmentOutDir,
134
+ resolvedPath
135
+ }));
136
+ return resolvedPath;
137
+ }
138
+ debugLog("resolveBuiltClientOutputPath falling back to shared resolver", () => ({
139
+ cachedPath,
140
+ workspaceRoot,
141
+ rootDir,
142
+ configuredOutDir,
143
+ environmentOutDir
144
+ }));
145
+ return resolveClientOutputPath(cachedPath, workspaceRoot, rootDir, configuredOutDir);
146
+ }
147
+ function getNitroPublicOutputDir(nitroConfig) {
148
+ const publicDir = nitroConfig.output?.publicDir;
149
+ if (!publicDir) throw new Error("Nitro public output directory is required to build the sitemap.");
150
+ return publicDir;
151
+ }
152
+ var DEBUG_NAMESPACE = "analog:vite-plugin-nitro";
153
+ function escapeRegExp(value) {
154
+ return value.replace(/[|\\{}()[\]^$+?.*]/g, "\\$&");
155
+ }
156
+ function isDebugEnabled(namespace) {
157
+ const debugValue = process.env["DEBUG"];
158
+ if (!debugValue) return false;
159
+ return debugValue.split(/[\s,]+/).filter(Boolean).some((pattern) => {
160
+ return new RegExp(`^${escapeRegExp(pattern).replace(/\\\*/g, ".*")}$`).test(namespace);
161
+ });
162
+ }
163
+ function debugLog(label, details) {
164
+ if (!isDebugEnabled(DEBUG_NAMESPACE)) return;
165
+ const resolvedDetails = typeof details === "function" ? details() : details;
166
+ if (resolvedDetails && Object.keys(resolvedDetails).length > 0) {
167
+ console.log(`DEBUG: ${label}`, resolvedDetails);
168
+ return;
169
+ }
170
+ console.log(`DEBUG: ${label}`);
171
+ }
172
+ function readDirectoryEntries(path) {
173
+ try {
174
+ return readdirSync(path).sort();
175
+ } catch (error) {
176
+ return [`<<unable to read directory: ${error instanceof Error ? error.message : String(error)}>>`];
177
+ }
178
+ }
179
+ function getPathDebugInfo(path) {
180
+ return {
181
+ rawPath: path,
182
+ normalizedPath: normalizePath(path),
183
+ exists: existsSync(path),
184
+ entries: existsSync(path) ? readDirectoryEntries(path) : []
185
+ };
186
+ }
187
+ function assetSourceToString(source) {
188
+ return typeof source === "string" ? source : Buffer.from(source).toString("utf8");
189
+ }
190
+ function captureClientIndexHtmlFromBundle(bundle, hook) {
191
+ const indexHtmlAsset = Object.values(bundle).find((chunk) => chunk.type === "asset" && chunk.fileName === "index.html" && typeof chunk.source !== "undefined");
192
+ if (!indexHtmlAsset?.source) {
193
+ debugLog(`client bundle did not expose index.html during ${hook}`, () => ({
194
+ hook,
195
+ bundleKeys: Object.keys(bundle).sort(),
196
+ assetFileNames: Object.values(bundle).filter((chunk) => chunk.type === "asset").map((chunk) => chunk.fileName).filter(Boolean)
197
+ }));
198
+ return;
199
+ }
200
+ const indexHtml = assetSourceToString(indexHtmlAsset.source);
201
+ debugLog(`captured client bundle index.html asset during ${hook}`, () => ({
202
+ hook,
203
+ fileName: indexHtmlAsset.fileName,
204
+ htmlLength: indexHtml.length
205
+ }));
206
+ return indexHtml;
207
+ }
208
+ function registerIndexHtmlVirtual(nitroConfig, clientOutputPath, inlineIndexHtml) {
209
+ const indexHtmlPath = resolve(clientOutputPath, "index.html");
210
+ debugLog("registerIndexHtmlVirtual inspecting client output", () => ({
211
+ platform: process.platform,
212
+ cwd: process.cwd(),
213
+ clientOutputPath,
214
+ clientOutputPathInfo: getPathDebugInfo(clientOutputPath),
215
+ indexHtmlPath,
216
+ indexHtmlExists: existsSync(indexHtmlPath),
217
+ hasInlineIndexHtml: typeof inlineIndexHtml === "string"
218
+ }));
219
+ if (!existsSync(indexHtmlPath) && typeof inlineIndexHtml !== "string") {
220
+ debugLog("registerIndexHtmlVirtual missing index.html", () => ({
221
+ platform: process.platform,
222
+ cwd: process.cwd(),
223
+ clientOutputPath,
224
+ clientOutputPathInfo: getPathDebugInfo(clientOutputPath),
225
+ indexHtmlPath,
226
+ hasInlineIndexHtml: typeof inlineIndexHtml === "string",
227
+ nitroOutput: nitroConfig.output,
228
+ nitroPublicAssets: nitroConfig.publicAssets
229
+ }));
230
+ throw new Error(`[analog] Client build output not found at ${indexHtmlPath}.\nEnsure the client environment build completed successfully before the server build.`);
231
+ }
232
+ const indexHtml = typeof inlineIndexHtml === "string" ? inlineIndexHtml : readFileSync(indexHtmlPath, "utf8");
233
+ debugLog("registerIndexHtmlVirtual using HTML template source", () => ({
234
+ source: typeof inlineIndexHtml === "string" ? "captured client bundle asset" : "client output index.html file",
235
+ indexHtmlPath
236
+ }));
237
+ nitroConfig.virtual = {
238
+ ...nitroConfig.virtual,
239
+ "#analog/index": `export default ${JSON.stringify(indexHtml)};`
240
+ };
161
241
  }
162
242
  /**
163
- * Converts the built SSR entry path into a specifier that Nitro's bundler
164
- * can resolve, including all relative `./assets/*` chunk imports inside
165
- * the entry.
166
- *
167
- * The returned path **must** be an absolute filesystem path with forward
168
- * slashes (e.g. `D:/a/analog/dist/apps/blog-app/ssr/main.server.js`).
169
- * This lets Rollup/Rolldown determine the entry's directory and resolve
170
- * sibling chunk imports like `./assets/core-DTazUigR.js` correctly.
171
- *
172
- * ## Why not pathToFileURL() on Windows?
173
- *
174
- * Earlier versions converted the path to a `file:///D:/a/...` URL on
175
- * Windows, which worked with Nitro v2 + Rollup. Nitro v3 switched its
176
- * default bundler to Rolldown, and Rolldown does **not** extract the
177
- * importer directory from `file://` URLs. This caused every relative
178
- * import inside the SSR entry to fail during the prerender build:
179
- *
180
- * [RESOLVE_ERROR] Could not resolve './assets/core-DTazUigR.js'
181
- * in ../../dist/apps/blog-app/ssr/main.server.js
182
- *
183
- * `normalizePath()` (from Vite) simply converts backslashes to forward
184
- * slashes, which both Rollup and Rolldown handle correctly on all
185
- * platforms.
186
- */
243
+ * Converts the built SSR entry path into a specifier that Nitro's bundler
244
+ * can resolve, including all relative `./assets/*` chunk imports inside
245
+ * the entry.
246
+ *
247
+ * The returned path **must** be an absolute filesystem path with forward
248
+ * slashes (e.g. `D:/a/analog/dist/apps/blog-app/ssr/main.server.js`).
249
+ * This lets Rollup/Rolldown determine the entry's directory and resolve
250
+ * sibling chunk imports like `./assets/core-DTazUigR.js` correctly.
251
+ *
252
+ * ## Why not pathToFileURL() on Windows?
253
+ *
254
+ * Earlier versions converted the path to a `file:///D:/a/...` URL on
255
+ * Windows, which worked with Nitro v2 + Rollup. Nitro v3 switched its
256
+ * default bundler to Rolldown, and Rolldown does **not** extract the
257
+ * importer directory from `file://` URLs. This caused every relative
258
+ * import inside the SSR entry to fail during the prerender build:
259
+ *
260
+ * [RESOLVE_ERROR] Could not resolve './assets/core-DTazUigR.js'
261
+ * in ../../dist/apps/blog-app/ssr/main.server.js
262
+ *
263
+ * `normalizePath()` (from Vite) simply converts backslashes to forward
264
+ * slashes, which both Rollup and Rolldown handle correctly on all
265
+ * platforms.
266
+ */
187
267
  function toNitroSsrEntrypointSpecifier(ssrEntryPath) {
188
- return normalizePath(ssrEntryPath);
268
+ return normalizePath(ssrEntryPath);
269
+ }
270
+ function applySsrEntryAlias(nitroConfig, options, workspaceRoot, rootDir) {
271
+ const ssrOutDir = options?.ssrBuildDir || resolve(workspaceRoot, "dist", rootDir, "ssr");
272
+ if (options?.ssr || nitroConfig.prerender?.routes?.length) {
273
+ const ssrEntry = toNitroSsrEntrypointSpecifier(resolveBuiltSsrEntryPath(ssrOutDir));
274
+ nitroConfig.alias = {
275
+ ...nitroConfig.alias,
276
+ "#analog/ssr": ssrEntry
277
+ };
278
+ }
189
279
  }
190
280
  function resolveBuiltSsrEntryPath(ssrOutDir) {
191
- const candidatePaths = [
192
- resolve(ssrOutDir, 'main.server.mjs'),
193
- resolve(ssrOutDir, 'main.server.js'),
194
- resolve(ssrOutDir, 'main.server'),
195
- ];
196
- const ssrEntryPath = candidatePaths.find((candidatePath) => existsSync(candidatePath));
197
- if (!ssrEntryPath) {
198
- throw new Error(`Unable to locate the built SSR entry in "${ssrOutDir}". Expected one of: ${candidatePaths.join(', ')}`);
199
- }
200
- return ssrEntryPath;
281
+ const candidatePaths = [
282
+ resolve(ssrOutDir, "main.server.mjs"),
283
+ resolve(ssrOutDir, "main.server.js"),
284
+ resolve(ssrOutDir, "main.server")
285
+ ];
286
+ const ssrEntryPath = candidatePaths.find((candidatePath) => existsSync(candidatePath));
287
+ if (!ssrEntryPath) throw new Error(`Unable to locate the built SSR entry in "${ssrOutDir}". Expected one of: ${candidatePaths.join(", ")}`);
288
+ return ssrEntryPath;
201
289
  }
202
- export function nitro(options, nitroOptions) {
203
- const workspaceRoot = options?.workspaceRoot ?? process.cwd();
204
- const sourceRoot = options?.sourceRoot ?? 'src';
205
- let isTest = process.env['NODE_ENV'] === 'test' || !!process.env['VITEST'];
206
- const baseURL = process.env['NITRO_APP_BASE_URL'] || '';
207
- const prefix = baseURL ? baseURL.substring(0, baseURL.length - 1) : '';
208
- const apiPrefix = `/${options?.apiPrefix || 'api'}`;
209
- const useAPIMiddleware = typeof options?.useAPIMiddleware !== 'undefined'
210
- ? options?.useAPIMiddleware
211
- : true;
212
- let isBuild = false;
213
- let isServe = false;
214
- let ssrBuild = false;
215
- let config;
216
- let nitroConfig;
217
- let environmentBuild = false;
218
- let hasAPIDir = false;
219
- const rollupExternalEntries = [];
220
- const routeSitemaps = {};
221
- const routeSourceFiles = {};
222
- let rootDir = workspaceRoot;
223
- return [
224
- (options?.ssr
225
- ? devServerPlugin({
226
- entryServer: options?.entryServer,
227
- index: options?.index,
228
- routeRules: nitroOptions?.routeRules,
229
- })
230
- : false),
231
- {
232
- name: '@analogjs/vite-plugin-nitro',
233
- async config(userConfig, { mode, command }) {
234
- isServe = command === 'serve';
235
- isBuild = command === 'build';
236
- ssrBuild = userConfig.build?.ssr === true;
237
- config = userConfig;
238
- isTest = isTest ? isTest : mode === 'test';
239
- rootDir = relative(workspaceRoot, config.root || '.') || '.';
240
- hasAPIDir = existsSync(resolve(workspaceRoot, rootDir, `${sourceRoot}/server/routes/${options?.apiPrefix || 'api'}`));
241
- const buildPreset = process.env['BUILD_PRESET'] ??
242
- nitroOptions?.preset ??
243
- (process.env['VERCEL'] ? 'vercel' : undefined);
244
- const pageHandlers = getPageHandlers({
245
- workspaceRoot,
246
- sourceRoot,
247
- rootDir,
248
- additionalPagesDirs: options?.additionalPagesDirs,
249
- hasAPIDir,
250
- });
251
- const resolvedClientOutputPath = resolveClientOutputPath(workspaceRoot, rootDir, config.build?.outDir, ssrBuild);
252
- rendererIndexEntry = normalizePath(resolve(resolvedClientOutputPath, 'index.html'));
253
- nitroConfig = {
254
- rootDir: normalizePath(rootDir),
255
- preset: buildPreset,
256
- compatibilityDate: '2025-11-19',
257
- logLevel: nitroOptions?.logLevel || 0,
258
- serverDir: normalizePath(`${sourceRoot}/server`),
259
- scanDirs: [
260
- normalizePath(`${rootDir}/${sourceRoot}/server`),
261
- ...(options?.additionalAPIDirs || []).map((dir) => normalizePath(`${workspaceRoot}${dir}`)),
262
- ],
263
- output: {
264
- dir: normalizePath(resolve(workspaceRoot, 'dist', rootDir, 'analog')),
265
- publicDir: normalizePath(resolve(workspaceRoot, 'dist', rootDir, 'analog/public')),
266
- },
267
- buildDir: normalizePath(resolve(workspaceRoot, 'dist', rootDir, '.nitro')),
268
- typescript: {
269
- generateTsConfig: false,
270
- },
271
- runtimeConfig: {
272
- apiPrefix: apiPrefix.substring(1),
273
- prefix,
274
- },
275
- // Analog provides its own renderer handler; prevent Nitro v3 from
276
- // auto-detecting index.html in rootDir and adding a conflicting one.
277
- renderer: false,
278
- imports: {
279
- autoImport: false,
280
- },
281
- hooks: {
282
- 'rollup:before': createRollupBeforeHook(rollupExternalEntries),
283
- },
284
- rollupConfig: {
285
- onwarn(warning) {
286
- if (warning.message.includes('empty chunk') &&
287
- warning.message.endsWith('.server')) {
288
- return;
289
- }
290
- },
291
- plugins: [pageEndpointsPlugin()],
292
- },
293
- handlers: [
294
- ...(hasAPIDir
295
- ? []
296
- : useAPIMiddleware
297
- ? [createNitroMiddlewareHandler('#ANALOG_API_MIDDLEWARE')]
298
- : []),
299
- ...pageHandlers,
300
- ],
301
- routeRules: hasAPIDir
302
- ? undefined
303
- : useAPIMiddleware
304
- ? undefined
305
- : {
306
- [`${prefix}${apiPrefix}/**`]: {
307
- proxy: { to: '/**' },
308
- },
309
- },
310
- virtual: {
311
- '#ANALOG_SSR_RENDERER': ssrRenderer(rendererIndexEntry),
312
- '#ANALOG_CLIENT_RENDERER': clientRenderer(rendererIndexEntry),
313
- ...(hasAPIDir ? {} : { '#ANALOG_API_MIDDLEWARE': apiMiddleware }),
314
- },
315
- };
316
- if (isVercelPreset(buildPreset)) {
317
- nitroConfig = withVercelOutputAPI(nitroConfig, workspaceRoot);
318
- }
319
- if (isCloudflarePreset(buildPreset)) {
320
- nitroConfig = withCloudflareOutput(nitroConfig);
321
- }
322
- if (isNetlifyPreset(buildPreset) &&
323
- rootDir === '.' &&
324
- !existsSync(resolve(workspaceRoot, 'netlify.toml'))) {
325
- nitroConfig = withNetlifyOutputAPI(nitroConfig, workspaceRoot);
326
- }
327
- if (isFirebaseAppHosting()) {
328
- nitroConfig = withAppHostingOutput(nitroConfig);
329
- }
330
- if (!ssrBuild && !isTest) {
331
- // store the client output path for the SSR build config
332
- clientOutputPath = resolvedClientOutputPath;
333
- }
334
- if (isBuild) {
335
- nitroConfig.publicAssets = [
336
- { dir: normalizePath(clientOutputPath), maxAge: 0 },
337
- ];
338
- // In Nitro v3, renderer.entry is resolved via resolveModulePath()
339
- // during options normalization, which requires a real filesystem path.
340
- // Virtual modules (prefixed with #) can't survive this resolution.
341
- // Instead, we add the renderer as a catch-all handler directly —
342
- // this is functionally equivalent to what Nitro does internally
343
- // (it converts renderer.entry into a { route: '/**', lazy: true }
344
- // handler), but avoids the filesystem resolution step.
345
- const rendererHandler = options?.ssr
346
- ? '#ANALOG_SSR_RENDERER'
347
- : '#ANALOG_CLIENT_RENDERER';
348
- nitroConfig.handlers = [
349
- ...(nitroConfig.handlers || []),
350
- {
351
- handler: rendererHandler,
352
- route: '/**',
353
- lazy: true,
354
- },
355
- ];
356
- if (isEmptyPrerenderRoutes(options)) {
357
- nitroConfig.prerender = {};
358
- nitroConfig.prerender.routes = ['/'];
359
- }
360
- if (options?.prerender) {
361
- nitroConfig.prerender = nitroConfig.prerender ?? {};
362
- nitroConfig.prerender.crawlLinks = options?.prerender?.discover;
363
- let routes = [];
364
- const prerenderRoutes = options?.prerender?.routes;
365
- if (isArrayWithElements(prerenderRoutes)) {
366
- routes = prerenderRoutes;
367
- }
368
- else if (typeof prerenderRoutes === 'function') {
369
- routes = await prerenderRoutes();
370
- }
371
- nitroConfig.prerender.routes = routes.reduce((prev, current) => {
372
- if (!current) {
373
- return prev;
374
- }
375
- if (typeof current === 'string') {
376
- prev.push(current);
377
- return prev;
378
- }
379
- if ('route' in current) {
380
- if (current.sitemap) {
381
- routeSitemaps[current.route] = current.sitemap;
382
- }
383
- if (current.outputSourceFile) {
384
- const sourcePath = resolve(workspaceRoot, rootDir, current.outputSourceFile);
385
- routeSourceFiles[current.route] = readFileSync(sourcePath, 'utf8');
386
- }
387
- prev.push(current.route);
388
- // Add the server-side data fetching endpoint URL
389
- if ('staticData' in current) {
390
- prev.push(`${apiPrefix}/_analog/pages/${current.route}`);
391
- }
392
- return prev;
393
- }
394
- const affectedFiles = getMatchingContentFilesWithFrontMatter(workspaceRoot, rootDir, current.contentDir);
395
- affectedFiles.forEach((f) => {
396
- const result = current.transform(f);
397
- if (result) {
398
- if (current.sitemap) {
399
- routeSitemaps[result] =
400
- current.sitemap && typeof current.sitemap === 'function'
401
- ? current.sitemap?.(f)
402
- : current.sitemap;
403
- }
404
- if (current.outputSourceFile) {
405
- const sourceContent = current.outputSourceFile(f);
406
- if (sourceContent) {
407
- routeSourceFiles[result] = sourceContent;
408
- }
409
- }
410
- prev.push(result);
411
- // Add the server-side data fetching endpoint URL
412
- if ('staticData' in current) {
413
- prev.push(`${apiPrefix}/_analog/pages/${result}`);
414
- }
415
- }
416
- });
417
- return prev;
418
- }, []);
419
- }
420
- // ── SSR / prerender Nitro config ─────────────────────────────
421
- //
422
- // This block configures Nitro for builds that rebundle the SSR
423
- // entry (main.server.{js,mjs}). That happens in two cases:
424
- //
425
- // 1. Full SSR apps — `options.ssr === true`
426
- // 2. Prerender-only — no runtime SSR, but the prerender build
427
- // still imports the SSR entry to render static pages.
428
- //
429
- // The original gate was `if (ssrBuild)`, which checks the Vite
430
- // top-level `build.ssr` flag. That worked with the legacy
431
- // single-pass build but breaks with two newer code paths:
432
- //
433
- // a. **Vite Environment API (Vite 6+)** — SSR config lives in
434
- // `environments.ssr.build.ssr`, not `build.ssr`, so
435
- // `ssrBuild` is always `false`.
436
- // b. **Prerender-only apps** (e.g. blog-app) `options.ssr`
437
- // is `false`, but prerender routes exist and the prerender
438
- // build still processes the SSR entry.
439
- //
440
- // Without this block:
441
- // - `rxjs` is never externalised RESOLVE_ERROR in the
442
- // Nitro prerender build (especially on Windows CI).
443
- // - `moduleSideEffects` for zone.js is never set → zone.js
444
- // side-effects may be tree-shaken.
445
- // - The handlers list is not reassembled with page endpoints
446
- // + the renderer catch-all.
447
- //
448
- // The widened condition covers all three code paths:
449
- // - `ssrBuild` → legacy closeBundle
450
- // - `options?.ssr` → Environment API SSR
451
- // - `nitroConfig.prerender?.routes?.length` → prerender-only
452
- if (ssrBuild ||
453
- options?.ssr ||
454
- nitroConfig.prerender?.routes?.length) {
455
- if (process.platform === 'win32') {
456
- nitroConfig.noExternals = appendNoExternals(nitroConfig.noExternals, 'std-env');
457
- }
458
- rollupExternalEntries.push('rxjs', 'node-fetch-native/dist/polyfill');
459
- nitroConfig = {
460
- ...nitroConfig,
461
- moduleSideEffects: ['zone.js/node', 'zone.js/fesm2015/zone-node'],
462
- handlers: [
463
- ...(hasAPIDir
464
- ? []
465
- : useAPIMiddleware
466
- ? [createNitroMiddlewareHandler('#ANALOG_API_MIDDLEWARE')]
467
- : []),
468
- ...pageHandlers,
469
- // Preserve the renderer catch-all handler added above
470
- {
471
- handler: rendererHandler,
472
- route: '/**',
473
- lazy: true,
474
- },
475
- ],
476
- };
477
- }
478
- }
479
- nitroConfig = mergeConfig(nitroConfig, nitroOptions);
480
- return {
481
- environments: {
482
- client: {
483
- build: {
484
- outDir: config?.build?.outDir ||
485
- resolve(workspaceRoot, 'dist', rootDir, 'client'),
486
- },
487
- },
488
- ssr: {
489
- build: {
490
- ssr: true,
491
- [vite.rolldownVersion ? 'rolldownOptions' : 'rollupOptions']: {
492
- input: options?.entryServer ||
493
- resolve(workspaceRoot, rootDir, `${sourceRoot}/main.server.ts`),
494
- },
495
- outDir: options?.ssrBuildDir ||
496
- resolve(workspaceRoot, 'dist', rootDir, 'ssr'),
497
- },
498
- },
499
- },
500
- builder: {
501
- sharedPlugins: true,
502
- buildApp: async (builder) => {
503
- environmentBuild = true;
504
- const builds = [builder.build(builder.environments['client'])];
505
- if (options?.ssr || nitroConfig.prerender?.routes?.length) {
506
- builds.push(builder.build(builder.environments['ssr']));
507
- }
508
- await Promise.all(builds);
509
- const ssrOutDir = options?.ssrBuildDir ||
510
- resolve(workspaceRoot, 'dist', rootDir, `ssr`);
511
- if (options?.ssr || nitroConfig.prerender?.routes?.length) {
512
- const ssrEntryPath = resolveBuiltSsrEntryPath(ssrOutDir);
513
- const ssrEntry = toNitroSsrEntrypointSpecifier(ssrEntryPath);
514
- nitroConfig.alias = {
515
- ...nitroConfig.alias,
516
- '#analog/ssr': ssrEntry,
517
- };
518
- }
519
- await buildServer(options, nitroConfig, routeSourceFiles);
520
- if (nitroConfig.prerender?.routes?.length &&
521
- options?.prerender?.sitemap) {
522
- const publicDir = nitroConfig.output?.publicDir;
523
- if (!publicDir) {
524
- throw new Error('Nitro public output directory is required to build the sitemap.');
525
- }
526
- console.log('Building Sitemap...');
527
- // sitemap needs to be built after all directories are built
528
- await buildSitemap(config, options.prerender.sitemap, nitroConfig.prerender.routes, publicDir, routeSitemaps);
529
- }
530
- console.log(`\n\nThe '@analogjs/platform' server has been successfully built.`);
531
- },
532
- },
533
- };
534
- },
535
- async configureServer(viteServer) {
536
- if (isServe && !isTest) {
537
- const nitro = await createNitro({
538
- dev: true,
539
- // Nitro's Vite builder now rejects `build()` in dev mode, but Analog's
540
- // dev integration still relies on the builder-driven reload hooks.
541
- // Force the server worker onto Rollup for this dev-only path.
542
- builder: 'rollup',
543
- ...nitroConfig,
544
- });
545
- const server = createDevServer(nitro);
546
- await build(nitro);
547
- const apiHandler = async (req, res) => {
548
- // Nitro v3's dev server is fetch-first, so adapt Vite's Node
549
- // request once and let Nitro respond with a standard Web Response.
550
- const response = await server.fetch(toWebRequest(req));
551
- await writeWebResponseToNode(res, response);
552
- };
553
- if (hasAPIDir) {
554
- viteServer.middlewares.use((req, res, next) => {
555
- if (req.url?.startsWith(`${prefix}${apiPrefix}`)) {
556
- void apiHandler(req, res).catch((error) => next(error));
557
- return;
558
- }
559
- next();
560
- });
561
- }
562
- else {
563
- viteServer.middlewares.use(apiPrefix, (req, res, next) => {
564
- void apiHandler(req, res).catch((error) => next(error));
565
- });
566
- }
567
- viteServer.httpServer?.once('listening', () => {
568
- process.env['ANALOG_HOST'] = !viteServer.config.server.host
569
- ? 'localhost'
570
- : viteServer.config.server.host;
571
- process.env['ANALOG_PORT'] = `${viteServer.config.server.port}`;
572
- });
573
- // handle upgrades if websockets are enabled
574
- if (nitroOptions?.experimental?.websocket) {
575
- viteServer.httpServer?.on('upgrade', server.upgrade);
576
- }
577
- console.log(`\n\nThe server endpoints are accessible under the "${prefix}${apiPrefix}" path.`);
578
- }
579
- },
580
- async closeBundle() {
581
- // Skip when build is triggered by the Environment API
582
- if (environmentBuild) {
583
- return;
584
- }
585
- if (ssrBuild) {
586
- return;
587
- }
588
- if (isBuild) {
589
- if (options?.ssr) {
590
- console.log('Building SSR application...');
591
- await buildSSRApp(config, options);
592
- }
593
- if (nitroConfig.prerender?.routes?.length &&
594
- options?.prerender?.sitemap) {
595
- console.log('Building Sitemap...');
596
- // sitemap needs to be built after all directories are built
597
- await buildSitemap(config, options.prerender.sitemap, nitroConfig.prerender.routes, clientOutputPath, routeSitemaps);
598
- }
599
- const closeBundleSsrOutDir = options?.ssrBuildDir ||
600
- resolve(workspaceRoot, 'dist', rootDir, `ssr`);
601
- if (options?.ssr || nitroConfig.prerender?.routes?.length) {
602
- const ssrEntryPath = resolveBuiltSsrEntryPath(closeBundleSsrOutDir);
603
- const ssrEntry = toNitroSsrEntrypointSpecifier(ssrEntryPath);
604
- nitroConfig.alias = {
605
- ...nitroConfig.alias,
606
- '#analog/ssr': ssrEntry,
607
- };
608
- }
609
- await buildServer(options, nitroConfig, routeSourceFiles);
610
- console.log(`\n\nThe '@analogjs/platform' server has been successfully built.`);
611
- }
612
- },
613
- },
614
- {
615
- name: '@analogjs/vite-plugin-nitro-api-prefix',
616
- config() {
617
- return {
618
- define: {
619
- ANALOG_API_PREFIX: `"${baseURL.substring(1)}${apiPrefix.substring(1)}"`,
620
- },
621
- };
622
- },
623
- },
624
- ];
290
+ function nitro(options, nitroOptions) {
291
+ const workspaceRoot = options?.workspaceRoot ?? process.cwd();
292
+ const sourceRoot = options?.sourceRoot ?? "src";
293
+ let isTest = process.env.NODE_ENV === "test" || !!process.env["VITEST"];
294
+ const baseURL = process.env["NITRO_APP_BASE_URL"] || "";
295
+ const prefix = baseURL ? baseURL.substring(0, baseURL.length - 1) : "";
296
+ const apiPrefix = `/${options?.apiPrefix || "api"}`;
297
+ const useAPIMiddleware = typeof options?.useAPIMiddleware !== "undefined" ? options?.useAPIMiddleware : true;
298
+ const viteRolldownOutput = options?.vite?.build?.rolldownOptions?.output;
299
+ const viteRolldownOutputConfig = viteRolldownOutput && !Array.isArray(viteRolldownOutput) ? viteRolldownOutput : void 0;
300
+ const codeSplitting = viteRolldownOutputConfig?.codeSplitting;
301
+ let isBuild = false;
302
+ let isServe = false;
303
+ let ssrBuild = false;
304
+ let config;
305
+ let nitroConfig;
306
+ let environmentBuild = false;
307
+ let hasAPIDir = false;
308
+ let clientOutputPath = "";
309
+ let clientIndexHtml;
310
+ let legacyClientSubBuild = false;
311
+ const rollupExternalEntries = [];
312
+ const sitemapRoutes = [];
313
+ const routeSitemaps = {};
314
+ const routeSourceFiles = {};
315
+ let rootDir = workspaceRoot;
316
+ return [
317
+ options?.ssr ? devServerPlugin({
318
+ entryServer: options?.entryServer,
319
+ index: options?.index,
320
+ routeRules: nitroOptions?.routeRules
321
+ }) : false,
322
+ {
323
+ name: "@analogjs/vite-plugin-nitro",
324
+ async config(userConfig, { mode, command }) {
325
+ isServe = command === "serve";
326
+ isBuild = command === "build";
327
+ ssrBuild = userConfig.build?.ssr === true;
328
+ config = userConfig;
329
+ isTest = isTest ? isTest : mode === "test";
330
+ rollupExternalEntries.length = 0;
331
+ clientIndexHtml = void 0;
332
+ sitemapRoutes.length = 0;
333
+ for (const key of Object.keys(routeSitemaps)) delete routeSitemaps[key];
334
+ for (const key of Object.keys(routeSourceFiles)) delete routeSourceFiles[key];
335
+ const resolvedConfigRoot = config.root ? resolve(workspaceRoot, config.root) : workspaceRoot;
336
+ rootDir = relative(workspaceRoot, resolvedConfigRoot) || ".";
337
+ hasAPIDir = existsSync(resolve(workspaceRoot, rootDir, `${sourceRoot}/server/routes/${options?.apiPrefix || "api"}`));
338
+ const buildPreset = process.env["BUILD_PRESET"] ?? nitroOptions?.preset ?? (process.env["VERCEL"] ? "vercel" : void 0);
339
+ const pageHandlers = getPageHandlers({
340
+ workspaceRoot,
341
+ sourceRoot,
342
+ rootDir,
343
+ additionalPagesDirs: options?.additionalPagesDirs,
344
+ hasAPIDir
345
+ });
346
+ const resolvedClientOutputPath = resolveClientOutputPath(clientOutputPath, workspaceRoot, rootDir, config.build?.outDir);
347
+ debugLog("nitro config resolved client output path", () => ({
348
+ platform: process.platform,
349
+ workspaceRoot,
350
+ configRoot: config.root,
351
+ resolvedConfigRoot,
352
+ rootDir,
353
+ buildOutDir: config.build?.outDir,
354
+ clientOutputPath,
355
+ resolvedClientOutputPath,
356
+ hasEnvironmentConfig: !!config.environments,
357
+ clientEnvironmentOutDir: config.environments?.["client"] && typeof config.environments["client"] === "object" && "build" in config.environments["client"] ? config.environments["client"].build?.outDir : void 0
358
+ }));
359
+ nitroConfig = {
360
+ rootDir: normalizePath(rootDir),
361
+ preset: buildPreset,
362
+ compatibilityDate: "2025-11-19",
363
+ logLevel: nitroOptions?.logLevel || 0,
364
+ serverDir: normalizePath(`${sourceRoot}/server`),
365
+ scanDirs: [normalizePath(`${rootDir}/${sourceRoot}/server`), ...(options?.additionalAPIDirs || []).map((dir) => normalizePath(`${workspaceRoot}${dir}`))],
366
+ output: {
367
+ dir: normalizePath(resolve(workspaceRoot, "dist", rootDir, "analog")),
368
+ publicDir: normalizePath(resolve(workspaceRoot, "dist", rootDir, "analog/public"))
369
+ },
370
+ buildDir: normalizePath(resolve(workspaceRoot, "dist", rootDir, ".nitro")),
371
+ typescript: { generateTsConfig: false },
372
+ runtimeConfig: {
373
+ apiPrefix: apiPrefix.substring(1),
374
+ prefix
375
+ },
376
+ renderer: false,
377
+ imports: { autoImport: false },
378
+ hooks: { "rollup:before": createRollupBeforeHook(rollupExternalEntries) },
379
+ rollupConfig: {
380
+ onwarn(warning) {
381
+ if (warning.message.includes("empty chunk") && warning.message.endsWith(".server")) return;
382
+ },
383
+ plugins: [pageEndpointsPlugin()]
384
+ },
385
+ handlers: [...hasAPIDir ? [] : useAPIMiddleware ? [createNitroMiddlewareHandler("#ANALOG_API_MIDDLEWARE")] : [], ...pageHandlers],
386
+ routeRules: hasAPIDir ? void 0 : useAPIMiddleware ? void 0 : { [`${prefix}${apiPrefix}/**`]: { proxy: { to: "/**" } } },
387
+ virtual: {
388
+ "#ANALOG_SSR_RENDERER": ssrRenderer(),
389
+ "#ANALOG_CLIENT_RENDERER": clientRenderer(),
390
+ ...hasAPIDir ? {} : { "#ANALOG_API_MIDDLEWARE": apiMiddleware }
391
+ }
392
+ };
393
+ if (isVercelPreset(buildPreset)) nitroConfig = withVercelOutputAPI(nitroConfig, workspaceRoot);
394
+ if (isCloudflarePreset(buildPreset)) nitroConfig = withCloudflareOutput(nitroConfig);
395
+ if (isNetlifyPreset(buildPreset) && rootDir === "." && !existsSync(resolve(workspaceRoot, "netlify.toml"))) nitroConfig = withNetlifyOutputAPI(nitroConfig, workspaceRoot);
396
+ if (isFirebaseAppHosting()) nitroConfig = withAppHostingOutput(nitroConfig);
397
+ if (!ssrBuild && !isTest) {
398
+ clientOutputPath = resolvedClientOutputPath;
399
+ debugLog("nitro config cached client output path for later SSR/Nitro build", () => ({
400
+ ssrBuild,
401
+ isTest,
402
+ clientOutputPath
403
+ }));
404
+ }
405
+ nitroConfig.alias = {};
406
+ if (isBuild) {
407
+ nitroConfig.publicAssets = [{
408
+ dir: normalizePath(resolvedClientOutputPath),
409
+ maxAge: 0
410
+ }];
411
+ const rendererHandler = options?.ssr ? "#ANALOG_SSR_RENDERER" : "#ANALOG_CLIENT_RENDERER";
412
+ nitroConfig.handlers = [...nitroConfig.handlers || [], {
413
+ handler: rendererHandler,
414
+ route: "/**",
415
+ lazy: true
416
+ }];
417
+ if (isEmptyPrerenderRoutes(options)) {
418
+ nitroConfig.prerender = {};
419
+ nitroConfig.prerender.routes = ["/"];
420
+ }
421
+ if (options?.prerender) {
422
+ nitroConfig.prerender = nitroConfig.prerender ?? {};
423
+ nitroConfig.prerender.crawlLinks = options?.prerender?.discover;
424
+ let routes = [];
425
+ const prerenderRoutes = options?.prerender?.routes;
426
+ const hasExplicitPrerenderRoutes = typeof prerenderRoutes === "function" || Array.isArray(prerenderRoutes);
427
+ if (isArrayWithElements(prerenderRoutes)) routes = prerenderRoutes;
428
+ else if (typeof prerenderRoutes === "function") routes = await prerenderRoutes();
429
+ const resolvedPrerenderRoutes = routes.reduce((prev, current) => {
430
+ if (!current) return prev;
431
+ if (typeof current === "string") {
432
+ prev.push(current);
433
+ sitemapRoutes.push(current);
434
+ return prev;
435
+ }
436
+ if ("route" in current) {
437
+ if (current.sitemap) routeSitemaps[current.route] = current.sitemap;
438
+ if (current.outputSourceFile) {
439
+ const sourcePath = resolve(workspaceRoot, rootDir, current.outputSourceFile);
440
+ routeSourceFiles[current.route] = readFileSync(sourcePath, "utf8");
441
+ }
442
+ prev.push(current.route);
443
+ sitemapRoutes.push(current.route);
444
+ if ("staticData" in current) prev.push(`${apiPrefix}/_analog/pages/${current.route}`);
445
+ return prev;
446
+ }
447
+ getMatchingContentFilesWithFrontMatter(workspaceRoot, rootDir, current.contentDir).forEach((f) => {
448
+ const result = current.transform(f);
449
+ if (result) {
450
+ if (current.sitemap) routeSitemaps[result] = current.sitemap && typeof current.sitemap === "function" ? current.sitemap?.(f) : current.sitemap;
451
+ if (current.outputSourceFile) {
452
+ const sourceContent = current.outputSourceFile(f);
453
+ if (sourceContent) routeSourceFiles[result] = sourceContent;
454
+ }
455
+ prev.push(result);
456
+ sitemapRoutes.push(result);
457
+ if ("staticData" in current) prev.push(`${apiPrefix}/_analog/pages/${result}`);
458
+ }
459
+ });
460
+ return prev;
461
+ }, []);
462
+ nitroConfig.prerender.routes = hasExplicitPrerenderRoutes || resolvedPrerenderRoutes.length ? resolvedPrerenderRoutes : nitroConfig.prerender.routes ?? [];
463
+ }
464
+ if (ssrBuild || options?.ssr || nitroConfig.prerender?.routes?.length) {
465
+ if (process.platform === "win32") nitroConfig.noExternals = appendNoExternals(nitroConfig.noExternals, "std-env");
466
+ rollupExternalEntries.push("rxjs", "node-fetch-native/dist/polyfill", "sharp");
467
+ nitroConfig = {
468
+ ...nitroConfig,
469
+ moduleSideEffects: ["zone.js/node", "zone.js/fesm2015/zone-node"],
470
+ handlers: [
471
+ ...hasAPIDir ? [] : useAPIMiddleware ? [createNitroMiddlewareHandler("#ANALOG_API_MIDDLEWARE")] : [],
472
+ ...pageHandlers,
473
+ {
474
+ handler: rendererHandler,
475
+ route: "/**",
476
+ lazy: true
477
+ }
478
+ ]
479
+ };
480
+ }
481
+ }
482
+ nitroConfig = mergeConfig(nitroConfig, nitroOptions);
483
+ if (environmentBuild || ssrBuild || isServe) return {};
484
+ return {
485
+ environments: {
486
+ client: { build: {
487
+ outDir: config?.build?.outDir || resolve(workspaceRoot, "dist", rootDir, "client"),
488
+ emptyOutDir: true,
489
+ ...isRolldown() && codeSplitting !== void 0 ? { rolldownOptions: { output: {
490
+ ...viteRolldownOutputConfig,
491
+ codeSplitting
492
+ } } } : {}
493
+ } },
494
+ ssr: { build: {
495
+ ssr: true,
496
+ [getBundleOptionsKey()]: { input: options?.entryServer || resolve(workspaceRoot, rootDir, `${sourceRoot}/main.server.ts`) },
497
+ outDir: options?.ssrBuildDir || resolve(workspaceRoot, "dist", rootDir, "ssr"),
498
+ emptyOutDir: false
499
+ } }
500
+ },
501
+ builder: {
502
+ sharedPlugins: true,
503
+ buildApp: async (builder) => {
504
+ environmentBuild = true;
505
+ debugLog("builder.buildApp starting", () => ({
506
+ platform: process.platform,
507
+ workspaceRoot,
508
+ rootDir,
509
+ cachedClientOutputPath: clientOutputPath,
510
+ configuredBuildOutDir: config.build?.outDir,
511
+ clientEnvironmentOutDir: getEnvironmentBuildOutDir(builder.environments["client"]),
512
+ ssrEnvironmentOutDir: getEnvironmentBuildOutDir(builder.environments["ssr"])
513
+ }));
514
+ await builder.build(builder.environments["client"]);
515
+ const postClientBuildOutputPath = resolveBuiltClientOutputPath(clientOutputPath, workspaceRoot, rootDir, config.build?.outDir, builder.environments["client"]);
516
+ registerIndexHtmlVirtual(nitroConfig, postClientBuildOutputPath, clientIndexHtml);
517
+ debugLog("builder.buildApp completed client build", () => ({
518
+ postClientBuildOutputPath,
519
+ postClientBuildOutputInfo: getPathDebugInfo(postClientBuildOutputPath),
520
+ postClientBuildIndexHtmlPath: resolve(postClientBuildOutputPath, "index.html"),
521
+ postClientBuildIndexHtmlExists: existsSync(resolve(postClientBuildOutputPath, "index.html"))
522
+ }));
523
+ if (options?.ssr || nitroConfig.prerender?.routes?.length) {
524
+ debugLog("builder.buildApp starting SSR build", () => ({
525
+ ssrEnabled: options?.ssr,
526
+ prerenderRoutes: nitroConfig.prerender?.routes
527
+ }));
528
+ await builder.build(builder.environments["ssr"]);
529
+ debugLog("builder.buildApp completed SSR build", () => ({ ssrOutputPath: options?.ssrBuildDir || resolve(workspaceRoot, "dist", rootDir, "ssr") }));
530
+ }
531
+ applySsrEntryAlias(nitroConfig, options, workspaceRoot, rootDir);
532
+ const resolvedClientOutputPath = resolveBuiltClientOutputPath(clientOutputPath, workspaceRoot, rootDir, config.build?.outDir, builder.environments["client"]);
533
+ nitroConfig.publicAssets = [{
534
+ dir: normalizePath(resolvedClientOutputPath),
535
+ maxAge: 0
536
+ }];
537
+ debugLog("builder.buildApp resolved final client output path before Nitro build", () => ({
538
+ resolvedClientOutputPath,
539
+ resolvedClientOutputInfo: getPathDebugInfo(resolvedClientOutputPath),
540
+ nitroPublicAssets: nitroConfig.publicAssets
541
+ }));
542
+ await buildServer(options, nitroConfig, routeSourceFiles);
543
+ if (nitroConfig.prerender?.routes?.length && options?.prerender?.sitemap) {
544
+ console.log("Building Sitemap...");
545
+ await buildSitemap(config, options.prerender.sitemap, sitemapRoutes.length ? sitemapRoutes : nitroConfig.prerender.routes, getNitroPublicOutputDir(nitroConfig), routeSitemaps, { apiPrefix: options?.apiPrefix || "api" });
546
+ }
547
+ console.log(`\n\nThe '@analogjs/platform' server has been successfully built.`);
548
+ }
549
+ }
550
+ };
551
+ },
552
+ generateBundle(_options, bundle) {
553
+ if (!isBuild || ssrBuild) return;
554
+ clientIndexHtml = captureClientIndexHtmlFromBundle(bundle, "generateBundle") ?? clientIndexHtml;
555
+ },
556
+ writeBundle(_options, bundle) {
557
+ if (!isBuild || ssrBuild) return;
558
+ clientIndexHtml = captureClientIndexHtmlFromBundle(bundle, "writeBundle") ?? clientIndexHtml;
559
+ },
560
+ async configureServer(viteServer) {
561
+ if (isServe && !isTest) {
562
+ const nitro = await createNitro({
563
+ dev: true,
564
+ builder: "rollup",
565
+ ...nitroConfig
566
+ });
567
+ const server = createDevServer(nitro);
568
+ await build(nitro);
569
+ const nitroSourceRoots = [normalizePath(resolve(workspaceRoot, rootDir, `${sourceRoot}/server`)), ...(options?.additionalAPIDirs || []).map((dir) => normalizePath(`${workspaceRoot}${dir}`))];
570
+ const isNitroSourceFile = (path) => {
571
+ const normalizedPath = normalizePath(path);
572
+ return nitroSourceRoots.some((root) => normalizedPath === root || normalizedPath.startsWith(`${root}/`));
573
+ };
574
+ let nitroRebuildPromise;
575
+ let nitroRebuildPending = false;
576
+ const rebuildNitroServer = () => {
577
+ if (nitroRebuildPromise) {
578
+ nitroRebuildPending = true;
579
+ return nitroRebuildPromise;
580
+ }
581
+ nitroRebuildPromise = (async () => {
582
+ do {
583
+ nitroRebuildPending = false;
584
+ await build(nitro);
585
+ } while (nitroRebuildPending);
586
+ viteServer.ws.send({ type: "full-reload" });
587
+ })().catch((error) => {
588
+ viteServer.config.logger.error(`[analog] Failed to rebuild Nitro dev server.\n${error instanceof Error ? error.stack || error.message : String(error)}`);
589
+ }).finally(() => {
590
+ nitroRebuildPromise = void 0;
591
+ });
592
+ return nitroRebuildPromise;
593
+ };
594
+ const onNitroSourceChange = (path) => {
595
+ if (!isNitroSourceFile(path)) return;
596
+ rebuildNitroServer();
597
+ };
598
+ viteServer.watcher.on("add", onNitroSourceChange);
599
+ viteServer.watcher.on("change", onNitroSourceChange);
600
+ viteServer.watcher.on("unlink", onNitroSourceChange);
601
+ const apiHandler = async (req, res) => {
602
+ await writeWebResponseToNode(res, await server.fetch(toWebRequest(req)));
603
+ };
604
+ if (hasAPIDir) viteServer.middlewares.use((req, res, next) => {
605
+ if (req.url?.startsWith(`${prefix}${apiPrefix}`)) {
606
+ apiHandler(req, res).catch((error) => next(error));
607
+ return;
608
+ }
609
+ next();
610
+ });
611
+ else viteServer.middlewares.use(apiPrefix, (req, res, next) => {
612
+ apiHandler(req, res).catch((error) => next(error));
613
+ });
614
+ viteServer.httpServer?.once("listening", () => {
615
+ process.env["ANALOG_HOST"] = !viteServer.config.server.host ? "localhost" : viteServer.config.server.host;
616
+ process.env["ANALOG_PORT"] = `${viteServer.config.server.port}`;
617
+ });
618
+ if (nitroOptions?.experimental?.websocket) viteServer.httpServer?.on("upgrade", server.upgrade);
619
+ console.log(`\n\nThe server endpoints are accessible under the "${prefix}${apiPrefix}" path.`);
620
+ }
621
+ },
622
+ async closeBundle() {
623
+ if (legacyClientSubBuild) return;
624
+ if (environmentBuild) return;
625
+ if (ssrBuild) return;
626
+ if (isBuild) {
627
+ const resolvedClientOutputPath = resolveClientOutputPath(clientOutputPath, workspaceRoot, rootDir, config.build?.outDir);
628
+ debugLog("closeBundle resolved client output path before legacy SSR build", () => ({
629
+ platform: process.platform,
630
+ workspaceRoot,
631
+ rootDir,
632
+ cachedClientOutputPath: clientOutputPath,
633
+ configuredBuildOutDir: config.build?.outDir,
634
+ resolvedClientOutputPath,
635
+ resolvedClientOutputInfo: getPathDebugInfo(resolvedClientOutputPath)
636
+ }));
637
+ const indexHtmlPath = resolve(resolvedClientOutputPath, "index.html");
638
+ if (!existsSync(indexHtmlPath) && typeof clientIndexHtml !== "string") {
639
+ debugLog("closeBundle rebuilding missing client output before SSR/Nitro", () => ({
640
+ platform: process.platform,
641
+ workspaceRoot,
642
+ rootDir,
643
+ configuredBuildOutDir: config.build?.outDir,
644
+ resolvedClientOutputPath,
645
+ indexHtmlPath
646
+ }));
647
+ legacyClientSubBuild = true;
648
+ try {
649
+ await buildClientApp(config, options);
650
+ } finally {
651
+ legacyClientSubBuild = false;
652
+ }
653
+ }
654
+ registerIndexHtmlVirtual(nitroConfig, resolvedClientOutputPath, clientIndexHtml);
655
+ if (options?.ssr) {
656
+ console.log("Building SSR application...");
657
+ await buildSSRApp(config, options);
658
+ debugLog("closeBundle completed standalone SSR build", () => ({
659
+ ssrBuildDir: options?.ssrBuildDir || resolve(workspaceRoot, "dist", rootDir, "ssr"),
660
+ clientOutputPathInfo: clientOutputPath ? getPathDebugInfo(clientOutputPath) : null
661
+ }));
662
+ }
663
+ applySsrEntryAlias(nitroConfig, options, workspaceRoot, rootDir);
664
+ debugLog("closeBundle resolved client output path before Nitro build", () => ({
665
+ platform: process.platform,
666
+ workspaceRoot,
667
+ rootDir,
668
+ cachedClientOutputPath: clientOutputPath,
669
+ configuredBuildOutDir: config.build?.outDir,
670
+ resolvedClientOutputPath,
671
+ resolvedClientOutputInfo: getPathDebugInfo(resolvedClientOutputPath)
672
+ }));
673
+ registerIndexHtmlVirtual(nitroConfig, resolvedClientOutputPath, clientIndexHtml);
674
+ await buildServer(options, nitroConfig, routeSourceFiles);
675
+ if (nitroConfig.prerender?.routes?.length && options?.prerender?.sitemap) {
676
+ console.log("Building Sitemap...");
677
+ await buildSitemap(config, options.prerender.sitemap, sitemapRoutes.length ? sitemapRoutes : nitroConfig.prerender.routes, getNitroPublicOutputDir(nitroConfig), routeSitemaps, { apiPrefix: options?.apiPrefix || "api" });
678
+ }
679
+ console.log(`\n\nThe '@analogjs/platform' server has been successfully built.`);
680
+ }
681
+ }
682
+ },
683
+ {
684
+ name: "@analogjs/vite-plugin-nitro-api-prefix",
685
+ config() {
686
+ return { define: { ANALOG_API_PREFIX: `"${baseURL.substring(1)}${apiPrefix.substring(1)}"` } };
687
+ }
688
+ }
689
+ ];
625
690
  }
626
691
  function isEmptyPrerenderRoutes(options) {
627
- if (!options || isArrayWithElements(options?.prerender?.routes)) {
628
- return false;
629
- }
630
- return !options.prerender?.routes;
692
+ if (!options || isArrayWithElements(options?.prerender?.routes)) return false;
693
+ return !options.prerender?.routes;
631
694
  }
632
695
  function isArrayWithElements(arr) {
633
- return !!(Array.isArray(arr) && arr.length);
696
+ return !!(Array.isArray(arr) && arr.length);
634
697
  }
635
- const isVercelPreset = (buildPreset) => process.env['VERCEL'] ||
636
- (buildPreset && buildPreset.toLowerCase().includes('vercel'));
637
- // Nitro v3 consolidates the old `vercel-edge` preset into `vercel` with
638
- // fluid compute enabled by default, so a single preset covers both
639
- // serverless and edge deployments.
640
- const withVercelOutputAPI = (nitroConfig, workspaceRoot) => ({
641
- ...nitroConfig,
642
- preset: nitroConfig?.preset ?? 'vercel',
643
- vercel: {
644
- ...nitroConfig?.vercel,
645
- entryFormat: nitroConfig?.vercel?.entryFormat ?? 'node',
646
- functions: {
647
- runtime: nitroConfig?.vercel?.functions?.runtime ?? 'nodejs24.x',
648
- ...nitroConfig?.vercel?.functions,
649
- },
650
- },
651
- output: {
652
- ...nitroConfig?.output,
653
- dir: normalizePath(resolve(workspaceRoot, '.vercel', 'output')),
654
- publicDir: normalizePath(resolve(workspaceRoot, '.vercel', 'output/static')),
655
- },
698
+ var withVercelOutputAPI = (nitroConfig, workspaceRoot) => ({
699
+ ...nitroConfig,
700
+ preset: nitroConfig?.preset ?? "vercel",
701
+ vercel: {
702
+ ...nitroConfig?.vercel,
703
+ entryFormat: nitroConfig?.vercel?.entryFormat ?? "node",
704
+ functions: {
705
+ runtime: nitroConfig?.vercel?.functions?.runtime ?? "nodejs24.x",
706
+ ...nitroConfig?.vercel?.functions
707
+ }
708
+ },
709
+ output: {
710
+ ...nitroConfig?.output,
711
+ dir: normalizePath(resolve(workspaceRoot, ".vercel", "output")),
712
+ publicDir: normalizePath(resolve(workspaceRoot, ".vercel", "output/static"))
713
+ }
656
714
  });
657
- // Nitro v3 uses underscore-separated preset names (e.g. `cloudflare_pages`),
658
- // but we accept both hyphen and underscore forms for backwards compatibility.
659
- const isCloudflarePreset = (buildPreset) => process.env['CF_PAGES'] ||
660
- (buildPreset &&
661
- (buildPreset.toLowerCase().includes('cloudflare-pages') ||
662
- buildPreset.toLowerCase().includes('cloudflare_pages')));
663
- const withCloudflareOutput = (nitroConfig) => ({
664
- ...nitroConfig,
665
- output: {
666
- ...nitroConfig?.output,
667
- serverDir: '{{ output.publicDir }}/_worker.js',
668
- },
715
+ var isCloudflarePreset = (buildPreset) => process.env["CF_PAGES"] || buildPreset && (buildPreset.toLowerCase().includes("cloudflare-pages") || buildPreset.toLowerCase().includes("cloudflare_pages"));
716
+ var withCloudflareOutput = (nitroConfig) => ({
717
+ ...nitroConfig,
718
+ output: {
719
+ ...nitroConfig?.output,
720
+ serverDir: "{{ output.publicDir }}/_worker.js"
721
+ }
669
722
  });
670
- const isFirebaseAppHosting = () => !!process.env['NG_BUILD_LOGS_JSON'];
671
- const withAppHostingOutput = (nitroConfig) => {
672
- let hasOutput = false;
673
- return {
674
- ...nitroConfig,
675
- serveStatic: true,
676
- rollupConfig: {
677
- ...nitroConfig.rollupConfig,
678
- output: {
679
- ...nitroConfig.rollupConfig?.output,
680
- entryFileNames: 'server.mjs',
681
- },
682
- },
683
- hooks: {
684
- ...nitroConfig.hooks,
685
- compiled: () => {
686
- if (!hasOutput) {
687
- const buildOutput = {
688
- errors: [],
689
- warnings: [],
690
- outputPaths: {
691
- root: pathToFileURL(`${nitroConfig.output?.dir}`),
692
- browser: pathToFileURL(`${nitroConfig.output?.publicDir}`),
693
- server: pathToFileURL(`${nitroConfig.output?.dir}/server`),
694
- },
695
- };
696
- // Log the build output for Firebase App Hosting to pick up
697
- console.log(JSON.stringify(buildOutput, null, 2));
698
- hasOutput = true;
699
- }
700
- },
701
- },
702
- };
723
+ var isFirebaseAppHosting = () => !!process.env["NG_BUILD_LOGS_JSON"];
724
+ var withAppHostingOutput = (nitroConfig) => {
725
+ let hasOutput = false;
726
+ return {
727
+ ...nitroConfig,
728
+ serveStatic: true,
729
+ rollupConfig: {
730
+ ...nitroConfig.rollupConfig,
731
+ output: {
732
+ ...nitroConfig.rollupConfig?.output,
733
+ entryFileNames: "server.mjs"
734
+ }
735
+ },
736
+ hooks: {
737
+ ...nitroConfig.hooks,
738
+ compiled: () => {
739
+ if (!hasOutput) {
740
+ const buildOutput = {
741
+ errors: [],
742
+ warnings: [],
743
+ outputPaths: {
744
+ root: pathToFileURL(`${nitroConfig.output?.dir}`),
745
+ browser: pathToFileURL(`${nitroConfig.output?.publicDir}`),
746
+ server: pathToFileURL(`${nitroConfig.output?.dir}/server`)
747
+ }
748
+ };
749
+ console.log(JSON.stringify(buildOutput, null, 2));
750
+ hasOutput = true;
751
+ }
752
+ }
753
+ }
754
+ };
703
755
  };
704
- const isNetlifyPreset = (buildPreset) => process.env['NETLIFY'] ||
705
- (buildPreset && buildPreset.toLowerCase().includes('netlify'));
706
- const withNetlifyOutputAPI = (nitroConfig, workspaceRoot) => ({
707
- ...nitroConfig,
708
- output: {
709
- ...nitroConfig?.output,
710
- dir: normalizePath(resolve(workspaceRoot, 'netlify/functions')),
711
- },
756
+ var isNetlifyPreset = (buildPreset) => process.env["NETLIFY"] || buildPreset && buildPreset.toLowerCase().includes("netlify");
757
+ var withNetlifyOutputAPI = (nitroConfig, workspaceRoot) => ({
758
+ ...nitroConfig,
759
+ output: {
760
+ ...nitroConfig?.output,
761
+ dir: normalizePath(resolve(workspaceRoot, "netlify/functions"))
762
+ }
712
763
  });
764
+ //#endregion
765
+ export { nitro };
766
+
713
767
  //# sourceMappingURL=vite-plugin-nitro.js.map