@analogjs/platform 3.0.0-alpha.98 → 3.0.0-beta.1

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 (144) hide show
  1. package/migrations/migrate-setup-testbed-browser-mode/migrate-setup-testbed-browser-mode.js +8 -6
  2. package/migrations/migrate-setup-testbed-browser-mode/migrate-setup-testbed-browser-mode.js.map +1 -1
  3. package/migrations/migrate-to-separated-plugins/migrate-to-separated-plugins.js.map +1 -1
  4. package/migrations/migrate-vitest-builder-aliases/migrate-vitest-builder-aliases.js.map +1 -1
  5. package/migrations/utils/source-edits.js.map +1 -1
  6. package/package.json +2 -2
  7. package/src/lib/analog-integration-plugin.js.map +1 -1
  8. package/src/lib/clear-client-page-endpoint.js.map +1 -1
  9. package/src/lib/content/marked/index.js.map +1 -1
  10. package/src/lib/content/marked/marked-content-highlighter.js.map +1 -1
  11. package/src/lib/content/marked/marked-setup.service.js.map +1 -1
  12. package/src/lib/content/prism/angular.js.map +1 -1
  13. package/src/lib/content/prism/index.js +1 -1
  14. package/src/lib/content/prism/index.js.map +1 -1
  15. package/src/lib/content/prism/prism-highlighter.js.map +1 -1
  16. package/src/lib/content/shiki/index.js +6 -4
  17. package/src/lib/content/shiki/index.js.map +1 -1
  18. package/src/lib/content/shiki/shiki-highlighter.js.map +1 -1
  19. package/src/lib/content/utils/escape-html.js.map +1 -1
  20. package/src/lib/content-plugin.js +18 -16
  21. package/src/lib/content-plugin.js.map +1 -1
  22. package/src/lib/deps-plugin.js.map +1 -1
  23. package/src/lib/discover-library-routes.js.map +1 -1
  24. package/src/lib/inject-server-fn-ids.js.map +1 -1
  25. package/src/lib/nitro/analog-nitro-plugin.js +8 -4
  26. package/src/lib/nitro/analog-nitro-plugin.js.map +1 -1
  27. package/src/lib/nitro/angular-linker-plugin.js +2 -1
  28. package/src/lib/nitro/angular-linker-plugin.js.map +1 -1
  29. package/src/lib/nitro/build-sitemap.js.map +1 -1
  30. package/src/lib/nitro/debug.js +8 -4
  31. package/src/lib/nitro/debug.js.map +1 -1
  32. package/src/lib/nitro/get-content-files.js +2 -1
  33. package/src/lib/nitro/get-content-files.js.map +1 -1
  34. package/src/lib/nitro/get-page-handlers.js +2 -1
  35. package/src/lib/nitro/get-page-handlers.js.map +1 -1
  36. package/src/lib/nitro/get-server-fn-handlers.js.map +1 -1
  37. package/src/lib/nitro/i18n-prerender.js +1 -1
  38. package/src/lib/nitro/i18n-prerender.js.map +1 -1
  39. package/src/lib/nitro/page-endpoints-plugin.js.map +1 -1
  40. package/src/lib/nitro/post-rendering-hook.js.map +1 -1
  41. package/src/lib/nitro/server-fn-endpoints.js +1 -1
  42. package/src/lib/nitro/server-fn-endpoints.js.map +1 -1
  43. package/src/lib/nx-plugin/_virtual/_rolldown/runtime.js +1 -1
  44. package/src/lib/nx-plugin/node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/string/camelCase.js.map +1 -1
  45. package/src/lib/nx-plugin/node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/string/capitalize.js.map +1 -1
  46. package/src/lib/nx-plugin/node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/string/words.js +1 -0
  47. package/src/lib/nx-plugin/node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/string/words.js.map +1 -1
  48. package/src/lib/nx-plugin/src/executors/vite-dev-server/compat.js +0 -1
  49. package/src/lib/nx-plugin/src/executors/vite-dev-server/compat.js.map +1 -1
  50. package/src/lib/nx-plugin/src/executors/vite-dev-server/vite-dev-server.impl.js +0 -1
  51. package/src/lib/nx-plugin/src/executors/vite-dev-server/vite-dev-server.impl.js.map +1 -1
  52. package/src/lib/nx-plugin/src/generators/app/compat.js +0 -1
  53. package/src/lib/nx-plugin/src/generators/app/compat.js.map +1 -1
  54. package/src/lib/nx-plugin/src/generators/app/generator.js +0 -1
  55. package/src/lib/nx-plugin/src/generators/app/generator.js.map +1 -1
  56. package/src/lib/nx-plugin/src/generators/app/lib/add-agent-context.js +0 -1
  57. package/src/lib/nx-plugin/src/generators/app/lib/add-agent-context.js.map +1 -1
  58. package/src/lib/nx-plugin/src/generators/app/lib/add-analog-project-config.js +48 -47
  59. package/src/lib/nx-plugin/src/generators/app/lib/add-analog-project-config.js.map +1 -1
  60. package/src/lib/nx-plugin/src/generators/app/lib/add-angular-app.js +0 -1
  61. package/src/lib/nx-plugin/src/generators/app/lib/add-angular-app.js.map +1 -1
  62. package/src/lib/nx-plugin/src/generators/app/lib/add-files.js +0 -1
  63. package/src/lib/nx-plugin/src/generators/app/lib/add-files.js.map +1 -1
  64. package/src/lib/nx-plugin/src/generators/app/lib/add-home-page.js +0 -1
  65. package/src/lib/nx-plugin/src/generators/app/lib/add-home-page.js.map +1 -1
  66. package/src/lib/nx-plugin/src/generators/app/lib/add-tailwind-config.js +1 -1
  67. package/src/lib/nx-plugin/src/generators/app/lib/add-tailwind-config.js.map +1 -1
  68. package/src/lib/nx-plugin/src/generators/app/lib/add-tailwind-helpers.js +0 -1
  69. package/src/lib/nx-plugin/src/generators/app/lib/add-tailwind-helpers.js.map +1 -1
  70. package/src/lib/nx-plugin/src/generators/app/lib/cleanup-files.js.map +1 -1
  71. package/src/lib/nx-plugin/src/generators/app/lib/update-index-html.js +0 -1
  72. package/src/lib/nx-plugin/src/generators/app/lib/update-index-html.js.map +1 -1
  73. package/src/lib/nx-plugin/src/generators/app/versions/minimum-supported-versions.js +0 -1
  74. package/src/lib/nx-plugin/src/generators/app/versions/minimum-supported-versions.js.map +1 -1
  75. package/src/lib/nx-plugin/src/generators/app/versions/tailwind-dependencies.js +5 -5
  76. package/src/lib/nx-plugin/src/generators/app/versions/tailwind-dependencies.js.map +1 -1
  77. package/src/lib/nx-plugin/src/generators/app/versions/versions.d.ts +4 -0
  78. package/src/lib/nx-plugin/src/generators/app/versions/versions.js +12 -0
  79. package/src/lib/nx-plugin/src/generators/app/versions/versions.js.map +1 -0
  80. package/src/lib/nx-plugin/src/generators/init/compat.js +0 -1
  81. package/src/lib/nx-plugin/src/generators/init/compat.js.map +1 -1
  82. package/src/lib/nx-plugin/src/generators/init/generator.js +0 -1
  83. package/src/lib/nx-plugin/src/generators/init/generator.js.map +1 -1
  84. package/src/lib/nx-plugin/src/generators/init/lib/add-analog-dependencies.js +0 -1
  85. package/src/lib/nx-plugin/src/generators/init/lib/add-analog-dependencies.js.map +1 -1
  86. package/src/lib/nx-plugin/src/generators/init/lib/update-app-tsconfig.js +0 -1
  87. package/src/lib/nx-plugin/src/generators/init/lib/update-app-tsconfig.js.map +1 -1
  88. package/src/lib/nx-plugin/src/generators/init/lib/update-build-target.js +0 -1
  89. package/src/lib/nx-plugin/src/generators/init/lib/update-build-target.js.map +1 -1
  90. package/src/lib/nx-plugin/src/generators/init/lib/update-git-ignore.js.map +1 -1
  91. package/src/lib/nx-plugin/src/generators/init/lib/update-index-html.js +0 -1
  92. package/src/lib/nx-plugin/src/generators/init/lib/update-index-html.js.map +1 -1
  93. package/src/lib/nx-plugin/src/generators/init/lib/update-main.js +0 -1
  94. package/src/lib/nx-plugin/src/generators/init/lib/update-main.js.map +1 -1
  95. package/src/lib/nx-plugin/src/generators/init/lib/update-package-json.js +2 -2
  96. package/src/lib/nx-plugin/src/generators/init/lib/update-package-json.js.map +1 -1
  97. package/src/lib/nx-plugin/src/generators/init/lib/update-serve-target.js +0 -1
  98. package/src/lib/nx-plugin/src/generators/init/lib/update-serve-target.js.map +1 -1
  99. package/src/lib/nx-plugin/src/generators/init/lib/update-test-target.js +0 -1
  100. package/src/lib/nx-plugin/src/generators/init/lib/update-test-target.js.map +1 -1
  101. package/src/lib/nx-plugin/src/generators/init/lib/update-test-tsconfig.js +0 -1
  102. package/src/lib/nx-plugin/src/generators/init/lib/update-test-tsconfig.js.map +1 -1
  103. package/src/lib/nx-plugin/src/generators/page/compat.js +0 -1
  104. package/src/lib/nx-plugin/src/generators/page/compat.js.map +1 -1
  105. package/src/lib/nx-plugin/src/generators/page/generator.js +2 -2
  106. package/src/lib/nx-plugin/src/generators/page/generator.js.map +1 -1
  107. package/src/lib/nx-plugin/src/generators/preset/generator.js +0 -1
  108. package/src/lib/nx-plugin/src/generators/preset/generator.js.map +1 -1
  109. package/src/lib/nx-plugin/src/utils/version-utils.js +0 -1
  110. package/src/lib/nx-plugin/src/utils/version-utils.js.map +1 -1
  111. package/src/lib/nx-plugin/src/utils/versions/dependencies.js +8 -9
  112. package/src/lib/nx-plugin/src/utils/versions/dependencies.js.map +1 -1
  113. package/src/lib/nx-plugin/src/utils/versions/dev-dependencies.js +13 -13
  114. package/src/lib/nx-plugin/src/utils/versions/dev-dependencies.js.map +1 -1
  115. package/src/lib/nx-plugin/src/utils/versions/versions.d.ts +15 -0
  116. package/src/lib/nx-plugin/src/utils/versions/versions.js +34 -0
  117. package/src/lib/nx-plugin/src/utils/versions/versions.js.map +1 -0
  118. package/src/lib/platform-plugin.js.map +1 -1
  119. package/src/lib/route-file-discovery.js.map +1 -1
  120. package/src/lib/route-idiom-diagnostics.js +3 -13
  121. package/src/lib/route-idiom-diagnostics.js.map +1 -1
  122. package/src/lib/route-manifest.js +1 -1
  123. package/src/lib/route-manifest.js.map +1 -1
  124. package/src/lib/router-plugin.js +18 -2
  125. package/src/lib/router-plugin.js.map +1 -1
  126. package/src/lib/server-fn-client-transform.js.map +1 -1
  127. package/src/lib/server-fn-id.js.map +1 -1
  128. package/src/lib/ssr/defer-streaming-plugin.js.map +1 -1
  129. package/src/lib/ssr/inject-html-plugin.js.map +1 -1
  130. package/src/lib/ssr/ssr-build-plugin.js.map +1 -1
  131. package/src/lib/typed-routes-plugin.js.map +1 -1
  132. package/src/lib/utils/debug-harness.js.map +1 -1
  133. package/src/lib/utils/debug-log-file.js +2 -1
  134. package/src/lib/utils/debug-log-file.js.map +1 -1
  135. package/src/lib/utils/debug.js +7 -4
  136. package/src/lib/utils/debug.js.map +1 -1
  137. package/src/lib/utils/rolldown.js.map +1 -1
  138. package/src/server-mode-plugin.js.map +1 -1
  139. package/src/lib/nx-plugin/src/generators/app/versions/nx_18_X/versions.d.ts +0 -28
  140. package/src/lib/nx-plugin/src/generators/app/versions/nx_18_X/versions.js +0 -12
  141. package/src/lib/nx-plugin/src/generators/app/versions/nx_18_X/versions.js.map +0 -1
  142. package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.d.ts +0 -15
  143. package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.js +0 -34
  144. package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"router-plugin.js","names":[],"sources":["../../../src/lib/router-plugin.ts"],"sourcesContent":["import { normalizePath, Plugin, UserConfig, ViteDevServer } from 'vite';\nimport { globSync } from 'tinyglobby';\nimport { readFileSync } from 'node:fs';\nimport { isAbsolute, relative, resolve } from 'node:path';\n\nimport { Options } from './options.js';\nimport {\n analyzeAnalogRouteFile,\n formatAnalogRouteIdiomDiagnostic,\n} from './route-idiom-diagnostics.js';\n\n/**\n * Router plugin that handles route file discovery and hot module replacement.\n *\n * This plugin provides three main functionalities:\n * 1. Route invalidation when files are added/deleted (HMR workaround)\n * 2. Dynamic route file discovery and import generation\n * 3. Content route file discovery for markdown and Analog content\n *\n * @param options Configuration options for the router plugin\n * @returns Array of Vite plugins for route handling\n *\n * IMPORTANT: This plugin uses tinyglobby for file discovery.\n * Key behavior with { dot: true, absolute: true }:\n * - Returns absolute paths for ALL discovered files\n * - Path normalization is required to match expected output format\n * - Files within project root must use relative paths in object keys\n * - Files outside project root keep absolute paths in object keys\n */\nexport function routerPlugin(options?: Options): Plugin[] {\n const workspaceRoot = normalizePath(\n options?.workspaceRoot ?? process.env['NX_WORKSPACE_ROOT'] ?? process.cwd(),\n );\n let config: UserConfig;\n let root: string;\n // Option dirs are workspace-relative, often written with a leading `/`.\n // Normalize them once into absolute workspace paths so watcher events,\n // glob patterns, and key generation all compare against the same shape.\n const normalizeWatchedDir = (dir: string) => {\n const normalizedDir = normalizePath(\n dir.startsWith(`${workspaceRoot}/`) || dir === workspaceRoot\n ? dir\n : dir.startsWith('/')\n ? `${workspaceRoot}${dir}`\n : resolve(workspaceRoot, dir),\n );\n return normalizedDir.endsWith('/')\n ? normalizedDir.slice(0, -1)\n : normalizedDir;\n };\n // Computed eagerly — these depend only on `options` and `workspaceRoot`,\n // both of which are fixed at construction time.\n const additionalPagesDirs = (options?.additionalPagesDirs || []).map((dir) =>\n normalizeWatchedDir(dir),\n );\n const additionalContentDirs = (options?.additionalContentDirs || []).map(\n (dir) => normalizeWatchedDir(dir),\n );\n // Returns every directory that can contain route-like files. The root-\n // relative entries are only available after the Vite `config` hook sets\n // `root`. The short-form fallbacks (`/src/app/pages`, etc.) let watcher\n // events match before `config` runs — they cover the common convention\n // where paths start with these prefixes.\n const getRouteLikeDirs = () => [\n ...(root\n ? [\n normalizeWatchedDir(`${root}/src/app/pages`),\n normalizeWatchedDir(`${root}/src/content`),\n ]\n : []),\n '/src/app/pages',\n '/src/content',\n ...additionalPagesDirs,\n ...additionalContentDirs,\n ];\n // These lists are used repeatedly by transform hooks during serve. Keeping\n // them warm avoids a full glob on every route/content invalidation.\n let routeFilesCache: string[] | undefined;\n let pageRouteFilesCache: string[] | undefined;\n let contentRouteFilesCache: string[] | undefined;\n let endpointFilesCache: string[] | undefined;\n const routeDiagnosticCache = new Map<string, string>();\n const isRouteLikeFile = (path: string) => {\n // Watcher paths from chokidar are already absolute — `normalizePath`\n // (forward-slash only) is sufficient; `resolve()` would be a no-op.\n const normalizedPath = normalizePath(path);\n\n return getRouteLikeDirs().some(\n (dir) => normalizedPath === dir || normalizedPath.startsWith(`${dir}/`),\n );\n };\n const discoverRouteFiles = () => {\n routeFilesCache ??= globSync(\n [\n `${root}/src/app/pages/**/*.page.ts`,\n ...additionalPagesDirs.map((dir) => `${dir}/**/*.page.ts`),\n ],\n { dot: true, absolute: true },\n );\n\n return routeFilesCache;\n };\n const discoverContentRouteFiles = () => {\n contentRouteFilesCache ??= globSync(\n [\n `${root}/src/app/pages/**/*.md`,\n `${root}/src/content/**/*.md`,\n ...additionalContentDirs.map((dir) => `${dir}/**/*.md`),\n ],\n { dot: true, absolute: true },\n );\n\n return contentRouteFilesCache;\n };\n const discoverEndpointFiles = () => {\n endpointFilesCache ??= globSync(\n [\n `${root}/src/app/pages/**/*.server.ts`,\n ...additionalPagesDirs.map((dir) => `${dir}/**/*.server.ts`),\n ],\n { dot: true, absolute: true },\n );\n\n return endpointFilesCache;\n };\n const discoverPageRouteFiles = () => {\n pageRouteFilesCache ??= discoverRouteFiles().filter((file) =>\n file.endsWith('.page.ts'),\n );\n\n return pageRouteFilesCache;\n };\n const invalidateDiscoveryCaches = () => {\n routeFilesCache = undefined;\n pageRouteFilesCache = undefined;\n contentRouteFilesCache = undefined;\n endpointFilesCache = undefined;\n };\n const reportRouteDiagnostics = (path: string) => {\n if (!path.endsWith('.page.ts')) {\n return;\n }\n\n try {\n const code = readFileSync(path, 'utf-8');\n const routeFiles = discoverPageRouteFiles();\n const diagnostics = analyzeAnalogRouteFile({\n filename: path,\n code,\n routeFiles,\n });\n\n const rendered = diagnostics.map((diagnostic) =>\n formatAnalogRouteIdiomDiagnostic(diagnostic, path, workspaceRoot),\n );\n const fingerprint = rendered.join('\\n\\n');\n\n if (!fingerprint) {\n routeDiagnosticCache.delete(path);\n return;\n }\n\n if (routeDiagnosticCache.get(path) === fingerprint) {\n return;\n }\n\n routeDiagnosticCache.set(path, fingerprint);\n rendered.forEach((message) => console.warn(message));\n } catch {\n routeDiagnosticCache.delete(path);\n }\n };\n const getModuleKey = (module: string) => {\n // Before config sets `root`, fall back to workspace-relative keys.\n if (!root) {\n return `/${normalizePath(relative(workspaceRoot, module))}`;\n }\n\n const relToRoot = normalizePath(relative(root, module));\n // Use true path containment instead of a raw prefix check so siblings like\n // `/apps/my-app-tools/...` are not mistaken for files inside `/apps/my-app`.\n const isInRoot = !relToRoot.startsWith('..') && !isAbsolute(relToRoot);\n\n if (isInRoot) {\n return `/${relToRoot}`;\n }\n\n return `/${normalizePath(relative(workspaceRoot, module))}`;\n };\n const invalidateFileModules = (server: ViteDevServer, path: string) => {\n const normalizedPath = normalizePath(path);\n // A newly added page can be discovered before its final contents settle.\n // Invalidate the page module itself so later edits don't keep serving the\n // first incomplete transform from Vite's module graph.\n const fileModules =\n server.moduleGraph.getModulesByFile?.(normalizedPath) ??\n server.moduleGraph.fileToModulesMap.get(normalizedPath);\n\n fileModules?.forEach((mod) => {\n server.moduleGraph.invalidateModule(mod);\n\n mod.importers.forEach((imp) => {\n server.moduleGraph.invalidateModule(imp);\n });\n });\n };\n\n return [\n {\n name: 'analogjs-router-invalidate-routes',\n configureServer(server) {\n /**\n * Invalidates route modules when files are added or deleted.\n * This is a workaround for Vite's HMR limitations with dynamic imports.\n *\n * @param path The file path that was added or deleted\n */\n function invalidateRoutes(\n path: string,\n event: 'add' | 'change' | 'unlink',\n ) {\n if (!isRouteLikeFile(path)) {\n return;\n }\n\n // Add/remove changes the route graph shape, so the discovery caches\n // must be rebuilt. Plain edits can keep using the current file set.\n if (event !== 'change') {\n invalidateDiscoveryCaches();\n }\n\n if (event === 'change') {\n reportRouteDiagnostics(path);\n } else if (event === 'unlink') {\n routeDiagnosticCache.delete(path);\n discoverPageRouteFiles().forEach((file) =>\n reportRouteDiagnostics(file),\n );\n } else {\n discoverPageRouteFiles().forEach((file) =>\n reportRouteDiagnostics(file),\n );\n }\n\n invalidateFileModules(server, path);\n\n // For an in-place edit we only need module invalidation. Keeping the\n // app alive here lets Angular/Vite attempt the narrower HMR path.\n if (event === 'change') {\n return;\n }\n\n server.moduleGraph.fileToModulesMap.forEach((mods) => {\n mods.forEach((mod) => {\n if (mod.id?.includes('analogjs') && mod.id?.includes('fesm')) {\n server.moduleGraph.invalidateModule(mod);\n\n mod.importers.forEach((imp) => {\n server.moduleGraph.invalidateModule(imp);\n });\n }\n });\n });\n\n server.ws.send('analog:debug-full-reload', {\n plugin: 'platform:router-plugin',\n reason: 'route-graph-shape-changed',\n event,\n path,\n });\n server.ws.send({\n type: 'full-reload',\n });\n }\n\n server.watcher.on('add', (path) => invalidateRoutes(path, 'add'));\n server.watcher.on('change', (path) => invalidateRoutes(path, 'change'));\n server.watcher.on('unlink', (path) => invalidateRoutes(path, 'unlink'));\n\n // Vite's watcher only covers the app root by default.\n // additionalPagesDirs / additionalContentDirs live outside the\n // root (e.g. libs/shared/feature in a monorepo), so file\n // add/rename/delete events are never fired for them. Explicitly\n // add these directories to chokidar so route invalidation works.\n for (const dir of [...additionalPagesDirs, ...additionalContentDirs]) {\n server.watcher.add(dir);\n }\n\n discoverPageRouteFiles().forEach((file) =>\n reportRouteDiagnostics(file),\n );\n },\n },\n {\n name: 'analog-glob-routes',\n // enforce: 'post' ensures this transform runs AFTER the Angular compiler\n // plugin, which replaces module content with its own compiled output.\n // Without this, the Angular plugin would overwrite the route replacements.\n enforce: 'post',\n config(_config) {\n config = _config;\n root = normalizePath(resolve(workspaceRoot, config.root || '.') || '.');\n },\n /**\n * Transforms code to replace ANALOG_ROUTE_FILES and ANALOG_CONTENT_ROUTE_FILES\n * placeholders with actual dynamic imports of discovered route and content files.\n *\n * @param code The source code to transform\n * @returns Transformed code with dynamic imports or undefined if no transformation needed\n */\n // Vite 8 / Rolldown filtered transform: the `filter.code` substring\n // pre-filter lets the bundler skip modules that don't contain the\n // marker. '_ROUTE_FILES' is a common substring of both\n // 'ANALOG_ROUTE_FILES' and 'ANALOG_CONTENT_ROUTE_FILES'.\n //\n // IMPORTANT: Do NOT change this to 'ANALOG_ROUTE_FILES' — that is NOT\n // a substring of 'ANALOG_CONTENT_ROUTE_FILES' (they diverge at\n // position 7: 'ANALOG_C...' vs 'ANALOG_R...'). When tsconfig path\n // aliases resolve @analogjs/router and @analogjs/router/content to\n // separate source files, each variable lives in its own module and\n // the filter must match both independently.\n transform: {\n filter: {\n code: '_ROUTE_FILES',\n },\n handler(code) {\n if (\n code.includes('ANALOG_ROUTE_FILES') ||\n code.includes('ANALOG_CONTENT_ROUTE_FILES')\n ) {\n // Discover route files using tinyglobby\n // NOTE: { absolute: true } returns absolute paths for ALL files\n const routeFiles = discoverRouteFiles();\n\n // Discover content files using tinyglobby\n const contentRouteFiles = discoverContentRouteFiles();\n\n let result = code.replace(\n 'ANALOG_ROUTE_FILES = {};',\n `\n ANALOG_ROUTE_FILES = {${routeFiles.map((module) => {\n // Keys are app-root-relative for in-app files,\n // workspace-relative for library files (additionalPagesDirs).\n // import() keeps absolute paths for Vite's module resolution.\n const key = getModuleKey(module);\n return `\"${key}\": () => import('${module}')`;\n })}};\n `,\n );\n\n result = result.replace(\n 'ANALOG_CONTENT_ROUTE_FILES = {};',\n `\n ANALOG_CONTENT_ROUTE_FILES = {${contentRouteFiles.map((module) => {\n const key = getModuleKey(module);\n return `\"${key}\": () => import('${module}?analog-content-file=true').then(m => m.default)`;\n })}};\n `,\n );\n\n result = result.replace(\n 'ANALOG_CONTENT_FILE_COUNT = 0',\n `ANALOG_CONTENT_FILE_COUNT = ${contentRouteFiles.length}`,\n );\n\n return {\n code: result,\n map: { mappings: '' },\n };\n }\n\n return;\n },\n },\n },\n {\n name: 'analog-glob-endpoints',\n // enforce: 'post' ensures this transform runs AFTER the Angular compiler\n // plugin, which replaces module content with its own compiled output.\n // Without this, the Angular plugin would overwrite the endpoint replacements.\n enforce: 'post',\n /**\n * Transforms code to replace ANALOG_PAGE_ENDPOINTS placeholder\n * with actual dynamic imports of discovered server endpoint files.\n *\n * @param code The source code to transform\n * @returns Transformed code with dynamic imports or undefined if no transformation needed\n */\n transform: {\n filter: {\n code: 'ANALOG_PAGE_ENDPOINTS',\n },\n handler(code) {\n if (code.includes('ANALOG_PAGE_ENDPOINTS')) {\n // Discover server endpoint files using tinyglobby\n const endpointFiles = discoverEndpointFiles();\n\n const result = code.replace(\n 'ANALOG_PAGE_ENDPOINTS = {};',\n `\n ANALOG_PAGE_ENDPOINTS = {${endpointFiles.map((module) => {\n const key = getModuleKey(module);\n return `\"${key}\": () => import('${module}')`;\n })}};\n `,\n );\n\n return {\n code: result,\n map: { mappings: '' },\n };\n }\n\n return;\n },\n },\n },\n ];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"router-plugin.js","names":[],"sources":["../../../src/lib/router-plugin.ts"],"sourcesContent":["import { normalizePath, Plugin, UserConfig, ViteDevServer } from 'vite';\nimport { globSync } from 'tinyglobby';\nimport { readFileSync } from 'node:fs';\nimport { isAbsolute, relative, resolve } from 'node:path';\n\nimport { Options } from './options.js';\nimport {\n analyzeAnalogRouteFile,\n formatAnalogRouteIdiomDiagnostic,\n} from './route-idiom-diagnostics.js';\n\n/**\n * Router plugin that handles route file discovery and hot module replacement.\n *\n * This plugin provides three main functionalities:\n * 1. Route invalidation when files are added/deleted (HMR workaround)\n * 2. Dynamic route file discovery and import generation\n * 3. Content route file discovery for markdown and Analog content\n *\n * @param options Configuration options for the router plugin\n * @returns Array of Vite plugins for route handling\n *\n * IMPORTANT: This plugin uses tinyglobby for file discovery.\n * Key behavior with { dot: true, absolute: true }:\n * - Returns absolute paths for ALL discovered files\n * - Path normalization is required to match expected output format\n * - Files within project root must use relative paths in object keys\n * - Files outside project root keep absolute paths in object keys\n */\nexport function routerPlugin(options?: Options): Plugin[] {\n const workspaceRoot = normalizePath(\n options?.workspaceRoot ?? process.env['NX_WORKSPACE_ROOT'] ?? process.cwd(),\n );\n let config: UserConfig;\n let root: string;\n // Option dirs are workspace-relative, often written with a leading `/`.\n // Normalize them once into absolute workspace paths so watcher events,\n // glob patterns, and key generation all compare against the same shape.\n const normalizeWatchedDir = (dir: string) => {\n const normalizedDir = normalizePath(\n dir.startsWith(`${workspaceRoot}/`) || dir === workspaceRoot\n ? dir\n : dir.startsWith('/')\n ? `${workspaceRoot}${dir}`\n : resolve(workspaceRoot, dir),\n );\n return normalizedDir.endsWith('/')\n ? normalizedDir.slice(0, -1)\n : normalizedDir;\n };\n // Computed eagerly — these depend only on `options` and `workspaceRoot`,\n // both of which are fixed at construction time.\n const additionalPagesDirs = (options?.additionalPagesDirs || []).map((dir) =>\n normalizeWatchedDir(dir),\n );\n const additionalContentDirs = (options?.additionalContentDirs || []).map(\n (dir) => normalizeWatchedDir(dir),\n );\n // Returns every directory that can contain route-like files. The root-\n // relative entries are only available after the Vite `config` hook sets\n // `root`. The short-form fallbacks (`/src/app/pages`, etc.) let watcher\n // events match before `config` runs — they cover the common convention\n // where paths start with these prefixes.\n const getRouteLikeDirs = () => [\n ...(root\n ? [\n normalizeWatchedDir(`${root}/src/app/pages`),\n normalizeWatchedDir(`${root}/src/content`),\n ]\n : []),\n '/src/app/pages',\n '/src/content',\n ...additionalPagesDirs,\n ...additionalContentDirs,\n ];\n // These lists are used repeatedly by transform hooks during serve. Keeping\n // them warm avoids a full glob on every route/content invalidation.\n let routeFilesCache: string[] | undefined;\n let pageRouteFilesCache: string[] | undefined;\n let contentRouteFilesCache: string[] | undefined;\n let endpointFilesCache: string[] | undefined;\n const routeDiagnosticCache = new Map<string, string>();\n const isRouteLikeFile = (path: string) => {\n // Watcher paths from chokidar are already absolute — `normalizePath`\n // (forward-slash only) is sufficient; `resolve()` would be a no-op.\n const normalizedPath = normalizePath(path);\n\n return getRouteLikeDirs().some(\n (dir) => normalizedPath === dir || normalizedPath.startsWith(`${dir}/`),\n );\n };\n const discoverRouteFiles = () => {\n routeFilesCache ??= globSync(\n [\n `${root}/src/app/pages/**/*.page.ts`,\n ...additionalPagesDirs.map((dir) => `${dir}/**/*.page.ts`),\n ],\n { dot: true, absolute: true },\n );\n\n return routeFilesCache;\n };\n const discoverContentRouteFiles = () => {\n contentRouteFilesCache ??= globSync(\n [\n `${root}/src/app/pages/**/*.md`,\n `${root}/src/content/**/*.md`,\n ...additionalContentDirs.map((dir) => `${dir}/**/*.md`),\n ],\n { dot: true, absolute: true },\n );\n\n return contentRouteFilesCache;\n };\n const discoverEndpointFiles = () => {\n endpointFilesCache ??= globSync(\n [\n `${root}/src/app/pages/**/*.server.ts`,\n ...additionalPagesDirs.map((dir) => `${dir}/**/*.server.ts`),\n ],\n { dot: true, absolute: true },\n );\n\n return endpointFilesCache;\n };\n const discoverPageRouteFiles = () => {\n pageRouteFilesCache ??= discoverRouteFiles().filter((file) =>\n file.endsWith('.page.ts'),\n );\n\n return pageRouteFilesCache;\n };\n const invalidateDiscoveryCaches = () => {\n routeFilesCache = undefined;\n pageRouteFilesCache = undefined;\n contentRouteFilesCache = undefined;\n endpointFilesCache = undefined;\n };\n const reportRouteDiagnostics = (path: string) => {\n if (!path.endsWith('.page.ts')) {\n return;\n }\n\n try {\n const code = readFileSync(path, 'utf-8');\n const routeFiles = discoverPageRouteFiles();\n const diagnostics = analyzeAnalogRouteFile({\n filename: path,\n code,\n routeFiles,\n });\n\n const rendered = diagnostics.map((diagnostic) =>\n formatAnalogRouteIdiomDiagnostic(diagnostic, path, workspaceRoot),\n );\n const fingerprint = rendered.join('\\n\\n');\n\n if (!fingerprint) {\n routeDiagnosticCache.delete(path);\n return;\n }\n\n if (routeDiagnosticCache.get(path) === fingerprint) {\n return;\n }\n\n routeDiagnosticCache.set(path, fingerprint);\n rendered.forEach((message) => console.warn(message));\n } catch {\n routeDiagnosticCache.delete(path);\n }\n };\n const getModuleKey = (module: string) => {\n // Before config sets `root`, fall back to workspace-relative keys.\n if (!root) {\n return `/${normalizePath(relative(workspaceRoot, module))}`;\n }\n\n const relToRoot = normalizePath(relative(root, module));\n // Use true path containment instead of a raw prefix check so siblings like\n // `/apps/my-app-tools/...` are not mistaken for files inside `/apps/my-app`.\n const isInRoot = !relToRoot.startsWith('..') && !isAbsolute(relToRoot);\n\n if (isInRoot) {\n return `/${relToRoot}`;\n }\n\n return `/${normalizePath(relative(workspaceRoot, module))}`;\n };\n const invalidateFileModules = (server: ViteDevServer, path: string) => {\n const normalizedPath = normalizePath(path);\n // A newly added page can be discovered before its final contents settle.\n // Invalidate the page module itself so later edits don't keep serving the\n // first incomplete transform from Vite's module graph.\n const fileModules =\n server.moduleGraph.getModulesByFile?.(normalizedPath) ??\n server.moduleGraph.fileToModulesMap.get(normalizedPath);\n\n fileModules?.forEach((mod) => {\n server.moduleGraph.invalidateModule(mod);\n\n mod.importers.forEach((imp) => {\n server.moduleGraph.invalidateModule(imp);\n });\n });\n };\n\n return [\n {\n name: 'analogjs-router-invalidate-routes',\n configureServer(server) {\n /**\n * Invalidates route modules when files are added or deleted.\n * This is a workaround for Vite's HMR limitations with dynamic imports.\n *\n * @param path The file path that was added or deleted\n */\n function invalidateRoutes(\n path: string,\n event: 'add' | 'change' | 'unlink',\n ) {\n if (!isRouteLikeFile(path)) {\n return;\n }\n\n // Add/remove changes the route graph shape, so the discovery caches\n // must be rebuilt. Plain edits can keep using the current file set.\n if (event !== 'change') {\n invalidateDiscoveryCaches();\n }\n\n if (event === 'change') {\n reportRouteDiagnostics(path);\n } else if (event === 'unlink') {\n routeDiagnosticCache.delete(path);\n discoverPageRouteFiles().forEach((file) =>\n reportRouteDiagnostics(file),\n );\n } else {\n discoverPageRouteFiles().forEach((file) =>\n reportRouteDiagnostics(file),\n );\n }\n\n invalidateFileModules(server, path);\n\n // For an in-place edit we only need module invalidation. Keeping the\n // app alive here lets Angular/Vite attempt the narrower HMR path.\n if (event === 'change') {\n return;\n }\n\n server.moduleGraph.fileToModulesMap.forEach((mods) => {\n mods.forEach((mod) => {\n if (mod.id?.includes('analogjs') && mod.id?.includes('fesm')) {\n server.moduleGraph.invalidateModule(mod);\n\n mod.importers.forEach((imp) => {\n server.moduleGraph.invalidateModule(imp);\n });\n }\n });\n });\n\n server.ws.send('analog:debug-full-reload', {\n plugin: 'platform:router-plugin',\n reason: 'route-graph-shape-changed',\n event,\n path,\n });\n server.ws.send({\n type: 'full-reload',\n });\n }\n\n server.watcher.on('add', (path) => invalidateRoutes(path, 'add'));\n server.watcher.on('change', (path) => invalidateRoutes(path, 'change'));\n server.watcher.on('unlink', (path) => invalidateRoutes(path, 'unlink'));\n\n // Vite's watcher only covers the app root by default.\n // additionalPagesDirs / additionalContentDirs live outside the\n // root (e.g. libs/shared/feature in a monorepo), so file\n // add/rename/delete events are never fired for them. Explicitly\n // add these directories to chokidar so route invalidation works.\n for (const dir of [...additionalPagesDirs, ...additionalContentDirs]) {\n server.watcher.add(dir);\n }\n\n discoverPageRouteFiles().forEach((file) =>\n reportRouteDiagnostics(file),\n );\n },\n },\n {\n name: 'analog-glob-routes',\n // enforce: 'post' ensures this transform runs AFTER the Angular compiler\n // plugin, which replaces module content with its own compiled output.\n // Without this, the Angular plugin would overwrite the route replacements.\n enforce: 'post',\n config(_config) {\n config = _config;\n root = normalizePath(resolve(workspaceRoot, config.root || '.') || '.');\n },\n /**\n * Transforms code to replace ANALOG_ROUTE_FILES and ANALOG_CONTENT_ROUTE_FILES\n * placeholders with actual dynamic imports of discovered route and content files.\n *\n * @param code The source code to transform\n * @returns Transformed code with dynamic imports or undefined if no transformation needed\n */\n // Vite 8 / Rolldown filtered transform: the `filter.code` substring\n // pre-filter lets the bundler skip modules that don't contain the\n // marker. '_ROUTE_FILES' is a common substring of both\n // 'ANALOG_ROUTE_FILES' and 'ANALOG_CONTENT_ROUTE_FILES'.\n //\n // IMPORTANT: Do NOT change this to 'ANALOG_ROUTE_FILES' — that is NOT\n // a substring of 'ANALOG_CONTENT_ROUTE_FILES' (they diverge at\n // position 7: 'ANALOG_C...' vs 'ANALOG_R...'). When tsconfig path\n // aliases resolve @analogjs/router and @analogjs/router/content to\n // separate source files, each variable lives in its own module and\n // the filter must match both independently.\n transform: {\n filter: {\n code: '_ROUTE_FILES',\n },\n handler(code) {\n if (\n code.includes('ANALOG_ROUTE_FILES') ||\n code.includes('ANALOG_CONTENT_ROUTE_FILES')\n ) {\n // Discover route files using tinyglobby\n // NOTE: { absolute: true } returns absolute paths for ALL files\n const routeFiles = discoverRouteFiles();\n\n // Discover content files using tinyglobby\n const contentRouteFiles = discoverContentRouteFiles();\n\n let result = code.replace(\n 'ANALOG_ROUTE_FILES = {};',\n `\n ANALOG_ROUTE_FILES = {${routeFiles.map((module) => {\n // Keys are app-root-relative for in-app files,\n // workspace-relative for library files (additionalPagesDirs).\n // import() keeps absolute paths for Vite's module resolution.\n const key = getModuleKey(module);\n return `\"${key}\": () => import('${module}')`;\n })}};\n `,\n );\n\n result = result.replace(\n 'ANALOG_CONTENT_ROUTE_FILES = {};',\n `\n ANALOG_CONTENT_ROUTE_FILES = {${contentRouteFiles.map((module) => {\n const key = getModuleKey(module);\n return `\"${key}\": () => import('${module}?analog-content-file=true').then(m => m.default)`;\n })}};\n `,\n );\n\n result = result.replace(\n 'ANALOG_CONTENT_FILE_COUNT = 0',\n `ANALOG_CONTENT_FILE_COUNT = ${contentRouteFiles.length}`,\n );\n\n return {\n code: result,\n map: { mappings: '' },\n };\n }\n\n return;\n },\n },\n },\n {\n name: 'analog-glob-endpoints',\n // enforce: 'post' ensures this transform runs AFTER the Angular compiler\n // plugin, which replaces module content with its own compiled output.\n // Without this, the Angular plugin would overwrite the endpoint replacements.\n enforce: 'post',\n /**\n * Transforms code to replace ANALOG_PAGE_ENDPOINTS placeholder\n * with actual dynamic imports of discovered server endpoint files.\n *\n * @param code The source code to transform\n * @returns Transformed code with dynamic imports or undefined if no transformation needed\n */\n transform: {\n filter: {\n code: 'ANALOG_PAGE_ENDPOINTS',\n },\n handler(code) {\n if (code.includes('ANALOG_PAGE_ENDPOINTS')) {\n // Discover server endpoint files using tinyglobby\n const endpointFiles = discoverEndpointFiles();\n\n const result = code.replace(\n 'ANALOG_PAGE_ENDPOINTS = {};',\n `\n ANALOG_PAGE_ENDPOINTS = {${endpointFiles.map((module) => {\n const key = getModuleKey(module);\n return `\"${key}\": () => import('${module}')`;\n })}};\n `,\n );\n\n return {\n code: result,\n map: { mappings: '' },\n };\n }\n\n return;\n },\n },\n },\n ];\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server-fn-client-transform.js","names":[],"sources":["../../../src/lib/server-fn-client-transform.ts"],"sourcesContent":["import { parseSync } from 'oxc-parser';\n// Light subpath (no vite/nitro barrel): the built scrub is loaded outside a\n// build too, so it must not pull the plugin graph in.\nimport { deriveServerFnId } from './server-fn-id.js';\n\n/**\n * Client-build scrub + proxy generation for server functions.\n *\n * On the client build a `*.server.ts` module must not carry server handler\n * bodies (which `inject()` server services, read the request, hit the database).\n * But the client still imports the named `serverFn` exports to dispatch them.\n * This transform regenerates the module so each `export const NAME = serverFn(\n * config, handler)` becomes a call to the client-safe `createServerFnRef`\n * factory — keeping the `{ id, url, method }` metadata `injectServerFn` needs,\n * dropping the handler and every server-only import. Because the regenerated\n * module references nothing else, the server code tree-shakes away entirely.\n *\n * Returns the new module source, or `null` when the module defines no server\n * function (the caller falls back to the page-endpoint scrub for those).\n */\nexport interface ServerFnProxy {\n name: string;\n id: string;\n method: 'GET' | 'POST';\n}\n\nexport interface ScrubResult {\n code: string;\n proxies: ServerFnProxy[];\n hadDefaultExport: boolean;\n}\n\nconst SERVER_FN_SOURCE = '@analogjs/router/server';\n\nexport function scrubServerFnModule(\n code: string,\n fileId = 'file.ts',\n): ScrubResult | null {\n // Cheap bail-out before parsing: no `serverFn` token, nothing to do.\n if (!code.includes('serverFn')) {\n return null;\n }\n\n const { program } = parseSync(fileId, code);\n const body = (program as { body: any[] }).body;\n\n const serverFnLocalNames = collectServerFnLocalNames(body);\n const hadDefaultExport = body.some(\n (node) => node.type === 'ExportDefaultDeclaration',\n );\n\n const proxies: ServerFnProxy[] = [];\n for (const node of body) {\n if (\n node.type !== 'ExportNamedDeclaration' ||\n node.declaration?.type !== 'VariableDeclaration'\n ) {\n continue;\n }\n for (const declarator of node.declaration.declarations) {\n if (\n declarator.id?.type !== 'Identifier' ||\n declarator.init?.type !== 'CallExpression' ||\n declarator.init.callee?.type !== 'Identifier' ||\n !serverFnLocalNames.has(declarator.init.callee.name)\n ) {\n continue;\n }\n proxies.push(\n toProxy(declarator.id.name, declarator.init.arguments ?? [], fileId),\n );\n }\n }\n\n // Every `serverFn(...)` call in the module must have become a proxy. If one\n // did not, the module uses a shape this transform cannot rewrite (e.g.\n // `const fn = serverFn(...); export { fn };`) and returning `null` would leave\n // it untouched — shipping the handler and its server imports to the browser.\n // Fail the build instead: a scrub that silently opts out is a code leak.\n const callCount = countServerFnCalls(program, serverFnLocalNames);\n if (callCount > proxies.length) {\n throw new Error(\n `[analog] ${fileId}: found ${callCount} serverFn call(s) but could only rewrite ${proxies.length} for the client build. ` +\n `Server functions must be declared as a directly exported const — \\`export const name = serverFn(…)\\` — so the handler can be stripped from the browser bundle.`,\n );\n }\n\n if (proxies.length === 0) {\n return null;\n }\n\n const lines = [\n `import { createServerFnRef } from '@analogjs/router';`,\n '',\n ...proxies.map(\n (p) =>\n `export const ${p.name} = createServerFnRef({ id: ${JSON.stringify(\n p.id,\n )}, method: ${JSON.stringify(p.method)} });`,\n ),\n ];\n // A file that is also a page endpoint keeps the load-shim the router expects.\n if (hadDefaultExport) {\n lines.push('', 'export default undefined;');\n }\n\n return { code: lines.join('\\n') + '\\n', proxies, hadDefaultExport };\n}\n\n/** Local identifier(s) `serverFn` is imported under from `@analogjs/router/server`. */\nfunction collectServerFnLocalNames(body: any[]): Set<string> {\n const names = new Set<string>();\n for (const node of body) {\n if (\n node.type !== 'ImportDeclaration' ||\n node.source?.value !== SERVER_FN_SOURCE\n ) {\n continue;\n }\n for (const spec of node.specifiers ?? []) {\n if (\n spec.type === 'ImportSpecifier' &&\n spec.imported?.name === 'serverFn'\n ) {\n names.add(spec.local.name);\n }\n }\n }\n // Fallback: if the module never imported it (unusual), still match the\n // conventional name so a mis-resolved import doesn't leak server code.\n if (names.size === 0) {\n names.add('serverFn');\n }\n return names;\n}\n\nfunction toProxy(name: string, args: any[], fileId: string): ServerFnProxy {\n const method = methodForCall(name, args, fileId);\n // The id is derived, never read from source — same algorithm the server\n // registration uses, so client proxy and server route always match.\n return { name, id: deriveServerFnId(fileId, name), method };\n}\n\n/**\n * Resolve the HTTP method from the call shape, matching the runtime overloads:\n * `serverFn(handler)` ⇒ GET, `serverFn(schema, handler)` ⇒ POST,\n * `serverFn(config, handler)` ⇒ config-driven.\n */\nfunction methodForCall(\n name: string,\n args: any[],\n fileId: string,\n): 'GET' | 'POST' {\n const arg0 = args[0];\n\n // serverFn(handler) — input-less GET.\n if (isFunctionNode(arg0)) return 'GET';\n\n // serverFn(config, handler) — read method/input off the object.\n if (arg0?.type === 'ObjectExpression') {\n let method: 'GET' | 'POST' | undefined;\n let hasInput = false;\n for (const prop of arg0.properties ?? []) {\n if (prop.type !== 'Property') continue;\n const key =\n prop.key?.type === 'Identifier' ? prop.key.name : prop.key?.value;\n if (key === 'method' && isStringLiteral(prop.value)) {\n method = prop.value.value as 'GET' | 'POST';\n } else if (key === 'input') {\n hasInput = true;\n }\n }\n if (method === 'GET' && hasInput) {\n throw new Error(\n `[analog] serverFn \"${name}\" in ${fileId} declares method: 'GET' with an input schema; GET carries no body. Use POST or drop the input.`,\n );\n }\n return method ?? (hasInput ? 'POST' : 'GET');\n }\n\n // serverFn(schema, handler) — a schema ⇒ POST + input.\n if (arg0 && args.length >= 2) return 'POST';\n\n throw new Error(\n `[analog] serverFn \"${name}\" in ${fileId} must be called as serverFn(handler), serverFn(schema, handler), or serverFn(config, handler).`,\n );\n}\n\nfunction isFunctionNode(node: any): boolean {\n return (\n node?.type === 'ArrowFunctionExpression' ||\n node?.type === 'FunctionExpression'\n );\n}\n\nfunction isStringLiteral(\n node: any,\n): node is { type: 'Literal'; value: string } {\n return node?.type === 'Literal' && typeof node.value === 'string';\n}\n\n/** Every `serverFn(...)` call anywhere in the module, not just exported ones. */\nfunction countServerFnCalls(node: unknown, names: Set<string>): number {\n if (!node || typeof node !== 'object') {\n return 0;\n }\n if (Array.isArray(node)) {\n return node.reduce(\n (total, child) => total + countServerFnCalls(child, names),\n 0,\n );\n }\n\n const record = node as Record<string, unknown> & { type?: string };\n let count = 0;\n if (record.type === 'CallExpression') {\n const callee = record['callee'] as { type?: string; name?: string };\n if (\n callee?.type === 'Identifier' &&\n callee.name &&\n names.has(callee.name)\n ) {\n count += 1;\n }\n }\n for (const key of Object.keys(record)) {\n count += countServerFnCalls(record[key], names);\n }\n return count;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"server-fn-client-transform.js","names":[],"sources":["../../../src/lib/server-fn-client-transform.ts"],"sourcesContent":["import { parseSync } from 'oxc-parser';\n// Light subpath (no vite/nitro barrel): the built scrub is loaded outside a\n// build too, so it must not pull the plugin graph in.\nimport { deriveServerFnId } from './server-fn-id.js';\n\n/**\n * Client-build scrub + proxy generation for server functions.\n *\n * On the client build a `*.server.ts` module must not carry server handler\n * bodies (which `inject()` server services, read the request, hit the database).\n * But the client still imports the named `serverFn` exports to dispatch them.\n * This transform regenerates the module so each `export const NAME = serverFn(\n * config, handler)` becomes a call to the client-safe `createServerFnRef`\n * factory — keeping the `{ id, url, method }` metadata `injectServerFn` needs,\n * dropping the handler and every server-only import. Because the regenerated\n * module references nothing else, the server code tree-shakes away entirely.\n *\n * Returns the new module source, or `null` when the module defines no server\n * function (the caller falls back to the page-endpoint scrub for those).\n */\nexport interface ServerFnProxy {\n name: string;\n id: string;\n method: 'GET' | 'POST';\n}\n\nexport interface ScrubResult {\n code: string;\n proxies: ServerFnProxy[];\n hadDefaultExport: boolean;\n}\n\nconst SERVER_FN_SOURCE = '@analogjs/router/server';\n\nexport function scrubServerFnModule(\n code: string,\n fileId = 'file.ts',\n): ScrubResult | null {\n // Cheap bail-out before parsing: no `serverFn` token, nothing to do.\n if (!code.includes('serverFn')) {\n return null;\n }\n\n const { program } = parseSync(fileId, code);\n const body = (program as { body: any[] }).body;\n\n const serverFnLocalNames = collectServerFnLocalNames(body);\n const hadDefaultExport = body.some(\n (node) => node.type === 'ExportDefaultDeclaration',\n );\n\n const proxies: ServerFnProxy[] = [];\n for (const node of body) {\n if (\n node.type !== 'ExportNamedDeclaration' ||\n node.declaration?.type !== 'VariableDeclaration'\n ) {\n continue;\n }\n for (const declarator of node.declaration.declarations) {\n if (\n declarator.id?.type !== 'Identifier' ||\n declarator.init?.type !== 'CallExpression' ||\n declarator.init.callee?.type !== 'Identifier' ||\n !serverFnLocalNames.has(declarator.init.callee.name)\n ) {\n continue;\n }\n proxies.push(\n toProxy(declarator.id.name, declarator.init.arguments ?? [], fileId),\n );\n }\n }\n\n // Every `serverFn(...)` call in the module must have become a proxy. If one\n // did not, the module uses a shape this transform cannot rewrite (e.g.\n // `const fn = serverFn(...); export { fn };`) and returning `null` would leave\n // it untouched — shipping the handler and its server imports to the browser.\n // Fail the build instead: a scrub that silently opts out is a code leak.\n const callCount = countServerFnCalls(program, serverFnLocalNames);\n if (callCount > proxies.length) {\n throw new Error(\n `[analog] ${fileId}: found ${callCount} serverFn call(s) but could only rewrite ${proxies.length} for the client build. ` +\n `Server functions must be declared as a directly exported const — \\`export const name = serverFn(…)\\` — so the handler can be stripped from the browser bundle.`,\n );\n }\n\n if (proxies.length === 0) {\n return null;\n }\n\n const lines = [\n `import { createServerFnRef } from '@analogjs/router';`,\n '',\n ...proxies.map(\n (p) =>\n `export const ${p.name} = createServerFnRef({ id: ${JSON.stringify(\n p.id,\n )}, method: ${JSON.stringify(p.method)} });`,\n ),\n ];\n // A file that is also a page endpoint keeps the load-shim the router expects.\n if (hadDefaultExport) {\n lines.push('', 'export default undefined;');\n }\n\n return { code: lines.join('\\n') + '\\n', proxies, hadDefaultExport };\n}\n\n/** Local identifier(s) `serverFn` is imported under from `@analogjs/router/server`. */\nfunction collectServerFnLocalNames(body: any[]): Set<string> {\n const names = new Set<string>();\n for (const node of body) {\n if (\n node.type !== 'ImportDeclaration' ||\n node.source?.value !== SERVER_FN_SOURCE\n ) {\n continue;\n }\n for (const spec of node.specifiers ?? []) {\n if (\n spec.type === 'ImportSpecifier' &&\n spec.imported?.name === 'serverFn'\n ) {\n names.add(spec.local.name);\n }\n }\n }\n // Fallback: if the module never imported it (unusual), still match the\n // conventional name so a mis-resolved import doesn't leak server code.\n if (names.size === 0) {\n names.add('serverFn');\n }\n return names;\n}\n\nfunction toProxy(name: string, args: any[], fileId: string): ServerFnProxy {\n const method = methodForCall(name, args, fileId);\n // The id is derived, never read from source — same algorithm the server\n // registration uses, so client proxy and server route always match.\n return { name, id: deriveServerFnId(fileId, name), method };\n}\n\n/**\n * Resolve the HTTP method from the call shape, matching the runtime overloads:\n * `serverFn(handler)` ⇒ GET, `serverFn(schema, handler)` ⇒ POST,\n * `serverFn(config, handler)` ⇒ config-driven.\n */\nfunction methodForCall(\n name: string,\n args: any[],\n fileId: string,\n): 'GET' | 'POST' {\n const arg0 = args[0];\n\n // serverFn(handler) — input-less GET.\n if (isFunctionNode(arg0)) return 'GET';\n\n // serverFn(config, handler) — read method/input off the object.\n if (arg0?.type === 'ObjectExpression') {\n let method: 'GET' | 'POST' | undefined;\n let hasInput = false;\n for (const prop of arg0.properties ?? []) {\n if (prop.type !== 'Property') continue;\n const key =\n prop.key?.type === 'Identifier' ? prop.key.name : prop.key?.value;\n if (key === 'method' && isStringLiteral(prop.value)) {\n method = prop.value.value as 'GET' | 'POST';\n } else if (key === 'input') {\n hasInput = true;\n }\n }\n if (method === 'GET' && hasInput) {\n throw new Error(\n `[analog] serverFn \"${name}\" in ${fileId} declares method: 'GET' with an input schema; GET carries no body. Use POST or drop the input.`,\n );\n }\n return method ?? (hasInput ? 'POST' : 'GET');\n }\n\n // serverFn(schema, handler) — a schema ⇒ POST + input.\n if (arg0 && args.length >= 2) return 'POST';\n\n throw new Error(\n `[analog] serverFn \"${name}\" in ${fileId} must be called as serverFn(handler), serverFn(schema, handler), or serverFn(config, handler).`,\n );\n}\n\nfunction isFunctionNode(node: any): boolean {\n return (\n node?.type === 'ArrowFunctionExpression' ||\n node?.type === 'FunctionExpression'\n );\n}\n\nfunction isStringLiteral(\n node: any,\n): node is { type: 'Literal'; value: string } {\n return node?.type === 'Literal' && typeof node.value === 'string';\n}\n\n/** Every `serverFn(...)` call anywhere in the module, not just exported ones. */\nfunction countServerFnCalls(node: unknown, names: Set<string>): number {\n if (!node || typeof node !== 'object') {\n return 0;\n }\n if (Array.isArray(node)) {\n return node.reduce(\n (total, child) => total + countServerFnCalls(child, names),\n 0,\n );\n }\n\n const record = node as Record<string, unknown> & { type?: string };\n let count = 0;\n if (record.type === 'CallExpression') {\n const callee = record['callee'] as { type?: string; name?: string };\n if (\n callee?.type === 'Identifier' &&\n callee.name &&\n names.has(callee.name)\n ) {\n count += 1;\n }\n }\n for (const key of Object.keys(record)) {\n count += countServerFnCalls(record[key], names);\n }\n return count;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server-fn-id.js","names":[],"sources":["../../../src/lib/server-fn-id.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { relative } from 'node:path';\n\n// Intentionally dependency-light (node built-ins only): this module is loaded by\n// the client scrub, so it must not drag in vite/nitro. POSIX-normalize inline.\nconst toPosix = (p: string): string => p.replace(/\\\\/g, '/');\n\n/**\n * Server-function ids are derived, never author-chosen, for security:\n *\n * - **Collision-free.** `hash(fileId + exportName)` is unique per file+export, so\n * two functions can never share a route and hijack each other's dispatch — a\n * real risk with hand-picked ids where two authors independently pick `getUser`.\n * - **Non-enumerable.** The public route is an opaque digest, not a guessable\n * verb like `/_analog/fn/deleteAccount`, shrinking the discoverable surface.\n * - **Not author-controlled.** Authors cannot expose a meaningful or duplicate\n * route by accident; the id is a pure function of location.\n *\n * Both build transforms (server registration + client proxy) MUST derive the\n * same id, so this is the single source of truth for the algorithm and the\n * `fileId` must be the **project-root-relative** POSIX path — identical across\n * the client, SSR, and Nitro builds regardless of absolute checkout location.\n */\n\n/** Project-root-relative POSIX module path used as the stable hash input. */\nexport function serverFnFileId(absPath: string, projectRoot: string): string {\n return toPosix(relative(projectRoot, absPath));\n}\n\n/**\n * Derives the opaque, collision-free id for a server function from its\n * project-root-relative file id and export name. 16 hex chars (64 bits) is far\n * beyond birthday-collision range for any realistic function count while keeping\n * the route short.\n */\nexport function deriveServerFnId(fileId: string, exportName: string): string {\n return createHash('sha256')\n .update(`${fileId}#${exportName}`)\n .digest('hex')\n .slice(0, 16);\n}\n"],"mappings":";;;AAKA,IAAM,WAAW,MAAsB,EAAE,QAAQ,OAAO,IAAI;;;;;;;;;;;;;;;;;;AAoB5D,SAAgB,eAAe,SAAiB,aAA6B;AAC3E,QAAO,QAAQ,SAAS,aAAa,QAAQ,CAAC;;;;;;;;AAShD,SAAgB,iBAAiB,QAAgB,YAA4B;AAC3E,QAAO,WAAW,SAAS,CACxB,OAAO,GAAG,OAAO,GAAG,aAAa,CACjC,OAAO,MAAM,CACb,MAAM,GAAG,GAAG"}
1
+ {"version":3,"file":"server-fn-id.js","names":[],"sources":["../../../src/lib/server-fn-id.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { relative } from 'node:path';\n\n// Intentionally dependency-light (node built-ins only): this module is loaded by\n// the client scrub, so it must not drag in vite/nitro. POSIX-normalize inline.\nconst toPosix = (p: string): string => p.replace(/\\\\/g, '/');\n\n/**\n * Server-function ids are derived, never author-chosen, for security:\n *\n * - **Collision-free.** `hash(fileId + exportName)` is unique per file+export, so\n * two functions can never share a route and hijack each other's dispatch — a\n * real risk with hand-picked ids where two authors independently pick `getUser`.\n * - **Non-enumerable.** The public route is an opaque digest, not a guessable\n * verb like `/_analog/fn/deleteAccount`, shrinking the discoverable surface.\n * - **Not author-controlled.** Authors cannot expose a meaningful or duplicate\n * route by accident; the id is a pure function of location.\n *\n * Both build transforms (server registration + client proxy) MUST derive the\n * same id, so this is the single source of truth for the algorithm and the\n * `fileId` must be the **project-root-relative** POSIX path — identical across\n * the client, SSR, and Nitro builds regardless of absolute checkout location.\n */\n\n/** Project-root-relative POSIX module path used as the stable hash input. */\nexport function serverFnFileId(absPath: string, projectRoot: string): string {\n return toPosix(relative(projectRoot, absPath));\n}\n\n/**\n * Derives the opaque, collision-free id for a server function from its\n * project-root-relative file id and export name. 16 hex chars (64 bits) is far\n * beyond birthday-collision range for any realistic function count while keeping\n * the route short.\n */\nexport function deriveServerFnId(fileId: string, exportName: string): string {\n return createHash('sha256')\n .update(`${fileId}#${exportName}`)\n .digest('hex')\n .slice(0, 16);\n}\n"],"mappings":";;;AAKA,IAAM,WAAW,MAAsB,EAAE,QAAQ,OAAO,GAAG;;;;;;;;;;;;;;;;;;AAoB3D,SAAgB,eAAe,SAAiB,aAA6B;CAC3E,OAAO,QAAQ,SAAS,aAAa,OAAO,CAAC;AAC/C;;;;;;;AAQA,SAAgB,iBAAiB,QAAgB,YAA4B;CAC3E,OAAO,WAAW,QAAQ,CAAC,CACxB,OAAO,GAAG,OAAO,GAAG,YAAY,CAAC,CACjC,OAAO,KAAK,CAAC,CACb,MAAM,GAAG,EAAE;AAChB"}
@@ -1 +1 @@
1
- {"version":3,"file":"defer-streaming-plugin.js","names":[],"sources":["../../../../src/lib/ssr/defer-streaming-plugin.ts"],"sourcesContent":["import type { Plugin } from 'vite';\nimport { parseSync, type Argument, type Statement } from 'oxc-parser';\n\n/** Minimum Angular major qualified for streaming; the actual runtime shape is checked below. */\nexport const MIN_STREAMING_ANGULAR_MAJOR = 21;\n\n/**\n * Whether the streaming SSR patch can be applied to the installed Angular.\n * A `null` major (version undetectable) returns `true`: don't block on a failed\n * detection — the plugin's anchor-drift detection catches a real mismatch at\n * build time and falls back to buffered rendering.\n */\nexport function streamingSupportedOnAngular(major: number | null): boolean {\n return major === null || major >= MIN_STREAMING_ANGULAR_MAJOR;\n}\n\ntype DeferAnalysis =\n | { kind: 'not-target' }\n | { kind: 'patchable'; offset: number }\n | { kind: 'drifted'; reason: string };\n\nfunction analyzeDeferRuntime(code: string): DeferAnalysis {\n if (!code.includes('function applyDeferBlockState('))\n return { kind: 'not-target' };\n const parsed = parseSync('angular-core.mjs', code);\n if (parsed.errors.length)\n return { kind: 'drifted', reason: 'invalid Angular core JavaScript' };\n const fn = parsed.program.body.find(\n (node) =>\n node.type === 'FunctionDeclaration' &&\n node.id?.name === 'applyDeferBlockState',\n );\n if (!fn || fn.type !== 'FunctionDeclaration' || !fn.body)\n return { kind: 'not-target' };\n const names = fn.params\n .map((param) => (param.type === 'Identifier' ? param.name : ''))\n .join(',');\n if (names !== 'newState,lDetails,lContainer,tNode,hostLView')\n return {\n kind: 'drifted',\n reason: 'changed applyDeferBlockState parameters',\n };\n const collector = parsed.program.body.some(\n (node) =>\n node.type === 'FunctionDeclaration' &&\n node.id?.name === 'collectNativeNodesInLContainer',\n );\n if (!collector)\n return {\n kind: 'drifted',\n reason: 'missing collectNativeNodesInLContainer',\n };\n const offset = profilerEndOffset(fn.body.body[fn.body.body.length - 1]);\n return offset === undefined\n ? { kind: 'drifted', reason: 'missing DeferBlockStateEnd profiler anchor' }\n : { kind: 'patchable', offset };\n}\n\nfunction profilerEndOffset(\n statement: Statement | undefined,\n): number | undefined {\n if (\n statement?.type !== 'ExpressionStatement' ||\n statement.expression.type !== 'CallExpression'\n )\n return;\n const call = statement.expression;\n if (\n call.callee.type !== 'Identifier' ||\n call.callee.name !== 'profiler' ||\n call.arguments.length !== 1\n )\n return;\n return isEndEvent(call.arguments[0]) ? statement.start : undefined;\n}\n\nfunction isEndEvent(event: Argument | undefined): boolean {\n if (event?.type === 'Literal') return typeof event.value === 'number';\n return (\n event?.type === 'MemberExpression' &&\n !event.computed &&\n event.object.type === 'Identifier' &&\n event.object.name === 'ProfilerEvent' &&\n event.property.type === 'Identifier' &&\n event.property.name === 'DeferBlockStateEnd'\n );\n}\n\nfunction patchDeferRuntime(code: string, offset: number): string {\n if (code.includes('globalThis.__analogSsrDeferCapture({')) return code;\n const capture =\n `try { if (newState === DeferBlockState.Complete && ` +\n `typeof ngServerMode !== 'undefined' && ngServerMode && ` +\n `typeof globalThis.__analogSsrDeferCapture === 'function') { ` +\n `globalThis.__analogSsrDeferCapture({ ssrUniqueId: lDetails[SSR_UNIQUE_ID], lContainer, hostLView }); } } catch (e) {}\\n `;\n return (\n code.slice(0, offset) +\n capture +\n code.slice(offset) +\n '\\nglobalThis.__analogSsrInternals = Object.assign(globalThis.__analogSsrInternals || {}, { collectNativeNodesInLContainer });\\n'\n );\n}\n\n/** Inject capture before the named defer function's final profiler call, preserving its original source. */\nexport function injectDeferStreamingHook(code: string): string | null {\n const info = analyzeDeferRuntime(code);\n return info.kind === 'patchable'\n ? patchDeferRuntime(code, info.offset)\n : null;\n}\n\n/** Inspect the named function's final profiler call without depending on enum ordinals. */\nexport function inspectAngularCoreModule(\n code: string,\n):\n | { kind: 'not-target' }\n | { kind: 'patchable' }\n | { kind: 'drifted'; reason: string } {\n const info = analyzeDeferRuntime(code);\n return info.kind === 'patchable' ? { kind: 'patchable' } : info;\n}\n\n/**\n * Vite plugin that applies {@link injectDeferStreamingHook} to `@angular/core`\n * during SSR builds, so `@analogjs/router`'s `renderStream` can flush `@defer`\n * blocks as they resolve on the server — without hand-patching node_modules.\n *\n * Only active for SSR. Mirrors the enforce/filter/ssr-gate shape of\n * `i18nDefRegistryPlugin`. If the `@defer` runtime module is found but its\n * anchors have drifted (Angular changed internals), or if it is never\n * encountered at all, the plugin warns rather than silently producing a build\n * that falls back to buffered rendering.\n */\nexport function deferStreamingPlugin(): Plugin {\n const applied = new Set<string>();\n const warnedDrift = new Set<string>();\n return {\n name: 'analogjs-defer-streaming',\n enforce: 'post',\n config() {\n return {\n ssr: { noExternal: ['@angular/core'] },\n environments: { ssr: { optimizeDeps: { exclude: ['@angular/core'] } } },\n };\n },\n transform: {\n filter: {\n id: /\\/@angular\\/core\\//,\n },\n handler(code, _id, options) {\n if (!options?.ssr) return;\n const environment = this.environment.name;\n const info = analyzeDeferRuntime(code);\n if (info.kind === 'not-target') return;\n if (info.kind === 'drifted') {\n if (!warnedDrift.has(environment)) {\n warnedDrift.add(environment);\n this.warn(\n `experimental streaming SSR: found @angular/core's @defer runtime ` +\n `but could not apply the resolution hook (${info.reason}). The ` +\n `installed Angular version likely changed internals the patch ` +\n `depends on; streaming will fall back to buffered rendering.`,\n );\n }\n return;\n }\n const out = patchDeferRuntime(code, info.offset);\n applied.add(environment);\n return { code: out };\n },\n },\n buildEnd() {\n // Require coverage from the SSR service. A later Nitro rebundle may\n // also encounter Angular modules imported by endpoint handlers.\n const environment = this.environment.name;\n if (environment !== 'ssr') return;\n if (!applied.has(environment) && !warnedDrift.has(environment)) {\n this.warn(\n `experimental streaming SSR is enabled but @angular/core's @defer ` +\n `runtime module was never encountered during the SSR build, so the ` +\n `resolution hook was not injected. Streaming will fall back to ` +\n `buffered rendering.`,\n );\n }\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"defer-streaming-plugin.js","names":[],"sources":["../../../../src/lib/ssr/defer-streaming-plugin.ts"],"sourcesContent":["import type { Plugin } from 'vite';\nimport { parseSync, type Argument, type Statement } from 'oxc-parser';\n\n/** Minimum Angular major qualified for streaming; the actual runtime shape is checked below. */\nexport const MIN_STREAMING_ANGULAR_MAJOR = 21;\n\n/**\n * Whether the streaming SSR patch can be applied to the installed Angular.\n * A `null` major (version undetectable) returns `true`: don't block on a failed\n * detection — the plugin's anchor-drift detection catches a real mismatch at\n * build time and falls back to buffered rendering.\n */\nexport function streamingSupportedOnAngular(major: number | null): boolean {\n return major === null || major >= MIN_STREAMING_ANGULAR_MAJOR;\n}\n\ntype DeferAnalysis =\n | { kind: 'not-target' }\n | { kind: 'patchable'; offset: number }\n | { kind: 'drifted'; reason: string };\n\nfunction analyzeDeferRuntime(code: string): DeferAnalysis {\n if (!code.includes('function applyDeferBlockState('))\n return { kind: 'not-target' };\n const parsed = parseSync('angular-core.mjs', code);\n if (parsed.errors.length)\n return { kind: 'drifted', reason: 'invalid Angular core JavaScript' };\n const fn = parsed.program.body.find(\n (node) =>\n node.type === 'FunctionDeclaration' &&\n node.id?.name === 'applyDeferBlockState',\n );\n if (!fn || fn.type !== 'FunctionDeclaration' || !fn.body)\n return { kind: 'not-target' };\n const names = fn.params\n .map((param) => (param.type === 'Identifier' ? param.name : ''))\n .join(',');\n if (names !== 'newState,lDetails,lContainer,tNode,hostLView')\n return {\n kind: 'drifted',\n reason: 'changed applyDeferBlockState parameters',\n };\n const collector = parsed.program.body.some(\n (node) =>\n node.type === 'FunctionDeclaration' &&\n node.id?.name === 'collectNativeNodesInLContainer',\n );\n if (!collector)\n return {\n kind: 'drifted',\n reason: 'missing collectNativeNodesInLContainer',\n };\n const offset = profilerEndOffset(fn.body.body[fn.body.body.length - 1]);\n return offset === undefined\n ? { kind: 'drifted', reason: 'missing DeferBlockStateEnd profiler anchor' }\n : { kind: 'patchable', offset };\n}\n\nfunction profilerEndOffset(\n statement: Statement | undefined,\n): number | undefined {\n if (\n statement?.type !== 'ExpressionStatement' ||\n statement.expression.type !== 'CallExpression'\n )\n return;\n const call = statement.expression;\n if (\n call.callee.type !== 'Identifier' ||\n call.callee.name !== 'profiler' ||\n call.arguments.length !== 1\n )\n return;\n return isEndEvent(call.arguments[0]) ? statement.start : undefined;\n}\n\nfunction isEndEvent(event: Argument | undefined): boolean {\n if (event?.type === 'Literal') return typeof event.value === 'number';\n return (\n event?.type === 'MemberExpression' &&\n !event.computed &&\n event.object.type === 'Identifier' &&\n event.object.name === 'ProfilerEvent' &&\n event.property.type === 'Identifier' &&\n event.property.name === 'DeferBlockStateEnd'\n );\n}\n\nfunction patchDeferRuntime(code: string, offset: number): string {\n if (code.includes('globalThis.__analogSsrDeferCapture({')) return code;\n const capture =\n `try { if (newState === DeferBlockState.Complete && ` +\n `typeof ngServerMode !== 'undefined' && ngServerMode && ` +\n `typeof globalThis.__analogSsrDeferCapture === 'function') { ` +\n `globalThis.__analogSsrDeferCapture({ ssrUniqueId: lDetails[SSR_UNIQUE_ID], lContainer, hostLView }); } } catch (e) {}\\n `;\n return (\n code.slice(0, offset) +\n capture +\n code.slice(offset) +\n '\\nglobalThis.__analogSsrInternals = Object.assign(globalThis.__analogSsrInternals || {}, { collectNativeNodesInLContainer });\\n'\n );\n}\n\n/** Inject capture before the named defer function's final profiler call, preserving its original source. */\nexport function injectDeferStreamingHook(code: string): string | null {\n const info = analyzeDeferRuntime(code);\n return info.kind === 'patchable'\n ? patchDeferRuntime(code, info.offset)\n : null;\n}\n\n/** Inspect the named function's final profiler call without depending on enum ordinals. */\nexport function inspectAngularCoreModule(\n code: string,\n):\n | { kind: 'not-target' }\n | { kind: 'patchable' }\n | { kind: 'drifted'; reason: string } {\n const info = analyzeDeferRuntime(code);\n return info.kind === 'patchable' ? { kind: 'patchable' } : info;\n}\n\n/**\n * Vite plugin that applies {@link injectDeferStreamingHook} to `@angular/core`\n * during SSR builds, so `@analogjs/router`'s `renderStream` can flush `@defer`\n * blocks as they resolve on the server — without hand-patching node_modules.\n *\n * Only active for SSR. Mirrors the enforce/filter/ssr-gate shape of\n * `i18nDefRegistryPlugin`. If the `@defer` runtime module is found but its\n * anchors have drifted (Angular changed internals), or if it is never\n * encountered at all, the plugin warns rather than silently producing a build\n * that falls back to buffered rendering.\n */\nexport function deferStreamingPlugin(): Plugin {\n const applied = new Set<string>();\n const warnedDrift = new Set<string>();\n return {\n name: 'analogjs-defer-streaming',\n enforce: 'post',\n config() {\n return {\n ssr: { noExternal: ['@angular/core'] },\n environments: { ssr: { optimizeDeps: { exclude: ['@angular/core'] } } },\n };\n },\n transform: {\n filter: {\n id: /\\/@angular\\/core\\//,\n },\n handler(code, _id, options) {\n if (!options?.ssr) return;\n const environment = this.environment.name;\n const info = analyzeDeferRuntime(code);\n if (info.kind === 'not-target') return;\n if (info.kind === 'drifted') {\n if (!warnedDrift.has(environment)) {\n warnedDrift.add(environment);\n this.warn(\n `experimental streaming SSR: found @angular/core's @defer runtime ` +\n `but could not apply the resolution hook (${info.reason}). The ` +\n `installed Angular version likely changed internals the patch ` +\n `depends on; streaming will fall back to buffered rendering.`,\n );\n }\n return;\n }\n const out = patchDeferRuntime(code, info.offset);\n applied.add(environment);\n return { code: out };\n },\n },\n buildEnd() {\n // Require coverage from the SSR service. A later Nitro rebundle may\n // also encounter Angular modules imported by endpoint handlers.\n const environment = this.environment.name;\n if (environment !== 'ssr') return;\n if (!applied.has(environment) && !warnedDrift.has(environment)) {\n this.warn(\n `experimental streaming SSR is enabled but @angular/core's @defer ` +\n `runtime module was never encountered during the SSR build, so the ` +\n `resolution hook was not injected. Streaming will fall back to ` +\n `buffered rendering.`,\n );\n }\n },\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"inject-html-plugin.js","names":[],"sources":["../../../../src/lib/ssr/inject-html-plugin.ts"],"sourcesContent":["import { Plugin } from 'vite';\nimport { readFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\n\nfunction eventReplayPlugin(): Plugin {\n return {\n name: 'analogjs-event-replay-plugin',\n async transformIndexHtml() {\n const eventReplayScript = await readFile(\n require.resolve('@angular/core/event-dispatch-contract.min.js'),\n 'utf-8',\n );\n\n return [\n {\n tag: 'script',\n attrs: {\n type: 'text/javascript',\n id: 'ng-event-dispatch-contract',\n },\n children: eventReplayScript,\n injectTo: 'body',\n },\n ];\n },\n };\n}\n\nexport function injectHTMLPlugin(): Plugin[] {\n return [eventReplayPlugin()];\n}\n"],"mappings":";;;AAIA,IAAM,UAAU,cAAc,OAAO,KAAK,IAAI;AAE9C,SAAS,oBAA4B;AACnC,QAAO;EACL,MAAM;EACN,MAAM,qBAAqB;AAMzB,UAAO,CACL;IACE,KAAK;IACL,OAAO;KACL,MAAM;KACN,IAAI;KACL;IACD,UAZsB,MAAM,SAC9B,QAAQ,QAAQ,+CAA+C,EAC/D,QACD;IAUG,UAAU;IACX,CACF;;EAEJ;;AAGH,SAAgB,mBAA6B;AAC3C,QAAO,CAAC,mBAAmB,CAAC"}
1
+ {"version":3,"file":"inject-html-plugin.js","names":[],"sources":["../../../../src/lib/ssr/inject-html-plugin.ts"],"sourcesContent":["import { Plugin } from 'vite';\nimport { readFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\n\nfunction eventReplayPlugin(): Plugin {\n return {\n name: 'analogjs-event-replay-plugin',\n async transformIndexHtml() {\n const eventReplayScript = await readFile(\n require.resolve('@angular/core/event-dispatch-contract.min.js'),\n 'utf-8',\n );\n\n return [\n {\n tag: 'script',\n attrs: {\n type: 'text/javascript',\n id: 'ng-event-dispatch-contract',\n },\n children: eventReplayScript,\n injectTo: 'body',\n },\n ];\n },\n };\n}\n\nexport function injectHTMLPlugin(): Plugin[] {\n return [eventReplayPlugin()];\n}\n"],"mappings":";;;AAIA,IAAM,UAAU,cAAc,YAAY,GAAG;AAE7C,SAAS,oBAA4B;CACnC,OAAO;EACL,MAAM;EACN,MAAM,qBAAqB;GAMzB,OAAO,CACL;IACE,KAAK;IACL,OAAO;KACL,MAAM;KACN,IAAI;IACN;IACA,UAAU,MAZkB,SAC9B,QAAQ,QAAQ,8CAA8C,GAC9D,OACF;IAUI,UAAU;GACZ,CACF;EACF;CACF;AACF;AAEA,SAAgB,mBAA6B;CAC3C,OAAO,CAAC,kBAAkB,CAAC;AAC7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-build-plugin.js","names":[],"sources":["../../../../src/lib/ssr/ssr-build-plugin.ts"],"sourcesContent":["import * as vite from 'vite';\n\n/**\n * SSR build patches for Angular platform-server, Zone.js, and Domino.\n *\n * **Why each patch exists:**\n * - zone-node: removes a `const global = globalThis` alias that shadows the\n * real global in strict mode.\n * - platform-server: rewrites `xhr2.XMLHttpRequest` for CJS/ESM compat and\n * replaces bare `global` references with `globalThis`.\n * - xhr2: strips Node-specific `os`/`process` references for SSR bundling.\n * - domino/sloppy.js: replaces `with()` statements that are illegal in ESM.\n */\nexport function ssrBuildPlugin(): vite.Plugin[] {\n const plugins: vite.Plugin[] = [\n {\n name: 'analogjs-ssr-build-plugin',\n apply: 'build',\n config() {\n return {\n ssr: {\n noExternal: ['xhr2'],\n },\n };\n },\n transform: {\n filter: {\n id: /zone-node|platform-server|xhr2\\.js|domino\\/lib\\/sloppy\\.js/,\n },\n handler(code, id) {\n if (\n id.includes('zone-node') &&\n code.includes('const global = globalThis;')\n ) {\n return {\n code: code.replace('const global = globalThis;', ''),\n };\n }\n\n if (id.includes('platform-server')) {\n let result = code.replace(\n 'new xhr2.XMLHttpRequest',\n 'new (xhr2.default.XMLHttpRequest || xhr2.default)',\n );\n\n // Narrowly scoped text replacement of the bare `global`\n // identifier. Matching `global.`, `global,` and ` global[`\n // targets the known occurrences in the @angular/platform-server\n // bundle while leaving strings, comments and asset paths that\n // merely contain the word `global` untouched (see #2426).\n result = result\n .replaceAll('global.', 'globalThis.')\n .replaceAll('global,', 'globalThis,')\n .replaceAll(' global[', ' globalThis[');\n\n return { code: result };\n }\n\n if (id.includes(vite.normalizePath('xhr2.js'))) {\n return {\n code: code\n .replace('os.type()', `''`)\n .replace('os.arch()', `''`)\n .replace('process.versions.node', `'node'`)\n .replace('process.versions.v8', `'v8'`),\n };\n }\n\n if (id.includes(vite.normalizePath('domino/lib/sloppy.js'))) {\n return {\n code: code.replace(/with\\(/gi, 'if('),\n };\n }\n\n return;\n },\n },\n },\n ];\n\n return plugins;\n}\n\n/**\n * Vite plugin that patches `@angular/core`'s `getComponentId()` to\n * mirror every compiled component definition to a global Set, bypassing\n * the `ngServerMode` guard that normally prevents registration on the\n * server. The set lives at `globalThis.__ngComponentDefs` and is read by\n * `@analogjs/router`'s SSR render function to null cached `tView`\n * objects between requests so that `$localize` tagged templates in\n * `consts()` re-evaluate with the freshly loaded translations.\n *\n * Only active when the platform's `i18n` option is configured.\n * Only transforms `@angular/core` modules in SSR builds.\n */\nexport function i18nDefRegistryPlugin(): vite.Plugin {\n let fixedLocale = false;\n const DETECT_MARKER = 'GENERATED_COMP_IDS.set(compId, componentDef.type);';\n const RETURN_STMT = 'return compId;\\n}';\n\n return {\n name: 'analogjs-i18n-def-registry',\n enforce: 'post',\n configResolved(config) {\n const locale = config.define?.['ANALOG_I18N_FIXED_LOCALE'];\n fixedLocale =\n config.command === 'build' && !!locale && locale !== 'undefined';\n },\n\n transform: {\n filter: {\n id: /\\/@angular\\/core\\//,\n },\n handler(code, id, options) {\n if (fixedLocale || !options?.ssr) return;\n if (!code.includes(DETECT_MARKER)) return;\n\n return {\n code: code.replace(\n RETURN_STMT,\n '(globalThis.__ngComponentDefs ??= new Set()).add(componentDef);\\n' +\n RETURN_STMT,\n ),\n };\n },\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,iBAAgC;AAmE9C,QAlE+B,CAC7B;EACE,MAAM;EACN,OAAO;EACP,SAAS;AACP,UAAO,EACL,KAAK,EACH,YAAY,CAAC,OAAO,EACrB,EACF;;EAEH,WAAW;GACT,QAAQ,EACN,IAAI,8DACL;GACD,QAAQ,MAAM,IAAI;AAChB,QACE,GAAG,SAAS,YAAY,IACxB,KAAK,SAAS,6BAA6B,CAE3C,QAAO,EACL,MAAM,KAAK,QAAQ,8BAA8B,GAAG,EACrD;AAGH,QAAI,GAAG,SAAS,kBAAkB,EAAE;KAClC,IAAI,SAAS,KAAK,QAChB,2BACA,oDACD;AAOD,cAAS,OACN,WAAW,WAAW,cAAc,CACpC,WAAW,WAAW,cAAc,CACpC,WAAW,YAAY,eAAe;AAEzC,YAAO,EAAE,MAAM,QAAQ;;AAGzB,QAAI,GAAG,SAAS,KAAK,cAAc,UAAU,CAAC,CAC5C,QAAO,EACL,MAAM,KACH,QAAQ,aAAa,KAAK,CAC1B,QAAQ,aAAa,KAAK,CAC1B,QAAQ,yBAAyB,SAAS,CAC1C,QAAQ,uBAAuB,OAAO,EAC1C;AAGH,QAAI,GAAG,SAAS,KAAK,cAAc,uBAAuB,CAAC,CACzD,QAAO,EACL,MAAM,KAAK,QAAQ,YAAY,MAAM,EACtC;;GAKN;EACF,CACF;;;;;;;;;;;;;;AAiBH,SAAgB,wBAAqC;CACnD,IAAI,cAAc;CAClB,MAAM,gBAAgB;CACtB,MAAM,cAAc;AAEpB,QAAO;EACL,MAAM;EACN,SAAS;EACT,eAAe,QAAQ;GACrB,MAAM,SAAS,OAAO,SAAS;AAC/B,iBACE,OAAO,YAAY,WAAW,CAAC,CAAC,UAAU,WAAW;;EAGzD,WAAW;GACT,QAAQ,EACN,IAAI,sBACL;GACD,QAAQ,MAAM,IAAI,SAAS;AACzB,QAAI,eAAe,CAAC,SAAS,IAAK;AAClC,QAAI,CAAC,KAAK,SAAS,cAAc,CAAE;AAEnC,WAAO,EACL,MAAM,KAAK,QACT,aACA,sEACE,YACH,EACF;;GAEJ;EACF"}
1
+ {"version":3,"file":"ssr-build-plugin.js","names":[],"sources":["../../../../src/lib/ssr/ssr-build-plugin.ts"],"sourcesContent":["import * as vite from 'vite';\n\n/**\n * SSR build patches for Angular platform-server, Zone.js, and Domino.\n *\n * **Why each patch exists:**\n * - zone-node: removes a `const global = globalThis` alias that shadows the\n * real global in strict mode.\n * - platform-server: rewrites `xhr2.XMLHttpRequest` for CJS/ESM compat and\n * replaces bare `global` references with `globalThis`.\n * - xhr2: strips Node-specific `os`/`process` references for SSR bundling.\n * - domino/sloppy.js: replaces `with()` statements that are illegal in ESM.\n */\nexport function ssrBuildPlugin(): vite.Plugin[] {\n const plugins: vite.Plugin[] = [\n {\n name: 'analogjs-ssr-build-plugin',\n apply: 'build',\n config() {\n return {\n ssr: {\n noExternal: ['xhr2'],\n },\n };\n },\n transform: {\n filter: {\n id: /zone-node|platform-server|xhr2\\.js|domino\\/lib\\/sloppy\\.js/,\n },\n handler(code, id) {\n if (\n id.includes('zone-node') &&\n code.includes('const global = globalThis;')\n ) {\n return {\n code: code.replace('const global = globalThis;', ''),\n };\n }\n\n if (id.includes('platform-server')) {\n let result = code.replace(\n 'new xhr2.XMLHttpRequest',\n 'new (xhr2.default.XMLHttpRequest || xhr2.default)',\n );\n\n // Narrowly scoped text replacement of the bare `global`\n // identifier. Matching `global.`, `global,` and ` global[`\n // targets the known occurrences in the @angular/platform-server\n // bundle while leaving strings, comments and asset paths that\n // merely contain the word `global` untouched (see #2426).\n result = result\n .replaceAll('global.', 'globalThis.')\n .replaceAll('global,', 'globalThis,')\n .replaceAll(' global[', ' globalThis[');\n\n return { code: result };\n }\n\n if (id.includes(vite.normalizePath('xhr2.js'))) {\n return {\n code: code\n .replace('os.type()', `''`)\n .replace('os.arch()', `''`)\n .replace('process.versions.node', `'node'`)\n .replace('process.versions.v8', `'v8'`),\n };\n }\n\n if (id.includes(vite.normalizePath('domino/lib/sloppy.js'))) {\n return {\n code: code.replace(/with\\(/gi, 'if('),\n };\n }\n\n return;\n },\n },\n },\n ];\n\n return plugins;\n}\n\n/**\n * Vite plugin that patches `@angular/core`'s `getComponentId()` to\n * mirror every compiled component definition to a global Set, bypassing\n * the `ngServerMode` guard that normally prevents registration on the\n * server. The set lives at `globalThis.__ngComponentDefs` and is read by\n * `@analogjs/router`'s SSR render function to null cached `tView`\n * objects between requests so that `$localize` tagged templates in\n * `consts()` re-evaluate with the freshly loaded translations.\n *\n * Only active when the platform's `i18n` option is configured.\n * Only transforms `@angular/core` modules in SSR builds.\n */\nexport function i18nDefRegistryPlugin(): vite.Plugin {\n let fixedLocale = false;\n const DETECT_MARKER = 'GENERATED_COMP_IDS.set(compId, componentDef.type);';\n const RETURN_STMT = 'return compId;\\n}';\n\n return {\n name: 'analogjs-i18n-def-registry',\n enforce: 'post',\n configResolved(config) {\n const locale = config.define?.['ANALOG_I18N_FIXED_LOCALE'];\n fixedLocale =\n config.command === 'build' && !!locale && locale !== 'undefined';\n },\n\n transform: {\n filter: {\n id: /\\/@angular\\/core\\//,\n },\n handler(code, id, options) {\n if (fixedLocale || !options?.ssr) return;\n if (!code.includes(DETECT_MARKER)) return;\n\n return {\n code: code.replace(\n RETURN_STMT,\n '(globalThis.__ngComponentDefs ??= new Set()).add(componentDef);\\n' +\n RETURN_STMT,\n ),\n };\n },\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,iBAAgC;CAmE9C,OAAO,CAjEL;EACE,MAAM;EACN,OAAO;EACP,SAAS;GACP,OAAO,EACL,KAAK,EACH,YAAY,CAAC,MAAM,EACrB,EACF;EACF;EACA,WAAW;GACT,QAAQ,EACN,IAAI,6DACN;GACA,QAAQ,MAAM,IAAI;IAChB,IACE,GAAG,SAAS,WAAW,KACvB,KAAK,SAAS,4BAA4B,GAE1C,OAAO,EACL,MAAM,KAAK,QAAQ,8BAA8B,EAAE,EACrD;IAGF,IAAI,GAAG,SAAS,iBAAiB,GAAG;KAClC,IAAI,SAAS,KAAK,QAChB,2BACA,mDACF;KAOA,SAAS,OACN,WAAW,WAAW,aAAa,CAAC,CACpC,WAAW,WAAW,aAAa,CAAC,CACpC,WAAW,YAAY,cAAc;KAExC,OAAO,EAAE,MAAM,OAAO;IACxB;IAEA,IAAI,GAAG,SAAS,KAAK,cAAc,SAAS,CAAC,GAC3C,OAAO,EACL,MAAM,KACH,QAAQ,aAAa,IAAI,CAAC,CAC1B,QAAQ,aAAa,IAAI,CAAC,CAC1B,QAAQ,yBAAyB,QAAQ,CAAC,CAC1C,QAAQ,uBAAuB,MAAM,EAC1C;IAGF,IAAI,GAAG,SAAS,KAAK,cAAc,sBAAsB,CAAC,GACxD,OAAO,EACL,MAAM,KAAK,QAAQ,YAAY,KAAK,EACtC;GAIJ;EACF;CACF,CAGK;AACT;;;;;;;;;;;;;AAcA,SAAgB,wBAAqC;CACnD,IAAI,cAAc;CAClB,MAAM,gBAAgB;CACtB,MAAM,cAAc;CAEpB,OAAO;EACL,MAAM;EACN,SAAS;EACT,eAAe,QAAQ;GACrB,MAAM,SAAS,OAAO,SAAS;GAC/B,cACE,OAAO,YAAY,WAAW,CAAC,CAAC,UAAU,WAAW;EACzD;EAEA,WAAW;GACT,QAAQ,EACN,IAAI,qBACN;GACA,QAAQ,MAAM,IAAI,SAAS;IACzB,IAAI,eAAe,CAAC,SAAS,KAAK;IAClC,IAAI,CAAC,KAAK,SAAS,aAAa,GAAG;IAEnC,OAAO,EACL,MAAM,KAAK,QACT,aACA,sEACE,WACJ,EACF;GACF;EACF;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"typed-routes-plugin.js","names":[],"sources":["../../../src/lib/typed-routes-plugin.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join, relative, resolve } from 'node:path';\nimport { readConfiguration } from '@angular/compiler-cli';\nimport { parseSync } from 'oxc-parser';\nimport { normalizePath, type Plugin } from 'vite';\n\nimport {\n createRouteFileDiscovery,\n type RouteFileDiscovery,\n} from './route-file-discovery.js';\nimport {\n generateRouteManifest,\n generateRouteTableDeclaration,\n} from './route-manifest.js';\n\nexport interface TypedRoutingOptions {\n /** Generated declaration path (.d.ts), relative to the app root. */\n outFile?: string;\n /** Fail builds when a checked-in route table is stale. Defaults to true. */\n verifyOnBuild?: boolean;\n}\n\nexport interface TypedRoutesPluginOptions extends TypedRoutingOptions {\n workspaceRoot?: string;\n additionalPagesDirs?: string[];\n additionalContentDirs?: string[];\n}\n\nexport function typedRoutes(options: TypedRoutesPluginOptions = {}): Plugin {\n const workspaceRoot = normalizePath(options.workspaceRoot ?? process.cwd());\n const outFile = options.outFile ?? 'src/routeTree.gen.d.ts';\n if (!outFile.endsWith('.d.ts')) {\n throw new Error(\n '[analog] Typed routing outFile must end in .d.ts. Include this declaration in your application tsconfig.',\n );\n }\n let root: string;\n let command: 'build' | 'serve';\n let discovery: RouteFileDiscovery;\n\n let getTsConfigPath: (() => string) | undefined;\n\n function verifyTypeInclusion(): void {\n if (!getTsConfigPath) return;\n const tsconfig = getTsConfigPath();\n const { rootNames, errors } = readConfiguration(tsconfig);\n if (errors.length) {\n throw new Error(\n `[analog] Cannot verify typed routing: unable to read ${tsconfig}.`,\n );\n }\n const outputPath = normalizePath(resolve(root, outFile));\n if (\n !rootNames.some((file) => normalizePath(resolve(file)) === outputPath)\n ) {\n const declaration = normalizePath(\n relative(dirname(tsconfig), outputPath),\n );\n throw new Error(\n `[analog] Typed route declaration is not included in ${tsconfig}. Add \"${declaration}\" to its \"files\" or \"include\" list so typed routing is available to the compiler and editor.`,\n );\n }\n }\n\n function generate(): void {\n const manifest = generateRouteManifest(\n [...discovery.getRouteFiles(), ...discovery.getContentFiles()],\n (filename) => (discovery.isAppLocal(filename) ? 0 : 1),\n );\n const collisions = manifest.collisions.filter(\n (collision) => collision.samePriority,\n );\n if (command === 'build' && collisions.length) {\n throw new Error(\n `[analog] Route collisions detected: ${collisions.map((c) => `${c.fullPath}: ${c.keptFile}, ${c.droppedFile}`).join('; ')}`,\n );\n }\n const outputPath = join(root, outFile);\n const output = generateRouteTableDeclaration(manifest, (filename) => {\n const pageFile = [\n join(root, filename),\n join(workspaceRoot, filename),\n filename,\n ].find((file) => existsSync(file));\n if (!pageFile) return undefined;\n const serverFile = pageFile.replace(/\\.page\\.ts$/, '.server.ts');\n return {\n routeMeta: hasNamedExport(pageFile, 'routeMeta')\n ? toImportSpecifier(dirname(outputPath), pageFile)\n : undefined,\n load:\n serverFile !== pageFile &&\n existsSync(serverFile) &&\n hasNamedExport(serverFile, 'load')\n ? toImportSpecifier(dirname(outputPath), serverFile)\n : undefined,\n };\n });\n const exists = existsSync(outputPath);\n const current = exists ? readFileSync(outputPath, 'utf8') : '';\n if (current !== output && !sameDeclarations(current, output)) {\n if (exists && command === 'build' && (options.verifyOnBuild ?? true)) {\n throw new Error(\n `[analog] Stale route file: ${outFile}. Run the dev server to regenerate it before building.`,\n );\n }\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, output);\n }\n }\n\n return {\n name: 'analog-typed-routes',\n config(config, env) {\n command = env.command;\n root = normalizePath(resolve(workspaceRoot, config.root ?? '.'));\n discovery = createRouteFileDiscovery({\n root,\n workspaceRoot,\n additionalPagesDirs: options.additionalPagesDirs ?? [],\n additionalContentDirs: options.additionalContentDirs ?? [],\n });\n // Generate before Angular reads the application tsconfig.\n generate();\n },\n configResolved(config) {\n const compiler = config.plugins.find(\n (plugin) =>\n plugin.name === '@analogjs/vite-plugin-angular' ||\n plugin.name === '@analogjs/vite-plugin-angular-fast-compile' ||\n plugin.name === '@analogjs/vite-plugin-angular-compilation-api',\n );\n getTsConfigPath = compiler?.api?.getTsConfigPath;\n if (compiler && !getTsConfigPath) {\n throw new Error(\n '[analog] Typed routing requires a matching @analogjs/vite-plugin-angular version to verify the application tsconfig.',\n );\n }\n verifyTypeInclusion();\n },\n buildStart() {\n discovery.reset();\n generate();\n verifyTypeInclusion();\n },\n configureServer(server) {\n server.watcher.add(join(root, outFile));\n for (const event of ['add', 'change', 'unlink'] as const) {\n server.watcher.on(event, (path) => {\n if (!discovery.getDiscoveredFileKind(path)) return;\n discovery.updateDiscoveredFile(path, event);\n generate();\n });\n }\n },\n };\n}\n\nfunction toImportSpecifier(fromDir: string, file: string): string {\n const specifier = normalizePath(relative(fromDir, file)).replace(/\\.ts$/, '');\n return specifier.startsWith('.') ? specifier : `./${specifier}`;\n}\n\nfunction hasNamedExport(file: string, name: string): boolean {\n const source = readFileSync(file, 'utf8');\n if (!source.includes(name)) return false;\n const { program, errors } = parseSync(file, source);\n if (errors.length) return false;\n return program.body.some((statement) => {\n if (\n statement.type !== 'ExportNamedDeclaration' ||\n statement.exportKind === 'type'\n ) {\n return false;\n }\n const declaration = statement.declaration;\n if (declaration?.type === 'VariableDeclaration') {\n return declaration.declarations.some(\n (item) => item.id.type === 'Identifier' && item.id.name === name,\n );\n }\n if (declaration?.type === 'FunctionDeclaration') {\n return declaration.id?.name === name;\n }\n return statement.specifiers.some(\n (specifier) =>\n specifier.exportKind !== 'type' &&\n (specifier.exported.type === 'Identifier'\n ? specifier.exported.name\n : specifier.exported.value) === name,\n );\n });\n}\n\nfunction sameDeclarations(current: string, output: string): boolean {\n const left = parseSync('routeTree.gen.d.ts', current);\n const right = parseSync('routeTree.gen.d.ts', output);\n if (left.errors.length || right.errors.length) return false;\n const withoutFormatting = (key: string, value: unknown) =>\n key === 'start' || key === 'end' || key === 'raw' ? undefined : value;\n return (\n JSON.stringify(left.program, withoutFormatting) ===\n JSON.stringify(right.program, withoutFormatting)\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"typed-routes-plugin.js","names":[],"sources":["../../../src/lib/typed-routes-plugin.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join, relative, resolve } from 'node:path';\nimport { readConfiguration } from '@angular/compiler-cli';\nimport { parseSync } from 'oxc-parser';\nimport { normalizePath, type Plugin } from 'vite';\n\nimport {\n createRouteFileDiscovery,\n type RouteFileDiscovery,\n} from './route-file-discovery.js';\nimport {\n generateRouteManifest,\n generateRouteTableDeclaration,\n} from './route-manifest.js';\n\nexport interface TypedRoutingOptions {\n /** Generated declaration path (.d.ts), relative to the app root. */\n outFile?: string;\n /** Fail builds when a checked-in route table is stale. Defaults to true. */\n verifyOnBuild?: boolean;\n}\n\nexport interface TypedRoutesPluginOptions extends TypedRoutingOptions {\n workspaceRoot?: string;\n additionalPagesDirs?: string[];\n additionalContentDirs?: string[];\n}\n\nexport function typedRoutes(options: TypedRoutesPluginOptions = {}): Plugin {\n const workspaceRoot = normalizePath(options.workspaceRoot ?? process.cwd());\n const outFile = options.outFile ?? 'src/routeTree.gen.d.ts';\n if (!outFile.endsWith('.d.ts')) {\n throw new Error(\n '[analog] Typed routing outFile must end in .d.ts. Include this declaration in your application tsconfig.',\n );\n }\n let root: string;\n let command: 'build' | 'serve';\n let discovery: RouteFileDiscovery;\n\n let getTsConfigPath: (() => string) | undefined;\n\n function verifyTypeInclusion(): void {\n if (!getTsConfigPath) return;\n const tsconfig = getTsConfigPath();\n const { rootNames, errors } = readConfiguration(tsconfig);\n if (errors.length) {\n throw new Error(\n `[analog] Cannot verify typed routing: unable to read ${tsconfig}.`,\n );\n }\n const outputPath = normalizePath(resolve(root, outFile));\n if (\n !rootNames.some((file) => normalizePath(resolve(file)) === outputPath)\n ) {\n const declaration = normalizePath(\n relative(dirname(tsconfig), outputPath),\n );\n throw new Error(\n `[analog] Typed route declaration is not included in ${tsconfig}. Add \"${declaration}\" to its \"files\" or \"include\" list so typed routing is available to the compiler and editor.`,\n );\n }\n }\n\n function generate(): void {\n const manifest = generateRouteManifest(\n [...discovery.getRouteFiles(), ...discovery.getContentFiles()],\n (filename) => (discovery.isAppLocal(filename) ? 0 : 1),\n );\n const collisions = manifest.collisions.filter(\n (collision) => collision.samePriority,\n );\n if (command === 'build' && collisions.length) {\n throw new Error(\n `[analog] Route collisions detected: ${collisions.map((c) => `${c.fullPath}: ${c.keptFile}, ${c.droppedFile}`).join('; ')}`,\n );\n }\n const outputPath = join(root, outFile);\n const output = generateRouteTableDeclaration(manifest, (filename) => {\n const pageFile = [\n join(root, filename),\n join(workspaceRoot, filename),\n filename,\n ].find((file) => existsSync(file));\n if (!pageFile) return undefined;\n const serverFile = pageFile.replace(/\\.page\\.ts$/, '.server.ts');\n return {\n routeMeta: hasNamedExport(pageFile, 'routeMeta')\n ? toImportSpecifier(dirname(outputPath), pageFile)\n : undefined,\n load:\n serverFile !== pageFile &&\n existsSync(serverFile) &&\n hasNamedExport(serverFile, 'load')\n ? toImportSpecifier(dirname(outputPath), serverFile)\n : undefined,\n };\n });\n const exists = existsSync(outputPath);\n const current = exists ? readFileSync(outputPath, 'utf8') : '';\n if (current !== output && !sameDeclarations(current, output)) {\n if (exists && command === 'build' && (options.verifyOnBuild ?? true)) {\n throw new Error(\n `[analog] Stale route file: ${outFile}. Run the dev server to regenerate it before building.`,\n );\n }\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, output);\n }\n }\n\n return {\n name: 'analog-typed-routes',\n config(config, env) {\n command = env.command;\n root = normalizePath(resolve(workspaceRoot, config.root ?? '.'));\n discovery = createRouteFileDiscovery({\n root,\n workspaceRoot,\n additionalPagesDirs: options.additionalPagesDirs ?? [],\n additionalContentDirs: options.additionalContentDirs ?? [],\n });\n // Generate before Angular reads the application tsconfig.\n generate();\n },\n configResolved(config) {\n const compiler = config.plugins.find(\n (plugin) =>\n plugin.name === '@analogjs/vite-plugin-angular' ||\n plugin.name === '@analogjs/vite-plugin-angular-fast-compile' ||\n plugin.name === '@analogjs/vite-plugin-angular-compilation-api',\n );\n getTsConfigPath = compiler?.api?.getTsConfigPath;\n if (compiler && !getTsConfigPath) {\n throw new Error(\n '[analog] Typed routing requires a matching @analogjs/vite-plugin-angular version to verify the application tsconfig.',\n );\n }\n verifyTypeInclusion();\n },\n buildStart() {\n discovery.reset();\n generate();\n verifyTypeInclusion();\n },\n configureServer(server) {\n server.watcher.add(join(root, outFile));\n for (const event of ['add', 'change', 'unlink'] as const) {\n server.watcher.on(event, (path) => {\n if (!discovery.getDiscoveredFileKind(path)) return;\n discovery.updateDiscoveredFile(path, event);\n generate();\n });\n }\n },\n };\n}\n\nfunction toImportSpecifier(fromDir: string, file: string): string {\n const specifier = normalizePath(relative(fromDir, file)).replace(/\\.ts$/, '');\n return specifier.startsWith('.') ? specifier : `./${specifier}`;\n}\n\nfunction hasNamedExport(file: string, name: string): boolean {\n const source = readFileSync(file, 'utf8');\n if (!source.includes(name)) return false;\n const { program, errors } = parseSync(file, source);\n if (errors.length) return false;\n return program.body.some((statement) => {\n if (\n statement.type !== 'ExportNamedDeclaration' ||\n statement.exportKind === 'type'\n ) {\n return false;\n }\n const declaration = statement.declaration;\n if (declaration?.type === 'VariableDeclaration') {\n return declaration.declarations.some(\n (item) => item.id.type === 'Identifier' && item.id.name === name,\n );\n }\n if (declaration?.type === 'FunctionDeclaration') {\n return declaration.id?.name === name;\n }\n return statement.specifiers.some(\n (specifier) =>\n specifier.exportKind !== 'type' &&\n (specifier.exported.type === 'Identifier'\n ? specifier.exported.name\n : specifier.exported.value) === name,\n );\n });\n}\n\nfunction sameDeclarations(current: string, output: string): boolean {\n const left = parseSync('routeTree.gen.d.ts', current);\n const right = parseSync('routeTree.gen.d.ts', output);\n if (left.errors.length || right.errors.length) return false;\n const withoutFormatting = (key: string, value: unknown) =>\n key === 'start' || key === 'end' || key === 'raw' ? undefined : value;\n return (\n JSON.stringify(left.program, withoutFormatting) ===\n JSON.stringify(right.program, withoutFormatting)\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"debug-harness.js","names":[],"sources":["../../../../src/lib/utils/debug-harness.ts"],"sourcesContent":["/**\n * Duplicates of this file (keep in sync):\n * packages/platform/src/lib/utils/debug-harness.ts\n * packages/vite-plugin-angular/src/lib/utils/debug-harness.ts\n */\nimport { join } from 'node:path';\nimport { enable } from 'obug';\nimport type { Debugger } from 'obug';\n\nimport {\n DEBUG_LOG_DIR,\n DEBUG_LOG_FILENAME,\n wrapInstancesForFileLog,\n wrapInstancesForScopedFileLog,\n} from './debug-log-file.js';\n\nexport type DebugMode = 'build' | 'dev';\n\nexport interface DebugModeOptions<S extends string = string> {\n scopes?: boolean | S[];\n mode?: DebugMode;\n /**\n * Write debug output to log files under `tmp/debug/` in the workspace root.\n * - `true` or `'single'` — all output to `tmp/debug/analog.log`\n * - `'scoped'` — one file per scope, e.g. `tmp/debug/analog.angular.hmr.log`\n */\n logFile?: boolean | 'single' | 'scoped';\n}\n\nexport type DebugOption<S extends string = string> =\n | boolean\n | S[]\n | DebugModeOptions<S>\n | DebugModeOptions<S>[];\n\nexport interface DebugHarness<S extends string = string> {\n applyDebugOption(\n debug: DebugOption<S> | undefined,\n workspaceRoot?: string,\n ): void;\n activateDeferredDebug(command: 'build' | 'serve'): void;\n /** @internal test-only reset */\n _resetDeferredDebug(): void;\n}\n\nfunction resolveNamespaces(\n scopes: boolean | string[] | undefined,\n fallback: string,\n): string | null {\n if (scopes === true || scopes === undefined) return fallback;\n if (Array.isArray(scopes) && scopes.length) return scopes.join(',');\n return null;\n}\n\nfunction extractLogFile(\n debug: DebugOption,\n): true | 'single' | 'scoped' | false {\n if (typeof debug === 'boolean') return false;\n if (Array.isArray(debug)) {\n if (debug.length === 0 || typeof debug[0] === 'string') return false;\n const entry = (debug as DebugModeOptions[]).find((e) => !!e.logFile);\n return entry?.logFile ?? false;\n }\n return (debug as DebugModeOptions).logFile ?? false;\n}\n\nexport function createDebugHarness<S extends string = string>(config: {\n fallbackNamespace: string;\n instanceGroups: Debugger[][];\n}): DebugHarness<S> {\n interface PendingEntry {\n entry: DebugModeOptions<S>;\n logFile: true | 'single' | 'scoped' | false;\n root: string;\n }\n\n let pendingDebug: PendingEntry[] = [];\n\n function installFileWrappers(\n logFile: true | 'single' | 'scoped',\n root: string,\n ): void {\n if (logFile === 'scoped') {\n const dirPath = join(root, DEBUG_LOG_DIR);\n for (const group of config.instanceGroups) {\n wrapInstancesForScopedFileLog(group, dirPath);\n }\n } else {\n const filePath = join(root, DEBUG_LOG_DIR, DEBUG_LOG_FILENAME);\n for (const group of config.instanceGroups) {\n wrapInstancesForFileLog(group, filePath);\n }\n }\n }\n\n function applyEntry(\n entry: DebugModeOptions<S>,\n fallback: string,\n logFile: true | 'single' | 'scoped' | false,\n root: string,\n ): void {\n if (!entry.mode) {\n const ns = resolveNamespaces(entry.scopes ?? true, fallback);\n if (ns) enable(ns);\n if (logFile) installFileWrappers(logFile, root);\n } else {\n pendingDebug.push({ entry, logFile, root });\n }\n }\n\n return {\n applyDebugOption(\n debug: DebugOption<S> | undefined,\n workspaceRoot?: string,\n ): void {\n if (debug == null || debug === false) return;\n\n const logFile = extractLogFile(debug);\n const root =\n workspaceRoot ?? process.env['NX_WORKSPACE_ROOT'] ?? process.cwd();\n\n if (typeof debug === 'boolean') {\n const ns = resolveNamespaces(debug, config.fallbackNamespace);\n if (ns) enable(ns);\n return;\n }\n\n if (Array.isArray(debug)) {\n if (debug.length === 0) return;\n\n if (typeof debug[0] === 'string') {\n const ns = (debug as string[]).join(',');\n if (ns) enable(ns);\n return;\n }\n\n for (const entry of debug as DebugModeOptions<S>[]) {\n const entryLogFile = entry.logFile ?? false;\n applyEntry(\n entry,\n config.fallbackNamespace,\n entryLogFile || logFile,\n root,\n );\n }\n return;\n }\n\n applyEntry(debug, config.fallbackNamespace, logFile, root);\n },\n\n activateDeferredDebug(command: 'build' | 'serve'): void {\n if (pendingDebug.length === 0) return;\n\n const currentMode = command === 'serve' ? 'dev' : 'build';\n\n for (const { entry, logFile, root } of pendingDebug) {\n if (entry.mode === currentMode) {\n const ns = resolveNamespaces(\n entry.scopes ?? true,\n config.fallbackNamespace,\n );\n if (ns) enable(ns);\n if (logFile) installFileWrappers(logFile, root);\n }\n }\n\n pendingDebug = [];\n },\n\n _resetDeferredDebug(): void {\n pendingDebug = [];\n },\n };\n}\n"],"mappings":";;;;;;;;;AA6CA,SAAS,kBACP,QACA,UACe;AACf,KAAI,WAAW,QAAQ,WAAW,KAAA,EAAW,QAAO;AACpD,KAAI,MAAM,QAAQ,OAAO,IAAI,OAAO,OAAQ,QAAO,OAAO,KAAK,IAAI;AACnE,QAAO;;AAGT,SAAS,eACP,OACoC;AACpC,KAAI,OAAO,UAAU,UAAW,QAAO;AACvC,KAAI,MAAM,QAAQ,MAAM,EAAE;AACxB,MAAI,MAAM,WAAW,KAAK,OAAO,MAAM,OAAO,SAAU,QAAO;AAE/D,SADe,MAA6B,MAAM,MAAM,CAAC,CAAC,EAAE,QAAQ,EACtD,WAAW;;AAE3B,QAAQ,MAA2B,WAAW;;AAGhD,SAAgB,mBAA8C,QAG1C;CAOlB,IAAI,eAA+B,EAAE;CAErC,SAAS,oBACP,SACA,MACM;AACN,MAAI,YAAY,UAAU;GACxB,MAAM,UAAU,KAAK,MAAM,cAAc;AACzC,QAAK,MAAM,SAAS,OAAO,eACzB,+BAA8B,OAAO,QAAQ;SAE1C;GACL,MAAM,WAAW,KAAK,MAAM,eAAe,mBAAmB;AAC9D,QAAK,MAAM,SAAS,OAAO,eACzB,yBAAwB,OAAO,SAAS;;;CAK9C,SAAS,WACP,OACA,UACA,SACA,MACM;AACN,MAAI,CAAC,MAAM,MAAM;GACf,MAAM,KAAK,kBAAkB,MAAM,UAAU,MAAM,SAAS;AAC5D,OAAI,GAAI,QAAO,GAAG;AAClB,OAAI,QAAS,qBAAoB,SAAS,KAAK;QAE/C,cAAa,KAAK;GAAE;GAAO;GAAS;GAAM,CAAC;;AAI/C,QAAO;EACL,iBACE,OACA,eACM;AACN,OAAI,SAAS,QAAQ,UAAU,MAAO;GAEtC,MAAM,UAAU,eAAe,MAAM;GACrC,MAAM,OACJ,iBAAiB,QAAQ,IAAI,wBAAwB,QAAQ,KAAK;AAEpE,OAAI,OAAO,UAAU,WAAW;IAC9B,MAAM,KAAK,kBAAkB,OAAO,OAAO,kBAAkB;AAC7D,QAAI,GAAI,QAAO,GAAG;AAClB;;AAGF,OAAI,MAAM,QAAQ,MAAM,EAAE;AACxB,QAAI,MAAM,WAAW,EAAG;AAExB,QAAI,OAAO,MAAM,OAAO,UAAU;KAChC,MAAM,KAAM,MAAmB,KAAK,IAAI;AACxC,SAAI,GAAI,QAAO,GAAG;AAClB;;AAGF,SAAK,MAAM,SAAS,OAAgC;KAClD,MAAM,eAAe,MAAM,WAAW;AACtC,gBACE,OACA,OAAO,mBACP,gBAAgB,SAChB,KACD;;AAEH;;AAGF,cAAW,OAAO,OAAO,mBAAmB,SAAS,KAAK;;EAG5D,sBAAsB,SAAkC;AACtD,OAAI,aAAa,WAAW,EAAG;GAE/B,MAAM,cAAc,YAAY,UAAU,QAAQ;AAElD,QAAK,MAAM,EAAE,OAAO,SAAS,UAAU,aACrC,KAAI,MAAM,SAAS,aAAa;IAC9B,MAAM,KAAK,kBACT,MAAM,UAAU,MAChB,OAAO,kBACR;AACD,QAAI,GAAI,QAAO,GAAG;AAClB,QAAI,QAAS,qBAAoB,SAAS,KAAK;;AAInD,kBAAe,EAAE;;EAGnB,sBAA4B;AAC1B,kBAAe,EAAE;;EAEpB"}
1
+ {"version":3,"file":"debug-harness.js","names":[],"sources":["../../../../src/lib/utils/debug-harness.ts"],"sourcesContent":["/**\n * Duplicates of this file (keep in sync):\n * packages/platform/src/lib/utils/debug-harness.ts\n * packages/vite-plugin-angular/src/lib/utils/debug-harness.ts\n */\nimport { join } from 'node:path';\nimport { enable } from 'obug';\nimport type { Debugger } from 'obug';\n\nimport {\n DEBUG_LOG_DIR,\n DEBUG_LOG_FILENAME,\n wrapInstancesForFileLog,\n wrapInstancesForScopedFileLog,\n} from './debug-log-file.js';\n\nexport type DebugMode = 'build' | 'dev';\n\nexport interface DebugModeOptions<S extends string = string> {\n scopes?: boolean | S[];\n mode?: DebugMode;\n /**\n * Write debug output to log files under `tmp/debug/` in the workspace root.\n * - `true` or `'single'` — all output to `tmp/debug/analog.log`\n * - `'scoped'` — one file per scope, e.g. `tmp/debug/analog.angular.hmr.log`\n */\n logFile?: boolean | 'single' | 'scoped';\n}\n\nexport type DebugOption<S extends string = string> =\n | boolean\n | S[]\n | DebugModeOptions<S>\n | DebugModeOptions<S>[];\n\nexport interface DebugHarness<S extends string = string> {\n applyDebugOption(\n debug: DebugOption<S> | undefined,\n workspaceRoot?: string,\n ): void;\n activateDeferredDebug(command: 'build' | 'serve'): void;\n /** @internal test-only reset */\n _resetDeferredDebug(): void;\n}\n\nfunction resolveNamespaces(\n scopes: boolean | string[] | undefined,\n fallback: string,\n): string | null {\n if (scopes === true || scopes === undefined) return fallback;\n if (Array.isArray(scopes) && scopes.length) return scopes.join(',');\n return null;\n}\n\nfunction extractLogFile(\n debug: DebugOption,\n): true | 'single' | 'scoped' | false {\n if (typeof debug === 'boolean') return false;\n if (Array.isArray(debug)) {\n if (debug.length === 0 || typeof debug[0] === 'string') return false;\n const entry = (debug as DebugModeOptions[]).find((e) => !!e.logFile);\n return entry?.logFile ?? false;\n }\n return (debug as DebugModeOptions).logFile ?? false;\n}\n\nexport function createDebugHarness<S extends string = string>(config: {\n fallbackNamespace: string;\n instanceGroups: Debugger[][];\n}): DebugHarness<S> {\n interface PendingEntry {\n entry: DebugModeOptions<S>;\n logFile: true | 'single' | 'scoped' | false;\n root: string;\n }\n\n let pendingDebug: PendingEntry[] = [];\n\n function installFileWrappers(\n logFile: true | 'single' | 'scoped',\n root: string,\n ): void {\n if (logFile === 'scoped') {\n const dirPath = join(root, DEBUG_LOG_DIR);\n for (const group of config.instanceGroups) {\n wrapInstancesForScopedFileLog(group, dirPath);\n }\n } else {\n const filePath = join(root, DEBUG_LOG_DIR, DEBUG_LOG_FILENAME);\n for (const group of config.instanceGroups) {\n wrapInstancesForFileLog(group, filePath);\n }\n }\n }\n\n function applyEntry(\n entry: DebugModeOptions<S>,\n fallback: string,\n logFile: true | 'single' | 'scoped' | false,\n root: string,\n ): void {\n if (!entry.mode) {\n const ns = resolveNamespaces(entry.scopes ?? true, fallback);\n if (ns) enable(ns);\n if (logFile) installFileWrappers(logFile, root);\n } else {\n pendingDebug.push({ entry, logFile, root });\n }\n }\n\n return {\n applyDebugOption(\n debug: DebugOption<S> | undefined,\n workspaceRoot?: string,\n ): void {\n if (debug == null || debug === false) return;\n\n const logFile = extractLogFile(debug);\n const root =\n workspaceRoot ?? process.env['NX_WORKSPACE_ROOT'] ?? process.cwd();\n\n if (typeof debug === 'boolean') {\n const ns = resolveNamespaces(debug, config.fallbackNamespace);\n if (ns) enable(ns);\n return;\n }\n\n if (Array.isArray(debug)) {\n if (debug.length === 0) return;\n\n if (typeof debug[0] === 'string') {\n const ns = (debug as string[]).join(',');\n if (ns) enable(ns);\n return;\n }\n\n for (const entry of debug as DebugModeOptions<S>[]) {\n const entryLogFile = entry.logFile ?? false;\n applyEntry(\n entry,\n config.fallbackNamespace,\n entryLogFile || logFile,\n root,\n );\n }\n return;\n }\n\n applyEntry(debug, config.fallbackNamespace, logFile, root);\n },\n\n activateDeferredDebug(command: 'build' | 'serve'): void {\n if (pendingDebug.length === 0) return;\n\n const currentMode = command === 'serve' ? 'dev' : 'build';\n\n for (const { entry, logFile, root } of pendingDebug) {\n if (entry.mode === currentMode) {\n const ns = resolveNamespaces(\n entry.scopes ?? true,\n config.fallbackNamespace,\n );\n if (ns) enable(ns);\n if (logFile) installFileWrappers(logFile, root);\n }\n }\n\n pendingDebug = [];\n },\n\n _resetDeferredDebug(): void {\n pendingDebug = [];\n },\n };\n}\n"],"mappings":";;;;;;;;;AA6CA,SAAS,kBACP,QACA,UACe;CACf,IAAI,WAAW,QAAQ,WAAW,KAAA,GAAW,OAAO;CACpD,IAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,QAAQ,OAAO,OAAO,KAAK,GAAG;CAClE,OAAO;AACT;AAEA,SAAS,eACP,OACoC;CACpC,IAAI,OAAO,UAAU,WAAW,OAAO;CACvC,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,IAAI,MAAM,WAAW,KAAK,OAAO,MAAM,OAAO,UAAU,OAAO;EAE/D,OADe,MAA6B,MAAM,MAAM,CAAC,CAAC,EAAE,OACrD,CAAA,EAAO,WAAW;CAC3B;CACA,OAAQ,MAA2B,WAAW;AAChD;AAEA,SAAgB,mBAA8C,QAG1C;CAOlB,IAAI,eAA+B,CAAC;CAEpC,SAAS,oBACP,SACA,MACM;EACN,IAAI,YAAY,UAAU;GACxB,MAAM,UAAU,KAAK,MAAM,aAAa;GACxC,KAAK,MAAM,SAAS,OAAO,gBACzB,8BAA8B,OAAO,OAAO;EAEhD,OAAO;GACL,MAAM,WAAW,KAAK,MAAM,eAAe,kBAAkB;GAC7D,KAAK,MAAM,SAAS,OAAO,gBACzB,wBAAwB,OAAO,QAAQ;EAE3C;CACF;CAEA,SAAS,WACP,OACA,UACA,SACA,MACM;EACN,IAAI,CAAC,MAAM,MAAM;GACf,MAAM,KAAK,kBAAkB,MAAM,UAAU,MAAM,QAAQ;GAC3D,IAAI,IAAI,OAAO,EAAE;GACjB,IAAI,SAAS,oBAAoB,SAAS,IAAI;EAChD,OACE,aAAa,KAAK;GAAE;GAAO;GAAS;EAAK,CAAC;CAE9C;CAEA,OAAO;EACL,iBACE,OACA,eACM;GACN,IAAI,SAAS,QAAQ,UAAU,OAAO;GAEtC,MAAM,UAAU,eAAe,KAAK;GACpC,MAAM,OACJ,iBAAiB,QAAQ,IAAI,wBAAwB,QAAQ,IAAI;GAEnE,IAAI,OAAO,UAAU,WAAW;IAC9B,MAAM,KAAK,kBAAkB,OAAO,OAAO,iBAAiB;IAC5D,IAAI,IAAI,OAAO,EAAE;IACjB;GACF;GAEA,IAAI,MAAM,QAAQ,KAAK,GAAG;IACxB,IAAI,MAAM,WAAW,GAAG;IAExB,IAAI,OAAO,MAAM,OAAO,UAAU;KAChC,MAAM,KAAM,MAAmB,KAAK,GAAG;KACvC,IAAI,IAAI,OAAO,EAAE;KACjB;IACF;IAEA,KAAK,MAAM,SAAS,OAAgC;KAClD,MAAM,eAAe,MAAM,WAAW;KACtC,WACE,OACA,OAAO,mBACP,gBAAgB,SAChB,IACF;IACF;IACA;GACF;GAEA,WAAW,OAAO,OAAO,mBAAmB,SAAS,IAAI;EAC3D;EAEA,sBAAsB,SAAkC;GACtD,IAAI,aAAa,WAAW,GAAG;GAE/B,MAAM,cAAc,YAAY,UAAU,QAAQ;GAElD,KAAK,MAAM,EAAE,OAAO,SAAS,UAAU,cACrC,IAAI,MAAM,SAAS,aAAa;IAC9B,MAAM,KAAK,kBACT,MAAM,UAAU,MAChB,OAAO,iBACT;IACA,IAAI,IAAI,OAAO,EAAE;IACjB,IAAI,SAAS,oBAAoB,SAAS,IAAI;GAChD;GAGF,eAAe,CAAC;EAClB;EAEA,sBAA4B;GAC1B,eAAe,CAAC;EAClB;CACF;AACF"}
@@ -31,7 +31,8 @@ function wrapLog(dbg, filePath) {
31
31
  dbg.log = function(...args) {
32
32
  originalLog.apply(this, args);
33
33
  try {
34
- appendFileSync(filePath, format(...args).replace(ANSI_RE, "") + "\n", "utf-8");
34
+ const line = format(...args).replace(ANSI_RE, "") + "\n";
35
+ appendFileSync(filePath, line, "utf-8");
35
36
  } catch {}
36
37
  };
37
38
  rec[WRAPPED_KEY] = filePath;
@@ -1 +1 @@
1
- {"version":3,"file":"debug-log-file.js","names":[],"sources":["../../../../src/lib/utils/debug-log-file.ts"],"sourcesContent":["/**\n * Duplicates of this file (keep in sync):\n * packages/platform/src/lib/utils/debug-log-file.ts\n * packages/vite-plugin-angular/src/lib/utils/debug-log-file.ts\n */\nimport { mkdirSync, writeFileSync, appendFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { format } from 'node:util';\nimport type { Debugger } from 'obug';\n\nconst TRUNCATED_KEY = '__analogDebugLogTruncated';\nconst WRAPPED_KEY = '__analogFileLogWrapped';\n// eslint-disable-next-line no-control-regex\nconst ANSI_RE = /\\x1B\\[[0-9;]*[A-Za-z]|\\x1B\\].*?\\x07/g;\n\nexport const DEBUG_LOG_DIR = 'tmp/debug';\nexport const DEBUG_LOG_FILENAME = 'analog.log';\n\nfunction ensureTruncated(filePath: string): void {\n const g = globalThis as Record<string, unknown>;\n const truncated = (g[TRUNCATED_KEY] as Set<string>) ?? new Set<string>();\n g[TRUNCATED_KEY] = truncated;\n if (truncated.has(filePath)) return;\n try {\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, '', 'utf-8');\n } catch {\n // best-effort: fall through to append mode if truncation fails\n }\n truncated.add(filePath);\n}\n\nfunction wrapLog(dbg: Debugger, filePath: string): void {\n const rec = dbg as Record<string, unknown>;\n if (rec[WRAPPED_KEY] === filePath) return;\n\n const originalLog =\n rec[WRAPPED_KEY] && rec['__analogOriginalLog']\n ? (rec['__analogOriginalLog'] as Debugger['log'])\n : dbg.log;\n\n rec['__analogOriginalLog'] = originalLog;\n dbg.log = function (this: Debugger, ...args: unknown[]) {\n originalLog.apply(this, args);\n try {\n const line = format(...args).replace(ANSI_RE, '') + '\\n';\n appendFileSync(filePath, line, 'utf-8');\n } catch {\n // debug logging must never crash the build\n }\n };\n rec[WRAPPED_KEY] = filePath;\n}\n\nexport function wrapInstancesForFileLog(\n instances: Debugger[],\n filePath: string,\n): void {\n ensureTruncated(filePath);\n for (const dbg of instances) {\n wrapLog(dbg, filePath);\n }\n}\n\nfunction scopeToFilename(namespace: string): string {\n return namespace.replace(/:/g, '.') + '.log';\n}\n\nexport function wrapInstancesForScopedFileLog(\n instances: Debugger[],\n dirPath: string,\n): void {\n for (const dbg of instances) {\n const scopedPath = join(dirPath, scopeToFilename(dbg.namespace));\n ensureTruncated(scopedPath);\n wrapLog(dbg, scopedPath);\n }\n}\n"],"mappings":";;;;;;;;;AAUA,IAAM,gBAAgB;AACtB,IAAM,cAAc;AAEpB,IAAM,UAAU;AAEhB,IAAa,gBAAgB;AAC7B,IAAa,qBAAqB;AAElC,SAAS,gBAAgB,UAAwB;CAC/C,MAAM,IAAI;CACV,MAAM,YAAa,EAAE,kCAAkC,IAAI,KAAa;AACxE,GAAE,iBAAiB;AACnB,KAAI,UAAU,IAAI,SAAS,CAAE;AAC7B,KAAI;AACF,YAAU,QAAQ,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AACjD,gBAAc,UAAU,IAAI,QAAQ;SAC9B;AAGR,WAAU,IAAI,SAAS;;AAGzB,SAAS,QAAQ,KAAe,UAAwB;CACtD,MAAM,MAAM;AACZ,KAAI,IAAI,iBAAiB,SAAU;CAEnC,MAAM,cACJ,IAAI,gBAAgB,IAAI,yBACnB,IAAI,yBACL,IAAI;AAEV,KAAI,yBAAyB;AAC7B,KAAI,MAAM,SAA0B,GAAG,MAAiB;AACtD,cAAY,MAAM,MAAM,KAAK;AAC7B,MAAI;AAEF,kBAAe,UADF,OAAO,GAAG,KAAK,CAAC,QAAQ,SAAS,GAAG,GAAG,MACrB,QAAQ;UACjC;;AAIV,KAAI,eAAe;;AAGrB,SAAgB,wBACd,WACA,UACM;AACN,iBAAgB,SAAS;AACzB,MAAK,MAAM,OAAO,UAChB,SAAQ,KAAK,SAAS;;AAI1B,SAAS,gBAAgB,WAA2B;AAClD,QAAO,UAAU,QAAQ,MAAM,IAAI,GAAG;;AAGxC,SAAgB,8BACd,WACA,SACM;AACN,MAAK,MAAM,OAAO,WAAW;EAC3B,MAAM,aAAa,KAAK,SAAS,gBAAgB,IAAI,UAAU,CAAC;AAChE,kBAAgB,WAAW;AAC3B,UAAQ,KAAK,WAAW"}
1
+ {"version":3,"file":"debug-log-file.js","names":[],"sources":["../../../../src/lib/utils/debug-log-file.ts"],"sourcesContent":["/**\n * Duplicates of this file (keep in sync):\n * packages/platform/src/lib/utils/debug-log-file.ts\n * packages/vite-plugin-angular/src/lib/utils/debug-log-file.ts\n */\nimport { mkdirSync, writeFileSync, appendFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { format } from 'node:util';\nimport type { Debugger } from 'obug';\n\nconst TRUNCATED_KEY = '__analogDebugLogTruncated';\nconst WRAPPED_KEY = '__analogFileLogWrapped';\n// eslint-disable-next-line no-control-regex\nconst ANSI_RE = /\\x1B\\[[0-9;]*[A-Za-z]|\\x1B\\].*?\\x07/g;\n\nexport const DEBUG_LOG_DIR = 'tmp/debug';\nexport const DEBUG_LOG_FILENAME = 'analog.log';\n\nfunction ensureTruncated(filePath: string): void {\n const g = globalThis as Record<string, unknown>;\n const truncated = (g[TRUNCATED_KEY] as Set<string>) ?? new Set<string>();\n g[TRUNCATED_KEY] = truncated;\n if (truncated.has(filePath)) return;\n try {\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, '', 'utf-8');\n } catch {\n // best-effort: fall through to append mode if truncation fails\n }\n truncated.add(filePath);\n}\n\nfunction wrapLog(dbg: Debugger, filePath: string): void {\n const rec = dbg as Record<string, unknown>;\n if (rec[WRAPPED_KEY] === filePath) return;\n\n const originalLog =\n rec[WRAPPED_KEY] && rec['__analogOriginalLog']\n ? (rec['__analogOriginalLog'] as Debugger['log'])\n : dbg.log;\n\n rec['__analogOriginalLog'] = originalLog;\n dbg.log = function (this: Debugger, ...args: unknown[]) {\n originalLog.apply(this, args);\n try {\n const line = format(...args).replace(ANSI_RE, '') + '\\n';\n appendFileSync(filePath, line, 'utf-8');\n } catch {\n // debug logging must never crash the build\n }\n };\n rec[WRAPPED_KEY] = filePath;\n}\n\nexport function wrapInstancesForFileLog(\n instances: Debugger[],\n filePath: string,\n): void {\n ensureTruncated(filePath);\n for (const dbg of instances) {\n wrapLog(dbg, filePath);\n }\n}\n\nfunction scopeToFilename(namespace: string): string {\n return namespace.replace(/:/g, '.') + '.log';\n}\n\nexport function wrapInstancesForScopedFileLog(\n instances: Debugger[],\n dirPath: string,\n): void {\n for (const dbg of instances) {\n const scopedPath = join(dirPath, scopeToFilename(dbg.namespace));\n ensureTruncated(scopedPath);\n wrapLog(dbg, scopedPath);\n }\n}\n"],"mappings":";;;;;;;;;AAUA,IAAM,gBAAgB;AACtB,IAAM,cAAc;AAEpB,IAAM,UAAU;AAEhB,IAAa,gBAAgB;AAC7B,IAAa,qBAAqB;AAElC,SAAS,gBAAgB,UAAwB;CAC/C,MAAM,IAAI;CACV,MAAM,YAAa,EAAE,kCAAkC,IAAI,IAAY;CACvE,EAAE,iBAAiB;CACnB,IAAI,UAAU,IAAI,QAAQ,GAAG;CAC7B,IAAI;EACF,UAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;EAChD,cAAc,UAAU,IAAI,OAAO;CACrC,QAAQ,CAER;CACA,UAAU,IAAI,QAAQ;AACxB;AAEA,SAAS,QAAQ,KAAe,UAAwB;CACtD,MAAM,MAAM;CACZ,IAAI,IAAI,iBAAiB,UAAU;CAEnC,MAAM,cACJ,IAAI,gBAAgB,IAAI,yBACnB,IAAI,yBACL,IAAI;CAEV,IAAI,yBAAyB;CAC7B,IAAI,MAAM,SAA0B,GAAG,MAAiB;EACtD,YAAY,MAAM,MAAM,IAAI;EAC5B,IAAI;GACF,MAAM,OAAO,OAAO,GAAG,IAAI,CAAC,CAAC,QAAQ,SAAS,EAAE,IAAI;GACpD,eAAe,UAAU,MAAM,OAAO;EACxC,QAAQ,CAER;CACF;CACA,IAAI,eAAe;AACrB;AAEA,SAAgB,wBACd,WACA,UACM;CACN,gBAAgB,QAAQ;CACxB,KAAK,MAAM,OAAO,WAChB,QAAQ,KAAK,QAAQ;AAEzB;AAEA,SAAS,gBAAgB,WAA2B;CAClD,OAAO,UAAU,QAAQ,MAAM,GAAG,IAAI;AACxC;AAEA,SAAgB,8BACd,WACA,SACM;CACN,KAAK,MAAM,OAAO,WAAW;EAC3B,MAAM,aAAa,KAAK,SAAS,gBAAgB,IAAI,SAAS,CAAC;EAC/D,gBAAgB,UAAU;EAC1B,QAAQ,KAAK,UAAU;CACzB;AACF"}
@@ -3,19 +3,22 @@ import { createDebugHarness } from "./debug-harness.js";
3
3
  import { createDebug } from "obug";
4
4
  //#region packages/platform/src/lib/utils/debug.ts
5
5
  var debugPlatform = createDebug("analog:platform");
6
+ var debugRoutes = createDebug("analog:platform:routes");
7
+ var debugContent = createDebug("analog:platform:content");
8
+ var debugTypedRouter = createDebug("analog:platform:typed-router");
6
9
  var harness = createDebugHarness({
7
10
  fallbackNamespace: "analog:*",
8
11
  instanceGroups: [[
9
12
  debugPlatform,
10
- createDebug("analog:platform:routes"),
11
- createDebug("analog:platform:content"),
12
- createDebug("analog:platform:typed-router")
13
+ debugRoutes,
14
+ debugContent,
15
+ debugTypedRouter
13
16
  ], nitroDebugInstances]
14
17
  });
15
18
  var applyDebugOption = harness.applyDebugOption;
16
19
  var activateDeferredDebug = harness.activateDeferredDebug;
17
20
  harness._resetDeferredDebug;
18
21
  //#endregion
19
- export { activateDeferredDebug, applyDebugOption, debugPlatform };
22
+ export { activateDeferredDebug, applyDebugOption, debugContent, debugPlatform, debugRoutes, debugTypedRouter };
20
23
 
21
24
  //# sourceMappingURL=debug.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"debug.js","names":[],"sources":["../../../../src/lib/utils/debug.ts"],"sourcesContent":["import { createDebug } from 'obug';\nimport { nitroDebugInstances } from '../nitro/debug.js';\nimport { createDebugHarness } from './debug-harness.js';\n\nexport const debugPlatform = createDebug('analog:platform');\nexport const debugRoutes = createDebug('analog:platform:routes');\nexport const debugContent = createDebug('analog:platform:content');\nexport const debugTypedRouter = createDebug('analog:platform:typed-router');\n\nconst platformDebugInstances = [\n debugPlatform,\n debugRoutes,\n debugContent,\n debugTypedRouter,\n];\n\nexport type DebugScope =\n | 'analog:*'\n | 'analog:platform'\n | 'analog:platform:*'\n | 'analog:platform:routes'\n | 'analog:platform:content'\n | 'analog:platform:typed-router'\n | 'analog:angular:*'\n | 'analog:angular:hmr'\n | 'analog:angular:styles'\n | 'analog:angular:compiler'\n | 'analog:angular:compilation-api'\n | 'analog:angular:style-pipeline'\n | 'analog:nitro'\n | 'analog:nitro:*'\n | 'analog:nitro:ssr'\n | 'analog:nitro:prerender'\n | (string & {});\n\nexport type DebugMode = 'build' | 'dev';\n\nexport interface DebugModeOptions {\n scopes?: boolean | DebugScope[];\n mode?: DebugMode;\n /**\n * Write debug output to log files under `tmp/debug/` in the workspace root.\n * - `true` or `'single'` — all output to `tmp/debug/analog.log`\n * - `'scoped'` — one file per scope, e.g. `tmp/debug/analog.angular.hmr.log`\n */\n logFile?: boolean | 'single' | 'scoped';\n}\n\nexport type DebugOption =\n | boolean\n | DebugScope[]\n | DebugModeOptions\n | DebugModeOptions[];\n\nconst harness = createDebugHarness({\n fallbackNamespace: 'analog:*',\n instanceGroups: [platformDebugInstances, nitroDebugInstances],\n});\n\nexport const applyDebugOption: (\n debug: DebugOption | undefined,\n workspaceRoot?: string,\n) => void = harness.applyDebugOption;\nexport const activateDeferredDebug: (command: 'build' | 'serve') => void =\n harness.activateDeferredDebug;\nexport const _resetDeferredDebug: () => void = harness._resetDeferredDebug;\n"],"mappings":";;;;AAIA,IAAa,gBAAgB,YAAY,kBAAkB;AAkD3D,IAAM,UAAU,mBAAmB;CACjC,mBAAmB;CACnB,gBAAgB,CA/Ca;EAC7B;EALyB,YAAY,yBAAyB;EACpC,YAAY,0BAA0B;EAClC,YAAY,+BAA+B;EAO1E,EA0C0C,oBAAoB;CAC9D,CAAC;AAEF,IAAa,mBAGD,QAAQ;AACpB,IAAa,wBACX,QAAQ;AACqC,QAAQ"}
1
+ {"version":3,"file":"debug.js","names":[],"sources":["../../../../src/lib/utils/debug.ts"],"sourcesContent":["import { createDebug } from 'obug';\nimport { nitroDebugInstances } from '../nitro/debug.js';\nimport { createDebugHarness } from './debug-harness.js';\n\nexport const debugPlatform = createDebug('analog:platform');\nexport const debugRoutes = createDebug('analog:platform:routes');\nexport const debugContent = createDebug('analog:platform:content');\nexport const debugTypedRouter = createDebug('analog:platform:typed-router');\n\nconst platformDebugInstances = [\n debugPlatform,\n debugRoutes,\n debugContent,\n debugTypedRouter,\n];\n\nexport type DebugScope =\n | 'analog:*'\n | 'analog:platform'\n | 'analog:platform:*'\n | 'analog:platform:routes'\n | 'analog:platform:content'\n | 'analog:platform:typed-router'\n | 'analog:angular:*'\n | 'analog:angular:hmr'\n | 'analog:angular:styles'\n | 'analog:angular:compiler'\n | 'analog:angular:compilation-api'\n | 'analog:angular:style-pipeline'\n | 'analog:nitro'\n | 'analog:nitro:*'\n | 'analog:nitro:ssr'\n | 'analog:nitro:prerender'\n | (string & {});\n\nexport type DebugMode = 'build' | 'dev';\n\nexport interface DebugModeOptions {\n scopes?: boolean | DebugScope[];\n mode?: DebugMode;\n /**\n * Write debug output to log files under `tmp/debug/` in the workspace root.\n * - `true` or `'single'` — all output to `tmp/debug/analog.log`\n * - `'scoped'` — one file per scope, e.g. `tmp/debug/analog.angular.hmr.log`\n */\n logFile?: boolean | 'single' | 'scoped';\n}\n\nexport type DebugOption =\n | boolean\n | DebugScope[]\n | DebugModeOptions\n | DebugModeOptions[];\n\nconst harness = createDebugHarness({\n fallbackNamespace: 'analog:*',\n instanceGroups: [platformDebugInstances, nitroDebugInstances],\n});\n\nexport const applyDebugOption: (\n debug: DebugOption | undefined,\n workspaceRoot?: string,\n) => void = harness.applyDebugOption;\nexport const activateDeferredDebug: (command: 'build' | 'serve') => void =\n harness.activateDeferredDebug;\nexport const _resetDeferredDebug: () => void = harness._resetDeferredDebug;\n"],"mappings":";;;;AAIA,IAAa,gBAAgB,YAAY,iBAAiB;AAC1D,IAAa,cAAc,YAAY,wBAAwB;AAC/D,IAAa,eAAe,YAAY,yBAAyB;AACjE,IAAa,mBAAmB,YAAY,8BAA8B;AA+C1E,IAAM,UAAU,mBAAmB;CACjC,mBAAmB;CACnB,gBAAgB,CAAC;EA9CjB;EACA;EACA;EACA;CA2CiB,GAAwB,mBAAmB;AAC9D,CAAC;AAED,IAAa,mBAGD,QAAQ;AACpB,IAAa,wBACX,QAAQ;AACqC,QAAQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"rolldown.js","names":[],"sources":["../../../../src/lib/utils/rolldown.ts"],"sourcesContent":["import * as vite from 'vite';\n\nexport function isRolldown(): boolean {\n return !!vite.rolldownVersion;\n}\n\nexport function getJsTransformConfigKey(): 'oxc' | 'esbuild' {\n return isRolldown() ? 'oxc' : 'esbuild';\n}\n\nexport function getBundleOptionsKey(): 'rolldownOptions' | 'rollupOptions' {\n return isRolldown() ? 'rolldownOptions' : 'rollupOptions';\n}\n"],"mappings":";;AAEA,SAAgB,aAAsB;AACpC,QAAO,CAAC,CAAC,KAAK;;AAGhB,SAAgB,0BAA6C;AAC3D,QAAO,YAAY,GAAG,QAAQ;;AAGhC,SAAgB,sBAA2D;AACzE,QAAO,YAAY,GAAG,oBAAoB"}
1
+ {"version":3,"file":"rolldown.js","names":[],"sources":["../../../../src/lib/utils/rolldown.ts"],"sourcesContent":["import * as vite from 'vite';\n\nexport function isRolldown(): boolean {\n return !!vite.rolldownVersion;\n}\n\nexport function getJsTransformConfigKey(): 'oxc' | 'esbuild' {\n return isRolldown() ? 'oxc' : 'esbuild';\n}\n\nexport function getBundleOptionsKey(): 'rolldownOptions' | 'rollupOptions' {\n return isRolldown() ? 'rolldownOptions' : 'rollupOptions';\n}\n"],"mappings":";;AAEA,SAAgB,aAAsB;CACpC,OAAO,CAAC,CAAC,KAAK;AAChB;AAEA,SAAgB,0BAA6C;CAC3D,OAAO,WAAW,IAAI,QAAQ;AAChC;AAEA,SAAgB,sBAA2D;CACzE,OAAO,WAAW,IAAI,oBAAoB;AAC5C"}
@@ -1 +1 @@
1
- {"version":3,"file":"server-mode-plugin.js","names":[],"sources":["../../src/server-mode-plugin.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { Plugin, normalizePath } from 'vite';\n\nexport const SERVER_MODE_ID = 'virtual:@analogjs/platform/server-mode';\nconst RESOLVED_SERVER_MODE_ID = `\\0${SERVER_MODE_ID}`;\n\n/**\n * Serves a module that sets `globalThis.ngServerMode = true`.\n *\n * Angular decides at provider-creation time whether browser-only features\n * such as event replay apply, and `provideClientHydration()` runs while the\n * app config module evaluates, before `platformServer()` or\n * `provideServerRendering()` set the flag. The SSR entry imports this module\n * first so the flag is set before any application module evaluates. A text\n * replacement cannot do this because Angular is pre-bundled for SSR.\n */\nexport function serverModePlugin(entryServer?: string): Plugin[] {\n let entryServerPath = '';\n\n return [\n {\n name: 'analogjs-server-mode-plugin',\n configResolved(config) {\n entryServerPath = normalizePath(\n resolve(config.root, entryServer ?? 'src/main.server.ts'),\n );\n },\n // Nitro v2 builds `main.server.ts` as the SSR entry directly, so set the\n // flag there too.\n transform(code, id, options) {\n if (options?.ssr && normalizePath(id) === entryServerPath) {\n return {\n code: `import ${JSON.stringify(SERVER_MODE_ID)};\\n${code}`,\n map: null,\n };\n }\n return undefined;\n },\n resolveId(id) {\n return id === SERVER_MODE_ID ? RESOLVED_SERVER_MODE_ID : undefined;\n },\n load(id) {\n return id === RESOLVED_SERVER_MODE_ID\n ? 'globalThis.ngServerMode = true;\\nexport {};\\n'\n : undefined;\n },\n },\n ];\n}\n"],"mappings":";;;AAGA,IAAa,iBAAiB;AAC9B,IAAM,0BAA0B,KAAK;;;;;;;;;;;AAYrC,SAAgB,iBAAiB,aAAgC;CAC/D,IAAI,kBAAkB;AAEtB,QAAO,CACL;EACE,MAAM;EACN,eAAe,QAAQ;AACrB,qBAAkB,cAChB,QAAQ,OAAO,MAAM,eAAe,qBAAqB,CAC1D;;EAIH,UAAU,MAAM,IAAI,SAAS;AAC3B,OAAI,SAAS,OAAO,cAAc,GAAG,KAAK,gBACxC,QAAO;IACL,MAAM,UAAU,KAAK,UAAU,eAAe,CAAC,KAAK;IACpD,KAAK;IACN;;EAIL,UAAU,IAAI;AACZ,UAAO,OAAA,2CAAwB,0BAA0B,KAAA;;EAE3D,KAAK,IAAI;AACP,UAAO,OAAO,0BACV,kDACA,KAAA;;EAEP,CACF"}
1
+ {"version":3,"file":"server-mode-plugin.js","names":[],"sources":["../../src/server-mode-plugin.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { Plugin, normalizePath } from 'vite';\n\nexport const SERVER_MODE_ID = 'virtual:@analogjs/platform/server-mode';\nconst RESOLVED_SERVER_MODE_ID = `\\0${SERVER_MODE_ID}`;\n\n/**\n * Serves a module that sets `globalThis.ngServerMode = true`.\n *\n * Angular decides at provider-creation time whether browser-only features\n * such as event replay apply, and `provideClientHydration()` runs while the\n * app config module evaluates, before `platformServer()` or\n * `provideServerRendering()` set the flag. The SSR entry imports this module\n * first so the flag is set before any application module evaluates. A text\n * replacement cannot do this because Angular is pre-bundled for SSR.\n */\nexport function serverModePlugin(entryServer?: string): Plugin[] {\n let entryServerPath = '';\n\n return [\n {\n name: 'analogjs-server-mode-plugin',\n configResolved(config) {\n entryServerPath = normalizePath(\n resolve(config.root, entryServer ?? 'src/main.server.ts'),\n );\n },\n // Nitro v2 builds `main.server.ts` as the SSR entry directly, so set the\n // flag there too.\n transform(code, id, options) {\n if (options?.ssr && normalizePath(id) === entryServerPath) {\n return {\n code: `import ${JSON.stringify(SERVER_MODE_ID)};\\n${code}`,\n map: null,\n };\n }\n return undefined;\n },\n resolveId(id) {\n return id === SERVER_MODE_ID ? RESOLVED_SERVER_MODE_ID : undefined;\n },\n load(id) {\n return id === RESOLVED_SERVER_MODE_ID\n ? 'globalThis.ngServerMode = true;\\nexport {};\\n'\n : undefined;\n },\n },\n ];\n}\n"],"mappings":";;;AAGA,IAAa,iBAAiB;AAC9B,IAAM,0BAA0B,KAAK;;;;;;;;;;;AAYrC,SAAgB,iBAAiB,aAAgC;CAC/D,IAAI,kBAAkB;CAEtB,OAAO,CACL;EACE,MAAM;EACN,eAAe,QAAQ;GACrB,kBAAkB,cAChB,QAAQ,OAAO,MAAM,eAAe,oBAAoB,CAC1D;EACF;EAGA,UAAU,MAAM,IAAI,SAAS;GAC3B,IAAI,SAAS,OAAO,cAAc,EAAE,MAAM,iBACxC,OAAO;IACL,MAAM,UAAU,KAAK,UAAU,cAAc,EAAE,KAAK;IACpD,KAAK;GACP;EAGJ;EACA,UAAU,IAAI;GACZ,OAAO,OAAA,2CAAwB,0BAA0B,KAAA;EAC3D;EACA,KAAK,IAAI;GACP,OAAO,OAAO,0BACV,kDACA,KAAA;EACN;CACF,CACF;AACF"}
@@ -1,28 +0,0 @@
1
- export declare const V18_X_NX_DEVKIT = "^20.0.0";
2
- export declare const V18_X_NX_ANGULAR = "^20.0.0";
3
- export declare const V18_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.98";
4
- export declare const V18_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.98";
5
- export declare const V18_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.98";
6
- export declare const V18_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.98";
7
- export declare const V18_X_FRONT_MATTER = "^4.0.2";
8
- export declare const V18_X_MARKED = "^18.0.0";
9
- export declare const V18_X_MARKED_GFM_HEADING_ID = "^4.1.1";
10
- export declare const V18_X_MARKED_HIGHLIGHT = "^2.2.1";
11
- export declare const V18_X_MARKED_MANGLE = "^1.1.10";
12
- export declare const V18_X_MERMAID = "^10.2.4";
13
- export declare const V18_X_PRISMJS = "^1.29.0";
14
- export declare const V18_X_TAILWINDCSS = "^4.2.2";
15
- export declare const V18_X_TAILWINDCSS_POSTCSS = "^4.2.2";
16
- export declare const V18_X_TAILWINDCSS_VITE = "^4.2.2";
17
- export declare const V18_X_POSTCSS = "^8.5.6";
18
- export declare const V18_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.98";
19
- export declare const V18_X_ANALOG_JS_VITE_PLUGIN_NITRO = "^3.0.0-alpha.98";
20
- export declare const V18_X_H3 = "^1.13.0";
21
- export declare const V18_X_ANGULAR_DEVKIT_BUILD_ANGULAR = "^20.0.0";
22
- export declare const V18_X_NX_VITE = "^21.0.0";
23
- export declare const V18_X_NX_LINTER = "^21.0.0";
24
- export declare const V18_X_JSDOM = "^22.1.0";
25
- export declare const V18_X_VITE = "^8.0.0";
26
- export declare const V18_X_VITE_TSCONFIG_PATHS = "^4.2.0";
27
- export declare const V18_X_VITEST = "^5.0.0";
28
- export declare const V18_X_ZOD = "^3.21.4";
@@ -1,12 +0,0 @@
1
- //#region packages/nx-plugin/src/generators/app/versions/nx_18_X/versions.ts
2
- var V18_X_TAILWINDCSS = "^4.2.2";
3
- var V18_X_TAILWINDCSS_POSTCSS = "^4.2.2";
4
- var V18_X_TAILWINDCSS_VITE = "^4.2.2";
5
- var V18_X_POSTCSS = "^8.5.6";
6
- //#endregion
7
- exports.V18_X_POSTCSS = V18_X_POSTCSS;
8
- exports.V18_X_TAILWINDCSS = V18_X_TAILWINDCSS;
9
- exports.V18_X_TAILWINDCSS_POSTCSS = V18_X_TAILWINDCSS_POSTCSS;
10
- exports.V18_X_TAILWINDCSS_VITE = V18_X_TAILWINDCSS_VITE;
11
-
12
- //# sourceMappingURL=versions.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"versions.js","names":[],"sources":["../../../../../../../../../../nx-plugin/src/generators/app/versions/nx_18_X/versions.ts"],"sourcesContent":["// V18_X\n// dependencies\nexport const V18_X_NX_DEVKIT = '^20.0.0';\nexport const V18_X_NX_ANGULAR = '^20.0.0';\nexport const V18_X_ANALOG_JS_CONTENT = '^3.0.0-alpha.98';\nexport const V18_X_ANALOG_JS_ROUTER = '^3.0.0-alpha.98';\nexport const V18_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = '^3.0.0-alpha.98';\nexport const V18_X_ANALOG_JS_VITEST_ANGULAR = '^3.0.0-alpha.98';\nexport const V18_X_FRONT_MATTER = '^4.0.2';\nexport const V18_X_MARKED = '^18.0.0';\nexport const V18_X_MARKED_GFM_HEADING_ID = '^4.1.1';\nexport const V18_X_MARKED_HIGHLIGHT = '^2.2.1';\nexport const V18_X_MARKED_MANGLE = '^1.1.10';\nexport const V18_X_MERMAID = '^10.2.4';\nexport const V18_X_PRISMJS = '^1.29.0';\nexport const V18_X_TAILWINDCSS = '^4.2.2';\nexport const V18_X_TAILWINDCSS_POSTCSS = '^4.2.2';\nexport const V18_X_TAILWINDCSS_VITE = '^4.2.2';\nexport const V18_X_POSTCSS = '^8.5.6';\n\n// devDependencies\nexport const V18_X_ANALOG_JS_PLATFORM = '^3.0.0-alpha.98';\nexport const V18_X_ANALOG_JS_VITE_PLUGIN_NITRO = '^3.0.0-alpha.98';\nexport const V18_X_H3 = '^1.13.0';\nexport const V18_X_ANGULAR_DEVKIT_BUILD_ANGULAR = '^20.0.0';\nexport const V18_X_NX_VITE = '^21.0.0';\nexport const V18_X_NX_LINTER = '^21.0.0';\nexport const V18_X_JSDOM = '^22.1.0';\nexport const V18_X_VITE = '^8.0.0';\nexport const V18_X_VITE_TSCONFIG_PATHS = '^4.2.0';\nexport const V18_X_VITEST = '^5.0.0';\nexport const V18_X_ZOD = '^3.21.4';\n"],"mappings":";AAeA,IAAa,oBAAoB;AACjC,IAAa,4BAA4B;AACzC,IAAa,yBAAyB;AACtC,IAAa,gBAAgB"}
@@ -1,15 +0,0 @@
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
- export declare const V19_X_MARKED = "^18.0.0";
4
- export declare const V19_X_MARKED_GFM_HEADING_ID = "^4.1.1";
5
- export declare const V19_X_MARKED_HIGHLIGHT = "^2.2.1";
6
- export declare const V19_X_MARKED_MANGLE = "^1.1.10";
7
- export declare const V19_X_PRISMJS = "^1.29.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";
11
- export declare const V19_X_JSDOM = "^22.0.0";
12
- export declare const V19_X_VITE_TSCONFIG_PATHS = "^4.2.0";
13
- export declare const NX_X_LATEST_VITE = "^8.0.0";
14
- export declare const NX_X_LATEST_VITEST = "^5.0.0";
15
- export declare const NX_X_LATEST_NX = "^23.2.0";
@@ -1,34 +0,0 @@
1
- //#region packages/nx-plugin/src/utils/versions/ng_19_X/versions.ts
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
- var V19_X_MARKED = "^18.0.0";
5
- var V19_X_MARKED_GFM_HEADING_ID = "^4.1.1";
6
- var V19_X_MARKED_HIGHLIGHT = "^2.2.1";
7
- var V19_X_MARKED_MANGLE = "^1.1.10";
8
- var V19_X_PRISMJS = "^1.29.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";
12
- var V19_X_JSDOM = "^22.0.0";
13
- var V19_X_VITE_TSCONFIG_PATHS = "^4.2.0";
14
- var NX_X_LATEST_VITE = "^8.0.0";
15
- var NX_X_LATEST_VITEST = "^5.0.0";
16
- var NX_X_LATEST_NX = "^23.2.0";
17
- //#endregion
18
- exports.NX_X_LATEST_NX = NX_X_LATEST_NX;
19
- exports.NX_X_LATEST_VITE = NX_X_LATEST_VITE;
20
- exports.NX_X_LATEST_VITEST = NX_X_LATEST_VITEST;
21
- exports.V19_X_ANALOG_JS_CONTENT = V19_X_ANALOG_JS_CONTENT;
22
- exports.V19_X_ANALOG_JS_PLATFORM = V19_X_ANALOG_JS_PLATFORM;
23
- exports.V19_X_ANALOG_JS_ROUTER = V19_X_ANALOG_JS_ROUTER;
24
- exports.V19_X_ANALOG_JS_VITEST_ANGULAR = V19_X_ANALOG_JS_VITEST_ANGULAR;
25
- exports.V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR;
26
- exports.V19_X_JSDOM = V19_X_JSDOM;
27
- exports.V19_X_MARKED = V19_X_MARKED;
28
- exports.V19_X_MARKED_GFM_HEADING_ID = V19_X_MARKED_GFM_HEADING_ID;
29
- exports.V19_X_MARKED_HIGHLIGHT = V19_X_MARKED_HIGHLIGHT;
30
- exports.V19_X_MARKED_MANGLE = V19_X_MARKED_MANGLE;
31
- exports.V19_X_PRISMJS = V19_X_PRISMJS;
32
- exports.V19_X_VITE_TSCONFIG_PATHS = V19_X_VITE_TSCONFIG_PATHS;
33
-
34
- //# sourceMappingURL=versions.js.map
@@ -1 +0,0 @@
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"}