@analogjs/platform 3.0.0-alpha.20 → 3.0.0-alpha.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "@analogjs/platform",
3
- "version": "3.0.0-alpha.20",
3
+ "version": "3.0.0-alpha.22",
4
4
  "description": "The fullstack meta-framework for Angular",
5
5
  "type": "module",
6
6
  "author": "Brandon Roberts <robertsbt@gmail.com>",
7
7
  "exports": {
8
- ".": "./src/index.js",
8
+ ".": {
9
+ "types": "./src/index.d.ts",
10
+ "default": "./src/index.js"
11
+ },
9
12
  "./package.json": "./package.json"
10
13
  },
11
14
  "keywords": [
@@ -28,13 +31,12 @@
28
31
  "url": "https://github.com/sponsors/brandonroberts"
29
32
  },
30
33
  "dependencies": {
31
- "@analogjs/vite-plugin-angular": "^3.0.0-alpha.20",
32
- "@analogjs/vite-plugin-nitro": "^3.0.0-alpha.20",
33
34
  "front-matter": "^4.0.2",
34
35
  "tinyglobby": "^0.2.15",
35
36
  "nitro": "3.0.260311-beta",
36
- "rolldown": "^1.0.0-rc.11",
37
- "nitropack": "^2.13.1",
37
+ "@analogjs/vite-plugin-angular": "^3.0.0-alpha.22",
38
+ "@analogjs/vite-plugin-nitro": "^3.0.0-alpha.22",
39
+ "rolldown": "^1.0.0-rc.12",
38
40
  "vitefu": "^1.1.2"
39
41
  },
