@analogjs/vite-plugin-nitro 3.0.0-alpha.30 → 3.0.0-alpha.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/index.d.ts +1 -1
- package/src/index.js.map +1 -1
- package/src/lib/build-sitemap.d.ts +14 -0
- package/src/lib/build-sitemap.js +4 -2
- package/src/lib/build-sitemap.js.map +1 -1
- package/src/lib/options.d.ts +11 -0
- package/src/lib/plugins/dev-server-plugin.js +8 -1
- package/src/lib/plugins/dev-server-plugin.js.map +1 -1
- package/src/lib/utils/i18n-prerender.d.ts +36 -0
- package/src/lib/utils/i18n-prerender.js +23 -0
- package/src/lib/utils/i18n-prerender.js.map +1 -0
- package/src/lib/utils/register-i18n-watcher.d.ts +15 -0
- package/src/lib/vite-plugin-nitro.js +9 -2
- package/src/lib/vite-plugin-nitro.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@analogjs/vite-plugin-nitro",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.32",
|
|
4
4
|
"description": "A Vite plugin for adding a nitro API server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Brandon Roberts <robertsbt@gmail.com>",
|
|
@@ -63,7 +63,8 @@
|
|
|
63
63
|
"@analogjs/storybook-angular",
|
|
64
64
|
"@analogjs/vite-plugin-angular",
|
|
65
65
|
"@analogjs/vite-plugin-nitro",
|
|
66
|
-
"@analogjs/vitest-angular"
|
|
66
|
+
"@analogjs/vitest-angular",
|
|
67
|
+
"@analogjs/angular-compiler"
|
|
67
68
|
],
|
|
68
69
|
"migrations": "./migrations/migration.json"
|
|
69
70
|
},
|
package/src/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { nitro } from "./lib/vite-plugin-nitro.js";
|
|
2
2
|
export { debugInstances } from "./lib/utils/debug.js";
|
|
3
3
|
export { nitro } from "./lib/vite-plugin-nitro.js";
|
|
4
|
-
export type { Options, SitemapConfig, SitemapEntry, SitemapExcludeRule, SitemapPriority, SitemapRouteDefinition, SitemapRouteInput, SitemapRouteSource, SitemapTransform, PrerenderSitemapConfig, PrerenderRouteConfig, PrerenderContentDir, PrerenderContentFile } from "./lib/options.js";
|
|
4
|
+
export type { Options, SitemapConfig, SitemapEntry, SitemapExcludeRule, SitemapPriority, SitemapRouteDefinition, SitemapRouteInput, SitemapRouteSource, SitemapTransform, PrerenderSitemapConfig, PrerenderRouteConfig, PrerenderContentDir, PrerenderContentFile, I18nPrerenderOptions } from "./lib/options.js";
|
|
5
5
|
declare module "nitro/types" {
|
|
6
6
|
interface NitroRouteConfig {
|
|
7
7
|
ssr?: boolean;
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import { nitro } from './lib/vite-plugin-nitro.js';\nexport { debugInstances } from './lib/utils/debug.js';\nexport { nitro } from './lib/vite-plugin-nitro.js';\nexport type {\n Options,\n SitemapConfig,\n SitemapEntry,\n SitemapExcludeRule,\n SitemapPriority,\n SitemapRouteDefinition,\n SitemapRouteInput,\n SitemapRouteSource,\n SitemapTransform,\n PrerenderSitemapConfig,\n PrerenderRouteConfig,\n PrerenderContentDir,\n PrerenderContentFile,\n} from './lib/options.js';\n\ndeclare module 'nitro/types' {\n interface NitroRouteConfig {\n ssr?: boolean;\n }\n\n interface NitroRouteRules {\n ssr?: boolean;\n }\n}\n\nexport default nitro;\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import { nitro } from './lib/vite-plugin-nitro.js';\nexport { debugInstances } from './lib/utils/debug.js';\nexport { nitro } from './lib/vite-plugin-nitro.js';\nexport type {\n Options,\n SitemapConfig,\n SitemapEntry,\n SitemapExcludeRule,\n SitemapPriority,\n SitemapRouteDefinition,\n SitemapRouteInput,\n SitemapRouteSource,\n SitemapTransform,\n PrerenderSitemapConfig,\n PrerenderRouteConfig,\n PrerenderContentDir,\n PrerenderContentFile,\n I18nPrerenderOptions,\n} from './lib/options.js';\n\ndeclare module 'nitro/types' {\n interface NitroRouteConfig {\n ssr?: boolean;\n }\n\n interface NitroRouteRules {\n ssr?: boolean;\n }\n}\n\nexport default nitro;\n"],"mappings":";;;AA8BA,IAAA,cAAe"}
|
|
@@ -6,4 +6,18 @@ export interface BuildSitemapOptions {
|
|
|
6
6
|
apiPrefix?: string;
|
|
7
7
|
}
|
|
8
8
|
export declare function buildSitemap(_config: UserConfig, sitemapConfig: SitemapConfig, routes: (string | undefined)[] | (() => Promise<(string | undefined)[]>), outputDir: string, routeSitemaps: Record<string, RouteSitemapConfig>, buildOptions?: BuildSitemapOptions): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Generates hreflang alternate URLs for a given page URL.
|
|
11
|
+
* For a URL like `https://example.com/fr/about`, it produces alternates
|
|
12
|
+
* for all configured locales.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getHreflangAlternates(pageUrl: string, host: string, i18n: I18nPrerenderOptions): {
|
|
15
|
+
locale: string;
|
|
16
|
+
href: string;
|
|
17
|
+
}[];
|
|
18
|
+
/**
|
|
19
|
+
* Strips a locale prefix from a URL path.
|
|
20
|
+
* E.g., '/fr/about' -> '/about', '/en' -> '/'
|
|
21
|
+
*/
|
|
22
|
+
export declare function stripLocalePrefix(path: string, locales: string[]): string;
|
|
9
23
|
export {};
|
package/src/lib/build-sitemap.js
CHANGED
|
@@ -123,11 +123,13 @@ async function resolveRouteInputs(routes) {
|
|
|
123
123
|
else routeList = [];
|
|
124
124
|
return routeList.filter(Boolean);
|
|
125
125
|
}
|
|
126
|
-
function createXml(elementName) {
|
|
126
|
+
function createXml(elementName, includeXhtml = false) {
|
|
127
|
+
const attrs = { xmlns: "https://www.sitemaps.org/schemas/sitemap/0.9" };
|
|
128
|
+
if (includeXhtml) attrs["xmlns:xhtml"] = "https://www.w3.org/1999/xhtml";
|
|
127
129
|
return create({
|
|
128
130
|
version: "1.0",
|
|
129
131
|
encoding: "UTF-8"
|
|
130
|
-
}).ele(elementName,
|
|
132
|
+
}).ele(elementName, attrs).com(`This file was automatically generated by Analog.`);
|
|
131
133
|
}
|
|
132
134
|
//#endregion
|
|
133
135
|
export { buildSitemap };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-sitemap.js","names":[],"sources":["../../../src/lib/build-sitemap.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { create } from 'xmlbuilder2';\nimport { XMLBuilder } from 'xmlbuilder2/lib/interfaces';\nimport { UserConfig } from 'vite';\nimport {\n PrerenderSitemapConfig,\n SitemapConfig,\n SitemapEntry,\n SitemapExcludeRule,\n SitemapRouteDefinition,\n SitemapRouteInput,\n SitemapRouteSource,\n} from './options';\n\ntype RouteSitemapConfig =\n | PrerenderSitemapConfig\n | (() => PrerenderSitemapConfig)\n | undefined;\n\nexport type PagesJson = SitemapEntry;\n\nexport interface BuildSitemapOptions {\n apiPrefix?: string;\n}\n\nexport async function buildSitemap(\n _config: UserConfig,\n sitemapConfig: SitemapConfig,\n routes: (string | undefined)[] | (() => Promise<(string | undefined)[]>),\n outputDir: string,\n routeSitemaps: Record<string, RouteSitemapConfig>,\n buildOptions: BuildSitemapOptions = {},\n): Promise<void> {\n const host = normalizeSitemapHost(sitemapConfig.host);\n const routeList = await collectSitemapRoutes(routes, sitemapConfig.include);\n const sitemapData = await resolveSitemapEntries(\n routeList,\n host,\n routeSitemaps,\n sitemapConfig,\n buildOptions,\n );\n\n if (!sitemapData.length) {\n return;\n }\n\n const sitemap = createXml('urlset');\n\n for (const item of sitemapData) {\n const page = sitemap.ele('url');\n page.ele('loc').txt(item.loc);\n\n if (item.lastmod) {\n page.ele('lastmod').txt(item.lastmod);\n }\n\n if (item.changefreq) {\n page.ele('changefreq').txt(item.changefreq);\n }\n\n if (item.priority !== undefined) {\n page.ele('priority').txt(String(item.priority));\n }\n }\n\n const resolvedOutputDir = resolve(outputDir);\n const mapPath = resolve(resolvedOutputDir, 'sitemap.xml');\n try {\n if (!resolvedOutputDir || resolvedOutputDir === resolve()) {\n throw new Error(\n 'Refusing to write the sitemap to the current working directory. Expected the Nitro public output directory instead.',\n );\n }\n\n if (!existsSync(resolvedOutputDir)) {\n mkdirSync(resolvedOutputDir, { recursive: true });\n }\n console.log(`Writing sitemap at ${mapPath}`);\n writeFileSync(mapPath, sitemap.end({ prettyPrint: true }));\n } catch (e) {\n console.error(`Unable to write file at ${mapPath}`, e);\n }\n}\n\nasync function resolveSitemapEntries(\n routes: SitemapRouteInput[],\n host: string,\n routeSitemaps: Record<string, RouteSitemapConfig>,\n sitemapConfig: SitemapConfig,\n buildOptions: BuildSitemapOptions,\n): Promise<SitemapEntry[]> {\n const defaults = sitemapConfig.defaults ?? {};\n const seen = new Set<string>();\n const entries: SitemapEntry[] = [];\n\n for (const route of routes) {\n const entry = await toSitemapEntry(\n route,\n host,\n routeSitemaps,\n defaults,\n sitemapConfig.transform,\n );\n\n if (!entry) {\n continue;\n }\n\n if (\n isInternalSitemapRoute(entry.route, buildOptions.apiPrefix) ||\n (await isExcludedSitemapRoute(entry, sitemapConfig.exclude))\n ) {\n continue;\n }\n\n if (seen.has(entry.loc)) {\n continue;\n }\n\n seen.add(entry.loc);\n entries.push(entry);\n }\n\n return entries;\n}\n\nasync function toSitemapEntry(\n route: SitemapRouteInput,\n host: string,\n routeSitemaps: Record<string, RouteSitemapConfig>,\n defaults: PrerenderSitemapConfig,\n transform: SitemapConfig['transform'],\n): Promise<SitemapEntry | undefined> {\n const normalizedRoute = normalizeSitemapRoute(\n typeof route === 'string' ? route : route?.route,\n );\n if (!normalizedRoute) {\n return undefined;\n }\n\n const baseEntry = createSitemapEntry(\n {\n ...defaults,\n ...resolveRouteSitemapConfig(routeSitemaps[normalizedRoute]),\n ...(typeof route === 'object' ? route : {}),\n route: normalizedRoute,\n },\n host,\n );\n\n if (!transform) {\n return baseEntry;\n }\n\n const transformed = await transform(baseEntry);\n if (!transformed) {\n return undefined;\n }\n\n return createSitemapEntry(\n {\n ...baseEntry,\n ...transformed,\n },\n host,\n );\n}\n\nfunction createSitemapEntry(\n routeDefinition: SitemapRouteDefinition,\n host: string,\n): SitemapEntry {\n const route = normalizeSitemapRoute(routeDefinition.route) ?? '/';\n\n return {\n route,\n loc: new URL(route, ensureTrailingSlash(host)).toString(),\n lastmod: routeDefinition.lastmod,\n changefreq: routeDefinition.changefreq,\n priority: routeDefinition.priority,\n };\n}\n\nfunction resolveRouteSitemapConfig(\n config: RouteSitemapConfig,\n): PrerenderSitemapConfig {\n if (!config) {\n return {};\n }\n\n return typeof config === 'function' ? config() : config;\n}\n\nfunction normalizeSitemapHost(host: string): string {\n const resolvedHost = new URL(host);\n resolvedHost.hash = '';\n return resolvedHost.toString();\n}\n\nfunction ensureTrailingSlash(host: string): string {\n return host.endsWith('/') ? host : `${host}/`;\n}\n\nfunction normalizeSitemapRoute(route: string | undefined): string | undefined {\n if (!route) {\n return undefined;\n }\n\n const trimmedRoute = route.trim();\n if (!trimmedRoute) {\n return undefined;\n }\n\n const pathWithQuery = trimmedRoute.split('#', 1)[0] ?? '';\n const [pathname, search] = pathWithQuery.split('?', 2);\n const normalizedPathname = pathname\n ? `/${pathname.replace(/^\\/+/, '').replace(/\\/{2,}/g, '/')}`\n : '/';\n\n return search ? `${normalizedPathname}?${search}` : normalizedPathname;\n}\n\nfunction isInternalSitemapRoute(route: string, apiPrefix = 'api'): boolean {\n const normalizedApiPrefix = normalizeSitemapRoute(`/${apiPrefix}`) ?? '/api';\n return (\n route === `${normalizedApiPrefix}/_analog/pages` ||\n route.startsWith(`${normalizedApiPrefix}/_analog/pages/`)\n );\n}\n\nasync function isExcludedSitemapRoute(\n entry: SitemapEntry,\n excludeRules: SitemapExcludeRule[] | undefined,\n): Promise<boolean> {\n if (!excludeRules?.length) {\n return false;\n }\n\n for (const rule of excludeRules) {\n if (typeof rule === 'function') {\n if (await rule(entry)) {\n return true;\n }\n continue;\n }\n\n if (rule instanceof RegExp) {\n if (rule.test(entry.route)) {\n return true;\n }\n continue;\n }\n\n if (toGlobRegExp(rule).test(entry.route)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction toGlobRegExp(pattern: string): RegExp {\n const doubleStarToken = '__ANALOG_DOUBLE_STAR__';\n const singleStarToken = '__ANALOG_SINGLE_STAR__';\n const escapedPattern = pattern\n .replace(/\\*\\*/g, doubleStarToken)\n .replace(/\\*/g, singleStarToken)\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&');\n const regexPattern = escapedPattern\n .replace(new RegExp(doubleStarToken, 'g'), '.*')\n .replace(new RegExp(singleStarToken, 'g'), '[^/]*');\n return new RegExp(`^${regexPattern}$`);\n}\n\nasync function collectSitemapRoutes(\n routes: (string | undefined)[] | (() => Promise<(string | undefined)[]>),\n include?: SitemapRouteSource,\n): Promise<SitemapRouteInput[]> {\n const routeList = await resolveRouteInputs(routes);\n const includedRoutes = include ? await resolveRouteInputs(include) : [];\n return [...routeList, ...includedRoutes];\n}\n\nasync function resolveRouteInputs(\n routes:\n | SitemapRouteSource\n | (string | undefined)[]\n | (() => Promise<(string | undefined)[]>),\n): Promise<SitemapRouteInput[]> {\n let routeList: SitemapRouteInput[];\n\n if (typeof routes === 'function') {\n routeList = await routes();\n } else if (Array.isArray(routes)) {\n routeList = routes;\n } else {\n routeList = [];\n }\n\n return routeList.filter(Boolean);\n}\n\nfunction createXml(elementName: 'urlset' | 'sitemapindex'): XMLBuilder {\n return create({ version: '1.0', encoding: 'UTF-8' })\n .ele(elementName, {\n xmlns: 'https://www.sitemaps.org/schemas/sitemap/0.9',\n })\n .com(`This file was automatically generated by Analog.`);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"build-sitemap.js","names":[],"sources":["../../../src/lib/build-sitemap.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { create } from 'xmlbuilder2';\nimport { XMLBuilder } from 'xmlbuilder2/lib/interfaces';\nimport { UserConfig } from 'vite';\nimport {\n PrerenderSitemapConfig,\n SitemapConfig,\n SitemapEntry,\n SitemapExcludeRule,\n SitemapRouteDefinition,\n SitemapRouteInput,\n SitemapRouteSource,\n} from './options';\n\ntype RouteSitemapConfig =\n | PrerenderSitemapConfig\n | (() => PrerenderSitemapConfig)\n | undefined;\n\nexport type PagesJson = SitemapEntry;\n\nexport interface BuildSitemapOptions {\n apiPrefix?: string;\n}\n\nexport async function buildSitemap(\n _config: UserConfig,\n sitemapConfig: SitemapConfig,\n routes: (string | undefined)[] | (() => Promise<(string | undefined)[]>),\n outputDir: string,\n routeSitemaps: Record<string, RouteSitemapConfig>,\n buildOptions: BuildSitemapOptions = {},\n): Promise<void> {\n const host = normalizeSitemapHost(sitemapConfig.host);\n const routeList = await collectSitemapRoutes(routes, sitemapConfig.include);\n const sitemapData = await resolveSitemapEntries(\n routeList,\n host,\n routeSitemaps,\n sitemapConfig,\n buildOptions,\n );\n\n if (!sitemapData.length) {\n return;\n }\n\n const sitemap = createXml('urlset');\n\n for (const item of sitemapData) {\n const page = sitemap.ele('url');\n page.ele('loc').txt(item.loc);\n\n if (item.lastmod) {\n page.ele('lastmod').txt(item.lastmod);\n }\n\n if (item.changefreq) {\n page.ele('changefreq').txt(item.changefreq);\n }\n\n if (item.priority !== undefined) {\n page.ele('priority').txt(String(item.priority));\n }\n }\n\n const resolvedOutputDir = resolve(outputDir);\n const mapPath = resolve(resolvedOutputDir, 'sitemap.xml');\n try {\n if (!resolvedOutputDir || resolvedOutputDir === resolve()) {\n throw new Error(\n 'Refusing to write the sitemap to the current working directory. Expected the Nitro public output directory instead.',\n );\n }\n\n if (!existsSync(resolvedOutputDir)) {\n mkdirSync(resolvedOutputDir, { recursive: true });\n }\n console.log(`Writing sitemap at ${mapPath}`);\n writeFileSync(mapPath, sitemap.end({ prettyPrint: true }));\n } catch (e) {\n console.error(`Unable to write file at ${mapPath}`, e);\n }\n}\n\nasync function resolveSitemapEntries(\n routes: SitemapRouteInput[],\n host: string,\n routeSitemaps: Record<string, RouteSitemapConfig>,\n sitemapConfig: SitemapConfig,\n buildOptions: BuildSitemapOptions,\n): Promise<SitemapEntry[]> {\n const defaults = sitemapConfig.defaults ?? {};\n const seen = new Set<string>();\n const entries: SitemapEntry[] = [];\n\n for (const route of routes) {\n const entry = await toSitemapEntry(\n route,\n host,\n routeSitemaps,\n defaults,\n sitemapConfig.transform,\n );\n\n if (!entry) {\n continue;\n }\n\n if (\n isInternalSitemapRoute(entry.route, buildOptions.apiPrefix) ||\n (await isExcludedSitemapRoute(entry, sitemapConfig.exclude))\n ) {\n continue;\n }\n\n if (seen.has(entry.loc)) {\n continue;\n }\n\n seen.add(entry.loc);\n entries.push(entry);\n }\n\n return entries;\n}\n\nasync function toSitemapEntry(\n route: SitemapRouteInput,\n host: string,\n routeSitemaps: Record<string, RouteSitemapConfig>,\n defaults: PrerenderSitemapConfig,\n transform: SitemapConfig['transform'],\n): Promise<SitemapEntry | undefined> {\n const normalizedRoute = normalizeSitemapRoute(\n typeof route === 'string' ? route : route?.route,\n );\n if (!normalizedRoute) {\n return undefined;\n }\n\n const baseEntry = createSitemapEntry(\n {\n ...defaults,\n ...resolveRouteSitemapConfig(routeSitemaps[normalizedRoute]),\n ...(typeof route === 'object' ? route : {}),\n route: normalizedRoute,\n },\n host,\n );\n\n if (!transform) {\n return baseEntry;\n }\n\n const transformed = await transform(baseEntry);\n if (!transformed) {\n return undefined;\n }\n\n return createSitemapEntry(\n {\n ...baseEntry,\n ...transformed,\n },\n host,\n );\n}\n\nfunction createSitemapEntry(\n routeDefinition: SitemapRouteDefinition,\n host: string,\n): SitemapEntry {\n const route = normalizeSitemapRoute(routeDefinition.route) ?? '/';\n\n return {\n route,\n loc: new URL(route, ensureTrailingSlash(host)).toString(),\n lastmod: routeDefinition.lastmod,\n changefreq: routeDefinition.changefreq,\n priority: routeDefinition.priority,\n };\n}\n\nfunction resolveRouteSitemapConfig(\n config: RouteSitemapConfig,\n): PrerenderSitemapConfig {\n if (!config) {\n return {};\n }\n\n return typeof config === 'function' ? config() : config;\n}\n\nfunction normalizeSitemapHost(host: string): string {\n const resolvedHost = new URL(host);\n resolvedHost.hash = '';\n return resolvedHost.toString();\n}\n\nfunction ensureTrailingSlash(host: string): string {\n return host.endsWith('/') ? host : `${host}/`;\n}\n\nfunction normalizeSitemapRoute(route: string | undefined): string | undefined {\n if (!route) {\n return undefined;\n }\n\n const trimmedRoute = route.trim();\n if (!trimmedRoute) {\n return undefined;\n }\n\n const pathWithQuery = trimmedRoute.split('#', 1)[0] ?? '';\n const [pathname, search] = pathWithQuery.split('?', 2);\n const normalizedPathname = pathname\n ? `/${pathname.replace(/^\\/+/, '').replace(/\\/{2,}/g, '/')}`\n : '/';\n\n return search ? `${normalizedPathname}?${search}` : normalizedPathname;\n}\n\nfunction isInternalSitemapRoute(route: string, apiPrefix = 'api'): boolean {\n const normalizedApiPrefix = normalizeSitemapRoute(`/${apiPrefix}`) ?? '/api';\n return (\n route === `${normalizedApiPrefix}/_analog/pages` ||\n route.startsWith(`${normalizedApiPrefix}/_analog/pages/`)\n );\n}\n\nasync function isExcludedSitemapRoute(\n entry: SitemapEntry,\n excludeRules: SitemapExcludeRule[] | undefined,\n): Promise<boolean> {\n if (!excludeRules?.length) {\n return false;\n }\n\n for (const rule of excludeRules) {\n if (typeof rule === 'function') {\n if (await rule(entry)) {\n return true;\n }\n continue;\n }\n\n if (rule instanceof RegExp) {\n if (rule.test(entry.route)) {\n return true;\n }\n continue;\n }\n\n if (toGlobRegExp(rule).test(entry.route)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction toGlobRegExp(pattern: string): RegExp {\n const doubleStarToken = '__ANALOG_DOUBLE_STAR__';\n const singleStarToken = '__ANALOG_SINGLE_STAR__';\n const escapedPattern = pattern\n .replace(/\\*\\*/g, doubleStarToken)\n .replace(/\\*/g, singleStarToken)\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&');\n const regexPattern = escapedPattern\n .replace(new RegExp(doubleStarToken, 'g'), '.*')\n .replace(new RegExp(singleStarToken, 'g'), '[^/]*');\n return new RegExp(`^${regexPattern}$`);\n}\n\nasync function collectSitemapRoutes(\n routes: (string | undefined)[] | (() => Promise<(string | undefined)[]>),\n include?: SitemapRouteSource,\n): Promise<SitemapRouteInput[]> {\n const routeList = await resolveRouteInputs(routes);\n const includedRoutes = include ? await resolveRouteInputs(include) : [];\n return [...routeList, ...includedRoutes];\n}\n\nasync function resolveRouteInputs(\n routes:\n | SitemapRouteSource\n | (string | undefined)[]\n | (() => Promise<(string | undefined)[]>),\n): Promise<SitemapRouteInput[]> {\n let routeList: SitemapRouteInput[];\n\n if (typeof routes === 'function') {\n routeList = await routes();\n } else if (Array.isArray(routes)) {\n routeList = routes;\n } else {\n routeList = [];\n }\n\n return routeList.filter(Boolean);\n}\n\n/**\n * Generates hreflang alternate URLs for a given page URL.\n * For a URL like `https://example.com/fr/about`, it produces alternates\n * for all configured locales.\n */\nexport function getHreflangAlternates(\n pageUrl: string,\n host: string,\n i18n: I18nPrerenderOptions,\n): { locale: string; href: string }[] {\n const alternates: { locale: string; href: string }[] = [];\n const normalizedHost = host.replace(/\\/+$/, '');\n\n // Extract the path portion after the host\n const path = pageUrl.replace(normalizedHost, '');\n\n // Strip locale prefix to get the base path\n const basePath = stripLocalePrefix(path, i18n.locales);\n\n for (const locale of i18n.locales) {\n const localizedPath =\n basePath === '/' || basePath === ''\n ? `/${locale}`\n : `/${locale}${basePath}`;\n alternates.push({\n locale,\n href: `${normalizedHost}${localizedPath}`,\n });\n }\n\n // Add x-default pointing to the default locale variant\n const defaultPath =\n basePath === '/' || basePath === ''\n ? `/${i18n.defaultLocale}`\n : `/${i18n.defaultLocale}${basePath}`;\n alternates.push({\n locale: 'x-default',\n href: `${normalizedHost}${defaultPath}`,\n });\n\n return alternates;\n}\n\n/**\n * Strips a locale prefix from a URL path.\n * E.g., '/fr/about' -> '/about', '/en' -> '/'\n */\nexport function stripLocalePrefix(path: string, locales: string[]): string {\n const segments = path.split('/').filter(Boolean);\n if (segments.length > 0 && locales.includes(segments[0])) {\n const rest = segments.slice(1).join('/');\n return rest ? `/${rest}` : '/';\n }\n return path || '/';\n}\n\nfunction createXml(\n elementName: 'urlset' | 'sitemapindex',\n includeXhtml = false,\n): XMLBuilder {\n const attrs: Record<string, string> = {\n xmlns: 'https://www.sitemaps.org/schemas/sitemap/0.9',\n };\n if (includeXhtml) {\n attrs['xmlns:xhtml'] = 'https://www.w3.org/1999/xhtml';\n }\n\n return create({ version: '1.0', encoding: 'UTF-8' })\n .ele(elementName, attrs)\n .com(`This file was automatically generated by Analog.`);\n}\n"],"mappings":"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"}
|
package/src/lib/options.d.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type { PrerenderRoute } from "nitro/types";
|
|
2
2
|
import type { UserConfig } from "vite";
|
|
3
|
+
export interface I18nPrerenderOptions {
|
|
4
|
+
/**
|
|
5
|
+
* The default/source locale for the application.
|
|
6
|
+
*/
|
|
7
|
+
defaultLocale: string;
|
|
8
|
+
/**
|
|
9
|
+
* List of supported locale identifiers.
|
|
10
|
+
* Each route will be prerendered once per locale with a locale prefix.
|
|
11
|
+
*/
|
|
12
|
+
locales: string[];
|
|
13
|
+
}
|
|
3
14
|
export interface Options {
|
|
4
15
|
ssr?: boolean;
|
|
5
16
|
ssrBuildDir?: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { writeWebResponseToNode } from "../utils/node-web-bridge.js";
|
|
2
2
|
import { registerDevServerMiddleware } from "../utils/register-dev-middleware.js";
|
|
3
|
+
import { detectLocaleFromRoute, setHtmlLang } from "../utils/i18n-prerender.js";
|
|
3
4
|
import { normalizePath } from "vite";
|
|
4
5
|
import { resolve } from "node:path";
|
|
5
6
|
import { readFileSync } from "node:fs";
|
|
@@ -29,6 +30,7 @@ function devServerPlugin(options) {
|
|
|
29
30
|
return async () => {
|
|
30
31
|
remove_html_middlewares(viteServer.middlewares);
|
|
31
32
|
registerDevServerMiddleware(root, sourceRoot, viteServer);
|
|
33
|
+
if (options.i18n) registerI18nWatcher(viteServer);
|
|
32
34
|
viteServer.middlewares.use(async (req, res) => {
|
|
33
35
|
let template = readFileSync(resolve(viteServer.config.root, index), "utf-8");
|
|
34
36
|
template = await viteServer.transformIndexHtml(req.originalUrl, template);
|
|
@@ -48,8 +50,13 @@ function devServerPlugin(options) {
|
|
|
48
50
|
await writeWebResponseToNode(res, result);
|
|
49
51
|
return;
|
|
50
52
|
}
|
|
53
|
+
let html = typeof result === "string" ? result : template;
|
|
54
|
+
if (options.i18n) {
|
|
55
|
+
const locale = detectLocaleFromRoute(req.originalUrl, options.i18n);
|
|
56
|
+
html = setHtmlLang(html, locale);
|
|
57
|
+
}
|
|
51
58
|
res.setHeader("Content-Type", "text/html");
|
|
52
|
-
res.end(
|
|
59
|
+
res.end(html);
|
|
53
60
|
} catch (e) {
|
|
54
61
|
viteServer.ssrFixStacktrace(e);
|
|
55
62
|
res.statusCode = 500;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-server-plugin.js","names":[],"sources":["../../../../src/lib/plugins/dev-server-plugin.ts"],"sourcesContent":["// SSR dev server, middleware and error page source modified from\n// https://github.com/solidjs/solid-start/blob/main/packages/start/dev/server.js\n\nimport {\n Connect,\n Plugin,\n UserConfig,\n ViteDevServer,\n normalizePath,\n} from 'vite';\nimport { resolve } from 'node:path';\nimport { readFileSync } from 'node:fs';\nimport { createRouter as createRadixRouter, toRouteMatcher } from 'radix3';\nimport { defu } from 'defu';\nimport type { NitroRouteRules } from 'nitro/types';\n\nimport { registerDevServerMiddleware } from '../utils/register-dev-middleware.js';\nimport { writeWebResponseToNode } from '../utils/node-web-bridge.js';\nimport { Options } from '../options.js';\n\ntype ServerOptions = Options & { routeRules?: Record<string, any> | undefined };\n\nexport function devServerPlugin(options: ServerOptions): Plugin {\n const workspaceRoot = options?.workspaceRoot || process.cwd();\n const sourceRoot = options?.sourceRoot ?? 'src';\n const index = options.index || 'index.html';\n let config: UserConfig;\n let root: string;\n let isTest = false;\n\n return {\n name: 'analogjs-dev-ssr-plugin',\n config(userConfig, { mode }) {\n config = userConfig;\n root = normalizePath(resolve(workspaceRoot, config.root || '.') || '.');\n isTest = isTest ? isTest : mode === 'test';\n return {\n appType: 'custom',\n resolve: {\n alias: {\n '~analog/entry-server':\n options.entryServer || `${root}/${sourceRoot}/main.server.ts`,\n },\n },\n };\n },\n configureServer(viteServer) {\n if (isTest) {\n return;\n }\n\n return async () => {\n remove_html_middlewares(viteServer.middlewares);\n registerDevServerMiddleware(root, sourceRoot, viteServer);\n\n viteServer.middlewares.use(async (req, res) => {\n let template = readFileSync(\n resolve(viteServer.config.root, index),\n 'utf-8',\n );\n\n template = await viteServer.transformIndexHtml(\n req.originalUrl as string,\n template,\n );\n\n const _routeRulesMatcher = toRouteMatcher(\n createRadixRouter({ routes: options.routeRules }),\n );\n const _getRouteRules = (path: string) =>\n defu(\n {},\n ..._routeRulesMatcher.matchAll(path).reverse(),\n ) as NitroRouteRules;\n\n try {\n let result: string | Response;\n // Check for route rules explicitly disabling SSR\n if (_getRouteRules(req.originalUrl as string).ssr === false) {\n result = template;\n } else {\n const entryServer = (\n await viteServer.ssrLoadModule('~analog/entry-server')\n )['default'];\n result = await entryServer(req.originalUrl, template, {\n req,\n res,\n });\n }\n\n if (result instanceof Response) {\n await writeWebResponseToNode(res, result);\n return;\n }\n res.setHeader('Content-Type', 'text/html');\n res.end(
|
|
1
|
+
{"version":3,"file":"dev-server-plugin.js","names":[],"sources":["../../../../src/lib/plugins/dev-server-plugin.ts"],"sourcesContent":["// SSR dev server, middleware and error page source modified from\n// https://github.com/solidjs/solid-start/blob/main/packages/start/dev/server.js\n\nimport {\n Connect,\n Plugin,\n UserConfig,\n ViteDevServer,\n normalizePath,\n} from 'vite';\nimport { resolve } from 'node:path';\nimport { readFileSync } from 'node:fs';\nimport { createRouter as createRadixRouter, toRouteMatcher } from 'radix3';\nimport { defu } from 'defu';\nimport type { NitroRouteRules } from 'nitro/types';\n\nimport { registerDevServerMiddleware } from '../utils/register-dev-middleware.js';\nimport { writeWebResponseToNode } from '../utils/node-web-bridge.js';\nimport { Options } from '../options.js';\nimport { detectLocaleFromRoute, setHtmlLang } from '../utils/i18n-prerender.js';\n\ntype ServerOptions = Options & { routeRules?: Record<string, any> | undefined };\n\nexport function devServerPlugin(options: ServerOptions): Plugin {\n const workspaceRoot = options?.workspaceRoot || process.cwd();\n const sourceRoot = options?.sourceRoot ?? 'src';\n const index = options.index || 'index.html';\n let config: UserConfig;\n let root: string;\n let isTest = false;\n\n return {\n name: 'analogjs-dev-ssr-plugin',\n config(userConfig, { mode }) {\n config = userConfig;\n root = normalizePath(resolve(workspaceRoot, config.root || '.') || '.');\n isTest = isTest ? isTest : mode === 'test';\n return {\n appType: 'custom',\n resolve: {\n alias: {\n '~analog/entry-server':\n options.entryServer || `${root}/${sourceRoot}/main.server.ts`,\n },\n },\n };\n },\n configureServer(viteServer) {\n if (isTest) {\n return;\n }\n\n return async () => {\n remove_html_middlewares(viteServer.middlewares);\n registerDevServerMiddleware(root, sourceRoot, viteServer);\n\n if (options.i18n) {\n registerI18nWatcher(viteServer);\n }\n\n viteServer.middlewares.use(async (req, res) => {\n let template = readFileSync(\n resolve(viteServer.config.root, index),\n 'utf-8',\n );\n\n template = await viteServer.transformIndexHtml(\n req.originalUrl as string,\n template,\n );\n\n const _routeRulesMatcher = toRouteMatcher(\n createRadixRouter({ routes: options.routeRules }),\n );\n const _getRouteRules = (path: string) =>\n defu(\n {},\n ..._routeRulesMatcher.matchAll(path).reverse(),\n ) as NitroRouteRules;\n\n try {\n let result: string | Response;\n // Check for route rules explicitly disabling SSR\n if (_getRouteRules(req.originalUrl as string).ssr === false) {\n result = template;\n } else {\n const entryServer = (\n await viteServer.ssrLoadModule('~analog/entry-server')\n )['default'];\n result = await entryServer(req.originalUrl, template, {\n req,\n res,\n });\n }\n\n if (result instanceof Response) {\n await writeWebResponseToNode(res, result);\n return;\n }\n\n // Inject lang attribute when i18n is configured\n let html = typeof result === 'string' ? result : template;\n if (options.i18n) {\n const locale = detectLocaleFromRoute(\n req.originalUrl as string,\n options.i18n,\n );\n html = setHtmlLang(html, locale);\n }\n\n res.setHeader('Content-Type', 'text/html');\n res.end(html);\n } catch (e) {\n viteServer.ssrFixStacktrace(e as Error);\n res.statusCode = 500;\n res.end(`\n <!DOCTYPE html>\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <title>Error</title>\n <script type=\"module\">\n import { ErrorOverlay } from '/@vite/client'\n document.body.appendChild(new ErrorOverlay(${JSON.stringify(\n prepareError(req, e),\n ).replace(/</g, '\\\\u003c')}))\n </script>\n </head>\n <body>\n </body>\n </html>\n `);\n }\n });\n };\n },\n };\n}\n\n/**\n * Removes Vite internal middleware\n *\n * @param server\n */\nfunction remove_html_middlewares(server: ViteDevServer['middlewares']) {\n const html_middlewares = [\n 'viteIndexHtmlMiddleware',\n 'vite404Middleware',\n 'viteSpaFallbackMiddleware',\n 'viteHtmlFallbackMiddleware',\n ];\n for (let i = server.stack.length - 1; i > 0; i--) {\n const handler = server.stack[i]?.handle;\n const handlerName =\n typeof handler === 'function' ? handler.name : undefined;\n if (handlerName && html_middlewares.includes(handlerName)) {\n server.stack.splice(i, 1);\n }\n }\n}\n\n/**\n * Formats error for SSR message in error overlay\n * @param req\n * @param error\n * @returns\n */\nfunction prepareError(req: Connect.IncomingMessage, error: unknown) {\n const e = error as Error;\n return {\n message: `An error occured while server rendering ${req.url}:\\n\\n\\t${\n typeof e === 'string' ? e : e.message\n } `,\n stack: typeof e === 'string' ? '' : e.stack,\n };\n}\n"],"mappings":";;;;;;;;;AAuBA,SAAgB,gBAAgB,SAAgC;CAC9D,MAAM,gBAAgB,SAAS,iBAAiB,QAAQ,KAAK;CAC7D,MAAM,aAAa,SAAS,cAAc;CAC1C,MAAM,QAAQ,QAAQ,SAAS;CAC/B,IAAI;CACJ,IAAI;CACJ,IAAI,SAAS;AAEb,QAAO;EACL,MAAM;EACN,OAAO,YAAY,EAAE,QAAQ;AAC3B,YAAS;AACT,UAAO,cAAc,QAAQ,eAAe,OAAO,QAAQ,IAAI,IAAI,IAAI;AACvE,YAAS,SAAS,SAAS,SAAS;AACpC,UAAO;IACL,SAAS;IACT,SAAS,EACP,OAAO,EACL,wBACE,QAAQ,eAAe,GAAG,KAAK,GAAG,WAAW,kBAChD,EACF;IACF;;EAEH,gBAAgB,YAAY;AAC1B,OAAI,OACF;AAGF,UAAO,YAAY;AACjB,4BAAwB,WAAW,YAAY;AAC/C,gCAA4B,MAAM,YAAY,WAAW;AAEzD,QAAI,QAAQ,KACV,qBAAoB,WAAW;AAGjC,eAAW,YAAY,IAAI,OAAO,KAAK,QAAQ;KAC7C,IAAI,WAAW,aACb,QAAQ,WAAW,OAAO,MAAM,MAAM,EACtC,QACD;AAED,gBAAW,MAAM,WAAW,mBAC1B,IAAI,aACJ,SACD;KAED,MAAM,qBAAqB,eACzB,aAAkB,EAAE,QAAQ,QAAQ,YAAY,CAAC,CAClD;KACD,MAAM,kBAAkB,SACtB,KACE,EAAE,EACF,GAAG,mBAAmB,SAAS,KAAK,CAAC,SAAS,CAC/C;AAEH,SAAI;MACF,IAAI;AAEJ,UAAI,eAAe,IAAI,YAAsB,CAAC,QAAQ,MACpD,UAAS;WACJ;OACL,MAAM,eACJ,MAAM,WAAW,cAAc,uBAAuB,EACtD;AACF,gBAAS,MAAM,YAAY,IAAI,aAAa,UAAU;QACpD;QACA;QACD,CAAC;;AAGJ,UAAI,kBAAkB,UAAU;AAC9B,aAAM,uBAAuB,KAAK,OAAO;AACzC;;MAIF,IAAI,OAAO,OAAO,WAAW,WAAW,SAAS;AACjD,UAAI,QAAQ,MAAM;OAChB,MAAM,SAAS,sBACb,IAAI,aACJ,QAAQ,KACT;AACD,cAAO,YAAY,MAAM,OAAO;;AAGlC,UAAI,UAAU,gBAAgB,YAAY;AAC1C,UAAI,IAAI,KAAK;cACN,GAAG;AACV,iBAAW,iBAAiB,EAAW;AACvC,UAAI,aAAa;AACjB,UAAI,IAAI;;;;;;;;iEAQ6C,KAAK,UAChD,aAAa,KAAK,EAAE,CACrB,CAAC,QAAQ,MAAM,UAAU,CAAC;;;;;;cAMjC;;MAEJ;;;EAGP;;;;;;;AAQH,SAAS,wBAAwB,QAAsC;CACrE,MAAM,mBAAmB;EACvB;EACA;EACA;EACA;EACD;AACD,MAAK,IAAI,IAAI,OAAO,MAAM,SAAS,GAAG,IAAI,GAAG,KAAK;EAChD,MAAM,UAAU,OAAO,MAAM,IAAI;EACjC,MAAM,cACJ,OAAO,YAAY,aAAa,QAAQ,OAAO,KAAA;AACjD,MAAI,eAAe,iBAAiB,SAAS,YAAY,CACvD,QAAO,MAAM,OAAO,GAAG,EAAE;;;;;;;;;AAW/B,SAAS,aAAa,KAA8B,OAAgB;CAClE,MAAM,IAAI;AACV,QAAO;EACL,SAAS,2CAA2C,IAAI,IAAI,SAC1D,OAAO,MAAM,WAAW,IAAI,EAAE,QAC/B;EACD,OAAO,OAAO,MAAM,WAAW,KAAK,EAAE;EACvC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { PrerenderRoute } from "nitropack";
|
|
2
|
+
import { I18nPrerenderOptions } from "../options.js";
|
|
3
|
+
/**
|
|
4
|
+
* Expands a list of routes to include locale-prefixed variants.
|
|
5
|
+
*
|
|
6
|
+
* For each route and each locale, generates a prefixed route:
|
|
7
|
+
* '/' + locale + route
|
|
8
|
+
*
|
|
9
|
+
* The default locale's routes are included both with and without the prefix
|
|
10
|
+
* so that `/about` and `/en/about` both render.
|
|
11
|
+
*
|
|
12
|
+
* @param routes - The original routes to expand
|
|
13
|
+
* @param i18n - The i18n prerender configuration
|
|
14
|
+
* @returns Expanded routes with locale prefixes
|
|
15
|
+
*/
|
|
16
|
+
export declare function expandRoutesWithLocales(routes: string[], i18n: I18nPrerenderOptions): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Creates a post-rendering hook that injects the `lang` attribute
|
|
19
|
+
* into the `<html>` tag of prerendered pages based on the route's
|
|
20
|
+
* locale prefix.
|
|
21
|
+
*
|
|
22
|
+
* @param i18n - The i18n prerender configuration
|
|
23
|
+
* @returns A post-rendering hook function
|
|
24
|
+
*/
|
|
25
|
+
export declare function createI18nPostRenderingHook(i18n: I18nPrerenderOptions): (route: PrerenderRoute) => Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Detects the locale from a prerendered route path by checking
|
|
28
|
+
* the first path segment against the configured locales.
|
|
29
|
+
*/
|
|
30
|
+
export declare function detectLocaleFromRoute(route: string, i18n: I18nPrerenderOptions): string;
|
|
31
|
+
/**
|
|
32
|
+
* Sets the `lang` attribute on the `<html>` tag in an HTML string.
|
|
33
|
+
* If a `lang` attribute already exists, it is replaced.
|
|
34
|
+
* If no `lang` attribute exists, it is added.
|
|
35
|
+
*/
|
|
36
|
+
export declare function setHtmlLang(html: string, locale: string): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region packages/vite-plugin-nitro/src/lib/utils/i18n-prerender.ts
|
|
2
|
+
/**
|
|
3
|
+
* Detects the locale from a prerendered route path by checking
|
|
4
|
+
* the first path segment against the configured locales.
|
|
5
|
+
*/
|
|
6
|
+
function detectLocaleFromRoute(route, i18n) {
|
|
7
|
+
const firstSegment = route.split("/").filter(Boolean)[0];
|
|
8
|
+
if (firstSegment && i18n.locales.includes(firstSegment)) return firstSegment;
|
|
9
|
+
return i18n.defaultLocale;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Sets the `lang` attribute on the `<html>` tag in an HTML string.
|
|
13
|
+
* If a `lang` attribute already exists, it is replaced.
|
|
14
|
+
* If no `lang` attribute exists, it is added.
|
|
15
|
+
*/
|
|
16
|
+
function setHtmlLang(html, locale) {
|
|
17
|
+
if (/<html[^>]*\slang\s*=\s*["'][^"']*["']/i.test(html)) return html.replace(/(<html[^>]*\s)lang\s*=\s*["'][^"']*["']/i, `$1lang="${locale}"`);
|
|
18
|
+
return html.replace(/<html/i, `<html lang="${locale}"`);
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { detectLocaleFromRoute, setHtmlLang };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=i18n-prerender.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n-prerender.js","names":[],"sources":["../../../../src/lib/utils/i18n-prerender.ts"],"sourcesContent":["import { PrerenderRoute } from 'nitropack';\nimport { I18nPrerenderOptions } from '../options.js';\n\n/**\n * Expands a list of routes to include locale-prefixed variants.\n *\n * For each route and each locale, generates a prefixed route:\n * '/' + locale + route\n *\n * The default locale's routes are included both with and without the prefix\n * so that `/about` and `/en/about` both render.\n *\n * @param routes - The original routes to expand\n * @param i18n - The i18n prerender configuration\n * @returns Expanded routes with locale prefixes\n */\nexport function expandRoutesWithLocales(\n routes: string[],\n i18n: I18nPrerenderOptions,\n): string[] {\n const expanded: string[] = [];\n\n for (const route of routes) {\n // Skip API routes — they don't need locale prefixes\n if (route.includes('/_analog/') || route.startsWith('/api/')) {\n expanded.push(route);\n continue;\n }\n\n for (const locale of i18n.locales) {\n const prefix = `/${locale}`;\n const localizedRoute = route === '/' ? prefix : `${prefix}${route}`;\n expanded.push(localizedRoute);\n }\n\n // Keep the unprefixed route for the default locale\n if (!expanded.includes(route)) {\n expanded.push(route);\n }\n }\n\n return expanded;\n}\n\n/**\n * Creates a post-rendering hook that injects the `lang` attribute\n * into the `<html>` tag of prerendered pages based on the route's\n * locale prefix.\n *\n * @param i18n - The i18n prerender configuration\n * @returns A post-rendering hook function\n */\nexport function createI18nPostRenderingHook(\n i18n: I18nPrerenderOptions,\n): (route: PrerenderRoute) => Promise<void> {\n return async (route: PrerenderRoute) => {\n if (!route.contents || typeof route.contents !== 'string') {\n return;\n }\n\n const locale = detectLocaleFromRoute(route.route, i18n);\n if (!locale) {\n return;\n }\n\n // Inject or replace the lang attribute on <html>\n route.contents = setHtmlLang(route.contents, locale);\n };\n}\n\n/**\n * Detects the locale from a prerendered route path by checking\n * the first path segment against the configured locales.\n */\nexport function detectLocaleFromRoute(\n route: string,\n i18n: I18nPrerenderOptions,\n): string {\n const segments = route.split('/').filter(Boolean);\n const firstSegment = segments[0];\n\n if (firstSegment && i18n.locales.includes(firstSegment)) {\n return firstSegment;\n }\n\n return i18n.defaultLocale;\n}\n\n/**\n * Sets the `lang` attribute on the `<html>` tag in an HTML string.\n * If a `lang` attribute already exists, it is replaced.\n * If no `lang` attribute exists, it is added.\n */\nexport function setHtmlLang(html: string, locale: string): string {\n // Replace existing lang attribute\n if (/<html[^>]*\\slang\\s*=\\s*[\"'][^\"']*[\"']/i.test(html)) {\n return html.replace(\n /(<html[^>]*\\s)lang\\s*=\\s*[\"'][^\"']*[\"']/i,\n `$1lang=\"${locale}\"`,\n );\n }\n\n // Add lang attribute to <html> tag\n return html.replace(/<html/i, `<html lang=\"${locale}\"`);\n}\n"],"mappings":";;;;;AA0EA,SAAgB,sBACd,OACA,MACQ;CAER,MAAM,eADW,MAAM,MAAM,IAAI,CAAC,OAAO,QAAQ,CACnB;AAE9B,KAAI,gBAAgB,KAAK,QAAQ,SAAS,aAAa,CACrD,QAAO;AAGT,QAAO,KAAK;;;;;;;AAQd,SAAgB,YAAY,MAAc,QAAwB;AAEhE,KAAI,yCAAyC,KAAK,KAAK,CACrD,QAAO,KAAK,QACV,4CACA,WAAW,OAAO,GACnB;AAIH,QAAO,KAAK,QAAQ,UAAU,eAAe,OAAO,GAAG"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ViteDevServer } from "vite";
|
|
2
|
+
/**
|
|
3
|
+
* Registers a file watcher that triggers a full page reload
|
|
4
|
+
* when translation files are added or modified.
|
|
5
|
+
*
|
|
6
|
+
* Matches files in i18n directories with .json, .xlf, .xmb, or .arb extensions.
|
|
7
|
+
*
|
|
8
|
+
* @param viteServer The Vite development server instance
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerI18nWatcher(viteServer: ViteDevServer): void;
|
|
11
|
+
/**
|
|
12
|
+
* Checks whether a file path looks like a translation file
|
|
13
|
+
* based on its location in an i18n directory and its extension.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isTranslationFile(path: string): boolean;
|
|
@@ -298,7 +298,8 @@ function nitro(options, nitroOptions) {
|
|
|
298
298
|
options?.ssr ? devServerPlugin({
|
|
299
299
|
entryServer: options?.entryServer,
|
|
300
300
|
index: options?.index,
|
|
301
|
-
routeRules: nitroOptions?.routeRules
|
|
301
|
+
routeRules: nitroOptions?.routeRules,
|
|
302
|
+
i18n: options?.i18n
|
|
302
303
|
}) : false,
|
|
303
304
|
{
|
|
304
305
|
name: "@analogjs/vite-plugin-nitro",
|
|
@@ -672,7 +673,13 @@ function nitro(options, nitroOptions) {
|
|
|
672
673
|
{
|
|
673
674
|
name: "@analogjs/vite-plugin-nitro-api-prefix",
|
|
674
675
|
config() {
|
|
675
|
-
return { define: {
|
|
676
|
+
return { define: {
|
|
677
|
+
ANALOG_API_PREFIX: `"${baseURL.substring(1)}${apiPrefix.substring(1)}"`,
|
|
678
|
+
...options?.i18n ? {
|
|
679
|
+
ANALOG_I18N_DEFAULT_LOCALE: JSON.stringify(options.i18n.defaultLocale),
|
|
680
|
+
ANALOG_I18N_LOCALES: JSON.stringify(options.i18n.locales)
|
|
681
|
+
} : {}
|
|
682
|
+
} };
|
|
676
683
|
}
|
|
677
684
|
}
|
|
678
685
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite-plugin-nitro.js","names":[],"sources":["../../../src/lib/vite-plugin-nitro.ts"],"sourcesContent":["import type { NitroConfig, NitroEventHandler, RollupConfig } from 'nitro/types';\nimport { build, createDevServer, createNitro } from 'nitro/builder';\nimport * as vite from 'vite';\nimport type { Plugin, UserConfig, ViteDevServer } from 'vite';\nimport { mergeConfig, normalizePath } from 'vite';\nimport { relative, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { existsSync, readFileSync, readdirSync } from 'node:fs';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nimport { buildServer, isVercelPreset } from './build-server.js';\nimport { buildClientApp, buildSSRApp } from './build-ssr.js';\nimport {\n Options,\n PrerenderContentDir,\n PrerenderContentFile,\n PrerenderRouteConfig,\n PrerenderSitemapConfig,\n} from './options.js';\nimport { pageEndpointsPlugin } from './plugins/page-endpoints.js';\nimport { getPageHandlers } from './utils/get-page-handlers.js';\nimport { buildSitemap } from './build-sitemap.js';\nimport { devServerPlugin } from './plugins/dev-server-plugin.js';\nimport {\n toWebRequest,\n writeWebResponseToNode,\n} from './utils/node-web-bridge.js';\nimport { getMatchingContentFilesWithFrontMatter } from './utils/get-content-files.js';\nimport {\n ssrRenderer,\n clientRenderer,\n apiMiddleware,\n} from './utils/renderers.js';\nimport { getBundleOptionsKey, isRolldown } from './utils/rolldown.js';\nimport { debugNitro, debugSsr } from './utils/debug.js';\n\nfunction createNitroMiddlewareHandler(handler: string): NitroEventHandler {\n return {\n route: '/**',\n handler,\n middleware: true,\n };\n}\n\n/**\n * Creates a `rollup:before` hook that marks specified packages as external\n * in Nitro's bundler config (applied to both the server build and the\n * prerender build).\n *\n * ## Subpath matching (Rolldown compatibility)\n *\n * When `bundlerConfig.external` is an **array**, Rollup automatically\n * prefix-matches entries — `'rxjs'` in the array will also externalise\n * `'rxjs/operators'`, `'rxjs/internal/Observable'`, etc.\n *\n * Rolldown (the default bundler in Nitro v3) does **not** do this. It\n * treats array entries as exact strings. To keep behaviour consistent\n * across both bundlers, the **function** branch already needed explicit\n * subpath matching. We now use the same `isExternal` helper for all\n * branches so that `'rxjs'` reliably matches `'rxjs/operators'`\n * regardless of whether the existing `external` value is a function,\n * array, or absent.\n *\n * Without this, the Nitro prerender build fails on Windows CI with:\n *\n * [RESOLVE_ERROR] Could not resolve 'rxjs/operators'\n */\nfunction createRollupBeforeHook(externalEntries: string[]) {\n const isExternal = (source: string) =>\n externalEntries.some(\n (entry) => source === entry || source.startsWith(entry + '/'),\n );\n\n return (_nitro: unknown, bundlerConfig: RollupConfig) => {\n sanitizeNitroBundlerConfig(_nitro, bundlerConfig);\n\n if (externalEntries.length === 0) {\n return;\n }\n\n const existing = bundlerConfig.external;\n if (!existing) {\n bundlerConfig.external = externalEntries;\n } else if (typeof existing === 'function') {\n bundlerConfig.external = (\n source: string,\n importer: string | undefined,\n isResolved: boolean,\n ) => existing(source, importer, isResolved) || isExternal(source);\n } else if (Array.isArray(existing)) {\n bundlerConfig.external = [...existing, ...externalEntries];\n } else {\n bundlerConfig.external = [existing as string, ...externalEntries];\n }\n };\n}\n\nfunction appendNoExternals(\n noExternals: NitroConfig['noExternals'],\n ...entries: string[]\n): NitroConfig['noExternals'] {\n if (!noExternals) {\n return entries;\n }\n\n return Array.isArray(noExternals)\n ? [...noExternals, ...entries]\n : noExternals;\n}\n\n/**\n * Patches Nitro's internal Rollup/Rolldown bundler config to work around\n * incompatibilities in the Nitro v3 alpha series.\n *\n * Called from the `rollup:before` hook, this function runs against the *final*\n * bundler config that Nitro assembles for its server/prerender builds — it\n * does NOT touch the normal Vite client or SSR environment configs.\n *\n * Each workaround is narrowly scoped and safe to remove once the corresponding\n * upstream Nitro issue is resolved.\n */\nfunction sanitizeNitroBundlerConfig(\n _nitro: unknown,\n bundlerConfig: RollupConfig,\n) {\n const output = bundlerConfig['output'];\n if (!output || Array.isArray(output) || typeof output !== 'object') {\n return;\n }\n\n // ── 1. Remove invalid `output.codeSplitting` ────────────────────────\n //\n // Nitro 3.0.1-alpha.2 adds `output.codeSplitting` to its internal bundler\n // config, but Rolldown rejects it as an unknown key:\n //\n // Warning: Invalid output options (1 issue found)\n // - For the \"codeSplitting\". Invalid key: Expected never but received \"codeSplitting\".\n //\n // Analog never sets this option. Removing it restores default bundler\n // behavior without changing any Analog semantics.\n if ('codeSplitting' in output) {\n delete (output as Record<string, unknown>)['codeSplitting'];\n }\n\n // ── 2. Remove invalid `output.manualChunks` ─────────────────────────\n //\n // Nitro's default config enables manual chunking for node_modules. Under\n // Nitro v3 alpha + Rollup 4.59 this crashes during the prerender rebundle:\n //\n // Cannot read properties of undefined (reading 'included')\n //\n // A single server bundle is acceptable for Analog's use case, so we strip\n // `manualChunks` until the upstream bug is fixed.\n if ('manualChunks' in output) {\n delete (output as Record<string, unknown>)['manualChunks'];\n }\n\n // ── 3. Escape route params in `output.chunkFileNames` ───────────────\n //\n // Nitro's `getChunkName()` derives chunk filenames from route patterns,\n // using its internal `routeToFsPath()` helper to convert route params\n // (`:productId` → `[productId]`) and catch-alls (`**` → `[...]`).\n //\n // Rollup/Rolldown interprets *any* `[token]` in the string returned by a\n // `chunkFileNames` function as a placeholder. Only a handful are valid —\n // `[name]`, `[hash]`, `[format]`, `[ext]` — so route-derived tokens like\n // `[productId]` or `[...]` trigger a build error:\n //\n // \"[productId]\" is not a valid placeholder in the \"output.chunkFileNames\" pattern.\n //\n // We wrap the original function to replace non-standard `[token]` patterns\n // with `_token_`, preserving the intended filename while avoiding the\n // placeholder validation error.\n //\n // Example: `_routes/products/[productId].mjs` → `_routes/products/_productId_.mjs`\n const VALID_ROLLUP_PLACEHOLDER = /^\\[(?:name|hash|format|ext)\\]$/;\n const chunkFileNames = (output as Record<string, unknown>)['chunkFileNames'];\n if (typeof chunkFileNames === 'function') {\n const originalFn = chunkFileNames as (...args: unknown[]) => unknown;\n (output as Record<string, unknown>)['chunkFileNames'] = (\n ...args: unknown[]\n ) => {\n const result = originalFn(...args);\n if (typeof result !== 'string') return result;\n return result.replace(/\\[[^\\]]+\\]/g, (match: string) =>\n VALID_ROLLUP_PLACEHOLDER.test(match)\n ? match\n : `_${match.slice(1, -1)}_`,\n );\n };\n }\n}\n\nfunction resolveClientOutputPath(\n cachedPath: string,\n workspaceRoot: string,\n rootDir: string,\n configuredOutDir: string | undefined,\n) {\n if (cachedPath) {\n debugNitro('resolveClientOutputPath using cached path', {\n cachedPath,\n workspaceRoot,\n rootDir,\n configuredOutDir,\n });\n return cachedPath;\n }\n\n if (configuredOutDir) {\n const resolvedPath = normalizePath(\n resolve(workspaceRoot, rootDir, configuredOutDir),\n );\n debugNitro('resolveClientOutputPath using configured build.outDir', {\n workspaceRoot,\n rootDir,\n configuredOutDir,\n resolvedPath,\n });\n return resolvedPath;\n }\n\n // When no explicit build.outDir is set, the environment build config defaults\n // to `<workspace>/dist/<root>/client` for the client build. The non-SSR\n // (client) and SSR paths must agree on this so that registerIndexHtmlVirtual()\n // and publicAssets read from the directory the client build actually wrote to.\n const resolvedPath = normalizePath(\n resolve(workspaceRoot, 'dist', rootDir, 'client'),\n );\n debugNitro('resolveClientOutputPath using default dist client path', {\n workspaceRoot,\n rootDir,\n configuredOutDir,\n resolvedPath,\n });\n return resolvedPath;\n}\n\nfunction getEnvironmentBuildOutDir(environment: unknown): string | undefined {\n if (!environment || typeof environment !== 'object') {\n return undefined;\n }\n\n const environmentConfig = environment as {\n config?: {\n build?: {\n outDir?: string;\n };\n };\n build?: {\n outDir?: string;\n };\n };\n\n return (\n environmentConfig.config?.build?.outDir ?? environmentConfig.build?.outDir\n );\n}\n\nfunction resolveBuiltClientOutputPath(\n cachedPath: string,\n workspaceRoot: string,\n rootDir: string,\n configuredOutDir: string | undefined,\n environment?: unknown,\n) {\n const environmentOutDir = getEnvironmentBuildOutDir(environment);\n if (environmentOutDir) {\n const resolvedPath = normalizePath(\n resolve(workspaceRoot, rootDir, environmentOutDir),\n );\n debugNitro('resolveBuiltClientOutputPath using environment outDir', {\n cachedPath,\n workspaceRoot,\n rootDir,\n configuredOutDir,\n environmentOutDir,\n resolvedPath,\n });\n return resolvedPath;\n }\n\n debugNitro('resolveBuiltClientOutputPath falling back to shared resolver', {\n cachedPath,\n workspaceRoot,\n rootDir,\n configuredOutDir,\n environmentOutDir,\n });\n return resolveClientOutputPath(\n cachedPath,\n workspaceRoot,\n rootDir,\n configuredOutDir,\n );\n}\n\nfunction getNitroPublicOutputDir(nitroConfig: NitroConfig): string {\n const publicDir = nitroConfig.output?.publicDir;\n if (!publicDir) {\n throw new Error(\n 'Nitro public output directory is required to build the sitemap.',\n );\n }\n\n return publicDir;\n}\n\nfunction readDirectoryEntries(path: string): string[] {\n try {\n return readdirSync(path).sort();\n } catch (error) {\n return [\n `<<unable to read directory: ${error instanceof Error ? error.message : String(error)}>>`,\n ];\n }\n}\n\nfunction getPathDebugInfo(path: string) {\n return {\n rawPath: path,\n normalizedPath: normalizePath(path),\n exists: existsSync(path),\n entries: existsSync(path) ? readDirectoryEntries(path) : [],\n };\n}\n\nfunction assetSourceToString(source: string | Uint8Array) {\n return typeof source === 'string'\n ? source\n : Buffer.from(source).toString('utf8');\n}\n\nfunction captureClientIndexHtmlFromBundle(\n bundle: Record<\n string,\n {\n type?: string;\n fileName?: string;\n source?: string | Uint8Array;\n }\n >,\n hook: 'generateBundle' | 'writeBundle',\n) {\n const indexHtmlAsset = Object.values(bundle).find(\n (chunk) =>\n chunk.type === 'asset' &&\n chunk.fileName === 'index.html' &&\n typeof chunk.source !== 'undefined',\n );\n\n if (!indexHtmlAsset?.source) {\n debugNitro(`client bundle did not expose index.html during ${hook}`, {\n hook,\n bundleKeys: Object.keys(bundle).sort(),\n assetFileNames: Object.values(bundle)\n .filter((chunk) => chunk.type === 'asset')\n .map((chunk) => chunk.fileName)\n .filter(Boolean),\n });\n return undefined;\n }\n\n const indexHtml = assetSourceToString(indexHtmlAsset.source);\n debugNitro(`captured client bundle index.html asset during ${hook}`, {\n hook,\n fileName: indexHtmlAsset.fileName,\n htmlLength: indexHtml.length,\n });\n return indexHtml;\n}\n\n// Nitro only needs the HTML template string. Prefer the on-disk file when it\n// exists, but allow the captured client asset to cover build flows where the\n// client output directory disappears before Nitro assembles its virtual modules.\nfunction registerIndexHtmlVirtual(\n nitroConfig: NitroConfig,\n clientOutputPath: string,\n inlineIndexHtml?: string,\n) {\n const indexHtmlPath = resolve(clientOutputPath, 'index.html');\n debugNitro('registerIndexHtmlVirtual inspecting client output', {\n platform: process.platform,\n cwd: process.cwd(),\n clientOutputPath,\n clientOutputPathInfo: getPathDebugInfo(clientOutputPath),\n indexHtmlPath,\n indexHtmlExists: existsSync(indexHtmlPath),\n hasInlineIndexHtml: typeof inlineIndexHtml === 'string',\n });\n if (!existsSync(indexHtmlPath) && typeof inlineIndexHtml !== 'string') {\n debugNitro('registerIndexHtmlVirtual missing index.html', {\n platform: process.platform,\n cwd: process.cwd(),\n clientOutputPath,\n clientOutputPathInfo: getPathDebugInfo(clientOutputPath),\n indexHtmlPath,\n hasInlineIndexHtml: typeof inlineIndexHtml === 'string',\n nitroOutput: nitroConfig.output,\n nitroPublicAssets: nitroConfig.publicAssets,\n });\n throw new Error(\n `[analog] Client build output not found at ${indexHtmlPath}.\\n` +\n `Ensure the client environment build completed successfully before the server build.`,\n );\n }\n const indexHtml =\n typeof inlineIndexHtml === 'string'\n ? inlineIndexHtml\n : readFileSync(indexHtmlPath, 'utf8');\n debugNitro('registerIndexHtmlVirtual using HTML template source', {\n source:\n typeof inlineIndexHtml === 'string'\n ? 'captured client bundle asset'\n : 'client output index.html file',\n indexHtmlPath,\n });\n nitroConfig.virtual = {\n ...nitroConfig.virtual,\n '#analog/index': `export default ${JSON.stringify(indexHtml)};`,\n };\n}\n\n/**\n * Converts the built SSR entry path into a specifier that Nitro's bundler\n * can resolve, including all relative `./assets/*` chunk imports inside\n * the entry.\n *\n * The returned path **must** be an absolute filesystem path with forward\n * slashes (e.g. `D:/a/analog/dist/apps/blog-app/ssr/main.server.js`).\n * This lets Rollup/Rolldown determine the entry's directory and resolve\n * sibling chunk imports like `./assets/core-DTazUigR.js` correctly.\n *\n * ## Why not pathToFileURL() on Windows?\n *\n * Earlier versions converted the path to a `file:///D:/a/...` URL on\n * Windows, which worked with Nitro v2 + Rollup. Nitro v3 switched its\n * default bundler to Rolldown, and Rolldown does **not** extract the\n * importer directory from `file://` URLs. This caused every relative\n * import inside the SSR entry to fail during the prerender build:\n *\n * [RESOLVE_ERROR] Could not resolve './assets/core-DTazUigR.js'\n * in ../../dist/apps/blog-app/ssr/main.server.js\n *\n * `normalizePath()` (from Vite) simply converts backslashes to forward\n * slashes, which both Rollup and Rolldown handle correctly on all\n * platforms.\n */\nfunction toNitroSsrEntrypointSpecifier(ssrEntryPath: string) {\n return normalizePath(ssrEntryPath);\n}\n\nfunction applySsrEntryAlias(\n nitroConfig: NitroConfig,\n options: Options | undefined,\n workspaceRoot: string,\n rootDir: string,\n): void {\n const ssrOutDir =\n options?.ssrBuildDir || resolve(workspaceRoot, 'dist', rootDir, 'ssr');\n if (options?.ssr || nitroConfig.prerender?.routes?.length) {\n const ssrEntryPath = resolveBuiltSsrEntryPath(ssrOutDir);\n const ssrEntry = toNitroSsrEntrypointSpecifier(ssrEntryPath);\n nitroConfig.alias = {\n ...nitroConfig.alias,\n '#analog/ssr': ssrEntry,\n };\n }\n}\n\nfunction resolveBuiltSsrEntryPath(ssrOutDir: string) {\n const candidatePaths = [\n resolve(ssrOutDir, 'main.server.mjs'),\n resolve(ssrOutDir, 'main.server.js'),\n resolve(ssrOutDir, 'main.server'),\n ];\n\n const ssrEntryPath = candidatePaths.find((candidatePath) =>\n existsSync(candidatePath),\n );\n\n if (!ssrEntryPath) {\n throw new Error(\n `Unable to locate the built SSR entry in \"${ssrOutDir}\". Expected one of: ${candidatePaths.join(\n ', ',\n )}`,\n );\n }\n\n return ssrEntryPath;\n}\n\nexport function nitro(options?: Options, nitroOptions?: NitroConfig): Plugin[] {\n const workspaceRoot = options?.workspaceRoot ?? process.cwd();\n const sourceRoot = options?.sourceRoot ?? 'src';\n let isTest = process.env['NODE_ENV'] === 'test' || !!process.env['VITEST'];\n const baseURL = process.env['NITRO_APP_BASE_URL'] || '';\n const prefix = baseURL ? baseURL.substring(0, baseURL.length - 1) : '';\n const apiPrefix = `/${options?.apiPrefix || 'api'}`;\n const useAPIMiddleware =\n typeof options?.useAPIMiddleware !== 'undefined'\n ? options?.useAPIMiddleware\n : true;\n const viteRolldownOutput = options?.vite?.build?.rolldownOptions?.output;\n // Vite's native build typing allows `output` to be either a single object or\n // an array. Analog only forwards `codeSplitting` into the client environment\n // when there is a single output object to merge into.\n const viteRolldownOutputConfig =\n viteRolldownOutput && !Array.isArray(viteRolldownOutput)\n ? viteRolldownOutput\n : undefined;\n const codeSplitting = viteRolldownOutputConfig?.codeSplitting;\n\n let isBuild = false;\n let isServe = false;\n let ssrBuild = false;\n let config: UserConfig;\n let nitroConfig: NitroConfig;\n let environmentBuild = false;\n let hasAPIDir = false;\n let clientOutputPath = '';\n let clientIndexHtml: string | undefined;\n let legacyClientSubBuild = false;\n const rollupExternalEntries: string[] = [];\n const sitemapRoutes: string[] = [];\n const routeSitemaps: Record<\n string,\n PrerenderSitemapConfig | (() => PrerenderSitemapConfig)\n > = {};\n const routeSourceFiles: Record<string, string> = {};\n let rootDir = workspaceRoot;\n\n return [\n (options?.ssr\n ? devServerPlugin({\n entryServer: options?.entryServer,\n index: options?.index,\n routeRules: nitroOptions?.routeRules,\n })\n : false) as Plugin,\n {\n name: '@analogjs/vite-plugin-nitro',\n async config(userConfig, { mode, command }) {\n isServe = command === 'serve';\n isBuild = command === 'build';\n ssrBuild = userConfig.build?.ssr === true;\n config = userConfig;\n isTest = isTest ? isTest : mode === 'test';\n rollupExternalEntries.length = 0;\n clientIndexHtml = undefined;\n sitemapRoutes.length = 0;\n for (const key of Object.keys(routeSitemaps)) {\n delete routeSitemaps[key];\n }\n for (const key of Object.keys(routeSourceFiles)) {\n delete routeSourceFiles[key];\n }\n\n const resolvedConfigRoot = config.root\n ? resolve(workspaceRoot, config.root)\n : workspaceRoot;\n rootDir = relative(workspaceRoot, resolvedConfigRoot) || '.';\n hasAPIDir = existsSync(\n resolve(\n workspaceRoot,\n rootDir,\n `${sourceRoot}/server/routes/${options?.apiPrefix || 'api'}`,\n ),\n );\n const buildPreset =\n process.env['BUILD_PRESET'] ??\n (nitroOptions?.preset as string | undefined) ??\n (process.env['VERCEL'] ? 'vercel' : undefined);\n\n const pageHandlers = getPageHandlers({\n workspaceRoot,\n sourceRoot,\n rootDir,\n additionalPagesDirs: options?.additionalPagesDirs,\n hasAPIDir,\n });\n const resolvedClientOutputPath = resolveClientOutputPath(\n clientOutputPath,\n workspaceRoot,\n rootDir,\n config.build?.outDir,\n );\n debugNitro('nitro config resolved client output path', {\n platform: process.platform,\n workspaceRoot,\n configRoot: config.root,\n resolvedConfigRoot,\n rootDir,\n buildOutDir: config.build?.outDir,\n clientOutputPath,\n resolvedClientOutputPath,\n hasEnvironmentConfig: !!config.environments,\n clientEnvironmentOutDir:\n config.environments?.['client'] &&\n typeof config.environments['client'] === 'object' &&\n 'build' in config.environments['client']\n ? (\n config.environments['client'] as {\n build?: { outDir?: string };\n }\n ).build?.outDir\n : undefined,\n });\n\n nitroConfig = {\n rootDir: normalizePath(rootDir),\n preset: buildPreset,\n compatibilityDate: '2025-11-19',\n logLevel: nitroOptions?.logLevel || 0,\n serverDir: normalizePath(`${sourceRoot}/server`),\n scanDirs: [\n normalizePath(`${rootDir}/${sourceRoot}/server`),\n ...(options?.additionalAPIDirs || []).map((dir) =>\n normalizePath(`${workspaceRoot}${dir}`),\n ),\n ],\n output: {\n dir: normalizePath(\n resolve(workspaceRoot, 'dist', rootDir, 'analog'),\n ),\n publicDir: normalizePath(\n resolve(workspaceRoot, 'dist', rootDir, 'analog/public'),\n ),\n },\n buildDir: normalizePath(\n resolve(workspaceRoot, 'dist', rootDir, '.nitro'),\n ),\n typescript: {\n generateTsConfig: false,\n },\n runtimeConfig: {\n apiPrefix: apiPrefix.substring(1),\n prefix,\n },\n // Analog provides its own renderer handler; prevent Nitro v3 from\n // auto-detecting index.html in rootDir and adding a conflicting one.\n renderer: false,\n imports: {\n autoImport: false,\n },\n hooks: {\n 'rollup:before': createRollupBeforeHook(rollupExternalEntries),\n },\n rollupConfig: {\n onwarn(warning) {\n if (\n warning.message.includes('empty chunk') &&\n warning.message.endsWith('.server')\n ) {\n return;\n }\n },\n plugins: [pageEndpointsPlugin()],\n },\n handlers: [\n ...(hasAPIDir\n ? []\n : useAPIMiddleware\n ? [createNitroMiddlewareHandler('#ANALOG_API_MIDDLEWARE')]\n : []),\n ...pageHandlers,\n ],\n routeRules: hasAPIDir\n ? undefined\n : useAPIMiddleware\n ? undefined\n : {\n [`${prefix}${apiPrefix}/**`]: {\n proxy: { to: '/**' },\n },\n },\n virtual: {\n '#ANALOG_SSR_RENDERER': ssrRenderer(),\n '#ANALOG_CLIENT_RENDERER': clientRenderer(),\n ...(hasAPIDir ? {} : { '#ANALOG_API_MIDDLEWARE': apiMiddleware }),\n },\n };\n\n if (isVercelPreset(buildPreset)) {\n nitroConfig = withVercelOutputAPI(nitroConfig, workspaceRoot);\n }\n\n if (isCloudflarePreset(buildPreset)) {\n nitroConfig = withCloudflareOutput(nitroConfig);\n }\n\n if (\n isNetlifyPreset(buildPreset) &&\n rootDir === '.' &&\n !existsSync(resolve(workspaceRoot, 'netlify.toml'))\n ) {\n nitroConfig = withNetlifyOutputAPI(nitroConfig, workspaceRoot);\n }\n\n if (isFirebaseAppHosting()) {\n nitroConfig = withAppHostingOutput(nitroConfig);\n }\n\n if (!ssrBuild && !isTest) {\n // store the client output path for the SSR build config\n clientOutputPath = resolvedClientOutputPath;\n debugNitro(\n 'nitro config cached client output path for later SSR/Nitro build',\n {\n ssrBuild,\n isTest,\n clientOutputPath,\n },\n );\n }\n\n // Start with a clean alias map. #analog/index is registered as a Nitro\n // virtual module after the client build, inlining the HTML template so\n // the server bundle imports it instead of using readFileSync with an\n // absolute path.\n nitroConfig.alias = {};\n\n if (isBuild) {\n nitroConfig.publicAssets = [\n { dir: normalizePath(resolvedClientOutputPath), maxAge: 0 },\n ];\n\n // In Nitro v3, renderer.entry is resolved via resolveModulePath()\n // during options normalization, which requires a real filesystem path.\n // Virtual modules (prefixed with #) can't survive this resolution.\n // Instead, we add the renderer as a catch-all handler directly —\n // this is functionally equivalent to what Nitro does internally\n // (it converts renderer.entry into a { route: '/**', lazy: true }\n // handler), but avoids the filesystem resolution step.\n const rendererHandler = options?.ssr\n ? '#ANALOG_SSR_RENDERER'\n : '#ANALOG_CLIENT_RENDERER';\n nitroConfig.handlers = [\n ...(nitroConfig.handlers || []),\n {\n handler: rendererHandler,\n route: '/**',\n lazy: true,\n },\n ];\n\n if (isEmptyPrerenderRoutes(options)) {\n nitroConfig.prerender = {};\n nitroConfig.prerender.routes = ['/'];\n }\n\n if (options?.prerender) {\n nitroConfig.prerender = nitroConfig.prerender ?? {};\n nitroConfig.prerender.crawlLinks = options?.prerender?.discover;\n\n let routes: (\n | string\n | PrerenderContentDir\n | PrerenderRouteConfig\n | undefined\n )[] = [];\n\n const prerenderRoutes = options?.prerender?.routes;\n const hasExplicitPrerenderRoutes =\n typeof prerenderRoutes === 'function' ||\n Array.isArray(prerenderRoutes);\n if (\n isArrayWithElements<string | PrerenderContentDir>(prerenderRoutes)\n ) {\n routes = prerenderRoutes;\n } else if (typeof prerenderRoutes === 'function') {\n routes = await prerenderRoutes();\n }\n\n const resolvedPrerenderRoutes = routes.reduce<string[]>(\n (prev, current) => {\n if (!current) {\n return prev;\n }\n if (typeof current === 'string') {\n prev.push(current);\n sitemapRoutes.push(current);\n return prev;\n }\n\n if ('route' in current) {\n if (current.sitemap) {\n routeSitemaps[current.route] = current.sitemap;\n }\n\n if (current.outputSourceFile) {\n const sourcePath = resolve(\n workspaceRoot,\n rootDir,\n current.outputSourceFile,\n );\n routeSourceFiles[current.route] = readFileSync(\n sourcePath,\n 'utf8',\n );\n }\n\n prev.push(current.route);\n sitemapRoutes.push(current.route);\n\n // Add the server-side data fetching endpoint URL\n if ('staticData' in current) {\n prev.push(`${apiPrefix}/_analog/pages/${current.route}`);\n }\n\n return prev;\n }\n\n const affectedFiles: PrerenderContentFile[] =\n getMatchingContentFilesWithFrontMatter(\n workspaceRoot,\n rootDir,\n current.contentDir,\n );\n\n affectedFiles.forEach((f) => {\n const result = current.transform(f);\n\n if (result) {\n if (current.sitemap) {\n routeSitemaps[result] =\n current.sitemap && typeof current.sitemap === 'function'\n ? current.sitemap?.(f)\n : current.sitemap;\n }\n\n if (current.outputSourceFile) {\n const sourceContent = current.outputSourceFile(f);\n if (sourceContent) {\n routeSourceFiles[result] = sourceContent;\n }\n }\n\n prev.push(result);\n sitemapRoutes.push(result);\n\n // Add the server-side data fetching endpoint URL\n if ('staticData' in current) {\n prev.push(`${apiPrefix}/_analog/pages/${result}`);\n }\n }\n });\n\n return prev;\n },\n [],\n );\n\n nitroConfig.prerender.routes =\n hasExplicitPrerenderRoutes || resolvedPrerenderRoutes.length\n ? resolvedPrerenderRoutes\n : (nitroConfig.prerender.routes ?? []);\n }\n\n // ── SSR / prerender Nitro config ─────────────────────────────\n //\n // This block configures Nitro for builds that rebundle the SSR\n // entry (main.server.{js,mjs}). That happens in two cases:\n //\n // 1. Full SSR apps — `options.ssr === true`\n // 2. Prerender-only — no runtime SSR, but the prerender build\n // still imports the SSR entry to render static pages.\n //\n // The original gate was `if (ssrBuild)`, which checks the Vite\n // top-level `build.ssr` flag. That works for SSR-only builds but\n // misses two Vite 6+ paths:\n //\n // a. **Vite Environment API (Vite 6+)** — SSR config lives in\n // `environments.ssr.build.ssr`, not `build.ssr`, so\n // `ssrBuild` is always `false`.\n // b. **Prerender-only apps** (e.g. blog-app) — `options.ssr`\n // is `false`, but prerender routes exist and the prerender\n // build still processes the SSR entry.\n //\n // Without this block:\n // - `rxjs` is never externalised → RESOLVE_ERROR in the\n // Nitro prerender build (especially on Windows CI).\n // - `moduleSideEffects` for zone.js is never set → zone.js\n // side-effects may be tree-shaken.\n // - The handlers list is not reassembled with page endpoints\n // + the renderer catch-all.\n //\n // The widened condition covers all supported build paths:\n // - `ssrBuild` → SSR-only build\n // - `options?.ssr` → Environment API SSR\n // - `nitroConfig.prerender?.routes?.length` → prerender-only\n if (\n ssrBuild ||\n options?.ssr ||\n nitroConfig.prerender?.routes?.length\n ) {\n nitroConfig.noExternals = appendNoExternals(\n nitroConfig.noExternals,\n 'es-toolkit',\n );\n\n if (process.platform === 'win32') {\n nitroConfig.noExternals = appendNoExternals(\n nitroConfig.noExternals,\n 'std-env',\n );\n }\n\n rollupExternalEntries.push(\n 'rxjs',\n 'node-fetch-native/dist/polyfill',\n // sharp is a native module with platform-specific binaries\n // (e.g. @img/sharp-darwin-arm64). pnpm creates symlinks for\n // ALL optional platform deps but only installs the matching\n // one — leaving broken symlinks that crash Nitro's bundler\n // with ENOENT during realpath(). Externalizing sharp avoids\n // bundling it entirely; it resolves from node_modules at\n // runtime instead.\n 'sharp',\n );\n\n nitroConfig = {\n ...nitroConfig,\n moduleSideEffects: ['zone.js/node', 'zone.js/fesm2015/zone-node'],\n handlers: [\n ...(hasAPIDir\n ? []\n : useAPIMiddleware\n ? [createNitroMiddlewareHandler('#ANALOG_API_MIDDLEWARE')]\n : []),\n ...pageHandlers,\n // Preserve the renderer catch-all handler added above\n {\n handler: rendererHandler,\n route: '/**',\n lazy: true,\n },\n ],\n };\n }\n }\n\n nitroConfig = mergeConfig(\n nitroConfig,\n nitroOptions as Record<string, any>,\n );\n\n // Only configure Vite 8 environments + builder on the top-level\n // build invocation. When buildApp's builder.build() calls re-enter\n // the config hook, returning environments/builder again would create\n // recursive buildApp invocations — each nesting another client build\n // that re-triggers config, producing an infinite loop of\n // \"building client environment... ✓ 1 modules transformed\".\n //\n // environmentBuild — already inside a buildApp call (recursion guard)\n // ssrBuild — legacy SSR-only sub-build\n // isServe — dev server / Vitest test runner (command: 'serve')\n if (environmentBuild || ssrBuild || isServe) {\n return {};\n }\n\n return {\n environments: {\n client: {\n build: {\n outDir:\n config?.build?.outDir ||\n resolve(workspaceRoot, 'dist', rootDir, 'client'),\n emptyOutDir: true,\n // Forward code-splitting config to Rolldown when running\n // under Vite 8+. `false` disables splitting (inlines all\n // dynamic imports); an object configures chunk groups.\n // The `!== undefined` check ensures `codeSplitting: false`\n // is forwarded correctly (a truthy check would swallow it).\n ...(isRolldown() && codeSplitting !== undefined\n ? {\n rolldownOptions: {\n output: {\n // Preserve any sibling Rolldown output options while\n // overriding just `codeSplitting` for the client build.\n ...viteRolldownOutputConfig,\n codeSplitting,\n },\n },\n }\n : {}),\n },\n },\n ssr: {\n build: {\n ssr: true,\n [getBundleOptionsKey()]: {\n input:\n options?.entryServer ||\n resolve(\n workspaceRoot,\n rootDir,\n `${sourceRoot}/main.server.ts`,\n ),\n },\n outDir:\n options?.ssrBuildDir ||\n resolve(workspaceRoot, 'dist', rootDir, 'ssr'),\n // Preserve the client build output. The client environment is\n // built first and Nitro reads its index.html after SSR finishes.\n emptyOutDir: false,\n },\n },\n },\n builder: {\n sharedPlugins: true,\n buildApp: async (builder) => {\n environmentBuild = true;\n debugNitro('builder.buildApp starting', {\n platform: process.platform,\n workspaceRoot,\n rootDir,\n cachedClientOutputPath: clientOutputPath,\n configuredBuildOutDir: config.build?.outDir,\n clientEnvironmentOutDir: getEnvironmentBuildOutDir(\n builder.environments['client'],\n ),\n ssrEnvironmentOutDir: getEnvironmentBuildOutDir(\n builder.environments['ssr'],\n ),\n });\n\n // Client must complete before SSR — the server build reads the\n // client's index.html via registerIndexHtmlVirtual(). Running\n // them in parallel caused a race on Windows where emptyOutDir\n // could delete client output before the server read it.\n await builder.build(builder.environments['client']);\n const postClientBuildOutputPath = resolveBuiltClientOutputPath(\n clientOutputPath,\n workspaceRoot,\n rootDir,\n config.build?.outDir,\n builder.environments['client'],\n );\n // Capture the client template before any SSR/prerender work runs.\n // On Windows, later phases can leave the client output directory\n // unavailable even though the client build itself succeeded.\n registerIndexHtmlVirtual(\n nitroConfig,\n postClientBuildOutputPath,\n clientIndexHtml,\n );\n debugNitro('builder.buildApp completed client build', {\n postClientBuildOutputPath,\n postClientBuildOutputInfo: getPathDebugInfo(\n postClientBuildOutputPath,\n ),\n postClientBuildIndexHtmlPath: resolve(\n postClientBuildOutputPath,\n 'index.html',\n ),\n postClientBuildIndexHtmlExists: existsSync(\n resolve(postClientBuildOutputPath, 'index.html'),\n ),\n });\n\n if (options?.ssr || nitroConfig.prerender?.routes?.length) {\n debugSsr('builder.buildApp starting SSR build', {\n ssrEnabled: options?.ssr,\n prerenderRoutes: nitroConfig.prerender?.routes,\n });\n await builder.build(builder.environments['ssr']);\n debugSsr('builder.buildApp completed SSR build', {\n ssrOutputPath:\n options?.ssrBuildDir ||\n resolve(workspaceRoot, 'dist', rootDir, 'ssr'),\n });\n }\n\n applySsrEntryAlias(nitroConfig, options, workspaceRoot, rootDir);\n\n const resolvedClientOutputPath = resolveBuiltClientOutputPath(\n clientOutputPath,\n workspaceRoot,\n rootDir,\n config.build?.outDir,\n builder.environments['client'],\n );\n\n nitroConfig.publicAssets = [\n { dir: normalizePath(resolvedClientOutputPath), maxAge: 0 },\n ];\n debugNitro(\n 'builder.buildApp resolved final client output path before Nitro build',\n {\n resolvedClientOutputPath,\n resolvedClientOutputInfo: getPathDebugInfo(\n resolvedClientOutputPath,\n ),\n nitroPublicAssets: nitroConfig.publicAssets,\n },\n );\n\n await buildServer(options, nitroConfig, routeSourceFiles);\n\n if (\n nitroConfig.prerender?.routes?.length &&\n options?.prerender?.sitemap\n ) {\n console.log('Building Sitemap...');\n // sitemap needs to be built after all directories are built\n await buildSitemap(\n config,\n options.prerender.sitemap,\n sitemapRoutes.length\n ? sitemapRoutes\n : nitroConfig.prerender.routes,\n getNitroPublicOutputDir(nitroConfig),\n routeSitemaps,\n { apiPrefix: options?.apiPrefix || 'api' },\n );\n }\n\n console.log(\n `\\n\\nThe '@analogjs/platform' server has been successfully built.`,\n );\n },\n },\n };\n },\n generateBundle(\n _options,\n bundle: Record<\n string,\n {\n type?: string;\n fileName?: string;\n source?: string | Uint8Array;\n }\n >,\n ) {\n if (!isBuild || ssrBuild) {\n return;\n }\n\n clientIndexHtml =\n captureClientIndexHtmlFromBundle(bundle, 'generateBundle') ??\n clientIndexHtml;\n },\n writeBundle(\n _options,\n bundle: Record<\n string,\n {\n type?: string;\n fileName?: string;\n source?: string | Uint8Array;\n }\n >,\n ) {\n if (!isBuild || ssrBuild) {\n return;\n }\n\n clientIndexHtml =\n captureClientIndexHtmlFromBundle(bundle, 'writeBundle') ??\n clientIndexHtml;\n },\n async configureServer(viteServer: ViteDevServer) {\n if (isServe && !isTest) {\n const nitro = await createNitro({\n dev: true,\n // Nitro's Vite builder now rejects `build()` in dev mode, but Analog's\n // dev integration still relies on the builder-driven reload hooks.\n // Force the server worker onto Rollup for this dev-only path.\n builder: 'rollup',\n ...nitroConfig,\n });\n const server = createDevServer(nitro);\n await build(nitro);\n const nitroSourceRoots = [\n normalizePath(\n resolve(workspaceRoot, rootDir, `${sourceRoot}/server`),\n ),\n ...(options?.additionalAPIDirs || []).map((dir) =>\n normalizePath(`${workspaceRoot}${dir}`),\n ),\n ];\n const isNitroSourceFile = (path: string) => {\n const normalizedPath = normalizePath(path);\n return nitroSourceRoots.some(\n (root) =>\n normalizedPath === root ||\n normalizedPath.startsWith(`${root}/`),\n );\n };\n let nitroRebuildPromise: Promise<void> | undefined;\n let nitroRebuildPending = false;\n const rebuildNitroServer = () => {\n if (nitroRebuildPromise) {\n // Coalesce rapid file events so a save that touches multiple server\n // route files results in one follow-up rebuild instead of many.\n nitroRebuildPending = true;\n return nitroRebuildPromise;\n }\n\n nitroRebuildPromise = (async () => {\n do {\n nitroRebuildPending = false;\n // Nitro API routes are not part of Vite's normal client HMR graph,\n // so rebuild the Nitro dev server to pick up handler edits.\n await build(nitro);\n } while (nitroRebuildPending);\n\n // Reload the page after the server rebuild completes so the next\n // request observes the updated API route implementation.\n viteServer.ws.send('analog:debug-full-reload', {\n plugin: 'vite-plugin-nitro',\n reason: 'nitro-server-rebuilt',\n });\n viteServer.ws.send({ type: 'full-reload' });\n })()\n .catch((error: unknown) => {\n viteServer.config.logger.error(\n `[analog] Failed to rebuild Nitro dev server.\\n${error instanceof Error ? error.stack || error.message : String(error)}`,\n );\n })\n .finally(() => {\n nitroRebuildPromise = undefined;\n });\n\n return nitroRebuildPromise;\n };\n const onNitroSourceChange = (path: string) => {\n if (!isNitroSourceFile(path)) {\n return;\n }\n\n void rebuildNitroServer();\n };\n\n // Watch the full Nitro source roots instead of only the API route\n // directory. API handlers often read helper modules, shared data, or\n // middleware from elsewhere under `src/server`, and those edits should\n // still rebuild the Nitro dev server and refresh connected browsers.\n viteServer.watcher.on('add', onNitroSourceChange);\n viteServer.watcher.on('change', onNitroSourceChange);\n viteServer.watcher.on('unlink', onNitroSourceChange);\n\n const apiHandler = async (\n req: IncomingMessage,\n res: ServerResponse,\n ) => {\n // Nitro v3's dev server is fetch-first, so adapt Vite's Node\n // request once and let Nitro respond with a standard Web Response.\n const response = await server.fetch(toWebRequest(req));\n await writeWebResponseToNode(res, response);\n };\n\n if (hasAPIDir) {\n viteServer.middlewares.use(\n (\n req: IncomingMessage,\n res: ServerResponse,\n next: (error?: unknown) => void,\n ) => {\n if (req.url?.startsWith(`${prefix}${apiPrefix}`)) {\n void apiHandler(req, res).catch((error) => next(error));\n return;\n }\n\n next();\n },\n );\n } else {\n viteServer.middlewares.use(\n apiPrefix,\n (\n req: IncomingMessage,\n res: ServerResponse,\n next: (error?: unknown) => void,\n ) => {\n void apiHandler(req, res).catch((error) => next(error));\n },\n );\n }\n\n viteServer.httpServer?.once('listening', () => {\n process.env['ANALOG_HOST'] = !viteServer.config.server.host\n ? 'localhost'\n : (viteServer.config.server.host as string);\n process.env['ANALOG_PORT'] = `${viteServer.config.server.port}`;\n });\n\n // handle upgrades if websockets are enabled\n if (nitroOptions?.experimental?.websocket) {\n debugNitro('experimental websocket upgrade handler enabled');\n viteServer.httpServer?.on('upgrade', server.upgrade);\n }\n\n console.log(\n `\\n\\nThe server endpoints are accessible under the \"${prefix}${apiPrefix}\" path.`,\n );\n }\n },\n\n async closeBundle() {\n if (legacyClientSubBuild) {\n return;\n }\n\n // When builder.buildApp ran, it already handled the full\n // client → SSR → Nitro pipeline. Skip to avoid double work.\n if (environmentBuild) {\n return;\n }\n\n // SSR sub-build — Vite re-enters the plugin with build.ssr;\n // Nitro server assembly happens only after the client pass.\n if (ssrBuild) {\n return;\n }\n\n // Nx executors (and any caller that runs `vite build` without\n // the Environment API) never trigger builder.buildApp, so\n // closeBundle is the only place to drive the SSR + Nitro build.\n if (isBuild) {\n const resolvedClientOutputPath = resolveClientOutputPath(\n clientOutputPath,\n workspaceRoot,\n rootDir,\n config.build?.outDir,\n );\n debugNitro(\n 'closeBundle resolved client output path before legacy SSR build',\n {\n platform: process.platform,\n workspaceRoot,\n rootDir,\n cachedClientOutputPath: clientOutputPath,\n configuredBuildOutDir: config.build?.outDir,\n resolvedClientOutputPath,\n resolvedClientOutputInfo: getPathDebugInfo(\n resolvedClientOutputPath,\n ),\n },\n );\n const indexHtmlPath = resolve(resolvedClientOutputPath, 'index.html');\n if (\n !existsSync(indexHtmlPath) &&\n typeof clientIndexHtml !== 'string'\n ) {\n debugNitro(\n 'closeBundle rebuilding missing client output before SSR/Nitro',\n {\n platform: process.platform,\n workspaceRoot,\n rootDir,\n configuredBuildOutDir: config.build?.outDir,\n resolvedClientOutputPath,\n indexHtmlPath,\n },\n );\n legacyClientSubBuild = true;\n try {\n await buildClientApp(config, options);\n } finally {\n legacyClientSubBuild = false;\n }\n }\n // Capture the client HTML before kicking off the standalone SSR build.\n // This mirrors the successful sequencing from before the closeBundle\n // refactor and avoids depending on the client directory surviving the\n // nested SSR build on Windows.\n registerIndexHtmlVirtual(\n nitroConfig,\n resolvedClientOutputPath,\n clientIndexHtml,\n );\n\n if (options?.ssr) {\n console.log('Building SSR application...');\n await buildSSRApp(config, options);\n debugSsr('closeBundle completed standalone SSR build', {\n ssrBuildDir:\n options?.ssrBuildDir ||\n resolve(workspaceRoot, 'dist', rootDir, 'ssr'),\n clientOutputPathInfo: clientOutputPath\n ? getPathDebugInfo(clientOutputPath)\n : null,\n });\n }\n\n applySsrEntryAlias(nitroConfig, options, workspaceRoot, rootDir);\n debugNitro(\n 'closeBundle resolved client output path before Nitro build',\n {\n platform: process.platform,\n workspaceRoot,\n rootDir,\n cachedClientOutputPath: clientOutputPath,\n configuredBuildOutDir: config.build?.outDir,\n resolvedClientOutputPath,\n resolvedClientOutputInfo: getPathDebugInfo(\n resolvedClientOutputPath,\n ),\n },\n );\n registerIndexHtmlVirtual(\n nitroConfig,\n resolvedClientOutputPath,\n clientIndexHtml,\n );\n\n await buildServer(options, nitroConfig, routeSourceFiles);\n\n if (\n nitroConfig.prerender?.routes?.length &&\n options?.prerender?.sitemap\n ) {\n console.log('Building Sitemap...');\n await buildSitemap(\n config,\n options.prerender.sitemap,\n sitemapRoutes.length\n ? sitemapRoutes\n : nitroConfig.prerender.routes,\n getNitroPublicOutputDir(nitroConfig),\n routeSitemaps,\n { apiPrefix: options?.apiPrefix || 'api' },\n );\n }\n\n console.log(\n `\\n\\nThe '@analogjs/platform' server has been successfully built.`,\n );\n }\n },\n },\n {\n name: '@analogjs/vite-plugin-nitro-api-prefix',\n config() {\n return {\n define: {\n ANALOG_API_PREFIX: `\"${baseURL.substring(1)}${apiPrefix.substring(1)}\"`,\n },\n };\n },\n },\n ];\n}\n\nfunction isEmptyPrerenderRoutes(options?: Options): boolean {\n if (!options || isArrayWithElements(options?.prerender?.routes)) {\n return false;\n }\n return !options.prerender?.routes;\n}\n\nfunction isArrayWithElements<T>(arr: unknown): arr is [T, ...T[]] {\n return !!(Array.isArray(arr) && arr.length);\n}\n\nconst VERCEL_PRESET = 'vercel';\n// Nitro v3 consolidates the old `vercel-edge` preset into `vercel` with\n// fluid compute enabled by default, so a single preset covers both\n// serverless and edge deployments.\nconst withVercelOutputAPI = (\n nitroConfig: NitroConfig | undefined,\n workspaceRoot: string,\n) => ({\n ...nitroConfig,\n preset: nitroConfig?.preset ?? 'vercel',\n vercel: {\n ...nitroConfig?.vercel,\n entryFormat: nitroConfig?.vercel?.entryFormat ?? 'node',\n functions: {\n runtime: nitroConfig?.vercel?.functions?.runtime ?? 'nodejs24.x',\n ...nitroConfig?.vercel?.functions,\n },\n },\n output: {\n ...nitroConfig?.output,\n dir: normalizePath(resolve(workspaceRoot, '.vercel', 'output')),\n publicDir: normalizePath(\n resolve(workspaceRoot, '.vercel', 'output/static'),\n ),\n },\n});\n\n// Nitro v3 uses underscore-separated preset names (e.g. `cloudflare_pages`),\n// but we accept both hyphen and underscore forms for backwards compatibility.\nconst isCloudflarePreset = (buildPreset: string | undefined) =>\n process.env['CF_PAGES'] ||\n (buildPreset &&\n (buildPreset.toLowerCase().includes('cloudflare-pages') ||\n buildPreset.toLowerCase().includes('cloudflare_pages')));\n\nconst withCloudflareOutput = (nitroConfig: NitroConfig | undefined) => ({\n ...nitroConfig,\n output: {\n ...nitroConfig?.output,\n serverDir: '{{ output.publicDir }}/_worker.js',\n },\n});\n\nconst isFirebaseAppHosting = () => !!process.env['NG_BUILD_LOGS_JSON'];\nconst withAppHostingOutput = (nitroConfig: NitroConfig) => {\n let hasOutput = false;\n\n return <NitroConfig>{\n ...nitroConfig,\n serveStatic: true,\n rollupConfig: {\n ...nitroConfig.rollupConfig,\n output: {\n ...nitroConfig.rollupConfig?.output,\n entryFileNames: 'server.mjs',\n },\n },\n hooks: {\n ...nitroConfig.hooks,\n compiled: () => {\n if (!hasOutput) {\n const buildOutput = {\n errors: [],\n warnings: [],\n outputPaths: {\n root: pathToFileURL(`${nitroConfig.output?.dir}`),\n browser: pathToFileURL(`${nitroConfig.output?.publicDir}`),\n server: pathToFileURL(`${nitroConfig.output?.dir}/server`),\n },\n };\n\n // Log the build output for Firebase App Hosting to pick up\n console.log(JSON.stringify(buildOutput, null, 2));\n hasOutput = true;\n }\n },\n },\n };\n};\n\nconst isNetlifyPreset = (buildPreset: string | undefined) =>\n process.env['NETLIFY'] ||\n (buildPreset && buildPreset.toLowerCase().includes('netlify'));\n\nconst withNetlifyOutputAPI = (\n nitroConfig: NitroConfig | undefined,\n workspaceRoot: string,\n) => ({\n ...nitroConfig,\n output: {\n ...nitroConfig?.output,\n dir: normalizePath(resolve(workspaceRoot, 'netlify/functions')),\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vite-plugin-nitro.js","names":[],"sources":["../../../src/lib/vite-plugin-nitro.ts"],"sourcesContent":["import type { NitroConfig, NitroEventHandler, RollupConfig } from 'nitro/types';\nimport { build, createDevServer, createNitro } from 'nitro/builder';\nimport * as vite from 'vite';\nimport type { Plugin, UserConfig, ViteDevServer } from 'vite';\nimport { mergeConfig, normalizePath } from 'vite';\nimport { relative, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { existsSync, readFileSync, readdirSync } from 'node:fs';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nimport { buildServer, isVercelPreset } from './build-server.js';\nimport { buildClientApp, buildSSRApp } from './build-ssr.js';\nimport {\n Options,\n PrerenderContentDir,\n PrerenderContentFile,\n PrerenderRouteConfig,\n PrerenderSitemapConfig,\n} from './options.js';\nimport { pageEndpointsPlugin } from './plugins/page-endpoints.js';\nimport { getPageHandlers } from './utils/get-page-handlers.js';\nimport { buildSitemap } from './build-sitemap.js';\nimport { devServerPlugin } from './plugins/dev-server-plugin.js';\nimport {\n toWebRequest,\n writeWebResponseToNode,\n} from './utils/node-web-bridge.js';\nimport { getMatchingContentFilesWithFrontMatter } from './utils/get-content-files.js';\nimport {\n ssrRenderer,\n clientRenderer,\n apiMiddleware,\n} from './utils/renderers.js';\nimport { getBundleOptionsKey, isRolldown } from './utils/rolldown.js';\nimport { debugNitro, debugSsr } from './utils/debug.js';\n\nfunction createNitroMiddlewareHandler(handler: string): NitroEventHandler {\n return {\n route: '/**',\n handler,\n middleware: true,\n };\n}\n\n/**\n * Creates a `rollup:before` hook that marks specified packages as external\n * in Nitro's bundler config (applied to both the server build and the\n * prerender build).\n *\n * ## Subpath matching (Rolldown compatibility)\n *\n * When `bundlerConfig.external` is an **array**, Rollup automatically\n * prefix-matches entries — `'rxjs'` in the array will also externalise\n * `'rxjs/operators'`, `'rxjs/internal/Observable'`, etc.\n *\n * Rolldown (the default bundler in Nitro v3) does **not** do this. It\n * treats array entries as exact strings. To keep behaviour consistent\n * across both bundlers, the **function** branch already needed explicit\n * subpath matching. We now use the same `isExternal` helper for all\n * branches so that `'rxjs'` reliably matches `'rxjs/operators'`\n * regardless of whether the existing `external` value is a function,\n * array, or absent.\n *\n * Without this, the Nitro prerender build fails on Windows CI with:\n *\n * [RESOLVE_ERROR] Could not resolve 'rxjs/operators'\n */\nfunction createRollupBeforeHook(externalEntries: string[]) {\n const isExternal = (source: string) =>\n externalEntries.some(\n (entry) => source === entry || source.startsWith(entry + '/'),\n );\n\n return (_nitro: unknown, bundlerConfig: RollupConfig) => {\n sanitizeNitroBundlerConfig(_nitro, bundlerConfig);\n\n if (externalEntries.length === 0) {\n return;\n }\n\n const existing = bundlerConfig.external;\n if (!existing) {\n bundlerConfig.external = externalEntries;\n } else if (typeof existing === 'function') {\n bundlerConfig.external = (\n source: string,\n importer: string | undefined,\n isResolved: boolean,\n ) => existing(source, importer, isResolved) || isExternal(source);\n } else if (Array.isArray(existing)) {\n bundlerConfig.external = [...existing, ...externalEntries];\n } else {\n bundlerConfig.external = [existing as string, ...externalEntries];\n }\n };\n}\n\nfunction appendNoExternals(\n noExternals: NitroConfig['noExternals'],\n ...entries: string[]\n): NitroConfig['noExternals'] {\n if (!noExternals) {\n return entries;\n }\n\n return Array.isArray(noExternals)\n ? [...noExternals, ...entries]\n : noExternals;\n}\n\n/**\n * Patches Nitro's internal Rollup/Rolldown bundler config to work around\n * incompatibilities in the Nitro v3 alpha series.\n *\n * Called from the `rollup:before` hook, this function runs against the *final*\n * bundler config that Nitro assembles for its server/prerender builds — it\n * does NOT touch the normal Vite client or SSR environment configs.\n *\n * Each workaround is narrowly scoped and safe to remove once the corresponding\n * upstream Nitro issue is resolved.\n */\nfunction sanitizeNitroBundlerConfig(\n _nitro: unknown,\n bundlerConfig: RollupConfig,\n) {\n const output = bundlerConfig['output'];\n if (!output || Array.isArray(output) || typeof output !== 'object') {\n return;\n }\n\n // ── 1. Remove invalid `output.codeSplitting` ────────────────────────\n //\n // Nitro 3.0.1-alpha.2 adds `output.codeSplitting` to its internal bundler\n // config, but Rolldown rejects it as an unknown key:\n //\n // Warning: Invalid output options (1 issue found)\n // - For the \"codeSplitting\". Invalid key: Expected never but received \"codeSplitting\".\n //\n // Analog never sets this option. Removing it restores default bundler\n // behavior without changing any Analog semantics.\n if ('codeSplitting' in output) {\n delete (output as Record<string, unknown>)['codeSplitting'];\n }\n\n // ── 2. Remove invalid `output.manualChunks` ─────────────────────────\n //\n // Nitro's default config enables manual chunking for node_modules. Under\n // Nitro v3 alpha + Rollup 4.59 this crashes during the prerender rebundle:\n //\n // Cannot read properties of undefined (reading 'included')\n //\n // A single server bundle is acceptable for Analog's use case, so we strip\n // `manualChunks` until the upstream bug is fixed.\n if ('manualChunks' in output) {\n delete (output as Record<string, unknown>)['manualChunks'];\n }\n\n // ── 3. Escape route params in `output.chunkFileNames` ───────────────\n //\n // Nitro's `getChunkName()` derives chunk filenames from route patterns,\n // using its internal `routeToFsPath()` helper to convert route params\n // (`:productId` → `[productId]`) and catch-alls (`**` → `[...]`).\n //\n // Rollup/Rolldown interprets *any* `[token]` in the string returned by a\n // `chunkFileNames` function as a placeholder. Only a handful are valid —\n // `[name]`, `[hash]`, `[format]`, `[ext]` — so route-derived tokens like\n // `[productId]` or `[...]` trigger a build error:\n //\n // \"[productId]\" is not a valid placeholder in the \"output.chunkFileNames\" pattern.\n //\n // We wrap the original function to replace non-standard `[token]` patterns\n // with `_token_`, preserving the intended filename while avoiding the\n // placeholder validation error.\n //\n // Example: `_routes/products/[productId].mjs` → `_routes/products/_productId_.mjs`\n const VALID_ROLLUP_PLACEHOLDER = /^\\[(?:name|hash|format|ext)\\]$/;\n const chunkFileNames = (output as Record<string, unknown>)['chunkFileNames'];\n if (typeof chunkFileNames === 'function') {\n const originalFn = chunkFileNames as (...args: unknown[]) => unknown;\n (output as Record<string, unknown>)['chunkFileNames'] = (\n ...args: unknown[]\n ) => {\n const result = originalFn(...args);\n if (typeof result !== 'string') return result;\n return result.replace(/\\[[^\\]]+\\]/g, (match: string) =>\n VALID_ROLLUP_PLACEHOLDER.test(match)\n ? match\n : `_${match.slice(1, -1)}_`,\n );\n };\n }\n}\n\nfunction resolveClientOutputPath(\n cachedPath: string,\n workspaceRoot: string,\n rootDir: string,\n configuredOutDir: string | undefined,\n) {\n if (cachedPath) {\n debugNitro('resolveClientOutputPath using cached path', {\n cachedPath,\n workspaceRoot,\n rootDir,\n configuredOutDir,\n });\n return cachedPath;\n }\n\n if (configuredOutDir) {\n const resolvedPath = normalizePath(\n resolve(workspaceRoot, rootDir, configuredOutDir),\n );\n debugNitro('resolveClientOutputPath using configured build.outDir', {\n workspaceRoot,\n rootDir,\n configuredOutDir,\n resolvedPath,\n });\n return resolvedPath;\n }\n\n // When no explicit build.outDir is set, the environment build config defaults\n // to `<workspace>/dist/<root>/client` for the client build. The non-SSR\n // (client) and SSR paths must agree on this so that registerIndexHtmlVirtual()\n // and publicAssets read from the directory the client build actually wrote to.\n const resolvedPath = normalizePath(\n resolve(workspaceRoot, 'dist', rootDir, 'client'),\n );\n debugNitro('resolveClientOutputPath using default dist client path', {\n workspaceRoot,\n rootDir,\n configuredOutDir,\n resolvedPath,\n });\n return resolvedPath;\n}\n\nfunction getEnvironmentBuildOutDir(environment: unknown): string | undefined {\n if (!environment || typeof environment !== 'object') {\n return undefined;\n }\n\n const environmentConfig = environment as {\n config?: {\n build?: {\n outDir?: string;\n };\n };\n build?: {\n outDir?: string;\n };\n };\n\n return (\n environmentConfig.config?.build?.outDir ?? environmentConfig.build?.outDir\n );\n}\n\nfunction resolveBuiltClientOutputPath(\n cachedPath: string,\n workspaceRoot: string,\n rootDir: string,\n configuredOutDir: string | undefined,\n environment?: unknown,\n) {\n const environmentOutDir = getEnvironmentBuildOutDir(environment);\n if (environmentOutDir) {\n const resolvedPath = normalizePath(\n resolve(workspaceRoot, rootDir, environmentOutDir),\n );\n debugNitro('resolveBuiltClientOutputPath using environment outDir', {\n cachedPath,\n workspaceRoot,\n rootDir,\n configuredOutDir,\n environmentOutDir,\n resolvedPath,\n });\n return resolvedPath;\n }\n\n debugNitro('resolveBuiltClientOutputPath falling back to shared resolver', {\n cachedPath,\n workspaceRoot,\n rootDir,\n configuredOutDir,\n environmentOutDir,\n });\n return resolveClientOutputPath(\n cachedPath,\n workspaceRoot,\n rootDir,\n configuredOutDir,\n );\n}\n\nfunction getNitroPublicOutputDir(nitroConfig: NitroConfig): string {\n const publicDir = nitroConfig.output?.publicDir;\n if (!publicDir) {\n throw new Error(\n 'Nitro public output directory is required to build the sitemap.',\n );\n }\n\n return publicDir;\n}\n\nfunction readDirectoryEntries(path: string): string[] {\n try {\n return readdirSync(path).sort();\n } catch (error) {\n return [\n `<<unable to read directory: ${error instanceof Error ? error.message : String(error)}>>`,\n ];\n }\n}\n\nfunction getPathDebugInfo(path: string) {\n return {\n rawPath: path,\n normalizedPath: normalizePath(path),\n exists: existsSync(path),\n entries: existsSync(path) ? readDirectoryEntries(path) : [],\n };\n}\n\nfunction assetSourceToString(source: string | Uint8Array) {\n return typeof source === 'string'\n ? source\n : Buffer.from(source).toString('utf8');\n}\n\nfunction captureClientIndexHtmlFromBundle(\n bundle: Record<\n string,\n {\n type?: string;\n fileName?: string;\n source?: string | Uint8Array;\n }\n >,\n hook: 'generateBundle' | 'writeBundle',\n) {\n const indexHtmlAsset = Object.values(bundle).find(\n (chunk) =>\n chunk.type === 'asset' &&\n chunk.fileName === 'index.html' &&\n typeof chunk.source !== 'undefined',\n );\n\n if (!indexHtmlAsset?.source) {\n debugNitro(`client bundle did not expose index.html during ${hook}`, {\n hook,\n bundleKeys: Object.keys(bundle).sort(),\n assetFileNames: Object.values(bundle)\n .filter((chunk) => chunk.type === 'asset')\n .map((chunk) => chunk.fileName)\n .filter(Boolean),\n });\n return undefined;\n }\n\n const indexHtml = assetSourceToString(indexHtmlAsset.source);\n debugNitro(`captured client bundle index.html asset during ${hook}`, {\n hook,\n fileName: indexHtmlAsset.fileName,\n htmlLength: indexHtml.length,\n });\n return indexHtml;\n}\n\n// Nitro only needs the HTML template string. Prefer the on-disk file when it\n// exists, but allow the captured client asset to cover build flows where the\n// client output directory disappears before Nitro assembles its virtual modules.\nfunction registerIndexHtmlVirtual(\n nitroConfig: NitroConfig,\n clientOutputPath: string,\n inlineIndexHtml?: string,\n) {\n const indexHtmlPath = resolve(clientOutputPath, 'index.html');\n debugNitro('registerIndexHtmlVirtual inspecting client output', {\n platform: process.platform,\n cwd: process.cwd(),\n clientOutputPath,\n clientOutputPathInfo: getPathDebugInfo(clientOutputPath),\n indexHtmlPath,\n indexHtmlExists: existsSync(indexHtmlPath),\n hasInlineIndexHtml: typeof inlineIndexHtml === 'string',\n });\n if (!existsSync(indexHtmlPath) && typeof inlineIndexHtml !== 'string') {\n debugNitro('registerIndexHtmlVirtual missing index.html', {\n platform: process.platform,\n cwd: process.cwd(),\n clientOutputPath,\n clientOutputPathInfo: getPathDebugInfo(clientOutputPath),\n indexHtmlPath,\n hasInlineIndexHtml: typeof inlineIndexHtml === 'string',\n nitroOutput: nitroConfig.output,\n nitroPublicAssets: nitroConfig.publicAssets,\n });\n throw new Error(\n `[analog] Client build output not found at ${indexHtmlPath}.\\n` +\n `Ensure the client environment build completed successfully before the server build.`,\n );\n }\n const indexHtml =\n typeof inlineIndexHtml === 'string'\n ? inlineIndexHtml\n : readFileSync(indexHtmlPath, 'utf8');\n debugNitro('registerIndexHtmlVirtual using HTML template source', {\n source:\n typeof inlineIndexHtml === 'string'\n ? 'captured client bundle asset'\n : 'client output index.html file',\n indexHtmlPath,\n });\n nitroConfig.virtual = {\n ...nitroConfig.virtual,\n '#analog/index': `export default ${JSON.stringify(indexHtml)};`,\n };\n}\n\n/**\n * Converts the built SSR entry path into a specifier that Nitro's bundler\n * can resolve, including all relative `./assets/*` chunk imports inside\n * the entry.\n *\n * The returned path **must** be an absolute filesystem path with forward\n * slashes (e.g. `D:/a/analog/dist/apps/blog-app/ssr/main.server.js`).\n * This lets Rollup/Rolldown determine the entry's directory and resolve\n * sibling chunk imports like `./assets/core-DTazUigR.js` correctly.\n *\n * ## Why not pathToFileURL() on Windows?\n *\n * Earlier versions converted the path to a `file:///D:/a/...` URL on\n * Windows, which worked with Nitro v2 + Rollup. Nitro v3 switched its\n * default bundler to Rolldown, and Rolldown does **not** extract the\n * importer directory from `file://` URLs. This caused every relative\n * import inside the SSR entry to fail during the prerender build:\n *\n * [RESOLVE_ERROR] Could not resolve './assets/core-DTazUigR.js'\n * in ../../dist/apps/blog-app/ssr/main.server.js\n *\n * `normalizePath()` (from Vite) simply converts backslashes to forward\n * slashes, which both Rollup and Rolldown handle correctly on all\n * platforms.\n */\nfunction toNitroSsrEntrypointSpecifier(ssrEntryPath: string) {\n return normalizePath(ssrEntryPath);\n}\n\nfunction applySsrEntryAlias(\n nitroConfig: NitroConfig,\n options: Options | undefined,\n workspaceRoot: string,\n rootDir: string,\n): void {\n const ssrOutDir =\n options?.ssrBuildDir || resolve(workspaceRoot, 'dist', rootDir, 'ssr');\n if (options?.ssr || nitroConfig.prerender?.routes?.length) {\n const ssrEntryPath = resolveBuiltSsrEntryPath(ssrOutDir);\n const ssrEntry = toNitroSsrEntrypointSpecifier(ssrEntryPath);\n nitroConfig.alias = {\n ...nitroConfig.alias,\n '#analog/ssr': ssrEntry,\n };\n }\n}\n\nfunction resolveBuiltSsrEntryPath(ssrOutDir: string) {\n const candidatePaths = [\n resolve(ssrOutDir, 'main.server.mjs'),\n resolve(ssrOutDir, 'main.server.js'),\n resolve(ssrOutDir, 'main.server'),\n ];\n\n const ssrEntryPath = candidatePaths.find((candidatePath) =>\n existsSync(candidatePath),\n );\n\n if (!ssrEntryPath) {\n throw new Error(\n `Unable to locate the built SSR entry in \"${ssrOutDir}\". Expected one of: ${candidatePaths.join(\n ', ',\n )}`,\n );\n }\n\n return ssrEntryPath;\n}\n\nexport function nitro(options?: Options, nitroOptions?: NitroConfig): Plugin[] {\n const workspaceRoot = options?.workspaceRoot ?? process.cwd();\n const sourceRoot = options?.sourceRoot ?? 'src';\n let isTest = process.env['NODE_ENV'] === 'test' || !!process.env['VITEST'];\n const baseURL = process.env['NITRO_APP_BASE_URL'] || '';\n const prefix = baseURL ? baseURL.substring(0, baseURL.length - 1) : '';\n const apiPrefix = `/${options?.apiPrefix || 'api'}`;\n const useAPIMiddleware =\n typeof options?.useAPIMiddleware !== 'undefined'\n ? options?.useAPIMiddleware\n : true;\n const viteRolldownOutput = options?.vite?.build?.rolldownOptions?.output;\n // Vite's native build typing allows `output` to be either a single object or\n // an array. Analog only forwards `codeSplitting` into the client environment\n // when there is a single output object to merge into.\n const viteRolldownOutputConfig =\n viteRolldownOutput && !Array.isArray(viteRolldownOutput)\n ? viteRolldownOutput\n : undefined;\n const codeSplitting = viteRolldownOutputConfig?.codeSplitting;\n\n let isBuild = false;\n let isServe = false;\n let ssrBuild = false;\n let config: UserConfig;\n let nitroConfig: NitroConfig;\n let environmentBuild = false;\n let hasAPIDir = false;\n let clientOutputPath = '';\n let clientIndexHtml: string | undefined;\n let legacyClientSubBuild = false;\n const rollupExternalEntries: string[] = [];\n const sitemapRoutes: string[] = [];\n const routeSitemaps: Record<\n string,\n PrerenderSitemapConfig | (() => PrerenderSitemapConfig)\n > = {};\n const routeSourceFiles: Record<string, string> = {};\n let rootDir = workspaceRoot;\n\n return [\n (options?.ssr\n ? devServerPlugin({\n entryServer: options?.entryServer,\n index: options?.index,\n routeRules: nitroOptions?.routeRules,\n i18n: options?.i18n,\n })\n : false) as Plugin,\n {\n name: '@analogjs/vite-plugin-nitro',\n async config(userConfig, { mode, command }) {\n isServe = command === 'serve';\n isBuild = command === 'build';\n ssrBuild = userConfig.build?.ssr === true;\n config = userConfig;\n isTest = isTest ? isTest : mode === 'test';\n rollupExternalEntries.length = 0;\n clientIndexHtml = undefined;\n sitemapRoutes.length = 0;\n for (const key of Object.keys(routeSitemaps)) {\n delete routeSitemaps[key];\n }\n for (const key of Object.keys(routeSourceFiles)) {\n delete routeSourceFiles[key];\n }\n\n const resolvedConfigRoot = config.root\n ? resolve(workspaceRoot, config.root)\n : workspaceRoot;\n rootDir = relative(workspaceRoot, resolvedConfigRoot) || '.';\n hasAPIDir = existsSync(\n resolve(\n workspaceRoot,\n rootDir,\n `${sourceRoot}/server/routes/${options?.apiPrefix || 'api'}`,\n ),\n );\n const buildPreset =\n process.env['BUILD_PRESET'] ??\n (nitroOptions?.preset as string | undefined) ??\n (process.env['VERCEL'] ? 'vercel' : undefined);\n\n const pageHandlers = getPageHandlers({\n workspaceRoot,\n sourceRoot,\n rootDir,\n additionalPagesDirs: options?.additionalPagesDirs,\n hasAPIDir,\n });\n const resolvedClientOutputPath = resolveClientOutputPath(\n clientOutputPath,\n workspaceRoot,\n rootDir,\n config.build?.outDir,\n );\n debugNitro('nitro config resolved client output path', {\n platform: process.platform,\n workspaceRoot,\n configRoot: config.root,\n resolvedConfigRoot,\n rootDir,\n buildOutDir: config.build?.outDir,\n clientOutputPath,\n resolvedClientOutputPath,\n hasEnvironmentConfig: !!config.environments,\n clientEnvironmentOutDir:\n config.environments?.['client'] &&\n typeof config.environments['client'] === 'object' &&\n 'build' in config.environments['client']\n ? (\n config.environments['client'] as {\n build?: { outDir?: string };\n }\n ).build?.outDir\n : undefined,\n });\n\n nitroConfig = {\n rootDir: normalizePath(rootDir),\n preset: buildPreset,\n compatibilityDate: '2025-11-19',\n logLevel: nitroOptions?.logLevel || 0,\n serverDir: normalizePath(`${sourceRoot}/server`),\n scanDirs: [\n normalizePath(`${rootDir}/${sourceRoot}/server`),\n ...(options?.additionalAPIDirs || []).map((dir) =>\n normalizePath(`${workspaceRoot}${dir}`),\n ),\n ],\n output: {\n dir: normalizePath(\n resolve(workspaceRoot, 'dist', rootDir, 'analog'),\n ),\n publicDir: normalizePath(\n resolve(workspaceRoot, 'dist', rootDir, 'analog/public'),\n ),\n },\n buildDir: normalizePath(\n resolve(workspaceRoot, 'dist', rootDir, '.nitro'),\n ),\n typescript: {\n generateTsConfig: false,\n },\n runtimeConfig: {\n apiPrefix: apiPrefix.substring(1),\n prefix,\n },\n // Analog provides its own renderer handler; prevent Nitro v3 from\n // auto-detecting index.html in rootDir and adding a conflicting one.\n renderer: false,\n imports: {\n autoImport: false,\n },\n hooks: {\n 'rollup:before': createRollupBeforeHook(rollupExternalEntries),\n },\n rollupConfig: {\n onwarn(warning) {\n if (\n warning.message.includes('empty chunk') &&\n warning.message.endsWith('.server')\n ) {\n return;\n }\n },\n plugins: [pageEndpointsPlugin()],\n },\n handlers: [\n ...(hasAPIDir\n ? []\n : useAPIMiddleware\n ? [createNitroMiddlewareHandler('#ANALOG_API_MIDDLEWARE')]\n : []),\n ...pageHandlers,\n ],\n routeRules: hasAPIDir\n ? undefined\n : useAPIMiddleware\n ? undefined\n : {\n [`${prefix}${apiPrefix}/**`]: {\n proxy: { to: '/**' },\n },\n },\n virtual: {\n '#ANALOG_SSR_RENDERER': ssrRenderer(),\n '#ANALOG_CLIENT_RENDERER': clientRenderer(),\n ...(hasAPIDir ? {} : { '#ANALOG_API_MIDDLEWARE': apiMiddleware }),\n },\n };\n\n if (isVercelPreset(buildPreset)) {\n nitroConfig = withVercelOutputAPI(nitroConfig, workspaceRoot);\n }\n\n if (isCloudflarePreset(buildPreset)) {\n nitroConfig = withCloudflareOutput(nitroConfig);\n }\n\n if (\n isNetlifyPreset(buildPreset) &&\n rootDir === '.' &&\n !existsSync(resolve(workspaceRoot, 'netlify.toml'))\n ) {\n nitroConfig = withNetlifyOutputAPI(nitroConfig, workspaceRoot);\n }\n\n if (isFirebaseAppHosting()) {\n nitroConfig = withAppHostingOutput(nitroConfig);\n }\n\n if (!ssrBuild && !isTest) {\n // store the client output path for the SSR build config\n clientOutputPath = resolvedClientOutputPath;\n debugNitro(\n 'nitro config cached client output path for later SSR/Nitro build',\n {\n ssrBuild,\n isTest,\n clientOutputPath,\n },\n );\n }\n\n // Start with a clean alias map. #analog/index is registered as a Nitro\n // virtual module after the client build, inlining the HTML template so\n // the server bundle imports it instead of using readFileSync with an\n // absolute path.\n nitroConfig.alias = {};\n\n if (isBuild) {\n nitroConfig.publicAssets = [\n { dir: normalizePath(resolvedClientOutputPath), maxAge: 0 },\n ];\n\n // In Nitro v3, renderer.entry is resolved via resolveModulePath()\n // during options normalization, which requires a real filesystem path.\n // Virtual modules (prefixed with #) can't survive this resolution.\n // Instead, we add the renderer as a catch-all handler directly —\n // this is functionally equivalent to what Nitro does internally\n // (it converts renderer.entry into a { route: '/**', lazy: true }\n // handler), but avoids the filesystem resolution step.\n const rendererHandler = options?.ssr\n ? '#ANALOG_SSR_RENDERER'\n : '#ANALOG_CLIENT_RENDERER';\n nitroConfig.handlers = [\n ...(nitroConfig.handlers || []),\n {\n handler: rendererHandler,\n route: '/**',\n lazy: true,\n },\n ];\n\n if (isEmptyPrerenderRoutes(options)) {\n nitroConfig.prerender = {};\n nitroConfig.prerender.routes = ['/'];\n }\n\n if (options?.prerender) {\n nitroConfig.prerender = nitroConfig.prerender ?? {};\n nitroConfig.prerender.crawlLinks = options?.prerender?.discover;\n\n let routes: (\n | string\n | PrerenderContentDir\n | PrerenderRouteConfig\n | undefined\n )[] = [];\n\n const prerenderRoutes = options?.prerender?.routes;\n const hasExplicitPrerenderRoutes =\n typeof prerenderRoutes === 'function' ||\n Array.isArray(prerenderRoutes);\n if (\n isArrayWithElements<string | PrerenderContentDir>(prerenderRoutes)\n ) {\n routes = prerenderRoutes;\n } else if (typeof prerenderRoutes === 'function') {\n routes = await prerenderRoutes();\n }\n\n const resolvedPrerenderRoutes = routes.reduce<string[]>(\n (prev, current) => {\n if (!current) {\n return prev;\n }\n if (typeof current === 'string') {\n prev.push(current);\n sitemapRoutes.push(current);\n return prev;\n }\n\n if ('route' in current) {\n if (current.sitemap) {\n routeSitemaps[current.route] = current.sitemap;\n }\n\n if (current.outputSourceFile) {\n const sourcePath = resolve(\n workspaceRoot,\n rootDir,\n current.outputSourceFile,\n );\n routeSourceFiles[current.route] = readFileSync(\n sourcePath,\n 'utf8',\n );\n }\n\n prev.push(current.route);\n sitemapRoutes.push(current.route);\n\n // Add the server-side data fetching endpoint URL\n if ('staticData' in current) {\n prev.push(`${apiPrefix}/_analog/pages/${current.route}`);\n }\n\n return prev;\n }\n\n const affectedFiles: PrerenderContentFile[] =\n getMatchingContentFilesWithFrontMatter(\n workspaceRoot,\n rootDir,\n current.contentDir,\n );\n\n affectedFiles.forEach((f) => {\n const result = current.transform(f);\n\n if (result) {\n if (current.sitemap) {\n routeSitemaps[result] =\n current.sitemap && typeof current.sitemap === 'function'\n ? current.sitemap?.(f)\n : current.sitemap;\n }\n\n if (current.outputSourceFile) {\n const sourceContent = current.outputSourceFile(f);\n if (sourceContent) {\n routeSourceFiles[result] = sourceContent;\n }\n }\n\n prev.push(result);\n sitemapRoutes.push(result);\n\n // Add the server-side data fetching endpoint URL\n if ('staticData' in current) {\n prev.push(`${apiPrefix}/_analog/pages/${result}`);\n }\n }\n });\n\n return prev;\n },\n [],\n );\n\n nitroConfig.prerender.routes =\n hasExplicitPrerenderRoutes || resolvedPrerenderRoutes.length\n ? resolvedPrerenderRoutes\n : (nitroConfig.prerender.routes ?? []);\n }\n\n // ── SSR / prerender Nitro config ─────────────────────────────\n //\n // This block configures Nitro for builds that rebundle the SSR\n // entry (main.server.{js,mjs}). That happens in two cases:\n //\n // 1. Full SSR apps — `options.ssr === true`\n // 2. Prerender-only — no runtime SSR, but the prerender build\n // still imports the SSR entry to render static pages.\n //\n // The original gate was `if (ssrBuild)`, which checks the Vite\n // top-level `build.ssr` flag. That works for SSR-only builds but\n // misses two Vite 6+ paths:\n //\n // a. **Vite Environment API (Vite 6+)** — SSR config lives in\n // `environments.ssr.build.ssr`, not `build.ssr`, so\n // `ssrBuild` is always `false`.\n // b. **Prerender-only apps** (e.g. blog-app) — `options.ssr`\n // is `false`, but prerender routes exist and the prerender\n // build still processes the SSR entry.\n //\n // Without this block:\n // - `rxjs` is never externalised → RESOLVE_ERROR in the\n // Nitro prerender build (especially on Windows CI).\n // - `moduleSideEffects` for zone.js is never set → zone.js\n // side-effects may be tree-shaken.\n // - The handlers list is not reassembled with page endpoints\n // + the renderer catch-all.\n //\n // The widened condition covers all supported build paths:\n // - `ssrBuild` → SSR-only build\n // - `options?.ssr` → Environment API SSR\n // - `nitroConfig.prerender?.routes?.length` → prerender-only\n if (\n ssrBuild ||\n options?.ssr ||\n nitroConfig.prerender?.routes?.length\n ) {\n nitroConfig.noExternals = appendNoExternals(\n nitroConfig.noExternals,\n 'es-toolkit',\n );\n\n if (process.platform === 'win32') {\n nitroConfig.noExternals = appendNoExternals(\n nitroConfig.noExternals,\n 'std-env',\n );\n }\n\n rollupExternalEntries.push(\n 'rxjs',\n 'node-fetch-native/dist/polyfill',\n // sharp is a native module with platform-specific binaries\n // (e.g. @img/sharp-darwin-arm64). pnpm creates symlinks for\n // ALL optional platform deps but only installs the matching\n // one — leaving broken symlinks that crash Nitro's bundler\n // with ENOENT during realpath(). Externalizing sharp avoids\n // bundling it entirely; it resolves from node_modules at\n // runtime instead.\n 'sharp',\n );\n\n nitroConfig = {\n ...nitroConfig,\n moduleSideEffects: ['zone.js/node', 'zone.js/fesm2015/zone-node'],\n handlers: [\n ...(hasAPIDir\n ? []\n : useAPIMiddleware\n ? [createNitroMiddlewareHandler('#ANALOG_API_MIDDLEWARE')]\n : []),\n ...pageHandlers,\n // Preserve the renderer catch-all handler added above\n {\n handler: rendererHandler,\n route: '/**',\n lazy: true,\n },\n ],\n };\n }\n }\n\n nitroConfig = mergeConfig(\n nitroConfig,\n nitroOptions as Record<string, any>,\n );\n\n // Only configure Vite 8 environments + builder on the top-level\n // build invocation. When buildApp's builder.build() calls re-enter\n // the config hook, returning environments/builder again would create\n // recursive buildApp invocations — each nesting another client build\n // that re-triggers config, producing an infinite loop of\n // \"building client environment... ✓ 1 modules transformed\".\n //\n // environmentBuild — already inside a buildApp call (recursion guard)\n // ssrBuild — legacy SSR-only sub-build\n // isServe — dev server / Vitest test runner (command: 'serve')\n if (environmentBuild || ssrBuild || isServe) {\n return {};\n }\n\n return {\n environments: {\n client: {\n build: {\n outDir:\n config?.build?.outDir ||\n resolve(workspaceRoot, 'dist', rootDir, 'client'),\n emptyOutDir: true,\n // Forward code-splitting config to Rolldown when running\n // under Vite 8+. `false` disables splitting (inlines all\n // dynamic imports); an object configures chunk groups.\n // The `!== undefined` check ensures `codeSplitting: false`\n // is forwarded correctly (a truthy check would swallow it).\n ...(isRolldown() && codeSplitting !== undefined\n ? {\n rolldownOptions: {\n output: {\n // Preserve any sibling Rolldown output options while\n // overriding just `codeSplitting` for the client build.\n ...viteRolldownOutputConfig,\n codeSplitting,\n },\n },\n }\n : {}),\n },\n },\n ssr: {\n build: {\n ssr: true,\n [getBundleOptionsKey()]: {\n input:\n options?.entryServer ||\n resolve(\n workspaceRoot,\n rootDir,\n `${sourceRoot}/main.server.ts`,\n ),\n },\n outDir:\n options?.ssrBuildDir ||\n resolve(workspaceRoot, 'dist', rootDir, 'ssr'),\n // Preserve the client build output. The client environment is\n // built first and Nitro reads its index.html after SSR finishes.\n emptyOutDir: false,\n },\n },\n },\n builder: {\n sharedPlugins: true,\n buildApp: async (builder) => {\n environmentBuild = true;\n debugNitro('builder.buildApp starting', {\n platform: process.platform,\n workspaceRoot,\n rootDir,\n cachedClientOutputPath: clientOutputPath,\n configuredBuildOutDir: config.build?.outDir,\n clientEnvironmentOutDir: getEnvironmentBuildOutDir(\n builder.environments['client'],\n ),\n ssrEnvironmentOutDir: getEnvironmentBuildOutDir(\n builder.environments['ssr'],\n ),\n });\n\n // Client must complete before SSR — the server build reads the\n // client's index.html via registerIndexHtmlVirtual(). Running\n // them in parallel caused a race on Windows where emptyOutDir\n // could delete client output before the server read it.\n await builder.build(builder.environments['client']);\n const postClientBuildOutputPath = resolveBuiltClientOutputPath(\n clientOutputPath,\n workspaceRoot,\n rootDir,\n config.build?.outDir,\n builder.environments['client'],\n );\n // Capture the client template before any SSR/prerender work runs.\n // On Windows, later phases can leave the client output directory\n // unavailable even though the client build itself succeeded.\n registerIndexHtmlVirtual(\n nitroConfig,\n postClientBuildOutputPath,\n clientIndexHtml,\n );\n debugNitro('builder.buildApp completed client build', {\n postClientBuildOutputPath,\n postClientBuildOutputInfo: getPathDebugInfo(\n postClientBuildOutputPath,\n ),\n postClientBuildIndexHtmlPath: resolve(\n postClientBuildOutputPath,\n 'index.html',\n ),\n postClientBuildIndexHtmlExists: existsSync(\n resolve(postClientBuildOutputPath, 'index.html'),\n ),\n });\n\n if (options?.ssr || nitroConfig.prerender?.routes?.length) {\n debugSsr('builder.buildApp starting SSR build', {\n ssrEnabled: options?.ssr,\n prerenderRoutes: nitroConfig.prerender?.routes,\n });\n await builder.build(builder.environments['ssr']);\n debugSsr('builder.buildApp completed SSR build', {\n ssrOutputPath:\n options?.ssrBuildDir ||\n resolve(workspaceRoot, 'dist', rootDir, 'ssr'),\n });\n }\n\n applySsrEntryAlias(nitroConfig, options, workspaceRoot, rootDir);\n\n const resolvedClientOutputPath = resolveBuiltClientOutputPath(\n clientOutputPath,\n workspaceRoot,\n rootDir,\n config.build?.outDir,\n builder.environments['client'],\n );\n\n nitroConfig.publicAssets = [\n { dir: normalizePath(resolvedClientOutputPath), maxAge: 0 },\n ];\n debugNitro(\n 'builder.buildApp resolved final client output path before Nitro build',\n {\n resolvedClientOutputPath,\n resolvedClientOutputInfo: getPathDebugInfo(\n resolvedClientOutputPath,\n ),\n nitroPublicAssets: nitroConfig.publicAssets,\n },\n );\n\n await buildServer(options, nitroConfig, routeSourceFiles);\n\n if (\n nitroConfig.prerender?.routes?.length &&\n options?.prerender?.sitemap\n ) {\n console.log('Building Sitemap...');\n // sitemap needs to be built after all directories are built\n await buildSitemap(\n config,\n options.prerender.sitemap,\n sitemapRoutes.length\n ? sitemapRoutes\n : nitroConfig.prerender.routes,\n getNitroPublicOutputDir(nitroConfig),\n routeSitemaps,\n { apiPrefix: options?.apiPrefix || 'api' },\n );\n }\n\n console.log(\n `\\n\\nThe '@analogjs/platform' server has been successfully built.`,\n );\n },\n },\n };\n },\n generateBundle(\n _options,\n bundle: Record<\n string,\n {\n type?: string;\n fileName?: string;\n source?: string | Uint8Array;\n }\n >,\n ) {\n if (!isBuild || ssrBuild) {\n return;\n }\n\n clientIndexHtml =\n captureClientIndexHtmlFromBundle(bundle, 'generateBundle') ??\n clientIndexHtml;\n },\n writeBundle(\n _options,\n bundle: Record<\n string,\n {\n type?: string;\n fileName?: string;\n source?: string | Uint8Array;\n }\n >,\n ) {\n if (!isBuild || ssrBuild) {\n return;\n }\n\n clientIndexHtml =\n captureClientIndexHtmlFromBundle(bundle, 'writeBundle') ??\n clientIndexHtml;\n },\n async configureServer(viteServer: ViteDevServer) {\n if (isServe && !isTest) {\n const nitro = await createNitro({\n dev: true,\n // Nitro's Vite builder now rejects `build()` in dev mode, but Analog's\n // dev integration still relies on the builder-driven reload hooks.\n // Force the server worker onto Rollup for this dev-only path.\n builder: 'rollup',\n ...nitroConfig,\n });\n const server = createDevServer(nitro);\n await build(nitro);\n const nitroSourceRoots = [\n normalizePath(\n resolve(workspaceRoot, rootDir, `${sourceRoot}/server`),\n ),\n ...(options?.additionalAPIDirs || []).map((dir) =>\n normalizePath(`${workspaceRoot}${dir}`),\n ),\n ];\n const isNitroSourceFile = (path: string) => {\n const normalizedPath = normalizePath(path);\n return nitroSourceRoots.some(\n (root) =>\n normalizedPath === root ||\n normalizedPath.startsWith(`${root}/`),\n );\n };\n let nitroRebuildPromise: Promise<void> | undefined;\n let nitroRebuildPending = false;\n const rebuildNitroServer = () => {\n if (nitroRebuildPromise) {\n // Coalesce rapid file events so a save that touches multiple server\n // route files results in one follow-up rebuild instead of many.\n nitroRebuildPending = true;\n return nitroRebuildPromise;\n }\n\n nitroRebuildPromise = (async () => {\n do {\n nitroRebuildPending = false;\n // Nitro API routes are not part of Vite's normal client HMR graph,\n // so rebuild the Nitro dev server to pick up handler edits.\n await build(nitro);\n } while (nitroRebuildPending);\n\n // Reload the page after the server rebuild completes so the next\n // request observes the updated API route implementation.\n viteServer.ws.send('analog:debug-full-reload', {\n plugin: 'vite-plugin-nitro',\n reason: 'nitro-server-rebuilt',\n });\n viteServer.ws.send({ type: 'full-reload' });\n })()\n .catch((error: unknown) => {\n viteServer.config.logger.error(\n `[analog] Failed to rebuild Nitro dev server.\\n${error instanceof Error ? error.stack || error.message : String(error)}`,\n );\n })\n .finally(() => {\n nitroRebuildPromise = undefined;\n });\n\n return nitroRebuildPromise;\n };\n const onNitroSourceChange = (path: string) => {\n if (!isNitroSourceFile(path)) {\n return;\n }\n\n void rebuildNitroServer();\n };\n\n // Watch the full Nitro source roots instead of only the API route\n // directory. API handlers often read helper modules, shared data, or\n // middleware from elsewhere under `src/server`, and those edits should\n // still rebuild the Nitro dev server and refresh connected browsers.\n viteServer.watcher.on('add', onNitroSourceChange);\n viteServer.watcher.on('change', onNitroSourceChange);\n viteServer.watcher.on('unlink', onNitroSourceChange);\n\n const apiHandler = async (\n req: IncomingMessage,\n res: ServerResponse,\n ) => {\n // Nitro v3's dev server is fetch-first, so adapt Vite's Node\n // request once and let Nitro respond with a standard Web Response.\n const response = await server.fetch(toWebRequest(req));\n await writeWebResponseToNode(res, response);\n };\n\n if (hasAPIDir) {\n viteServer.middlewares.use(\n (\n req: IncomingMessage,\n res: ServerResponse,\n next: (error?: unknown) => void,\n ) => {\n if (req.url?.startsWith(`${prefix}${apiPrefix}`)) {\n void apiHandler(req, res).catch((error) => next(error));\n return;\n }\n\n next();\n },\n );\n } else {\n viteServer.middlewares.use(\n apiPrefix,\n (\n req: IncomingMessage,\n res: ServerResponse,\n next: (error?: unknown) => void,\n ) => {\n void apiHandler(req, res).catch((error) => next(error));\n },\n );\n }\n\n viteServer.httpServer?.once('listening', () => {\n process.env['ANALOG_HOST'] = !viteServer.config.server.host\n ? 'localhost'\n : (viteServer.config.server.host as string);\n process.env['ANALOG_PORT'] = `${viteServer.config.server.port}`;\n });\n\n // handle upgrades if websockets are enabled\n if (nitroOptions?.experimental?.websocket) {\n debugNitro('experimental websocket upgrade handler enabled');\n viteServer.httpServer?.on('upgrade', server.upgrade);\n }\n\n console.log(\n `\\n\\nThe server endpoints are accessible under the \"${prefix}${apiPrefix}\" path.`,\n );\n }\n },\n\n async closeBundle() {\n if (legacyClientSubBuild) {\n return;\n }\n\n // When builder.buildApp ran, it already handled the full\n // client → SSR → Nitro pipeline. Skip to avoid double work.\n if (environmentBuild) {\n return;\n }\n\n // SSR sub-build — Vite re-enters the plugin with build.ssr;\n // Nitro server assembly happens only after the client pass.\n if (ssrBuild) {\n return;\n }\n\n // Nx executors (and any caller that runs `vite build` without\n // the Environment API) never trigger builder.buildApp, so\n // closeBundle is the only place to drive the SSR + Nitro build.\n if (isBuild) {\n const resolvedClientOutputPath = resolveClientOutputPath(\n clientOutputPath,\n workspaceRoot,\n rootDir,\n config.build?.outDir,\n );\n debugNitro(\n 'closeBundle resolved client output path before legacy SSR build',\n {\n platform: process.platform,\n workspaceRoot,\n rootDir,\n cachedClientOutputPath: clientOutputPath,\n configuredBuildOutDir: config.build?.outDir,\n resolvedClientOutputPath,\n resolvedClientOutputInfo: getPathDebugInfo(\n resolvedClientOutputPath,\n ),\n },\n );\n const indexHtmlPath = resolve(resolvedClientOutputPath, 'index.html');\n if (\n !existsSync(indexHtmlPath) &&\n typeof clientIndexHtml !== 'string'\n ) {\n debugNitro(\n 'closeBundle rebuilding missing client output before SSR/Nitro',\n {\n platform: process.platform,\n workspaceRoot,\n rootDir,\n configuredBuildOutDir: config.build?.outDir,\n resolvedClientOutputPath,\n indexHtmlPath,\n },\n );\n legacyClientSubBuild = true;\n try {\n await buildClientApp(config, options);\n } finally {\n legacyClientSubBuild = false;\n }\n }\n // Capture the client HTML before kicking off the standalone SSR build.\n // This mirrors the successful sequencing from before the closeBundle\n // refactor and avoids depending on the client directory surviving the\n // nested SSR build on Windows.\n registerIndexHtmlVirtual(\n nitroConfig,\n resolvedClientOutputPath,\n clientIndexHtml,\n );\n\n if (options?.ssr) {\n console.log('Building SSR application...');\n await buildSSRApp(config, options);\n debugSsr('closeBundle completed standalone SSR build', {\n ssrBuildDir:\n options?.ssrBuildDir ||\n resolve(workspaceRoot, 'dist', rootDir, 'ssr'),\n clientOutputPathInfo: clientOutputPath\n ? getPathDebugInfo(clientOutputPath)\n : null,\n });\n }\n\n applySsrEntryAlias(nitroConfig, options, workspaceRoot, rootDir);\n debugNitro(\n 'closeBundle resolved client output path before Nitro build',\n {\n platform: process.platform,\n workspaceRoot,\n rootDir,\n cachedClientOutputPath: clientOutputPath,\n configuredBuildOutDir: config.build?.outDir,\n resolvedClientOutputPath,\n resolvedClientOutputInfo: getPathDebugInfo(\n resolvedClientOutputPath,\n ),\n },\n );\n registerIndexHtmlVirtual(\n nitroConfig,\n resolvedClientOutputPath,\n clientIndexHtml,\n );\n\n await buildServer(options, nitroConfig, routeSourceFiles);\n\n if (\n nitroConfig.prerender?.routes?.length &&\n options?.prerender?.sitemap\n ) {\n console.log('Building Sitemap...');\n await buildSitemap(\n config,\n options.prerender.sitemap,\n sitemapRoutes.length\n ? sitemapRoutes\n : nitroConfig.prerender.routes,\n getNitroPublicOutputDir(nitroConfig),\n routeSitemaps,\n { apiPrefix: options?.apiPrefix || 'api' },\n );\n }\n\n console.log(\n `\\n\\nThe '@analogjs/platform' server has been successfully built.`,\n );\n }\n },\n },\n {\n name: '@analogjs/vite-plugin-nitro-api-prefix',\n config() {\n return {\n define: {\n ANALOG_API_PREFIX: `\"${baseURL.substring(1)}${apiPrefix.substring(1)}\"`,\n ...(options?.i18n\n ? {\n ANALOG_I18N_DEFAULT_LOCALE: JSON.stringify(\n options.i18n.defaultLocale,\n ),\n ANALOG_I18N_LOCALES: JSON.stringify(options.i18n.locales),\n }\n : {}),\n },\n };\n },\n },\n ];\n}\n\nfunction isEmptyPrerenderRoutes(options?: Options): boolean {\n if (!options || isArrayWithElements(options?.prerender?.routes)) {\n return false;\n }\n return !options.prerender?.routes;\n}\n\nfunction isArrayWithElements<T>(arr: unknown): arr is [T, ...T[]] {\n return !!(Array.isArray(arr) && arr.length);\n}\n\nconst VERCEL_PRESET = 'vercel';\n// Nitro v3 consolidates the old `vercel-edge` preset into `vercel` with\n// fluid compute enabled by default, so a single preset covers both\n// serverless and edge deployments.\nconst withVercelOutputAPI = (\n nitroConfig: NitroConfig | undefined,\n workspaceRoot: string,\n) => ({\n ...nitroConfig,\n preset: nitroConfig?.preset ?? 'vercel',\n vercel: {\n ...nitroConfig?.vercel,\n entryFormat: nitroConfig?.vercel?.entryFormat ?? 'node',\n functions: {\n runtime: nitroConfig?.vercel?.functions?.runtime ?? 'nodejs24.x',\n ...nitroConfig?.vercel?.functions,\n },\n },\n output: {\n ...nitroConfig?.output,\n dir: normalizePath(resolve(workspaceRoot, '.vercel', 'output')),\n publicDir: normalizePath(\n resolve(workspaceRoot, '.vercel', 'output/static'),\n ),\n },\n});\n\n// Nitro v3 uses underscore-separated preset names (e.g. `cloudflare_pages`),\n// but we accept both hyphen and underscore forms for backwards compatibility.\nconst isCloudflarePreset = (buildPreset: string | undefined) =>\n process.env['CF_PAGES'] ||\n (buildPreset &&\n (buildPreset.toLowerCase().includes('cloudflare-pages') ||\n buildPreset.toLowerCase().includes('cloudflare_pages')));\n\nconst withCloudflareOutput = (nitroConfig: NitroConfig | undefined) => ({\n ...nitroConfig,\n output: {\n ...nitroConfig?.output,\n serverDir: '{{ output.publicDir }}/_worker.js',\n },\n});\n\nconst isFirebaseAppHosting = () => !!process.env['NG_BUILD_LOGS_JSON'];\nconst withAppHostingOutput = (nitroConfig: NitroConfig) => {\n let hasOutput = false;\n\n return <NitroConfig>{\n ...nitroConfig,\n serveStatic: true,\n rollupConfig: {\n ...nitroConfig.rollupConfig,\n output: {\n ...nitroConfig.rollupConfig?.output,\n entryFileNames: 'server.mjs',\n },\n },\n hooks: {\n ...nitroConfig.hooks,\n compiled: () => {\n if (!hasOutput) {\n const buildOutput = {\n errors: [],\n warnings: [],\n outputPaths: {\n root: pathToFileURL(`${nitroConfig.output?.dir}`),\n browser: pathToFileURL(`${nitroConfig.output?.publicDir}`),\n server: pathToFileURL(`${nitroConfig.output?.dir}/server`),\n },\n };\n\n // Log the build output for Firebase App Hosting to pick up\n console.log(JSON.stringify(buildOutput, null, 2));\n hasOutput = true;\n }\n },\n },\n };\n};\n\nconst isNetlifyPreset = (buildPreset: string | undefined) =>\n process.env['NETLIFY'] ||\n (buildPreset && buildPreset.toLowerCase().includes('netlify'));\n\nconst withNetlifyOutputAPI = (\n nitroConfig: NitroConfig | undefined,\n workspaceRoot: string,\n) => ({\n ...nitroConfig,\n output: {\n ...nitroConfig?.output,\n dir: normalizePath(resolve(workspaceRoot, 'netlify/functions')),\n },\n});\n"],"mappings":"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"}
|