@analogjs/platform 3.0.0-alpha.97 → 3.0.0-alpha.98

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/migrations/migrate-setup-testbed-browser-mode/migrate-setup-testbed-browser-mode.d.ts +7 -0
  2. package/migrations/migrate-setup-testbed-browser-mode/migrate-setup-testbed-browser-mode.js +68 -0
  3. package/migrations/migrate-setup-testbed-browser-mode/migrate-setup-testbed-browser-mode.js.map +1 -0
  4. package/migrations/migrate-to-separated-plugins/migrate-to-separated-plugins.js +1 -6
  5. package/migrations/migrate-to-separated-plugins/migrate-to-separated-plugins.js.map +1 -1
  6. package/migrations/migrate-vitest-builder-aliases/migrate-vitest-builder-aliases.d.ts +6 -0
  7. package/migrations/migrate-vitest-builder-aliases/migrate-vitest-builder-aliases.js +62 -0
  8. package/migrations/migrate-vitest-builder-aliases/migrate-vitest-builder-aliases.js.map +1 -0
  9. package/migrations/migration.json +10 -0
  10. package/migrations/utils/source-edits.d.ts +9 -0
  11. package/migrations/utils/source-edits.js +33 -0
  12. package/migrations/utils/source-edits.js.map +1 -0
  13. package/package.json +5 -5
  14. package/src/lib/nx-plugin/builders.json +1 -2
  15. package/src/lib/nx-plugin/executors.json +1 -7
  16. package/src/lib/nx-plugin/generators.json +0 -10
  17. package/src/lib/nx-plugin/src/generators/app/generator.js +2 -5
  18. package/src/lib/nx-plugin/src/generators/app/generator.js.map +1 -1
  19. package/src/lib/nx-plugin/src/generators/app/versions/minimum-supported-versions.d.ts +1 -4
  20. package/src/lib/nx-plugin/src/generators/app/versions/minimum-supported-versions.js +2 -5
  21. package/src/lib/nx-plugin/src/generators/app/versions/minimum-supported-versions.js.map +1 -1
  22. package/src/lib/nx-plugin/src/generators/app/versions/nx_18_X/versions.d.ts +6 -6
  23. package/src/lib/nx-plugin/src/generators/app/versions/nx_18_X/versions.js.map +1 -1
  24. package/src/lib/nx-plugin/src/generators/init/lib/add-analog-dependencies.js +1 -1
  25. package/src/lib/nx-plugin/src/generators/init/lib/add-analog-dependencies.js.map +1 -1
  26. package/src/lib/nx-plugin/src/generators/preset/generator.js +2 -5
  27. package/src/lib/nx-plugin/src/generators/preset/generator.js.map +1 -1
  28. package/src/lib/nx-plugin/src/utils/versions/dev-dependencies.d.ts +2 -2
  29. package/src/lib/nx-plugin/src/utils/versions/dev-dependencies.js +7 -10
  30. package/src/lib/nx-plugin/src/utils/versions/dev-dependencies.js.map +1 -1
  31. package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.d.ts +6 -10
  32. package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.js +6 -12
  33. package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.js.map +1 -1
  34. package/src/lib/route-file-discovery.js +4 -16
  35. package/src/lib/route-file-discovery.js.map +1 -1
  36. package/src/lib/route-idiom-diagnostics.js +2 -11
  37. package/src/lib/route-idiom-diagnostics.js.map +1 -1
  38. package/src/lib/route-manifest.d.ts +5 -5
  39. package/src/lib/route-manifest.js +8 -8
  40. package/src/lib/route-manifest.js.map +1 -1
  41. package/src/lib/router-plugin.js +2 -15
  42. package/src/lib/router-plugin.js.map +1 -1
  43. package/src/lib/ssr/inject-html-plugin.js +1 -2
  44. package/src/lib/ssr/inject-html-plugin.js.map +1 -1
  45. package/src/lib/nx-plugin/src/executors/vitest/compat.d.ts +0 -3
  46. package/src/lib/nx-plugin/src/executors/vitest/compat.js +0 -8
  47. package/src/lib/nx-plugin/src/executors/vitest/compat.js.map +0 -1
  48. package/src/lib/nx-plugin/src/executors/vitest/schema.d.ts +0 -1
  49. package/src/lib/nx-plugin/src/executors/vitest/schema.json +0 -35
  50. package/src/lib/nx-plugin/src/executors/vitest/vitest.impl.d.ts +0 -2
  51. package/src/lib/nx-plugin/src/executors/vitest/vitest.impl.js +0 -7
  52. package/src/lib/nx-plugin/src/executors/vitest/vitest.impl.js.map +0 -1
  53. package/src/lib/nx-plugin/src/generators/app/lib/add-analog-dependencies.d.ts +0 -2
  54. package/src/lib/nx-plugin/src/generators/app/lib/initialize-analog-workspace.d.ts +0 -3
  55. package/src/lib/nx-plugin/src/generators/app/versions/dependencies.d.ts +0 -5
  56. package/src/lib/nx-plugin/src/generators/app/versions/dev-dependencies.d.ts +0 -4
  57. package/src/lib/nx-plugin/src/generators/app/versions/nx-dependencies.d.ts +0 -4
  58. package/src/lib/nx-plugin/src/generators/setup-vitest/compat.d.ts +0 -9
  59. package/src/lib/nx-plugin/src/generators/setup-vitest/compat.js +0 -19
  60. package/src/lib/nx-plugin/src/generators/setup-vitest/compat.js.map +0 -1
  61. package/src/lib/nx-plugin/src/generators/setup-vitest/files/src/test-setup.ts__template__ +0 -22
  62. package/src/lib/nx-plugin/src/generators/setup-vitest/files/vite.config.mts__template__ +0 -23
  63. package/src/lib/nx-plugin/src/generators/setup-vitest/generator.d.ts +0 -4
  64. package/src/lib/nx-plugin/src/generators/setup-vitest/generator.js +0 -38
  65. package/src/lib/nx-plugin/src/generators/setup-vitest/generator.js.map +0 -1
  66. package/src/lib/nx-plugin/src/generators/setup-vitest/lib/add-analog-dependencies.d.ts +0 -2
  67. package/src/lib/nx-plugin/src/generators/setup-vitest/lib/add-analog-dependencies.js +0 -11
  68. package/src/lib/nx-plugin/src/generators/setup-vitest/lib/add-analog-dependencies.js.map +0 -1
  69. package/src/lib/nx-plugin/src/generators/setup-vitest/lib/update-test-target.d.ts +0 -3
  70. package/src/lib/nx-plugin/src/generators/setup-vitest/lib/update-test-target.js +0 -24
  71. package/src/lib/nx-plugin/src/generators/setup-vitest/lib/update-test-target.js.map +0 -1
  72. package/src/lib/nx-plugin/src/generators/setup-vitest/lib/update-tsconfig.d.ts +0 -3
  73. package/src/lib/nx-plugin/src/generators/setup-vitest/lib/update-tsconfig.js +0 -23
  74. package/src/lib/nx-plugin/src/generators/setup-vitest/lib/update-tsconfig.js.map +0 -1
  75. package/src/lib/nx-plugin/src/generators/setup-vitest/schema.d.ts +0 -7
  76. package/src/lib/nx-plugin/src/generators/setup-vitest/schema.json +0 -19
@@ -1 +1 @@
1
- {"version":3,"file":"generator.js","names":[],"sources":["../../../../../../../../nx-plugin/src/generators/preset/generator.ts"],"sourcesContent":["import {\n ensurePackage,\n generateFiles,\n NX_VERSION,\n Tree,\n updateJson,\n} from '@nx/devkit';\nimport { join } from 'node:path';\nimport {\n belowMinimumSupportedOxlintNxVersion,\n MINIMUM_SUPPORTED_OXLINT_NX_VERSION,\n} from '../app/versions/minimum-supported-versions';\nimport { PresetGeneratorSchema } from './schema';\n\nexport default async function (\n tree: Tree,\n options: PresetGeneratorSchema,\n): Promise<() => void> {\n ensurePackage('@nx/angular', NX_VERSION);\n ensurePackage('@nx/vite', NX_VERSION);\n if (options.linter === 'oxlint') {\n if (belowMinimumSupportedOxlintNxVersion(NX_VERSION)) {\n throw new Error(\n `Nx v${MINIMUM_SUPPORTED_OXLINT_NX_VERSION} or newer is required to use the oxlint linter`,\n );\n }\n ensurePackage('@nx/oxlint', NX_VERSION);\n } else if (options.linter !== 'none') {\n ensurePackage('@nx/eslint', NX_VERSION);\n }\n ensurePackage('@angular-devkit/core', 'latest');\n ensurePackage('rxjs', 'latest');\n\n const generatorModule = await import('../app/generator');\n const appGenerator =\n 'default' in generatorModule ? generatorModule.default : generatorModule;\n\n const installTask = await appGenerator(tree, {\n ...options,\n skipAgentContext: true,\n });\n\n // Seed agent context at the workspace root so AI coding assistants pick up\n // Analog conventions (see node_modules/@analogjs/platform/AGENTS.md).\n generateFiles(\n tree,\n join(__dirname, '..', 'app', 'files', 'agents'),\n '.',\n options,\n );\n\n if (tree.exists('/tsconfig.base.json')) {\n updateJson<{\n compilerOptions?: {\n baseUrl?: string;\n };\n }>(tree, '/tsconfig.base.json', (json) => {\n delete json.compilerOptions?.baseUrl;\n return json;\n });\n }\n\n return installTask;\n}\n"],"mappings":";;;;;AAcA,eAAA,kBACE,MACA,SACqB;AACrB,EAAA,GAAA,WAAA,eAAc,eAAe,WAAA,WAAW;AACxC,EAAA,GAAA,WAAA,eAAc,YAAY,WAAA,WAAW;AACrC,KAAI,QAAQ,WAAW,UAAU;AAC/B,MAAI,mCAAA,qCAAqC,WAAA,WAAW,CAClD,OAAM,IAAI,MACR,OAAO,mCAAA,oCAAoC,gDAC5C;AAEH,GAAA,GAAA,WAAA,eAAc,cAAc,WAAA,WAAW;YAC9B,QAAQ,WAAW,OAC5B,EAAA,GAAA,WAAA,eAAc,cAAc,WAAA,WAAW;AAEzC,EAAA,GAAA,WAAA,eAAc,wBAAwB,SAAS;AAC/C,EAAA,GAAA,WAAA,eAAc,QAAQ,SAAS;CAE/B,MAAM,kBAAkB,MAAA,QAAA,SAAA,CAAA,WAAA,QAAM,sBAAA,CAAA;CAI9B,MAAM,cAAc,OAFlB,aAAa,kBAAkB,gBAAgB,UAAU,iBAEpB,MAAM;EAC3C,GAAG;EACH,kBAAkB;EACnB,CAAC;AAIF,EAAA,GAAA,WAAA,eACE,OAAA,GAAA,UAAA,MACK,WAAW,MAAM,OAAO,SAAS,SAAS,EAC/C,KACA,QACD;AAED,KAAI,KAAK,OAAO,sBAAsB,CACpC,EAAA,GAAA,WAAA,YAIG,MAAM,wBAAwB,SAAS;AACxC,SAAO,KAAK,iBAAiB;AAC7B,SAAO;GACP;AAGJ,QAAO"}
1
+ {"version":3,"file":"generator.js","names":[],"sources":["../../../../../../../../nx-plugin/src/generators/preset/generator.ts"],"sourcesContent":["import {\n ensurePackage,\n generateFiles,\n NX_VERSION,\n Tree,\n updateJson,\n} from '@nx/devkit';\nimport { join } from 'node:path';\nimport { PresetGeneratorSchema } from './schema';\n\nexport default async function (\n tree: Tree,\n options: PresetGeneratorSchema,\n): Promise<() => void> {\n ensurePackage('@nx/angular', NX_VERSION);\n ensurePackage('@nx/vite', NX_VERSION);\n if (options.linter === 'oxlint') {\n ensurePackage('@nx/oxlint', NX_VERSION);\n } else if (options.linter !== 'none') {\n ensurePackage('@nx/eslint', NX_VERSION);\n }\n ensurePackage('@angular-devkit/core', 'latest');\n ensurePackage('rxjs', 'latest');\n\n const generatorModule = await import('../app/generator');\n const appGenerator =\n 'default' in generatorModule ? generatorModule.default : generatorModule;\n\n const installTask = await appGenerator(tree, {\n ...options,\n skipAgentContext: true,\n });\n\n // Seed agent context at the workspace root so AI coding assistants pick up\n // Analog conventions (see node_modules/@analogjs/platform/AGENTS.md).\n generateFiles(\n tree,\n join(__dirname, '..', 'app', 'files', 'agents'),\n '.',\n options,\n );\n\n if (tree.exists('/tsconfig.base.json')) {\n updateJson<{\n compilerOptions?: {\n baseUrl?: string;\n };\n }>(tree, '/tsconfig.base.json', (json) => {\n delete json.compilerOptions?.baseUrl;\n return json;\n });\n }\n\n return installTask;\n}\n"],"mappings":";;;;AAUA,eAAA,kBACE,MACA,SACqB;AACrB,EAAA,GAAA,WAAA,eAAc,eAAe,WAAA,WAAW;AACxC,EAAA,GAAA,WAAA,eAAc,YAAY,WAAA,WAAW;AACrC,KAAI,QAAQ,WAAW,SACrB,EAAA,GAAA,WAAA,eAAc,cAAc,WAAA,WAAW;UAC9B,QAAQ,WAAW,OAC5B,EAAA,GAAA,WAAA,eAAc,cAAc,WAAA,WAAW;AAEzC,EAAA,GAAA,WAAA,eAAc,wBAAwB,SAAS;AAC/C,EAAA,GAAA,WAAA,eAAc,QAAQ,SAAS;CAE/B,MAAM,kBAAkB,MAAA,QAAA,SAAA,CAAA,WAAA,QAAM,sBAAA,CAAA;CAI9B,MAAM,cAAc,OAFlB,aAAa,kBAAkB,gBAAgB,UAAU,iBAEpB,MAAM;EAC3C,GAAG;EACH,kBAAkB;EACnB,CAAC;AAIF,EAAA,GAAA,WAAA,eACE,OAAA,GAAA,UAAA,MACK,WAAW,MAAM,OAAO,SAAS,SAAS,EAC/C,KACA,QACD;AAED,KAAI,KAAK,OAAO,sBAAsB,CACpC,EAAA,GAAA,WAAA,YAIG,MAAM,wBAAwB,SAAS;AACxC,SAAO,KAAK,iBAAiB;AAC7B,SAAO;GACP;AAGJ,QAAO"}
@@ -1,4 +1,4 @@
1
- declare const devDependencyKeys: readonly ["@analogjs/platform", "@analogjs/vite-plugin-angular", "jsdom", "vite-tsconfig-paths", "vitest", "vite", "@vitest/coverage-v8", "@vitest/ui"];
1
+ declare const devDependencyKeys: readonly ["@analogjs/platform", "@analogjs/vite-plugin-angular", "@analogjs/vitest-angular", "@nx/vite", "jsdom", "vite-tsconfig-paths", "vitest", "vite", "@vitest/coverage-v8", "@vitest/ui"];
2
2
  export type AnalogDevDependency = (typeof devDependencyKeys)[number];
