@analogjs/vite-plugin-nitro 3.0.0-alpha.9 → 3.0.0-alpha.91
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 +30 -6
- package/src/index.d.ts +19 -9
- package/src/index.js +4 -1
- package/src/index.js.map +1 -1
- package/src/lib/build-server.d.ts +2 -2
- package/src/lib/build-server.js +1 -62
- package/src/lib/build-server.js.map +1 -1
- package/src/lib/build-sitemap.d.ts +23 -9
- package/src/lib/build-sitemap.js +118 -37
- package/src/lib/build-sitemap.js.map +1 -1
- package/src/lib/build-ssr.d.ts +3 -2
- package/src/lib/build-ssr.js +12 -2
- package/src/lib/build-ssr.js.map +1 -1
- package/src/lib/hooks/post-rendering-hook.d.ts +1 -1
- package/src/lib/hooks/post-rendering-hook.js.map +1 -1
- package/src/lib/options.d.ts +148 -114
- package/src/lib/plugins/dev-server-plugin.d.ts +3 -3
- package/src/lib/plugins/dev-server-plugin.js +9 -1
- package/src/lib/plugins/dev-server-plugin.js.map +1 -1
- package/src/lib/plugins/page-endpoints.d.ts +5 -5
- package/src/lib/plugins/page-endpoints.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/debug.d.ts +5 -0
- package/src/lib/utils/debug.js +15 -0
- package/src/lib/utils/debug.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-content-files.d.ts +55 -55
- package/src/lib/utils/get-content-files.js +12 -5
- package/src/lib/utils/get-content-files.js.map +1 -1
- package/src/lib/utils/get-page-handlers.d.ts +55 -58
- 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 +29 -0
- package/src/lib/utils/get-server-fn-handlers.js +66 -0
- package/src/lib/utils/get-server-fn-handlers.js.map +1 -0
- 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/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/load-esm.d.ts +18 -18
- package/src/lib/utils/node-web-bridge.d.ts +1 -1
- package/src/lib/utils/node-web-bridge.js +18 -2
- package/src/lib/utils/node-web-bridge.js.map +1 -1
- package/src/lib/utils/register-dev-middleware.d.ts +12 -12
- package/src/lib/utils/register-dev-middleware.js.map +1 -1
- package/src/lib/utils/register-i18n-watcher.d.ts +15 -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 +29 -49
- package/src/lib/utils/renderers.js +5 -52
- package/src/lib/utils/renderers.js.map +1 -1
- package/src/lib/utils/rolldown.d.ts +1 -1
- package/src/lib/utils/rolldown.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.d.ts +3 -3
- package/src/lib/vite-plugin-nitro.js +406 -53
- package/src/lib/vite-plugin-nitro.js.map +1 -1
package/src/lib/options.d.ts
CHANGED
|
@@ -1,129 +1,163 @@
|
|
|
1
|
-
import type { PrerenderRoute } from
|
|
2
|
-
import type { UserConfig } from
|
|
1
|
+
import type { PrerenderRoute } from "nitro/types";
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
* `build.rolldownOptions.output.codeSplitting` are forwarded when present.
|
|
43
|
-
*/
|
|
44
|
-
vite?: {
|
|
45
|
-
build?: UserConfig['build'];
|
|
46
|
-
};
|
|
15
|
+
ssr?: boolean;
|
|
16
|
+
ssrBuildDir?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Prerender the static pages without producing the server output.
|
|
19
|
+
*/
|
|
20
|
+
static?: boolean;
|
|
21
|
+
prerender?: PrerenderOptions;
|
|
22
|
+
entryServer?: string;
|
|
23
|
+
index?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Relative path to source files. Default is 'src'.
|
|
26
|
+
*/
|
|
27
|
+
sourceRoot?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Absolute path to workspace root. Default is 'process.cwd()'
|
|
30
|
+
*/
|
|
31
|
+
workspaceRoot?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Additional page paths to include
|
|
34
|
+
*/
|
|
35
|
+
additionalPagesDirs?: string[];
|
|
36
|
+
/**
|
|
37
|
+
* Additional API paths to include
|
|
38
|
+
*/
|
|
39
|
+
additionalAPIDirs?: string[];
|
|
40
|
+
/**
|
|
41
|
+
* Additional directories to scan for `*.server.ts` server-function modules,
|
|
42
|
+
* beyond `<sourceRoot>`. Paths are relative to the workspace root.
|
|
43
|
+
*/
|
|
44
|
+
additionalServerFnDirs?: string[];
|
|
45
|
+
apiPrefix?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Vite-native build passthrough. Rolldown-only options such as
|
|
48
|
+
* `build.rolldownOptions.output.codeSplitting` are forwarded when present.
|
|
49
|
+
*/
|
|
50
|
+
vite?: {
|
|
51
|
+
build?: UserConfig["build"];
|
|
52
|
+
};
|
|
47
53
|
}
|
|
48
54
|
export interface PrerenderOptions {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Add additional routes to prerender through crawling page links.
|
|
57
|
+
*/
|
|
58
|
+
discover?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* List of routes to prerender resolved statically or dynamically.
|
|
61
|
+
*/
|
|
62
|
+
routes?: (string | PrerenderContentDir | PrerenderRouteConfig)[] | (() => Promise<(string | PrerenderContentDir | PrerenderRouteConfig | undefined)[]>);
|
|
63
|
+
sitemap?: SitemapConfig;
|
|
64
|
+
/** List of functions that run for each route after pre-rendering is complete. */
|
|
65
|
+
postRenderingHooks?: ((routes: PrerenderRoute) => Promise<void>)[];
|
|
66
|
+
}
|
|
67
|
+
export type SitemapPriority = number | `${number}`;
|
|
68
|
+
export interface SitemapRouteDefinition {
|
|
69
|
+
route: string;
|
|
70
|
+
lastmod?: string;
|
|
71
|
+
changefreq?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
72
|
+
priority?: SitemapPriority;
|
|
73
|
+
}
|
|
74
|
+
export interface SitemapEntry extends SitemapRouteDefinition {
|
|
75
|
+
loc: string;
|
|
60
76
|
}
|
|
77
|
+
export type SitemapRouteInput = string | SitemapRouteDefinition | undefined;
|
|
78
|
+
export type SitemapRouteSource = SitemapRouteInput[] | (() => Promise<SitemapRouteInput[]>);
|
|
79
|
+
export type SitemapExcludeRule = string | RegExp | ((entry: SitemapEntry) => boolean | Promise<boolean>);
|
|
80
|
+
export type SitemapTransform = (entry: SitemapEntry) => SitemapRouteDefinition | false | Promise<SitemapRouteDefinition | false>;
|
|
61
81
|
export interface SitemapConfig {
|
|
62
|
-
|
|
82
|
+
host: string;
|
|
83
|
+
include?: SitemapRouteSource;
|
|
84
|
+
exclude?: SitemapExcludeRule[];
|
|
85
|
+
defaults?: PrerenderSitemapConfig;
|
|
86
|
+
transform?: SitemapTransform;
|
|
63
87
|
}
|
|
64
88
|
export interface PrerenderContentDir {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
/**
|
|
90
|
+
* The directory where files should be grabbed from.
|
|
91
|
+
* @example `/src/contents/blog`
|
|
92
|
+
*/
|
|
93
|
+
contentDir: string;
|
|
94
|
+
/**
|
|
95
|
+
* Transform the matching content files path into a route.
|
|
96
|
+
* The function is called for each matching content file within the specified contentDir.
|
|
97
|
+
* @param file information of the matching file (`path`, `name`, `extension`, `attributes`, `content`)
|
|
98
|
+
* @returns a string with the route should be returned (e. g. `/blog/<slug>`) or the value `false`, when the route should not be prerendered.
|
|
99
|
+
*/
|
|
100
|
+
transform: (file: PrerenderContentFile) => string | false;
|
|
101
|
+
/**
|
|
102
|
+
* Customize the sitemap definition for the prerendered route
|
|
103
|
+
*
|
|
104
|
+
* https://www.sitemaps.org/protocol.html#xmlTagDefinitions
|
|
105
|
+
*/
|
|
106
|
+
sitemap?: PrerenderSitemapConfig | ((file: PrerenderContentFile) => PrerenderSitemapConfig);
|
|
107
|
+
/**
|
|
108
|
+
* Output the source markdown content alongside the prerendered route.
|
|
109
|
+
* The source file will be accessible at the route path with a .md extension.
|
|
110
|
+
* @param file information of the matching file including its content
|
|
111
|
+
* @returns the markdown content string to output, or `false` to skip outputting for this file
|
|
112
|
+
*/
|
|
113
|
+
outputSourceFile?: (file: PrerenderContentFile) => string | false;
|
|
114
|
+
/**
|
|
115
|
+
* Recurse into subdirectories of `contentDir` when discovering files.
|
|
116
|
+
* When enabled, the matching file's directory relative to `contentDir`
|
|
117
|
+
* is exposed via `PrerenderContentFile.relativePath` so transforms can
|
|
118
|
+
* disambiguate identically-named files across subdirectories.
|
|
119
|
+
* @default false
|
|
120
|
+
*/
|
|
121
|
+
recursive?: boolean;
|
|
90
122
|
}
|
|
91
123
|
/**
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
124
|
+
* @param path the path to the content file
|
|
125
|
+
* @param name the basename of the matching content file without the file extension
|
|
126
|
+
* @param extension the file extension
|
|
127
|
+
* @param attributes the frontmatter attributes extracted from the frontmatter section of the file
|
|
128
|
+
* @param content the raw file content including frontmatter
|
|
129
|
+
* @param relativePath when `recursive` is enabled, the directory of the file relative to `contentDir` (empty string for files at the top level)
|
|
130
|
+
* @returns a string with the route should be returned (e. g. `/blog/<slug>`) or the value `false`, when the route should not be prerendered.
|
|
131
|
+
*/
|
|
99
132
|
export interface PrerenderContentFile {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
133
|
+
path: string;
|
|
134
|
+
attributes: Record<string, any>;
|
|
135
|
+
name: string;
|
|
136
|
+
extension: string;
|
|
137
|
+
content: string;
|
|
138
|
+
relativePath?: string;
|
|
105
139
|
}
|
|
106
140
|
export interface PrerenderSitemapConfig {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
141
|
+
lastmod?: string;
|
|
142
|
+
changefreq?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
143
|
+
priority?: SitemapPriority;
|
|
110
144
|
}
|
|
111
145
|
export interface PrerenderRouteConfig {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
146
|
+
route: string;
|
|
147
|
+
/**
|
|
148
|
+
* Customize the sitemap definition for the prerendered route
|
|
149
|
+
*
|
|
150
|
+
* https://www.sitemaps.org/protocol.html#xmlTagDefinitions
|
|
151
|
+
*/
|
|
152
|
+
sitemap?: PrerenderSitemapConfig | (() => PrerenderSitemapConfig);
|
|
153
|
+
/**
|
|
154
|
+
* Prerender static data for the prerendered route
|
|
155
|
+
*/
|
|
156
|
+
staticData?: boolean;
|
|
157
|
+
/**
|
|
158
|
+
* Path to the source markdown file to output alongside the prerendered route.
|
|
159
|
+
* The source file will be accessible at the route path with a .md extension.
|
|
160
|
+
* @example 'src/content/overview.md'
|
|
161
|
+
*/
|
|
162
|
+
outputSourceFile?: string;
|
|
129
163
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Plugin } from
|
|
2
|
-
import { Options } from
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
import { Options } from "../options.js";
|
|
3
3
|
type ServerOptions = Options & {
|
|
4
|
-
|
|
4
|
+
routeRules?: Record<string, any> | undefined;
|
|
5
5
|
};
|
|
6
6
|
export declare function devServerPlugin(options: ServerOptions): Plugin;
|
|
7
7
|
export {};
|
|
@@ -1,5 +1,7 @@
|
|
|
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";
|
|
4
|
+
import { detectLocaleFromRoute, setHtmlLang } from "../utils/i18n-prerender.js";
|
|
3
5
|
import { normalizePath } from "vite";
|
|
4
6
|
import { resolve } from "node:path";
|
|
5
7
|
import { readFileSync } from "node:fs";
|
|
@@ -29,6 +31,7 @@ function devServerPlugin(options) {
|
|
|
29
31
|
return async () => {
|
|
30
32
|
remove_html_middlewares(viteServer.middlewares);
|
|
31
33
|
registerDevServerMiddleware(root, sourceRoot, viteServer);
|
|
34
|
+
if (options.i18n) registerI18nWatcher(viteServer);
|
|
32
35
|
viteServer.middlewares.use(async (req, res) => {
|
|
33
36
|
let template = readFileSync(resolve(viteServer.config.root, index), "utf-8");
|
|
34
37
|
template = await viteServer.transformIndexHtml(req.originalUrl, template);
|
|
@@ -48,8 +51,13 @@ function devServerPlugin(options) {
|
|
|
48
51
|
await writeWebResponseToNode(res, result);
|
|
49
52
|
return;
|
|
50
53
|
}
|
|
54
|
+
let html = typeof result === "string" ? result : template;
|
|
55
|
+
if (options.i18n) {
|
|
56
|
+
const locale = detectLocaleFromRoute(req.originalUrl, options.i18n);
|
|
57
|
+
html = setHtmlLang(html, locale);
|
|
58
|
+
}
|
|
51
59
|
res.setHeader("Content-Type", "text/html");
|
|
52
|
-
res.end(
|
|
60
|
+
res.end(html);
|
|
53
61
|
} catch (e) {
|
|
54
62
|
viteServer.ssrFixStacktrace(e);
|
|
55
63
|
res.statusCode = 500;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-server-plugin.js","names":[],"sources":["
|
|
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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare function pageEndpointsPlugin(): {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
name: string;
|
|
3
|
+
transform(_code: string, id: string): Promise<{
|
|
4
|
+
code: string;
|
|
5
|
+
map: null;
|
|
6
|
+
} | undefined>;
|
|
7
7
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-endpoints.js","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"page-endpoints.js","names":[],"sources":["../../../../src/lib/plugins/page-endpoints.ts"],"sourcesContent":["import { parseSync } from 'oxc-parser';\nimport { normalizePath } from 'vite';\nimport { SERVER_FETCH_FACTORY_SNIPPET } from '../utils/renderers.js';\n\nexport function pageEndpointsPlugin() {\n return {\n name: 'analogjs-vite-plugin-nitro-rollup-page-endpoint',\n async transform(\n _code: string,\n id: string,\n ): Promise<{ code: string; map: null } | undefined> {\n if (normalizePath(id).includes('/pages/') && id.endsWith('.server.ts')) {\n const result = parseSync(id, _code, {\n sourceType: 'module',\n lang: 'ts',\n });\n\n const fileExports: string[] = result.module.staticExports.flatMap((e) =>\n e.entries\n .filter((entry) => entry.exportName.name !== null)\n .map((entry) => entry.exportName.name as string),\n );\n\n // In h3 v2 / Nitro v3, event.node is undefined during prerendering\n // (which uses the fetch-based pipeline, not Node.js http). We use\n // optional chaining so that page endpoints work in both Node.js\n // server and fetch-based prerender contexts.\n // Nitro v3 no longer guarantees the private `nitro/deps/ofetch`\n // subpath that older codegen relied on.\n //\n // Page loaders expect Nitro-style `$fetch` semantics (parsed data plus\n // internal relative-route support), so construct a request-local fetch\n // using public APIs:\n // - `createFetch` from `ofetch` for `$fetch` behavior\n // - `fetchWithEvent` from `h3` for internal Nitro request routing\n //\n // This avoids both unstable private Nitro imports and assumptions about\n // a global runtime `$fetch` being available during prerender.\n const code = `\n import { defineHandler, fetchWithEvent } from 'nitro/h3';\n import { createFetch } from 'ofetch';\n\n ${\n fileExports.includes('load')\n ? _code\n : `\n ${_code}\n export const load = () => {\n return {};\n }`\n }\n\n ${\n fileExports.includes('action')\n ? ''\n : `\n export const action = () => {\n return {};\n }\n `\n }\n\n export default defineHandler(async(event) => {\n ${SERVER_FETCH_FACTORY_SNIPPET}\n\n if (event.method === 'GET') {\n try {\n return await load({\n params: event.context.params,\n req: event.node?.req,\n res: event.node?.res,\n fetch: serverFetch,\n event\n });\n } catch(e) {\n console.error(\\` An error occurred: \\${e}\\`)\n throw e;\n }\n } else {\n try {\n return await action({\n params: event.context.params,\n req: event.node?.req,\n res: event.node?.res,\n fetch: serverFetch,\n event\n });\n } catch(e) {\n console.error(\\` An error occurred: \\${e}\\`)\n throw e;\n }\n }\n });\n `;\n\n return {\n code,\n map: null,\n };\n }\n\n return;\n },\n };\n}\n"],"mappings":";;;;AAIA,SAAgB,sBAAsB;AACpC,QAAO;EACL,MAAM;EACN,MAAM,UACJ,OACA,IACkD;AAClD,OAAI,cAAc,GAAG,CAAC,SAAS,UAAU,IAAI,GAAG,SAAS,aAAa,EAAE;IAMtE,MAAM,cALS,UAAU,IAAI,OAAO;KAClC,YAAY;KACZ,MAAM;KACP,CAAC,CAEmC,OAAO,cAAc,SAAS,MACjE,EAAE,QACC,QAAQ,UAAU,MAAM,WAAW,SAAS,KAAK,CACjD,KAAK,UAAU,MAAM,WAAW,KAAe,CACnD;AA0ED,WAAO;KACL,MA1DW;;;;cAKP,YAAY,SAAS,OAAO,GACxB,QACA;kBACA,MAAM;;;mBAIX;;cAGC,YAAY,SAAS,SAAS,GAC1B,KACA;;;;gBAKL;;;gBAGG,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAkCnC,KAAK;KACN;;;EAKN"}
|
|
@@ -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,15 @@
|
|
|
1
|
+
import { createDebug } from "obug";
|
|
2
|
+
//#region packages/vite-plugin-nitro/src/lib/utils/debug.ts
|
|
3
|
+
var debugNitro = createDebug("analog:nitro");
|
|
4
|
+
var debugSsr = createDebug("analog:nitro:ssr");
|
|
5
|
+
var debugPrerender = createDebug("analog:nitro:prerender");
|
|
6
|
+
/** All debug instances in this package, for external wrapping (e.g. file logging). */
|
|
7
|
+
var debugInstances = [
|
|
8
|
+
debugNitro,
|
|
9
|
+
debugSsr,
|
|
10
|
+
debugPrerender
|
|
11
|
+
];
|
|
12
|
+
//#endregion
|
|
13
|
+
export { debugInstances, debugNitro, debugPrerender, debugSsr };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=debug.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug.js","names":[],"sources":["../../../../src/lib/utils/debug.ts"],"sourcesContent":["import { createDebug } from 'obug';\n\nexport const debugNitro = createDebug('analog:nitro');\nexport const debugSsr = createDebug('analog:nitro:ssr');\nexport const debugPrerender = createDebug('analog:nitro:prerender');\n\n/** All debug instances in this package, for external wrapping (e.g. file logging). */\nexport const debugInstances = [debugNitro, debugSsr, debugPrerender];\n"],"mappings":";;AAEA,IAAa,aAAa,YAAY,eAAe;AACrD,IAAa,WAAW,YAAY,mBAAmB;AACvD,IAAa,iBAAiB,YAAY,yBAAyB;;AAGnE,IAAa,iBAAiB;CAAC;CAAY;CAAU;CAAe"}
|
|
@@ -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"}
|