@analogjs/platform 3.0.0-alpha.90 → 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 (53) 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/index.d.ts +1 -2
  6. package/src/index.js +1 -2
  7. package/src/index.js.map +1 -1
  8. package/src/lib/clear-client-page-endpoint.js +1 -0
  9. package/src/lib/clear-client-page-endpoint.js.map +1 -1
  10. package/src/lib/deps-plugin.js +17 -6
  11. package/src/lib/deps-plugin.js.map +1 -1
  12. package/src/lib/nitro/analog-nitro-plugin.js +102 -45
  13. package/src/lib/nitro/analog-nitro-plugin.js.map +1 -1
  14. package/src/lib/nitro/get-server-fn-handlers.d.ts +29 -0
  15. package/src/lib/nitro/get-server-fn-handlers.js +66 -0
  16. package/src/lib/nitro/get-server-fn-handlers.js.map +1 -0
  17. package/src/lib/nitro/server-fn-endpoints.d.ts +41 -0
  18. package/src/lib/nitro/server-fn-endpoints.js +57 -0
  19. package/src/lib/nitro/server-fn-endpoints.js.map +1 -0
  20. package/src/lib/nx-plugin/src/generators/app/files/template-angular/src/server/routes/api/v1/hello.ts__template__ +2 -2
  21. package/src/lib/nx-plugin/src/generators/app/files/template-angular/vite.config.ts__template__ +4 -0
  22. package/src/lib/nx-plugin/src/generators/app/versions/nx_18_X/versions.d.ts +7 -5
  23. package/src/lib/nx-plugin/src/generators/app/versions/nx_18_X/versions.js.map +1 -1
  24. package/src/lib/nx-plugin/src/generators/preset/__snapshots__/generator.spec.ts.snap +3 -1
  25. package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.d.ts +5 -5
  26. package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.js +5 -5
  27. package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.js.map +1 -1
  28. package/src/lib/options.d.ts +9 -49
  29. package/src/lib/platform-plugin.js +42 -6
  30. package/src/lib/platform-plugin.js.map +1 -1
  31. package/src/lib/route-file-discovery.d.ts +3 -1
  32. package/src/lib/route-file-discovery.js +19 -8
  33. package/src/lib/route-file-discovery.js.map +1 -1
  34. package/src/lib/route-manifest.d.ts +22 -26
  35. package/src/lib/route-manifest.js +58 -189
  36. package/src/lib/route-manifest.js.map +1 -1
  37. package/src/lib/ssr/defer-streaming-plugin.js +97 -0
  38. package/src/lib/ssr/defer-streaming-plugin.js.map +1 -0
  39. package/src/lib/typed-routes-plugin.d.ts +6 -43
  40. package/src/lib/typed-routes-plugin.js +80 -195
  41. package/src/lib/typed-routes-plugin.js.map +1 -1
  42. package/src/lib/utils/debug.js +4 -7
  43. package/src/lib/utils/debug.js.map +1 -1
  44. package/src/lib/utils/rolldown.js +1 -1
  45. package/src/server-mode-plugin.d.ts +1 -1
  46. package/src/server-mode-plugin.js +13 -1
  47. package/src/server-mode-plugin.js.map +1 -1
  48. package/src/lib/json-ld-manifest-plugin.d.ts +0 -41
  49. package/src/lib/json-ld-manifest-plugin.js +0 -84
  50. package/src/lib/json-ld-manifest-plugin.js.map +0 -1
  51. package/src/lib/route-generation-plugin.d.ts +0 -3
  52. package/src/lib/route-generation-plugin.js +0 -45
  53. package/src/lib/route-generation-plugin.js.map +0 -1
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Whether the streaming SSR patch can be applied to the installed Angular.
3
+ * A `null` major (version undetectable) returns `true`: don't block on a failed
4
+ * detection — the plugin's anchor-drift detection catches a real mismatch at
5
+ * build time and falls back to buffered rendering.
6
+ */
7
+ function streamingSupportedOnAngular(major) {
8
+ return major === null || major >= 21;
9
+ }
10
+ /**
11
+ * Pure transform that injects the streaming-SSR per-block resolution hook into
12
+ * `@angular/core`'s compiled output. It is the same class of transform as
13
+ * `i18nDefRegistryPlugin` in ./ssr-build-plugin — a string patch of Angular's
14
+ * bundle applied only to SSR builds. Exported separately so it can be unit
15
+ * tested against a bundle string.
16
+ *
17
+ * Two edits, both keyed on single-occurrence anchors in the module that holds
18
+ * the defer runtime:
19
+ * 1. Inside `applyDeferBlockState`, fire `globalThis.__analogSsrDeferCapture`
20
+ * when a `@defer` block reaches `Complete` on the server, passing the
21
+ * block's live `lContainer` — this is the per-block resolution signal a
22
+ * streaming renderer consumes.
23
+ * 2. Expose `collectNativeNodesInLContainer` via `globalThis.__analogSsrInternals`
24
+ * so the renderer can serialize a block's subtree.
25
+ *
26
+ * Returns `null` when the module is not the one carrying the defer runtime, so
27
+ * it is a no-op on every other Angular module.
28
+ */
29
+ function injectDeferStreamingHook(code) {
30
+ const END_ANCHOR = "profiler(ProfilerEvent.DeferBlockStateEnd);";
31
+ if (!code.includes("function applyDeferBlockState(") || !code.includes(END_ANCHOR)) return null;
32
+ let out = code.replace(END_ANCHOR, "try { if (newState === DeferBlockState.Complete && typeof ngServerMode !== 'undefined' && ngServerMode && typeof globalThis.__analogSsrDeferCapture === 'function') { globalThis.__analogSsrDeferCapture({ ssrUniqueId: lDetails[SSR_UNIQUE_ID], lContainer, hostLView }); } } catch (e) {}\n " + END_ANCHOR);
33
+ if (code.includes("function collectNativeNodesInLContainer(")) out += "\nglobalThis.__analogSsrInternals = Object.assign(globalThis.__analogSsrInternals || {}, { collectNativeNodesInLContainer });\n";
34
+ return out;
35
+ }
36
+ /**
37
+ * Classify an `@angular/core` module for the streaming patch. The patch anchors
38
+ * on internal symbol names, so when Angular changes those the transform would
39
+ * otherwise become a silent no-op and streaming would degrade to buffered with
40
+ * no signal. This distinguishes "not the target module" (skip quietly) from
41
+ * "this IS the `@defer` runtime module but the anchors drifted" (worth warning).
42
+ */
43
+ function inspectAngularCoreModule(code) {
44
+ if (!code.includes("function applyDeferBlockState(")) return { kind: "not-target" };
45
+ const missing = [];
46
+ if (!code.includes("profiler(ProfilerEvent.DeferBlockStateEnd);")) missing.push("DeferBlockStateEnd profiler anchor");
47
+ if (!code.includes("function collectNativeNodesInLContainer(")) missing.push("collectNativeNodesInLContainer");
48
+ return missing.length === 0 ? { kind: "patchable" } : {
49
+ kind: "drifted",
50
+ reason: `missing ${missing.join(", ")}`
51
+ };
52
+ }
53
+ /**
54
+ * Vite plugin that applies {@link injectDeferStreamingHook} to `@angular/core`
55
+ * during SSR builds, so `@analogjs/router`'s `renderStream` can flush `@defer`
56
+ * blocks as they resolve on the server — without hand-patching node_modules.
57
+ *
58
+ * Only active for SSR. Mirrors the enforce/filter/ssr-gate shape of
59
+ * `i18nDefRegistryPlugin`. If the `@defer` runtime module is found but its
60
+ * anchors have drifted (Angular changed internals), or if it is never
61
+ * encountered at all, the plugin warns rather than silently producing a build
62
+ * that falls back to buffered rendering.
63
+ */
64
+ function deferStreamingPlugin() {
65
+ let applied = false;
66
+ let warnedDrift = false;
67
+ return {
68
+ name: "analogjs-defer-streaming",
69
+ enforce: "post",
70
+ transform: {
71
+ filter: { id: /\/@angular\/core\// },
72
+ handler(code, _id, options) {
73
+ if (!options?.ssr) return;
74
+ const info = inspectAngularCoreModule(code);
75
+ if (info.kind === "not-target") return;
76
+ if (info.kind === "drifted") {
77
+ if (!warnedDrift) {
78
+ warnedDrift = true;
79
+ this.warn(`experimental streaming SSR: found @angular/core's @defer runtime but could not apply the resolution hook (${info.reason}). The installed Angular version likely changed internals the patch depends on; streaming will fall back to buffered rendering.`);
80
+ }
81
+ return;
82
+ }
83
+ const out = injectDeferStreamingHook(code);
84
+ if (!out) return;
85
+ applied = true;
86
+ return { code: out };
87
+ }
88
+ },
89
+ buildEnd() {
90
+ if (!applied && !warnedDrift) this.warn("experimental streaming SSR is enabled but @angular/core's @defer runtime module was never encountered during the SSR build, so the resolution hook was not injected. Streaming will fall back to buffered rendering.");
91
+ }
92
+ };
93
+ }
94
+ //#endregion
95
+ export { deferStreamingPlugin, streamingSupportedOnAngular };
96
+
97
+ //# sourceMappingURL=defer-streaming-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defer-streaming-plugin.js","names":[],"sources":["../../../../src/lib/ssr/defer-streaming-plugin.ts"],"sourcesContent":["import type { Plugin } from 'vite';\n\n/**\n * Minimum Angular major whose compiled `@angular/core` FESM matches the\n * streaming patch anchors. Incremental hydration is a stable public API from\n * v20 (`withIncrementalHydration`, `@publicApi 20.0`), but v20's FESM inlines\n * the injection anchor's `DeferBlockStateEnd` profiler event to its numeric\n * ordinal, whereas v21+ keeps the symbolic `ProfilerEvent.DeferBlockStateEnd`\n * form the anchor matches on. Keying on the literal ordinal would be fragile\n * (enum values shift between versions), so v21 is the floor.\n */\nexport const MIN_STREAMING_ANGULAR_MAJOR = 21;\n\n/**\n * Whether the streaming SSR patch can be applied to the installed Angular.\n * A `null` major (version undetectable) returns `true`: don't block on a failed\n * detection — the plugin's anchor-drift detection catches a real mismatch at\n * build time and falls back to buffered rendering.\n */\nexport function streamingSupportedOnAngular(major: number | null): boolean {\n return major === null || major >= MIN_STREAMING_ANGULAR_MAJOR;\n}\n\n/**\n * Pure transform that injects the streaming-SSR per-block resolution hook into\n * `@angular/core`'s compiled output. It is the same class of transform as\n * `i18nDefRegistryPlugin` in ./ssr-build-plugin — a string patch of Angular's\n * bundle applied only to SSR builds. Exported separately so it can be unit\n * tested against a bundle string.\n *\n * Two edits, both keyed on single-occurrence anchors in the module that holds\n * the defer runtime:\n * 1. Inside `applyDeferBlockState`, fire `globalThis.__analogSsrDeferCapture`\n * when a `@defer` block reaches `Complete` on the server, passing the\n * block's live `lContainer` — this is the per-block resolution signal a\n * streaming renderer consumes.\n * 2. Expose `collectNativeNodesInLContainer` via `globalThis.__analogSsrInternals`\n * so the renderer can serialize a block's subtree.\n *\n * Returns `null` when the module is not the one carrying the defer runtime, so\n * it is a no-op on every other Angular module.\n */\nexport function injectDeferStreamingHook(code: string): string | null {\n const END_ANCHOR = 'profiler(ProfilerEvent.DeferBlockStateEnd);';\n if (\n !code.includes('function applyDeferBlockState(') ||\n !code.includes(END_ANCHOR)\n ) {\n return null;\n }\n\n // Wrap the entire guard — not just the call — so a drifted Angular runtime\n // that still matches the anchors but renamed these locals fails as a silent\n // no-op instead of throwing a ReferenceError inside applyDeferBlockState.\n const capture =\n `try { if (newState === DeferBlockState.Complete && ` +\n `typeof ngServerMode !== 'undefined' && ngServerMode && ` +\n `typeof globalThis.__analogSsrDeferCapture === 'function') { ` +\n `globalThis.__analogSsrDeferCapture({ ssrUniqueId: lDetails[SSR_UNIQUE_ID], lContainer, hostLView }); } } catch (e) {}\\n `;\n\n let out = code.replace(END_ANCHOR, capture + END_ANCHOR);\n\n if (code.includes('function collectNativeNodesInLContainer(')) {\n out +=\n '\\nglobalThis.__analogSsrInternals = Object.assign(globalThis.__analogSsrInternals || {}, { collectNativeNodesInLContainer });\\n';\n }\n\n return out;\n}\n\n/**\n * Classify an `@angular/core` module for the streaming patch. The patch anchors\n * on internal symbol names, so when Angular changes those the transform would\n * otherwise become a silent no-op and streaming would degrade to buffered with\n * no signal. This distinguishes \"not the target module\" (skip quietly) from\n * \"this IS the `@defer` runtime module but the anchors drifted\" (worth warning).\n */\nexport function inspectAngularCoreModule(\n code: string,\n):\n | { kind: 'not-target' }\n | { kind: 'patchable' }\n | { kind: 'drifted'; reason: string } {\n if (!code.includes('function applyDeferBlockState(')) {\n return { kind: 'not-target' };\n }\n const missing: string[] = [];\n if (!code.includes('profiler(ProfilerEvent.DeferBlockStateEnd);')) {\n missing.push('DeferBlockStateEnd profiler anchor');\n }\n if (!code.includes('function collectNativeNodesInLContainer(')) {\n missing.push('collectNativeNodesInLContainer');\n }\n return missing.length === 0\n ? { kind: 'patchable' }\n : { kind: 'drifted', reason: `missing ${missing.join(', ')}` };\n}\n\n/**\n * Vite plugin that applies {@link injectDeferStreamingHook} to `@angular/core`\n * during SSR builds, so `@analogjs/router`'s `renderStream` can flush `@defer`\n * blocks as they resolve on the server — without hand-patching node_modules.\n *\n * Only active for SSR. Mirrors the enforce/filter/ssr-gate shape of\n * `i18nDefRegistryPlugin`. If the `@defer` runtime module is found but its\n * anchors have drifted (Angular changed internals), or if it is never\n * encountered at all, the plugin warns rather than silently producing a build\n * that falls back to buffered rendering.\n */\nexport function deferStreamingPlugin(): Plugin {\n let applied = false;\n let warnedDrift = false;\n return {\n name: 'analogjs-defer-streaming',\n enforce: 'post',\n transform: {\n filter: {\n id: /\\/@angular\\/core\\//,\n },\n handler(code, _id, options) {\n if (!options?.ssr) return;\n const info = inspectAngularCoreModule(code);\n if (info.kind === 'not-target') return;\n if (info.kind === 'drifted') {\n if (!warnedDrift) {\n warnedDrift = true;\n this.warn(\n `experimental streaming SSR: found @angular/core's @defer runtime ` +\n `but could not apply the resolution hook (${info.reason}). The ` +\n `installed Angular version likely changed internals the patch ` +\n `depends on; streaming will fall back to buffered rendering.`,\n );\n }\n return;\n }\n const out = injectDeferStreamingHook(code);\n if (!out) return;\n applied = true;\n return { code: out };\n },\n },\n buildEnd() {\n if (!applied && !warnedDrift) {\n this.warn(\n `experimental streaming SSR is enabled but @angular/core's @defer ` +\n `runtime module was never encountered during the SSR build, so the ` +\n `resolution hook was not injected. Streaming will fall back to ` +\n `buffered rendering.`,\n );\n }\n },\n };\n}\n"],"mappings":";;;;;;AAmBA,SAAgB,4BAA4B,OAA+B;AACzE,QAAO,UAAU,QAAQ,SAAA;;;;;;;;;;;;;;;;;;;;;AAsB3B,SAAgB,yBAAyB,MAA6B;CACpE,MAAM,aAAa;AACnB,KACE,CAAC,KAAK,SAAS,iCAAiC,IAChD,CAAC,KAAK,SAAS,WAAW,CAE1B,QAAO;CAYT,IAAI,MAAM,KAAK,QAAQ,YALrB,oSAK2C,WAAW;AAExD,KAAI,KAAK,SAAS,2CAA2C,CAC3D,QACE;AAGJ,QAAO;;;;;;;;;AAUT,SAAgB,yBACd,MAIsC;AACtC,KAAI,CAAC,KAAK,SAAS,iCAAiC,CAClD,QAAO,EAAE,MAAM,cAAc;CAE/B,MAAM,UAAoB,EAAE;AAC5B,KAAI,CAAC,KAAK,SAAS,8CAA8C,CAC/D,SAAQ,KAAK,qCAAqC;AAEpD,KAAI,CAAC,KAAK,SAAS,2CAA2C,CAC5D,SAAQ,KAAK,iCAAiC;AAEhD,QAAO,QAAQ,WAAW,IACtB,EAAE,MAAM,aAAa,GACrB;EAAE,MAAM;EAAW,QAAQ,WAAW,QAAQ,KAAK,KAAK;EAAI;;;;;;;;;;;;;AAclE,SAAgB,uBAA+B;CAC7C,IAAI,UAAU;CACd,IAAI,cAAc;AAClB,QAAO;EACL,MAAM;EACN,SAAS;EACT,WAAW;GACT,QAAQ,EACN,IAAI,sBACL;GACD,QAAQ,MAAM,KAAK,SAAS;AAC1B,QAAI,CAAC,SAAS,IAAK;IACnB,MAAM,OAAO,yBAAyB,KAAK;AAC3C,QAAI,KAAK,SAAS,aAAc;AAChC,QAAI,KAAK,SAAS,WAAW;AAC3B,SAAI,CAAC,aAAa;AAChB,oBAAc;AACd,WAAK,KACH,6GAC8C,KAAK,OAAO,iIAG3D;;AAEH;;IAEF,MAAM,MAAM,yBAAyB,KAAK;AAC1C,QAAI,CAAC,IAAK;AACV,cAAU;AACV,WAAO,EAAE,MAAM,KAAK;;GAEvB;EACD,WAAW;AACT,OAAI,CAAC,WAAW,CAAC,YACf,MAAK,KACH,uNAID;;EAGN"}
@@ -1,50 +1,13 @@
1
1
  import { type Plugin } from "vite";
2
- export interface TypedRoutesPluginOptions {
3
- /**
4
- * Output path for the single generated route module,
5
- * relative to the app root.
6
- *
7
- * @default 'src/routeTree.gen.ts'
8
- */
2
+ export interface TypedRoutingOptions {
3
+ /** Generated declaration path (.d.ts), relative to the app root. */
9
4
  outFile?: string;
10
- /**
11
- * Workspace root used to resolve additional route/content directories.
12
- *
13
- * @default process.env['NX_WORKSPACE_ROOT'] ?? process.cwd()
14
- */
5
+ /** Fail builds when a checked-in route table is stale. Defaults to true. */
6
+ verifyOnBuild?: boolean;
7
+ }
8
+ export interface TypedRoutesPluginOptions extends TypedRoutingOptions {
15
9
  workspaceRoot?: string;
16
- /**
17
- * Additional page directories to scan for `.page.ts` files.
18
- */
19
10
  additionalPagesDirs?: string[];
20
- /**
21
- * Additional content directories to scan for `.md` files.
22
- */
23
11
  additionalContentDirs?: string[];
24
- /**
25
- * Include generated `routeJsonLdManifest` exports in the generated route file.
26
- *
27
- * @default true
28
- */
29
- jsonLdManifest?: boolean;
30
- /**
31
- * When true, compare generated output against the existing file and
32
- * throw an error if they differ instead of writing. Useful for CI to
33
- * detect stale checked-in route files.
34
- *
35
- * @default false
36
- */
37
- verify?: boolean;
38
- /**
39
- * When true, production builds fail after regenerating a stale checked-in
40
- * route file. This preserves self-healing writes in development while making
41
- * build-time freshness issues visible by default.
42
- *
43
- * @default true
44
- */
45
- verifyOnBuild?: boolean;
46
12
  }
47
- /**
48
- * Vite plugin that generates a single typed route module for Analog file routes.
49
- */
50
13
  export declare function typedRoutes(options?: TypedRoutesPluginOptions): Plugin;
@@ -1,232 +1,117 @@
1
- import { debugTypedRouter } from "./utils/debug.js";
2
- import { detectSchemaExports, filenameToRoutePath, formatManifestSummary, generateRouteManifest, generateRouteTableDeclaration, generateRouteTreeDeclaration } from "./route-manifest.js";
3
- import { detectJsonLdModuleExports, extractMarkdownJsonLd, generateJsonLdManifestSource } from "./json-ld-manifest-plugin.js";
4
1
  import { createRouteFileDiscovery } from "./route-file-discovery.js";
2
+ import { generateRouteManifest, generateRouteTableDeclaration } from "./route-manifest.js";
5
3
  import { dirname, join, relative, resolve } from "node:path";
6
4
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
5
  import { normalizePath } from "vite";
6
+ import { parseSync } from "oxc-parser";
7
+ import { readConfiguration } from "@angular/compiler-cli";
8
8
  //#region packages/platform/src/lib/typed-routes-plugin.ts
9
- var DEFAULT_OUT_FILE = "src/routeTree.gen.ts";
10
- function resolvePluginOptions(options = {}) {
11
- return {
12
- outFile: options.outFile ?? DEFAULT_OUT_FILE,
13
- workspaceRoot: options.workspaceRoot ?? process.env["NX_WORKSPACE_ROOT"] ?? process.cwd(),
14
- additionalPagesDirs: options.additionalPagesDirs ?? [],
15
- additionalContentDirs: options.additionalContentDirs ?? [],
16
- jsonLdManifest: options.jsonLdManifest ?? true,
17
- verify: options.verify ?? false,
18
- verifyOnBuild: options.verifyOnBuild ?? true
19
- };
20
- }
21
- /**
22
- * Vite plugin that generates a single typed route module for Analog file routes.
23
- */
24
9
  function typedRoutes(options = {}) {
25
- const resolvedOptions = resolvePluginOptions(options);
26
- const workspaceRoot = normalizePath(resolvedOptions.workspaceRoot);
27
- let root = "";
28
- let command = "serve";
10
+ const workspaceRoot = normalizePath(options.workspaceRoot ?? process.cwd());
11
+ const outFile = options.outFile ?? "src/routeTree.gen.d.ts";
12
+ if (!outFile.endsWith(".d.ts")) throw new Error("[analog] Typed routing outFile must end in .d.ts. Include this declaration in your application tsconfig.");
13
+ let root;
14
+ let command;
29
15
  let discovery;
30
- function isFreshnessCheck() {
31
- return resolvedOptions.verify || command === "build" && resolvedOptions.verifyOnBuild;
32
- }
33
- function resolveDiscoveredFile(filename) {
34
- const fromRoot = join(root, filename);
35
- if (existsSync(fromRoot)) return fromRoot;
36
- return join(workspaceRoot, filename);
37
- }
38
- function detectSchemas(relativeFilename) {
39
- if (!relativeFilename.endsWith(".ts")) return {
40
- hasParamsSchema: false,
41
- hasQuerySchema: false
42
- };
43
- try {
44
- return detectSchemaExports(readFileSync(resolveDiscoveredFile(relativeFilename), "utf-8"));
45
- } catch {
46
- return {
47
- hasParamsSchema: false,
48
- hasQuerySchema: false
49
- };
16
+ let getTsConfigPath;
17
+ function verifyTypeInclusion() {
18
+ if (!getTsConfigPath) return;
19
+ const tsconfig = getTsConfigPath();
20
+ const { rootNames, errors } = readConfiguration(tsconfig);
21
+ if (errors.length) throw new Error(`[analog] Cannot verify typed routing: unable to read ${tsconfig}.`);
22
+ const outputPath = normalizePath(resolve(root, outFile));
23
+ if (!rootNames.some((file) => normalizePath(resolve(file)) === outputPath)) {
24
+ const declaration = normalizePath(relative(dirname(tsconfig), outputPath));
25
+ throw new Error(`[analog] Typed route declaration is not included in ${tsconfig}. Add "${declaration}" to its "files" or "include" list so typed routing is available to the compiler and editor.`);
50
26
  }
51
27
  }
52
- /**
53
- * Ensures the generated route file is imported from an app entry file
54
- * so the module augmentation is always part of the TypeScript program.
55
- */
56
- function ensureEntryImport() {
57
- const entryFiles = ["src/main.ts", "src/main.server.ts"];
58
- function importSpecifierFor(entryFile) {
59
- const rel = relative(dirname(entryFile), resolvedOptions.outFile).replace(/\.ts$/, "").replace(/\\/g, "/");
60
- return rel.startsWith(".") ? rel : "./" + rel;
61
- }
62
- for (const entryFile of entryFiles) {
63
- const entryPath = join(root, entryFile);
64
- if (!existsSync(entryPath)) continue;
65
- const content = readFileSync(entryPath, "utf-8");
66
- const specifier = importSpecifierFor(entryFile);
67
- const escaped = specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
68
- if (new RegExp(`import\\s+['"]${escaped}(\\.ts|\\.js)?['"]`).test(content)) return;
69
- if (isFreshnessCheck()) return;
70
- const importLine = `import '${specifier}';`;
71
- const lines = content.split("\n");
72
- let lastImportLine = -1;
73
- for (let i = 0; i < lines.length; i++) if (/^import\s/.test(lines[i])) lastImportLine = i;
74
- if (lastImportLine >= 0) lines.splice(lastImportLine + 1, 0, importLine);
75
- else lines.unshift(importLine);
76
- writeFileSync(entryPath, lines.join("\n"), "utf-8");
77
- console.log(`[analog] Added route tree import to ${entryFile}`);
78
- return;
79
- }
80
- const specifier = importSpecifierFor("src/main.ts");
81
- if (isFreshnessCheck()) return;
82
- console.warn(`[analog] Could not find an entry file (src/main.ts or src/main.server.ts) to add the route tree import. Add \`import '${specifier}';\` to your app entry file to ensure typed routing is active.`);
83
- }
84
28
  function generate() {
85
- const routeFiles = discovery.getRouteFiles();
86
- const contentFiles = discovery.getContentFiles();
87
- debugTypedRouter("discovered files", {
88
- routeFiles: routeFiles.length,
89
- contentFiles: contentFiles.length
90
- });
91
- const manifest = generateRouteManifest([...routeFiles, ...contentFiles], detectSchemas, (filename) => discovery.isAppLocal(filename) ? 0 : 1);
92
- const declaration = generateRouteTableDeclaration(manifest);
93
- const canonicalFiles = new Set(manifest.routes.map((route) => route.filename));
94
- const jsonLdEntries = buildJsonLdEntries(resolveDiscoveredFile, routeFiles.filter((filename) => canonicalFiles.has(filename)), contentFiles.filter((filename) => canonicalFiles.has(filename)));
95
- const output = combineGeneratedModules(declaration, generateRouteTreeDeclaration(manifest, { jsonLdFiles: jsonLdEntries.map((entry) => entry.sourceFile) }), resolvedOptions.jsonLdManifest && jsonLdEntries.length > 0 ? generateJsonLdManifestSource(jsonLdEntries, resolvedOptions.outFile) : "");
96
- const hardCollisions = manifest.collisions.filter((c) => c.samePriority);
97
- if (manifest.collisions.length > 0) debugTypedRouter("route collisions", {
98
- total: manifest.collisions.length,
99
- hard: hardCollisions.length,
100
- collisions: manifest.collisions.map((c) => ({
101
- path: c.fullPath,
102
- kept: c.keptFile,
103
- dropped: c.droppedFile
104
- }))
29
+ const manifest = generateRouteManifest([...discovery.getRouteFiles(), ...discovery.getContentFiles()], (filename) => discovery.isAppLocal(filename) ? 0 : 1);
30
+ const collisions = manifest.collisions.filter((collision) => collision.samePriority);
31
+ if (command === "build" && collisions.length) throw new Error(`[analog] Route collisions detected: ${collisions.map((c) => `${c.fullPath}: ${c.keptFile}, ${c.droppedFile}`).join("; ")}`);
32
+ const outputPath = join(root, outFile);
33
+ const output = generateRouteTableDeclaration(manifest, (filename) => {
34
+ const pageFile = [
35
+ join(root, filename),
36
+ join(workspaceRoot, filename),
37
+ filename
38
+ ].find((file) => existsSync(file));
39
+ if (!pageFile) return void 0;
40
+ const serverFile = pageFile.replace(/\.page\.ts$/, ".server.ts");
41
+ return {
42
+ routeMeta: hasNamedExport(pageFile, "routeMeta") ? toImportSpecifier(dirname(outputPath), pageFile) : void 0,
43
+ load: serverFile !== pageFile && existsSync(serverFile) && hasNamedExport(serverFile, "load") ? toImportSpecifier(dirname(outputPath), serverFile) : void 0
44
+ };
105
45
  });
106
- if (hardCollisions.length > 0 && command === "build") {
107
- const details = hardCollisions.map((c) => ` '${c.fullPath}': '${c.keptFile}' vs '${c.droppedFile}'`).join("\n");
108
- throw new Error(`[analog] Route collisions detected during build:\n${details}\n\nEach route path must be defined by exactly one source file. Remove or rename the conflicting files to resolve the collision.`);
109
- }
110
- if (manifest.routes.length > 0) console.log(formatManifestSummary(manifest));
111
- const outPath = join(root, resolvedOptions.outFile);
112
- const outDir = dirname(outPath);
113
- const hadExistingOutput = existsSync(outPath);
114
- if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });
115
- let existing = "";
116
- try {
117
- existing = readFileSync(outPath, "utf-8");
118
- } catch {}
119
- if (output.includes(root)) console.warn(`[analog] Generated route output contains an absolute path prefix (${root}). Route keys and sourceFile values should be workspace-relative.`);
120
- const normalizeEndings = (s) => s.replace(/\r\n/g, "\n");
121
- if (normalizeEndings(existing) !== normalizeEndings(output)) {
122
- debugTypedRouter("route file changed", {
123
- outFile: resolvedOptions.outFile,
124
- routes: manifest.routes.length,
125
- verify: resolvedOptions.verify,
126
- verifyOnBuild: resolvedOptions.verifyOnBuild,
127
- command
128
- });
129
- if (resolvedOptions.verify) throw new Error(`[analog] Stale route file detected: ${resolvedOptions.outFile}\nThe checked-in generated route file does not match the current route sources.\nRegenerate route files and commit the updated output.`);
130
- writeFileSync(outPath, output, "utf-8");
131
- if (command === "build" && resolvedOptions.verifyOnBuild && hadExistingOutput) throw new Error(`[analog] Stale route file detected during build: ${resolvedOptions.outFile}\nThe generated route file was updated to match the current route sources.\nReview the updated output, commit it if it is checked in, and rerun the build.`);
46
+ const exists = existsSync(outputPath);
47
+ const current = exists ? readFileSync(outputPath, "utf8") : "";
48
+ if (current !== output && !sameDeclarations(current, output)) {
49
+ if (exists && command === "build" && (options.verifyOnBuild ?? true)) throw new Error(`[analog] Stale route file: ${outFile}. Run the dev server to regenerate it before building.`);
50
+ mkdirSync(dirname(outputPath), { recursive: true });
51
+ writeFileSync(outputPath, output);
132
52
  }
133
53
  }
134
54
  return {
135
55
  name: "analog-typed-routes",
136
56
  config(config, env) {
137
57
  command = env.command;
138
- root = normalizePath(resolve(workspaceRoot, config.root || ".") || ".");
58
+ root = normalizePath(resolve(workspaceRoot, config.root ?? "."));
139
59
  discovery = createRouteFileDiscovery({
140
60
  root,
141
61
  workspaceRoot,
142
- additionalPagesDirs: resolvedOptions.additionalPagesDirs,
143
- additionalContentDirs: resolvedOptions.additionalContentDirs
62
+ additionalPagesDirs: options.additionalPagesDirs ?? [],
63
+ additionalContentDirs: options.additionalContentDirs ?? []
144
64
  });
65
+ generate();
66
+ },
67
+ configResolved(config) {
68
+ const compiler = config.plugins.find((plugin) => plugin.name === "@analogjs/vite-plugin-angular" || plugin.name === "@analogjs/vite-plugin-angular-fast-compile" || plugin.name === "@analogjs/vite-plugin-angular-compilation-api");
69
+ getTsConfigPath = compiler?.api?.getTsConfigPath;
70
+ if (compiler && !getTsConfigPath) throw new Error("[analog] Typed routing requires a matching @analogjs/vite-plugin-angular version to verify the application tsconfig.");
71
+ verifyTypeInclusion();
145
72
  },
146
73
  buildStart() {
74
+ discovery.reset();
147
75
  generate();
148
- if (!isFreshnessCheck()) ensureEntryImport();
76
+ verifyTypeInclusion();
149
77
  },
150
78
  configureServer(server) {
151
- const regenerate = (path, event) => {
79
+ server.watcher.add(join(root, outFile));
80
+ for (const event of [
81
+ "add",
82
+ "change",
83
+ "unlink"
84
+ ]) server.watcher.on(event, (path) => {
152
85
  if (!discovery.getDiscoveredFileKind(path)) return;
153
- debugTypedRouter("watch regenerate", {
154
- event,
155
- path
156
- });
157
86
  discovery.updateDiscoveredFile(path, event);
158
87
  generate();
159
- };
160
- server.watcher.on("add", (path) => regenerate(path, "add"));
161
- server.watcher.on("change", (path) => regenerate(path, "change"));
162
- server.watcher.on("unlink", (path) => regenerate(path, "unlink"));
88
+ });
163
89
  }
164
90
  };
165
91
  }
166
- function buildJsonLdEntries(resolveFile, routeFiles, contentFiles) {
167
- const entries = [];
168
- let importIndex = 0;
169
- routeFiles.forEach((filename) => {
170
- try {
171
- if (!detectJsonLdModuleExports(readFileSync(resolveFile(filename), "utf-8"))) return;
172
- entries.push({
173
- kind: "module",
174
- routePath: filenameToRoutePath(filename),
175
- sourceFile: filename,
176
- importAlias: `routeModule${importIndex++}`
177
- });
178
- } catch {}
179
- });
180
- contentFiles.forEach((filename) => {
181
- try {
182
- const jsonLd = extractMarkdownJsonLd(readFileSync(resolveFile(filename), "utf-8"));
183
- if (jsonLd.length === 0) return;
184
- entries.push({
185
- kind: "content",
186
- routePath: filenameToRoutePath(filename),
187
- sourceFile: filename,
188
- jsonLd
189
- });
190
- } catch {}
191
- });
192
- return entries.sort((a, b) => a.routePath.localeCompare(b.routePath));
92
+ function toImportSpecifier(fromDir, file) {
93
+ const specifier = normalizePath(relative(fromDir, file)).replace(/\.ts$/, "");
94
+ return specifier.startsWith(".") ? specifier : `./${specifier}`;
193
95
  }
194
- function combineGeneratedModules(...sources) {
195
- const imports = [];
196
- const seenImports = /* @__PURE__ */ new Set();
197
- const bodies = [];
198
- for (const source of sources) {
199
- const { body, importLines } = splitGeneratedModule(source);
200
- for (const importLine of importLines) if (!seenImports.has(importLine)) {
201
- seenImports.add(importLine);
202
- imports.push(importLine);
203
- }
204
- if (body.trim()) bodies.push(body.trim());
205
- }
206
- return [
207
- "// This file is auto-generated by @analogjs/platform",
208
- "// Do not edit manually",
209
- "",
210
- ...imports.length > 0 ? [...imports, ""] : [],
211
- bodies.join("\n\n"),
212
- ""
213
- ].join("\n");
96
+ function hasNamedExport(file, name) {
97
+ const source = readFileSync(file, "utf8");
98
+ if (!source.includes(name)) return false;
99
+ const { program, errors } = parseSync(file, source);
100
+ if (errors.length) return false;
101
+ return program.body.some((statement) => {
102
+ if (statement.type !== "ExportNamedDeclaration" || statement.exportKind === "type") return false;
103
+ const declaration = statement.declaration;
104
+ if (declaration?.type === "VariableDeclaration") return declaration.declarations.some((item) => item.id.type === "Identifier" && item.id.name === name);
105
+ if (declaration?.type === "FunctionDeclaration") return declaration.id?.name === name;
106
+ return statement.specifiers.some((specifier) => specifier.exportKind !== "type" && (specifier.exported.type === "Identifier" ? specifier.exported.name : specifier.exported.value) === name);
107
+ });
214
108
  }
215
- function splitGeneratedModule(source) {
216
- const lines = source.split("\n");
217
- let index = 0;
218
- while (index < lines.length && lines[index].startsWith("//")) index++;
219
- while (index < lines.length && lines[index] === "") index++;
220
- const importLines = [];
221
- while (index < lines.length && lines[index].startsWith("import ")) {
222
- importLines.push(lines[index]);
223
- index++;
224
- }
225
- while (index < lines.length && lines[index] === "") index++;
226
- return {
227
- importLines,
228
- body: lines.slice(index).join("\n")
229
- };
109
+ function sameDeclarations(current, output) {
110
+ const left = parseSync("routeTree.gen.d.ts", current);
111
+ const right = parseSync("routeTree.gen.d.ts", output);
112
+ if (left.errors.length || right.errors.length) return false;
113
+ const withoutFormatting = (key, value) => key === "start" || key === "end" || key === "raw" ? void 0 : value;
114
+ return JSON.stringify(left.program, withoutFormatting) === JSON.stringify(right.program, withoutFormatting);
230
115
  }
231
116
  //#endregion
232
117
  export { typedRoutes };
@@ -1 +1 @@
1
- {"version":3,"file":"typed-routes-plugin.js","names":[],"sources":["../../../src/lib/typed-routes-plugin.ts"],"sourcesContent":["import { normalizePath, type Plugin } from 'vite';\nimport { resolve, join, dirname, relative } from 'node:path';\nimport { writeFileSync, mkdirSync, existsSync, readFileSync } from 'node:fs';\n\nimport {\n generateRouteManifest,\n generateRouteTableDeclaration,\n generateRouteTreeDeclaration,\n detectSchemaExports,\n formatManifestSummary,\n filenameToRoutePath,\n} from './route-manifest.js';\nimport type { RouteSchemaInfo } from './route-manifest.js';\nimport {\n detectJsonLdModuleExports,\n extractMarkdownJsonLd,\n generateJsonLdManifestSource,\n type JsonLdManifestEntry,\n} from './json-ld-manifest-plugin.js';\nimport {\n createRouteFileDiscovery,\n type RouteFileDiscovery,\n} from './route-file-discovery.js';\nimport { debugTypedRouter } from './utils/debug.js';\n\nconst DEFAULT_OUT_FILE = 'src/routeTree.gen.ts';\n\nexport interface TypedRoutesPluginOptions {\n /**\n * Output path for the single generated route module,\n * relative to the app root.\n *\n * @default 'src/routeTree.gen.ts'\n */\n outFile?: string;\n /**\n * Workspace root used to resolve additional route/content directories.\n *\n * @default process.env['NX_WORKSPACE_ROOT'] ?? process.cwd()\n */\n workspaceRoot?: string;\n /**\n * Additional page directories to scan for `.page.ts` files.\n */\n additionalPagesDirs?: string[];\n /**\n * Additional content directories to scan for `.md` files.\n */\n additionalContentDirs?: string[];\n /**\n * Include generated `routeJsonLdManifest` exports in the generated route file.\n *\n * @default true\n */\n jsonLdManifest?: boolean;\n /**\n * When true, compare generated output against the existing file and\n * throw an error if they differ instead of writing. Useful for CI to\n * detect stale checked-in route files.\n *\n * @default false\n */\n verify?: boolean;\n /**\n * When true, production builds fail after regenerating a stale checked-in\n * route file. This preserves self-healing writes in development while making\n * build-time freshness issues visible by default.\n *\n * @default true\n */\n verifyOnBuild?: boolean;\n}\n\nfunction resolvePluginOptions(\n options: TypedRoutesPluginOptions = {},\n): Required<TypedRoutesPluginOptions> {\n return {\n outFile: options.outFile ?? DEFAULT_OUT_FILE,\n workspaceRoot:\n options.workspaceRoot ??\n process.env['NX_WORKSPACE_ROOT'] ??\n process.cwd(),\n additionalPagesDirs: options.additionalPagesDirs ?? [],\n additionalContentDirs: options.additionalContentDirs ?? [],\n jsonLdManifest: options.jsonLdManifest ?? true,\n verify: options.verify ?? false,\n verifyOnBuild: options.verifyOnBuild ?? true,\n };\n}\n\n/**\n * Vite plugin that generates a single typed route module for Analog file routes.\n */\nexport function typedRoutes(options: TypedRoutesPluginOptions = {}): Plugin {\n const resolvedOptions = resolvePluginOptions(options);\n const workspaceRoot = normalizePath(resolvedOptions.workspaceRoot);\n let root = '';\n let command: 'build' | 'serve' = 'serve';\n let discovery: RouteFileDiscovery;\n\n function isFreshnessCheck(): boolean {\n return (\n resolvedOptions.verify ||\n (command === 'build' && resolvedOptions.verifyOnBuild)\n );\n }\n\n function resolveDiscoveredFile(filename: string): string {\n const fromRoot = join(root, filename);\n if (existsSync(fromRoot)) return fromRoot;\n return join(workspaceRoot, filename);\n }\n\n function detectSchemas(relativeFilename: string): RouteSchemaInfo {\n if (!relativeFilename.endsWith('.ts')) {\n return { hasParamsSchema: false, hasQuerySchema: false };\n }\n\n try {\n const absPath = resolveDiscoveredFile(relativeFilename);\n const content = readFileSync(absPath, 'utf-8');\n return detectSchemaExports(content);\n } catch {\n return { hasParamsSchema: false, hasQuerySchema: false };\n }\n }\n\n /**\n * Ensures the generated route file is imported from an app entry file\n * so the module augmentation is always part of the TypeScript program.\n */\n function ensureEntryImport(): void {\n const entryFiles = ['src/main.ts', 'src/main.server.ts'];\n\n // Compute the import specifier relative to the entry file\n function importSpecifierFor(entryFile: string): string {\n const rel = relative(dirname(entryFile), resolvedOptions.outFile)\n .replace(/\\.ts$/, '')\n .replace(/\\\\/g, '/');\n return rel.startsWith('.') ? rel : './' + rel;\n }\n\n for (const entryFile of entryFiles) {\n const entryPath = join(root, entryFile);\n if (!existsSync(entryPath)) continue;\n\n const content = readFileSync(entryPath, 'utf-8');\n const specifier = importSpecifierFor(entryFile);\n\n // Check if any variation of the import already exists\n const escaped = specifier.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pattern = new RegExp(`import\\\\s+['\"]${escaped}(\\\\.ts|\\\\.js)?['\"]`);\n if (pattern.test(content)) {\n return;\n }\n\n if (isFreshnessCheck()) {\n return;\n }\n\n // Insert the import after the last existing import line\n const importLine = `import '${specifier}';`;\n const lines = content.split('\\n');\n let lastImportLine = -1;\n\n for (let i = 0; i < lines.length; i++) {\n if (/^import\\s/.test(lines[i])) {\n lastImportLine = i;\n }\n }\n\n if (lastImportLine >= 0) {\n lines.splice(lastImportLine + 1, 0, importLine);\n } else {\n lines.unshift(importLine);\n }\n\n writeFileSync(entryPath, lines.join('\\n'), 'utf-8');\n console.log(`[analog] Added route tree import to ${entryFile}`);\n return;\n }\n\n // No suitable entry file found\n const specifier = importSpecifierFor('src/main.ts');\n if (isFreshnessCheck()) {\n return;\n }\n console.warn(\n `[analog] Could not find an entry file (src/main.ts or src/main.server.ts) ` +\n `to add the route tree import. Add \\`import '${specifier}';\\` ` +\n `to your app entry file to ensure typed routing is active.`,\n );\n }\n\n function generate(): void {\n const routeFiles = discovery.getRouteFiles();\n const contentFiles = discovery.getContentFiles();\n debugTypedRouter('discovered files', {\n routeFiles: routeFiles.length,\n contentFiles: contentFiles.length,\n });\n const allFiles = [...routeFiles, ...contentFiles];\n const manifest = generateRouteManifest(\n allFiles,\n detectSchemas,\n (filename) => (discovery.isAppLocal(filename) ? 0 : 1),\n );\n const declaration = generateRouteTableDeclaration(manifest);\n const canonicalFiles = new Set(\n manifest.routes.map((route) => route.filename),\n );\n const jsonLdEntries = buildJsonLdEntries(\n resolveDiscoveredFile,\n routeFiles.filter((filename) => canonicalFiles.has(filename)),\n contentFiles.filter((filename) => canonicalFiles.has(filename)),\n );\n const routeTree = generateRouteTreeDeclaration(manifest, {\n jsonLdFiles: jsonLdEntries.map((entry) => entry.sourceFile),\n });\n const output = combineGeneratedModules(\n declaration,\n routeTree,\n resolvedOptions.jsonLdManifest && jsonLdEntries.length > 0\n ? generateJsonLdManifestSource(jsonLdEntries, resolvedOptions.outFile)\n : '',\n );\n\n const hardCollisions = manifest.collisions.filter((c) => c.samePriority);\n if (manifest.collisions.length > 0) {\n debugTypedRouter('route collisions', {\n total: manifest.collisions.length,\n hard: hardCollisions.length,\n collisions: manifest.collisions.map((c) => ({\n path: c.fullPath,\n kept: c.keptFile,\n dropped: c.droppedFile,\n })),\n });\n }\n if (hardCollisions.length > 0 && command === 'build') {\n const details = hardCollisions\n .map((c) => ` '${c.fullPath}': '${c.keptFile}' vs '${c.droppedFile}'`)\n .join('\\n');\n throw new Error(\n `[analog] Route collisions detected during build:\\n${details}\\n\\n` +\n `Each route path must be defined by exactly one source file. ` +\n `Remove or rename the conflicting files to resolve the collision.`,\n );\n }\n\n if (manifest.routes.length > 0) {\n console.log(formatManifestSummary(manifest));\n }\n\n const outPath = join(root, resolvedOptions.outFile);\n const outDir = dirname(outPath);\n const hadExistingOutput = existsSync(outPath);\n\n if (!existsSync(outDir)) {\n mkdirSync(outDir, { recursive: true });\n }\n\n let existing = '';\n\n try {\n existing = readFileSync(outPath, 'utf-8');\n } catch {\n // file does not exist yet\n }\n\n // Build-time guard: detect absolute path leaks in generated output.\n // Machine-specific prefixes must never appear in route keys or sourceFile values.\n if (output.includes(root)) {\n console.warn(\n `[analog] Generated route output contains an absolute path prefix (${root}). ` +\n `Route keys and sourceFile values should be workspace-relative.`,\n );\n }\n\n // Normalize line endings before comparison so that files checked in\n // with LF don't appear stale on Windows where readFileSync may return CRLF.\n const normalizeEndings = (s: string) => s.replace(/\\r\\n/g, '\\n');\n if (normalizeEndings(existing) !== normalizeEndings(output)) {\n debugTypedRouter('route file changed', {\n outFile: resolvedOptions.outFile,\n routes: manifest.routes.length,\n verify: resolvedOptions.verify,\n verifyOnBuild: resolvedOptions.verifyOnBuild,\n command,\n });\n if (resolvedOptions.verify) {\n throw new Error(\n `[analog] Stale route file detected: ${resolvedOptions.outFile}\\n` +\n `The checked-in generated route file does not match the current route sources.\\n` +\n `Regenerate route files and commit the updated output.`,\n );\n }\n\n writeFileSync(outPath, output, 'utf-8');\n\n if (\n command === 'build' &&\n resolvedOptions.verifyOnBuild &&\n hadExistingOutput\n ) {\n throw new Error(\n `[analog] Stale route file detected during build: ${resolvedOptions.outFile}\\n` +\n `The generated route file was updated to match the current route sources.\\n` +\n `Review the updated output, commit it if it is checked in, and rerun the build.`,\n );\n }\n }\n }\n\n return {\n name: 'analog-typed-routes',\n config(config, env) {\n command = env.command;\n root = normalizePath(resolve(workspaceRoot, config.root || '.') || '.');\n discovery = createRouteFileDiscovery({\n root,\n workspaceRoot,\n additionalPagesDirs: resolvedOptions.additionalPagesDirs,\n additionalContentDirs: resolvedOptions.additionalContentDirs,\n });\n },\n buildStart() {\n generate();\n if (!isFreshnessCheck()) {\n ensureEntryImport();\n }\n },\n configureServer(server) {\n const regenerate = (path: string, event: 'add' | 'change' | 'unlink') => {\n // Reuse the discovery matcher so watch-time updates stay in sync with\n // the initial scan and don't pull Nitro server routes into routeTree.gen.ts.\n if (!discovery.getDiscoveredFileKind(path)) {\n return;\n }\n\n debugTypedRouter('watch regenerate', { event, path });\n discovery.updateDiscoveredFile(path, event);\n generate();\n };\n\n server.watcher.on('add', (path) => regenerate(path, 'add'));\n server.watcher.on('change', (path) => regenerate(path, 'change'));\n server.watcher.on('unlink', (path) => regenerate(path, 'unlink'));\n },\n };\n}\n\nfunction buildJsonLdEntries(\n resolveFile: (filename: string) => string,\n routeFiles: string[],\n contentFiles: string[],\n): JsonLdManifestEntry[] {\n const entries: JsonLdManifestEntry[] = [];\n let importIndex = 0;\n\n routeFiles.forEach((filename) => {\n try {\n const source = readFileSync(resolveFile(filename), 'utf-8');\n if (!detectJsonLdModuleExports(source)) {\n return;\n }\n\n entries.push({\n kind: 'module',\n routePath: filenameToRoutePath(filename),\n sourceFile: filename,\n importAlias: `routeModule${importIndex++}`,\n });\n } catch {\n // ignore unreadable route file\n }\n });\n\n contentFiles.forEach((filename) => {\n try {\n const source = readFileSync(resolveFile(filename), 'utf-8');\n const jsonLd = extractMarkdownJsonLd(source);\n\n if (jsonLd.length === 0) {\n return;\n }\n\n entries.push({\n kind: 'content',\n routePath: filenameToRoutePath(filename),\n sourceFile: filename,\n jsonLd,\n });\n } catch {\n // ignore unreadable content file\n }\n });\n\n return entries.sort((a, b) => a.routePath.localeCompare(b.routePath));\n}\n\nfunction combineGeneratedModules(...sources: string[]): string {\n const imports: string[] = [];\n const seenImports = new Set<string>();\n const bodies: string[] = [];\n\n for (const source of sources) {\n const { body, importLines } = splitGeneratedModule(source);\n for (const importLine of importLines) {\n if (!seenImports.has(importLine)) {\n seenImports.add(importLine);\n imports.push(importLine);\n }\n }\n if (body.trim()) {\n bodies.push(body.trim());\n }\n }\n\n return [\n '// This file is auto-generated by @analogjs/platform',\n '// Do not edit manually',\n '',\n ...(imports.length > 0 ? [...imports, ''] : []),\n bodies.join('\\n\\n'),\n '',\n ].join('\\n');\n}\n\nfunction splitGeneratedModule(source: string): {\n importLines: string[];\n body: string;\n} {\n const lines = source.split('\\n');\n let index = 0;\n\n while (index < lines.length && lines[index].startsWith('//')) {\n index++;\n }\n\n while (index < lines.length && lines[index] === '') {\n index++;\n }\n\n const importLines: string[] = [];\n while (index < lines.length && lines[index].startsWith('import ')) {\n importLines.push(lines[index]);\n index++;\n }\n\n while (index < lines.length && lines[index] === '') {\n index++;\n }\n\n return {\n importLines,\n body: lines.slice(index).join('\\n'),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"typed-routes-plugin.js","names":[],"sources":["../../../src/lib/typed-routes-plugin.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join, relative, resolve } from 'node:path';\nimport { readConfiguration } from '@angular/compiler-cli';\nimport { parseSync } from 'oxc-parser';\nimport { normalizePath, type Plugin } from 'vite';\n\nimport {\n createRouteFileDiscovery,\n type RouteFileDiscovery,\n} from './route-file-discovery.js';\nimport {\n generateRouteManifest,\n generateRouteTableDeclaration,\n} from './route-manifest.js';\n\nexport interface TypedRoutingOptions {\n /** Generated declaration path (.d.ts), relative to the app root. */\n outFile?: string;\n /** Fail builds when a checked-in route table is stale. Defaults to true. */\n verifyOnBuild?: boolean;\n}\n\nexport interface TypedRoutesPluginOptions extends TypedRoutingOptions {\n workspaceRoot?: string;\n additionalPagesDirs?: string[];\n additionalContentDirs?: string[];\n}\n\nexport function typedRoutes(options: TypedRoutesPluginOptions = {}): Plugin {\n const workspaceRoot = normalizePath(options.workspaceRoot ?? process.cwd());\n const outFile = options.outFile ?? 'src/routeTree.gen.d.ts';\n if (!outFile.endsWith('.d.ts')) {\n throw new Error(\n '[analog] Typed routing outFile must end in .d.ts. Include this declaration in your application tsconfig.',\n );\n }\n let root: string;\n let command: 'build' | 'serve';\n let discovery: RouteFileDiscovery;\n\n let getTsConfigPath: (() => string) | undefined;\n\n function verifyTypeInclusion(): void {\n if (!getTsConfigPath) return;\n const tsconfig = getTsConfigPath();\n const { rootNames, errors } = readConfiguration(tsconfig);\n if (errors.length) {\n throw new Error(\n `[analog] Cannot verify typed routing: unable to read ${tsconfig}.`,\n );\n }\n const outputPath = normalizePath(resolve(root, outFile));\n if (\n !rootNames.some((file) => normalizePath(resolve(file)) === outputPath)\n ) {\n const declaration = normalizePath(\n relative(dirname(tsconfig), outputPath),\n );\n throw new Error(\n `[analog] Typed route declaration is not included in ${tsconfig}. Add \"${declaration}\" to its \"files\" or \"include\" list so typed routing is available to the compiler and editor.`,\n );\n }\n }\n\n function generate(): void {\n const manifest = generateRouteManifest(\n [...discovery.getRouteFiles(), ...discovery.getContentFiles()],\n (filename) => (discovery.isAppLocal(filename) ? 0 : 1),\n );\n const collisions = manifest.collisions.filter(\n (collision) => collision.samePriority,\n );\n if (command === 'build' && collisions.length) {\n throw new Error(\n `[analog] Route collisions detected: ${collisions.map((c) => `${c.fullPath}: ${c.keptFile}, ${c.droppedFile}`).join('; ')}`,\n );\n }\n const outputPath = join(root, outFile);\n const output = generateRouteTableDeclaration(manifest, (filename) => {\n const pageFile = [\n join(root, filename),\n join(workspaceRoot, filename),\n filename,\n ].find((file) => existsSync(file));\n if (!pageFile) return undefined;\n const serverFile = pageFile.replace(/\\.page\\.ts$/, '.server.ts');\n return {\n routeMeta: hasNamedExport(pageFile, 'routeMeta')\n ? toImportSpecifier(dirname(outputPath), pageFile)\n : undefined,\n load:\n serverFile !== pageFile &&\n existsSync(serverFile) &&\n hasNamedExport(serverFile, 'load')\n ? toImportSpecifier(dirname(outputPath), serverFile)\n : undefined,\n };\n });\n const exists = existsSync(outputPath);\n const current = exists ? readFileSync(outputPath, 'utf8') : '';\n if (current !== output && !sameDeclarations(current, output)) {\n if (exists && command === 'build' && (options.verifyOnBuild ?? true)) {\n throw new Error(\n `[analog] Stale route file: ${outFile}. Run the dev server to regenerate it before building.`,\n );\n }\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, output);\n }\n }\n\n return {\n name: 'analog-typed-routes',\n config(config, env) {\n command = env.command;\n root = normalizePath(resolve(workspaceRoot, config.root ?? '.'));\n discovery = createRouteFileDiscovery({\n root,\n workspaceRoot,\n additionalPagesDirs: options.additionalPagesDirs ?? [],\n additionalContentDirs: options.additionalContentDirs ?? [],\n });\n // Generate before Angular reads the application tsconfig.\n generate();\n },\n configResolved(config) {\n const compiler = config.plugins.find(\n (plugin) =>\n plugin.name === '@analogjs/vite-plugin-angular' ||\n plugin.name === '@analogjs/vite-plugin-angular-fast-compile' ||\n plugin.name === '@analogjs/vite-plugin-angular-compilation-api',\n );\n getTsConfigPath = compiler?.api?.getTsConfigPath;\n if (compiler && !getTsConfigPath) {\n throw new Error(\n '[analog] Typed routing requires a matching @analogjs/vite-plugin-angular version to verify the application tsconfig.',\n );\n }\n verifyTypeInclusion();\n },\n buildStart() {\n discovery.reset();\n generate();\n verifyTypeInclusion();\n },\n configureServer(server) {\n server.watcher.add(join(root, outFile));\n for (const event of ['add', 'change', 'unlink'] as const) {\n server.watcher.on(event, (path) => {\n if (!discovery.getDiscoveredFileKind(path)) return;\n discovery.updateDiscoveredFile(path, event);\n generate();\n });\n }\n },\n };\n}\n\nfunction toImportSpecifier(fromDir: string, file: string): string {\n const specifier = normalizePath(relative(fromDir, file)).replace(/\\.ts$/, '');\n return specifier.startsWith('.') ? specifier : `./${specifier}`;\n}\n\nfunction hasNamedExport(file: string, name: string): boolean {\n const source = readFileSync(file, 'utf8');\n if (!source.includes(name)) return false;\n const { program, errors } = parseSync(file, source);\n if (errors.length) return false;\n return program.body.some((statement) => {\n if (\n statement.type !== 'ExportNamedDeclaration' ||\n statement.exportKind === 'type'\n ) {\n return false;\n }\n const declaration = statement.declaration;\n if (declaration?.type === 'VariableDeclaration') {\n return declaration.declarations.some(\n (item) => item.id.type === 'Identifier' && item.id.name === name,\n );\n }\n if (declaration?.type === 'FunctionDeclaration') {\n return declaration.id?.name === name;\n }\n return statement.specifiers.some(\n (specifier) =>\n specifier.exportKind !== 'type' &&\n (specifier.exported.type === 'Identifier'\n ? specifier.exported.name\n : specifier.exported.value) === name,\n );\n });\n}\n\nfunction sameDeclarations(current: string, output: string): boolean {\n const left = parseSync('routeTree.gen.d.ts', current);\n const right = parseSync('routeTree.gen.d.ts', output);\n if (left.errors.length || right.errors.length) return false;\n const withoutFormatting = (key: string, value: unknown) =>\n key === 'start' || key === 'end' || key === 'raw' ? undefined : value;\n return (\n JSON.stringify(left.program, withoutFormatting) ===\n JSON.stringify(right.program, withoutFormatting)\n );\n}\n"],"mappings":"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"}
@@ -3,22 +3,19 @@ import { createDebugHarness } from "./debug-harness.js";
3
3
  import { createDebug } from "obug";
4
4
  //#region packages/platform/src/lib/utils/debug.ts
5
5
  var debugPlatform = createDebug("analog:platform");
6
- var debugRoutes = createDebug("analog:platform:routes");
7
- var debugContent = createDebug("analog:platform:content");
8
- var debugTypedRouter = createDebug("analog:platform:typed-router");
9
6
  var harness = createDebugHarness({
10
7
  fallbackNamespace: "analog:*",
11
8
  instanceGroups: [[
12
9
  debugPlatform,
13
- debugRoutes,
14
- debugContent,
15
- debugTypedRouter
10
+ createDebug("analog:platform:routes"),
11
+ createDebug("analog:platform:content"),
12
+ createDebug("analog:platform:typed-router")
16
13
  ], nitroDebugInstances]
17
14
  });
18
15
  var applyDebugOption = harness.applyDebugOption;
19
16
  var activateDeferredDebug = harness.activateDeferredDebug;
20
17
  harness._resetDeferredDebug;
21
18
  //#endregion
22
- export { activateDeferredDebug, applyDebugOption, debugPlatform, debugTypedRouter };
19
+ export { activateDeferredDebug, applyDebugOption, debugPlatform };
23
20
 
24
21
  //# sourceMappingURL=debug.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"debug.js","names":[],"sources":["../../../../src/lib/utils/debug.ts"],"sourcesContent":["import { createDebug } from 'obug';\nimport { nitroDebugInstances } from '../nitro/debug.js';\nimport { createDebugHarness } from './debug-harness.js';\n\nexport const debugPlatform = createDebug('analog:platform');\nexport const debugRoutes = createDebug('analog:platform:routes');\nexport const debugContent = createDebug('analog:platform:content');\nexport const debugTypedRouter = createDebug('analog:platform:typed-router');\n\nconst platformDebugInstances = [\n debugPlatform,\n debugRoutes,\n debugContent,\n debugTypedRouter,\n];\n\nexport type DebugScope =\n | 'analog:*'\n | 'analog:platform'\n | 'analog:platform:*'\n | 'analog:platform:routes'\n | 'analog:platform:content'\n | 'analog:platform:typed-router'\n | 'analog:angular:*'\n | 'analog:angular:hmr'\n | 'analog:angular:styles'\n | 'analog:angular:compiler'\n | 'analog:angular:compilation-api'\n | 'analog:angular:style-pipeline'\n | 'analog:nitro'\n | 'analog:nitro:*'\n | 'analog:nitro:ssr'\n | 'analog:nitro:prerender'\n | (string & {});\n\nexport type DebugMode = 'build' | 'dev';\n\nexport interface DebugModeOptions {\n scopes?: boolean | DebugScope[];\n mode?: DebugMode;\n /**\n * Write debug output to log files under `tmp/debug/` in the workspace root.\n * - `true` or `'single'` — all output to `tmp/debug/analog.log`\n * - `'scoped'` — one file per scope, e.g. `tmp/debug/analog.angular.hmr.log`\n */\n logFile?: boolean | 'single' | 'scoped';\n}\n\nexport type DebugOption =\n | boolean\n | DebugScope[]\n | DebugModeOptions\n | DebugModeOptions[];\n\nconst harness = createDebugHarness({\n fallbackNamespace: 'analog:*',\n instanceGroups: [platformDebugInstances, nitroDebugInstances],\n});\n\nexport const applyDebugOption: (\n debug: DebugOption | undefined,\n workspaceRoot?: string,\n) => void = harness.applyDebugOption;\nexport const activateDeferredDebug: (command: 'build' | 'serve') => void =\n harness.activateDeferredDebug;\nexport const _resetDeferredDebug: () => void = harness._resetDeferredDebug;\n"],"mappings":";;;;AAIA,IAAa,gBAAgB,YAAY,kBAAkB;AAC3D,IAAa,cAAc,YAAY,yBAAyB;AAChE,IAAa,eAAe,YAAY,0BAA0B;AAClE,IAAa,mBAAmB,YAAY,+BAA+B;AA+C3E,IAAM,UAAU,mBAAmB;CACjC,mBAAmB;CACnB,gBAAgB,CA/Ca;EAC7B;EACA;EACA;EACA;EACD,EA0C0C,oBAAoB;CAC9D,CAAC;AAEF,IAAa,mBAGD,QAAQ;AACpB,IAAa,wBACX,QAAQ;AACqC,QAAQ"}
1
+ {"version":3,"file":"debug.js","names":[],"sources":["../../../../src/lib/utils/debug.ts"],"sourcesContent":["import { createDebug } from 'obug';\nimport { nitroDebugInstances } from '../nitro/debug.js';\nimport { createDebugHarness } from './debug-harness.js';\n\nexport const debugPlatform = createDebug('analog:platform');\nexport const debugRoutes = createDebug('analog:platform:routes');\nexport const debugContent = createDebug('analog:platform:content');\nexport const debugTypedRouter = createDebug('analog:platform:typed-router');\n\nconst platformDebugInstances = [\n debugPlatform,\n debugRoutes,\n debugContent,\n debugTypedRouter,\n];\n\nexport type DebugScope =\n | 'analog:*'\n | 'analog:platform'\n | 'analog:platform:*'\n | 'analog:platform:routes'\n | 'analog:platform:content'\n | 'analog:platform:typed-router'\n | 'analog:angular:*'\n | 'analog:angular:hmr'\n | 'analog:angular:styles'\n | 'analog:angular:compiler'\n | 'analog:angular:compilation-api'\n | 'analog:angular:style-pipeline'\n | 'analog:nitro'\n | 'analog:nitro:*'\n | 'analog:nitro:ssr'\n | 'analog:nitro:prerender'\n | (string & {});\n\nexport type DebugMode = 'build' | 'dev';\n\nexport interface DebugModeOptions {\n scopes?: boolean | DebugScope[];\n mode?: DebugMode;\n /**\n * Write debug output to log files under `tmp/debug/` in the workspace root.\n * - `true` or `'single'` — all output to `tmp/debug/analog.log`\n * - `'scoped'` — one file per scope, e.g. `tmp/debug/analog.angular.hmr.log`\n */\n logFile?: boolean | 'single' | 'scoped';\n}\n\nexport type DebugOption =\n | boolean\n | DebugScope[]\n | DebugModeOptions\n | DebugModeOptions[];\n\nconst harness = createDebugHarness({\n fallbackNamespace: 'analog:*',\n instanceGroups: [platformDebugInstances, nitroDebugInstances],\n});\n\nexport const applyDebugOption: (\n debug: DebugOption | undefined,\n workspaceRoot?: string,\n) => void = harness.applyDebugOption;\nexport const activateDeferredDebug: (command: 'build' | 'serve') => void =\n harness.activateDeferredDebug;\nexport const _resetDeferredDebug: () => void = harness._resetDeferredDebug;\n"],"mappings":";;;;AAIA,IAAa,gBAAgB,YAAY,kBAAkB;AAkD3D,IAAM,UAAU,mBAAmB;CACjC,mBAAmB;CACnB,gBAAgB,CA/Ca;EAC7B;EALyB,YAAY,yBAAyB;EACpC,YAAY,0BAA0B;EAClC,YAAY,+BAA+B;EAO1E,EA0C0C,oBAAoB;CAC9D,CAAC;AAEF,IAAa,mBAGD,QAAQ;AACpB,IAAa,wBACX,QAAQ;AACqC,QAAQ"}
@@ -10,6 +10,6 @@ function getBundleOptionsKey() {
10
10
  return isRolldown() ? "rolldownOptions" : "rollupOptions";
11
11
  }
12
12
  //#endregion
13
- export { getBundleOptionsKey, getJsTransformConfigKey };
13
+ export { getBundleOptionsKey, getJsTransformConfigKey, isRolldown };
14
14
 
15
15
  //# sourceMappingURL=rolldown.js.map
@@ -10,4 +10,4 @@ export declare const SERVER_MODE_ID = "virtual:@analogjs/platform/server-mode";
10
10
  * first so the flag is set before any application module evaluates. A text
11
11
  * replacement cannot do this because Angular is pre-bundled for SSR.
12
12
  */
13
- export declare function serverModePlugin(): Plugin[];
13
+ export declare function serverModePlugin(entryServer?: string): Plugin[];