3
- export declare const getAnalogDevDependencies: (ngVersion: string, nxVersion?: string) => Record<AnalogDevDependency, string>;
3
+ export declare const getAnalogDevDependencies: (ngVersion: string) => Record<AnalogDevDependency, string>;
4
4
  export {};
@@ -3,22 +3,19 @@ const require_versions = require("./ng_19_X/versions.js");
3
3
  let _nx_devkit = require("@nx/devkit");
4
4
  let semver = require("semver");
5
5
  //#region packages/nx-plugin/src/utils/versions/dev-dependencies.ts
6
- var getAnalogDevDependencies = (ngVersion, nxVersion) => {
7
- return { ...getDevDependencies(ngVersion.replace(/[~^]/, ""), nxVersion ? nxVersion.replace(/[~^]/, "") : null) };
8
- };
9
- var getDevDependencies = (escapedAngularVersion, escapedNxVersion) => {
10
- if ((0, semver.lt)(escapedAngularVersion, "20.0.0")) throw new Error(_nx_devkit.stripIndents`Angular v20.0.0 or newer is required.`);
6
+ var getAnalogDevDependencies = (ngVersion) => {
7
+ if ((0, semver.lt)(ngVersion.replace(/[~^]/, ""), "20.0.0")) throw new Error(_nx_devkit.stripIndents`Angular v20.0.0 or newer is required.`);
11
8
  return {
12
9
  "@analogjs/platform": require_versions.V19_X_ANALOG_JS_PLATFORM,
13
10
  "@analogjs/vite-plugin-angular": require_versions.V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR,
14
11
  "@analogjs/vitest-angular": require_versions.V19_X_ANALOG_JS_VITEST_ANGULAR,
15
- "@nx/vite": escapedNxVersion && !(0, semver.lt)(escapedNxVersion, "23.0.0") ? require_versions.NX_X_LATEST_NX : require_versions.V19_X_NX_VITE,
12
+ "@nx/vite": require_versions.NX_X_LATEST_NX,
16
13
  jsdom: require_versions.V19_X_JSDOM,
17
14
  "vite-tsconfig-paths": require_versions.V19_X_VITE_TSCONFIG_PATHS,
18
- vite: escapedNxVersion && (0, semver.lt)(escapedNxVersion, "20.5.0") ? require_versions.V19_X_VITE : require_versions.NX_X_LATEST_VITE,
19
- vitest: (0, semver.lt)(escapedAngularVersion, "21.0.0") || escapedNxVersion && (0, semver.lt)(escapedNxVersion, "22.3.0") ? require_versions.V19_X_VITEST : require_versions.NX_X_LATEST_VITEST,
20
- "@vitest/coverage-v8": (0, semver.lt)(escapedAngularVersion, "21.0.0") || escapedNxVersion && (0, semver.lt)(escapedNxVersion, "22.3.0") ? require_versions.V19_X_VITEST : require_versions.NX_X_LATEST_VITEST,
21
- "@vitest/ui": (0, semver.lt)(escapedAngularVersion, "21.0.0") || escapedNxVersion && (0, semver.lt)(escapedNxVersion, "22.3.0") ? require_versions.V19_X_VITEST : require_versions.NX_X_LATEST_VITEST
15
+ vite: require_versions.NX_X_LATEST_VITE,
16
+ vitest: require_versions.NX_X_LATEST_VITEST,
17
+ "@vitest/coverage-v8": require_versions.NX_X_LATEST_VITEST,
18
+ "@vitest/ui": require_versions.NX_X_LATEST_VITEST
22
19
  };
23
20
  };
24
21
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"dev-dependencies.js","names":[],"sources":["../../../../../../../../nx-plugin/src/utils/versions/dev-dependencies.ts"],"sourcesContent":["import { lt } from 'semver';\nimport { stripIndents } from '@nx/devkit';\nimport {\n V19_X_ANALOG_JS_PLATFORM,\n V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR,\n V19_X_ANALOG_JS_VITEST_ANGULAR,\n V19_X_NX_VITE,\n V19_X_JSDOM,\n V19_X_VITE_TSCONFIG_PATHS,\n V19_X_VITEST,\n V19_X_VITE,\n NX_X_LATEST_VITE,\n NX_X_LATEST_VITEST,\n NX_X_LATEST_NX,\n} from './ng_19_X/versions';\n\nconst devDependencyKeys = [\n '@analogjs/platform',\n '@analogjs/vite-plugin-angular',\n 'jsdom',\n 'vite-tsconfig-paths',\n 'vitest',\n 'vite',\n '@vitest/coverage-v8',\n '@vitest/ui',\n] as const;\nexport type AnalogDevDependency = (typeof devDependencyKeys)[number];\n\nexport const getAnalogDevDependencies = (\n ngVersion: string,\n nxVersion?: string,\n): Record<AnalogDevDependency, string> => {\n const escapedNgVersion = ngVersion.replace(/[~^]/, '');\n const escapedNxVersion = nxVersion ? nxVersion.replace(/[~^]/, '') : null;\n\n const devDependencies = getDevDependencies(\n escapedNgVersion,\n escapedNxVersion,\n );\n\n return { ...devDependencies };\n};\n\nconst getDevDependencies = (\n escapedAngularVersion: string,\n escapedNxVersion: string | null,\n) => {\n // fail out for versions <20.0.0\n if (lt(escapedAngularVersion, '20.0.0')) {\n throw new Error(stripIndents`Angular v20.0.0 or newer is required.`);\n }\n\n // return latest deps for versions >=20.0.0\n return {\n '@analogjs/platform': V19_X_ANALOG_JS_PLATFORM,\n '@analogjs/vite-plugin-angular': V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR,\n '@analogjs/vitest-angular': V19_X_ANALOG_JS_VITEST_ANGULAR,\n '@nx/vite':\n escapedNxVersion && !lt(escapedNxVersion, '23.0.0')\n ? NX_X_LATEST_NX\n : V19_X_NX_VITE,\n jsdom: V19_X_JSDOM,\n 'vite-tsconfig-paths': V19_X_VITE_TSCONFIG_PATHS,\n vite:\n escapedNxVersion && lt(escapedNxVersion, '20.5.0')\n ? V19_X_VITE\n : NX_X_LATEST_VITE,\n vitest:\n lt(escapedAngularVersion, '21.0.0') ||\n (escapedNxVersion && lt(escapedNxVersion, '22.3.0'))\n ? V19_X_VITEST\n : NX_X_LATEST_VITEST,\n '@vitest/coverage-v8':\n lt(escapedAngularVersion, '21.0.0') ||\n (escapedNxVersion && lt(escapedNxVersion, '22.3.0'))\n ? V19_X_VITEST\n : NX_X_LATEST_VITEST,\n '@vitest/ui':\n lt(escapedAngularVersion, '21.0.0') ||\n (escapedNxVersion && lt(escapedNxVersion, '22.3.0'))\n ? V19_X_VITEST\n : NX_X_LATEST_VITEST,\n };\n};\n"],"mappings":";;;;;AA4BA,IAAa,4BACX,WACA,cACwC;AASxC,QAAO,EAAE,GALe,mBAHC,UAAU,QAAQ,QAAQ,GAAG,EAC7B,YAAY,UAAU,QAAQ,QAAQ,GAAG,GAAG,KAKpE,EAE4B;;AAG/B,IAAM,sBACJ,uBACA,qBACG;AAEH,MAAA,GAAA,OAAA,IAAO,uBAAuB,SAAS,CACrC,OAAM,IAAI,MAAM,WAAA,YAAY,wCAAwC;AAItE,QAAO;EACL,sBAAsB,iBAAA;EACtB,iCAAiC,iBAAA;EACjC,4BAA4B,iBAAA;EAC5B,YACE,oBAAoB,EAAA,GAAA,OAAA,IAAI,kBAAkB,SAAS,GAC/C,iBAAA,iBACA,iBAAA;EACN,OAAO,iBAAA;EACP,uBAAuB,iBAAA;EACvB,MACE,qBAAA,GAAA,OAAA,IAAuB,kBAAkB,SAAS,GAC9C,iBAAA,aACA,iBAAA;EACN,SAAA,GAAA,OAAA,IACK,uBAAuB,SAAS,IAClC,qBAAA,GAAA,OAAA,IAAuB,kBAAkB,SAAS,GAC/C,iBAAA,eACA,iBAAA;EACN,wBAAA,GAAA,OAAA,IACK,uBAAuB,SAAS,IAClC,qBAAA,GAAA,OAAA,IAAuB,kBAAkB,SAAS,GAC/C,iBAAA,eACA,iBAAA;EACN,eAAA,GAAA,OAAA,IACK,uBAAuB,SAAS,IAClC,qBAAA,GAAA,OAAA,IAAuB,kBAAkB,SAAS,GAC/C,iBAAA,eACA,iBAAA;EACP"}
1
+ {"version":3,"file":"dev-dependencies.js","names":[],"sources":["../../../../../../../../nx-plugin/src/utils/versions/dev-dependencies.ts"],"sourcesContent":["import { lt } from 'semver';\nimport { stripIndents } from '@nx/devkit';\nimport {\n V19_X_ANALOG_JS_PLATFORM,\n V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR,\n V19_X_ANALOG_JS_VITEST_ANGULAR,\n V19_X_JSDOM,\n V19_X_VITE_TSCONFIG_PATHS,\n NX_X_LATEST_VITE,\n NX_X_LATEST_VITEST,\n NX_X_LATEST_NX,\n} from './ng_19_X/versions';\n\nconst devDependencyKeys = [\n '@analogjs/platform',\n '@analogjs/vite-plugin-angular',\n '@analogjs/vitest-angular',\n '@nx/vite',\n 'jsdom',\n 'vite-tsconfig-paths',\n 'vitest',\n 'vite',\n '@vitest/coverage-v8',\n '@vitest/ui',\n] as const;\nexport type AnalogDevDependency = (typeof devDependencyKeys)[number];\n\nexport const getAnalogDevDependencies = (\n ngVersion: string,\n): Record<AnalogDevDependency, string> => {\n const escapedNgVersion = ngVersion.replace(/[~^]/, '');\n\n // fail out for versions <20.0.0\n if (lt(escapedNgVersion, '20.0.0')) {\n throw new Error(stripIndents`Angular v20.0.0 or newer is required.`);\n }\n\n return {\n '@analogjs/platform': V19_X_ANALOG_JS_PLATFORM,\n '@analogjs/vite-plugin-angular': V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR,\n '@analogjs/vitest-angular': V19_X_ANALOG_JS_VITEST_ANGULAR,\n '@nx/vite': NX_X_LATEST_NX,\n jsdom: V19_X_JSDOM,\n 'vite-tsconfig-paths': V19_X_VITE_TSCONFIG_PATHS,\n vite: NX_X_LATEST_VITE,\n vitest: NX_X_LATEST_VITEST,\n '@vitest/coverage-v8': NX_X_LATEST_VITEST,\n '@vitest/ui': NX_X_LATEST_VITEST,\n };\n};\n"],"mappings":";;;;;AA2BA,IAAa,4BACX,cACwC;AAIxC,MAAA,GAAA,OAAA,IAHyB,UAAU,QAAQ,QAAQ,GAAG,EAG7B,SAAS,CAChC,OAAM,IAAI,MAAM,WAAA,YAAY,wCAAwC;AAGtE,QAAO;EACL,sBAAsB,iBAAA;EACtB,iCAAiC,iBAAA;EACjC,4BAA4B,iBAAA;EAC5B,YAAY,iBAAA;EACZ,OAAO,iBAAA;EACP,uBAAuB,iBAAA;EACvB,MAAM,iBAAA;EACN,QAAQ,iBAAA;EACR,uBAAuB,iBAAA;EACvB,cAAc,iBAAA;EACf"}
@@ -1,19 +1,15 @@
1
- export declare const V19_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.97";
2
- export declare const V19_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.97";
1
+ export declare const V19_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.98";
2
+ export declare const V19_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.98";
3
3
  export declare const V19_X_MARKED = "^18.0.0";
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.97";
9
- export declare const V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.97";
10
- export declare const V19_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.97";
11
- export declare const V19_X_NX_ANGULAR = "^22.0.0";
12
- export declare const V19_X_NX_VITE = "^22.0.0";
8
+ export declare const V19_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.98";
9
+ export declare const V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.98";
10
+ export declare const V19_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.98";
13
11
  export declare const V19_X_JSDOM = "^22.0.0";