40
42
  "peerDependencies": {
@@ -1,8 +1,8 @@
1
1
  import { getBundleOptionsKey } from "./utils/rolldown.js";
2
+ import { readFileSync } from "node:fs";
3
+ import { relative, resolve } from "node:path";
2
4
  import * as vite from "vite";
3
5
  import { globSync } from "tinyglobby";
4
- import { relative, resolve } from "node:path";
5
- import { readFileSync } from "node:fs";
6
6
  //#region packages/platform/src/lib/content-plugin.ts
7
7
  /**
8
8
  * Content plugin that provides markdown and content file processing for Analog.
@@ -0,0 +1,14 @@
1
+ export interface DiscoveredLibraryRoutes {
2
+ additionalPagesDirs: string[];
3
+ additionalContentDirs: string[];
4
+ additionalAPIDirs: string[];
5
+ }
6
+ /**
7
+ * Reads `tsconfig.base.json` (or `tsconfig.json`) path aliases from the
8
+ * workspace root and checks each library for conventional route directories
9
+ * (`src/pages`, `src/content`, `src/api`).
10
+ *
11
+ * Returns workspace-relative paths (e.g. `/libs/shared/feature`) suitable
12
+ * for merging into the `additional*Dirs` options.
13
+ */
14
+ export declare function discoverLibraryRoutes(workspaceRoot: string): DiscoveredLibraryRoutes;
@@ -0,0 +1,59 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ //#region packages/platform/src/lib/discover-library-routes.ts
4
+ var empty = Object.freeze({
5
+ additionalPagesDirs: Object.freeze([]),
6
+ additionalContentDirs: Object.freeze([]),
7
+ additionalAPIDirs: Object.freeze([])
8
+ });
9
+ /**
10
+ * Reads `tsconfig.base.json` (or `tsconfig.json`) path aliases from the
11
+ * workspace root and checks each library for conventional route directories
12
+ * (`src/pages`, `src/content`, `src/api`).
13
+ *
14
+ * Returns workspace-relative paths (e.g. `/libs/shared/feature`) suitable
15
+ * for merging into the `additional*Dirs` options.
16
+ */
17
+ function discoverLibraryRoutes(workspaceRoot) {
18
+ let raw;
19
+ try {
20
+ const basePath = join(workspaceRoot, "tsconfig.base.json");
21
+ const fallbackPath = join(workspaceRoot, "tsconfig.json");
22
+ raw = existsSync(basePath) ? readFileSync(basePath, "utf-8") : readFileSync(fallbackPath, "utf-8");
23
+ } catch {
24
+ return empty;
25
+ }
26
+ let paths;
27
+ try {
28
+ paths = JSON.parse(raw)?.compilerOptions?.paths ?? {};
29
+ } catch {
30
+ return empty;
31
+ }
32
+ const result = {
33
+ additionalPagesDirs: [],
34
+ additionalContentDirs: [],
35
+ additionalAPIDirs: []
36
+ };
37
+ const seen = /* @__PURE__ */ new Set();
38
+ for (const [alias, targets] of Object.entries(paths)) {
39
+ if (alias.startsWith("@analogjs/")) continue;
40
+ const target = targets?.[0];
41
+ if (!target) continue;
42
+ const normalized = target.startsWith("./") ? target.slice(2) : target;
43
+ if (!normalized.startsWith("libs/")) continue;
44
+ const srcIndex = normalized.indexOf("/src/");
45
+ if (srcIndex === -1) continue;
46
+ const libRoot = normalized.slice(0, srcIndex);
47
+ if (seen.has(libRoot)) continue;
48
+ seen.add(libRoot);
49
+ const absoluteLibRoot = join(workspaceRoot, libRoot);
50
+ if (existsSync(join(absoluteLibRoot, "src/pages"))) result.additionalPagesDirs.push(`/${libRoot}`);
51
+ if (existsSync(join(absoluteLibRoot, "src/content"))) result.additionalContentDirs.push(`/${libRoot}/src/content`);
52
+ if (existsSync(join(absoluteLibRoot, "src/api"))) result.additionalAPIDirs.push(`/${libRoot}/src/api`);
53
+ }
54
+ return result;
55
+ }
56
+ //#endregion
57
+ export { discoverLibraryRoutes };
58
+
59
+ //# sourceMappingURL=discover-library-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover-library-routes.js","names":[],"sources":["../../../src/lib/discover-library-routes.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport interface DiscoveredLibraryRoutes {\n additionalPagesDirs: string[];\n additionalContentDirs: string[];\n additionalAPIDirs: string[];\n}\n\nconst empty: DiscoveredLibraryRoutes = Object.freeze({\n additionalPagesDirs: Object.freeze([] as string[]),\n additionalContentDirs: Object.freeze([] as string[]),\n additionalAPIDirs: Object.freeze([] as string[]),\n});\n\n/**\n * Reads `tsconfig.base.json` (or `tsconfig.json`) path aliases from the\n * workspace root and checks each library for conventional route directories\n * (`src/pages`, `src/content`, `src/api`).\n *\n * Returns workspace-relative paths (e.g. `/libs/shared/feature`) suitable\n * for merging into the `additional*Dirs` options.\n */\nexport function discoverLibraryRoutes(\n workspaceRoot: string,\n): DiscoveredLibraryRoutes {\n let raw: string;\n try {\n const basePath = join(workspaceRoot, 'tsconfig.base.json');\n const fallbackPath = join(workspaceRoot, 'tsconfig.json');\n raw = existsSync(basePath)\n ? readFileSync(basePath, 'utf-8')\n : readFileSync(fallbackPath, 'utf-8');\n } catch {\n return empty;\n }\n\n let paths: Record<string, string[]>;\n try {\n const tsconfig = JSON.parse(raw);\n paths = tsconfig?.compilerOptions?.paths ?? {};\n } catch {\n return empty;\n }\n\n const result: DiscoveredLibraryRoutes = {\n additionalPagesDirs: [],\n additionalContentDirs: [],\n additionalAPIDirs: [],\n };\n const seen = new Set<string>();\n\n for (const [alias, targets] of Object.entries(paths)) {\n if (alias.startsWith('@analogjs/')) {\n continue;\n }\n\n const target = targets?.[0];\n if (!target) {\n continue;\n }\n\n const normalized = target.startsWith('./') ? target.slice(2) : target;\n\n if (!normalized.startsWith('libs/')) {\n continue;\n }\n\n const srcIndex = normalized.indexOf('/src/');\n if (srcIndex === -1) {\n continue;\n }\n\n const libRoot = normalized.slice(0, srcIndex);\n\n if (seen.has(libRoot)) {\n continue;\n }\n seen.add(libRoot);\n\n const absoluteLibRoot = join(workspaceRoot, libRoot);\n\n if (existsSync(join(absoluteLibRoot, 'src/pages'))) {\n result.additionalPagesDirs.push(`/${libRoot}`);\n }\n\n if (existsSync(join(absoluteLibRoot, 'src/content'))) {\n result.additionalContentDirs.push(`/${libRoot}/src/content`);\n }\n\n if (existsSync(join(absoluteLibRoot, 'src/api'))) {\n result.additionalAPIDirs.push(`/${libRoot}/src/api`);\n }\n }\n\n return result;\n}\n"],"mappings":";;;AASA,IAAM,QAAiC,OAAO,OAAO;CACnD,qBAAqB,OAAO,OAAO,EAAE,CAAa;CAClD,uBAAuB,OAAO,OAAO,EAAE,CAAa;CACpD,mBAAmB,OAAO,OAAO,EAAE,CAAa;CACjD,CAAC;;;;;;;;;AAUF,SAAgB,sBACd,eACyB;CACzB,IAAI;AACJ,KAAI;EACF,MAAM,WAAW,KAAK,eAAe,qBAAqB;EAC1D,MAAM,eAAe,KAAK,eAAe,gBAAgB;AACzD,QAAM,WAAW,SAAS,GACtB,aAAa,UAAU,QAAQ,GAC/B,aAAa,cAAc,QAAQ;SACjC;AACN,SAAO;;CAGT,IAAI;AACJ,KAAI;AAEF,UADiB,KAAK,MAAM,IAAI,EACd,iBAAiB,SAAS,EAAE;SACxC;AACN,SAAO;;CAGT,MAAM,SAAkC;EACtC,qBAAqB,EAAE;EACvB,uBAAuB,EAAE;EACzB,mBAAmB,EAAE;EACtB;CACD,MAAM,uBAAO,IAAI,KAAa;AAE9B,MAAK,MAAM,CAAC,OAAO,YAAY,OAAO,QAAQ,MAAM,EAAE;AACpD,MAAI,MAAM,WAAW,aAAa,CAChC;EAGF,MAAM,SAAS,UAAU;AACzB,MAAI,CAAC,OACH;EAGF,MAAM,aAAa,OAAO,WAAW,KAAK,GAAG,OAAO,MAAM,EAAE,GAAG;AAE/D,MAAI,CAAC,WAAW,WAAW,QAAQ,CACjC;EAGF,MAAM,WAAW,WAAW,QAAQ,QAAQ;AAC5C,MAAI,aAAa,GACf;EAGF,MAAM,UAAU,WAAW,MAAM,GAAG,SAAS;AAE7C,MAAI,KAAK,IAAI,QAAQ,CACnB;AAEF,OAAK,IAAI,QAAQ;EAEjB,MAAM,kBAAkB,KAAK,eAAe,QAAQ;AAEpD,MAAI,WAAW,KAAK,iBAAiB,YAAY,CAAC,CAChD,QAAO,oBAAoB,KAAK,IAAI,UAAU;AAGhD,MAAI,WAAW,KAAK,iBAAiB,cAAc,CAAC,CAClD,QAAO,sBAAsB,KAAK,IAAI,QAAQ,cAAc;AAG9D,MAAI,WAAW,KAAK,iBAAiB,UAAU,CAAC,CAC9C,QAAO,kBAAkB,KAAK,IAAI,QAAQ,UAAU;;AAIxD,QAAO"}
@@ -1,6 +1,6 @@
1
1
  import { normalizeJsonLd } from "./route-manifest.js";
2
- import { normalizePath } from "vite";
3
2
  import { dirname, relative } from "node:path";
3
+ import { normalizePath } from "vite";
4
4
  import fm from "front-matter";
5
5
  //#region packages/platform/src/lib/json-ld-manifest-plugin.ts
6
6
  /**
@@ -1,9 +1,9 @@
1
1
  export declare const V18_X_NX_DEVKIT = "^20.0.0";
2
2
  export declare const V18_X_NX_ANGULAR = "^20.0.0";
3
- export declare const V18_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.20";
4
- export declare const V18_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.20";
5
- export declare const V18_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.20";
6
- export declare const V18_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.20";
3
+ export declare const V18_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.22";
4
+ export declare const V18_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.22";
5
+ export declare const V18_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.22";
6
+ export declare const V18_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.22";
7
7
  export declare const V18_X_FRONT_MATTER = "^4.0.2";
8
8
  export declare const V18_X_MARKED = "^15.0.7";
9
9
  export declare const V18_X_MARKED_GFM_HEADING_ID = "^4.1.1";
@@ -13,7 +13,7 @@ export declare const V18_X_MERMAID = "^10.2.4";
13
13
  export declare const V18_X_PRISMJS = "^1.29.0";
14
14
  export declare const V18_X_TAILWINDCSS = "^4.2.2";
15
15
  export declare const V18_X_TAILWINDCSS_VITE = "^4.2.2";
16
- export declare const V18_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.20";
16
+ export declare const V18_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.22";
17
17
  export declare const V18_X_ANGULAR_DEVKIT_BUILD_ANGULAR = "^19.0.0";
18
18
  export declare const V18_X_NX_VITE = "^21.0.0";
19
19
  export declare const V18_X_NX_LINTER = "^21.0.0";
@@ -1 +1 @@
1
- {"version":3,"file":"versions.js","names":[],"sources":["../../../../../../../../../../nx-plugin/src/generators/app/versions/nx_18_X/versions.ts"],"sourcesContent":["// V18_X\n// dependencies\nexport const V18_X_NX_DEVKIT = '^20.0.0';\nexport const V18_X_NX_ANGULAR = '^20.0.0';\nexport const V18_X_ANALOG_JS_CONTENT = '^3.0.0-alpha.20';\nexport const V18_X_ANALOG_JS_ROUTER = '^3.0.0-alpha.20';\nexport const V18_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = '^3.0.0-alpha.20';\nexport const V18_X_ANALOG_JS_VITEST_ANGULAR = '^3.0.0-alpha.20';\nexport const V18_X_FRONT_MATTER = '^4.0.2';\nexport const V18_X_MARKED = '^15.0.7';\nexport const V18_X_MARKED_GFM_HEADING_ID = '^4.1.1';\nexport const V18_X_MARKED_HIGHLIGHT = '^2.2.1';\nexport const V18_X_MARKED_MANGLE = '^1.1.10';\nexport const V18_X_MERMAID = '^10.2.4';\nexport const V18_X_PRISMJS = '^1.29.0';\nexport const V18_X_TAILWINDCSS = '^4.2.2';\nexport const V18_X_TAILWINDCSS_VITE = '^4.2.2';\n\n// devDependencies\nexport const V18_X_ANALOG_JS_PLATFORM = '^3.0.0-alpha.20';\nexport const V18_X_ANGULAR_DEVKIT_BUILD_ANGULAR = '^19.0.0';\nexport const V18_X_NX_VITE = '^21.0.0';\nexport const V18_X_NX_LINTER = '^21.0.0';\nexport const V18_X_JSDOM = '^22.1.0';\nexport const V18_X_VITE = '^8.0.0';\nexport const V18_X_VITE_TSCONFIG_PATHS = '^4.2.0';\nexport const V18_X_VITEST = '^4.0.0';\nexport const V18_X_ZOD = '^3.21.4';\n"],"mappings":";AAeA,IAAa,oBAAoB;AACjC,IAAa,yBAAyB"}
1
+ {"version":3,"file":"versions.js","names":[],"sources":["../../../../../../../../../../nx-plugin/src/generators/app/versions/nx_18_X/versions.ts"],"sourcesContent":["// V18_X\n// dependencies\nexport const V18_X_NX_DEVKIT = '^20.0.0';\nexport const V18_X_NX_ANGULAR = '^20.0.0';\nexport const V18_X_ANALOG_JS_CONTENT = '^3.0.0-alpha.22';\nexport const V18_X_ANALOG_JS_ROUTER = '^3.0.0-alpha.22';\nexport const V18_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = '^3.0.0-alpha.22';\nexport const V18_X_ANALOG_JS_VITEST_ANGULAR = '^3.0.0-alpha.22';\nexport const V18_X_FRONT_MATTER = '^4.0.2';\nexport const V18_X_MARKED = '^15.0.7';\nexport const V18_X_MARKED_GFM_HEADING_ID = '^4.1.1';\nexport const V18_X_MARKED_HIGHLIGHT = '^2.2.1';\nexport const V18_X_MARKED_MANGLE = '^1.1.10';\nexport const V18_X_MERMAID = '^10.2.4';\nexport const V18_X_PRISMJS = '^1.29.0';\nexport const V18_X_TAILWINDCSS = '^4.2.2';\nexport const V18_X_TAILWINDCSS_VITE = '^4.2.2';\n\n// devDependencies\nexport const V18_X_ANALOG_JS_PLATFORM = '^3.0.0-alpha.22';\nexport const V18_X_ANGULAR_DEVKIT_BUILD_ANGULAR = '^19.0.0';\nexport const V18_X_NX_VITE = '^21.0.0';\nexport const V18_X_NX_LINTER = '^21.0.0';\nexport const V18_X_JSDOM = '^22.1.0';\nexport const V18_X_VITE = '^8.0.0';\nexport const V18_X_VITE_TSCONFIG_PATHS = '^4.2.0';\nexport const V18_X_VITEST = '^4.0.0';\nexport const V18_X_ZOD = '^3.21.4';\n"],"mappings":";AAeA,IAAa,oBAAoB;AACjC,IAAa,yBAAyB"}
@@ -1,13 +1,13 @@
1
- export declare const V19_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.20";
2
- export declare const V19_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.20";
1
+ export declare const V19_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.22";
2
+ export declare const V19_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.22";
3
3
  export declare const V19_X_MARKED = "^15.0.7";
4
4
  export declare const V19_X_MARKED_GFM_HEADING_ID = "^4.1.1";
5
5
  export declare const V19_X_MARKED_HIGHLIGHT = "^2.2.1";
6
6
  export declare const V19_X_MARKED_MANGLE = "^1.1.10";
7
7
  export declare const V19_X_PRISMJS = "^1.29.0";
8
- export declare const V19_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.20";
9
- export declare const V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.20";
10
- export declare const V19_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.20";
8
+ export declare const V19_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.22";
9
+ export declare const V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.22";
10
+ export declare const V19_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.22";
11
11
  export declare const V19_X_NX_ANGULAR = "^22.0.0";
12
12
  export declare const V19_X_NX_VITE = "^22.0.0";
13
13
  export declare const V19_X_JSDOM = "^22.0.0";
@@ -1,14 +1,14 @@
1
1
  //#region packages/nx-plugin/src/utils/versions/ng_19_X/versions.ts
2
- var V19_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.20";
3
- var V19_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.20";
2
+ var V19_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.22";
3
+ var V19_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.22";
4
4
  var V19_X_MARKED = "^15.0.7";
5
5
  var V19_X_MARKED_GFM_HEADING_ID = "^4.1.1";
6
6
  var V19_X_MARKED_HIGHLIGHT = "^2.2.1";
7
7
  var V19_X_MARKED_MANGLE = "^1.1.10";
8
8
  var V19_X_PRISMJS = "^1.29.0";
9
- var V19_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.20";
10
- var V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.20";
11
- var V19_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.20";
9
+ var V19_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.22";
10
+ var V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.22";
11
+ var V19_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.22";
12
12
  var V19_X_NX_VITE = "^22.0.0";
13
13
  var V19_X_JSDOM = "^22.0.0";
14
14
  var V19_X_VITE_TSCONFIG_PATHS = "^4.2.0";
@@ -1 +1 @@
1
- {"version":3,"file":"versions.js","names":[],"sources":["../../../../../../../../../nx-plugin/src/utils/versions/ng_19_X/versions.ts"],"sourcesContent":["// V19_X\nexport const V19_X_ANALOG_JS_ROUTER = '^3.0.0-alpha.20';\nexport const V19_X_ANALOG_JS_CONTENT = '^3.0.0-alpha.20';\nexport const V19_X_MARKED = '^15.0.7';\nexport const V19_X_MARKED_GFM_HEADING_ID = '^4.1.1';\nexport const V19_X_MARKED_HIGHLIGHT = '^2.2.1';\nexport const V19_X_MARKED_MANGLE = '^1.1.10';\nexport const V19_X_PRISMJS = '^1.29.0';\n\n// devDependencies\nexport const V19_X_ANALOG_JS_PLATFORM = '^3.0.0-alpha.20';\nexport const V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = '^3.0.0-alpha.20';\nexport const V19_X_ANALOG_JS_VITEST_ANGULAR = '^3.0.0-alpha.20';\nexport const V19_X_NX_ANGULAR = '^22.0.0';\nexport const V19_X_NX_VITE = '^22.0.0';\nexport const V19_X_JSDOM = '^22.0.0';\nexport const V19_X_VITE_TSCONFIG_PATHS = '^4.2.0';\nexport const V19_X_VITEST = '^3.0.0';\nexport const V19_X_VITE = '^6.0.0';\nexport const NX_X_LATEST_VITE = '^8.0.0';\nexport const NX_X_LATEST_VITEST = '^4.0.0';\n"],"mappings":";AACA,IAAa,yBAAyB;AACtC,IAAa,0BAA0B;AACvC,IAAa,eAAe;AAC5B,IAAa,8BAA8B;AAC3C,IAAa,yBAAyB;AACtC,IAAa,sBAAsB;AACnC,IAAa,gBAAgB;AAG7B,IAAa,2BAA2B;AACxC,IAAa,sCAAsC;AACnD,IAAa,iCAAiC;AAE9C,IAAa,gBAAgB;AAC7B,IAAa,cAAc;AAC3B,IAAa,4BAA4B;AACzC,IAAa,eAAe;AAC5B,IAAa,aAAa;AAC1B,IAAa,mBAAmB;AAChC,IAAa,qBAAqB"}
1
+ {"version":3,"file":"versions.js","names":[],"sources":["../../../../../../../../../nx-plugin/src/utils/versions/ng_19_X/versions.ts"],"sourcesContent":["// V19_X\nexport const V19_X_ANALOG_JS_ROUTER = '^3.0.0-alpha.22';\nexport const V19_X_ANALOG_JS_CONTENT = '^3.0.0-alpha.22';\nexport const V19_X_MARKED = '^15.0.7';\nexport const V19_X_MARKED_GFM_HEADING_ID = '^4.1.1';\nexport const V19_X_MARKED_HIGHLIGHT = '^2.2.1';\nexport const V19_X_MARKED_MANGLE = '^1.1.10';\nexport const V19_X_PRISMJS = '^1.29.0';\n\n// devDependencies\nexport const V19_X_ANALOG_JS_PLATFORM = '^3.0.0-alpha.22';\nexport const V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = '^3.0.0-alpha.22';\nexport const V19_X_ANALOG_JS_VITEST_ANGULAR = '^3.0.0-alpha.22';\nexport const V19_X_NX_ANGULAR = '^22.0.0';\nexport const V19_X_NX_VITE = '^22.0.0';\nexport const V19_X_JSDOM = '^22.0.0';\nexport const V19_X_VITE_TSCONFIG_PATHS = '^4.2.0';\nexport const V19_X_VITEST = '^3.0.0';\nexport const V19_X_VITE = '^6.0.0';\nexport const NX_X_LATEST_VITE = '^8.0.0';\nexport const NX_X_LATEST_VITEST = '^4.0.0';\n"],"mappings":";AACA,IAAa,yBAAyB;AACtC,IAAa,0BAA0B;AACvC,IAAa,eAAe;AAC5B,IAAa,8BAA8B;AAC3C,IAAa,yBAAyB;AACtC,IAAa,sBAAsB;AACnC,IAAa,gBAAgB;AAG7B,IAAa,2BAA2B;AACxC,IAAa,sCAAsC;AACnD,IAAa,iCAAiC;AAE9C,IAAa,gBAAgB;AAC7B,IAAa,cAAc;AAC3B,IAAa,4BAA4B;AACzC,IAAa,eAAe;AAC5B,IAAa,aAAa;AAC1B,IAAa,mBAAmB;AAChC,IAAa,qBAAqB"}
@@ -77,6 +77,17 @@ export interface Options {
77
77
  */
78
78
  additionalAPIDirs?: string[];
79
79
  /**
80
+ * Automatically discover route directories (pages, content, API) in
81
+ * workspace libraries by scanning tsconfig.base.json path aliases.
82
+ *
83
+ * Discovered directories are merged with any explicit
84
+ * `additionalPagesDirs`, `additionalContentDirs`, and
85
+ * `additionalAPIDirs` values.
86
+ *
87
+ * @default false
88
+ */
89
+ discoverRoutes?: boolean;
90
+ /**
80
91
  * Additional files to include in compilation
81
92
  */
82
93
  include?: string[];
@@ -1,3 +1,4 @@
1
+ import { discoverLibraryRoutes } from "./discover-library-routes.js";
1
2
  import { routerPlugin } from "./router-plugin.js";
2
3
  import { ssrBuildPlugin } from "./ssr/ssr-build-plugin.js";
3
4
  import { contentPlugin } from "./content-plugin.js";
@@ -19,6 +20,12 @@ function platformPlugin(opts = {}) {
19
20
  ssr: true,
20
21
  ...opts
21
22
  };
23
+ if (platformOptions.discoverRoutes) {
24
+ const discovered = discoverLibraryRoutes(platformOptions.workspaceRoot ?? process.env["NX_WORKSPACE_ROOT"] ?? process.cwd());
25
+ platformOptions.additionalPagesDirs = [...new Set([...platformOptions.additionalPagesDirs ?? [], ...discovered.additionalPagesDirs])];
26
+ platformOptions.additionalContentDirs = [...new Set([...platformOptions.additionalContentDirs ?? [], ...discovered.additionalContentDirs])];
27
+ platformOptions.additionalAPIDirs = [...new Set([...platformOptions.additionalAPIDirs ?? [], ...discovered.additionalAPIDirs])];
28
+ }
22
29
  const useAngularCompilationAPI = platformOptions.experimental?.useAngularCompilationAPI ?? viteOptions?.experimental?.useAngularCompilationAPI;
23
30
  let nitroOptions = platformOptions?.nitro;
24
31
  if (nitroOptions?.routeRules) nitroOptions = {
@@ -1 +1 @@
1
- {"version":3,"file":"platform-plugin.js","names":[],"sources":["../../../src/lib/platform-plugin.ts"],"sourcesContent":["import { Plugin } from 'vite';\nimport viteNitroPlugin from '@analogjs/vite-plugin-nitro';\nimport angular from '@analogjs/vite-plugin-angular';\n\nimport { Options } from './options.js';\nimport { routerPlugin } from './router-plugin.js';\nimport { ssrBuildPlugin } from './ssr/ssr-build-plugin.js';\nimport { contentPlugin } from './content-plugin.js';\nimport { clearClientPageEndpointsPlugin } from './clear-client-page-endpoint.js';\nimport { depsPlugin } from './deps-plugin.js';\nimport { injectHTMLPlugin } from './ssr/inject-html-plugin.js';\nimport { serverModePlugin } from '../server-mode-plugin.js';\nimport { routeGenerationPlugin } from './route-generation-plugin.js';\n\n// Bridge Plugin types from external @analogjs packages that resolve a different vite instance\nfunction externalPlugins(plugins: unknown): Plugin[] {\n return plugins as Plugin[];\n}\n\nexport function platformPlugin(opts: Options = {}): Plugin[] {\n const isTest = process.env['NODE_ENV'] === 'test' || !!process.env['VITEST'];\n const viteOptions = opts?.vite === false ? undefined : opts?.vite;\n const { ...platformOptions } = {\n ssr: true,\n ...opts,\n };\n const useAngularCompilationAPI =\n platformOptions.experimental?.useAngularCompilationAPI ??\n viteOptions?.experimental?.useAngularCompilationAPI;\n let nitroOptions = platformOptions?.nitro;\n\n if (nitroOptions?.routeRules) {\n nitroOptions = {\n ...nitroOptions,\n routeRules: Object.keys(nitroOptions.routeRules).reduce(\n (config, curr) => {\n return {\n ...config,\n [curr]: {\n ...config[curr],\n headers: {\n ...config[curr].headers,\n 'x-analog-no-ssr':\n config[curr]?.ssr === false ? 'true' : undefined,\n } as any,\n },\n };\n },\n nitroOptions.routeRules,\n ),\n };\n }\n\n return [\n ...externalPlugins(viteNitroPlugin(platformOptions as any, nitroOptions)),\n ...(platformOptions.ssr\n ? [...ssrBuildPlugin(), ...injectHTMLPlugin()]\n : []),\n ...(!isTest ? depsPlugin(platformOptions) : []),\n ...routerPlugin(platformOptions),\n routeGenerationPlugin(platformOptions),\n ...contentPlugin(platformOptions?.content, platformOptions),\n ...(opts?.vite === false\n ? []\n : externalPlugins(\n angular({\n jit: platformOptions.jit,\n workspaceRoot: platformOptions.workspaceRoot,\n // Let the Angular plugin keep its own dev-friendly default unless the\n // app explicitly opts into stricter serve-time diagnostics.\n disableTypeChecking: platformOptions.disableTypeChecking,\n include: [\n ...(platformOptions.include ?? []),\n ...(platformOptions.additionalPagesDirs ?? []).map(\n (pageDir) => `${pageDir}/**/*.page.ts`,\n ),\n ],\n additionalContentDirs: platformOptions.additionalContentDirs,\n liveReload: platformOptions.liveReload,\n inlineStylesExtension: platformOptions.inlineStylesExtension,\n fileReplacements: platformOptions.fileReplacements,\n ...(viteOptions ?? {}),\n experimental: {\n ...(viteOptions?.experimental ?? {}),\n useAngularCompilationAPI,\n },\n }),\n )),\n ...serverModePlugin(),\n ...clearClientPageEndpointsPlugin(),\n ];\n}\n"],"mappings":";;;;;;;;;;;AAeA,SAAS,gBAAgB,SAA4B;AACnD,QAAO;;AAGT,SAAgB,eAAe,OAAgB,EAAE,EAAY;CAC3D,MAAM,SAAA,QAAA,IAAA,aAAqC,UAAU,CAAC,CAAC,QAAQ,IAAI;CACnE,MAAM,cAAc,MAAM,SAAS,QAAQ,KAAA,IAAY,MAAM;CAC7D,MAAM,EAAE,GAAG,oBAAoB;EAC7B,KAAK;EACL,GAAG;EACJ;CACD,MAAM,2BACJ,gBAAgB,cAAc,4BAC9B,aAAa,cAAc;CAC7B,IAAI,eAAe,iBAAiB;AAEpC,KAAI,cAAc,WAChB,gBAAe;EACb,GAAG;EACH,YAAY,OAAO,KAAK,aAAa,WAAW,CAAC,QAC9C,QAAQ,SAAS;AAChB,UAAO;IACL,GAAG;KACF,OAAO;KACN,GAAG,OAAO;KACV,SAAS;MACP,GAAG,OAAO,MAAM;MAChB,mBACE,OAAO,OAAO,QAAQ,QAAQ,SAAS,KAAA;MAC1C;KACF;IACF;KAEH,aAAa,WACd;EACF;AAGH,QAAO;EACL,GAAG,gBAAgB,gBAAgB,iBAAwB,aAAa,CAAC;EACzE,GAAI,gBAAgB,MAChB,CAAC,GAAG,gBAAgB,EAAE,GAAG,kBAAkB,CAAC,GAC5C,EAAE;EACN,GAAI,CAAC,SAAS,WAAW,gBAAgB,GAAG,EAAE;EAC9C,GAAG,aAAa,gBAAgB;EAChC,sBAAsB,gBAAgB;EACtC,GAAG,cAAc,iBAAiB,SAAS,gBAAgB;EAC3D,GAAI,MAAM,SAAS,QACf,EAAE,GACF,gBACE,QAAQ;GACN,KAAK,gBAAgB;GACrB,eAAe,gBAAgB;GAG/B,qBAAqB,gBAAgB;GACrC,SAAS,CACP,GAAI,gBAAgB,WAAW,EAAE,EACjC,IAAI,gBAAgB,uBAAuB,EAAE,EAAE,KAC5C,YAAY,GAAG,QAAQ,eACzB,CACF;GACD,uBAAuB,gBAAgB;GACvC,YAAY,gBAAgB;GAC5B,uBAAuB,gBAAgB;GACvC,kBAAkB,gBAAgB;GAClC,GAAI,eAAe,EAAE;GACrB,cAAc;IACZ,GAAI,aAAa,gBAAgB,EAAE;IACnC;IACD;GACF,CAAC,CACH;EACL,GAAG,kBAAkB;EACrB,GAAG,gCAAgC;EACpC"}
1
+ {"version":3,"file":"platform-plugin.js","names":[],"sources":["../../../src/lib/platform-plugin.ts"],"sourcesContent":["import { Plugin } from 'vite';\nimport viteNitroPlugin from '@analogjs/vite-plugin-nitro';\nimport angular from '@analogjs/vite-plugin-angular';\n\nimport { Options } from './options.js';\nimport { discoverLibraryRoutes } from './discover-library-routes.js';\nimport { routerPlugin } from './router-plugin.js';\nimport { ssrBuildPlugin } from './ssr/ssr-build-plugin.js';\nimport { contentPlugin } from './content-plugin.js';\nimport { clearClientPageEndpointsPlugin } from './clear-client-page-endpoint.js';\nimport { depsPlugin } from './deps-plugin.js';\nimport { injectHTMLPlugin } from './ssr/inject-html-plugin.js';\nimport { serverModePlugin } from '../server-mode-plugin.js';\nimport { routeGenerationPlugin } from './route-generation-plugin.js';\n\n// Bridge Plugin types from external @analogjs packages that resolve a different vite instance\nfunction externalPlugins(plugins: unknown): Plugin[] {\n return plugins as Plugin[];\n}\n\nexport function platformPlugin(opts: Options = {}): Plugin[] {\n const isTest = process.env['NODE_ENV'] === 'test' || !!process.env['VITEST'];\n const viteOptions = opts?.vite === false ? undefined : opts?.vite;\n const { ...platformOptions } = {\n ssr: true,\n ...opts,\n };\n if (platformOptions.discoverRoutes) {\n const workspaceRoot =\n platformOptions.workspaceRoot ??\n process.env['NX_WORKSPACE_ROOT'] ??\n process.cwd();\n const discovered = discoverLibraryRoutes(workspaceRoot);\n platformOptions.additionalPagesDirs = [\n ...new Set([\n ...(platformOptions.additionalPagesDirs ?? []),\n ...discovered.additionalPagesDirs,\n ]),\n ];\n platformOptions.additionalContentDirs = [\n ...new Set([\n ...(platformOptions.additionalContentDirs ?? []),\n ...discovered.additionalContentDirs,\n ]),\n ];\n platformOptions.additionalAPIDirs = [\n ...new Set([\n ...(platformOptions.additionalAPIDirs ?? []),\n ...discovered.additionalAPIDirs,\n ]),\n ];\n }\n\n const useAngularCompilationAPI =\n platformOptions.experimental?.useAngularCompilationAPI ??\n viteOptions?.experimental?.useAngularCompilationAPI;\n let nitroOptions = platformOptions?.nitro;\n\n if (nitroOptions?.routeRules) {\n nitroOptions = {\n ...nitroOptions,\n routeRules: Object.keys(nitroOptions.routeRules).reduce(\n (config, curr) => {\n return {\n ...config,\n [curr]: {\n ...config[curr],\n headers: {\n ...config[curr].headers,\n 'x-analog-no-ssr':\n config[curr]?.ssr === false ? 'true' : undefined,\n } as any,\n },\n };\n },\n nitroOptions.routeRules,\n ),\n };\n }\n\n return [\n ...externalPlugins(viteNitroPlugin(platformOptions as any, nitroOptions)),\n ...(platformOptions.ssr\n ? [...ssrBuildPlugin(), ...injectHTMLPlugin()]\n : []),\n ...(!isTest ? depsPlugin(platformOptions) : []),\n ...routerPlugin(platformOptions),\n routeGenerationPlugin(platformOptions),\n ...contentPlugin(platformOptions?.content, platformOptions),\n ...(opts?.vite === false\n ? []\n : externalPlugins(\n angular({\n jit: platformOptions.jit,\n workspaceRoot: platformOptions.workspaceRoot,\n // Let the Angular plugin keep its own dev-friendly default unless the\n // app explicitly opts into stricter serve-time diagnostics.\n disableTypeChecking: platformOptions.disableTypeChecking,\n include: [\n ...(platformOptions.include ?? []),\n ...(platformOptions.additionalPagesDirs ?? []).map(\n (pageDir) => `${pageDir}/**/*.page.ts`,\n ),\n ],\n additionalContentDirs: platformOptions.additionalContentDirs,\n liveReload: platformOptions.liveReload,\n inlineStylesExtension: platformOptions.inlineStylesExtension,\n fileReplacements: platformOptions.fileReplacements,\n ...(viteOptions ?? {}),\n experimental: {\n ...(viteOptions?.experimental ?? {}),\n useAngularCompilationAPI,\n },\n }),\n )),\n ...serverModePlugin(),\n ...clearClientPageEndpointsPlugin(),\n ];\n}\n"],"mappings":";;;;;;;;;;;;AAgBA,SAAS,gBAAgB,SAA4B;AACnD,QAAO;;AAGT,SAAgB,eAAe,OAAgB,EAAE,EAAY;CAC3D,MAAM,SAAA,QAAA,IAAA,aAAqC,UAAU,CAAC,CAAC,QAAQ,IAAI;CACnE,MAAM,cAAc,MAAM,SAAS,QAAQ,KAAA,IAAY,MAAM;CAC7D,MAAM,EAAE,GAAG,oBAAoB;EAC7B,KAAK;EACL,GAAG;EACJ;AACD,KAAI,gBAAgB,gBAAgB;EAKlC,MAAM,aAAa,sBAHjB,gBAAgB,iBAChB,QAAQ,IAAI,wBACZ,QAAQ,KAAK,CACwC;AACvD,kBAAgB,sBAAsB,CACpC,GAAG,IAAI,IAAI,CACT,GAAI,gBAAgB,uBAAuB,EAAE,EAC7C,GAAG,WAAW,oBACf,CAAC,CACH;AACD,kBAAgB,wBAAwB,CACtC,GAAG,IAAI,IAAI,CACT,GAAI,gBAAgB,yBAAyB,EAAE,EAC/C,GAAG,WAAW,sBACf,CAAC,CACH;AACD,kBAAgB,oBAAoB,CAClC,GAAG,IAAI,IAAI,CACT,GAAI,gBAAgB,qBAAqB,EAAE,EAC3C,GAAG,WAAW,kBACf,CAAC,CACH;;CAGH,MAAM,2BACJ,gBAAgB,cAAc,4BAC9B,aAAa,cAAc;CAC7B,IAAI,eAAe,iBAAiB;AAEpC,KAAI,cAAc,WAChB,gBAAe;EACb,GAAG;EACH,YAAY,OAAO,KAAK,aAAa,WAAW,CAAC,QAC9C,QAAQ,SAAS;AAChB,UAAO;IACL,GAAG;KACF,OAAO;KACN,GAAG,OAAO;KACV,SAAS;MACP,GAAG,OAAO,MAAM;MAChB,mBACE,OAAO,OAAO,QAAQ,QAAQ,SAAS,KAAA;MAC1C;KACF;IACF;KAEH,aAAa,WACd;EACF;AAGH,QAAO;EACL,GAAG,gBAAgB,gBAAgB,iBAAwB,aAAa,CAAC;EACzE,GAAI,gBAAgB,MAChB,CAAC,GAAG,gBAAgB,EAAE,GAAG,kBAAkB,CAAC,GAC5C,EAAE;EACN,GAAI,CAAC,SAAS,WAAW,gBAAgB,GAAG,EAAE;EAC9C,GAAG,aAAa,gBAAgB;EAChC,sBAAsB,gBAAgB;EACtC,GAAG,cAAc,iBAAiB,SAAS,gBAAgB;EAC3D,GAAI,MAAM,SAAS,QACf,EAAE,GACF,gBACE,QAAQ;GACN,KAAK,gBAAgB;GACrB,eAAe,gBAAgB;GAG/B,qBAAqB,gBAAgB;GACrC,SAAS,CACP,GAAI,gBAAgB,WAAW,EAAE,EACjC,IAAI,gBAAgB,uBAAuB,EAAE,EAAE,KAC5C,YAAY,GAAG,QAAQ,eACzB,CACF;GACD,uBAAuB,gBAAgB;GACvC,YAAY,gBAAgB;GAC5B,uBAAuB,gBAAgB;GACvC,kBAAkB,gBAAgB;GAClC,GAAI,eAAe,EAAE;GACrB,cAAc;IACZ,GAAI,aAAa,gBAAgB,EAAE;IACnC;IACD;GACF,CAAC,CACH;EACL,GAAG,kBAAkB;EACrB,GAAG,gCAAgC;EACpC"}
@@ -1,6 +1,6 @@
1
+ import { resolve } from "node:path";
1
2
  import { normalizePath } from "vite";
2
3
  import { globSync } from "tinyglobby";
3
- import { resolve } from "node:path";
4
4
  //#region packages/platform/src/lib/route-file-discovery.ts
5
5
  function createRouteFileDiscovery(options) {
6
6
  const { root, workspaceRoot, additionalPagesDirs, additionalContentDirs } = options;
@@ -13,7 +13,7 @@ export interface RouteSchemaInfo {
13
13
  hasQuerySchema: boolean;
14
14
  }
15
15
  export interface GenerateRouteTreeDeclarationOptions {
16
- jsonLdPaths?: Iterable<string>;
16
+ jsonLdFiles?: Iterable<string>;
17
17
  }
18
18
  export interface RouteEntry {
19
19
  /** Stable structural route id derived from the source filename */
@@ -43,8 +43,18 @@ export interface RouteEntry {
43
43
  /** Whether the route contains an optional catch-all parameter */
44
44
  isOptionalCatchAll: boolean;
45
45
  }
46
+ export interface RouteCollision {
47
+ fullPath: string;
48
+ keptFile: string;
49
+ droppedFile: string;
50
+ /** True when both files have the same collision priority (hard error). */
51
+ samePriority: boolean;
52
+ }
46
53
  export interface RouteManifest {
47
54
  routes: RouteEntry[];
55
+ collisions: RouteCollision[];
56
+ /** Canonical route per fullPath — precomputed once to avoid redundant work. */
57
+ canonicalByFullPath: Map<string, RouteEntry>;
48
58
  }
49
59
  /**
50
60
  * Converts a discovered filename to a route path pattern.
@@ -106,6 +106,7 @@ var NO_SCHEMAS = {
106
106
  */
107
107
  function generateRouteManifest(filenames, schemaDetector, collisionPriority) {
108
108
  const routes = [];
109
+ const collisions = [];
109
110
  const seenByFullPath = /* @__PURE__ */ new Map();
110
111
  const getPriority = collisionPriority ?? getCollisionPriority;
111
112
  const prioritizedFilenames = [...filenames].sort((a, b) => {
@@ -119,12 +120,33 @@ function generateRouteManifest(filenames, schemaDetector, collisionPriority) {
119
120
  const params = extractRouteParams(fullPath);
120
121
  const schemas = schemaDetector ? schemaDetector(filename) : NO_SCHEMAS;
121
122
  const id = filenameToRouteId(filename);
122
- if (seenByFullPath.has(fullPath)) {
123
- const winningFilename = seenByFullPath.get(fullPath);
124
- console.warn(`[Analog] Route collision: '${fullPath}' is defined by both '${winningFilename}' and '${filename}'. Keeping '${winningFilename}' based on route source precedence and skipping duplicate.`);
125
- continue;
123
+ const isPathlessLayout = isPathlessLayoutId(id);
124
+ const currentPriority = getPriority(filename);
125
+ if (!isPathlessLayout) {
126
+ if (seenByFullPath.has(fullPath)) {
127
+ const winner = seenByFullPath.get(fullPath);
128
+ if (winner.filename === filename) continue;
129
+ const isLayoutIndexPair = (a, b) => {
130
+ const indexRe = /\/index\.(page\.)?(ts|js|md|analog|ag)$/;
131
+ const layoutRe = /\.(page\.)?(ts|js|analog|ag)$/;
132
+ if (indexRe.test(a) && layoutRe.test(b)) return a.replace(indexRe, "") === b.replace(layoutRe, "");
133
+ return false;
134
+ };
135
+ if (isLayoutIndexPair(winner.filename, filename) || isLayoutIndexPair(filename, winner.filename)) continue;
136
+ collisions.push({
137
+ fullPath,
138
+ keptFile: winner.filename,
139
+ droppedFile: filename,
140
+ samePriority: winner.priority === currentPriority
141
+ });
142
+ console.warn(`[Analog] Route collision: '${fullPath}' is defined by both '${winner.filename}' and '${filename}'. Keeping '${winner.filename}' based on route source precedence and skipping duplicate.`);
143
+ continue;
144
+ }
145
+ seenByFullPath.set(fullPath, {
146
+ filename,
147
+ priority: currentPriority
148
+ });
126
149
  }
127
- seenByFullPath.set(fullPath, filename);
128
150
  routes.push({
129
151
  id,
130
152
  path: fullPath,
@@ -136,7 +158,7 @@ function generateRouteManifest(filenames, schemaDetector, collisionPriority) {
136
158
  parentId: null,
137
159
  children: [],
138
160
  isIndex: id === "/index" || id.endsWith("/index"),
139
- isGroup: id.includes("/(") || /^\/*\(/.test(id),
161
+ isGroup: isPathlessLayout,
140
162
  isCatchAll: params.some((param) => param.type === "catchAll"),
141
163
  isOptionalCatchAll: params.some((param) => param.type === "optionalCatchAll")
142
164
  });
@@ -147,20 +169,43 @@ function generateRouteManifest(filenames, schemaDetector, collisionPriority) {
147
169
  if (aW !== bW) return aW - bW;
148
170
  return a.fullPath.localeCompare(b.fullPath);
149
171
  });
150
- const routeByFullPath = new Map(routes.map((route) => [route.fullPath, route]));
172
+ const routeByFullPath = canonicalRoutesByFullPath(routes);
173
+ const routeById = new Map(routes.map((route) => [route.id, route]));
151
174
  for (const route of routes) {
152
- const parent = findNearestParentRoute(route.fullPath, routeByFullPath);
175
+ const structuralParent = route.id.includes("/(") ? findNearestParentById(route.id, routeById) : void 0;
176
+ const fullPathParent = findNearestParentRoute(route.fullPath, routeByFullPath);
177
+ const parent = structuralParent ?? fullPathParent;
153
178
  route.parentId = parent?.id ?? null;
154
179
  route.path = computeLocalPath(route.fullPath, parent?.fullPath ?? null);
155
180
  }
156
- const routeById = new Map(routes.map((route) => [route.id, route]));
157
181
  for (const route of routes) if (route.parentId) routeById.get(route.parentId)?.children.push(route.id);
158
182
  for (const route of routes) if (route.schemas.hasParamsSchema && route.params.length === 0) console.warn(`[Analog] Route '${route.fullPath}' exports routeParamsSchema but has no dynamic params in the filename.`);
159
183
  for (const route of routes) {
160
184
  if (route.parentId && !routeById.has(route.parentId)) console.warn(`[Analog] Route '${route.id}' has parentId '${route.parentId}' which does not match any route id in the manifest.`);
161
185
  for (const childId of route.children) if (!routeById.has(childId)) console.warn(`[Analog] Route '${route.id}' lists child '${childId}' which does not match any route id in the manifest.`);
162
186
  }
163
- return { routes };
187
+ return {
188
+ routes,
189
+ collisions,
190
+ canonicalByFullPath: routeByFullPath
191
+ };
192
+ }
193
+ function canonicalRoutesByFullPath(routes) {
194
+ const map = /* @__PURE__ */ new Map();
195
+ for (const route of routes) {
196
+ const existing = map.get(route.fullPath);
197
+ if (!existing) map.set(route.fullPath, route);
198
+ else if (existing.isGroup && !route.isGroup) map.set(route.fullPath, route);
199
+ else if (existing.isGroup && route.isGroup) {
200
+ if (route.id.localeCompare(existing.id) < 0) map.set(route.fullPath, route);
201
+ }
202
+ }
203
+ return map;
204
+ }
205
+ function isPathlessLayoutId(id) {
206
+ const segments = id.split("/").filter(Boolean);
207
+ if (segments.length === 0) return false;
208
+ return /^\([^.[\]]+\)$/.test(segments[segments.length - 1]);
164
209
  }
165
210
  function getRouteWeight(path) {
166
211
  if (path.includes("[[...")) return 3;
@@ -224,7 +269,7 @@ function generateRouteTableDeclaration(manifest) {
224
269
  if (hasAnySchema) lines.push("");
225
270
  lines.push("declare module '@analogjs/router' {");
226
271
  lines.push(" interface AnalogRouteTable {");
227
- for (const route of manifest.routes) {
272
+ for (const route of manifest.canonicalByFullPath.values()) {
228
273
  const paramsAlias = schemaImports.get(`${route.fullPath}:params`);
229
274
  const queryAlias = schemaImports.get(`${route.fullPath}:query`);
230
275
  const paramsType = generateParamsType(route.params);
@@ -250,7 +295,7 @@ function generateRouteTableDeclaration(manifest) {
250
295
  */
251
296
  function generateRouteTreeDeclaration(manifest, options = {}) {
252
297
  const lines = [];
253
- const jsonLdPaths = new Set(options.jsonLdPaths ?? []);
298
+ const jsonLdFiles = new Set(options.jsonLdFiles ?? []);
254
299
  lines.push("// This file is auto-generated by @analogjs/platform");
255
300
  lines.push("// Do not edit manually");
256
301
  lines.push("");
@@ -282,7 +327,7 @@ function generateRouteTreeDeclaration(manifest, options = {}) {
282
327
  lines.push("}");
283
328
  lines.push("");
284
329
  lines.push("export interface AnalogFileRoutesByFullPath {");
285
- for (const route of manifest.routes) lines.push(` ${toTsKey(route.fullPath)}: AnalogFileRoutesById[${toTsStringLiteral(route.id)}];`);
330
+ for (const route of manifest.canonicalByFullPath.values()) lines.push(` ${toTsKey(route.fullPath)}: AnalogFileRoutesById[${toTsStringLiteral(route.id)}];`);
286
331
  lines.push("}");
287
332
  lines.push("");
288
333
  lines.push("export type AnalogRouteTreeId = keyof AnalogFileRoutesById;");
@@ -301,7 +346,7 @@ function generateRouteTreeDeclaration(manifest, options = {}) {
301
346
  lines.push(` kind: ${toTsStringLiteral(route.kind)},`);
302
347
  lines.push(` hasParamsSchema: ${String(route.schemas.hasParamsSchema)},`);
303
348
  lines.push(` hasQuerySchema: ${String(route.schemas.hasQuerySchema)},`);
304
- lines.push(` hasJsonLd: ${String(jsonLdPaths.has(route.fullPath))},`);
349
+ lines.push(` hasJsonLd: ${String(jsonLdFiles.has(route.filename))},`);
305
350
  lines.push(` isIndex: ${String(route.isIndex)},`);
306
351
  lines.push(` isGroup: ${String(route.isGroup)},`);
307
352
  lines.push(` isCatchAll: ${String(route.isCatchAll)},`);
@@ -310,7 +355,7 @@ function generateRouteTreeDeclaration(manifest, options = {}) {
310
355
  }
311
356
  lines.push(" },");
312
357
  lines.push(" byFullPath: {");
313
- for (const route of manifest.routes) lines.push(` ${toObjectKey(route.fullPath)}: ${toTsStringLiteral(route.id)},`);
358
+ for (const route of manifest.canonicalByFullPath.values()) lines.push(` ${toObjectKey(route.fullPath)}: ${toTsStringLiteral(route.id)},`);
314
359
  lines.push(" },");
315
360
  lines.push("} as const;");
316
361
  lines.push("");
@@ -333,6 +378,15 @@ function generateParamsType(params) {
333
378
  function isValidIdentifier(name) {
334
379
  return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
335
380
  }
381
+ function findNearestParentById(id, routesById) {
382
+ if (id === "/") return;
383
+ const segments = id.split("/").filter(Boolean);
384
+ for (let index = segments.length - 1; index > 0; index--) {
385
+ const candidate = "/" + segments.slice(0, index).join("/");
386
+ const route = routesById.get(candidate);
387
+ if (route) return route;
388
+ }
389
+ }
336
390
  function findNearestParentRoute(fullPath, routesByFullPath) {
337
391
  if (fullPath === "/") return;
338
392
  const segments = fullPath.slice(1).split("/");
@@ -1 +1 @@
1
- {"version":3,"file":"route-manifest.js","names":[],"sources":["../../../src/lib/route-manifest.ts"],"sourcesContent":["/**\n * Route-manifest engine for typed file routes.\n *\n * Pure functions (no Angular dependencies) for converting discovered\n * filenames into typed route manifests and generated declarations.\n */\n\nexport interface RouteParamInfo {\n name: string;\n type: 'dynamic' | 'catchAll' | 'optionalCatchAll';\n}\n\nexport interface RouteSchemaInfo {\n hasParamsSchema: boolean;\n hasQuerySchema: boolean;\n}\n\nexport interface GenerateRouteTreeDeclarationOptions {\n jsonLdPaths?: Iterable<string>;\n}\n\nexport interface RouteEntry {\n /** Stable structural route id derived from the source filename */\n id: string;\n /** The route path segment relative to the nearest existing parent route */\n path: string;\n /** The fully resolved navigation path pattern (e.g., '/users/[id]') */\n fullPath: string;\n /** Extracted parameter information */\n params: RouteParamInfo[];\n /** Original filename that produced this route */\n filename: string;\n /** Schema export info (detected from file content) */\n schemas: RouteSchemaInfo;\n /** Type of source that produced this route */\n kind: 'page' | 'content';\n /** Parent route id, or null for top-level routes */\n parentId: string | null;\n /** Child route ids */\n children: string[];\n /** Whether the source filename represents an index route */\n isIndex: boolean;\n /** Whether the source filename includes route-group/pathless segments */\n isGroup: boolean;\n /** Whether the route contains a required catch-all parameter */\n isCatchAll: boolean;\n /** Whether the route contains an optional catch-all parameter */\n isOptionalCatchAll: boolean;\n}\n\nexport interface RouteManifest {\n routes: RouteEntry[];\n}\n\n/**\n * Converts a discovered filename to a route path pattern.\n *\n * Uses the same stripping rules as the existing route system\n * but preserves bracket param syntax instead of converting to\n * Angular's `:param` syntax.\n *\n * The regex applies four alternations (left to right, all replaced with ''):\n * 1. `^(.*?)[\\\\/](?:routes|pages|content)[\\\\/]` — anchored, strips everything\n * up to and including the first /routes/, /pages/, or /content/ segment.\n * Handles app-local paths (`/src/app/pages/`) AND additional dirs\n * (`/libs/shared/feature/src/content/`) uniformly.\n * 2. `[\\\\/](?:app[\\\\/](?:routes|pages)|src[\\\\/]content)[\\\\/]` — non-anchored\n * fallback for legacy paths where the directory marker appears mid-string.\n * 3. `\\.page\\.(js|ts|analog|ag)$` — strips page file extensions.\n * 4. `\\.(ts|md|analog|ag)$` — strips remaining file extensions.\n *\n * Examples:\n * - '/app/routes/index.ts' -> '/'\n * - '/app/routes/about.ts' -> '/about'\n * - '/src/app/pages/users/[id].page.ts' -> '/users/[id]'\n * - '/app/routes/blog.[slug].ts' -> '/blog/[slug]'\n * - '/src/app/pages/(auth)/login.page.ts' -> '/login'\n * - '/src/app/pages/docs/[...slug].page.ts' -> '/docs/[...slug]'\n * - '/src/app/pages/shop/[[...category]].page.ts' -> '/shop/[[...category]]'\n * - '/libs/shared/feature/src/content/test.md' -> '/test'\n */\nexport function filenameToRoutePath(filename: string): string {\n let path = filename.replace(\n /^(?:[a-zA-Z]:[\\\\/])?(.*?)[\\\\/](?:routes|pages|content)[\\\\/]|(?:[\\\\/](?:app[\\\\/](?:routes|pages)|src[\\\\/]content)[\\\\/])|(\\.page\\.(js|ts|analog|ag)$)|(\\.(ts|md|analog|ag)$)/g,\n '',\n );\n\n const brackets: string[] = [];\n path = path.replace(/\\[\\[?\\.{0,3}[^\\]]*\\]?\\]/g, (match) => {\n brackets.push(match);\n // eslint-disable-next-line no-control-regex\n return `\\0B${brackets.length - 1}\\0`;\n });\n path = path.replace(/\\./g, '/');\n // eslint-disable-next-line no-control-regex\n path = path.replace(/\\0B(\\d+)\\0/g, (_, idx) => brackets[Number(idx)]);\n\n const segments = path.split('/').filter(Boolean);\n const processed: string[] = [];\n\n for (const segment of segments) {\n if (/^\\([^.[\\]]*\\)$/.test(segment)) continue;\n processed.push(segment);\n }\n\n if (processed.length > 0 && processed[processed.length - 1] === 'index') {\n processed.pop();\n }\n\n return '/' + processed.join('/');\n}\n\n/**\n * Converts a discovered filename to a stable structural route id.\n *\n * Unlike `filenameToRoutePath`, this preserves route groups and `index`\n * segments so that multiple files resolving to the same URL shape can still\n * have distinct structural identities in the generated route tree metadata.\n *\n * Uses the same directory-stripping regex as `filenameToRoutePath` —\n * changes to the regex must be kept in sync between both functions.\n */\nexport function filenameToRouteId(filename: string): string {\n let path = filename.replace(\n /^(?:[a-zA-Z]:[\\\\/])?(.*?)[\\\\/](?:routes|pages|content)[\\\\/]|(?:[\\\\/](?:app[\\\\/](?:routes|pages)|src[\\\\/]content)[\\\\/])|(\\.page\\.(js|ts|analog|ag)$)|(\\.(ts|md|analog|ag)$)/g,\n '',\n );\n\n const brackets: string[] = [];\n path = path.replace(/\\[\\[?\\.{0,3}[^\\]]*\\]?\\]/g, (match) => {\n brackets.push(match);\n // eslint-disable-next-line no-control-regex\n return `\\0B${brackets.length - 1}\\0`;\n });\n path = path.replace(/\\./g, '/');\n // eslint-disable-next-line no-control-regex\n path = path.replace(/\\0B(\\d+)\\0/g, (_, idx) => brackets[Number(idx)]);\n\n const segments = path.split('/').filter(Boolean);\n\n return '/' + segments.join('/');\n}\n\n/**\n * Extracts parameter information from a route path pattern.\n */\nexport function extractRouteParams(routePath: string): RouteParamInfo[] {\n const params: RouteParamInfo[] = [];\n\n for (const match of routePath.matchAll(/\\[\\[\\.\\.\\.([^\\]]+)\\]\\]/g)) {\n params.push({ name: match[1], type: 'optionalCatchAll' });\n }\n for (const match of routePath.matchAll(/(?<!\\[)\\[\\.\\.\\.([^\\]]+)\\](?!\\])/g)) {\n params.push({ name: match[1], type: 'catchAll' });\n }\n for (const match of routePath.matchAll(/(?<!\\[)\\[(?!\\.)([^\\]]+)\\](?!\\])/g)) {\n params.push({ name: match[1], type: 'dynamic' });\n }\n\n return params;\n}\n\n/**\n * Detects whether a route file exports schema constants.\n */\nexport function detectSchemaExports(fileContent: string): RouteSchemaInfo {\n return {\n hasParamsSchema: /export\\s+const\\s+routeParamsSchema\\b/.test(fileContent),\n hasQuerySchema: /export\\s+const\\s+routeQuerySchema\\b/.test(fileContent),\n };\n}\n\nconst NO_SCHEMAS: RouteSchemaInfo = {\n hasParamsSchema: false,\n hasQuerySchema: false,\n};\n\n/**\n * Generates a route manifest from a list of discovered filenames.\n *\n * @param collisionPriority - Optional callback that returns a numeric priority\n * for each filename (lower wins). When provided, this replaces the default\n * hard-coded path-substring heuristic with config-derived precedence.\n */\nexport function generateRouteManifest(\n filenames: string[],\n schemaDetector?: (filename: string) => RouteSchemaInfo,\n collisionPriority?: (filename: string) => number,\n): RouteManifest {\n const routes: RouteEntry[] = [];\n const seenByFullPath = new Map<string, string>();\n const getPriority = collisionPriority ?? getCollisionPriority;\n\n // Prefer app-local route files over shared/external sources when two files\n // resolve to the same URL. This keeps `additionalPagesDirs` additive instead\n // of unexpectedly overriding the route that lives inside the app itself.\n const prioritizedFilenames = [...filenames].sort((a, b) => {\n const aPriority = getPriority(a);\n const bPriority = getPriority(b);\n if (aPriority !== bPriority) {\n return aPriority - bPriority;\n }\n return a.localeCompare(b);\n });\n\n for (const filename of prioritizedFilenames) {\n const fullPath = filenameToRoutePath(filename);\n const params = extractRouteParams(fullPath);\n const schemas = schemaDetector ? schemaDetector(filename) : NO_SCHEMAS;\n const id = filenameToRouteId(filename);\n\n if (seenByFullPath.has(fullPath)) {\n const winningFilename = seenByFullPath.get(fullPath);\n console.warn(\n `[Analog] Route collision: '${fullPath}' is defined by both ` +\n `'${winningFilename}' and '${filename}'. ` +\n `Keeping '${winningFilename}' based on route source precedence and skipping duplicate.`,\n );\n continue;\n }\n seenByFullPath.set(fullPath, filename);\n\n routes.push({\n id,\n path: fullPath,\n fullPath,\n params,\n filename,\n schemas,\n kind: filename.endsWith('.md') ? 'content' : 'page',\n parentId: null,\n children: [],\n isIndex: id === '/index' || id.endsWith('/index'),\n isGroup: id.includes('/(') || /^\\/*\\(/.test(id),\n isCatchAll: params.some((param) => param.type === 'catchAll'),\n isOptionalCatchAll: params.some(\n (param) => param.type === 'optionalCatchAll',\n ),\n });\n }\n\n routes.sort((a, b) => {\n const aW = getRouteWeight(a.fullPath);\n const bW = getRouteWeight(b.fullPath);\n if (aW !== bW) return aW - bW;\n return a.fullPath.localeCompare(b.fullPath);\n });\n\n const routeByFullPath = new Map(\n routes.map((route) => [route.fullPath, route]),\n );\n\n for (const route of routes) {\n const parent = findNearestParentRoute(route.fullPath, routeByFullPath);\n route.parentId = parent?.id ?? null;\n route.path = computeLocalPath(route.fullPath, parent?.fullPath ?? null);\n }\n\n const routeById = new Map(routes.map((route) => [route.id, route]));\n for (const route of routes) {\n if (route.parentId) {\n routeById.get(route.parentId)?.children.push(route.id);\n }\n }\n\n for (const route of routes) {\n if (route.schemas.hasParamsSchema && route.params.length === 0) {\n console.warn(\n `[Analog] Route '${route.fullPath}' exports routeParamsSchema` +\n ` but has no dynamic params in the filename.`,\n );\n }\n }\n\n // Build-time consistency check: every parentId and child reference must\n // point to a real route in the manifest. Invalid references indicate a\n // bug in the hierarchy computation.\n for (const route of routes) {\n if (route.parentId && !routeById.has(route.parentId)) {\n console.warn(\n `[Analog] Route '${route.id}' has parentId '${route.parentId}' ` +\n `which does not match any route id in the manifest.`,\n );\n }\n for (const childId of route.children) {\n if (!routeById.has(childId)) {\n console.warn(\n `[Analog] Route '${route.id}' lists child '${childId}' ` +\n `which does not match any route id in the manifest.`,\n );\n }\n }\n }\n\n return { routes };\n}\n\nfunction getRouteWeight(path: string): number {\n if (path.includes('[[...')) return 3;\n if (path.includes('[...')) return 2;\n if (path.includes('[')) return 1;\n return 0;\n}\n\nfunction getCollisionPriority(filename: string): number {\n if (\n filename.includes('/src/app/pages/') ||\n filename.includes('/src/app/routes/') ||\n filename.includes('/app/pages/') ||\n filename.includes('/app/routes/') ||\n filename.includes('/src/content/')\n ) {\n return 0;\n }\n\n return 1;\n}\n\n/**\n * Produces a human-readable summary of the generated route manifest.\n */\nexport function formatManifestSummary(manifest: RouteManifest): string {\n const lines: string[] = [];\n const total = manifest.routes.length;\n const withSchemas = manifest.routes.filter(\n (r) => r.schemas.hasParamsSchema || r.schemas.hasQuerySchema,\n ).length;\n const staticCount = manifest.routes.filter(\n (r) => r.params.length === 0,\n ).length;\n const dynamicCount = total - staticCount;\n\n lines.push(`[Analog] Generated typed routes:`);\n lines.push(\n ` ${total} routes (${staticCount} static, ${dynamicCount} dynamic)`,\n );\n if (withSchemas > 0) {\n lines.push(` ${withSchemas} with schema validation`);\n }\n\n for (const route of manifest.routes) {\n const flags: string[] = [];\n if (route.schemas.hasParamsSchema) flags.push('params-schema');\n if (route.schemas.hasQuerySchema) flags.push('query-schema');\n const suffix = flags.length > 0 ? ` [${flags.join(', ')}]` : '';\n lines.push(` ${route.fullPath}${suffix}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Generates the route-table section for the combined generated route module.\n */\nexport function generateRouteTableDeclaration(manifest: RouteManifest): string {\n const lines: string[] = [];\n const hasAnySchema = manifest.routes.some(\n (r) => r.schemas.hasParamsSchema || r.schemas.hasQuerySchema,\n );\n\n lines.push('// This file is auto-generated by @analogjs/platform');\n lines.push('// Do not edit manually');\n lines.push('');\n\n if (hasAnySchema) {\n lines.push(\n \"import type { StandardSchemaV1 } from '@standard-schema/spec';\",\n );\n }\n\n const schemaImports = new Map<string, string>();\n let aliasIndex = 0;\n\n for (const route of manifest.routes) {\n if (route.schemas.hasParamsSchema || route.schemas.hasQuerySchema) {\n const importPath = filenameToImportPath(route.filename);\n const names: string[] = [];\n\n if (route.schemas.hasParamsSchema) {\n const alias = `_p${aliasIndex}`;\n names.push(`routeParamsSchema as ${alias}`);\n schemaImports.set(`${route.fullPath}:params`, alias);\n }\n if (route.schemas.hasQuerySchema) {\n const alias = `_q${aliasIndex}`;\n names.push(`routeQuerySchema as ${alias}`);\n schemaImports.set(`${route.fullPath}:query`, alias);\n }\n\n lines.push(`import type { ${names.join(', ')} } from '${importPath}';`);\n aliasIndex++;\n }\n }\n\n if (hasAnySchema) {\n lines.push('');\n }\n\n lines.push(\"declare module '@analogjs/router' {\");\n lines.push(' interface AnalogRouteTable {');\n\n for (const route of manifest.routes) {\n const paramsAlias = schemaImports.get(`${route.fullPath}:params`);\n const queryAlias = schemaImports.get(`${route.fullPath}:query`);\n\n const paramsType = generateParamsType(route.params);\n const queryType = 'Record<string, string | string[] | undefined>';\n\n const paramsOutputType = paramsAlias\n ? `StandardSchemaV1.InferOutput<typeof ${paramsAlias}>`\n : paramsType;\n const queryOutputType = queryAlias\n ? `StandardSchemaV1.InferOutput<typeof ${queryAlias}>`\n : queryType;\n\n lines.push(` '${route.fullPath}': {`);\n lines.push(` params: ${paramsType};`);\n lines.push(` paramsOutput: ${paramsOutputType};`);\n lines.push(` query: ${queryType};`);\n lines.push(` queryOutput: ${queryOutputType};`);\n lines.push(` };`);\n }\n\n lines.push(' }');\n lines.push('}');\n lines.push('');\n lines.push('export {};');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n/**\n * Generates the route-tree section for the combined generated route module.\n */\nexport function generateRouteTreeDeclaration(\n manifest: RouteManifest,\n options: GenerateRouteTreeDeclarationOptions = {},\n): string {\n const lines: string[] = [];\n const jsonLdPaths = new Set(options.jsonLdPaths ?? []);\n\n lines.push('// This file is auto-generated by @analogjs/platform');\n lines.push('// Do not edit manually');\n lines.push('');\n lines.push('export interface AnalogGeneratedRouteRecord<');\n lines.push(' TId extends string = string,');\n lines.push(' TPath extends string = string,');\n lines.push(' TFullPath extends string = string,');\n lines.push(' TParentId extends string | null = string | null,');\n lines.push(' TChildren extends readonly string[] = readonly string[],');\n lines.push('> {');\n lines.push(' id: TId;');\n lines.push(' path: TPath;');\n lines.push(' fullPath: TFullPath;');\n lines.push(' parentId: TParentId;');\n lines.push(' children: TChildren;');\n lines.push(' sourceFile: string;');\n lines.push(\" kind: 'page' | 'content';\");\n lines.push(' hasParamsSchema: boolean;');\n lines.push(' hasQuerySchema: boolean;');\n lines.push(' hasJsonLd: boolean;');\n lines.push(' isIndex: boolean;');\n lines.push(' isGroup: boolean;');\n lines.push(' isCatchAll: boolean;');\n lines.push(' isOptionalCatchAll: boolean;');\n lines.push('}');\n lines.push('');\n lines.push('export interface AnalogFileRoutesById {');\n for (const route of manifest.routes) {\n lines.push(\n ` ${toTsKey(route.id)}: AnalogGeneratedRouteRecord<${toTsStringLiteral(route.id)}, ${toTsStringLiteral(route.path)}, ${toTsStringLiteral(route.fullPath)}, ${route.parentId ? toTsStringLiteral(route.parentId) : 'null'}, ${toReadonlyTupleType(route.children)}>;`,\n );\n }\n lines.push('}');\n lines.push('');\n lines.push('export interface AnalogFileRoutesByFullPath {');\n for (const route of manifest.routes) {\n lines.push(\n ` ${toTsKey(route.fullPath)}: AnalogFileRoutesById[${toTsStringLiteral(route.id)}];`,\n );\n }\n lines.push('}');\n lines.push('');\n lines.push('export type AnalogRouteTreeId = keyof AnalogFileRoutesById;');\n lines.push(\n 'export type AnalogRouteTreeFullPath = keyof AnalogFileRoutesByFullPath;',\n );\n lines.push('');\n lines.push('export const analogRouteTree = {');\n lines.push(' byId: {');\n for (const route of manifest.routes) {\n lines.push(` ${toObjectKey(route.id)}: {`);\n lines.push(` id: ${toTsStringLiteral(route.id)},`);\n lines.push(` path: ${toTsStringLiteral(route.path)},`);\n lines.push(` fullPath: ${toTsStringLiteral(route.fullPath)},`);\n lines.push(\n ` parentId: ${route.parentId ? toTsStringLiteral(route.parentId) : 'null'},`,\n );\n lines.push(` children: ${toReadonlyTupleValue(route.children)},`);\n lines.push(` sourceFile: ${toTsStringLiteral(route.filename)},`);\n lines.push(` kind: ${toTsStringLiteral(route.kind)},`);\n lines.push(\n ` hasParamsSchema: ${String(route.schemas.hasParamsSchema)},`,\n );\n lines.push(\n ` hasQuerySchema: ${String(route.schemas.hasQuerySchema)},`,\n );\n lines.push(` hasJsonLd: ${String(jsonLdPaths.has(route.fullPath))},`);\n lines.push(` isIndex: ${String(route.isIndex)},`);\n lines.push(` isGroup: ${String(route.isGroup)},`);\n lines.push(` isCatchAll: ${String(route.isCatchAll)},`);\n lines.push(\n ` isOptionalCatchAll: ${String(route.isOptionalCatchAll)},`,\n );\n lines.push(\n ` } satisfies AnalogFileRoutesById[${toTsStringLiteral(route.id)}],`,\n );\n }\n lines.push(' },');\n lines.push(' byFullPath: {');\n for (const route of manifest.routes) {\n lines.push(\n ` ${toObjectKey(route.fullPath)}: ${toTsStringLiteral(route.id)},`,\n );\n }\n lines.push(' },');\n lines.push('} as const;');\n lines.push('');\n\n return lines.join('\\n');\n}\n\nfunction filenameToImportPath(filename: string): string {\n const stripped = filename.replace(/^\\//, '').replace(/\\.ts$/, '');\n return '../' + stripped;\n}\n\nfunction generateParamsType(params: RouteParamInfo[]): string {\n if (params.length === 0) return 'Record<string, never>';\n\n const entries = params.map((p) => {\n const key = isValidIdentifier(p.name) ? p.name : `'${p.name}'`;\n switch (p.type) {\n case 'dynamic':\n return `${key}: string`;\n case 'catchAll':\n return `${key}: string[]`;\n case 'optionalCatchAll':\n return `${key}?: string[]`;\n }\n });\n\n return `{ ${entries.join('; ')} }`;\n}\n\nfunction isValidIdentifier(name: string): boolean {\n return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);\n}\n\nfunction findNearestParentRoute(\n fullPath: string,\n routesByFullPath: Map<string, RouteEntry>,\n): RouteEntry | undefined {\n if (fullPath === '/') {\n return undefined;\n }\n\n const segments = fullPath.slice(1).split('/');\n for (let index = segments.length - 1; index > 0; index--) {\n const candidate = '/' + segments.slice(0, index).join('/');\n const route = routesByFullPath.get(candidate);\n if (route) {\n return route;\n }\n }\n\n return undefined;\n}\n\nfunction computeLocalPath(\n fullPath: string,\n parentFullPath: string | null,\n): string {\n if (fullPath === '/') {\n return '/';\n }\n\n if (!parentFullPath) {\n return fullPath.slice(1);\n }\n\n const suffix = fullPath.slice(parentFullPath.length).replace(/^\\/+/, '');\n return suffix || '/';\n}\n\nfunction toTsStringLiteral(value: string): string {\n return JSON.stringify(value);\n}\n\nfunction toTsKey(value: string): string {\n return toTsStringLiteral(value);\n}\n\nfunction toObjectKey(value: string): string {\n return isValidIdentifier(value) ? value : toTsStringLiteral(value);\n}\n\nfunction toReadonlyTupleType(values: readonly string[]): string {\n if (values.length === 0) {\n return 'readonly []';\n }\n\n return `readonly [${values.map((value) => toTsStringLiteral(value)).join(', ')}]`;\n}\n\nfunction toReadonlyTupleValue(values: readonly string[]): string {\n if (values.length === 0) {\n return '[] as const';\n }\n\n return `[${values.map((value) => toTsStringLiteral(value)).join(', ')}] as const`;\n}\n\n// --- JSON-LD utilities ---\n\nexport type JsonLdObject = Record<string, unknown>;\n\nexport function isJsonLdObject(value: unknown): value is JsonLdObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport function normalizeJsonLd(value: unknown): JsonLdObject[] {\n if (Array.isArray(value)) {\n return value.filter(isJsonLdObject);\n }\n\n return isJsonLdObject(value) ? [value] : [];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiFA,SAAgB,oBAAoB,UAA0B;CAC5D,IAAI,OAAO,SAAS,QAClB,+KACA,GACD;CAED,MAAM,WAAqB,EAAE;AAC7B,QAAO,KAAK,QAAQ,6BAA6B,UAAU;AACzD,WAAS,KAAK,MAAM;AAEpB,SAAO,MAAM,SAAS,SAAS,EAAE;GACjC;AACF,QAAO,KAAK,QAAQ,OAAO,IAAI;AAE/B,QAAO,KAAK,QAAQ,gBAAgB,GAAG,QAAQ,SAAS,OAAO,IAAI,EAAE;CAErE,MAAM,WAAW,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ;CAChD,MAAM,YAAsB,EAAE;AAE9B,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,iBAAiB,KAAK,QAAQ,CAAE;AACpC,YAAU,KAAK,QAAQ;;AAGzB,KAAI,UAAU,SAAS,KAAK,UAAU,UAAU,SAAS,OAAO,QAC9D,WAAU,KAAK;AAGjB,QAAO,MAAM,UAAU,KAAK,IAAI;;;;;;;;;;;;AAalC,SAAgB,kBAAkB,UAA0B;CAC1D,IAAI,OAAO,SAAS,QAClB,+KACA,GACD;CAED,MAAM,WAAqB,EAAE;AAC7B,QAAO,KAAK,QAAQ,6BAA6B,UAAU;AACzD,WAAS,KAAK,MAAM;AAEpB,SAAO,MAAM,SAAS,SAAS,EAAE;GACjC;AACF,QAAO,KAAK,QAAQ,OAAO,IAAI;AAE/B,QAAO,KAAK,QAAQ,gBAAgB,GAAG,QAAQ,SAAS,OAAO,IAAI,EAAE;AAIrE,QAAO,MAFU,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,CAE1B,KAAK,IAAI;;;;;AAMjC,SAAgB,mBAAmB,WAAqC;CACtE,MAAM,SAA2B,EAAE;AAEnC,MAAK,MAAM,SAAS,UAAU,SAAS,0BAA0B,CAC/D,QAAO,KAAK;EAAE,MAAM,MAAM;EAAI,MAAM;EAAoB,CAAC;AAE3D,MAAK,MAAM,SAAS,UAAU,SAAS,mCAAmC,CACxE,QAAO,KAAK;EAAE,MAAM,MAAM;EAAI,MAAM;EAAY,CAAC;AAEnD,MAAK,MAAM,SAAS,UAAU,SAAS,mCAAmC,CACxE,QAAO,KAAK;EAAE,MAAM,MAAM;EAAI,MAAM;EAAW,CAAC;AAGlD,QAAO;;;;;AAMT,SAAgB,oBAAoB,aAAsC;AACxE,QAAO;EACL,iBAAiB,uCAAuC,KAAK,YAAY;EACzE,gBAAgB,sCAAsC,KAAK,YAAY;EACxE;;AAGH,IAAM,aAA8B;CAClC,iBAAiB;CACjB,gBAAgB;CACjB;;;;;;;;AASD,SAAgB,sBACd,WACA,gBACA,mBACe;CACf,MAAM,SAAuB,EAAE;CAC/B,MAAM,iCAAiB,IAAI,KAAqB;CAChD,MAAM,cAAc,qBAAqB;CAKzC,MAAM,uBAAuB,CAAC,GAAG,UAAU,CAAC,MAAM,GAAG,MAAM;EACzD,MAAM,YAAY,YAAY,EAAE;EAChC,MAAM,YAAY,YAAY,EAAE;AAChC,MAAI,cAAc,UAChB,QAAO,YAAY;AAErB,SAAO,EAAE,cAAc,EAAE;GACzB;AAEF,MAAK,MAAM,YAAY,sBAAsB;EAC3C,MAAM,WAAW,oBAAoB,SAAS;EAC9C,MAAM,SAAS,mBAAmB,SAAS;EAC3C,MAAM,UAAU,iBAAiB,eAAe,SAAS,GAAG;EAC5D,MAAM,KAAK,kBAAkB,SAAS;AAEtC,MAAI,eAAe,IAAI,SAAS,EAAE;GAChC,MAAM,kBAAkB,eAAe,IAAI,SAAS;AACpD,WAAQ,KACN,8BAA8B,SAAS,wBACjC,gBAAgB,SAAS,SAAS,cAC1B,gBAAgB,4DAC/B;AACD;;AAEF,iBAAe,IAAI,UAAU,SAAS;AAEtC,SAAO,KAAK;GACV;GACA,MAAM;GACN;GACA;GACA;GACA;GACA,MAAM,SAAS,SAAS,MAAM,GAAG,YAAY;GAC7C,UAAU;GACV,UAAU,EAAE;GACZ,SAAS,OAAO,YAAY,GAAG,SAAS,SAAS;GACjD,SAAS,GAAG,SAAS,KAAK,IAAI,SAAS,KAAK,GAAG;GAC/C,YAAY,OAAO,MAAM,UAAU,MAAM,SAAS,WAAW;GAC7D,oBAAoB,OAAO,MACxB,UAAU,MAAM,SAAS,mBAC3B;GACF,CAAC;;AAGJ,QAAO,MAAM,GAAG,MAAM;EACpB,MAAM,KAAK,eAAe,EAAE,SAAS;EACrC,MAAM,KAAK,eAAe,EAAE,SAAS;AACrC,MAAI,OAAO,GAAI,QAAO,KAAK;AAC3B,SAAO,EAAE,SAAS,cAAc,EAAE,SAAS;GAC3C;CAEF,MAAM,kBAAkB,IAAI,IAC1B,OAAO,KAAK,UAAU,CAAC,MAAM,UAAU,MAAM,CAAC,CAC/C;AAED,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,SAAS,uBAAuB,MAAM,UAAU,gBAAgB;AACtE,QAAM,WAAW,QAAQ,MAAM;AAC/B,QAAM,OAAO,iBAAiB,MAAM,UAAU,QAAQ,YAAY,KAAK;;CAGzE,MAAM,YAAY,IAAI,IAAI,OAAO,KAAK,UAAU,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC;AACnE,MAAK,MAAM,SAAS,OAClB,KAAI,MAAM,SACR,WAAU,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,MAAM,GAAG;AAI1D,MAAK,MAAM,SAAS,OAClB,KAAI,MAAM,QAAQ,mBAAmB,MAAM,OAAO,WAAW,EAC3D,SAAQ,KACN,mBAAmB,MAAM,SAAS,wEAEnC;AAOL,MAAK,MAAM,SAAS,QAAQ;AAC1B,MAAI,MAAM,YAAY,CAAC,UAAU,IAAI,MAAM,SAAS,CAClD,SAAQ,KACN,mBAAmB,MAAM,GAAG,kBAAkB,MAAM,SAAS,sDAE9D;AAEH,OAAK,MAAM,WAAW,MAAM,SAC1B,KAAI,CAAC,UAAU,IAAI,QAAQ,CACzB,SAAQ,KACN,mBAAmB,MAAM,GAAG,iBAAiB,QAAQ,sDAEtD;;AAKP,QAAO,EAAE,QAAQ;;AAGnB,SAAS,eAAe,MAAsB;AAC5C,KAAI,KAAK,SAAS,QAAQ,CAAE,QAAO;AACnC,KAAI,KAAK,SAAS,OAAO,CAAE,QAAO;AAClC,KAAI,KAAK,SAAS,IAAI,CAAE,QAAO;AAC/B,QAAO;;AAGT,SAAS,qBAAqB,UAA0B;AACtD,KACE,SAAS,SAAS,kBAAkB,IACpC,SAAS,SAAS,mBAAmB,IACrC,SAAS,SAAS,cAAc,IAChC,SAAS,SAAS,eAAe,IACjC,SAAS,SAAS,gBAAgB,CAElC,QAAO;AAGT,QAAO;;;;;AAMT,SAAgB,sBAAsB,UAAiC;CACrE,MAAM,QAAkB,EAAE;CAC1B,MAAM,QAAQ,SAAS,OAAO;CAC9B,MAAM,cAAc,SAAS,OAAO,QACjC,MAAM,EAAE,QAAQ,mBAAmB,EAAE,QAAQ,eAC/C,CAAC;CACF,MAAM,cAAc,SAAS,OAAO,QACjC,MAAM,EAAE,OAAO,WAAW,EAC5B,CAAC;CACF,MAAM,eAAe,QAAQ;AAE7B,OAAM,KAAK,mCAAmC;AAC9C,OAAM,KACJ,KAAK,MAAM,WAAW,YAAY,WAAW,aAAa,WAC3D;AACD,KAAI,cAAc,EAChB,OAAM,KAAK,KAAK,YAAY,yBAAyB;AAGvD,MAAK,MAAM,SAAS,SAAS,QAAQ;EACnC,MAAM,QAAkB,EAAE;AAC1B,MAAI,MAAM,QAAQ,gBAAiB,OAAM,KAAK,gBAAgB;AAC9D,MAAI,MAAM,QAAQ,eAAgB,OAAM,KAAK,eAAe;EAC5D,MAAM,SAAS,MAAM,SAAS,IAAI,KAAK,MAAM,KAAK,KAAK,CAAC,KAAK;AAC7D,QAAM,KAAK,KAAK,MAAM,WAAW,SAAS;;AAG5C,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAgB,8BAA8B,UAAiC;CAC7E,MAAM,QAAkB,EAAE;CAC1B,MAAM,eAAe,SAAS,OAAO,MAClC,MAAM,EAAE,QAAQ,mBAAmB,EAAE,QAAQ,eAC/C;AAED,OAAM,KAAK,uDAAuD;AAClE,OAAM,KAAK,0BAA0B;AACrC,OAAM,KAAK,GAAG;AAEd,KAAI,aACF,OAAM,KACJ,iEACD;CAGH,MAAM,gCAAgB,IAAI,KAAqB;CAC/C,IAAI,aAAa;AAEjB,MAAK,MAAM,SAAS,SAAS,OAC3B,KAAI,MAAM,QAAQ,mBAAmB,MAAM,QAAQ,gBAAgB;EACjE,MAAM,aAAa,qBAAqB,MAAM,SAAS;EACvD,MAAM,QAAkB,EAAE;AAE1B,MAAI,MAAM,QAAQ,iBAAiB;GACjC,MAAM,QAAQ,KAAK;AACnB,SAAM,KAAK,wBAAwB,QAAQ;AAC3C,iBAAc,IAAI,GAAG,MAAM,SAAS,UAAU,MAAM;;AAEtD,MAAI,MAAM,QAAQ,gBAAgB;GAChC,MAAM,QAAQ,KAAK;AACnB,SAAM,KAAK,uBAAuB,QAAQ;AAC1C,iBAAc,IAAI,GAAG,MAAM,SAAS,SAAS,MAAM;;AAGrD,QAAM,KAAK,iBAAiB,MAAM,KAAK,KAAK,CAAC,WAAW,WAAW,IAAI;AACvE;;AAIJ,KAAI,aACF,OAAM,KAAK,GAAG;AAGhB,OAAM,KAAK,sCAAsC;AACjD,OAAM,KAAK,iCAAiC;AAE5C,MAAK,MAAM,SAAS,SAAS,QAAQ;EACnC,MAAM,cAAc,cAAc,IAAI,GAAG,MAAM,SAAS,SAAS;EACjE,MAAM,aAAa,cAAc,IAAI,GAAG,MAAM,SAAS,QAAQ;EAE/D,MAAM,aAAa,mBAAmB,MAAM,OAAO;EACnD,MAAM,YAAY;EAElB,MAAM,mBAAmB,cACrB,uCAAuC,YAAY,KACnD;EACJ,MAAM,kBAAkB,aACpB,uCAAuC,WAAW,KAClD;AAEJ,QAAM,KAAK,QAAQ,MAAM,SAAS,MAAM;AACxC,QAAM,KAAK,iBAAiB,WAAW,GAAG;AAC1C,QAAM,KAAK,uBAAuB,iBAAiB,GAAG;AACtD,QAAM,KAAK,gBAAgB,UAAU,GAAG;AACxC,QAAM,KAAK,sBAAsB,gBAAgB,GAAG;AACpD,QAAM,KAAK,SAAS;;AAGtB,OAAM,KAAK,MAAM;AACjB,OAAM,KAAK,IAAI;AACf,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,aAAa;AACxB,OAAM,KAAK,GAAG;AAEd,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAgB,6BACd,UACA,UAA+C,EAAE,EACzC;CACR,MAAM,QAAkB,EAAE;CAC1B,MAAM,cAAc,IAAI,IAAI,QAAQ,eAAe,EAAE,CAAC;AAEtD,OAAM,KAAK,uDAAuD;AAClE,OAAM,KAAK,0BAA0B;AACrC,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,+CAA+C;AAC1D,OAAM,KAAK,iCAAiC;AAC5C,OAAM,KAAK,mCAAmC;AAC9C,OAAM,KAAK,uCAAuC;AAClD,OAAM,KAAK,qDAAqD;AAChE,OAAM,KAAK,6DAA6D;AACxE,OAAM,KAAK,MAAM;AACjB,OAAM,KAAK,aAAa;AACxB,OAAM,KAAK,iBAAiB;AAC5B,OAAM,KAAK,yBAAyB;AACpC,OAAM,KAAK,yBAAyB;AACpC,OAAM,KAAK,yBAAyB;AACpC,OAAM,KAAK,wBAAwB;AACnC,OAAM,KAAK,8BAA8B;AACzC,OAAM,KAAK,8BAA8B;AACzC,OAAM,KAAK,6BAA6B;AACxC,OAAM,KAAK,wBAAwB;AACnC,OAAM,KAAK,sBAAsB;AACjC,OAAM,KAAK,sBAAsB;AACjC,OAAM,KAAK,yBAAyB;AACpC,OAAM,KAAK,iCAAiC;AAC5C,OAAM,KAAK,IAAI;AACf,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,0CAA0C;AACrD,MAAK,MAAM,SAAS,SAAS,OAC3B,OAAM,KACJ,KAAK,QAAQ,MAAM,GAAG,CAAC,+BAA+B,kBAAkB,MAAM,GAAG,CAAC,IAAI,kBAAkB,MAAM,KAAK,CAAC,IAAI,kBAAkB,MAAM,SAAS,CAAC,IAAI,MAAM,WAAW,kBAAkB,MAAM,SAAS,GAAG,OAAO,IAAI,oBAAoB,MAAM,SAAS,CAAC,IACnQ;AAEH,OAAM,KAAK,IAAI;AACf,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,gDAAgD;AAC3D,MAAK,MAAM,SAAS,SAAS,OAC3B,OAAM,KACJ,KAAK,QAAQ,MAAM,SAAS,CAAC,yBAAyB,kBAAkB,MAAM,GAAG,CAAC,IACnF;AAEH,OAAM,KAAK,IAAI;AACf,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,8DAA8D;AACzE,OAAM,KACJ,0EACD;AACD,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,mCAAmC;AAC9C,OAAM,KAAK,YAAY;AACvB,MAAK,MAAM,SAAS,SAAS,QAAQ;AACnC,QAAM,KAAK,OAAO,YAAY,MAAM,GAAG,CAAC,KAAK;AAC7C,QAAM,KAAK,aAAa,kBAAkB,MAAM,GAAG,CAAC,GAAG;AACvD,QAAM,KAAK,eAAe,kBAAkB,MAAM,KAAK,CAAC,GAAG;AAC3D,QAAM,KAAK,mBAAmB,kBAAkB,MAAM,SAAS,CAAC,GAAG;AACnE,QAAM,KACJ,mBAAmB,MAAM,WAAW,kBAAkB,MAAM,SAAS,GAAG,OAAO,GAChF;AACD,QAAM,KAAK,mBAAmB,qBAAqB,MAAM,SAAS,CAAC,GAAG;AACtE,QAAM,KAAK,qBAAqB,kBAAkB,MAAM,SAAS,CAAC,GAAG;AACrE,QAAM,KAAK,eAAe,kBAAkB,MAAM,KAAK,CAAC,GAAG;AAC3D,QAAM,KACJ,0BAA0B,OAAO,MAAM,QAAQ,gBAAgB,CAAC,GACjE;AACD,QAAM,KACJ,yBAAyB,OAAO,MAAM,QAAQ,eAAe,CAAC,GAC/D;AACD,QAAM,KAAK,oBAAoB,OAAO,YAAY,IAAI,MAAM,SAAS,CAAC,CAAC,GAAG;AAC1E,QAAM,KAAK,kBAAkB,OAAO,MAAM,QAAQ,CAAC,GAAG;AACtD,QAAM,KAAK,kBAAkB,OAAO,MAAM,QAAQ,CAAC,GAAG;AACtD,QAAM,KAAK,qBAAqB,OAAO,MAAM,WAAW,CAAC,GAAG;AAC5D,QAAM,KACJ,6BAA6B,OAAO,MAAM,mBAAmB,CAAC,GAC/D;AACD,QAAM,KACJ,wCAAwC,kBAAkB,MAAM,GAAG,CAAC,IACrE;;AAEH,OAAM,KAAK,OAAO;AAClB,OAAM,KAAK,kBAAkB;AAC7B,MAAK,MAAM,SAAS,SAAS,OAC3B,OAAM,KACJ,OAAO,YAAY,MAAM,SAAS,CAAC,IAAI,kBAAkB,MAAM,GAAG,CAAC,GACpE;AAEH,OAAM,KAAK,OAAO;AAClB,OAAM,KAAK,cAAc;AACzB,OAAM,KAAK,GAAG;AAEd,QAAO,MAAM,KAAK,KAAK;;AAGzB,SAAS,qBAAqB,UAA0B;AAEtD,QAAO,QADU,SAAS,QAAQ,OAAO,GAAG,CAAC,QAAQ,SAAS,GAAG;;AAInE,SAAS,mBAAmB,QAAkC;AAC5D,KAAI,OAAO,WAAW,EAAG,QAAO;AAchC,QAAO,KAZS,OAAO,KAAK,MAAM;EAChC,MAAM,MAAM,kBAAkB,EAAE,KAAK,GAAG,EAAE,OAAO,IAAI,EAAE,KAAK;AAC5D,UAAQ,EAAE,MAAV;GACE,KAAK,UACH,QAAO,GAAG,IAAI;GAChB,KAAK,WACH,QAAO,GAAG,IAAI;GAChB,KAAK,mBACH,QAAO,GAAG,IAAI;;GAElB,CAEkB,KAAK,KAAK,CAAC;;AAGjC,SAAS,kBAAkB,MAAuB;AAChD,QAAO,6BAA6B,KAAK,KAAK;;AAGhD,SAAS,uBACP,UACA,kBACwB;AACxB,KAAI,aAAa,IACf;CAGF,MAAM,WAAW,SAAS,MAAM,EAAE,CAAC,MAAM,IAAI;AAC7C,MAAK,IAAI,QAAQ,SAAS,SAAS,GAAG,QAAQ,GAAG,SAAS;EACxD,MAAM,YAAY,MAAM,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,IAAI;EAC1D,MAAM,QAAQ,iBAAiB,IAAI,UAAU;AAC7C,MAAI,MACF,QAAO;;;AAOb,SAAS,iBACP,UACA,gBACQ;AACR,KAAI,aAAa,IACf,QAAO;AAGT,KAAI,CAAC,eACH,QAAO,SAAS,MAAM,EAAE;AAI1B,QADe,SAAS,MAAM,eAAe,OAAO,CAAC,QAAQ,QAAQ,GAAG,IACvD;;AAGnB,SAAS,kBAAkB,OAAuB;AAChD,QAAO,KAAK,UAAU,MAAM;;AAG9B,SAAS,QAAQ,OAAuB;AACtC,QAAO,kBAAkB,MAAM;;AAGjC,SAAS,YAAY,OAAuB;AAC1C,QAAO,kBAAkB,MAAM,GAAG,QAAQ,kBAAkB,MAAM;;AAGpE,SAAS,oBAAoB,QAAmC;AAC9D,KAAI,OAAO,WAAW,EACpB,QAAO;AAGT,QAAO,aAAa,OAAO,KAAK,UAAU,kBAAkB,MAAM,CAAC,CAAC,KAAK,KAAK,CAAC;;AAGjF,SAAS,qBAAqB,QAAmC;AAC/D,KAAI,OAAO,WAAW,EACpB,QAAO;AAGT,QAAO,IAAI,OAAO,KAAK,UAAU,kBAAkB,MAAM,CAAC,CAAC,KAAK,KAAK,CAAC;;AAOxE,SAAgB,eAAe,OAAuC;AACpE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM;;AAG7E,SAAgB,gBAAgB,OAAgC;AAC9D,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,MAAM,OAAO,eAAe;AAGrC,QAAO,eAAe,MAAM,GAAG,CAAC,MAAM,GAAG,EAAE"}
1
+ {"version":3,"file":"route-manifest.js","names":[],"sources":["../../../src/lib/route-manifest.ts"],"sourcesContent":["/**\n * Route-manifest engine for typed file routes.\n *\n * Pure functions (no Angular dependencies) for converting discovered\n * filenames into typed route manifests and generated declarations.\n */\n\nexport interface RouteParamInfo {\n name: string;\n type: 'dynamic' | 'catchAll' | 'optionalCatchAll';\n}\n\nexport interface RouteSchemaInfo {\n hasParamsSchema: boolean;\n hasQuerySchema: boolean;\n}\n\nexport interface GenerateRouteTreeDeclarationOptions {\n jsonLdFiles?: Iterable<string>;\n}\n\nexport interface RouteEntry {\n /** Stable structural route id derived from the source filename */\n id: string;\n /** The route path segment relative to the nearest existing parent route */\n path: string;\n /** The fully resolved navigation path pattern (e.g., '/users/[id]') */\n fullPath: string;\n /** Extracted parameter information */\n params: RouteParamInfo[];\n /** Original filename that produced this route */\n filename: string;\n /** Schema export info (detected from file content) */\n schemas: RouteSchemaInfo;\n /** Type of source that produced this route */\n kind: 'page' | 'content';\n /** Parent route id, or null for top-level routes */\n parentId: string | null;\n /** Child route ids */\n children: string[];\n /** Whether the source filename represents an index route */\n isIndex: boolean;\n /** Whether the source filename includes route-group/pathless segments */\n isGroup: boolean;\n /** Whether the route contains a required catch-all parameter */\n isCatchAll: boolean;\n /** Whether the route contains an optional catch-all parameter */\n isOptionalCatchAll: boolean;\n}\n\nexport interface RouteCollision {\n fullPath: string;\n keptFile: string;\n droppedFile: string;\n /** True when both files have the same collision priority (hard error). */\n samePriority: boolean;\n}\n\nexport interface RouteManifest {\n routes: RouteEntry[];\n collisions: RouteCollision[];\n /** Canonical route per fullPath — precomputed once to avoid redundant work. */\n canonicalByFullPath: Map<string, RouteEntry>;\n}\n\n/**\n * Converts a discovered filename to a route path pattern.\n *\n * Uses the same stripping rules as the existing route system\n * but preserves bracket param syntax instead of converting to\n * Angular's `:param` syntax.\n *\n * The regex applies four alternations (left to right, all replaced with ''):\n * 1. `^(.*?)[\\\\/](?:routes|pages|content)[\\\\/]` — anchored, strips everything\n * up to and including the first /routes/, /pages/, or /content/ segment.\n * Handles app-local paths (`/src/app/pages/`) AND additional dirs\n * (`/libs/shared/feature/src/content/`) uniformly.\n * 2. `[\\\\/](?:app[\\\\/](?:routes|pages)|src[\\\\/]content)[\\\\/]` — non-anchored\n * fallback for legacy paths where the directory marker appears mid-string.\n * 3. `\\.page\\.(js|ts|analog|ag)$` — strips page file extensions.\n * 4. `\\.(ts|md|analog|ag)$` — strips remaining file extensions.\n *\n * Examples:\n * - '/app/routes/index.ts' -> '/'\n * - '/app/routes/about.ts' -> '/about'\n * - '/src/app/pages/users/[id].page.ts' -> '/users/[id]'\n * - '/app/routes/blog.[slug].ts' -> '/blog/[slug]'\n * - '/src/app/pages/(auth)/login.page.ts' -> '/login'\n * - '/src/app/pages/docs/[...slug].page.ts' -> '/docs/[...slug]'\n * - '/src/app/pages/shop/[[...category]].page.ts' -> '/shop/[[...category]]'\n * - '/libs/shared/feature/src/content/test.md' -> '/test'\n */\nexport function filenameToRoutePath(filename: string): string {\n let path = filename.replace(\n /^(?:[a-zA-Z]:[\\\\/])?(.*?)[\\\\/](?:routes|pages|content)[\\\\/]|(?:[\\\\/](?:app[\\\\/](?:routes|pages)|src[\\\\/]content)[\\\\/])|(\\.page\\.(js|ts|analog|ag)$)|(\\.(ts|md|analog|ag)$)/g,\n '',\n );\n\n const brackets: string[] = [];\n path = path.replace(/\\[\\[?\\.{0,3}[^\\]]*\\]?\\]/g, (match) => {\n brackets.push(match);\n // eslint-disable-next-line no-control-regex\n return `\\0B${brackets.length - 1}\\0`;\n });\n path = path.replace(/\\./g, '/');\n // eslint-disable-next-line no-control-regex\n path = path.replace(/\\0B(\\d+)\\0/g, (_, idx) => brackets[Number(idx)]);\n\n const segments = path.split('/').filter(Boolean);\n const processed: string[] = [];\n\n for (const segment of segments) {\n if (/^\\([^.[\\]]*\\)$/.test(segment)) continue;\n processed.push(segment);\n }\n\n if (processed.length > 0 && processed[processed.length - 1] === 'index') {\n processed.pop();\n }\n\n return '/' + processed.join('/');\n}\n\n/**\n * Converts a discovered filename to a stable structural route id.\n *\n * Unlike `filenameToRoutePath`, this preserves route groups and `index`\n * segments so that multiple files resolving to the same URL shape can still\n * have distinct structural identities in the generated route tree metadata.\n *\n * Uses the same directory-stripping regex as `filenameToRoutePath` —\n * changes to the regex must be kept in sync between both functions.\n */\nexport function filenameToRouteId(filename: string): string {\n let path = filename.replace(\n /^(?:[a-zA-Z]:[\\\\/])?(.*?)[\\\\/](?:routes|pages|content)[\\\\/]|(?:[\\\\/](?:app[\\\\/](?:routes|pages)|src[\\\\/]content)[\\\\/])|(\\.page\\.(js|ts|analog|ag)$)|(\\.(ts|md|analog|ag)$)/g,\n '',\n );\n\n const brackets: string[] = [];\n path = path.replace(/\\[\\[?\\.{0,3}[^\\]]*\\]?\\]/g, (match) => {\n brackets.push(match);\n // eslint-disable-next-line no-control-regex\n return `\\0B${brackets.length - 1}\\0`;\n });\n path = path.replace(/\\./g, '/');\n // eslint-disable-next-line no-control-regex\n path = path.replace(/\\0B(\\d+)\\0/g, (_, idx) => brackets[Number(idx)]);\n\n const segments = path.split('/').filter(Boolean);\n\n return '/' + segments.join('/');\n}\n\n/**\n * Extracts parameter information from a route path pattern.\n */\nexport function extractRouteParams(routePath: string): RouteParamInfo[] {\n const params: RouteParamInfo[] = [];\n\n for (const match of routePath.matchAll(/\\[\\[\\.\\.\\.([^\\]]+)\\]\\]/g)) {\n params.push({ name: match[1], type: 'optionalCatchAll' });\n }\n for (const match of routePath.matchAll(/(?<!\\[)\\[\\.\\.\\.([^\\]]+)\\](?!\\])/g)) {\n params.push({ name: match[1], type: 'catchAll' });\n }\n for (const match of routePath.matchAll(/(?<!\\[)\\[(?!\\.)([^\\]]+)\\](?!\\])/g)) {\n params.push({ name: match[1], type: 'dynamic' });\n }\n\n return params;\n}\n\n/**\n * Detects whether a route file exports schema constants.\n */\nexport function detectSchemaExports(fileContent: string): RouteSchemaInfo {\n return {\n hasParamsSchema: /export\\s+const\\s+routeParamsSchema\\b/.test(fileContent),\n hasQuerySchema: /export\\s+const\\s+routeQuerySchema\\b/.test(fileContent),\n };\n}\n\nconst NO_SCHEMAS: RouteSchemaInfo = {\n hasParamsSchema: false,\n hasQuerySchema: false,\n};\n\n/**\n * Generates a route manifest from a list of discovered filenames.\n *\n * @param collisionPriority - Optional callback that returns a numeric priority\n * for each filename (lower wins). When provided, this replaces the default\n * hard-coded path-substring heuristic with config-derived precedence.\n */\nexport function generateRouteManifest(\n filenames: string[],\n schemaDetector?: (filename: string) => RouteSchemaInfo,\n collisionPriority?: (filename: string) => number,\n): RouteManifest {\n const routes: RouteEntry[] = [];\n const collisions: RouteCollision[] = [];\n const seenByFullPath = new Map<\n string,\n { filename: string; priority: number }\n >();\n const getPriority = collisionPriority ?? getCollisionPriority;\n\n // Prefer app-local route files over shared/external sources when two files\n // resolve to the same URL. This keeps `additionalPagesDirs` additive instead\n // of unexpectedly overriding the route that lives inside the app itself.\n const prioritizedFilenames = [...filenames].sort((a, b) => {\n const aPriority = getPriority(a);\n const bPriority = getPriority(b);\n if (aPriority !== bPriority) {\n return aPriority - bPriority;\n }\n return a.localeCompare(b);\n });\n\n for (const filename of prioritizedFilenames) {\n const fullPath = filenameToRoutePath(filename);\n const params = extractRouteParams(fullPath);\n const schemas = schemaDetector ? schemaDetector(filename) : NO_SCHEMAS;\n const id = filenameToRouteId(filename);\n const isPathlessLayout = isPathlessLayoutId(id);\n\n const currentPriority = getPriority(filename);\n\n // Pathless layouts (e.g. (auth).page.ts) are structural wrappers that\n // render a <router-outlet> — they coexist with index.page.ts at the same\n // fullPath without collision. The Angular router handles them as nested\n // layout routes, not competing page components.\n if (!isPathlessLayout) {\n if (seenByFullPath.has(fullPath)) {\n const winner = seenByFullPath.get(fullPath)!;\n if (winner.filename === filename) {\n continue;\n }\n // A layout file (e.g., docs.page.ts) and its index child\n // (e.g., docs/index.page.ts) intentionally share the same route\n // path — the layout wraps the index as a parent-child pair.\n const isLayoutIndexPair = (a: string, b: string) => {\n const indexRe = /\\/index\\.(page\\.)?(ts|js|md|analog|ag)$/;\n const layoutRe = /\\.(page\\.)?(ts|js|analog|ag)$/;\n if (indexRe.test(a) && layoutRe.test(b)) {\n const dir = a.replace(indexRe, '');\n const layout = b.replace(layoutRe, '');\n return dir === layout;\n }\n return false;\n };\n if (\n isLayoutIndexPair(winner.filename, filename) ||\n isLayoutIndexPair(filename, winner.filename)\n ) {\n continue;\n }\n collisions.push({\n fullPath,\n keptFile: winner.filename,\n droppedFile: filename,\n samePriority: winner.priority === currentPriority,\n });\n console.warn(\n `[Analog] Route collision: '${fullPath}' is defined by both ` +\n `'${winner.filename}' and '${filename}'. ` +\n `Keeping '${winner.filename}' based on route source precedence and skipping duplicate.`,\n );\n continue;\n }\n seenByFullPath.set(fullPath, { filename, priority: currentPriority });\n }\n\n routes.push({\n id,\n path: fullPath,\n fullPath,\n params,\n filename,\n schemas,\n kind: filename.endsWith('.md') ? 'content' : 'page',\n parentId: null,\n children: [],\n isIndex: id === '/index' || id.endsWith('/index'),\n isGroup: isPathlessLayout,\n isCatchAll: params.some((param) => param.type === 'catchAll'),\n isOptionalCatchAll: params.some(\n (param) => param.type === 'optionalCatchAll',\n ),\n });\n }\n\n routes.sort((a, b) => {\n const aW = getRouteWeight(a.fullPath);\n const bW = getRouteWeight(b.fullPath);\n if (aW !== bW) return aW - bW;\n return a.fullPath.localeCompare(b.fullPath);\n });\n\n const routeByFullPath = canonicalRoutesByFullPath(routes);\n\n const routeById = new Map(routes.map((route) => [route.id, route]));\n\n for (const route of routes) {\n // Use structural id-based parent lookup for any route whose id\n // contains a group segment — this wires group children (e.g.\n // /(auth)/sign-up) to their pathless layout parent (/(auth)).\n // This also correctly handles nested groups like\n // /dashboard/(settings)/profile: findNearestParentById walks up\n // id segments and finds /(settings) if it exists, otherwise falls\n // through to fullPathParent which resolves to /dashboard.\n // Non-group routes always use the canonical fullPath-based lookup.\n const hasGroupSegment = route.id.includes('/(');\n const structuralParent = hasGroupSegment\n ? findNearestParentById(route.id, routeById)\n : undefined;\n const fullPathParent = findNearestParentRoute(\n route.fullPath,\n routeByFullPath,\n );\n const parent = structuralParent ?? fullPathParent;\n route.parentId = parent?.id ?? null;\n route.path = computeLocalPath(route.fullPath, parent?.fullPath ?? null);\n }\n\n for (const route of routes) {\n if (route.parentId) {\n routeById.get(route.parentId)?.children.push(route.id);\n }\n }\n\n for (const route of routes) {\n if (route.schemas.hasParamsSchema && route.params.length === 0) {\n console.warn(\n `[Analog] Route '${route.fullPath}' exports routeParamsSchema` +\n ` but has no dynamic params in the filename.`,\n );\n }\n }\n\n // Build-time consistency check: every parentId and child reference must\n // point to a real route in the manifest. Invalid references indicate a\n // bug in the hierarchy computation.\n for (const route of routes) {\n if (route.parentId && !routeById.has(route.parentId)) {\n console.warn(\n `[Analog] Route '${route.id}' has parentId '${route.parentId}' ` +\n `which does not match any route id in the manifest.`,\n );\n }\n for (const childId of route.children) {\n if (!routeById.has(childId)) {\n console.warn(\n `[Analog] Route '${route.id}' lists child '${childId}' ` +\n `which does not match any route id in the manifest.`,\n );\n }\n }\n }\n\n return { routes, collisions, canonicalByFullPath: routeByFullPath };\n}\n\nfunction canonicalRoutesByFullPath(\n routes: RouteEntry[],\n): Map<string, RouteEntry> {\n const map = new Map<string, RouteEntry>();\n for (const route of routes) {\n const existing = map.get(route.fullPath);\n if (!existing) {\n map.set(route.fullPath, route);\n } else if (existing.isGroup && !route.isGroup) {\n // Non-group routes always take precedence over group layouts.\n map.set(route.fullPath, route);\n } else if (existing.isGroup && route.isGroup) {\n // Both are group layouts — tiebreak by id to ensure stable selection\n // regardless of filesystem or glob ordering across platforms.\n if (route.id.localeCompare(existing.id) < 0) {\n map.set(route.fullPath, route);\n }\n }\n }\n return map;\n}\n\n// Matches group names like (auth), (home) — intentionally excludes dots and\n// brackets so names like (auth.v2) or ([id]) are NOT treated as pathless\n// layouts. Dot-containing names collide with dynamic-segment syntax.\nfunction isPathlessLayoutId(id: string): boolean {\n const segments = id.split('/').filter(Boolean);\n if (segments.length === 0) return false;\n return /^\\([^.[\\]]+\\)$/.test(segments[segments.length - 1]);\n}\n\nfunction getRouteWeight(path: string): number {\n if (path.includes('[[...')) return 3;\n if (path.includes('[...')) return 2;\n if (path.includes('[')) return 1;\n return 0;\n}\n\nfunction getCollisionPriority(filename: string): number {\n if (\n filename.includes('/src/app/pages/') ||\n filename.includes('/src/app/routes/') ||\n filename.includes('/app/pages/') ||\n filename.includes('/app/routes/') ||\n filename.includes('/src/content/')\n ) {\n return 0;\n }\n\n return 1;\n}\n\n/**\n * Produces a human-readable summary of the generated route manifest.\n */\nexport function formatManifestSummary(manifest: RouteManifest): string {\n const lines: string[] = [];\n const total = manifest.routes.length;\n const withSchemas = manifest.routes.filter(\n (r) => r.schemas.hasParamsSchema || r.schemas.hasQuerySchema,\n ).length;\n const staticCount = manifest.routes.filter(\n (r) => r.params.length === 0,\n ).length;\n const dynamicCount = total - staticCount;\n\n lines.push(`[Analog] Generated typed routes:`);\n lines.push(\n ` ${total} routes (${staticCount} static, ${dynamicCount} dynamic)`,\n );\n if (withSchemas > 0) {\n lines.push(` ${withSchemas} with schema validation`);\n }\n\n for (const route of manifest.routes) {\n const flags: string[] = [];\n if (route.schemas.hasParamsSchema) flags.push('params-schema');\n if (route.schemas.hasQuerySchema) flags.push('query-schema');\n const suffix = flags.length > 0 ? ` [${flags.join(', ')}]` : '';\n lines.push(` ${route.fullPath}${suffix}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Generates the route-table section for the combined generated route module.\n */\nexport function generateRouteTableDeclaration(manifest: RouteManifest): string {\n const lines: string[] = [];\n const hasAnySchema = manifest.routes.some(\n (r) => r.schemas.hasParamsSchema || r.schemas.hasQuerySchema,\n );\n\n lines.push('// This file is auto-generated by @analogjs/platform');\n lines.push('// Do not edit manually');\n lines.push('');\n\n if (hasAnySchema) {\n lines.push(\n \"import type { StandardSchemaV1 } from '@standard-schema/spec';\",\n );\n }\n\n const schemaImports = new Map<string, string>();\n let aliasIndex = 0;\n\n for (const route of manifest.routes) {\n if (route.schemas.hasParamsSchema || route.schemas.hasQuerySchema) {\n const importPath = filenameToImportPath(route.filename);\n const names: string[] = [];\n\n if (route.schemas.hasParamsSchema) {\n const alias = `_p${aliasIndex}`;\n names.push(`routeParamsSchema as ${alias}`);\n schemaImports.set(`${route.fullPath}:params`, alias);\n }\n if (route.schemas.hasQuerySchema) {\n const alias = `_q${aliasIndex}`;\n names.push(`routeQuerySchema as ${alias}`);\n schemaImports.set(`${route.fullPath}:query`, alias);\n }\n\n lines.push(`import type { ${names.join(', ')} } from '${importPath}';`);\n aliasIndex++;\n }\n }\n\n if (hasAnySchema) {\n lines.push('');\n }\n\n lines.push(\"declare module '@analogjs/router' {\");\n lines.push(' interface AnalogRouteTable {');\n\n for (const route of manifest.canonicalByFullPath.values()) {\n const paramsAlias = schemaImports.get(`${route.fullPath}:params`);\n const queryAlias = schemaImports.get(`${route.fullPath}:query`);\n\n const paramsType = generateParamsType(route.params);\n const queryType = 'Record<string, string | string[] | undefined>';\n\n const paramsOutputType = paramsAlias\n ? `StandardSchemaV1.InferOutput<typeof ${paramsAlias}>`\n : paramsType;\n const queryOutputType = queryAlias\n ? `StandardSchemaV1.InferOutput<typeof ${queryAlias}>`\n : queryType;\n\n lines.push(` '${route.fullPath}': {`);\n lines.push(` params: ${paramsType};`);\n lines.push(` paramsOutput: ${paramsOutputType};`);\n lines.push(` query: ${queryType};`);\n lines.push(` queryOutput: ${queryOutputType};`);\n lines.push(` };`);\n }\n\n lines.push(' }');\n lines.push('}');\n lines.push('');\n lines.push('export {};');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n/**\n * Generates the route-tree section for the combined generated route module.\n */\nexport function generateRouteTreeDeclaration(\n manifest: RouteManifest,\n options: GenerateRouteTreeDeclarationOptions = {},\n): string {\n const lines: string[] = [];\n const jsonLdFiles = new Set(options.jsonLdFiles ?? []);\n\n lines.push('// This file is auto-generated by @analogjs/platform');\n lines.push('// Do not edit manually');\n lines.push('');\n lines.push('export interface AnalogGeneratedRouteRecord<');\n lines.push(' TId extends string = string,');\n lines.push(' TPath extends string = string,');\n lines.push(' TFullPath extends string = string,');\n lines.push(' TParentId extends string | null = string | null,');\n lines.push(' TChildren extends readonly string[] = readonly string[],');\n lines.push('> {');\n lines.push(' id: TId;');\n lines.push(' path: TPath;');\n lines.push(' fullPath: TFullPath;');\n lines.push(' parentId: TParentId;');\n lines.push(' children: TChildren;');\n lines.push(' sourceFile: string;');\n lines.push(\" kind: 'page' | 'content';\");\n lines.push(' hasParamsSchema: boolean;');\n lines.push(' hasQuerySchema: boolean;');\n lines.push(' hasJsonLd: boolean;');\n lines.push(' isIndex: boolean;');\n lines.push(' isGroup: boolean;');\n lines.push(' isCatchAll: boolean;');\n lines.push(' isOptionalCatchAll: boolean;');\n lines.push('}');\n lines.push('');\n lines.push('export interface AnalogFileRoutesById {');\n for (const route of manifest.routes) {\n lines.push(\n ` ${toTsKey(route.id)}: AnalogGeneratedRouteRecord<${toTsStringLiteral(route.id)}, ${toTsStringLiteral(route.path)}, ${toTsStringLiteral(route.fullPath)}, ${route.parentId ? toTsStringLiteral(route.parentId) : 'null'}, ${toReadonlyTupleType(route.children)}>;`,\n );\n }\n lines.push('}');\n lines.push('');\n lines.push('export interface AnalogFileRoutesByFullPath {');\n for (const route of manifest.canonicalByFullPath.values()) {\n lines.push(\n ` ${toTsKey(route.fullPath)}: AnalogFileRoutesById[${toTsStringLiteral(route.id)}];`,\n );\n }\n lines.push('}');\n lines.push('');\n lines.push('export type AnalogRouteTreeId = keyof AnalogFileRoutesById;');\n lines.push(\n 'export type AnalogRouteTreeFullPath = keyof AnalogFileRoutesByFullPath;',\n );\n lines.push('');\n lines.push('export const analogRouteTree = {');\n lines.push(' byId: {');\n for (const route of manifest.routes) {\n lines.push(` ${toObjectKey(route.id)}: {`);\n lines.push(` id: ${toTsStringLiteral(route.id)},`);\n lines.push(` path: ${toTsStringLiteral(route.path)},`);\n lines.push(` fullPath: ${toTsStringLiteral(route.fullPath)},`);\n lines.push(\n ` parentId: ${route.parentId ? toTsStringLiteral(route.parentId) : 'null'},`,\n );\n lines.push(` children: ${toReadonlyTupleValue(route.children)},`);\n lines.push(` sourceFile: ${toTsStringLiteral(route.filename)},`);\n lines.push(` kind: ${toTsStringLiteral(route.kind)},`);\n lines.push(\n ` hasParamsSchema: ${String(route.schemas.hasParamsSchema)},`,\n );\n lines.push(\n ` hasQuerySchema: ${String(route.schemas.hasQuerySchema)},`,\n );\n lines.push(` hasJsonLd: ${String(jsonLdFiles.has(route.filename))},`);\n lines.push(` isIndex: ${String(route.isIndex)},`);\n lines.push(` isGroup: ${String(route.isGroup)},`);\n lines.push(` isCatchAll: ${String(route.isCatchAll)},`);\n lines.push(\n ` isOptionalCatchAll: ${String(route.isOptionalCatchAll)},`,\n );\n lines.push(\n ` } satisfies AnalogFileRoutesById[${toTsStringLiteral(route.id)}],`,\n );\n }\n lines.push(' },');\n lines.push(' byFullPath: {');\n for (const route of manifest.canonicalByFullPath.values()) {\n lines.push(\n ` ${toObjectKey(route.fullPath)}: ${toTsStringLiteral(route.id)},`,\n );\n }\n lines.push(' },');\n lines.push('} as const;');\n lines.push('');\n\n return lines.join('\\n');\n}\n\nfunction filenameToImportPath(filename: string): string {\n const stripped = filename.replace(/^\\//, '').replace(/\\.ts$/, '');\n return '../' + stripped;\n}\n\nfunction generateParamsType(params: RouteParamInfo[]): string {\n if (params.length === 0) return 'Record<string, never>';\n\n const entries = params.map((p) => {\n const key = isValidIdentifier(p.name) ? p.name : `'${p.name}'`;\n switch (p.type) {\n case 'dynamic':\n return `${key}: string`;\n case 'catchAll':\n return `${key}: string[]`;\n case 'optionalCatchAll':\n return `${key}?: string[]`;\n }\n });\n\n return `{ ${entries.join('; ')} }`;\n}\n\nfunction isValidIdentifier(name: string): boolean {\n return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);\n}\n\nfunction findNearestParentById(\n id: string,\n routesById: Map<string, RouteEntry>,\n): RouteEntry | undefined {\n if (id === '/') {\n return undefined;\n }\n\n const segments = id.split('/').filter(Boolean);\n for (let index = segments.length - 1; index > 0; index--) {\n const candidate = '/' + segments.slice(0, index).join('/');\n const route = routesById.get(candidate);\n if (route) {\n return route;\n }\n }\n\n return undefined;\n}\n\nfunction findNearestParentRoute(\n fullPath: string,\n routesByFullPath: Map<string, RouteEntry>,\n): RouteEntry | undefined {\n if (fullPath === '/') {\n return undefined;\n }\n\n const segments = fullPath.slice(1).split('/');\n for (let index = segments.length - 1; index > 0; index--) {\n const candidate = '/' + segments.slice(0, index).join('/');\n const route = routesByFullPath.get(candidate);\n if (route) {\n return route;\n }\n }\n\n return undefined;\n}\n\nfunction computeLocalPath(\n fullPath: string,\n parentFullPath: string | null,\n): string {\n if (fullPath === '/') {\n return '/';\n }\n\n if (!parentFullPath) {\n return fullPath.slice(1);\n }\n\n const suffix = fullPath.slice(parentFullPath.length).replace(/^\\/+/, '');\n return suffix || '/';\n}\n\nfunction toTsStringLiteral(value: string): string {\n return JSON.stringify(value);\n}\n\nfunction toTsKey(value: string): string {\n return toTsStringLiteral(value);\n}\n\nfunction toObjectKey(value: string): string {\n return isValidIdentifier(value) ? value : toTsStringLiteral(value);\n}\n\nfunction toReadonlyTupleType(values: readonly string[]): string {\n if (values.length === 0) {\n return 'readonly []';\n }\n\n return `readonly [${values.map((value) => toTsStringLiteral(value)).join(', ')}]`;\n}\n\nfunction toReadonlyTupleValue(values: readonly string[]): string {\n if (values.length === 0) {\n return '[] as const';\n }\n\n return `[${values.map((value) => toTsStringLiteral(value)).join(', ')}] as const`;\n}\n\n// --- JSON-LD utilities ---\n\nexport type JsonLdObject = Record<string, unknown>;\n\nexport function isJsonLdObject(value: unknown): value is JsonLdObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport function normalizeJsonLd(value: unknown): JsonLdObject[] {\n if (Array.isArray(value)) {\n return value.filter(isJsonLdObject);\n }\n\n return isJsonLdObject(value) ? [value] : [];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4FA,SAAgB,oBAAoB,UAA0B;CAC5D,IAAI,OAAO,SAAS,QAClB,+KACA,GACD;CAED,MAAM,WAAqB,EAAE;AAC7B,QAAO,KAAK,QAAQ,6BAA6B,UAAU;AACzD,WAAS,KAAK,MAAM;AAEpB,SAAO,MAAM,SAAS,SAAS,EAAE;GACjC;AACF,QAAO,KAAK,QAAQ,OAAO,IAAI;AAE/B,QAAO,KAAK,QAAQ,gBAAgB,GAAG,QAAQ,SAAS,OAAO,IAAI,EAAE;CAErE,MAAM,WAAW,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ;CAChD,MAAM,YAAsB,EAAE;AAE9B,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,iBAAiB,KAAK,QAAQ,CAAE;AACpC,YAAU,KAAK,QAAQ;;AAGzB,KAAI,UAAU,SAAS,KAAK,UAAU,UAAU,SAAS,OAAO,QAC9D,WAAU,KAAK;AAGjB,QAAO,MAAM,UAAU,KAAK,IAAI;;;;;;;;;;;;AAalC,SAAgB,kBAAkB,UAA0B;CAC1D,IAAI,OAAO,SAAS,QAClB,+KACA,GACD;CAED,MAAM,WAAqB,EAAE;AAC7B,QAAO,KAAK,QAAQ,6BAA6B,UAAU;AACzD,WAAS,KAAK,MAAM;AAEpB,SAAO,MAAM,SAAS,SAAS,EAAE;GACjC;AACF,QAAO,KAAK,QAAQ,OAAO,IAAI;AAE/B,QAAO,KAAK,QAAQ,gBAAgB,GAAG,QAAQ,SAAS,OAAO,IAAI,EAAE;AAIrE,QAAO,MAFU,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,CAE1B,KAAK,IAAI;;;;;AAMjC,SAAgB,mBAAmB,WAAqC;CACtE,MAAM,SAA2B,EAAE;AAEnC,MAAK,MAAM,SAAS,UAAU,SAAS,0BAA0B,CAC/D,QAAO,KAAK;EAAE,MAAM,MAAM;EAAI,MAAM;EAAoB,CAAC;AAE3D,MAAK,MAAM,SAAS,UAAU,SAAS,mCAAmC,CACxE,QAAO,KAAK;EAAE,MAAM,MAAM;EAAI,MAAM;EAAY,CAAC;AAEnD,MAAK,MAAM,SAAS,UAAU,SAAS,mCAAmC,CACxE,QAAO,KAAK;EAAE,MAAM,MAAM;EAAI,MAAM;EAAW,CAAC;AAGlD,QAAO;;;;;AAMT,SAAgB,oBAAoB,aAAsC;AACxE,QAAO;EACL,iBAAiB,uCAAuC,KAAK,YAAY;EACzE,gBAAgB,sCAAsC,KAAK,YAAY;EACxE;;AAGH,IAAM,aAA8B;CAClC,iBAAiB;CACjB,gBAAgB;CACjB;;;;;;;;AASD,SAAgB,sBACd,WACA,gBACA,mBACe;CACf,MAAM,SAAuB,EAAE;CAC/B,MAAM,aAA+B,EAAE;CACvC,MAAM,iCAAiB,IAAI,KAGxB;CACH,MAAM,cAAc,qBAAqB;CAKzC,MAAM,uBAAuB,CAAC,GAAG,UAAU,CAAC,MAAM,GAAG,MAAM;EACzD,MAAM,YAAY,YAAY,EAAE;EAChC,MAAM,YAAY,YAAY,EAAE;AAChC,MAAI,cAAc,UAChB,QAAO,YAAY;AAErB,SAAO,EAAE,cAAc,EAAE;GACzB;AAEF,MAAK,MAAM,YAAY,sBAAsB;EAC3C,MAAM,WAAW,oBAAoB,SAAS;EAC9C,MAAM,SAAS,mBAAmB,SAAS;EAC3C,MAAM,UAAU,iBAAiB,eAAe,SAAS,GAAG;EAC5D,MAAM,KAAK,kBAAkB,SAAS;EACtC,MAAM,mBAAmB,mBAAmB,GAAG;EAE/C,MAAM,kBAAkB,YAAY,SAAS;AAM7C,MAAI,CAAC,kBAAkB;AACrB,OAAI,eAAe,IAAI,SAAS,EAAE;IAChC,MAAM,SAAS,eAAe,IAAI,SAAS;AAC3C,QAAI,OAAO,aAAa,SACtB;IAKF,MAAM,qBAAqB,GAAW,MAAc;KAClD,MAAM,UAAU;KAChB,MAAM,WAAW;AACjB,SAAI,QAAQ,KAAK,EAAE,IAAI,SAAS,KAAK,EAAE,CAGrC,QAFY,EAAE,QAAQ,SAAS,GAAG,KACnB,EAAE,QAAQ,UAAU,GAAG;AAGxC,YAAO;;AAET,QACE,kBAAkB,OAAO,UAAU,SAAS,IAC5C,kBAAkB,UAAU,OAAO,SAAS,CAE5C;AAEF,eAAW,KAAK;KACd;KACA,UAAU,OAAO;KACjB,aAAa;KACb,cAAc,OAAO,aAAa;KACnC,CAAC;AACF,YAAQ,KACN,8BAA8B,SAAS,wBACjC,OAAO,SAAS,SAAS,SAAS,cAC1B,OAAO,SAAS,4DAC/B;AACD;;AAEF,kBAAe,IAAI,UAAU;IAAE;IAAU,UAAU;IAAiB,CAAC;;AAGvE,SAAO,KAAK;GACV;GACA,MAAM;GACN;GACA;GACA;GACA;GACA,MAAM,SAAS,SAAS,MAAM,GAAG,YAAY;GAC7C,UAAU;GACV,UAAU,EAAE;GACZ,SAAS,OAAO,YAAY,GAAG,SAAS,SAAS;GACjD,SAAS;GACT,YAAY,OAAO,MAAM,UAAU,MAAM,SAAS,WAAW;GAC7D,oBAAoB,OAAO,MACxB,UAAU,MAAM,SAAS,mBAC3B;GACF,CAAC;;AAGJ,QAAO,MAAM,GAAG,MAAM;EACpB,MAAM,KAAK,eAAe,EAAE,SAAS;EACrC,MAAM,KAAK,eAAe,EAAE,SAAS;AACrC,MAAI,OAAO,GAAI,QAAO,KAAK;AAC3B,SAAO,EAAE,SAAS,cAAc,EAAE,SAAS;GAC3C;CAEF,MAAM,kBAAkB,0BAA0B,OAAO;CAEzD,MAAM,YAAY,IAAI,IAAI,OAAO,KAAK,UAAU,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC;AAEnE,MAAK,MAAM,SAAS,QAAQ;EAU1B,MAAM,mBADkB,MAAM,GAAG,SAAS,KAAK,GAE3C,sBAAsB,MAAM,IAAI,UAAU,GAC1C,KAAA;EACJ,MAAM,iBAAiB,uBACrB,MAAM,UACN,gBACD;EACD,MAAM,SAAS,oBAAoB;AACnC,QAAM,WAAW,QAAQ,MAAM;AAC/B,QAAM,OAAO,iBAAiB,MAAM,UAAU,QAAQ,YAAY,KAAK;;AAGzE,MAAK,MAAM,SAAS,OAClB,KAAI,MAAM,SACR,WAAU,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,MAAM,GAAG;AAI1D,MAAK,MAAM,SAAS,OAClB,KAAI,MAAM,QAAQ,mBAAmB,MAAM,OAAO,WAAW,EAC3D,SAAQ,KACN,mBAAmB,MAAM,SAAS,wEAEnC;AAOL,MAAK,MAAM,SAAS,QAAQ;AAC1B,MAAI,MAAM,YAAY,CAAC,UAAU,IAAI,MAAM,SAAS,CAClD,SAAQ,KACN,mBAAmB,MAAM,GAAG,kBAAkB,MAAM,SAAS,sDAE9D;AAEH,OAAK,MAAM,WAAW,MAAM,SAC1B,KAAI,CAAC,UAAU,IAAI,QAAQ,CACzB,SAAQ,KACN,mBAAmB,MAAM,GAAG,iBAAiB,QAAQ,sDAEtD;;AAKP,QAAO;EAAE;EAAQ;EAAY,qBAAqB;EAAiB;;AAGrE,SAAS,0BACP,QACyB;CACzB,MAAM,sBAAM,IAAI,KAAyB;AACzC,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,WAAW,IAAI,IAAI,MAAM,SAAS;AACxC,MAAI,CAAC,SACH,KAAI,IAAI,MAAM,UAAU,MAAM;WACrB,SAAS,WAAW,CAAC,MAAM,QAEpC,KAAI,IAAI,MAAM,UAAU,MAAM;WACrB,SAAS,WAAW,MAAM;OAG/B,MAAM,GAAG,cAAc,SAAS,GAAG,GAAG,EACxC,KAAI,IAAI,MAAM,UAAU,MAAM;;;AAIpC,QAAO;;AAMT,SAAS,mBAAmB,IAAqB;CAC/C,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,OAAO,QAAQ;AAC9C,KAAI,SAAS,WAAW,EAAG,QAAO;AAClC,QAAO,iBAAiB,KAAK,SAAS,SAAS,SAAS,GAAG;;AAG7D,SAAS,eAAe,MAAsB;AAC5C,KAAI,KAAK,SAAS,QAAQ,CAAE,QAAO;AACnC,KAAI,KAAK,SAAS,OAAO,CAAE,QAAO;AAClC,KAAI,KAAK,SAAS,IAAI,CAAE,QAAO;AAC/B,QAAO;;AAGT,SAAS,qBAAqB,UAA0B;AACtD,KACE,SAAS,SAAS,kBAAkB,IACpC,SAAS,SAAS,mBAAmB,IACrC,SAAS,SAAS,cAAc,IAChC,SAAS,SAAS,eAAe,IACjC,SAAS,SAAS,gBAAgB,CAElC,QAAO;AAGT,QAAO;;;;;AAMT,SAAgB,sBAAsB,UAAiC;CACrE,MAAM,QAAkB,EAAE;CAC1B,MAAM,QAAQ,SAAS,OAAO;CAC9B,MAAM,cAAc,SAAS,OAAO,QACjC,MAAM,EAAE,QAAQ,mBAAmB,EAAE,QAAQ,eAC/C,CAAC;CACF,MAAM,cAAc,SAAS,OAAO,QACjC,MAAM,EAAE,OAAO,WAAW,EAC5B,CAAC;CACF,MAAM,eAAe,QAAQ;AAE7B,OAAM,KAAK,mCAAmC;AAC9C,OAAM,KACJ,KAAK,MAAM,WAAW,YAAY,WAAW,aAAa,WAC3D;AACD,KAAI,cAAc,EAChB,OAAM,KAAK,KAAK,YAAY,yBAAyB;AAGvD,MAAK,MAAM,SAAS,SAAS,QAAQ;EACnC,MAAM,QAAkB,EAAE;AAC1B,MAAI,MAAM,QAAQ,gBAAiB,OAAM,KAAK,gBAAgB;AAC9D,MAAI,MAAM,QAAQ,eAAgB,OAAM,KAAK,eAAe;EAC5D,MAAM,SAAS,MAAM,SAAS,IAAI,KAAK,MAAM,KAAK,KAAK,CAAC,KAAK;AAC7D,QAAM,KAAK,KAAK,MAAM,WAAW,SAAS;;AAG5C,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAgB,8BAA8B,UAAiC;CAC7E,MAAM,QAAkB,EAAE;CAC1B,MAAM,eAAe,SAAS,OAAO,MAClC,MAAM,EAAE,QAAQ,mBAAmB,EAAE,QAAQ,eAC/C;AAED,OAAM,KAAK,uDAAuD;AAClE,OAAM,KAAK,0BAA0B;AACrC,OAAM,KAAK,GAAG;AAEd,KAAI,aACF,OAAM,KACJ,iEACD;CAGH,MAAM,gCAAgB,IAAI,KAAqB;CAC/C,IAAI,aAAa;AAEjB,MAAK,MAAM,SAAS,SAAS,OAC3B,KAAI,MAAM,QAAQ,mBAAmB,MAAM,QAAQ,gBAAgB;EACjE,MAAM,aAAa,qBAAqB,MAAM,SAAS;EACvD,MAAM,QAAkB,EAAE;AAE1B,MAAI,MAAM,QAAQ,iBAAiB;GACjC,MAAM,QAAQ,KAAK;AACnB,SAAM,KAAK,wBAAwB,QAAQ;AAC3C,iBAAc,IAAI,GAAG,MAAM,SAAS,UAAU,MAAM;;AAEtD,MAAI,MAAM,QAAQ,gBAAgB;GAChC,MAAM,QAAQ,KAAK;AACnB,SAAM,KAAK,uBAAuB,QAAQ;AAC1C,iBAAc,IAAI,GAAG,MAAM,SAAS,SAAS,MAAM;;AAGrD,QAAM,KAAK,iBAAiB,MAAM,KAAK,KAAK,CAAC,WAAW,WAAW,IAAI;AACvE;;AAIJ,KAAI,aACF,OAAM,KAAK,GAAG;AAGhB,OAAM,KAAK,sCAAsC;AACjD,OAAM,KAAK,iCAAiC;AAE5C,MAAK,MAAM,SAAS,SAAS,oBAAoB,QAAQ,EAAE;EACzD,MAAM,cAAc,cAAc,IAAI,GAAG,MAAM,SAAS,SAAS;EACjE,MAAM,aAAa,cAAc,IAAI,GAAG,MAAM,SAAS,QAAQ;EAE/D,MAAM,aAAa,mBAAmB,MAAM,OAAO;EACnD,MAAM,YAAY;EAElB,MAAM,mBAAmB,cACrB,uCAAuC,YAAY,KACnD;EACJ,MAAM,kBAAkB,aACpB,uCAAuC,WAAW,KAClD;AAEJ,QAAM,KAAK,QAAQ,MAAM,SAAS,MAAM;AACxC,QAAM,KAAK,iBAAiB,WAAW,GAAG;AAC1C,QAAM,KAAK,uBAAuB,iBAAiB,GAAG;AACtD,QAAM,KAAK,gBAAgB,UAAU,GAAG;AACxC,QAAM,KAAK,sBAAsB,gBAAgB,GAAG;AACpD,QAAM,KAAK,SAAS;;AAGtB,OAAM,KAAK,MAAM;AACjB,OAAM,KAAK,IAAI;AACf,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,aAAa;AACxB,OAAM,KAAK,GAAG;AAEd,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAgB,6BACd,UACA,UAA+C,EAAE,EACzC;CACR,MAAM,QAAkB,EAAE;CAC1B,MAAM,cAAc,IAAI,IAAI,QAAQ,eAAe,EAAE,CAAC;AAEtD,OAAM,KAAK,uDAAuD;AAClE,OAAM,KAAK,0BAA0B;AACrC,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,+CAA+C;AAC1D,OAAM,KAAK,iCAAiC;AAC5C,OAAM,KAAK,mCAAmC;AAC9C,OAAM,KAAK,uCAAuC;AAClD,OAAM,KAAK,qDAAqD;AAChE,OAAM,KAAK,6DAA6D;AACxE,OAAM,KAAK,MAAM;AACjB,OAAM,KAAK,aAAa;AACxB,OAAM,KAAK,iBAAiB;AAC5B,OAAM,KAAK,yBAAyB;AACpC,OAAM,KAAK,yBAAyB;AACpC,OAAM,KAAK,yBAAyB;AACpC,OAAM,KAAK,wBAAwB;AACnC,OAAM,KAAK,8BAA8B;AACzC,OAAM,KAAK,8BAA8B;AACzC,OAAM,KAAK,6BAA6B;AACxC,OAAM,KAAK,wBAAwB;AACnC,OAAM,KAAK,sBAAsB;AACjC,OAAM,KAAK,sBAAsB;AACjC,OAAM,KAAK,yBAAyB;AACpC,OAAM,KAAK,iCAAiC;AAC5C,OAAM,KAAK,IAAI;AACf,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,0CAA0C;AACrD,MAAK,MAAM,SAAS,SAAS,OAC3B,OAAM,KACJ,KAAK,QAAQ,MAAM,GAAG,CAAC,+BAA+B,kBAAkB,MAAM,GAAG,CAAC,IAAI,kBAAkB,MAAM,KAAK,CAAC,IAAI,kBAAkB,MAAM,SAAS,CAAC,IAAI,MAAM,WAAW,kBAAkB,MAAM,SAAS,GAAG,OAAO,IAAI,oBAAoB,MAAM,SAAS,CAAC,IACnQ;AAEH,OAAM,KAAK,IAAI;AACf,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,gDAAgD;AAC3D,MAAK,MAAM,SAAS,SAAS,oBAAoB,QAAQ,CACvD,OAAM,KACJ,KAAK,QAAQ,MAAM,SAAS,CAAC,yBAAyB,kBAAkB,MAAM,GAAG,CAAC,IACnF;AAEH,OAAM,KAAK,IAAI;AACf,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,8DAA8D;AACzE,OAAM,KACJ,0EACD;AACD,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,mCAAmC;AAC9C,OAAM,KAAK,YAAY;AACvB,MAAK,MAAM,SAAS,SAAS,QAAQ;AACnC,QAAM,KAAK,OAAO,YAAY,MAAM,GAAG,CAAC,KAAK;AAC7C,QAAM,KAAK,aAAa,kBAAkB,MAAM,GAAG,CAAC,GAAG;AACvD,QAAM,KAAK,eAAe,kBAAkB,MAAM,KAAK,CAAC,GAAG;AAC3D,QAAM,KAAK,mBAAmB,kBAAkB,MAAM,SAAS,CAAC,GAAG;AACnE,QAAM,KACJ,mBAAmB,MAAM,WAAW,kBAAkB,MAAM,SAAS,GAAG,OAAO,GAChF;AACD,QAAM,KAAK,mBAAmB,qBAAqB,MAAM,SAAS,CAAC,GAAG;AACtE,QAAM,KAAK,qBAAqB,kBAAkB,MAAM,SAAS,CAAC,GAAG;AACrE,QAAM,KAAK,eAAe,kBAAkB,MAAM,KAAK,CAAC,GAAG;AAC3D,QAAM,KACJ,0BAA0B,OAAO,MAAM,QAAQ,gBAAgB,CAAC,GACjE;AACD,QAAM,KACJ,yBAAyB,OAAO,MAAM,QAAQ,eAAe,CAAC,GAC/D;AACD,QAAM,KAAK,oBAAoB,OAAO,YAAY,IAAI,MAAM,SAAS,CAAC,CAAC,GAAG;AAC1E,QAAM,KAAK,kBAAkB,OAAO,MAAM,QAAQ,CAAC,GAAG;AACtD,QAAM,KAAK,kBAAkB,OAAO,MAAM,QAAQ,CAAC,GAAG;AACtD,QAAM,KAAK,qBAAqB,OAAO,MAAM,WAAW,CAAC,GAAG;AAC5D,QAAM,KACJ,6BAA6B,OAAO,MAAM,mBAAmB,CAAC,GAC/D;AACD,QAAM,KACJ,wCAAwC,kBAAkB,MAAM,GAAG,CAAC,IACrE;;AAEH,OAAM,KAAK,OAAO;AAClB,OAAM,KAAK,kBAAkB;AAC7B,MAAK,MAAM,SAAS,SAAS,oBAAoB,QAAQ,CACvD,OAAM,KACJ,OAAO,YAAY,MAAM,SAAS,CAAC,IAAI,kBAAkB,MAAM,GAAG,CAAC,GACpE;AAEH,OAAM,KAAK,OAAO;AAClB,OAAM,KAAK,cAAc;AACzB,OAAM,KAAK,GAAG;AAEd,QAAO,MAAM,KAAK,KAAK;;AAGzB,SAAS,qBAAqB,UAA0B;AAEtD,QAAO,QADU,SAAS,QAAQ,OAAO,GAAG,CAAC,QAAQ,SAAS,GAAG;;AAInE,SAAS,mBAAmB,QAAkC;AAC5D,KAAI,OAAO,WAAW,EAAG,QAAO;AAchC,QAAO,KAZS,OAAO,KAAK,MAAM;EAChC,MAAM,MAAM,kBAAkB,EAAE,KAAK,GAAG,EAAE,OAAO,IAAI,EAAE,KAAK;AAC5D,UAAQ,EAAE,MAAV;GACE,KAAK,UACH,QAAO,GAAG,IAAI;GAChB,KAAK,WACH,QAAO,GAAG,IAAI;GAChB,KAAK,mBACH,QAAO,GAAG,IAAI;;GAElB,CAEkB,KAAK,KAAK,CAAC;;AAGjC,SAAS,kBAAkB,MAAuB;AAChD,QAAO,6BAA6B,KAAK,KAAK;;AAGhD,SAAS,sBACP,IACA,YACwB;AACxB,KAAI,OAAO,IACT;CAGF,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,OAAO,QAAQ;AAC9C,MAAK,IAAI,QAAQ,SAAS,SAAS,GAAG,QAAQ,GAAG,SAAS;EACxD,MAAM,YAAY,MAAM,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,IAAI;EAC1D,MAAM,QAAQ,WAAW,IAAI,UAAU;AACvC,MAAI,MACF,QAAO;;;AAOb,SAAS,uBACP,UACA,kBACwB;AACxB,KAAI,aAAa,IACf;CAGF,MAAM,WAAW,SAAS,MAAM,EAAE,CAAC,MAAM,IAAI;AAC7C,MAAK,IAAI,QAAQ,SAAS,SAAS,GAAG,QAAQ,GAAG,SAAS;EACxD,MAAM,YAAY,MAAM,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,IAAI;EAC1D,MAAM,QAAQ,iBAAiB,IAAI,UAAU;AAC7C,MAAI,MACF,QAAO;;;AAOb,SAAS,iBACP,UACA,gBACQ;AACR,KAAI,aAAa,IACf,QAAO;AAGT,KAAI,CAAC,eACH,QAAO,SAAS,MAAM,EAAE;AAI1B,QADe,SAAS,MAAM,eAAe,OAAO,CAAC,QAAQ,QAAQ,GAAG,IACvD;;AAGnB,SAAS,kBAAkB,OAAuB;AAChD,QAAO,KAAK,UAAU,MAAM;;AAG9B,SAAS,QAAQ,OAAuB;AACtC,QAAO,kBAAkB,MAAM;;AAGjC,SAAS,YAAY,OAAuB;AAC1C,QAAO,kBAAkB,MAAM,GAAG,QAAQ,kBAAkB,MAAM;;AAGpE,SAAS,oBAAoB,QAAmC;AAC9D,KAAI,OAAO,WAAW,EACpB,QAAO;AAGT,QAAO,aAAa,OAAO,KAAK,UAAU,kBAAkB,MAAM,CAAC,CAAC,KAAK,KAAK,CAAC;;AAGjF,SAAS,qBAAqB,QAAmC;AAC/D,KAAI,OAAO,WAAW,EACpB,QAAO;AAGT,QAAO,IAAI,OAAO,KAAK,UAAU,kBAAkB,MAAM,CAAC,CAAC,KAAK,KAAK,CAAC;;AAOxE,SAAgB,eAAe,OAAuC;AACpE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM;;AAG7E,SAAgB,gBAAgB,OAAgC;AAC9D,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,MAAM,OAAO,eAAe;AAGrC,QAAO,eAAe,MAAM,GAAG,CAAC,MAAM,GAAG,EAAE"}
@@ -1,6 +1,6 @@
1
+ import { isAbsolute, relative, resolve } from "node:path";
1
2
  import { normalizePath } from "vite";
2
3
  import { globSync } from "tinyglobby";
3
- import { isAbsolute, relative, resolve } from "node:path";
4
4
  //#region packages/platform/src/lib/router-plugin.ts
5
5
  /**
6
6
  * Router plugin that handles route file discovery and hot module replacement.
@@ -1,5 +1,5 @@
1
- import path from "node:path";
2
1
  import { readFileSync } from "node:fs";
2
+ import path from "node:path";
3
3
  //#region packages/platform/src/lib/tailwind-preprocessor.ts
4
4
  /**
5
5
  * Creates a stylesheet preprocessor that injects Tailwind v4 `@reference`
@@ -1,9 +1,9 @@
1
1
  import { detectSchemaExports, filenameToRoutePath, formatManifestSummary, generateRouteManifest, generateRouteTableDeclaration, generateRouteTreeDeclaration } from "./route-manifest.js";
2
2
  import { detectJsonLdModuleExports, extractMarkdownJsonLd, generateJsonLdManifestSource } from "./json-ld-manifest-plugin.js";
3
3
  import { createRouteFileDiscovery } from "./route-file-discovery.js";
4
- import { normalizePath } from "vite";
5
- import { dirname, join, relative, resolve } from "node:path";
6
4
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
+ import { dirname, join, relative, resolve } from "node:path";
6
+ import { normalizePath } from "vite";
7
7
  //#region packages/platform/src/lib/typed-routes-plugin.ts
8
8
  var DEFAULT_OUT_FILE = "src/routeTree.gen.ts";
9
9
  function resolvePluginOptions(options = {}) {
@@ -87,7 +87,12 @@ function typedRoutes(options = {}) {
87
87
  const declaration = generateRouteTableDeclaration(manifest);
88
88
  const canonicalFiles = new Set(manifest.routes.map((route) => route.filename));
89
89
  const jsonLdEntries = buildJsonLdEntries(resolveDiscoveredFile, routeFiles.filter((filename) => canonicalFiles.has(filename)), contentFiles.filter((filename) => canonicalFiles.has(filename)));
90
- const output = combineGeneratedModules(declaration, generateRouteTreeDeclaration(manifest, { jsonLdPaths: jsonLdEntries.map((entry) => entry.routePath) }), resolvedOptions.jsonLdManifest && jsonLdEntries.length > 0 ? generateJsonLdManifestSource(jsonLdEntries, resolvedOptions.outFile) : "");
90
+ const output = combineGeneratedModules(declaration, generateRouteTreeDeclaration(manifest, { jsonLdFiles: jsonLdEntries.map((entry) => entry.sourceFile) }), resolvedOptions.jsonLdManifest && jsonLdEntries.length > 0 ? generateJsonLdManifestSource(jsonLdEntries, resolvedOptions.outFile) : "");
91
+ const hardCollisions = manifest.collisions.filter((c) => c.samePriority);
92
+ if (hardCollisions.length > 0 && command === "build") {
93
+ const details = hardCollisions.map((c) => ` '${c.fullPath}': '${c.keptFile}' vs '${c.droppedFile}'`).join("\n");
94
+ 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.`);
95
+ }
91
96
  if (manifest.routes.length > 0) console.log(formatManifestSummary(manifest));
92
97
  const outPath = join(root, resolvedOptions.outFile);
93
98
  const outDir = dirname(outPath);
@@ -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';\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 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 jsonLdPaths: jsonLdEntries.map((entry) => entry.routePath),\n });\n const output = combineGeneratedModules(\n declaration,\n routeTree,\n resolvedOptions.jsonLdManifest && jsonLdEntries.length > 0\n ? generateJsonLdManifestSource(jsonLdEntries, resolvedOptions.outFile)\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 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 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 { 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';\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 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 (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 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 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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"}