@analogjs/platform 3.0.0-alpha.91 → 3.0.0-alpha.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/migrations/migrate-to-separated-plugins/migrate-to-separated-plugins.d.ts +2 -0
  2. package/migrations/migrate-to-separated-plugins/migrate-to-separated-plugins.js +203 -0
  3. package/migrations/migrate-to-separated-plugins/migrate-to-separated-plugins.js.map +1 -0
  4. package/package.json +11 -7
  5. package/src/lib/clear-client-page-endpoint.js +1 -0
  6. package/src/lib/clear-client-page-endpoint.js.map +1 -1
  7. package/src/lib/deps-plugin.js +17 -6
  8. package/src/lib/deps-plugin.js.map +1 -1
  9. package/src/lib/nitro/analog-nitro-plugin.js +102 -45
  10. package/src/lib/nitro/analog-nitro-plugin.js.map +1 -1
  11. package/src/lib/nitro/get-server-fn-handlers.d.ts +29 -0
  12. package/src/lib/nitro/get-server-fn-handlers.js +66 -0
  13. package/src/lib/nitro/get-server-fn-handlers.js.map +1 -0
  14. package/src/lib/nitro/server-fn-endpoints.d.ts +41 -0
  15. package/src/lib/nitro/server-fn-endpoints.js +57 -0
  16. package/src/lib/nitro/server-fn-endpoints.js.map +1 -0
  17. package/src/lib/nx-plugin/src/generators/app/files/template-angular/src/server/routes/api/v1/hello.ts__template__ +2 -2
  18. package/src/lib/nx-plugin/src/generators/app/files/template-angular/vite.config.ts__template__ +4 -0
  19. package/src/lib/nx-plugin/src/generators/app/versions/nx_18_X/versions.d.ts +7 -5
  20. package/src/lib/nx-plugin/src/generators/app/versions/nx_18_X/versions.js.map +1 -1
  21. package/src/lib/nx-plugin/src/generators/preset/__snapshots__/generator.spec.ts.snap +3 -1
  22. package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.d.ts +5 -5
  23. package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.js +5 -5
  24. package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.js.map +1 -1
  25. package/src/lib/options.d.ts +8 -0
  26. package/src/lib/platform-plugin.js +34 -3
  27. package/src/lib/platform-plugin.js.map +1 -1
  28. package/src/lib/ssr/defer-streaming-plugin.js +97 -0
  29. package/src/lib/ssr/defer-streaming-plugin.js.map +1 -0
  30. package/src/lib/utils/rolldown.js +1 -1
  31. package/src/server-mode-plugin.d.ts +1 -1
  32. package/src/server-mode-plugin.js +13 -1
  33. package/src/server-mode-plugin.js.map +1 -1