14
12
  export declare const V19_X_VITE_TSCONFIG_PATHS = "^4.2.0";
15
- export declare const V19_X_VITEST = "^3.0.0";
16
- export declare const V19_X_VITE = "^6.0.0";
17
13
  export declare const NX_X_LATEST_VITE = "^8.0.0";
18
14
  export declare const NX_X_LATEST_VITEST = "^5.0.0";
19
- export declare const NX_X_LATEST_NX = "^23.0.0";
15
+ export declare const NX_X_LATEST_NX = "^23.2.0";
@@ -1,22 +1,19 @@
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.97";
3
- var V19_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.97";
2
+ var V19_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.98";
3
+ var V19_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.98";
4
4
  var V19_X_MARKED = "^18.0.0";
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.97";
10
- var V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.97";
11
- var V19_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.97";
12
- var V19_X_NX_VITE = "^22.0.0";
9
+ var V19_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.98";
10
+ var V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.98";
11
+ var V19_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.98";
13
12
  var V19_X_JSDOM = "^22.0.0";
14
13
  var V19_X_VITE_TSCONFIG_PATHS = "^4.2.0";
15
- var V19_X_VITEST = "^3.0.0";
16
- var V19_X_VITE = "^6.0.0";
17
14
  var NX_X_LATEST_VITE = "^8.0.0";
18
15
  var NX_X_LATEST_VITEST = "^5.0.0";
19
- var NX_X_LATEST_NX = "^23.0.0";
16
+ var NX_X_LATEST_NX = "^23.2.0";
20
17
  //#endregion
21
18
  exports.NX_X_LATEST_NX = NX_X_LATEST_NX;
22
19
  exports.NX_X_LATEST_VITE = NX_X_LATEST_VITE;
@@ -31,10 +28,7 @@ exports.V19_X_MARKED = V19_X_MARKED;
31
28
  exports.V19_X_MARKED_GFM_HEADING_ID = V19_X_MARKED_GFM_HEADING_ID;
32
29
  exports.V19_X_MARKED_HIGHLIGHT = V19_X_MARKED_HIGHLIGHT;
33
30
  exports.V19_X_MARKED_MANGLE = V19_X_MARKED_MANGLE;
34
- exports.V19_X_NX_VITE = V19_X_NX_VITE;
35
31
  exports.V19_X_PRISMJS = V19_X_PRISMJS;
36
- exports.V19_X_VITE = V19_X_VITE;
37
- exports.V19_X_VITEST = V19_X_VITEST;
38
32
  exports.V19_X_VITE_TSCONFIG_PATHS = V19_X_VITE_TSCONFIG_PATHS;
39
33
 
40
34
  //# sourceMappingURL=versions.js.map
@@ -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.97';\nexport const V19_X_ANALOG_JS_CONTENT = '^3.0.0-alpha.97';\nexport const V19_X_MARKED = '^18.0.0';\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.97';\nexport const V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = '^3.0.0-alpha.97';\nexport const V19_X_ANALOG_JS_VITEST_ANGULAR = '^3.0.0-alpha.97';\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 = '^5.0.0';\nexport const NX_X_LATEST_NX = '^23.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;AAClC,IAAa,iBAAiB"}
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.98';\nexport const V19_X_ANALOG_JS_CONTENT = '^3.0.0-alpha.98';\nexport const V19_X_MARKED = '^18.0.0';\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.98';\nexport const V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = '^3.0.0-alpha.98';\nexport const V19_X_ANALOG_JS_VITEST_ANGULAR = '^3.0.0-alpha.98';\nexport const V19_X_JSDOM = '^22.0.0';\nexport const V19_X_VITE_TSCONFIG_PATHS = '^4.2.0';\nexport const NX_X_LATEST_VITE = '^8.0.0';\nexport const NX_X_LATEST_VITEST = '^5.0.0';\nexport const NX_X_LATEST_NX = '^23.2.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;AAC9C,IAAa,cAAc;AAC3B,IAAa,4BAA4B;AACzC,IAAa,mBAAmB;AAChC,IAAa,qBAAqB;AAClC,IAAa,iBAAiB"}
@@ -21,12 +21,8 @@ function createRouteFileDiscovery(options) {
21
21
  return path === dir || path.startsWith(`${dir}/`);
22
22
  }
23
23
  function getAppLocalFileKind(path) {
24
- if (path.endsWith(".md") && [
25
- "src/app/routes",
26
- "src/app/pages",
27
- "src/content"
28
- ].some((dir) => isWithinDir(path, `${root}/${dir}`))) return "content";
29
- if (path.endsWith(".ts") && ["app/routes", "src/app/routes"].some((dir) => isWithinDir(path, `${root}/${dir}`)) || path.endsWith(".page.ts") && isWithinDir(path, `${root}/src/app/pages`)) return "route";
24
+ if (path.endsWith(".md") && ["src/app/pages", "src/content"].some((dir) => isWithinDir(path, `${root}/${dir}`))) return "content";
25
+ if (path.endsWith(".page.ts") && isWithinDir(path, `${root}/src/app/pages`)) return "route";
30
26
  return null;
31
27
  }
