@analogjs/vite-plugin-nitro 3.0.0-alpha.64 → 3.0.0-alpha.65
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 +6 -1
- package/src/index.d.ts +8 -0
- package/src/index.js +3 -1
- package/src/index.js.map +1 -1
- package/src/lib/build-sitemap.d.ts +1 -1
- package/src/lib/build-sitemap.js.map +1 -1
- package/src/lib/options.d.ts +4 -9
- package/src/lib/plugins/dev-server-plugin.js +1 -0
- package/src/lib/plugins/dev-server-plugin.js.map +1 -1
- package/src/lib/plugins/server-fn-id-plugin.d.ts +11 -0
- package/src/lib/plugins/server-fn-id-plugin.js +27 -0
- package/src/lib/plugins/server-fn-id-plugin.js.map +1 -0
- package/src/lib/utils/derive-server-fn-id.d.ts +25 -0
- package/src/lib/utils/derive-server-fn-id.js +37 -0
- package/src/lib/utils/derive-server-fn-id.js.map +1 -0
- package/src/lib/utils/get-page-handlers.d.ts +6 -9
- package/src/lib/utils/get-page-handlers.js +7 -10
- package/src/lib/utils/get-page-handlers.js.map +1 -1
- package/src/lib/utils/get-server-fn-handlers.d.ts +28 -0
- package/src/lib/utils/get-server-fn-handlers.js +45 -0
- package/src/lib/utils/get-server-fn-handlers.js.map +1 -0
- package/src/lib/utils/inject-server-fn-ids.d.ts +17 -0
- package/src/lib/utils/inject-server-fn-ids.js +94 -0
- package/src/lib/utils/inject-server-fn-ids.js.map +1 -0
- package/src/lib/utils/register-i18n-watcher.js +27 -0
- package/src/lib/utils/register-i18n-watcher.js.map +1 -0
- package/src/lib/utils/renderers.d.ts +0 -20
- package/src/lib/utils/renderers.js +1 -61
- package/src/lib/utils/renderers.js.map +1 -1
- package/src/lib/utils/server-fn-endpoints.d.ts +41 -0
- package/src/lib/utils/server-fn-endpoints.js +57 -0
- package/src/lib/utils/server-fn-endpoints.js.map +1 -0
- package/src/lib/vite-plugin-nitro.js +33 -31
- 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.65",
|
|
4
4
|
"description": "A Vite plugin for adding a nitro API server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Brandon Roberts <robertsbt@gmail.com>",
|
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
"import": "./src/lib/utils/debug.js",
|
|
16
16
|
"default": "./src/lib/utils/debug.js"
|
|
17
17
|
},
|
|
18
|
+
"./server-fn-id": {
|
|
19
|
+
"types": "./src/lib/utils/derive-server-fn-id.d.ts",
|
|
20
|
+
"import": "./src/lib/utils/derive-server-fn-id.js",
|
|
21
|
+
"default": "./src/lib/utils/derive-server-fn-id.js"
|
|
22
|
+
},
|
|
18
23
|
"./package.json": "./package.json"
|
|
19
24
|
},
|
|
20
25
|
"keywords": [
|
package/src/index.d.ts
CHANGED
|
@@ -2,12 +2,20 @@ 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
4
|
export type { Options, SitemapConfig, SitemapEntry, SitemapExcludeRule, SitemapPriority, SitemapRouteDefinition, SitemapRouteInput, SitemapRouteSource, SitemapTransform, PrerenderSitemapConfig, PrerenderRouteConfig, PrerenderContentDir, PrerenderContentFile, I18nPrerenderOptions } from "./lib/options.js";
|
|
5
|
+
export { deriveServerFnId, serverFnFileId } from "./lib/utils/derive-server-fn-id.js";
|
|
6
|
+
export { injectServerFnIds, type InjectServerFnIdsResult } from "./lib/utils/inject-server-fn-ids.js";
|
|
5
7
|
declare module "nitro/types" {
|
|
6
8
|
interface NitroRouteConfig {
|
|
7
9
|
ssr?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Disable progressive streaming SSR for matching routes (falls back to a
|
|
12
|
+
* buffered render). Only meaningful when `experimental.streaming` is on.
|
|
13
|
+
*/
|
|
14
|
+
streaming?: boolean;
|
|
8
15
|
}
|
|
9
16
|
interface NitroRouteRules {
|
|
10
17
|
ssr?: boolean;
|
|
18
|
+
streaming?: boolean;
|
|
11
19
|
}
|
|
12
20
|
}
|
|
13
21
|
export default nitro;
|
package/src/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { deriveServerFnId, serverFnFileId } from "./lib/utils/derive-server-fn-id.js";
|
|
2
|
+
import { injectServerFnIds } from "./lib/utils/inject-server-fn-ids.js";
|
|
1
3
|
import { debugInstances } from "./lib/utils/debug.js";
|
|
2
4
|
import { nitro } from "./lib/vite-plugin-nitro.js";
|
|
3
5
|
//#region packages/vite-plugin-nitro/src/index.ts
|
|
4
6
|
var src_default = nitro;
|
|
5
7
|
//#endregion
|
|
6
|
-
export { debugInstances, src_default as default, nitro };
|
|
8
|
+
export { debugInstances, src_default as default, deriveServerFnId, injectServerFnIds, nitro, serverFnFileId };
|
|
7
9
|
|
|
8
10
|
//# sourceMappingURL=index.js.map
|
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 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":"
|
|
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\n// Server-function id derivation, shared with @analogjs/platform's client scrub\n// so both sides compute identical opaque ids (single source of truth).\nexport {\n deriveServerFnId,\n serverFnFileId,\n} from './lib/utils/derive-server-fn-id.js';\nexport {\n injectServerFnIds,\n type InjectServerFnIdsResult,\n} from './lib/utils/inject-server-fn-ids.js';\n\ndeclare module 'nitro/types' {\n interface NitroRouteConfig {\n ssr?: boolean;\n /**\n * Disable progressive streaming SSR for matching routes (falls back to a\n * buffered render). Only meaningful when `experimental.streaming` is on.\n */\n streaming?: boolean;\n }\n\n interface NitroRouteRules {\n ssr?: boolean;\n streaming?: boolean;\n }\n}\n\nexport default nitro;\n"],"mappings":";;;;;AA+CA,IAAA,cAAe"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { UserConfig } from "vite";
|
|
2
|
-
import { PrerenderSitemapConfig, SitemapConfig, SitemapEntry } from "./options";
|
|
2
|
+
import { I18nPrerenderOptions, PrerenderSitemapConfig, SitemapConfig, SitemapEntry } from "./options";
|
|
3
3
|
type RouteSitemapConfig = PrerenderSitemapConfig | (() => PrerenderSitemapConfig) | undefined;
|
|
4
4
|
export type PagesJson = SitemapEntry;
|
|
5
5
|
export interface BuildSitemapOptions {
|
|
@@ -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\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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"}
|
|
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 I18nPrerenderOptions,\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
|
@@ -37,17 +37,12 @@ export interface Options {
|
|
|
37
37
|
* Additional API paths to include
|
|
38
38
|
*/
|
|
39
39
|
additionalAPIDirs?: string[];
|
|
40
|
-
apiPrefix?: string;
|
|
41
40
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* requests to / in the production server build.
|
|
45
|
-
*
|
|
46
|
-
* @deprecated
|
|
47
|
-
* Use the src/server/routes/api folder
|
|
48
|
-
* for API routes.
|
|
41
|
+
* Additional directories to scan for `*.server.ts` server-function modules,
|
|
42
|
+
* beyond `<sourceRoot>`. Paths are relative to the workspace root.
|
|
49
43
|
*/
|
|
50
|
-
|
|
44
|
+
additionalServerFnDirs?: string[];
|
|
45
|
+
apiPrefix?: string;
|
|
51
46
|
/**
|
|
52
47
|
* Vite-native build passthrough. Rolldown-only options such as
|
|
53
48
|
* `build.rolldownOptions.output.codeSplitting` are forwarded when present.
|
|
@@ -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 { registerI18nWatcher } from "../utils/register-i18n-watcher.js";
|
|
3
4
|
import { detectLocaleFromRoute, setHtmlLang } from "../utils/i18n-prerender.js";
|
|
4
5
|
import { normalizePath } from "vite";
|
|
5
6
|
import { resolve } from "node:path";
|
|
@@ -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';\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":"
|
|
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 { registerI18nWatcher } from '../utils/register-i18n-watcher.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":";;;;;;;;;;AAwBA,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,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nitro-build transform that stamps the derived id into each `serverFn` config
|
|
3
|
+
* in a `*.server.ts`, so a function registers under the same opaque route the
|
|
4
|
+
* client proxy dispatches to. The SSR app build does the same via the platform
|
|
5
|
+
* plugin; this covers the separate Nitro server graph the dispatch handler pulls
|
|
6
|
+
* the modules into.
|
|
7
|
+
*/
|
|
8
|
+
export declare function serverFnIdPlugin(projectRoot: string): {
|
|
9
|
+
name: string;
|
|
10
|
+
transform(code: string, id: string);
|
|
11
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { serverFnFileId } from "../utils/derive-server-fn-id.js";
|
|
2
|
+
import { injectServerFnIds } from "../utils/inject-server-fn-ids.js";
|
|
3
|
+
//#region packages/vite-plugin-nitro/src/lib/plugins/server-fn-id-plugin.ts
|
|
4
|
+
/**
|
|
5
|
+
* Nitro-build transform that stamps the derived id into each `serverFn` config
|
|
6
|
+
* in a `*.server.ts`, so a function registers under the same opaque route the
|
|
7
|
+
* client proxy dispatches to. The SSR app build does the same via the platform
|
|
8
|
+
* plugin; this covers the separate Nitro server graph the dispatch handler pulls
|
|
9
|
+
* the modules into.
|
|
10
|
+
*/
|
|
11
|
+
function serverFnIdPlugin(projectRoot) {
|
|
12
|
+
return {
|
|
13
|
+
name: "analogjs-server-fn-id",
|
|
14
|
+
transform(code, id) {
|
|
15
|
+
if (!id.endsWith(".server.ts")) return;
|
|
16
|
+
const injected = injectServerFnIds(code, serverFnFileId(id, projectRoot));
|
|
17
|
+
return injected ? {
|
|
18
|
+
code: injected.code,
|
|
19
|
+
map: null
|
|
20
|
+
} : void 0;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { serverFnIdPlugin };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=server-fn-id-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-fn-id-plugin.js","names":[],"sources":["../../../../src/lib/plugins/server-fn-id-plugin.ts"],"sourcesContent":["import { serverFnFileId } from '../utils/derive-server-fn-id.js';\nimport { injectServerFnIds } from '../utils/inject-server-fn-ids.js';\n\n/**\n * Nitro-build transform that stamps the derived id into each `serverFn` config\n * in a `*.server.ts`, so a function registers under the same opaque route the\n * client proxy dispatches to. The SSR app build does the same via the platform\n * plugin; this covers the separate Nitro server graph the dispatch handler pulls\n * the modules into.\n */\nexport function serverFnIdPlugin(projectRoot: string) {\n return {\n name: 'analogjs-server-fn-id',\n transform(code: string, id: string) {\n if (!id.endsWith('.server.ts')) {\n return;\n }\n const injected = injectServerFnIds(code, serverFnFileId(id, projectRoot));\n return injected ? { code: injected.code, map: null } : undefined;\n },\n };\n}\n"],"mappings":";;;;;;;;;;AAUA,SAAgB,iBAAiB,aAAqB;AACpD,QAAO;EACL,MAAM;EACN,UAAU,MAAc,IAAY;AAClC,OAAI,CAAC,GAAG,SAAS,aAAa,CAC5B;GAEF,MAAM,WAAW,kBAAkB,MAAM,eAAe,IAAI,YAAY,CAAC;AACzE,UAAO,WAAW;IAAE,MAAM,SAAS;IAAM,KAAK;IAAM,GAAG,KAAA;;EAE1D"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-function ids are derived, never author-chosen, for security:
|
|
3
|
+
*
|
|
4
|
+
* - **Collision-free.** `hash(fileId + exportName)` is unique per file+export, so
|
|
5
|
+
* two functions can never share a route and hijack each other's dispatch — a
|
|
6
|
+
* real risk with hand-picked ids where two authors independently pick `getUser`.
|
|
7
|
+
* - **Non-enumerable.** The public route is an opaque digest, not a guessable
|
|
8
|
+
* verb like `/_analog/fn/deleteAccount`, shrinking the discoverable surface.
|
|
9
|
+
* - **Not author-controlled.** Authors cannot expose a meaningful or duplicate
|
|
10
|
+
* route by accident; the id is a pure function of location.
|
|
11
|
+
*
|
|
12
|
+
* Both build transforms (server registration + client proxy) MUST derive the
|
|
13
|
+
* same id, so this is the single source of truth for the algorithm and the
|
|
14
|
+
* `fileId` must be the **project-root-relative** POSIX path — identical across
|
|
15
|
+
* the client, SSR, and Nitro builds regardless of absolute checkout location.
|
|
16
|
+
*/
|
|
17
|
+
/** Project-root-relative POSIX module path used as the stable hash input. */
|
|
18
|
+
export declare function serverFnFileId(absPath: string, projectRoot: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Derives the opaque, collision-free id for a server function from its
|
|
21
|
+
* project-root-relative file id and export name. 16 hex chars (64 bits) is far
|
|
22
|
+
* beyond birthday-collision range for any realistic function count while keeping
|
|
23
|
+
* the route short.
|
|
24
|
+
*/
|
|
25
|
+
export declare function deriveServerFnId(fileId: string, exportName: string): string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { relative } from "node:path";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
//#region packages/vite-plugin-nitro/src/lib/utils/derive-server-fn-id.ts
|
|
4
|
+
var toPosix = (p) => p.replace(/\\/g, "/");
|
|
5
|
+
/**
|
|
6
|
+
* Server-function ids are derived, never author-chosen, for security:
|
|
7
|
+
*
|
|
8
|
+
* - **Collision-free.** `hash(fileId + exportName)` is unique per file+export, so
|
|
9
|
+
* two functions can never share a route and hijack each other's dispatch — a
|
|
10
|
+
* real risk with hand-picked ids where two authors independently pick `getUser`.
|
|
11
|
+
* - **Non-enumerable.** The public route is an opaque digest, not a guessable
|
|
12
|
+
* verb like `/_analog/fn/deleteAccount`, shrinking the discoverable surface.
|
|
13
|
+
* - **Not author-controlled.** Authors cannot expose a meaningful or duplicate
|
|
14
|
+
* route by accident; the id is a pure function of location.
|
|
15
|
+
*
|
|
16
|
+
* Both build transforms (server registration + client proxy) MUST derive the
|
|
17
|
+
* same id, so this is the single source of truth for the algorithm and the
|
|
18
|
+
* `fileId` must be the **project-root-relative** POSIX path — identical across
|
|
19
|
+
* the client, SSR, and Nitro builds regardless of absolute checkout location.
|
|
20
|
+
*/
|
|
21
|
+
/** Project-root-relative POSIX module path used as the stable hash input. */
|
|
22
|
+
function serverFnFileId(absPath, projectRoot) {
|
|
23
|
+
return toPosix(relative(projectRoot, absPath));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Derives the opaque, collision-free id for a server function from its
|
|
27
|
+
* project-root-relative file id and export name. 16 hex chars (64 bits) is far
|
|
28
|
+
* beyond birthday-collision range for any realistic function count while keeping
|
|
29
|
+
* the route short.
|
|
30
|
+
*/
|
|
31
|
+
function deriveServerFnId(fileId, exportName) {
|
|
32
|
+
return createHash("sha256").update(`${fileId}#${exportName}`).digest("hex").slice(0, 16);
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { deriveServerFnId, serverFnFileId };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=derive-server-fn-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive-server-fn-id.js","names":[],"sources":["../../../../src/lib/utils/derive-server-fn-id.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { relative } from 'node:path';\n\n// Intentionally dependency-light (node built-ins only): this module is loaded by\n// the client scrub, so it must not drag in vite/nitro. POSIX-normalize inline.\nconst toPosix = (p: string): string => p.replace(/\\\\/g, '/');\n\n/**\n * Server-function ids are derived, never author-chosen, for security:\n *\n * - **Collision-free.** `hash(fileId + exportName)` is unique per file+export, so\n * two functions can never share a route and hijack each other's dispatch — a\n * real risk with hand-picked ids where two authors independently pick `getUser`.\n * - **Non-enumerable.** The public route is an opaque digest, not a guessable\n * verb like `/_analog/fn/deleteAccount`, shrinking the discoverable surface.\n * - **Not author-controlled.** Authors cannot expose a meaningful or duplicate\n * route by accident; the id is a pure function of location.\n *\n * Both build transforms (server registration + client proxy) MUST derive the\n * same id, so this is the single source of truth for the algorithm and the\n * `fileId` must be the **project-root-relative** POSIX path — identical across\n * the client, SSR, and Nitro builds regardless of absolute checkout location.\n */\n\n/** Project-root-relative POSIX module path used as the stable hash input. */\nexport function serverFnFileId(absPath: string, projectRoot: string): string {\n return toPosix(relative(projectRoot, absPath));\n}\n\n/**\n * Derives the opaque, collision-free id for a server function from its\n * project-root-relative file id and export name. 16 hex chars (64 bits) is far\n * beyond birthday-collision range for any realistic function count while keeping\n * the route short.\n */\nexport function deriveServerFnId(fileId: string, exportName: string): string {\n return createHash('sha256')\n .update(`${fileId}#${exportName}`)\n .digest('hex')\n .slice(0, 16);\n}\n"],"mappings":";;;AAKA,IAAM,WAAW,MAAsB,EAAE,QAAQ,OAAO,IAAI;;;;;;;;;;;;;;;;;;AAoB5D,SAAgB,eAAe,SAAiB,aAA6B;AAC3E,QAAO,QAAQ,SAAS,aAAa,QAAQ,CAAC;;;;;;;;AAShD,SAAgB,iBAAiB,QAAgB,YAA4B;AAC3E,QAAO,WAAW,SAAS,CACxB,OAAO,GAAG,OAAO,GAAG,aAAa,CACjC,OAAO,MAAM,CACb,MAAM,GAAG,GAAG"}
|
|
@@ -4,7 +4,6 @@ type GetHandlersArgs = {
|
|
|
4
4
|
sourceRoot: string;
|
|
5
5
|
rootDir: string;
|
|
6
6
|
additionalPagesDirs?: string[];
|
|
7
|
-
hasAPIDir?: boolean;
|
|
8
7
|
};
|
|
9
8
|
/**
|
|
10
9
|
* Discovers and generates Nitro event handlers for server-side page routes.
|
|
@@ -19,7 +18,6 @@ type GetHandlersArgs = {
|
|
|
19
18
|
* @param sourceRoot The source directory path (e.g., 'src')
|
|
20
19
|
* @param rootDir The project root directory relative to workspace
|
|
21
20
|
* @param additionalPagesDirs Optional array of additional pages directories to scan
|
|
22
|
-
* @param hasAPIDir Whether the project has an API directory (affects route prefixing)
|
|
23
21
|
* @returns Array of NitroEventHandler objects with handler paths and route patterns
|
|
24
22
|
*
|
|
25
23
|
* Example usage:
|
|
@@ -28,7 +26,6 @@ type GetHandlersArgs = {
|
|
|
28
26
|
* sourceRoot: 'src',
|
|
29
27
|
* rootDir: 'apps/my-app',
|
|
30
28
|
* additionalPagesDirs: ['/libs/shared/pages'],
|
|
31
|
-
* hasAPIDir: true
|
|
32
29
|
* });
|
|
33
30
|
*
|
|
34
31
|
* Sample discovered file paths:
|
|
@@ -38,10 +35,10 @@ type GetHandlersArgs = {
|
|
|
38
35
|
* - /workspace/apps/my-app/src/app/pages/(auth)/login.server.ts
|
|
39
36
|
*
|
|
40
37
|
* Route transformation examples:
|
|
41
|
-
* - index.server.ts → /_analog/pages/index
|
|
42
|
-
* - users/[id].server.ts → /_analog/pages/users/:id
|
|
43
|
-
* - products/[...slug].server.ts → /_analog/pages/products/**:slug
|
|
44
|
-
* - (auth)/login.server.ts → /_analog/pages/-auth-/login
|
|
38
|
+
* - index.server.ts → /api/_analog/pages/index
|
|
39
|
+
* - users/[id].server.ts → /api/_analog/pages/users/:id
|
|
40
|
+
* - products/[...slug].server.ts → /api/_analog/pages/products/**:slug
|
|
41
|
+
* - (auth)/login.server.ts → /api/_analog/pages/-auth-/login
|
|
45
42
|
*
|
|
46
43
|
* tinyglobby vs fast-glob comparison:
|
|
47
44
|
* - Both support the same glob patterns for file discovery
|
|
@@ -56,7 +53,7 @@ type GetHandlersArgs = {
|
|
|
56
53
|
* 3. Converts [...param] to **:param for catch-all routes
|
|
57
54
|
* 4. Converts (group) to -group- for route groups
|
|
58
55
|
* 5. Converts dots to forward slashes
|
|
59
|
-
* 6. Prefixes with /_analog/pages
|
|
56
|
+
* 6. Prefixes with /api/_analog/pages
|
|
60
57
|
*/
|
|
61
|
-
export declare function getPageHandlers({ workspaceRoot, sourceRoot, rootDir, additionalPagesDirs
|
|
58
|
+
export declare function getPageHandlers({ workspaceRoot, sourceRoot, rootDir, additionalPagesDirs }: GetHandlersArgs): NitroEventHandler[];
|
|
62
59
|
export {};
|
|
@@ -15,7 +15,6 @@ import { globSync } from "tinyglobby";
|
|
|
15
15
|
* @param sourceRoot The source directory path (e.g., 'src')
|
|
16
16
|
* @param rootDir The project root directory relative to workspace
|
|
17
17
|
* @param additionalPagesDirs Optional array of additional pages directories to scan
|
|
18
|
-
* @param hasAPIDir Whether the project has an API directory (affects route prefixing)
|
|
19
18
|
* @returns Array of NitroEventHandler objects with handler paths and route patterns
|
|
20
19
|
*
|
|
21
20
|
* Example usage:
|
|
@@ -24,7 +23,6 @@ import { globSync } from "tinyglobby";
|
|
|
24
23
|
* sourceRoot: 'src',
|
|
25
24
|
* rootDir: 'apps/my-app',
|
|
26
25
|
* additionalPagesDirs: ['/libs/shared/pages'],
|
|
27
|
-
* hasAPIDir: true
|
|
28
26
|
* });
|
|
29
27
|
*
|
|
30
28
|
* Sample discovered file paths:
|
|
@@ -34,10 +32,10 @@ import { globSync } from "tinyglobby";
|
|
|
34
32
|
* - /workspace/apps/my-app/src/app/pages/(auth)/login.server.ts
|
|
35
33
|
*
|
|
36
34
|
* Route transformation examples:
|
|
37
|
-
* - index.server.ts → /_analog/pages/index
|
|
38
|
-
* - users/[id].server.ts → /_analog/pages/users/:id
|
|
39
|
-
* - products/[...slug].server.ts → /_analog/pages/products/**:slug
|
|
40
|
-
* - (auth)/login.server.ts → /_analog/pages/-auth-/login
|
|
35
|
+
* - index.server.ts → /api/_analog/pages/index
|
|
36
|
+
* - users/[id].server.ts → /api/_analog/pages/users/:id
|
|
37
|
+
* - products/[...slug].server.ts → /api/_analog/pages/products/**:slug
|
|
38
|
+
* - (auth)/login.server.ts → /api/_analog/pages/-auth-/login
|
|
41
39
|
*
|
|
42
40
|
* tinyglobby vs fast-glob comparison:
|
|
43
41
|
* - Both support the same glob patterns for file discovery
|
|
@@ -52,17 +50,16 @@ import { globSync } from "tinyglobby";
|
|
|
52
50
|
* 3. Converts [...param] to **:param for catch-all routes
|
|
53
51
|
* 4. Converts (group) to -group- for route groups
|
|
54
52
|
* 5. Converts dots to forward slashes
|
|
55
|
-
* 6. Prefixes with /_analog/pages
|
|
53
|
+
* 6. Prefixes with /api/_analog/pages
|
|
56
54
|
*/
|
|
57
|
-
function getPageHandlers({ workspaceRoot, sourceRoot, rootDir, additionalPagesDirs
|
|
55
|
+
function getPageHandlers({ workspaceRoot, sourceRoot, rootDir, additionalPagesDirs }) {
|
|
58
56
|
return globSync([`${normalizePath(resolve(workspaceRoot, rootDir))}/${sourceRoot}/app/pages/**/*.server.ts`, ...(additionalPagesDirs || []).map((dir) => `${workspaceRoot}${dir}/**/*.server.ts`)], {
|
|
59
57
|
dot: true,
|
|
60
58
|
absolute: true
|
|
61
59
|
}).map((endpointFile) => {
|
|
62
|
-
const route = normalizePath(endpointFile).replace(/^(.*?)\/pages/, "/pages").replace(/\.server\.ts$/, "").replace(/\[\.{3}(.+)\]/g, "**:$1").replace(/\[\.{3}(\w+)\]/g, "**:$1").replace(/\/\((.*?)\)$/, "/-$1-").replace(/\[(\w+)\]/g, ":$1").replace(/\./g, "/");
|
|
63
60
|
return {
|
|
64
61
|
handler: endpointFile,
|
|
65
|
-
route:
|
|
62
|
+
route: `/api/_analog${normalizePath(endpointFile).replace(/^(.*?)\/pages/, "/pages").replace(/\.server\.ts$/, "").replace(/\[\.{3}(.+)\]/g, "**:$1").replace(/\[\.{3}(\w+)\]/g, "**:$1").replace(/\/\((.*?)\)$/, "/-$1-").replace(/\[(\w+)\]/g, ":$1").replace(/\./g, "/")}`,
|
|
66
63
|
lazy: true
|
|
67
64
|
};
|
|
68
65
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-page-handlers.js","names":[],"sources":["../../../../src/lib/utils/get-page-handlers.ts"],"sourcesContent":["import { resolve, relative } from 'node:path';\nimport { globSync } from 'tinyglobby';\n\nimport type { NitroEventHandler } from 'nitro/types';\nimport { normalizePath } from 'vite';\n\ntype GetHandlersArgs = {\n workspaceRoot: string;\n sourceRoot: string;\n rootDir: string;\n additionalPagesDirs?: string[];\n
|
|
1
|
+
{"version":3,"file":"get-page-handlers.js","names":[],"sources":["../../../../src/lib/utils/get-page-handlers.ts"],"sourcesContent":["import { resolve, relative } from 'node:path';\nimport { globSync } from 'tinyglobby';\n\nimport type { NitroEventHandler } from 'nitro/types';\nimport { normalizePath } from 'vite';\n\ntype GetHandlersArgs = {\n workspaceRoot: string;\n sourceRoot: string;\n rootDir: string;\n additionalPagesDirs?: string[];\n};\n\n/**\n * Discovers and generates Nitro event handlers for server-side page routes.\n *\n * This function:\n * 1. Discovers all .server.ts files in the app/pages directory and additional pages directories\n * 2. Converts file paths to route patterns using Angular-style route syntax\n * 3. Generates Nitro event handlers with proper route mapping and lazy loading\n * 4. Handles dynamic route parameters and catch-all routes\n *\n * @param workspaceRoot The workspace root directory path\n * @param sourceRoot The source directory path (e.g., 'src')\n * @param rootDir The project root directory relative to workspace\n * @param additionalPagesDirs Optional array of additional pages directories to scan\n * @returns Array of NitroEventHandler objects with handler paths and route patterns\n *\n * Example usage:\n * const handlers = getPageHandlers({\n * workspaceRoot: '/workspace',\n * sourceRoot: 'src',\n * rootDir: 'apps/my-app',\n * additionalPagesDirs: ['/libs/shared/pages'],\n * });\n *\n * Sample discovered file paths:\n * - /workspace/apps/my-app/src/app/pages/index.server.ts\n * - /workspace/apps/my-app/src/app/pages/users/[id].server.ts\n * - /workspace/apps/my-app/src/app/pages/products/[...slug].server.ts\n * - /workspace/apps/my-app/src/app/pages/(auth)/login.server.ts\n *\n * Route transformation examples:\n * - index.server.ts → /api/_analog/pages/index\n * - users/[id].server.ts → /api/_analog/pages/users/:id\n * - products/[...slug].server.ts → /api/_analog/pages/products/**:slug\n * - (auth)/login.server.ts → /api/_analog/pages/-auth-/login\n *\n * tinyglobby vs fast-glob comparison:\n * - Both support the same glob patterns for file discovery\n * - Both are efficient for finding server-side page files\n * - tinyglobby is now used instead of fast-glob\n * - tinyglobby provides similar functionality with smaller bundle size\n * - tinyglobby's globSync returns absolute paths when absolute: true is set\n *\n * Route transformation rules:\n * 1. Removes .server.ts extension\n * 2. Converts [param] to :param for dynamic routes\n * 3. Converts [...param] to **:param for catch-all routes\n * 4. Converts (group) to -group- for route groups\n * 5. Converts dots to forward slashes\n * 6. Prefixes with /api/_analog/pages\n */\nexport function getPageHandlers({\n workspaceRoot,\n sourceRoot,\n rootDir,\n additionalPagesDirs,\n}: GetHandlersArgs): NitroEventHandler[] {\n // Normalize the project root path for consistent path handling\n const root = normalizePath(resolve(workspaceRoot, rootDir));\n\n // Discover all .server.ts files in the app/pages directory and additional pages directories\n // Pattern: looks for any .server.ts files in app/pages/**/*.server.ts and additional directories\n const endpointFiles: string[] = globSync(\n [\n `${root}/${sourceRoot}/app/pages/**/*.server.ts`,\n ...(additionalPagesDirs || []).map(\n (dir) => `${workspaceRoot}${dir}/**/*.server.ts`,\n ),\n ],\n { dot: true, absolute: true },\n );\n\n // Transform each discovered file into a Nitro event handler\n const handlers: NitroEventHandler[] = endpointFiles.map((endpointFile) => {\n // Normalize the endpoint file path for consistent path handling\n const normalized = normalizePath(endpointFile);\n // Transform the normalized path into a route pattern\n const route = normalized\n .replace(/^(.*?)\\/pages/, '/pages')\n .replace(/\\.server\\.ts$/, '') // Remove .server.ts extension\n .replace(/\\[\\.{3}(.+)\\]/g, '**:$1') // Convert [...param] to **:param (catch-all routes)\n .replace(/\\[\\.{3}(\\w+)\\]/g, '**:$1') // Alternative catch-all pattern\n .replace(/\\/\\((.*?)\\)$/, '/-$1-') // Convert (group) to -group- (route groups)\n .replace(/\\[(\\w+)\\]/g, ':$1') // Convert [param] to :param (dynamic routes)\n .replace(/\\./g, '/'); // Convert dots to forward slashes\n\n // Return Nitro event handler with absolute handler path and transformed route\n return {\n handler: endpointFile,\n route: `/api/_analog${route}`,\n lazy: true,\n };\n });\n\n return handlers;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DA,SAAgB,gBAAgB,EAC9B,eACA,YACA,SACA,uBACuC;AAsCvC,QAhCgC,SAC9B,CACE,GANS,cAAc,QAAQ,eAAe,QAAQ,CAAC,CAM/C,GAAG,WAAW,4BACtB,IAAI,uBAAuB,EAAE,EAAE,KAC5B,QAAQ,GAAG,gBAAgB,IAAI,iBACjC,CACF,EACD;EAAE,KAAK;EAAM,UAAU;EAAM,CAC9B,CAGmD,KAAK,iBAAiB;AAcxE,SAAO;GACL,SAAS;GACT,OAAO,eAdU,cAAc,aAAa,CAG3C,QAAQ,iBAAiB,SAAS,CAClC,QAAQ,iBAAiB,GAAG,CAC5B,QAAQ,kBAAkB,QAAQ,CAClC,QAAQ,mBAAmB,QAAQ,CACnC,QAAQ,gBAAgB,QAAQ,CAChC,QAAQ,cAAc,MAAM,CAC5B,QAAQ,OAAO,IAAI;GAMpB,MAAM;GACP;GACD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type GetServerFnHandlersArgs = {
|
|
2
|
+
workspaceRoot: string;
|
|
3
|
+
sourceRoot: string;
|
|
4
|
+
rootDir: string;
|
|
5
|
+
additionalServerFnDirs?: string[];
|
|
6
|
+
};
|
|
7
|
+
export type ServerFnHandlerModule = {
|
|
8
|
+
/** Absolute, normalized path to a discovered `*.server.ts` module. */
|
|
9
|
+
file: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Discovers the `*.server.ts` modules that may define server functions.
|
|
13
|
+
*
|
|
14
|
+
* Unlike page endpoints (one Nitro handler per file), server functions all
|
|
15
|
+
* share a single `/_analog/fn/:id` dispatch route. The discovered modules are
|
|
16
|
+
* imported for their registration side-effects — each `serverFn(...)` call
|
|
17
|
+
* registers itself into the server-side registry at import time — after which
|
|
18
|
+
* dispatch looks up the requested function by id.
|
|
19
|
+
*
|
|
20
|
+
* Scope is `<projectRoot>/<sourceRoot>/**\/*.server.ts` because the RFC allows a
|
|
21
|
+
* server function to live in any `.server.ts` module, including existing page
|
|
22
|
+
* server files. Files that define no server function simply register nothing.
|
|
23
|
+
* Angular SSR config (`app.config.server.ts`) is excluded — it is not a route or
|
|
24
|
+
* function module and must not be pulled into the dispatch bundle.
|
|
25
|
+
*
|
|
26
|
+
* @returns discovered modules, de-duplicated and sorted for deterministic output
|
|
27
|
+
*/
|
|
28
|
+
export declare function getServerFnHandlers({ workspaceRoot, sourceRoot, rootDir, additionalServerFnDirs }: GetServerFnHandlersArgs): ServerFnHandlerModule[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { normalizePath } from "vite";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { globSync } from "tinyglobby";
|
|
4
|
+
//#region packages/vite-plugin-nitro/src/lib/utils/get-server-fn-handlers.ts
|
|
5
|
+
/**
|
|
6
|
+
* `*.server.ts` files that are matched by the glob but are never server-function
|
|
7
|
+
* hosts, so importing them for registration side-effects would be wrong.
|
|
8
|
+
*
|
|
9
|
+
* The SSR entries (`main.server.ts`, `main-cf.server.ts`, …) sit at the top of
|
|
10
|
+
* the source root and bootstrap the whole Angular application; importing one
|
|
11
|
+
* would pull the entire app into the dispatch bundle. They are matched by
|
|
12
|
+
* directory rather than by name so a page named `main.server.ts` still counts.
|
|
13
|
+
*/
|
|
14
|
+
var EXCLUDED_SERVER_FILES = [/\/app\.config\.server\.ts$/];
|
|
15
|
+
/**
|
|
16
|
+
* Discovers the `*.server.ts` modules that may define server functions.
|
|
17
|
+
*
|
|
18
|
+
* Unlike page endpoints (one Nitro handler per file), server functions all
|
|
19
|
+
* share a single `/_analog/fn/:id` dispatch route. The discovered modules are
|
|
20
|
+
* imported for their registration side-effects — each `serverFn(...)` call
|
|
21
|
+
* registers itself into the server-side registry at import time — after which
|
|
22
|
+
* dispatch looks up the requested function by id.
|
|
23
|
+
*
|
|
24
|
+
* Scope is `<projectRoot>/<sourceRoot>/**\/*.server.ts` because the RFC allows a
|
|
25
|
+
* server function to live in any `.server.ts` module, including existing page
|
|
26
|
+
* server files. Files that define no server function simply register nothing.
|
|
27
|
+
* Angular SSR config (`app.config.server.ts`) is excluded — it is not a route or
|
|
28
|
+
* function module and must not be pulled into the dispatch bundle.
|
|
29
|
+
*
|
|
30
|
+
* @returns discovered modules, de-duplicated and sorted for deterministic output
|
|
31
|
+
*/
|
|
32
|
+
function getServerFnHandlers({ workspaceRoot, sourceRoot, rootDir, additionalServerFnDirs }) {
|
|
33
|
+
const root = normalizePath(resolve(workspaceRoot, rootDir));
|
|
34
|
+
const files = globSync([`${root}/${sourceRoot}/**/*.server.ts`, ...(additionalServerFnDirs || []).map((dir) => `${workspaceRoot}${dir}/**/*.server.ts`)], {
|
|
35
|
+
dot: true,
|
|
36
|
+
absolute: true
|
|
37
|
+
}).map((file) => normalizePath(file));
|
|
38
|
+
const sourceRootDir = `${root}/${sourceRoot}/`;
|
|
39
|
+
const seen = /* @__PURE__ */ new Set();
|
|
40
|
+
return files.filter((file) => !EXCLUDED_SERVER_FILES.some((re) => re.test(file))).filter((file) => !file.startsWith(sourceRootDir) || file.slice(sourceRootDir.length).includes("/")).filter((file) => seen.has(file) ? false : (seen.add(file), true)).sort().map((file) => ({ file }));
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { getServerFnHandlers };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=get-server-fn-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-server-fn-handlers.js","names":[],"sources":["../../../../src/lib/utils/get-server-fn-handlers.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { globSync } from 'tinyglobby';\n\nimport { normalizePath } from 'vite';\n\nexport type GetServerFnHandlersArgs = {\n workspaceRoot: string;\n sourceRoot: string;\n rootDir: string;\n additionalServerFnDirs?: string[];\n};\n\nexport type ServerFnHandlerModule = {\n /** Absolute, normalized path to a discovered `*.server.ts` module. */\n file: string;\n};\n\n/**\n * `*.server.ts` files that are matched by the glob but are never server-function\n * hosts, so importing them for registration side-effects would be wrong.\n *\n * The SSR entries (`main.server.ts`, `main-cf.server.ts`, …) sit at the top of\n * the source root and bootstrap the whole Angular application; importing one\n * would pull the entire app into the dispatch bundle. They are matched by\n * directory rather than by name so a page named `main.server.ts` still counts.\n */\nconst EXCLUDED_SERVER_FILES: RegExp[] = [/\\/app\\.config\\.server\\.ts$/];\n\n/**\n * Discovers the `*.server.ts` modules that may define server functions.\n *\n * Unlike page endpoints (one Nitro handler per file), server functions all\n * share a single `/_analog/fn/:id` dispatch route. The discovered modules are\n * imported for their registration side-effects — each `serverFn(...)` call\n * registers itself into the server-side registry at import time — after which\n * dispatch looks up the requested function by id.\n *\n * Scope is `<projectRoot>/<sourceRoot>/**\\/*.server.ts` because the RFC allows a\n * server function to live in any `.server.ts` module, including existing page\n * server files. Files that define no server function simply register nothing.\n * Angular SSR config (`app.config.server.ts`) is excluded — it is not a route or\n * function module and must not be pulled into the dispatch bundle.\n *\n * @returns discovered modules, de-duplicated and sorted for deterministic output\n */\nexport function getServerFnHandlers({\n workspaceRoot,\n sourceRoot,\n rootDir,\n additionalServerFnDirs,\n}: GetServerFnHandlersArgs): ServerFnHandlerModule[] {\n const root = normalizePath(resolve(workspaceRoot, rootDir));\n\n const files = globSync(\n [\n `${root}/${sourceRoot}/**/*.server.ts`,\n ...(additionalServerFnDirs || []).map(\n (dir) => `${workspaceRoot}${dir}/**/*.server.ts`,\n ),\n ],\n { dot: true, absolute: true },\n ).map((file) => normalizePath(file));\n\n const sourceRootDir = `${root}/${sourceRoot}/`;\n const seen = new Set<string>();\n return files\n .filter((file) => !EXCLUDED_SERVER_FILES.some((re) => re.test(file)))\n .filter(\n (file) =>\n !file.startsWith(sourceRootDir) ||\n file.slice(sourceRootDir.length).includes('/'),\n )\n .filter((file) => (seen.has(file) ? false : (seen.add(file), true)))\n .sort()\n .map((file) => ({ file }));\n}\n"],"mappings":";;;;;;;;;;;;;AA0BA,IAAM,wBAAkC,CAAC,6BAA6B;;;;;;;;;;;;;;;;;;AAmBtE,SAAgB,oBAAoB,EAClC,eACA,YACA,SACA,0BACmD;CACnD,MAAM,OAAO,cAAc,QAAQ,eAAe,QAAQ,CAAC;CAE3D,MAAM,QAAQ,SACZ,CACE,GAAG,KAAK,GAAG,WAAW,kBACtB,IAAI,0BAA0B,EAAE,EAAE,KAC/B,QAAQ,GAAG,gBAAgB,IAAI,iBACjC,CACF,EACD;EAAE,KAAK;EAAM,UAAU;EAAM,CAC9B,CAAC,KAAK,SAAS,cAAc,KAAK,CAAC;CAEpC,MAAM,gBAAgB,GAAG,KAAK,GAAG,WAAW;CAC5C,MAAM,uBAAO,IAAI,KAAa;AAC9B,QAAO,MACJ,QAAQ,SAAS,CAAC,sBAAsB,MAAM,OAAO,GAAG,KAAK,KAAK,CAAC,CAAC,CACpE,QACE,SACC,CAAC,KAAK,WAAW,cAAc,IAC/B,KAAK,MAAM,cAAc,OAAO,CAAC,SAAS,IAAI,CACjD,CACA,QAAQ,SAAU,KAAK,IAAI,KAAK,GAAG,SAAS,KAAK,IAAI,KAAK,EAAE,MAAO,CACnE,MAAM,CACN,KAAK,UAAU,EAAE,MAAM,EAAE"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface InjectServerFnIdsResult {
|
|
2
|
+
code: string;
|
|
3
|
+
ids: {
|
|
4
|
+
name: string;
|
|
5
|
+
id: string;
|
|
6
|
+
}[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Server/SSR-build transform: injects the derived `id` into each
|
|
10
|
+
* `export const NAME = serverFn(config, handler)` so the function registers
|
|
11
|
+
* under the same opaque id the client proxy dispatches to. Unlike the client
|
|
12
|
+
* scrub this keeps the handler and every other statement intact — it only edits
|
|
13
|
+
* the config object — so the server module still runs the real implementation.
|
|
14
|
+
*
|
|
15
|
+
* Returns `null` when the module defines no server function.
|
|
16
|
+
*/
|
|
17
|
+
export declare function injectServerFnIds(code: string, fileId: string): InjectServerFnIdsResult | null;
|