@@ -0,0 +1,2 @@
1
+ import { Rule } from "@angular-devkit/schematics";
2
+ export default function migrateToSeparatedPlugins(): Rule;
@@ -0,0 +1,203 @@
1
+ import { NodePackageInstallTask } from "@angular-devkit/schematics/tasks/index.js";
2
+ import * as ts from "typescript";
3
+ //#region packages/platform/migrations/migrate-to-separated-plugins/migrate-to-separated-plugins.ts
4
+ var ANALOG_PLATFORM_IMPORT = `from '@analogjs/platform'`;
5
+ var ANGULAR_PLUGIN_IMPORT = `from '@analogjs/vite-plugin-angular'`;
6
+ var NITRO_IMPORTS = [`from '@analogjs/vite-plugin-nitro'`, `from 'nitro/vite'`];
7
+ var ANGULAR_PLUGIN_PKG = "@analogjs/vite-plugin-angular";
8
+ var NITRO_PLUGIN_PKG = "@analogjs/vite-plugin-nitro";
9
+ var MIGRATION_DOC_URL = "https://analogjs.org/docs/guides/migrating-v2-to-v3#analog-angular-and-nitro-are-now-separate-plugins";
10
+ var VITE_CONFIG_EXTENSIONS = [
11
+ ".ts",
12
+ ".mts",
13
+ ".js",
14
+ ".mjs"
15
+ ];
16
+ function isViteConfig(filePath) {
17
+ const file = filePath.slice(filePath.lastIndexOf("/") + 1);
18
+ if (!file.startsWith("vite.config.")) return false;
19
+ return VITE_CONFIG_EXTENSIONS.some((ext) => file.endsWith(ext));
20
+ }
21
+ function usesLegacyPluginShape(source) {
22
+ if (!source.includes(ANALOG_PLATFORM_IMPORT)) return false;
23
+ if (source.includes(ANGULAR_PLUGIN_IMPORT) && NITRO_IMPORTS.some((nitroImport) => source.includes(nitroImport))) return false;
24
+ return /\banalog\s*\(/.test(source);
25
+ }
26
+ function readPackageJson(tree) {
27
+ const content = tree.read("/package.json");
28
+ if (!content) return null;
29
+ const raw = content.toString("utf-8");
30
+ try {
31
+ return {
32
+ raw,
33
+ pkg: JSON.parse(raw)
34
+ };
35
+ } catch {
36
+ return null;
37
+ }
38
+ }
39
+ function getDepVersion(pkg, name) {
40
+ const dev = pkg["devDependencies"] ?? {};
41
+ const reg = pkg["dependencies"] ?? {};
42
+ return dev[name] ?? reg[name];
43
+ }
44
+ function addDependencies(tree, context) {
45
+ const info = readPackageJson(tree);
46
+ if (!info) return false;
47
+ const { pkg, raw } = info;
48
+ const devDeps = { ...pkg["devDependencies"] ?? {} };
49
+ let changed = false;
50
+ const platformVersion = getDepVersion(pkg, "@analogjs/platform") ?? "*";
51
+ const missingPlugins = [ANGULAR_PLUGIN_PKG, NITRO_PLUGIN_PKG].filter((name) => !getDepVersion(pkg, name) && !(name === NITRO_PLUGIN_PKG && getDepVersion(pkg, "nitro")));
52
+ for (const name of missingPlugins) {
53
+ devDeps[name] = platformVersion;
54
+ changed = true;
55
+ context.logger.info(`Added '${name}': '${platformVersion}' to devDependencies.`);
56
+ }
57
+ if (!changed) return false;
58
+ pkg["devDependencies"] = devDeps;
59
+ const trailingNewline = raw.endsWith("\n") ? "\n" : "";
60
+ tree.overwrite("/package.json", JSON.stringify(pkg, null, 2) + trailingNewline);
61
+ context.addTask(new NodePackageInstallTask());
62
+ return true;
63
+ }
64
+ function applyEdits(source, edits) {
65
+ const sorted = [...edits].sort((a, b) => b.start - a.start);
66
+ let result = source;
67
+ for (const edit of sorted) result = result.slice(0, edit.start) + edit.text + result.slice(edit.end);
68
+ return result;
69
+ }
70
+ function getLineIndent(source, pos) {
71
+ let lineStart = pos;
72
+ while (lineStart > 0 && source[lineStart - 1] !== "\n") lineStart--;
73
+ let indent = "";
74
+ for (let i = lineStart; i < source.length; i++) {
75
+ const ch = source[i];
76
+ if (ch === " " || ch === " ") indent += ch;
77
+ else break;
78
+ }
79
+ return indent;
80
+ }
81
+ /**
82
+ * Tries to lift `vite: {...}` and `nitro: {...}` properties off the
83
+ * single `analog(...)` call into companion `angular(...)` / `nitro(...)`
84
+ * calls. Returns the rewritten source on success, or null if the file
85
+ * doesn't match the supported pattern (we then fall back to logging
86
+ * instructions rather than risking a corrupted file).
87
+ *
88
+ * Supported pattern: exactly one `analog(...)` call whose argument is
89
+ * an object literal. Properties named `vite` and `nitro` are recognized;
90
+ * everything else stays on `analog`.
91
+ */
92
+ function tryTransformViteConfig(filePath, source) {
93
+ let sourceFile;
94
+ try {
95
+ sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
96
+ } catch {
97
+ return null;
98
+ }
99
+ let analogImportEnd = -1;
100
+ let angularImported = false;
101
+ let nitroImported = false;
102
+ const analogCalls = [];
103
+ function visit(node) {
104
+ if (ts.isImportDeclaration(node)) {
105
+ const ml = node.moduleSpecifier;
106
+ if (ts.isStringLiteral(ml)) {
107
+ if (ml.text === "@analogjs/platform") analogImportEnd = node.end;
108
+ else if (ml.text === "@analogjs/vite-plugin-angular") angularImported = true;
109
+ else if (ml.text === "@analogjs/vite-plugin-nitro" || ml.text === "nitro/vite") nitroImported = true;
110
+ }
111
+ } else if (ts.isCallExpression(node)) {
112
+ if (ts.isIdentifier(node.expression) && node.expression.text === "analog") analogCalls.push(node);
113
+ }
114
+ ts.forEachChild(node, visit);
115
+ }
116
+ visit(sourceFile);
117
+ if (analogImportEnd === -1) return null;
118
+ if (analogCalls.length !== 1) return null;
119
+ const analogCall = analogCalls[0];
120
+ const callStart = analogCall.getStart(sourceFile);
121
+ const callEnd = analogCall.getEnd();
122
+ let viteValueText = null;
123
+ let nitroValueText = null;
124
+ let remainingPropsText = null;
125
+ const arg = analogCall.arguments[0];
126
+ if (arg && ts.isObjectLiteralExpression(arg)) {
127
+ const remaining = [];
128
+ for (const prop of arg.properties) if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && (prop.name.text === "vite" || prop.name.text === "nitro")) {
129
+ const value = prop.initializer;
130
+ const text = source.slice(value.getStart(sourceFile), value.getEnd());
131
+ if (prop.name.text === "vite") viteValueText = text;
132
+ else nitroValueText = text;
133
+ } else remaining.push(prop);
134
+ if (remaining.length > 0) {
135
+ const propTexts = remaining.map((p) => source.slice(p.getStart(sourceFile), p.getEnd()));
136
+ const indent = getLineIndent(source, arg.getStart(sourceFile));
137
+ const propIndent = indent + " ";
138
+ remainingPropsText = `{\n${propTexts.map((t) => `${propIndent}${t}`).join(",\n")},\n${indent}}`;
139
+ }
140
+ } else if (arg !== void 0) return null;
141
+ const indent = getLineIndent(source, callStart);
142
+ const parts = [remainingPropsText ? `analog(${remainingPropsText})` : `analog()`];
143
+ if (viteValueText !== null) parts.push(`angular(${viteValueText})`);
144
+ else parts.push("angular()");
145
+ if (nitroValueText !== null) parts.push(`nitro(${nitroValueText})`);
146
+ else parts.push("nitro()");
147
+ const edits = [{
148
+ start: callStart,
149
+ end: callEnd,
150
+ text: parts.join(`,\n${indent}`)
151
+ }];
152
+ const importsToAdd = [];
153
+ if (!angularImported) importsToAdd.push(`import angular from '@analogjs/vite-plugin-angular';`);
154
+ if (!nitroImported) importsToAdd.push(`import nitro from '@analogjs/vite-plugin-nitro';`);
155
+ if (importsToAdd.length > 0) edits.push({
156
+ start: analogImportEnd,
157
+ end: analogImportEnd,
158
+ text: `\n${importsToAdd.join("\n")}`
159
+ });
160
+ return {
161
+ source: applyEdits(source, edits),
162
+ movedVite: viteValueText !== null,
163
+ movedNitro: nitroValueText !== null
164
+ };
165
+ }
166
+ function migrateToSeparatedPlugins() {
167
+ return (tree, context) => {
168
+ const filesUsingLegacyShape = [];
169
+ const rewrittenFiles = [];
170
+ const unhandledFiles = [];
171
+ tree.visit((filePath) => {
172
+ if (filePath.includes("/node_modules/")) return;
173
+ if (!isViteConfig(filePath)) return;
174
+ const content = tree.read(filePath);
175
+ if (!content) return;
176
+ const source = content.toString("utf-8");
177
+ if (!usesLegacyPluginShape(source)) return;
178
+ filesUsingLegacyShape.push(filePath);
179
+ const transformed = tryTransformViteConfig(filePath, source);
180
+ if (transformed) {
181
+ tree.overwrite(filePath, transformed.source);
182
+ rewrittenFiles.push(filePath);
183
+ } else unhandledFiles.push(filePath);
184
+ });
185
+ if (filesUsingLegacyShape.length === 0) return tree;
186
+ if (rewrittenFiles.length > 0) {
187
+ context.logger.info(`Rewrote ${rewrittenFiles.length} vite.config file(s) to the separated \`analog() + angular() + nitro()\` shape:`);
188
+ for (const file of rewrittenFiles) context.logger.info(` - ${file}`);
189
+ context.logger.info(`Review the result — only \`vite\` and \`nitro\` were moved automatically. Other angular-passthrough options (\`liveReload\`, \`fastCompile\`, \`fileReplacements\`, etc.) still need to be relocated by hand.\nSee ${MIGRATION_DOC_URL}`);
190
+ }
191
+ if (unhandledFiles.length > 0) {
192
+ context.logger.info(`Could not safely rewrite ${unhandledFiles.length} vite.config file(s); migrate them by hand:`);
193
+ for (const file of unhandledFiles) context.logger.info(` - ${file}`);
194
+ context.logger.info(`Split \`analog()\` into \`analog() + angular() + nitro()\` and move each option to its owning plugin.\nSee ${MIGRATION_DOC_URL}`);
195
+ }
196
+ addDependencies(tree, context);
197
+ return tree;
198
+ };
199
+ }
200
+ //#endregion
201
+ export { migrateToSeparatedPlugins as default };
202
+
203
+ //# sourceMappingURL=migrate-to-separated-plugins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate-to-separated-plugins.js","names":[],"sources":["../../../migrations/migrate-to-separated-plugins/migrate-to-separated-plugins.ts"],"sourcesContent":["import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';\n// The explicit file path resolves when this runs as native ESM.\nimport { NodePackageInstallTask } from '@angular-devkit/schematics/tasks/index.js';\nimport * as ts from 'typescript';\n\nconst ANALOG_PLATFORM_IMPORT = `from '@analogjs/platform'`;\nconst ANGULAR_PLUGIN_IMPORT = `from '@analogjs/vite-plugin-angular'`;\nconst NITRO_IMPORTS = [\n `from '@analogjs/vite-plugin-nitro'`,\n `from 'nitro/vite'`,\n];\nconst ANGULAR_PLUGIN_PKG = '@analogjs/vite-plugin-angular';\nconst NITRO_PLUGIN_PKG = '@analogjs/vite-plugin-nitro';\nconst MIGRATION_DOC_URL =\n 'https://analogjs.org/docs/guides/migrating-v2-to-v3#analog-angular-and-nitro-are-now-separate-plugins';\n\nconst VITE_CONFIG_EXTENSIONS = ['.ts', '.mts', '.js', '.mjs'];\n\nfunction isViteConfig(filePath: string): boolean {\n const file = filePath.slice(filePath.lastIndexOf('/') + 1);\n if (!file.startsWith('vite.config.')) {\n return false;\n }\n return VITE_CONFIG_EXTENSIONS.some((ext) => file.endsWith(ext));\n}\n\nfunction usesLegacyPluginShape(source: string): boolean {\n if (!source.includes(ANALOG_PLATFORM_IMPORT)) {\n return false;\n }\n if (\n source.includes(ANGULAR_PLUGIN_IMPORT) &&\n NITRO_IMPORTS.some((nitroImport) => source.includes(nitroImport))\n ) {\n return false;\n }\n return /\\banalog\\s*\\(/.test(source);\n}\n\nfunction readPackageJson(\n tree: Tree,\n): { raw: string; pkg: Record<string, unknown> } | null {\n const content = tree.read('/package.json');\n if (!content) return null;\n const raw = content.toString('utf-8');\n try {\n return { raw, pkg: JSON.parse(raw) };\n } catch {\n return null;\n }\n}\n\nfunction getDepVersion(\n pkg: Record<string, unknown>,\n name: string,\n): string | undefined {\n const dev =\n (pkg['devDependencies'] as Record<string, string> | undefined) ?? {};\n const reg = (pkg['dependencies'] as Record<string, string> | undefined) ?? {};\n return dev[name] ?? reg[name];\n}\n\nfunction addDependencies(tree: Tree, context: SchematicContext): boolean {\n const info = readPackageJson(tree);\n if (!info) return false;\n\n const { pkg, raw } = info;\n const devDeps = {\n ...((pkg['devDependencies'] as Record<string, string> | undefined) ?? {}),\n };\n let changed = false;\n\n // Match the @analogjs/platform pin so the plugins stay aligned with the\n // rest of the Analog packages already in this workspace.\n const platformVersion = getDepVersion(pkg, '@analogjs/platform') ?? '*';\n const missingPlugins = [ANGULAR_PLUGIN_PKG, NITRO_PLUGIN_PKG].filter(\n (name) =>\n !getDepVersion(pkg, name) &&\n // Apps already on Nitro v3 keep `nitro()` from `nitro/vite`.\n !(name === NITRO_PLUGIN_PKG && getDepVersion(pkg, 'nitro')),\n );\n for (const name of missingPlugins) {\n devDeps[name] = platformVersion;\n changed = true;\n context.logger.info(\n `Added '${name}': '${platformVersion}' to devDependencies.`,\n );\n }\n\n if (!changed) return false;\n\n pkg['devDependencies'] = devDeps;\n const trailingNewline = raw.endsWith('\\n') ? '\\n' : '';\n tree.overwrite(\n '/package.json',\n JSON.stringify(pkg, null, 2) + trailingNewline,\n );\n context.addTask(new NodePackageInstallTask());\n return true;\n}\n\ninterface TransformResult {\n source: string;\n movedVite: boolean;\n movedNitro: boolean;\n}\n\ninterface Edit {\n start: number;\n end: number;\n text: string;\n}\n\nfunction applyEdits(source: string, edits: Edit[]): string {\n // Apply right-to-left so earlier offsets stay valid.\n const sorted = [...edits].sort((a, b) => b.start - a.start);\n let result = source;\n for (const edit of sorted) {\n result = result.slice(0, edit.start) + edit.text + result.slice(edit.end);\n }\n return result;\n}\n\nfunction getLineIndent(source: string, pos: number): string {\n let lineStart = pos;\n while (lineStart > 0 && source[lineStart - 1] !== '\\n') {\n lineStart--;\n }\n let indent = '';\n for (let i = lineStart; i < source.length; i++) {\n const ch = source[i];\n if (ch === ' ' || ch === '\\t') indent += ch;\n else break;\n }\n return indent;\n}\n\n/**\n * Tries to lift `vite: {...}` and `nitro: {...}` properties off the\n * single `analog(...)` call into companion `angular(...)` / `nitro(...)`\n * calls. Returns the rewritten source on success, or null if the file\n * doesn't match the supported pattern (we then fall back to logging\n * instructions rather than risking a corrupted file).\n *\n * Supported pattern: exactly one `analog(...)` call whose argument is\n * an object literal. Properties named `vite` and `nitro` are recognized;\n * everything else stays on `analog`.\n */\nfunction tryTransformViteConfig(\n filePath: string,\n source: string,\n): TransformResult | null {\n let sourceFile: ts.SourceFile;\n try {\n sourceFile = ts.createSourceFile(\n filePath,\n source,\n ts.ScriptTarget.Latest,\n true,\n ts.ScriptKind.TS,\n );\n } catch {\n return null;\n }\n\n let analogImportEnd = -1;\n let angularImported = false;\n let nitroImported = false;\n const analogCalls: ts.CallExpression[] = [];\n\n function visit(node: ts.Node): void {\n if (ts.isImportDeclaration(node)) {\n const ml = node.moduleSpecifier;\n if (ts.isStringLiteral(ml)) {\n if (ml.text === '@analogjs/platform') {\n analogImportEnd = node.end;\n } else if (ml.text === '@analogjs/vite-plugin-angular') {\n angularImported = true;\n } else if (\n ml.text === '@analogjs/vite-plugin-nitro' ||\n ml.text === 'nitro/vite'\n ) {\n nitroImported = true;\n }\n }\n } else if (ts.isCallExpression(node)) {\n if (\n ts.isIdentifier(node.expression) &&\n node.expression.text === 'analog'\n ) {\n analogCalls.push(node);\n }\n }\n ts.forEachChild(node, visit);\n }\n visit(sourceFile);\n\n if (analogImportEnd === -1) return null;\n if (analogCalls.length !== 1) return null;\n\n const analogCall = analogCalls[0];\n const callStart = analogCall.getStart(sourceFile);\n const callEnd = analogCall.getEnd();\n\n let viteValueText: string | null = null;\n let nitroValueText: string | null = null;\n let remainingPropsText: string | null = null;\n\n const arg = analogCall.arguments[0];\n if (arg && ts.isObjectLiteralExpression(arg)) {\n const remaining: ts.ObjectLiteralElementLike[] = [];\n for (const prop of arg.properties) {\n if (\n ts.isPropertyAssignment(prop) &&\n ts.isIdentifier(prop.name) &&\n (prop.name.text === 'vite' || prop.name.text === 'nitro')\n ) {\n const value = prop.initializer;\n const text = source.slice(value.getStart(sourceFile), value.getEnd());\n if (prop.name.text === 'vite') viteValueText = text;\n else nitroValueText = text;\n } else {\n remaining.push(prop);\n }\n }\n\n if (remaining.length > 0) {\n // Reconstruct the analog object literal from the remaining props,\n // preserving their original source text (comments, spacing).\n const propTexts = remaining.map((p) =>\n source.slice(p.getStart(sourceFile), p.getEnd()),\n );\n const objStart = arg.getStart(sourceFile);\n const indent = getLineIndent(source, objStart);\n const propIndent = indent + ' ';\n remainingPropsText = `{\\n${propTexts.map((t) => `${propIndent}${t}`).join(',\\n')},\\n${indent}}`;\n }\n } else if (arg !== undefined) {\n // analog(otherExpression) — can't statically split a variable or call\n // expression. Fall back to logging instructions.\n return null;\n }\n // analog() with no argument falls through to the rewrite below; we just\n // emit `analog(), angular(), nitro()` to scaffold the new plugin chain.\n\n const indent = getLineIndent(source, callStart);\n const newAnalogCall = remainingPropsText\n ? `analog(${remainingPropsText})`\n : `analog()`;\n const parts: string[] = [newAnalogCall];\n if (viteValueText !== null) parts.push(`angular(${viteValueText})`);\n else parts.push('angular()');\n if (nitroValueText !== null) parts.push(`nitro(${nitroValueText})`);\n else parts.push('nitro()');\n const replacement = parts.join(`,\\n${indent}`);\n\n const edits: Edit[] = [{ start: callStart, end: callEnd, text: replacement }];\n\n const importsToAdd: string[] = [];\n if (!angularImported) {\n importsToAdd.push(`import angular from '@analogjs/vite-plugin-angular';`);\n }\n if (!nitroImported) {\n importsToAdd.push(`import nitro from '@analogjs/vite-plugin-nitro';`);\n }\n if (importsToAdd.length > 0) {\n edits.push({\n start: analogImportEnd,\n end: analogImportEnd,\n text: `\\n${importsToAdd.join('\\n')}`,\n });\n }\n\n const rewritten = applyEdits(source, edits);\n\n return {\n source: rewritten,\n movedVite: viteValueText !== null,\n movedNitro: nitroValueText !== null,\n };\n}\n\nexport default function migrateToSeparatedPlugins(): Rule {\n return (tree: Tree, context: SchematicContext) => {\n const filesUsingLegacyShape: string[] = [];\n const rewrittenFiles: string[] = [];\n const unhandledFiles: string[] = [];\n\n tree.visit((filePath) => {\n if (filePath.includes('/node_modules/')) return;\n if (!isViteConfig(filePath)) return;\n\n const content = tree.read(filePath);\n if (!content) return;\n\n const source = content.toString('utf-8');\n if (!usesLegacyPluginShape(source)) return;\n\n filesUsingLegacyShape.push(filePath);\n\n const transformed = tryTransformViteConfig(filePath, source);\n if (transformed) {\n tree.overwrite(filePath, transformed.source);\n rewrittenFiles.push(filePath);\n } else {\n unhandledFiles.push(filePath);\n }\n });\n\n if (filesUsingLegacyShape.length === 0) {\n return tree;\n }\n\n if (rewrittenFiles.length > 0) {\n context.logger.info(\n `Rewrote ${rewrittenFiles.length} vite.config file(s) to the separated \\`analog() + angular() + nitro()\\` shape:`,\n );\n for (const file of rewrittenFiles) {\n context.logger.info(` - ${file}`);\n }\n context.logger.info(\n `Review the result — only \\`vite\\` and \\`nitro\\` were moved automatically. Other angular-passthrough options (\\`liveReload\\`, \\`fastCompile\\`, \\`fileReplacements\\`, etc.) still need to be relocated by hand.\\nSee ${MIGRATION_DOC_URL}`,\n );\n }\n\n if (unhandledFiles.length > 0) {\n context.logger.info(\n `Could not safely rewrite ${unhandledFiles.length} vite.config file(s); migrate them by hand:`,\n );\n for (const file of unhandledFiles) {\n context.logger.info(` - ${file}`);\n }\n context.logger.info(\n `Split \\`analog()\\` into \\`analog() + angular() + nitro()\\` and move each option to its owning plugin.\\nSee ${MIGRATION_DOC_URL}`,\n );\n }\n\n addDependencies(tree, context);\n\n return tree;\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@analogjs/platform",
3
- "version": "3.0.0-alpha.91",
3
+ "version": "3.0.0-alpha.92",
4
4
  "description": "The fullstack meta-framework for Angular",