32
28
  function getDiscoveredFileKind(path) {
@@ -39,11 +35,7 @@ function createRouteFileDiscovery(options) {
39
35
  return null;
40
36
  }
41
37
  function scanRouteFiles() {
42
- const appLocalGlobs = [
43
- `${root}/app/routes/**/*.ts`,
44
- `${root}/src/app/routes/**/*.ts`,
45
- `${root}/src/app/pages/**/*.page.ts`
46
- ];
38
+ const appLocalGlobs = [`${root}/src/app/pages/**/*.page.ts`];
47
39
  const additionalGlobs = additionalPagesDirs.map((dir) => `${joinDir(dir)}/**/*.page.ts`);
48
40
  const appLocal = globSync(appLocalGlobs, {
49
41
  dot: true,
@@ -57,11 +49,7 @@ function createRouteFileDiscovery(options) {
57
49
  return [...appLocal, ...additional];
58
50
  }
59
51
  function scanContentFiles() {
60
- const appLocalGlobs = [
61
- `${root}/src/app/routes/**/*.md`,
62
- `${root}/src/app/pages/**/*.md`,
63
- `${root}/src/content/**/*.md`
64
- ];
52
+ const appLocalGlobs = [`${root}/src/app/pages/**/*.md`, `${root}/src/content/**/*.md`];
65
53
  const additionalGlobs = additionalContentDirs.map((dir) => `${joinDir(dir)}/**/*.md`);
66
54
  const appLocal = globSync(appLocalGlobs, {
67
55
  dot: true,
@@ -1 +1 @@
1
- {"version":3,"file":"route-file-discovery.js","names":[],"sources":["../../../src/lib/route-file-discovery.ts"],"sourcesContent":["import { normalizePath } from 'vite';\nimport { resolve } from 'node:path';\nimport { globSync } from 'tinyglobby';\n\nexport interface RouteFileDiscoveryOptions {\n root: string;\n workspaceRoot: string;\n additionalPagesDirs: string[];\n additionalContentDirs: string[];\n}\n\nexport type RouteFileKind = 'route' | 'content' | 'server';\n\nexport interface RouteFileDiscovery {\n getRouteFiles(): string[];\n getContentFiles(): string[];\n /** `server` marks page `.server.ts` files, which affect `load` types only. */\n getDiscoveredFileKind(path: string): RouteFileKind | null;\n updateDiscoveredFile(path: string, event: 'add' | 'change' | 'unlink'): void;\n /**\n * Returns true if the normalized filename was discovered from an app-local\n * glob (under the configured root) rather than from an additional directory.\n *\n * Used by collision priority to prefer app-local routes over shared/external\n * routes based on actual configured roots instead of hard-coded path\n * substrings.\n */\n isAppLocal(normalizedFilename: string): boolean;\n reset(): void;\n}\n\nexport function createRouteFileDiscovery(\n options: RouteFileDiscoveryOptions,\n): RouteFileDiscovery {\n const { root, workspaceRoot, additionalPagesDirs, additionalContentDirs } =\n options;\n\n let routeFilesCache = new Set<string>();\n let contentFilesCache = new Set<string>();\n let appLocalFilesCache = new Set<string>();\n let initialized = false;\n const joinDir = (dir: string) =>\n isWithinDir(dir, workspaceRoot)\n ? normalizePath(dir)\n : dir.startsWith('/')\n ? normalizePath(`${workspaceRoot}${dir}`)\n : normalizePath(resolve(workspaceRoot, dir));\n const additionalPagesRoots = additionalPagesDirs.map(joinDir);\n const additionalContentRoots = additionalContentDirs.map(joinDir);\n\n function normalizePath_(absolutePath: string): string {\n const normalized = normalizePath(absolutePath);\n if (isWithinDir(normalized, root)) {\n return normalized.slice(root.length);\n }\n if (isWithinDir(normalized, workspaceRoot)) {\n return normalized.slice(workspaceRoot.length);\n }\n return normalized;\n }\n\n function isWithinDir(path: string, dir: string): boolean {\n return path === dir || path.startsWith(`${dir}/`);\n }\n\n function getAppLocalFileKind(path: string): 'route' | 'content' | null {\n if (\n path.endsWith('.md') &&\n ['src/app/routes', 'src/app/pages', 'src/content'].some((dir) =>\n isWithinDir(path, `${root}/${dir}`),\n )\n )\n return 'content';\n if (\n (path.endsWith('.ts') &&\n ['app/routes', 'src/app/routes'].some((dir) =>\n isWithinDir(path, `${root}/${dir}`),\n )) ||\n (path.endsWith('.page.ts') && isWithinDir(path, `${root}/src/app/pages`))\n )\n return 'route';\n return null;\n }\n\n function getDiscoveredFileKind(path: string): RouteFileKind | null {\n const normalized = normalizePath(path);\n const localKind = getAppLocalFileKind(normalized);\n if (localKind) return localKind;\n if (\n normalized.endsWith('.md') &&\n additionalContentRoots.some((dir) => isWithinDir(normalized, dir))\n ) {\n return 'content';\n }\n if (\n normalized.endsWith('.page.ts') &&\n additionalPagesRoots.some((dir) => isWithinDir(normalized, dir))\n ) {\n return 'route';\n }\n if (\n normalized.endsWith('.server.ts') &&\n [`${root}/src/app/pages`, ...additionalPagesRoots].some((dir) =>\n isWithinDir(normalized, dir),\n )\n ) {\n return 'server';\n }\n return null;\n }\n\n function scanRouteFiles(): string[] {\n const appLocalGlobs = [\n `${root}/app/routes/**/*.ts`,\n `${root}/src/app/routes/**/*.ts`,\n `${root}/src/app/pages/**/*.page.ts`,\n ];\n const additionalGlobs = additionalPagesDirs.map(\n (dir) => `${joinDir(dir)}/**/*.page.ts`,\n );\n\n const appLocal = globSync(appLocalGlobs, {\n dot: true,\n absolute: true,\n }).map(normalizePath_);\n\n const additional = globSync(additionalGlobs, {\n dot: true,\n absolute: true,\n }).map(normalizePath_);\n\n for (const f of appLocal) {\n appLocalFilesCache.add(f);\n }\n\n return [...appLocal, ...additional];\n }\n\n function scanContentFiles(): string[] {\n const appLocalGlobs = [\n `${root}/src/app/routes/**/*.md`,\n `${root}/src/app/pages/**/*.md`,\n `${root}/src/content/**/*.md`,\n ];\n const additionalGlobs = additionalContentDirs.map(\n (dir) => `${joinDir(dir)}/**/*.md`,\n );\n\n const appLocal = globSync(appLocalGlobs, {\n dot: true,\n absolute: true,\n }).map(normalizePath_);\n\n const additional = globSync(additionalGlobs, {\n dot: true,\n absolute: true,\n }).map(normalizePath_);\n\n for (const f of appLocal) {\n appLocalFilesCache.add(f);\n }\n\n return [...appLocal, ...additional];\n }\n\n function isAppLocalByPath(absolutePath: string): boolean {\n const normalized = normalizePath(absolutePath);\n return getAppLocalFileKind(normalized) !== null;\n }\n\n function ensureInitialized(): void {\n if (initialized) {\n return;\n }\n\n routeFilesCache = new Set(scanRouteFiles());\n contentFilesCache = new Set(scanContentFiles());\n initialized = true;\n }\n\n return {\n getRouteFiles(): string[] {\n ensureInitialized();\n return [...routeFilesCache].sort();\n },\n\n getContentFiles(): string[] {\n ensureInitialized();\n return [...contentFilesCache].sort();\n },\n\n getDiscoveredFileKind(path: string): RouteFileKind | null {\n return getDiscoveredFileKind(path);\n },\n\n updateDiscoveredFile(\n path: string,\n event: 'add' | 'change' | 'unlink',\n ): void {\n ensureInitialized();\n const kind = getDiscoveredFileKind(path);\n if (!kind || kind === 'server') {\n return;\n }\n\n const normalizedPath = normalizePath_(path);\n const targetCache =\n kind === 'content' ? contentFilesCache : routeFilesCache;\n\n if (event === 'unlink') {\n targetCache.delete(normalizedPath);\n appLocalFilesCache.delete(normalizedPath);\n return;\n }\n\n targetCache.add(normalizedPath);\n if (isAppLocalByPath(path)) {\n appLocalFilesCache.add(normalizedPath);\n }\n },\n\n isAppLocal(normalizedFilename: string): boolean {\n ensureInitialized();\n return appLocalFilesCache.has(normalizedFilename);\n },\n\n reset(): void {\n initialized = false;\n appLocalFilesCache = new Set<string>();\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"route-file-discovery.js","names":[],"sources":["../../../src/lib/route-file-discovery.ts"],"sourcesContent":["import { normalizePath } from 'vite';\nimport { resolve } from 'node:path';\nimport { globSync } from 'tinyglobby';\n\nexport interface RouteFileDiscoveryOptions {\n root: string;\n workspaceRoot: string;\n additionalPagesDirs: string[];\n additionalContentDirs: string[];\n}\n\nexport type RouteFileKind = 'route' | 'content' | 'server';\n\nexport interface RouteFileDiscovery {\n getRouteFiles(): string[];\n getContentFiles(): string[];\n /** `server` marks page `.server.ts` files, which affect `load` types only. */\n getDiscoveredFileKind(path: string): RouteFileKind | null;\n updateDiscoveredFile(path: string, event: 'add' | 'change' | 'unlink'): void;\n /**\n * Returns true if the normalized filename was discovered from an app-local\n * glob (under the configured root) rather than from an additional directory.\n *\n * Used by collision priority to prefer app-local routes over shared/external\n * routes based on actual configured roots instead of hard-coded path\n * substrings.\n */\n isAppLocal(normalizedFilename: string): boolean;\n reset(): void;\n}\n\nexport function createRouteFileDiscovery(\n options: RouteFileDiscoveryOptions,\n): RouteFileDiscovery {\n const { root, workspaceRoot, additionalPagesDirs, additionalContentDirs } =\n options;\n\n let routeFilesCache = new Set<string>();\n let contentFilesCache = new Set<string>();\n let appLocalFilesCache = new Set<string>();\n let initialized = false;\n const joinDir = (dir: string) =>\n isWithinDir(dir, workspaceRoot)\n ? normalizePath(dir)\n : dir.startsWith('/')\n ? normalizePath(`${workspaceRoot}${dir}`)\n : normalizePath(resolve(workspaceRoot, dir));\n const additionalPagesRoots = additionalPagesDirs.map(joinDir);\n const additionalContentRoots = additionalContentDirs.map(joinDir);\n\n function normalizePath_(absolutePath: string): string {\n const normalized = normalizePath(absolutePath);\n if (isWithinDir(normalized, root)) {\n return normalized.slice(root.length);\n }\n if (isWithinDir(normalized, workspaceRoot)) {\n return normalized.slice(workspaceRoot.length);\n }\n return normalized;\n }\n\n function isWithinDir(path: string, dir: string): boolean {\n return path === dir || path.startsWith(`${dir}/`);\n }\n\n function getAppLocalFileKind(path: string): 'route' | 'content' | null {\n if (\n path.endsWith('.md') &&\n ['src/app/pages', 'src/content'].some((dir) =>\n isWithinDir(path, `${root}/${dir}`),\n )\n )\n return 'content';\n if (path.endsWith('.page.ts') && isWithinDir(path, `${root}/src/app/pages`))\n return 'route';\n return null;\n }\n\n function getDiscoveredFileKind(path: string): RouteFileKind | null {\n const normalized = normalizePath(path);\n const localKind = getAppLocalFileKind(normalized);\n if (localKind) return localKind;\n if (\n normalized.endsWith('.md') &&\n additionalContentRoots.some((dir) => isWithinDir(normalized, dir))\n ) {\n return 'content';\n }\n if (\n normalized.endsWith('.page.ts') &&\n additionalPagesRoots.some((dir) => isWithinDir(normalized, dir))\n ) {\n return 'route';\n }\n if (\n normalized.endsWith('.server.ts') &&\n [`${root}/src/app/pages`, ...additionalPagesRoots].some((dir) =>\n isWithinDir(normalized, dir),\n )\n ) {\n return 'server';\n }\n return null;\n }\n\n function scanRouteFiles(): string[] {\n const appLocalGlobs = [`${root}/src/app/pages/**/*.page.ts`];\n const additionalGlobs = additionalPagesDirs.map(\n (dir) => `${joinDir(dir)}/**/*.page.ts`,\n );\n\n const appLocal = globSync(appLocalGlobs, {\n dot: true,\n absolute: true,\n }).map(normalizePath_);\n\n const additional = globSync(additionalGlobs, {\n dot: true,\n absolute: true,\n }).map(normalizePath_);\n\n for (const f of appLocal) {\n appLocalFilesCache.add(f);\n }\n\n return [...appLocal, ...additional];\n }\n\n function scanContentFiles(): string[] {\n const appLocalGlobs = [\n `${root}/src/app/pages/**/*.md`,\n `${root}/src/content/**/*.md`,\n ];\n const additionalGlobs = additionalContentDirs.map(\n (dir) => `${joinDir(dir)}/**/*.md`,\n );\n\n const appLocal = globSync(appLocalGlobs, {\n dot: true,\n absolute: true,\n }).map(normalizePath_);\n\n const additional = globSync(additionalGlobs, {\n dot: true,\n absolute: true,\n }).map(normalizePath_);\n\n for (const f of appLocal) {\n appLocalFilesCache.add(f);\n }\n\n return [...appLocal, ...additional];\n }\n\n function isAppLocalByPath(absolutePath: string): boolean {\n const normalized = normalizePath(absolutePath);\n return getAppLocalFileKind(normalized) !== null;\n }\n\n function ensureInitialized(): void {\n if (initialized) {\n return;\n }\n\n routeFilesCache = new Set(scanRouteFiles());\n contentFilesCache = new Set(scanContentFiles());\n initialized = true;\n }\n\n return {\n getRouteFiles(): string[] {\n ensureInitialized();\n return [...routeFilesCache].sort();\n },\n\n getContentFiles(): string[] {\n ensureInitialized();\n return [...contentFilesCache].sort();\n },\n\n getDiscoveredFileKind(path: string): RouteFileKind | null {\n return getDiscoveredFileKind(path);\n },\n\n updateDiscoveredFile(\n path: string,\n event: 'add' | 'change' | 'unlink',\n ): void {\n ensureInitialized();\n const kind = getDiscoveredFileKind(path);\n if (!kind || kind === 'server') {\n return;\n }\n\n const normalizedPath = normalizePath_(path);\n const targetCache =\n kind === 'content' ? contentFilesCache : routeFilesCache;\n\n if (event === 'unlink') {\n targetCache.delete(normalizedPath);\n appLocalFilesCache.delete(normalizedPath);\n return;\n }\n\n targetCache.add(normalizedPath);\n if (isAppLocalByPath(path)) {\n appLocalFilesCache.add(normalizedPath);\n }\n },\n\n isAppLocal(normalizedFilename: string): boolean {\n ensureInitialized();\n return appLocalFilesCache.has(normalizedFilename);\n },\n\n reset(): void {\n initialized = false;\n appLocalFilesCache = new Set<string>();\n },\n };\n}\n"],"mappings":"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"}
@@ -79,8 +79,8 @@ function isLikelyLayoutRoute(filename, routeFiles) {
79
79
  return routeFiles.some((routeFile) => routeFile !== filename && routeFile.startsWith(`${routeStem}/`));
80
80
  }
81
81
  function getRouteMetaInfo(program, bindingName) {
82
- const routeMetaNode = unwrapRouteMetaObject(getExportedBindingInitializer(program, bindingName));
83
- if (!routeMetaNode) return null;
82
+ const routeMetaNode = getExportedBindingInitializer(program, bindingName);
83
+ if (routeMetaNode?.type !== "ObjectExpression") return null;
84
84
  let redirectTo;
85
85
  for (const property of routeMetaNode.properties ?? []) {
86
86
  if (property?.type !== "Property") continue;
@@ -92,15 +92,6 @@ function getRouteMetaInfo(program, bindingName) {
92
92
  redirectTo
93
93
  };
94
94
  }
95
- function unwrapRouteMetaObject(initializer) {
96
- if (!initializer) return null;
97
- if (initializer.type === "ObjectExpression") return initializer;
98
- if (initializer.type === "CallExpression" && initializer.callee?.type === "Identifier" && initializer.callee.name === "defineRouteMeta") {
99
- const firstArgument = initializer.arguments?.[0];
100
- return firstArgument?.type === "ObjectExpression" ? firstArgument : null;
101
- }
102
- return null;
103
- }
104
95
  function getExportedBindingInitializer(program, bindingName) {
105
96
  for (const statement of program.body ?? []) {
106
97
  if (statement?.type === "VariableDeclaration") {
@@ -1 +1 @@
1
- {"version":3,"file":"route-idiom-diagnostics.js","names":[],"sources":["../../../src/lib/route-idiom-diagnostics.ts"],"sourcesContent":["import { parseSync, type OxcError, type Severity } from 'oxc-parser';\nimport { relative } from 'node:path';\nimport { normalizePath } from 'vite';\n\nexport interface AnalogRouteIdiomDiagnostic {\n code: string;\n severity: 'error' | 'warning';\n message: string;\n details?: string;\n}\n\nexport interface AnalyzeAnalogRouteFileOptions {\n filename: string;\n code: string;\n routeFiles?: string[];\n}\n\nconst PAGE_FILE_RE = /\\.page\\.ts$/;\nconst ROUTER_OUTLET_RE = /\\bRouterOutlet\\b|<router-outlet(?:\\s|>)/;\n\nexport function analyzeAnalogRouteFile(\n options: AnalyzeAnalogRouteFileOptions,\n): AnalogRouteIdiomDiagnostic[] {\n const { filename, code, routeFiles = [] } = options;\n const parseResult = parseSync(filename, code, {\n lang: 'ts',\n sourceType: 'module',\n range: true,\n showSemanticErrors: true,\n });\n\n const parseDiagnostics = parseResult.errors.map((error) =>\n toParseDiagnostic(error),\n );\n\n if (parseDiagnostics.some((diagnostic) => diagnostic.severity === 'error')) {\n return parseDiagnostics;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const program: any = parseResult.program;\n const exportedBindings = collectExportedBindings(program);\n const routeMetaBindingName = exportedBindings.routeMeta;\n const routeJsonLdBindingName = exportedBindings.routeJsonLd;\n const diagnostics = [...parseDiagnostics];\n\n const routeMetaInfo = routeMetaBindingName\n ? getRouteMetaInfo(program, routeMetaBindingName)\n : null;\n\n if (!exportedBindings.hasDefaultExport && !routeMetaInfo?.hasRedirect) {\n diagnostics.push({\n code: 'missing-default-export',\n severity: 'warning',\n message:\n 'Route files should default-export the page component, unless they are redirect-only routes.',\n details:\n 'Add `export default class ...` or define `routeMeta.redirectTo` for a redirect route.',\n });\n }\n\n if (exportedBindings.hasDefaultExport && routeMetaInfo?.hasRedirect) {\n diagnostics.push({\n code: 'redirect-with-component',\n severity: 'warning',\n message: 'Redirect routes should not also export a page component.',\n details:\n 'Analog ignores the default export when `routeMeta.redirectTo` is present. Remove the component export or remove the redirect.',\n });\n }\n\n if (\n routeMetaInfo?.hasRedirect &&\n routeMetaInfo.redirectTo &&\n !routeMetaInfo.redirectTo.startsWith('/')\n ) {\n diagnostics.push({\n code: 'relative-redirect',\n severity: 'warning',\n message: '`routeMeta.redirectTo` should use an absolute path.',\n details:\n 'Nested redirects are documented to use absolute targets such as `/cities/new-york`.',\n });\n }\n\n if (routeJsonLdBindingName) {\n diagnostics.push({\n code: 'legacy-route-jsonld-export',\n severity: 'warning',\n message:\n 'Prefer `routeMeta.jsonLd` over the legacy top-level `routeJsonLd` export.',\n details:\n 'Keeping JSON-LD inside `routeMeta` makes the route module easier to read and matches the current docs.',\n });\n }\n\n if (\n isLikelyLayoutRoute(filename, routeFiles) &&\n !ROUTER_OUTLET_RE.test(code)\n ) {\n diagnostics.push({\n code: 'layout-without-router-outlet',\n severity: 'warning',\n message:\n 'This route file looks like a layout shell, but it does not reference `RouterOutlet` or `<router-outlet>`.',\n details:\n 'Parent layout pages usually import `RouterOutlet` and render an outlet so child routes have somewhere to mount.',\n });\n }\n\n return diagnostics;\n}\n\nexport function formatAnalogRouteIdiomDiagnostic(\n diagnostic: AnalogRouteIdiomDiagnostic,\n filename: string,\n workspaceRoot: string,\n): string {\n const displayName = toDisplayPath(filename, workspaceRoot);\n const header = `[Analog] ${displayName} (${diagnostic.code})`;\n const severity = diagnostic.severity.toUpperCase();\n\n if (diagnostic.details) {\n return `${header}\\n${severity}: ${diagnostic.message}\\n${diagnostic.details}`;\n }\n\n return `${header}\\n${severity}: ${diagnostic.message}`;\n}\n\nfunction toDisplayPath(filename: string, workspaceRoot: string): string {\n const normalizedFilename = normalizePath(filename);\n const normalizedRoot = normalizePath(workspaceRoot);\n const relativePath = normalizePath(\n relative(normalizedRoot, normalizedFilename),\n );\n\n if (relativePath && !relativePath.startsWith('..')) {\n return `/${relativePath}`;\n }\n\n return normalizedFilename;\n}\n\nfunction toParseDiagnostic(error: OxcError): AnalogRouteIdiomDiagnostic {\n return {\n code: 'oxc-parse',\n severity: error.severity === severityError ? 'error' : 'warning',\n message: error.message,\n details: error.codeframe ?? error.helpMessage ?? undefined,\n };\n}\n\nconst severityError: Severity = 'Error';\n\nfunction isLikelyLayoutRoute(filename: string, routeFiles: string[]): boolean {\n if (!PAGE_FILE_RE.test(filename)) {\n return false;\n }\n\n const routeStem = filename.replace(PAGE_FILE_RE, '');\n return routeFiles.some(\n (routeFile) =>\n routeFile !== filename && routeFile.startsWith(`${routeStem}/`),\n );\n}\n\nfunction getRouteMetaInfo(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n program: any,\n bindingName: string,\n): {\n hasRedirect: boolean;\n redirectTo?: string;\n} | null {\n const initializer = getExportedBindingInitializer(program, bindingName);\n const routeMetaNode = unwrapRouteMetaObject(initializer);\n\n if (!routeMetaNode) {\n return null;\n }\n\n let redirectTo: string | undefined;\n\n for (const property of routeMetaNode.properties ?? []) {\n if (property?.type !== 'Property') {\n continue;\n }\n\n const keyName = getPropertyName(property.key);\n if (keyName !== 'redirectTo') {\n continue;\n }\n\n redirectTo = getStringValue(property.value);\n }\n\n return {\n hasRedirect: typeof redirectTo === 'string' && redirectTo.length > 0,\n redirectTo,\n };\n}\n\nfunction unwrapRouteMetaObject(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n initializer: any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): any | null {\n if (!initializer) {\n return null;\n }\n\n if (initializer.type === 'ObjectExpression') {\n return initializer;\n }\n\n if (\n initializer.type === 'CallExpression' &&\n initializer.callee?.type === 'Identifier' &&\n initializer.callee.name === 'defineRouteMeta'\n ) {\n const firstArgument = initializer.arguments?.[0];\n return firstArgument?.type === 'ObjectExpression' ? firstArgument : null;\n }\n\n return null;\n}\n\nfunction getExportedBindingInitializer(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n program: any,\n bindingName: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): any | undefined {\n for (const statement of program.body ?? []) {\n if (statement?.type === 'VariableDeclaration') {\n const initializer = getVariableInitializer(statement, bindingName);\n if (initializer) {\n return initializer;\n }\n continue;\n }\n\n if (\n statement?.type === 'ExportNamedDeclaration' &&\n statement.declaration?.type === 'VariableDeclaration'\n ) {\n const initializer = getVariableInitializer(\n statement.declaration,\n bindingName,\n );\n if (initializer) {\n return initializer;\n }\n }\n }\n\n return undefined;\n}\n\nfunction getVariableInitializer(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n declaration: any,\n bindingName: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): any | undefined {\n for (const declarator of declaration.declarations ?? []) {\n if (\n declarator.id?.type === 'Identifier' &&\n declarator.id.name === bindingName\n ) {\n return declarator.init;\n }\n }\n\n return undefined;\n}\n\nfunction collectExportedBindings(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n program: any,\n): {\n hasDefaultExport: boolean;\n routeMeta?: string;\n routeJsonLd?: string;\n} {\n let hasDefaultExport = false;\n let routeMeta: string | undefined;\n let routeJsonLd: string | undefined;\n\n for (const statement of program.body ?? []) {\n if (statement?.type === 'ExportDefaultDeclaration') {\n hasDefaultExport = true;\n continue;\n }\n\n if (statement?.type !== 'ExportNamedDeclaration') {\n continue;\n }\n\n const declaration = statement.declaration;\n if (declaration?.type === 'VariableDeclaration') {\n for (const declarator of declaration.declarations ?? []) {\n if (declarator.id?.type !== 'Identifier') {\n continue;\n }\n\n if (declarator.id.name === 'routeMeta') {\n routeMeta = 'routeMeta';\n }\n\n if (declarator.id.name === 'routeJsonLd') {\n routeJsonLd = 'routeJsonLd';\n }\n }\n }\n\n for (const specifier of statement.specifiers ?? []) {\n if (\n specifier?.type !== 'ExportSpecifier' ||\n specifier.local?.type !== 'Identifier'\n ) {\n continue;\n }\n\n if (specifier.exported?.type !== 'Identifier') {\n continue;\n }\n\n if (specifier.exported.name === 'routeMeta') {\n routeMeta = specifier.local.name;\n }\n\n if (specifier.exported.name === 'routeJsonLd') {\n routeJsonLd = specifier.local.name;\n }\n }\n }\n\n return {\n hasDefaultExport,\n routeMeta,\n routeJsonLd,\n };\n}\n\nfunction getPropertyName(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n node: any,\n): string | undefined {\n if (node?.type === 'Identifier') {\n return node.name;\n }\n\n if (\n (node?.type === 'Literal' || node?.type === 'StringLiteral') &&\n typeof node.value === 'string'\n ) {\n return node.value;\n }\n\n return undefined;\n}\n\nfunction getStringValue(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n node: any,\n): string | undefined {\n if (\n (node?.type === 'Literal' || node?.type === 'StringLiteral') &&\n typeof node.value === 'string'\n ) {\n return node.value;\n }\n\n if (\n node?.type === 'TemplateLiteral' &&\n node.expressions?.length === 0 &&\n node.quasis?.length === 1\n ) {\n return node.quasis[0].value.cooked ?? node.quasis[0].value.raw;\n }\n\n return undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"route-idiom-diagnostics.js","names":[],"sources":["../../../src/lib/route-idiom-diagnostics.ts"],"sourcesContent":["import { parseSync, type OxcError, type Severity } from 'oxc-parser';\nimport { relative } from 'node:path';\nimport { normalizePath } from 'vite';\n\nexport interface AnalogRouteIdiomDiagnostic {\n code: string;\n severity: 'error' | 'warning';\n message: string;\n details?: string;\n}\n\nexport interface AnalyzeAnalogRouteFileOptions {\n filename: string;\n code: string;\n routeFiles?: string[];\n}\n\nconst PAGE_FILE_RE = /\\.page\\.ts$/;\nconst ROUTER_OUTLET_RE = /\\bRouterOutlet\\b|<router-outlet(?:\\s|>)/;\n\nexport function analyzeAnalogRouteFile(\n options: AnalyzeAnalogRouteFileOptions,\n): AnalogRouteIdiomDiagnostic[] {\n const { filename, code, routeFiles = [] } = options;\n const parseResult = parseSync(filename, code, {\n lang: 'ts',\n sourceType: 'module',\n range: true,\n showSemanticErrors: true,\n });\n\n const parseDiagnostics = parseResult.errors.map((error) =>\n toParseDiagnostic(error),\n );\n\n if (parseDiagnostics.some((diagnostic) => diagnostic.severity === 'error')) {\n return parseDiagnostics;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const program: any = parseResult.program;\n const exportedBindings = collectExportedBindings(program);\n const routeMetaBindingName = exportedBindings.routeMeta;\n const routeJsonLdBindingName = exportedBindings.routeJsonLd;\n const diagnostics = [...parseDiagnostics];\n\n const routeMetaInfo = routeMetaBindingName\n ? getRouteMetaInfo(program, routeMetaBindingName)\n : null;\n\n if (!exportedBindings.hasDefaultExport && !routeMetaInfo?.hasRedirect) {\n diagnostics.push({\n code: 'missing-default-export',\n severity: 'warning',\n message:\n 'Route files should default-export the page component, unless they are redirect-only routes.',\n details:\n 'Add `export default class ...` or define `routeMeta.redirectTo` for a redirect route.',\n });\n }\n\n if (exportedBindings.hasDefaultExport && routeMetaInfo?.hasRedirect) {\n diagnostics.push({\n code: 'redirect-with-component',\n severity: 'warning',\n message: 'Redirect routes should not also export a page component.',\n details:\n 'Analog ignores the default export when `routeMeta.redirectTo` is present. Remove the component export or remove the redirect.',\n });\n }\n\n if (\n routeMetaInfo?.hasRedirect &&\n routeMetaInfo.redirectTo &&\n !routeMetaInfo.redirectTo.startsWith('/')\n ) {\n diagnostics.push({\n code: 'relative-redirect',\n severity: 'warning',\n message: '`routeMeta.redirectTo` should use an absolute path.',\n details:\n 'Nested redirects are documented to use absolute targets such as `/cities/new-york`.',\n });\n }\n\n if (routeJsonLdBindingName) {\n diagnostics.push({\n code: 'legacy-route-jsonld-export',\n severity: 'warning',\n message:\n 'Prefer `routeMeta.jsonLd` over the legacy top-level `routeJsonLd` export.',\n details:\n 'Keeping JSON-LD inside `routeMeta` makes the route module easier to read and matches the current docs.',\n });\n }\n\n if (\n isLikelyLayoutRoute(filename, routeFiles) &&\n !ROUTER_OUTLET_RE.test(code)\n ) {\n diagnostics.push({\n code: 'layout-without-router-outlet',\n severity: 'warning',\n message:\n 'This route file looks like a layout shell, but it does not reference `RouterOutlet` or `<router-outlet>`.',\n details:\n 'Parent layout pages usually import `RouterOutlet` and render an outlet so child routes have somewhere to mount.',\n });\n }\n\n return diagnostics;\n}\n\nexport function formatAnalogRouteIdiomDiagnostic(\n diagnostic: AnalogRouteIdiomDiagnostic,\n filename: string,\n workspaceRoot: string,\n): string {\n const displayName = toDisplayPath(filename, workspaceRoot);\n const header = `[Analog] ${displayName} (${diagnostic.code})`;\n const severity = diagnostic.severity.toUpperCase();\n\n if (diagnostic.details) {\n return `${header}\\n${severity}: ${diagnostic.message}\\n${diagnostic.details}`;\n }\n\n return `${header}\\n${severity}: ${diagnostic.message}`;\n}\n\nfunction toDisplayPath(filename: string, workspaceRoot: string): string {\n const normalizedFilename = normalizePath(filename);\n const normalizedRoot = normalizePath(workspaceRoot);\n const relativePath = normalizePath(\n relative(normalizedRoot, normalizedFilename),\n );\n\n if (relativePath && !relativePath.startsWith('..')) {\n return `/${relativePath}`;\n }\n\n return normalizedFilename;\n}\n\nfunction toParseDiagnostic(error: OxcError): AnalogRouteIdiomDiagnostic {\n return {\n code: 'oxc-parse',\n severity: error.severity === severityError ? 'error' : 'warning',\n message: error.message,\n details: error.codeframe ?? error.helpMessage ?? undefined,\n };\n}\n\nconst severityError: Severity = 'Error';\n\nfunction isLikelyLayoutRoute(filename: string, routeFiles: string[]): boolean {\n if (!PAGE_FILE_RE.test(filename)) {\n return false;\n }\n\n const routeStem = filename.replace(PAGE_FILE_RE, '');\n return routeFiles.some(\n (routeFile) =>\n routeFile !== filename && routeFile.startsWith(`${routeStem}/`),\n );\n}\n\nfunction getRouteMetaInfo(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n program: any,\n bindingName: string,\n): {\n hasRedirect: boolean;\n redirectTo?: string;\n} | null {\n const routeMetaNode = getExportedBindingInitializer(program, bindingName);\n\n if (routeMetaNode?.type !== 'ObjectExpression') {\n return null;\n }\n\n let redirectTo: string | undefined;\n\n for (const property of routeMetaNode.properties ?? []) {\n if (property?.type !== 'Property') {\n continue;\n }\n\n const keyName = getPropertyName(property.key);\n if (keyName !== 'redirectTo') {\n continue;\n }\n\n redirectTo = getStringValue(property.value);\n }\n\n return {\n hasRedirect: typeof redirectTo === 'string' && redirectTo.length > 0,\n redirectTo,\n };\n}\n\nfunction getExportedBindingInitializer(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n program: any,\n bindingName: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): any | undefined {\n for (const statement of program.body ?? []) {\n if (statement?.type === 'VariableDeclaration') {\n const initializer = getVariableInitializer(statement, bindingName);\n if (initializer) {\n return initializer;\n }\n continue;\n }\n\n if (\n statement?.type === 'ExportNamedDeclaration' &&\n statement.declaration?.type === 'VariableDeclaration'\n ) {\n const initializer = getVariableInitializer(\n statement.declaration,\n bindingName,\n );\n if (initializer) {\n return initializer;\n }\n }\n }\n\n return undefined;\n}\n\nfunction getVariableInitializer(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n declaration: any,\n bindingName: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): any | undefined {\n for (const declarator of declaration.declarations ?? []) {\n if (\n declarator.id?.type === 'Identifier' &&\n declarator.id.name === bindingName\n ) {\n return declarator.init;\n }\n }\n\n return undefined;\n}\n\nfunction collectExportedBindings(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n program: any,\n): {\n hasDefaultExport: boolean;\n routeMeta?: string;\n routeJsonLd?: string;\n} {\n let hasDefaultExport = false;\n let routeMeta: string | undefined;\n let routeJsonLd: string | undefined;\n\n for (const statement of program.body ?? []) {\n if (statement?.type === 'ExportDefaultDeclaration') {\n hasDefaultExport = true;\n continue;\n }\n\n if (statement?.type !== 'ExportNamedDeclaration') {\n continue;\n }\n\n const declaration = statement.declaration;\n if (declaration?.type === 'VariableDeclaration') {\n for (const declarator of declaration.declarations ?? []) {\n if (declarator.id?.type !== 'Identifier') {\n continue;\n }\n\n if (declarator.id.name === 'routeMeta') {\n routeMeta = 'routeMeta';\n }\n\n if (declarator.id.name === 'routeJsonLd') {\n routeJsonLd = 'routeJsonLd';\n }\n }\n }\n\n for (const specifier of statement.specifiers ?? []) {\n if (\n specifier?.type !== 'ExportSpecifier' ||\n specifier.local?.type !== 'Identifier'\n ) {\n continue;\n }\n\n if (specifier.exported?.type !== 'Identifier') {\n continue;\n }\n\n if (specifier.exported.name === 'routeMeta') {\n routeMeta = specifier.local.name;\n }\n\n if (specifier.exported.name === 'routeJsonLd') {\n routeJsonLd = specifier.local.name;\n }\n }\n }\n\n return {\n hasDefaultExport,\n routeMeta,\n routeJsonLd,\n };\n}\n\nfunction getPropertyName(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n node: any,\n): string | undefined {\n if (node?.type === 'Identifier') {\n return node.name;\n }\n\n if (\n (node?.type === 'Literal' || node?.type === 'StringLiteral') &&\n typeof node.value === 'string'\n ) {\n return node.value;\n }\n\n return undefined;\n}\n\nfunction getStringValue(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n node: any,\n): string | undefined {\n if (\n (node?.type === 'Literal' || node?.type === 'StringLiteral') &&\n typeof node.value === 'string'\n ) {\n return node.value;\n }\n\n if (\n node?.type === 'TemplateLiteral' &&\n node.expressions?.length === 0 &&\n node.quasis?.length === 1\n ) {\n return node.quasis[0].value.cooked ?? node.quasis[0].value.raw;\n }\n\n return undefined;\n}\n"],"mappings":"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"}
@@ -63,16 +63,16 @@ export interface RouteManifest {
63
63
  * up to and including the first /routes/, /pages/, or /content/ segment.
64
64
  * Handles app-local paths (`/src/app/pages/`) AND additional dirs
65
65
  * (`/libs/shared/feature/src/content/`) uniformly.
66
- * 2. `[\\/](?:app[\\/](?:routes|pages)|src[\\/]content)[\\/]` — non-anchored
67
- * fallback for legacy paths where the directory marker appears mid-string.
66
+ * 2. `[\\/](?:app[\\/]pages|src[\\/]content)[\\/]` — non-anchored
67
+ * fallback for paths where the directory marker appears mid-string.
68
68
  * 3. `\.page\.(js|ts)$` — strips page file extensions.
69
69
  * 4. `\.(ts|md)$` — strips remaining file extensions.
70
70
  *
71
71
  * Examples:
72
- * - '/app/routes/index.ts' -> '/'
73
- * - '/app/routes/about.ts' -> '/about'
72
+ * - '/src/app/pages/index.page.ts' -> '/'
73
+ * - '/src/app/pages/about.page.ts' -> '/about'
74
74
  * - '/src/app/pages/users/[id].page.ts' -> '/users/[id]'
75
- * - '/app/routes/blog.[slug].ts' -> '/blog/[slug]'
75
+ * - '/src/app/pages/blog.[slug].page.ts' -> '/blog/[slug]'
76
76
  * - '/src/app/pages/(auth)/login.page.ts' -> '/login'
77
77
  * - '/src/app/pages/docs/[...slug].page.ts' -> '/docs/[...slug]'
78
78
  * - '/src/app/pages/shop/[[...category]].page.ts' -> '/shop/[[...category]]'
@@ -11,23 +11,23 @@
11
11
  * up to and including the first /routes/, /pages/, or /content/ segment.
12
12
  * Handles app-local paths (`/src/app/pages/`) AND additional dirs
13
13
  * (`/libs/shared/feature/src/content/`) uniformly.
14
- * 2. `[\\/](?:app[\\/](?:routes|pages)|src[\\/]content)[\\/]` — non-anchored
15
- * fallback for legacy paths where the directory marker appears mid-string.
14
+ * 2. `[\\/](?:app[\\/]pages|src[\\/]content)[\\/]` — non-anchored
15
+ * fallback for paths where the directory marker appears mid-string.
16
16
  * 3. `\.page\.(js|ts)$` — strips page file extensions.
17
17
  * 4. `\.(ts|md)$` — strips remaining file extensions.
18
18
  *
19
19
  * Examples:
20
- * - '/app/routes/index.ts' -> '/'
21
- * - '/app/routes/about.ts' -> '/about'
20
+ * - '/src/app/pages/index.page.ts' -> '/'
21
+ * - '/src/app/pages/about.page.ts' -> '/about'
22
22
  * - '/src/app/pages/users/[id].page.ts' -> '/users/[id]'
23
- * - '/app/routes/blog.[slug].ts' -> '/blog/[slug]'
23
+ * - '/src/app/pages/blog.[slug].page.ts' -> '/blog/[slug]'
24
24
  * - '/src/app/pages/(auth)/login.page.ts' -> '/login'
25
25
  * - '/src/app/pages/docs/[...slug].page.ts' -> '/docs/[...slug]'
26
26
  * - '/src/app/pages/shop/[[...category]].page.ts' -> '/shop/[[...category]]'
27
27
  * - '/libs/shared/feature/src/content/test.md' -> '/test'
28
28
  */
29
29
  function filenameToRoutePath(filename) {
30
- let path = filename.replace(/^(?:[a-zA-Z]:[\\/])?(.*?)[\\/](?:routes|pages|content)[\\/]|(?:[\\/](?:app[\\/](?:routes|pages)|src[\\/]content)[\\/])|(\.page\.(js|ts)$)|(\.(ts|md)$)/g, "");
30
+ let path = filename.replace(/^(?:[a-zA-Z]:[\\/])?(.*?)[\\/](?:routes|pages|content)[\\/]|(?:[\\/](?:app[\\/]pages|src[\\/]content)[\\/])|(\.page\.(js|ts)$)|(\.(ts|md)$)/g, "");
31
31
  const brackets = [];
32
32
  path = path.replace(/\[\[?\.{0,3}[^\]]*\]?\]/g, (match) => {
33
33
  brackets.push(match);
@@ -48,7 +48,7 @@ function filenameToRoutePath(filename) {
48
48
  * changes to the regex must be kept in sync between both functions.
49
49
  */
50
50
  function filenameToRouteId(filename) {
51
- let path = filename.replace(/^(?:[a-zA-Z]:[\\/])?(.*?)[\\/](?:routes|pages|content)[\\/]|(?:[\\/](?:app[\\/](?:routes|pages)|src[\\/]content)[\\/])|(\.page\.(js|ts)$)|(\.(ts|md)$)/g, "");
51
+ let path = filename.replace(/^(?:[a-zA-Z]:[\\/])?(.*?)[\\/](?:routes|pages|content)[\\/]|(?:[\\/](?:app[\\/]pages|src[\\/]content)[\\/])|(\.page\.(js|ts)$)|(\.(ts|md)$)/g, "");
52
52
  const brackets = [];
53
53
  path = path.replace(/\(.*?\)|\[\[?\.{0,3}[^\]]*\]?\]/g, (match) => {
54
54
  brackets.push(match);
@@ -213,7 +213,7 @@ function getRouteWeight(path) {
213
213
  return 0;
214
214
  }
215
215
  function getCollisionPriority(filename) {
216
- if (filename.includes("/src/app/pages/") || filename.includes("/src/app/routes/") || filename.includes("/app/pages/") || filename.includes("/app/routes/") || filename.includes("/src/content/")) return 0;
216
+ if (filename.includes("/src/app/pages/") || filename.includes("/app/pages/") || filename.includes("/src/content/")) return 0;
217
217
  return 1;
218
218
  }
219
219
  /**
@@ -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 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 /** 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 /** Layout files that wrap this route, outermost first */\n layouts: string[];\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 /** Full paths defined by more than one file, such as a layout and its index page. */\n sharedFullPaths: Set<string>;\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)$` — strips page file extensions.\n * 4. `\\.(ts|md)$` — 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)$)|(\\.(ts|md)$)/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 // Match the runtime router: strip index segments and groups before dots.\n path = path\n .replace(/\\(.*?\\)/g, '')\n .replace(/(^|[./])index(?=[./]|$)/g, '$1')\n .replace(/\\./g, '/');\n // eslint-disable-next-line no-control-regex\n path = path.replace(/\\0B(\\d+)\\0/g, (_, idx) => brackets[Number(idx)]);\n return '/' + path.split('/').filter(Boolean).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)$)|(\\.(ts|md)$)/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 * 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 collisionPriority?: (filename: string) => number,\n): RouteManifest {\n const routes: RouteEntry[] = [];\n const collisions: RouteCollision[] = [];\n const sharedFullPaths = new Set<string>();\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 // Layout routes are a file and a folder with the same name, so users.page.ts\n // wraps users/[id].page.ts but not users.[id].page.ts. Folders from all\n // pages dirs share one tree.\n const layoutByKey = new Map<string, string>();\n for (const filename of prioritizedFilenames) {\n const key = toRouteFileKey(filename);\n if (!filename.endsWith('.md') && !layoutByKey.has(key)) {\n layoutByKey.set(key, filename);\n }\n }\n\n for (const filename of prioritizedFilenames) {\n const fullPath = filenameToRoutePath(filename);\n const params = extractRouteParams(fullPath);\n const id = filenameToRouteId(filename);\n const isPathlessLayout = isPathlessLayoutId(id);\n // Different pathless branches can select the same URL with canMatch guards.\n const groupScope = id.slice(0, id.lastIndexOf(')') + 1);\n const collisionKey = JSON.stringify([fullPath, groupScope]);\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(collisionKey)) {\n const winner = seenByFullPath.get(collisionKey)!;\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)$/;\n const layoutRe = /\\.(page\\.)?(ts|js)$/;\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 sharedFullPaths.add(fullPath);\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(collisionKey, { filename, priority: currentPriority });\n }\n\n const segments = toRouteFileKey(filename).split('/');\n const layouts: string[] = [];\n for (let i = 1; i < segments.length; i++) {\n const layout = layoutByKey.get(segments.slice(0, i).join('/'));\n if (layout) layouts.push(layout);\n }\n\n routes.push({\n id,\n path: fullPath,\n fullPath,\n params,\n filename,\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 layouts,\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 if (routeByFullPath.get(route.fullPath) !== route) {\n sharedFullPaths.add(route.fullPath);\n }\n }\n\n return {\n routes,\n collisions,\n canonicalByFullPath: routeByFullPath,\n sharedFullPaths,\n };\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/** A route file's path within its pages directory, e.g. `users/[id]`. */\nfunction toRouteFileKey(filename: string): string {\n return filename.replace(\n /^.*?[\\\\/](?:routes|pages|content)[\\\\/]|\\.page\\.(?:js|ts)$|\\.(?:ts|md)$/g,\n '',\n );\n}\n\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/** Import specifiers for a page module's typed exports. */\nexport interface RouteModuleImports {\n routeMeta?: string;\n load?: string;\n}\n\n/**\n * Generates the route-table section for the combined generated route module.\n *\n * `resolveModuleImports` adds `routeMeta` and `load` types for pages whose\n * URL is defined by a single file, along with the `routeMeta` of the layouts\n * that wrap them. URLs shared by several files, such as a layout and its\n * index page, are ambiguous and only get params and query types.\n */\nexport function generateRouteTableDeclaration(\n manifest: RouteManifest,\n resolveModuleImports?: (filename: string) => RouteModuleImports | undefined,\n): string {\n const lines: string[] = [];\n lines.push('/** @noprettier */');\n lines.push('// This file is auto-generated by @analogjs/platform');\n lines.push(\"import type {} from '@analogjs/router';\");\n lines.push(\"declare module '@analogjs/router' {\");\n lines.push(' interface AnalogRouteTable {');\n\n const importType = (specifier: string, name: string) =>\n `typeof import(${JSON.stringify(specifier)}).${name}`;\n\n for (const route of manifest.canonicalByFullPath.values()) {\n const paramsType = generateParamsType(route.params);\n const queryType = 'Record<string, string | string[] | undefined>';\n\n lines.push(` ${JSON.stringify(route.fullPath)}: {`);\n lines.push(` params: ${paramsType};`);\n lines.push(` query: ${queryType};`);\n if (\n resolveModuleImports &&\n route.kind === 'page' &&\n !manifest.sharedFullPaths.has(route.fullPath)\n ) {\n const layoutRouteMeta = route.layouts.flatMap((layout) => {\n const specifier = resolveModuleImports(layout)?.routeMeta;\n return specifier ? [importType(specifier, 'routeMeta')] : [];\n });\n const imports = resolveModuleImports(route.filename);\n if (layoutRouteMeta.length) {\n lines.push(` layoutRouteMeta: [${layoutRouteMeta.join(', ')}];`);\n }\n if (imports?.routeMeta) {\n lines.push(\n ` routeMeta: ${importType(imports.routeMeta, 'routeMeta')};`,\n );\n }\n if (imports?.load) {\n lines.push(` load: ${importType(imports.load, 'load')};`);\n }\n }\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\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 : JSON.stringify(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"],"mappings":"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"}
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 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 /** 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 /** Layout files that wrap this route, outermost first */\n layouts: string[];\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 /** Full paths defined by more than one file, such as a layout and its index page. */\n sharedFullPaths: Set<string>;\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[\\\\/]pages|src[\\\\/]content)[\\\\/]` — non-anchored\n * fallback for paths where the directory marker appears mid-string.\n * 3. `\\.page\\.(js|ts)$` — strips page file extensions.\n * 4. `\\.(ts|md)$` — strips remaining file extensions.\n *\n * Examples:\n * - '/src/app/pages/index.page.ts' -> '/'\n * - '/src/app/pages/about.page.ts' -> '/about'\n * - '/src/app/pages/users/[id].page.ts' -> '/users/[id]'\n * - '/src/app/pages/blog.[slug].page.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[\\\\/]pages|src[\\\\/]content)[\\\\/])|(\\.page\\.(js|ts)$)|(\\.(ts|md)$)/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 // Match the runtime router: strip index segments and groups before dots.\n path = path\n .replace(/\\(.*?\\)/g, '')\n .replace(/(^|[./])index(?=[./]|$)/g, '$1')\n .replace(/\\./g, '/');\n // eslint-disable-next-line no-control-regex\n path = path.replace(/\\0B(\\d+)\\0/g, (_, idx) => brackets[Number(idx)]);\n return '/' + path.split('/').filter(Boolean).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[\\\\/]pages|src[\\\\/]content)[\\\\/])|(\\.page\\.(js|ts)$)|(\\.(ts|md)$)/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 * 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 collisionPriority?: (filename: string) => number,\n): RouteManifest {\n const routes: RouteEntry[] = [];\n const collisions: RouteCollision[] = [];\n const sharedFullPaths = new Set<string>();\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 // Layout routes are a file and a folder with the same name, so users.page.ts\n // wraps users/[id].page.ts but not users.[id].page.ts. Folders from all\n // pages dirs share one tree.\n const layoutByKey = new Map<string, string>();\n for (const filename of prioritizedFilenames) {\n const key = toRouteFileKey(filename);\n if (!filename.endsWith('.md') && !layoutByKey.has(key)) {\n layoutByKey.set(key, filename);\n }\n }\n\n for (const filename of prioritizedFilenames) {\n const fullPath = filenameToRoutePath(filename);\n const params = extractRouteParams(fullPath);\n const id = filenameToRouteId(filename);\n const isPathlessLayout = isPathlessLayoutId(id);\n // Different pathless branches can select the same URL with canMatch guards.\n const groupScope = id.slice(0, id.lastIndexOf(')') + 1);\n const collisionKey = JSON.stringify([fullPath, groupScope]);\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(collisionKey)) {\n const winner = seenByFullPath.get(collisionKey)!;\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)$/;\n const layoutRe = /\\.(page\\.)?(ts|js)$/;\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 sharedFullPaths.add(fullPath);\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(collisionKey, { filename, priority: currentPriority });\n }\n\n const segments = toRouteFileKey(filename).split('/');\n const layouts: string[] = [];\n for (let i = 1; i < segments.length; i++) {\n const layout = layoutByKey.get(segments.slice(0, i).join('/'));\n if (layout) layouts.push(layout);\n }\n\n routes.push({\n id,\n path: fullPath,\n fullPath,\n params,\n filename,\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 layouts,\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 if (routeByFullPath.get(route.fullPath) !== route) {\n sharedFullPaths.add(route.fullPath);\n }\n }\n\n return {\n routes,\n collisions,\n canonicalByFullPath: routeByFullPath,\n sharedFullPaths,\n };\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/** A route file's path within its pages directory, e.g. `users/[id]`. */\nfunction toRouteFileKey(filename: string): string {\n return filename.replace(\n /^.*?[\\\\/](?:routes|pages|content)[\\\\/]|\\.page\\.(?:js|ts)$|\\.(?:ts|md)$/g,\n '',\n );\n}\n\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('/app/pages/') ||\n filename.includes('/src/content/')\n ) {\n return 0;\n }\n\n return 1;\n}\n\n/** Import specifiers for a page module's typed exports. */\nexport interface RouteModuleImports {\n routeMeta?: string;\n load?: string;\n}\n\n/**\n * Generates the route-table section for the combined generated route module.\n *\n * `resolveModuleImports` adds `routeMeta` and `load` types for pages whose\n * URL is defined by a single file, along with the `routeMeta` of the layouts\n * that wrap them. URLs shared by several files, such as a layout and its\n * index page, are ambiguous and only get params and query types.\n */\nexport function generateRouteTableDeclaration(\n manifest: RouteManifest,\n resolveModuleImports?: (filename: string) => RouteModuleImports | undefined,\n): string {\n const lines: string[] = [];\n lines.push('/** @noprettier */');\n lines.push('// This file is auto-generated by @analogjs/platform');\n lines.push(\"import type {} from '@analogjs/router';\");\n lines.push(\"declare module '@analogjs/router' {\");\n lines.push(' interface AnalogRouteTable {');\n\n const importType = (specifier: string, name: string) =>\n `typeof import(${JSON.stringify(specifier)}).${name}`;\n\n for (const route of manifest.canonicalByFullPath.values()) {\n const paramsType = generateParamsType(route.params);\n const queryType = 'Record<string, string | string[] | undefined>';\n\n lines.push(` ${JSON.stringify(route.fullPath)}: {`);\n lines.push(` params: ${paramsType};`);\n lines.push(` query: ${queryType};`);\n if (\n resolveModuleImports &&\n route.kind === 'page' &&\n !manifest.sharedFullPaths.has(route.fullPath)\n ) {\n const layoutRouteMeta = route.layouts.flatMap((layout) => {\n const specifier = resolveModuleImports(layout)?.routeMeta;\n return specifier ? [importType(specifier, 'routeMeta')] : [];\n });\n const imports = resolveModuleImports(route.filename);\n if (layoutRouteMeta.length) {\n lines.push(` layoutRouteMeta: [${layoutRouteMeta.join(', ')}];`);\n }\n if (imports?.routeMeta) {\n lines.push(\n ` routeMeta: ${importType(imports.routeMeta, 'routeMeta')};`,\n );\n }\n if (imports?.load) {\n lines.push(` load: ${importType(imports.load, 'load')};`);\n }\n }\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\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 : JSON.stringify(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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqFA,SAAgB,oBAAoB,UAA0B;CAC5D,IAAI,OAAO,SAAS,QAClB,gJACA,GACD;CAED,MAAM,WAAqB,EAAE;AAC7B,QAAO,KAAK,QAAQ,6BAA6B,UAAU;AACzD,WAAS,KAAK,MAAM;AAEpB,SAAO,MAAM,SAAS,SAAS,EAAE;GACjC;AAEF,QAAO,KACJ,QAAQ,YAAY,GAAG,CACvB,QAAQ,4BAA4B,KAAK,CACzC,QAAQ,OAAO,IAAI;AAEtB,QAAO,KAAK,QAAQ,gBAAgB,GAAG,QAAQ,SAAS,OAAO,IAAI,EAAE;AACrE,QAAO,MAAM,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;;;;;;;;;;;;AAaxD,SAAgB,kBAAkB,UAA0B;CAC1D,IAAI,OAAO,SAAS,QAClB,gJACA,GACD;CAED,MAAM,WAAqB,EAAE;AAC7B,QAAO,KAAK,QAAQ,qCAAqC,UAAU;AACjE,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;;;;;;;;;AAUT,SAAgB,sBACd,WACA,mBACe;CACf,MAAM,SAAuB,EAAE;CAC/B,MAAM,aAA+B,EAAE;CACvC,MAAM,kCAAkB,IAAI,KAAa;CACzC,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;CAKF,MAAM,8BAAc,IAAI,KAAqB;AAC7C,MAAK,MAAM,YAAY,sBAAsB;EAC3C,MAAM,MAAM,eAAe,SAAS;AACpC,MAAI,CAAC,SAAS,SAAS,MAAM,IAAI,CAAC,YAAY,IAAI,IAAI,CACpD,aAAY,IAAI,KAAK,SAAS;;AAIlC,MAAK,MAAM,YAAY,sBAAsB;EAC3C,MAAM,WAAW,oBAAoB,SAAS;EAC9C,MAAM,SAAS,mBAAmB,SAAS;EAC3C,MAAM,KAAK,kBAAkB,SAAS;EACtC,MAAM,mBAAmB,mBAAmB,GAAG;EAE/C,MAAM,aAAa,GAAG,MAAM,GAAG,GAAG,YAAY,IAAI,GAAG,EAAE;EACvD,MAAM,eAAe,KAAK,UAAU,CAAC,UAAU,WAAW,CAAC;EAE3D,MAAM,kBAAkB,YAAY,SAAS;AAM7C,MAAI,CAAC,kBAAkB;AACrB,OAAI,eAAe,IAAI,aAAa,EAAE;IACpC,MAAM,SAAS,eAAe,IAAI,aAAa;AAC/C,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,EAC5C;AACA,qBAAgB,IAAI,SAAS;AAC7B;;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,cAAc;IAAE;IAAU,UAAU;IAAiB,CAAC;;EAG3E,MAAM,WAAW,eAAe,SAAS,CAAC,MAAM,IAAI;EACpD,MAAM,UAAoB,EAAE;AAC5B,OAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;GACxC,MAAM,SAAS,YAAY,IAAI,SAAS,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC;AAC9D,OAAI,OAAQ,SAAQ,KAAK,OAAO;;AAGlC,SAAO,KAAK;GACV;GACA,MAAM;GACN;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;GACD;GACD,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,QAAQ;AAC1B,MAAI,MAAM,SACR,WAAU,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,MAAM,GAAG;AAExD,MAAI,gBAAgB,IAAI,MAAM,SAAS,KAAK,MAC1C,iBAAgB,IAAI,MAAM,SAAS;;AAIvC,QAAO;EACL;EACA;EACA,qBAAqB;EACrB;EACD;;AAGH,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;;;AAIT,SAAS,eAAe,UAA0B;AAChD,QAAO,SAAS,QACd,2EACA,GACD;;AAGH,SAAS,mBAAmB,IAAqB;CAC/C,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,OAAO,QAAQ;AAC9C,KAAI,SAAS,WAAW,EAAG,QAAO;AAClC,QAAO,cAAc,KAAK,SAAS,SAAS,SAAS,GAAG;;AAG1D,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,cAAc,IAChC,SAAS,SAAS,gBAAgB,CAElC,QAAO;AAGT,QAAO;;;;;;;;;;AAiBT,SAAgB,8BACd,UACA,sBACQ;CACR,MAAM,QAAkB,EAAE;AAC1B,OAAM,KAAK,qBAAqB;AAChC,OAAM,KAAK,uDAAuD;AAClE,OAAM,KAAK,0CAA0C;AACrD,OAAM,KAAK,sCAAsC;AACjD,OAAM,KAAK,iCAAiC;CAE5C,MAAM,cAAc,WAAmB,SACrC,iBAAiB,KAAK,UAAU,UAAU,CAAC,IAAI;AAEjD,MAAK,MAAM,SAAS,SAAS,oBAAoB,QAAQ,EAAE;EACzD,MAAM,aAAa,mBAAmB,MAAM,OAAO;EACnD,MAAM,YAAY;AAElB,QAAM,KAAK,OAAO,KAAK,UAAU,MAAM,SAAS,CAAC,KAAK;AACtD,QAAM,KAAK,iBAAiB,WAAW,GAAG;AAC1C,QAAM,KAAK,gBAAgB,UAAU,GAAG;AACxC,MACE,wBACA,MAAM,SAAS,UACf,CAAC,SAAS,gBAAgB,IAAI,MAAM,SAAS,EAC7C;GACA,MAAM,kBAAkB,MAAM,QAAQ,SAAS,WAAW;IACxD,MAAM,YAAY,qBAAqB,OAAO,EAAE;AAChD,WAAO,YAAY,CAAC,WAAW,WAAW,YAAY,CAAC,GAAG,EAAE;KAC5D;GACF,MAAM,UAAU,qBAAqB,MAAM,SAAS;AACpD,OAAI,gBAAgB,OAClB,OAAM,KAAK,2BAA2B,gBAAgB,KAAK,KAAK,CAAC,IAAI;AAEvE,OAAI,SAAS,UACX,OAAM,KACJ,oBAAoB,WAAW,QAAQ,WAAW,YAAY,CAAC,GAChE;AAEH,OAAI,SAAS,KACX,OAAM,KAAK,eAAe,WAAW,QAAQ,MAAM,OAAO,CAAC,GAAG;;AAGlE,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;;AAGzB,SAAS,mBAAmB,QAAkC;AAC5D,KAAI,OAAO,WAAW,EAAG,QAAO;AAchC,QAAO,KAZS,OAAO,KAAK,MAAM;EAChC,MAAM,MAAM,kBAAkB,EAAE,KAAK,GAAG,EAAE,OAAO,KAAK,UAAU,EAAE,KAAK;AACvE,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"}
@@ -33,14 +33,7 @@ function routerPlugin(options) {
33
33
  const additionalPagesDirs = (options?.additionalPagesDirs || []).map((dir) => normalizeWatchedDir(dir));
34
34
  const additionalContentDirs = (options?.additionalContentDirs || []).map((dir) => normalizeWatchedDir(dir));
35
35
  const getRouteLikeDirs = () => [
36
- ...root ? [
37
- normalizeWatchedDir(`${root}/app/routes`),
38
- normalizeWatchedDir(`${root}/src/app/routes`),
39
- normalizeWatchedDir(`${root}/src/app/pages`),
40
- normalizeWatchedDir(`${root}/src/content`)
41
- ] : [],
42
- "/app/routes",
43
- "/src/app/routes",
36
+ ...root ? [normalizeWatchedDir(`${root}/src/app/pages`), normalizeWatchedDir(`${root}/src/content`)] : [],
44
37
  "/src/app/pages",
45
38
  "/src/content",
46
39
  ...additionalPagesDirs,
@@ -56,12 +49,7 @@ function routerPlugin(options) {
56
49
  return getRouteLikeDirs().some((dir) => normalizedPath === dir || normalizedPath.startsWith(`${dir}/`));
57
50
  };
58
51
  const discoverRouteFiles = () => {
59
- routeFilesCache ??= globSync([
60
- `${root}/app/routes/**/*.ts`,
61
- `${root}/src/app/routes/**/*.ts`,
62
- `${root}/src/app/pages/**/*.page.ts`,
63
- ...additionalPagesDirs.map((dir) => `${dir}/**/*.page.ts`)
64
- ], {
52
+ routeFilesCache ??= globSync([`${root}/src/app/pages/**/*.page.ts`, ...additionalPagesDirs.map((dir) => `${dir}/**/*.page.ts`)], {
65
53
  dot: true,
66
54
  absolute: true
67
55
  });
@@ -69,7 +57,6 @@ function routerPlugin(options) {
69
57
  };
70
58
  const discoverContentRouteFiles = () => {
71
59
  contentRouteFilesCache ??= globSync([
72
- `${root}/src/app/routes/**/*.md`,
73
60
  `${root}/src/app/pages/**/*.md`,
74
61
  `${root}/src/content/**/*.md`,
75
62
  ...additionalContentDirs.map((dir) => `${dir}/**/*.md`)