5
5
  "type": "module",
6
6
  "author": "Brandon Roberts <robertsbt@gmail.com>",
@@ -31,12 +31,10 @@
31
31
  "url": "https://github.com/sponsors/brandonroberts"
32
32
  },
33
33
  "dependencies": {
34
- "@analogjs/vite-plugin-angular": "3.0.0-alpha.91",
34
+ "@analogjs/vite-plugin-angular": "3.0.0-alpha.92",
35
35
  "es-toolkit": "^1.45.1",
36
36
  "front-matter": "^4.0.2",
37
- "h3": "^2.0.1-rc.31",
38
37
  "magic-string": "^0.30.21",
39
- "nitro": "3.0.260903-beta",
40
38
  "obug": "^2.1.1",
41
39
  "ofetch": "2.0.0-alpha.3",
42
40
  "oxc-parser": "^0.124.0",
@@ -46,7 +44,9 @@
46
44
  "xmlbuilder2": "^4.0.3"
47
45
  },
48
46
  "devDependencies": {
49
- "@babel/core": "^8.0.0"
47
+ "@babel/core": "^8.0.0",
48
+ "h3": "^2.0.1-rc.31",
49
+ "nitro": "3.0.260903-beta"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "@nx/angular": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0 || ^23.0.0 || ^23",
@@ -57,6 +57,7 @@
57
57
  "marked-highlight": "^2.2.3",
58
58
  "marked-mangle": "^1.1.12",
59
59
  "marked-shiki": "^1.2.1",
60
+ "nitro": "3.0.260903-beta",
60
61
  "shiki": "^1.29.2 || ^4.0.2",
61
62
  "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
62
63
  },
@@ -70,16 +71,19 @@
70
71
  "@nx/vite": {
71
72
  "optional": true
72
73
  },
73
- "shiki": {
74
+ "marked-highlight": {
74
75
  "optional": true
75
76
  },
76
77
  "marked-shiki": {
77
78
  "optional": true
78
79
  },
80
+ "nitro": {
81
+ "optional": true
82
+ },
79
83
  "prismjs": {
80
84
  "optional": true
81
85
  },
82
- "marked-highlight": {
86
+ "shiki": {
83
87
  "optional": true
84
88
  }
85
89
  },
@@ -8,6 +8,7 @@ function clearClientPageEndpointsPlugin() {
8
8
  let isBuild = true;
9
9
  return {
10
10
  name: "analogjs-platform-clear-client-page-endpoint",
11
+ enforce: "post",
11
12
  config() {
12
13
  return { build: { rollupOptions: { onwarn(warning) {
13
14
  if (warning.message.includes("empty chunk") && warning.message.endsWith(".server")) return;
@@ -1 +1 @@
1
- {"version":3,"file":"clear-client-page-endpoint.js","names":[],"sources":["../../../src/lib/clear-client-page-endpoint.ts"],"sourcesContent":["import { Plugin, normalizePath } from 'vite';\nimport { injectServerFnIds } from './inject-server-fn-ids.js';\nimport { serverFnFileId } from './server-fn-id.js';\n\nimport { scrubServerFnModule } from './server-fn-client-transform.js';\n\nexport function clearClientPageEndpointsPlugin(): Plugin {\n let projectRoot = process.cwd();\n let isBuild = true;\n\n return {\n name: 'analogjs-platform-clear-client-page-endpoint',\n config() {\n return {\n build: {\n rollupOptions: {\n onwarn(warning) {\n if (\n warning.message.includes('empty chunk') &&\n warning.message.endsWith('.server')\n ) {\n return;\n }\n },\n },\n },\n };\n },\n configResolved(config) {\n projectRoot = config.root;\n isBuild = config.command === 'build';\n },\n transform(code, id, options) {\n if (!id.endsWith('.server.ts')) {\n return;\n }\n\n // Server-function ids are derived from the project-root-relative path so\n // the client proxy and the server registration always resolve to the same\n // opaque, collision-free route.\n const fileId = serverFnFileId(id, projectRoot);\n\n if (options?.ssr) {\n // SSR build keeps the real handlers but stamps the derived id into each\n // serverFn config so it registers under the route the client calls.\n const injected = injectServerFnIds(code, fileId);\n return injected ? { code: injected.code, map: null } : undefined;\n }\n\n // Client build: a `.server.ts` defining server functions is rewritten to\n // client-safe proxies (the `{ id, url, method }` refs the browser\n // dispatches through); handlers and server imports are dropped so they\n // tree-shake away.\n const scrubbed = scrubServerFnModule(code, fileId);\n if (scrubbed) {\n // An empty mapping, not `null`: a null map lets the original module —\n // handlers and all — survive in `sourcesContent` further down the\n // pipeline, which would ship the server code to the browser after the\n // scrub went to the trouble of removing it.\n return { code: scrubbed.code, map: { mappings: '' } };\n }\n\n // Page load/action endpoints carry no client-consumable exports. Only the\n // client build empties them; the dev server never pulls them into the\n // browser graph, and emptying them there would break the SSR request.\n if (isBuild && id.includes(normalizePath('src/app/pages'))) {\n return {\n code: 'export default undefined;',\n map: null,\n };\n }\n\n return;\n },\n };\n}\n"],"mappings":";;;;;AAMA,SAAgB,iCAAyC;CACvD,IAAI,cAAc,QAAQ,KAAK;CAC/B,IAAI,UAAU;AAEd,QAAO;EACL,MAAM;EACN,SAAS;AACP,UAAO,EACL,OAAO,EACL,eAAe,EACb,OAAO,SAAS;AACd,QACE,QAAQ,QAAQ,SAAS,cAAc,IACvC,QAAQ,QAAQ,SAAS,UAAU,CAEnC;MAGL,EACF,EACF;;EAEH,eAAe,QAAQ;AACrB,iBAAc,OAAO;AACrB,aAAU,OAAO,YAAY;;EAE/B,UAAU,MAAM,IAAI,SAAS;AAC3B,OAAI,CAAC,GAAG,SAAS,aAAa,CAC5B;GAMF,MAAM,SAAS,eAAe,IAAI,YAAY;AAE9C,OAAI,SAAS,KAAK;IAGhB,MAAM,WAAW,kBAAkB,MAAM,OAAO;AAChD,WAAO,WAAW;KAAE,MAAM,SAAS;KAAM,KAAK;KAAM,GAAG,KAAA;;GAOzD,MAAM,WAAW,oBAAoB,MAAM,OAAO;AAClD,OAAI,SAKF,QAAO;IAAE,MAAM,SAAS;IAAM,KAAK,EAAE,UAAU,IAAI;IAAE;AAMvD,OAAI,WAAW,GAAG,SAAS,cAAc,gBAAgB,CAAC,CACxD,QAAO;IACL,MAAM;IACN,KAAK;IACN;;EAKN"}
1
+ {"version":3,"file":"clear-client-page-endpoint.js","names":[],"sources":["../../../src/lib/clear-client-page-endpoint.ts"],"sourcesContent":["import { Plugin, normalizePath } from 'vite';\nimport { injectServerFnIds } from './inject-server-fn-ids.js';\nimport { serverFnFileId } from './server-fn-id.js';\n\nimport { scrubServerFnModule } from './server-fn-client-transform.js';\n\nexport function clearClientPageEndpointsPlugin(): Plugin {\n let projectRoot = process.cwd();\n let isBuild = true;\n\n return {\n name: 'analogjs-platform-clear-client-page-endpoint',\n // After Angular compilation, which re-emits `.server.ts` files from its own\n // program and would otherwise drop this transform's output.\n enforce: 'post',\n config() {\n return {\n build: {\n rollupOptions: {\n onwarn(warning) {\n if (\n warning.message.includes('empty chunk') &&\n warning.message.endsWith('.server')\n ) {\n return;\n }\n },\n },\n },\n };\n },\n configResolved(config) {\n projectRoot = config.root;\n isBuild = config.command === 'build';\n },\n transform(code, id, options) {\n if (!id.endsWith('.server.ts')) {\n return;\n }\n\n // Server-function ids are derived from the project-root-relative path so\n // the client proxy and the server registration always resolve to the same\n // opaque, collision-free route.\n const fileId = serverFnFileId(id, projectRoot);\n\n if (options?.ssr) {\n // SSR build keeps the real handlers but stamps the derived id into each\n // serverFn config so it registers under the route the client calls.\n const injected = injectServerFnIds(code, fileId);\n return injected ? { code: injected.code, map: null } : undefined;\n }\n\n // Client build: a `.server.ts` defining server functions is rewritten to\n // client-safe proxies (the `{ id, url, method }` refs the browser\n // dispatches through); handlers and server imports are dropped so they\n // tree-shake away.\n const scrubbed = scrubServerFnModule(code, fileId);\n if (scrubbed) {\n // An empty mapping, not `null`: a null map lets the original module —\n // handlers and all — survive in `sourcesContent` further down the\n // pipeline, which would ship the server code to the browser after the\n // scrub went to the trouble of removing it.\n return { code: scrubbed.code, map: { mappings: '' } };\n }\n\n // Page load/action endpoints carry no client-consumable exports. Only the\n // client build empties them; the dev server never pulls them into the\n // browser graph, and emptying them there would break the SSR request.\n if (isBuild && id.includes(normalizePath('src/app/pages'))) {\n return {\n code: 'export default undefined;',\n map: null,\n };\n }\n\n return;\n },\n };\n}\n"],"mappings":";;;;;AAMA,SAAgB,iCAAyC;CACvD,IAAI,cAAc,QAAQ,KAAK;CAC/B,IAAI,UAAU;AAEd,QAAO;EACL,MAAM;EAGN,SAAS;EACT,SAAS;AACP,UAAO,EACL,OAAO,EACL,eAAe,EACb,OAAO,SAAS;AACd,QACE,QAAQ,QAAQ,SAAS,cAAc,IACvC,QAAQ,QAAQ,SAAS,UAAU,CAEnC;MAGL,EACF,EACF;;EAEH,eAAe,QAAQ;AACrB,iBAAc,OAAO;AACrB,aAAU,OAAO,YAAY;;EAE/B,UAAU,MAAM,IAAI,SAAS;AAC3B,OAAI,CAAC,GAAG,SAAS,aAAa,CAC5B;GAMF,MAAM,SAAS,eAAe,IAAI,YAAY;AAE9C,OAAI,SAAS,KAAK;IAGhB,MAAM,WAAW,kBAAkB,MAAM,OAAO;AAChD,WAAO,WAAW;KAAE,MAAM,SAAS;KAAM,KAAK;KAAM,GAAG,KAAA;;GAOzD,MAAM,WAAW,oBAAoB,MAAM,OAAO;AAClD,OAAI,SAKF,QAAO;IAAE,MAAM,SAAS;IAAM,KAAK,EAAE,UAAU,IAAI;IAAE;AAMvD,OAAI,WAAW,GAAG,SAAS,cAAc,gBAAgB,CAAC,CACxD,QAAO;IACL,MAAM;IACN,KAAK;IACN;;EAKN"}
@@ -1,8 +1,15 @@
1
1
  import { debugPlatform } from "./utils/debug.js";
2
- import { getJsTransformConfigKey } from "./utils/rolldown.js";
2
+ import { getJsTransformConfigKey, isRolldown } from "./utils/rolldown.js";
3
+ import { angularLinkerPlugin } from "./nitro/angular-linker-plugin.js";
3
4
  import { VERSION } from "@angular/compiler-cli";
4
5
  import { crawlFrameworkPkgs } from "vitefu";
5
6
  //#region packages/platform/src/lib/deps-plugin.ts
7
+ var ANGULAR_LINKED_SSR_DEPS = [
8
+ "@angular/compiler",
9
+ "@angular/core",
10
+ "@angular/platform-browser",
11
+ "@angular/platform-server"
12
+ ];
6
13
  function depsPlugin(options) {
7
14
  const workspaceRoot = options?.workspaceRoot ?? process.env["NX_WORKSPACE_ROOT"] ?? process.cwd();
8
15
  return [{
@@ -26,11 +33,15 @@ function depsPlugin(options) {
26
33
  "@taiga-ui/**",
27
34
  "@tanstack/angular-query-experimental"
28
35
  ],
29
- optimizeDeps: { include: [
30
- "@angular/common",
31
- "@angular/common/http",
32
- ...Number(VERSION.major) > 15 ? ["@angular/core/rxjs-interop"] : []
33
- ] }
36
+ optimizeDeps: {
37
+ include: [
38
+ ...isRolldown() ? ANGULAR_LINKED_SSR_DEPS : [],
39
+ "@angular/common",
40
+ "@angular/common/http",
41
+ ...Number(VERSION.major) > 15 ? ["@angular/core/rxjs-interop"] : []
42
+ ],
43
+ ...isRolldown() ? { rolldownOptions: { plugins: [angularLinkerPlugin()] } } : {}
44
+ }
34
45
  },
35
46
  optimizeDeps: {
36
47
  include: [
@@ -1 +1 @@
1
- {"version":3,"file":"deps-plugin.js","names":[],"sources":["../../../src/lib/deps-plugin.ts"],"sourcesContent":["import { VERSION } from '@angular/compiler-cli';\nimport type { Plugin } from 'vite';\nimport { crawlFrameworkPkgs } from 'vitefu';\n\nimport { Options } from './options.js';\nimport { debugPlatform } from './utils/debug.js';\nimport { getJsTransformConfigKey } from './utils/rolldown.js';\n\nexport function depsPlugin(options?: Options): Plugin[] {\n const workspaceRoot =\n options?.workspaceRoot ?? process.env['NX_WORKSPACE_ROOT'] ?? process.cwd();\n\n return [\n {\n name: 'analogjs-deps-plugin',\n config() {\n // Skip Vite's built-in ts/js transform so `@analogjs/vite-plugin-angular`\n // (when the user includes it) owns Angular file compilation. Users who\n // run an alternative compiler or compile through Angular's own\n // compilation API can override this in their own Vite config.\n const transformConfig = { exclude: ['**/*.ts', '**/*.js'] };\n debugPlatform('deps transform config', {\n jsTransformKey: getJsTransformConfigKey(),\n transformExcluded: true,\n });\n\n return {\n [getJsTransformConfigKey()]: transformConfig,\n ssr: {\n noExternal: [\n '@analogjs/**',\n 'es-toolkit',\n 'firebase/**',\n 'firebase-admin/**',\n 'rxfire',\n '@ng-web-apis/**',\n '@taiga-ui/**',\n '@tanstack/angular-query-experimental',\n ],\n // The server pre-bundle is resolved separately from the client\n // one, so a secondary entry point left out of it loads its own\n // copy of `@angular/core`. `inject()` then runs against a runtime\n // with no active injection context and SSR fails with NG0203.\n optimizeDeps: {\n include: [\n '@angular/common',\n '@angular/common/http',\n ...(Number(VERSION.major) > 15\n ? ['@angular/core/rxjs-interop']\n : []),\n ],\n },\n },\n optimizeDeps: {\n include: [\n '@angular/common',\n '@angular/common/http',\n ...(Number(VERSION.major) > 15\n ? ['@angular/core/rxjs-interop']\n : []),\n 'front-matter',\n ],\n exclude: [\n '@angular/platform-server',\n '@analogjs/content',\n '@analogjs/router',\n '@nx/angular',\n '@nx/vite',\n '@nx/devkit',\n '@nx/js',\n '@nx/devkit',\n '@nx/cypress',\n '@nx/jest',\n '@nx/js',\n '@nx/eslint',\n '@nx/webpack',\n '@nx/web',\n '@nx/workspace',\n '@nx/eslint',\n '@nx/module-federation',\n '@nx/rspack',\n 'webpack',\n 'fsevents',\n 'nx',\n ],\n },\n };\n },\n },\n {\n name: 'analogjs-auto-discover-deps',\n async config(config, { command }) {\n const pkgConfig = await crawlFrameworkPkgs({\n root: workspaceRoot,\n isBuild: command === 'build',\n viteUserConfig: config,\n isSemiFrameworkPkgByJson(pkgJson) {\n return pkgJson['module'] && pkgJson['module'].includes('fesm');\n },\n });\n return pkgConfig;\n },\n },\n ];\n}\n"],"mappings":";;;;;AAQA,SAAgB,WAAW,SAA6B;CACtD,MAAM,gBACJ,SAAS,iBAAiB,QAAQ,IAAI,wBAAwB,QAAQ,KAAK;AAE7E,QAAO,CACL;EACE,MAAM;EACN,SAAS;GAKP,MAAM,kBAAkB,EAAE,SAAS,CAAC,WAAW,UAAU,EAAE;AAC3D,iBAAc,yBAAyB;IACrC,gBAAgB,yBAAyB;IACzC,mBAAmB;IACpB,CAAC;AAEF,UAAO;KACJ,yBAAyB,GAAG;IAC7B,KAAK;KACH,YAAY;MACV;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACD;KAKD,cAAc,EACZ,SAAS;MACP;MACA;MACA,GAAI,OAAO,QAAQ,MAAM,GAAG,KACxB,CAAC,6BAA6B,GAC9B,EAAE;MACP,EACF;KACF;IACD,cAAc;KACZ,SAAS;MACP;MACA;MACA,GAAI,OAAO,QAAQ,MAAM,GAAG,KACxB,CAAC,6BAA6B,GAC9B,EAAE;MACN;MACD;KACD,SAAS;MACP;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACD;KACF;IACF;;EAEJ,EACD;EACE,MAAM;EACN,MAAM,OAAO,QAAQ,EAAE,WAAW;AAShC,UARkB,MAAM,mBAAmB;IACzC,MAAM;IACN,SAAS,YAAY;IACrB,gBAAgB;IAChB,yBAAyB,SAAS;AAChC,YAAO,QAAQ,aAAa,QAAQ,UAAU,SAAS,OAAO;;IAEjE,CAAC;;EAGL,CACF"}
1
+ {"version":3,"file":"deps-plugin.js","names":[],"sources":["../../../src/lib/deps-plugin.ts"],"sourcesContent":["import { VERSION } from '@angular/compiler-cli';\nimport type { Plugin } from 'vite';\nimport { crawlFrameworkPkgs } from 'vitefu';\n\nimport { angularLinkerPlugin } from './nitro/angular-linker-plugin.js';\nimport { Options } from './options.js';\nimport { debugPlatform } from './utils/debug.js';\nimport { getJsTransformConfigKey, isRolldown } from './utils/rolldown.js';\n\nconst ANGULAR_LINKED_SSR_DEPS = [\n '@angular/compiler',\n '@angular/core',\n '@angular/platform-browser',\n '@angular/platform-server',\n];\n\nexport function depsPlugin(options?: Options): Plugin[] {\n const workspaceRoot =\n options?.workspaceRoot ?? process.env['NX_WORKSPACE_ROOT'] ?? process.cwd();\n\n return [\n {\n name: 'analogjs-deps-plugin',\n config() {\n // Skip Vite's built-in ts/js transform so `@analogjs/vite-plugin-angular`\n // (when the user includes it) owns Angular file compilation. Users who\n // run an alternative compiler or compile through Angular's own\n // compilation API can override this in their own Vite config.\n const transformConfig = { exclude: ['**/*.ts', '**/*.js'] };\n debugPlatform('deps transform config', {\n jsTransformKey: getJsTransformConfigKey(),\n transformExcluded: true,\n });\n\n return {\n [getJsTransformConfigKey()]: transformConfig,\n ssr: {\n noExternal: [\n '@analogjs/**',\n 'es-toolkit',\n 'firebase/**',\n 'firebase-admin/**',\n 'rxfire',\n '@ng-web-apis/**',\n '@taiga-ui/**',\n '@tanstack/angular-query-experimental',\n ],\n // The server pre-bundle is resolved separately from the client\n // one, so a secondary entry point left out of it loads its own\n // copy of `@angular/core`. `inject()` then runs against a runtime\n // with no active injection context and SSR fails with NG0203.\n // With Rolldown, the rest of Angular is pre-bundled too and the\n // partial-compilation packages are linked, so the server needs no JIT.\n optimizeDeps: {\n include: [\n ...(isRolldown() ? ANGULAR_LINKED_SSR_DEPS : []),\n '@angular/common',\n '@angular/common/http',\n ...(Number(VERSION.major) > 15\n ? ['@angular/core/rxjs-interop']\n : []),\n ],\n ...(isRolldown()\n ? { rolldownOptions: { plugins: [angularLinkerPlugin()] } }\n : {}),\n },\n },\n optimizeDeps: {\n include: [\n '@angular/common',\n '@angular/common/http',\n ...(Number(VERSION.major) > 15\n ? ['@angular/core/rxjs-interop']\n : []),\n 'front-matter',\n ],\n exclude: [\n '@angular/platform-server',\n '@analogjs/content',\n '@analogjs/router',\n '@nx/angular',\n '@nx/vite',\n '@nx/devkit',\n '@nx/js',\n '@nx/devkit',\n '@nx/cypress',\n '@nx/jest',\n '@nx/js',\n '@nx/eslint',\n '@nx/webpack',\n '@nx/web',\n '@nx/workspace',\n '@nx/eslint',\n '@nx/module-federation',\n '@nx/rspack',\n 'webpack',\n 'fsevents',\n 'nx',\n ],\n },\n };\n },\n },\n {\n name: 'analogjs-auto-discover-deps',\n async config(config, { command }) {\n const pkgConfig = await crawlFrameworkPkgs({\n root: workspaceRoot,\n isBuild: command === 'build',\n viteUserConfig: config,\n isSemiFrameworkPkgByJson(pkgJson) {\n return pkgJson['module'] && pkgJson['module'].includes('fesm');\n },\n });\n return pkgConfig;\n },\n },\n ];\n}\n"],"mappings":";;;;;;AASA,IAAM,0BAA0B;CAC9B;CACA;CACA;CACA;CACD;AAED,SAAgB,WAAW,SAA6B;CACtD,MAAM,gBACJ,SAAS,iBAAiB,QAAQ,IAAI,wBAAwB,QAAQ,KAAK;AAE7E,QAAO,CACL;EACE,MAAM;EACN,SAAS;GAKP,MAAM,kBAAkB,EAAE,SAAS,CAAC,WAAW,UAAU,EAAE;AAC3D,iBAAc,yBAAyB;IACrC,gBAAgB,yBAAyB;IACzC,mBAAmB;IACpB,CAAC;AAEF,UAAO;KACJ,yBAAyB,GAAG;IAC7B,KAAK;KACH,YAAY;MACV;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACD;KAOD,cAAc;MACZ,SAAS;OACP,GAAI,YAAY,GAAG,0BAA0B,EAAE;OAC/C;OACA;OACA,GAAI,OAAO,QAAQ,MAAM,GAAG,KACxB,CAAC,6BAA6B,GAC9B,EAAE;OACP;MACD,GAAI,YAAY,GACZ,EAAE,iBAAiB,EAAE,SAAS,CAAC,qBAAqB,CAAC,EAAE,EAAE,GACzD,EAAE;MACP;KACF;IACD,cAAc;KACZ,SAAS;MACP;MACA;MACA,GAAI,OAAO,QAAQ,MAAM,GAAG,KACxB,CAAC,6BAA6B,GAC9B,EAAE;MACN;MACD;KACD,SAAS;MACP;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACD;KACF;IACF;;EAEJ,EACD;EACE,MAAM;EACN,MAAM,OAAO,QAAQ,EAAE,WAAW;AAShC,UARkB,MAAM,mBAAmB;IACzC,MAAM;IACN,SAAS,YAAY;IACrB,gBAAgB;IAChB,yBAAyB,SAAS;AAChC,YAAO,QAAQ,aAAa,QAAQ,UAAU,SAAS,OAAO;;IAEjE,CAAC;;EAGL,CACF"}
@@ -1,3 +1,5 @@
1
+ import { serverFnFileId } from "../server-fn-id.js";
2
+ import { injectServerFnIds } from "../inject-server-fn-ids.js";
1
3
  import { SERVER_MODE_ID } from "../../server-mode-plugin.js";
2
4
  import { getPageHandlers } from "./get-page-handlers.js";
3
5
  import { pageEndpointsPlugin } from "./page-endpoints-plugin.js";
@@ -5,25 +7,15 @@ import { getMatchingContentFilesWithFrontMatter } from "./get-content-files.js";
5
7
  import { buildSitemap } from "./build-sitemap.js";
6
8
  import { addPostRenderingHooks } from "./post-rendering-hook.js";
7
9
  import { createI18nPostRenderingHook, expandRoutesWithLocales } from "./i18n-prerender.js";
8
- import { angularLinkerPlugin } from "./angular-linker-plugin.js";
9
- import { basename, dirname, relative, resolve } from "node:path";
10
+ import { getServerFnHandlers } from "./get-server-fn-handlers.js";
11
+ import { SERVER_FN_DISPATCH_VIRTUAL, buildServerFnDispatchModule, getServerFnDispatchHandler } from "./server-fn-endpoints.js";
12
+ import { createRequire } from "node:module";
13
+ import { basename, dirname, join, relative, resolve } from "node:path";
10
14
  import { existsSync, readFileSync, readdirSync, unlinkSync } from "node:fs";
11
15
  import { mkdir, writeFile } from "node:fs/promises";
12
16
  //#region packages/platform/src/lib/nitro/analog-nitro-plugin.ts
13
17
  var SSR_ENTRY_VIRTUAL_ID = "\0virtual:@analogjs/nitro/ssr-entry";
14
18
  /**
15
- * Angular packages that ship in partial-compilation form and must pass
16
- * through the linker before the SSR / nitro bundle can execute without
17
- * JIT. Wired into `ssr.optimizeDeps.rolldownOptions.plugins`.
18
- */
19
- var ANGULAR_SSR_DEPS = [
20
- "@angular/compiler",
21
- "@angular/core",
22
- "@angular/common",
23
- "@angular/platform-browser",
24
- "@angular/platform-server"
25
- ];
26
- /**
27
19
  * Analog's NitroModule. Plug it into the Vite plugin chain alongside
28
20
  * `nitro()` from `nitro/vite`; nitro/vite picks up the `.nitro` property
29
21
  * and runs `setup(nitro)` once the Nitro instance is ready.
@@ -41,6 +33,8 @@ function analogNitroPlugin(options = {}) {
41
33
  let ssrEntryMarkerPath = "";
42
34
  let userPublicDir;
43
35
  let isBuild = false;
36
+ let enabled = false;
37
+ let nitroH3Resolver;
44
38
  const ssrDepsReady = /* @__PURE__ */ new WeakMap();
45
39
  let builtIndexHtml;
46
40
  function waitForSsrDeps(environment) {
@@ -77,8 +71,11 @@ function analogNitroPlugin(options = {}) {
77
71
  name: "@analogjs/nitro",
78
72
  enforce: "pre",
79
73
  config(userConfig, configEnv) {
74
+ enabled = hasNitroVitePlugin(userConfig);
75
+ if (!enabled) return;
80
76
  refreshContext(userConfig.root);
81
77
  isBuild = configEnv?.command === "build";
78
+ nitroH3Resolver = nitroH3Plugin(resolve(context.workspaceRoot, context.rootDir));
82
79
  if (userConfig.publicDir !== false) userPublicDir = resolve(context.workspaceRoot, context.rootDir, userConfig.publicDir ?? "public");
83
80
  if (!process.env["NITRO_PRESET"]) {
84
81
  if (process.env["BUILD_PRESET"]) process.env["NITRO_PRESET"] = process.env["BUILD_PRESET"];
@@ -92,24 +89,19 @@ function analogNitroPlugin(options = {}) {
92
89
  };
93
90
  if (ssr) {
94
91
  overrides.experimental = { vite: { services: { ssr: { entry: ssrEntryMarkerPath } } } };
95
- overrides.environments["ssr"] = {
96
- build: {
97
- outDir: resolve(context.workspaceRoot, "dist", context.rootDir, "ssr"),
98
- rollupOptions: { input: { index: ssrEntryMarkerPath } }
99
- },
100
- optimizeDeps: {
101
- include: ANGULAR_SSR_DEPS,
102
- rolldownOptions: { plugins: [angularLinkerPlugin()] }
103
- }
104
- };
92
+ overrides.environments["ssr"] = { build: {
93
+ outDir: resolve(context.workspaceRoot, "dist", context.rootDir, "ssr"),
94
+ rollupOptions: { input: { index: ssrEntryMarkerPath } }
95
+ } };
105
96
  }
106
97
  return overrides;
107
98
  },
108
99
  configureServer(server) {
109
100
  const environment = server.environments["ssr"];
110
- if (ssr && environment) waitForSsrDeps(environment).catch(() => void 0);
101
+ if (enabled && ssr && environment) waitForSsrDeps(environment).catch(() => void 0);
111
102
  },
112
- resolveId(id) {
103
+ resolveId(id, importer, resolveOptions) {
104
+ if (enabled && id === "h3") return nitroH3Resolver.resolveId.call(this, id, importer, resolveOptions);
113
105
  if (id === ssrEntryMarkerPath || id === SSR_ENTRY_VIRTUAL_ID) {
114
106
  if (ssr && !isBuild && this.environment.name === "ssr") return waitForSsrDeps(this.environment).then(() => SSR_ENTRY_VIRTUAL_ID);
115
107
  return SSR_ENTRY_VIRTUAL_ID;
@@ -121,7 +113,7 @@ function analogNitroPlugin(options = {}) {
121
113
  return generateSsrEntryWrapper(resolveEntryServer(), readIndexHtml());
122
114
  },
123
115
  writeBundle(_outputOptions, bundle) {
124
- if (this.environment?.name !== "client") return;
116
+ if (!enabled || this.environment?.name !== "client") return;
125
117
  const emitted = bundle[basename(indexHtmlPath())];
126
118
  if (emitted?.type === "asset") {
127
119
  const { source } = emitted;
@@ -215,10 +207,12 @@ function analogNitroPlugin(options = {}) {
215
207
  additionalPagesDirs: options.additionalPagesDirs
216
208
  });
217
209
  nitro.options.handlers.push(...pageHandlers);
210
+ registerServerFns(nitro, context);
218
211
  const serverDir = resolve(context.workspaceRoot, context.rootDir, `${context.sourceRoot}/server`);
219
- if (existsSync(serverDir) && !nitro.options.scanDirs.includes(serverDir)) nitro.options.scanDirs.push(serverDir);
212
+ const apiDirs = (options.additionalAPIDirs ?? []).map((dir) => resolve(context.workspaceRoot, `.${dir}`));
213
+ for (const dir of [serverDir, ...apiDirs]) if (existsSync(dir) && !nitro.options.scanDirs.includes(dir)) nitro.options.scanDirs.push(dir);
220
214
  nitro.hooks.hook("rollup:before", (_n, rollupConfig) => {
221
- if (Array.isArray(rollupConfig.plugins)) rollupConfig.plugins.push(pageEndpointsPlugin());
215
+ if (Array.isArray(rollupConfig.plugins)) rollupConfig.plugins.push(nitroH3Resolver, pageEndpointsPlugin(), serverFnIdPlugin(resolve(context.workspaceRoot, context.rootDir)));
222
216
  applyAnalogNitroExternals(rollupConfig);
223
217
  sanitizeNitroBundlerConfig(rollupConfig);
224
218
  });
@@ -230,10 +224,22 @@ function analogNitroPlugin(options = {}) {
230
224
  delete nitro.options.renderer.template;
231
225
  nitro.hooks.hook("prerender:config", (prerendererConfig) => {
232
226
  prerendererConfig.renderer = false;
227
+ prerendererConfig.routeRules = nitro.options.routeRules;
233
228
  });
234
229
  nitro.hooks.hook("prerender:init", (prerenderer) => {
235
230
  prerenderer.options.renderer = { handler: "#analog/ssr-renderer" };
236
231
  });
232
+ nitro.hooks.hook("prerender:routes", () => {
233
+ const publicDir = resolve(nitro.options.output.publicDir);
234
+ for (const extension of [
235
+ "",
236
+ ".br",
237
+ ".gz"
238
+ ]) {
239
+ const indexFile = resolve(publicDir, `index.html${extension}`);
240
+ if (existsSync(indexFile)) unlinkSync(indexFile);
241
+ }
242
+ });
237
243
  }
238
244
  injectAnalogRouteRuleHeaders(nitro);
239
245
  await wirePrerender(nitro, options, context, apiPrefix);
@@ -244,6 +250,59 @@ function analogNitroPlugin(options = {}) {
244
250
  } }
245
251
  };
246
252
  }
253
+ function hasNitroVitePlugin(userConfig) {
254
+ return (userConfig.plugins ?? []).flat(Infinity).some((plugin) => plugin?.name === "nitro:main");
255
+ }
256
+ /**
257
+ * Resolves `h3` from Nitro's own package, so server code always uses the h3
258
+ * version the server runs on.
259
+ */
260
+ function nitroH3Plugin(root) {
261
+ let nitroPackageJson;
262
+ return {
263
+ name: "analogjs-nitro-h3",
264
+ resolveId(id, _importer, resolveOptions) {
265
+ if (id !== "h3") return null;
266
+ nitroPackageJson ??= createRequire(join(root, "noop.js")).resolve("nitro/package.json");
267
+ return this.resolve(id, nitroPackageJson, {
268
+ ...resolveOptions,
269
+ skipSelf: true
270
+ });
271
+ }
272
+ };
273
+ }
274
+ /**
275
+ * Registers the `/_analog/fn/:id` dispatch handler when the app defines server
276
+ * functions in `*.server.ts` files.
277
+ */
278
+ function registerServerFns(nitro, context) {
279
+ const modules = getServerFnHandlers(context);
280
+ if (!modules.length) return;
281
+ const root = resolve(context.workspaceRoot, context.rootDir);
282
+ const appConfigModule = [`${root}/${context.sourceRoot}/app/app.config.server.ts`, `${root}/${context.sourceRoot}/app.config.server.ts`].find((candidate) => existsSync(candidate));
283
+ nitro.options.handlers.push(getServerFnDispatchHandler());
284
+ nitro.options.virtual[SERVER_FN_DISPATCH_VIRTUAL] = buildServerFnDispatchModule({
285
+ modules,
286
+ appConfigModule
287
+ });
288
+ }
289
+ /**
290
+ * Stamps the derived id into each `serverFn` in the Nitro server bundle, so it
291
+ * registers under the route the client calls.
292
+ */
293
+ function serverFnIdPlugin(projectRoot) {
294
+ return {
295
+ name: "analogjs-server-fn-id",
296
+ transform(code, id) {
297
+ if (!id.endsWith(".server.ts")) return;
298
+ const injected = injectServerFnIds(code, serverFnFileId(id, projectRoot));
299
+ return injected ? {
300
+ code: injected.code,
301
+ map: null
302
+ } : void 0;
303
+ }
304
+ };
305
+ }
247
306
  /**
248
307
  * Builds the h3 handler installed as Nitro's `renderer.handler`. Short-circuits
249
308
  * `ssr: false` routes to the raw client template; otherwise dispatches the
@@ -254,20 +313,30 @@ function analogNitroPlugin(options = {}) {
254
313
  function generateSsrRendererVirtual(template) {
255
314
  return `
256
315
  import { defineHandler } from 'nitro/h3';
316
+ import { getRouteRules } from 'nitro/app';
257
317
  import ssr from '#analog/ssr';
258
318
 
259
319
  const TEMPLATE = ${JSON.stringify(template)};
260
320
 
261
321
  export default defineHandler(async (event) => {
262
322
  event.res.headers.set('content-type', 'text/html; charset=utf-8');
263
- // 'x-analog-no-ssr' is stamped on response headers by
323
+ // 'x-analog-no-ssr' is stamped on the route rule headers by
264
324
  // injectAnalogRouteRuleHeaders for routeRules with \`ssr: false\`. Nitro
265
- // applies routeRule headers to the response before the renderer fires,
266
- // so we can short-circuit by reading them here.
267
- if (event.res.headers.get('x-analog-no-ssr') === 'true') {
325
+ // writes those headers after the handler runs, so read the matched rules.
326
+ const { routeRules } = getRouteRules(event.req.method, event.url.pathname);
327
+ if (routeRules?.headers?.['x-analog-no-ssr'] === 'true') {
268
328
  return TEMPLATE;
269
329
  }
270
330
  const service = ssr.default ?? ssr;
331
+ // The SSR service renders without a Node response, so forward the
332
+ // \`streaming: false\` rule on the request for the renderer to read.
333
+ if (routeRules?.headers?.['x-analog-no-streaming'] === 'true') {
334
+ const headers = new Headers(event.req.headers);
335
+ headers.set('x-analog-no-streaming', 'true');
336
+ return service.fetch(
337
+ new Request(event.req.url, { method: event.req.method, headers }),
338
+ );
339
+ }
271
340
  return service.fetch(event.req);
272
341
  });
273
342
  `;
@@ -496,18 +565,6 @@ async function wirePrerender(nitro, options, context, apiPrefix) {
496
565
  nitroPrerender.routes ??= [];
497
566
  nitroPrerender.routes.push(...expanded);
498
567
  if (prerender?.discover ?? false) nitroPrerender.crawlLinks = true;
499
- const prerendersRoot = nitroPrerender.routes.some((route) => route === "/");
500
- if ((options.ssr ?? true) && prerendersRoot) nitro.hooks.hook("prerender:init", () => {
501
- const publicDir = resolve(nitro.options.output.publicDir);
502
- for (const extension of [
503
- "",
504
- ".br",
505
- ".gz"
506
- ]) {
507
- const indexFile = resolve(publicDir, `index.html${extension}`);
508
- if (existsSync(indexFile)) unlinkSync(indexFile);
509
- }
510
- });
511
568
  if (prerender?.postRenderingHooks?.length) addPostRenderingHooks(nitro, prerender.postRenderingHooks);
512
569
  if (Object.keys(routeSourceFiles).length > 0) nitro.hooks.hook("prerender:done", async () => {
513
570
  const publicDir = resolve(nitro.options.output.publicDir);