@ecopages/core 0.2.0-beta.31 → 0.2.0-beta.32

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 (338) hide show
  1. package/README.md +1 -0
  2. package/package.json +82 -3
  3. package/src/adapters/abstract/application-adapter.d.ts +0 -8
  4. package/src/adapters/abstract/application-adapter.js +252 -261
  5. package/src/adapters/abstract/router-adapter.js +12 -4
  6. package/src/adapters/abstract/segment-path-matcher.js +132 -134
  7. package/src/adapters/abstract/server-adapter.js +22 -14
  8. package/src/adapters/abstract/ws-pattern-matcher.js +24 -26
  9. package/src/adapters/bun/bun-user-websocket-lifecycle.js +83 -86
  10. package/src/adapters/bun/client-bridge.js +51 -50
  11. package/src/adapters/bun/create-app.js +171 -161
  12. package/src/adapters/bun/hmr-manager.js +62 -57
  13. package/src/adapters/bun/index.js +2 -31
  14. package/src/adapters/bun/runtime-host.js +61 -54
  15. package/src/adapters/bun/server-adapter.d.ts +0 -1
  16. package/src/adapters/bun/server-adapter.js +561 -561
  17. package/src/adapters/bun/static-preview-host.js +43 -46
  18. package/src/adapters/create-app.js +11 -14
  19. package/src/adapters/index.js +2 -30
  20. package/src/adapters/node/create-app.js +123 -129
  21. package/src/adapters/node/http-request-bridge.js +147 -106
  22. package/src/adapters/node/node-client-bridge.js +62 -64
  23. package/src/adapters/node/node-hmr-manager.js +58 -48
  24. package/src/adapters/node/runtime-host.js +96 -89
  25. package/src/adapters/node/server-adapter-dependencies.js +18 -21
  26. package/src/adapters/node/server-adapter.d.ts +0 -1
  27. package/src/adapters/node/server-adapter.js +347 -326
  28. package/src/adapters/node/static-content-server.js +172 -191
  29. package/src/adapters/node/static-preview-host.js +87 -78
  30. package/src/adapters/shared/hmr/dev-transform-entrypoint-registry.d.ts +16 -0
  31. package/src/adapters/shared/hmr/dev-transform-entrypoint-registry.js +32 -0
  32. package/src/adapters/shared/hmr/hmr-html-response.js +46 -32
  33. package/src/adapters/shared/hmr/shared-hmr-manager.d.ts +29 -38
  34. package/src/adapters/shared/hmr/shared-hmr-manager.js +439 -425
  35. package/src/adapters/shared/http/api-request-pipeline.js +122 -119
  36. package/src/adapters/shared/http/api-response.js +77 -77
  37. package/src/adapters/shared/http/create-body-response.js +22 -19
  38. package/src/adapters/shared/http/define-api-handler.js +41 -55
  39. package/src/adapters/shared/http/explicit-static-render-preparation.js +26 -23
  40. package/src/adapters/shared/http/explicit-static-route-matcher.js +55 -62
  41. package/src/adapters/shared/http/file-route-middleware-pipeline.js +81 -75
  42. package/src/adapters/shared/http/fs-server-response-factory.js +101 -96
  43. package/src/adapters/shared/http/fs-server-response-matcher.js +239 -257
  44. package/src/adapters/shared/http/run-middleware-chain.js +15 -15
  45. package/src/adapters/shared/runtime/application-adapter.js +72 -87
  46. package/src/adapters/shared/runtime/copy-runtime-public-dir.js +25 -22
  47. package/src/adapters/shared/runtime/port-manager.js +207 -177
  48. package/src/adapters/shared/runtime/render-context.js +94 -79
  49. package/src/adapters/shared/runtime/runtime-app-bootstrap.js +33 -38
  50. package/src/adapters/shared/runtime/runtime-host.js +1 -0
  51. package/src/adapters/shared/runtime/runtime-server-lifecycle.d.ts +2 -3
  52. package/src/adapters/shared/runtime/runtime-server-lifecycle.js +65 -51
  53. package/src/adapters/shared/runtime/server-adapter.d.ts +1 -0
  54. package/src/adapters/shared/runtime/server-adapter.js +233 -217
  55. package/src/adapters/shared/runtime/server-route-handler.js +74 -70
  56. package/src/adapters/shared/runtime/server-static-builder.js +165 -184
  57. package/src/adapters/shared/runtime/static-preview-host.js +1 -0
  58. package/src/adapters/shared/ws/node-http-websocket-upgrades.js +97 -88
  59. package/src/adapters/shared/ws/websocket-lifecycle.js +57 -50
  60. package/src/build/README.md +52 -1
  61. package/src/build/app-build-manifest-runtime.js +129 -108
  62. package/src/build/browser/browser-runtime-manifest.js +47 -50
  63. package/src/build/browser/browser-runtime-plugin-helpers.js +32 -12
  64. package/src/build/browser/browser-runtime-plugin.js +206 -160
  65. package/src/build/browser/jsx-ownership-plugins.js +51 -37
  66. package/src/build/browser/lit-static-render-worker-context.js +3 -5
  67. package/src/build/build-adapter.js +276 -139
  68. package/src/build/cache/build-input-fingerprint.js +28 -26
  69. package/src/build/cache/cache-constants.js +4 -6
  70. package/src/build/cache/cache-keys.js +64 -56
  71. package/src/build/cache/pages-unified-graph-build.js +179 -178
  72. package/src/build/cache/production-build-cache.js +47 -54
  73. package/src/build/cache/server-entry-build-cache.js +134 -146
  74. package/src/build/contracts/build-contracts.d.ts +4 -4
  75. package/src/build/contracts/build-contracts.js +1 -0
  76. package/src/build/contracts/build-manifest.js +58 -43
  77. package/src/build/contracts/build-types.js +1 -0
  78. package/src/build/rolldown/rolldown-adapter-helpers.d.ts +9 -0
  79. package/src/build/rolldown/rolldown-adapter-helpers.js +405 -298
  80. package/src/build/rolldown/rolldown-build-adapter.js +53 -61
  81. package/src/build/rolldown/rolldown-build-invocation-metrics.js +31 -35
  82. package/src/build/rolldown/rolldown-plugin-bridge.js +264 -194
  83. package/src/build/rolldown/rolldown-source-transform-pass.js +59 -50
  84. package/src/build/rolldown/runtime-build-output-normalizer.js +227 -175
  85. package/src/build/rolldown/server-side-css-shim-plugin.js +54 -31
  86. package/src/build/runtime/build-profile-options.js +36 -36
  87. package/src/build/runtime/build-request-identity.js +70 -45
  88. package/src/build/runtime/build-request-policy.js +80 -42
  89. package/src/build/runtime/build-runtime.js +64 -54
  90. package/src/build/runtime/deduping-build-executor.js +38 -32
  91. package/src/build/runtime/parallel-build-executor.js +58 -50
  92. package/src/build/runtime/serialized-build-executor.js +78 -62
  93. package/src/cache/index.js +6 -6
  94. package/src/cache/module-parse-cache.js +99 -68
  95. package/src/client/navigation-scripts/classification.js +45 -37
  96. package/src/client/navigation-scripts/index.js +3 -23
  97. package/src/client/navigation-scripts/navigation-scripts.test.browser.js +58 -62
  98. package/src/client/navigation-scripts/registry.js +15 -11
  99. package/src/client/navigation-scripts/rerun-queue.js +81 -64
  100. package/src/client/scroll.js +32 -24
  101. package/src/client/scroll.test.browser.js +27 -33
  102. package/src/client/view-transitions.js +68 -55
  103. package/src/config/config-builder.d.ts +8 -0
  104. package/src/config/config-builder.js +637 -642
  105. package/src/config/constants.js +44 -24
  106. package/src/declarations.d.ts +1 -0
  107. package/src/dev/README.md +24 -0
  108. package/src/dev/client-bridge-registry.js +7 -12
  109. package/src/dev/dev-client-ownership.js +7 -5
  110. package/src/dev/hmr-manager-registry.js +7 -12
  111. package/src/dev/host-runtime.js +25 -30
  112. package/src/dev/runtime-server-started-message.js +2 -5
  113. package/src/dev/sc-server.js +106 -103
  114. package/src/dev/transform-server/dev-transform-bundler.d.ts +24 -0
  115. package/src/dev/transform-server/dev-transform-bundler.js +79 -0
  116. package/src/dev/transform-server/dev-transform-externalize-plugin.d.ts +12 -0
  117. package/src/dev/transform-server/dev-transform-externalize-plugin.js +29 -0
  118. package/src/dev/transform-server/dev-transform-import-rewriter.d.ts +16 -0
  119. package/src/dev/transform-server/dev-transform-import-rewriter.js +110 -0
  120. package/src/dev/transform-server/dev-transform-module-kind.d.ts +13 -0
  121. package/src/dev/transform-server/dev-transform-module-kind.js +17 -0
  122. package/src/dev/transform-server/dev-transform-runtime-specifiers.d.ts +5 -0
  123. package/src/dev/transform-server/dev-transform-runtime-specifiers.js +16 -0
  124. package/src/dev/transform-server/dev-transform-server.d.ts +41 -0
  125. package/src/dev/transform-server/dev-transform-server.js +191 -0
  126. package/src/dev/transform-server/dev-transform-url.d.ts +13 -0
  127. package/src/dev/transform-server/dev-transform-url.js +58 -0
  128. package/src/dev/transform-server/dev-transform-vendor-registry.d.ts +27 -0
  129. package/src/dev/transform-server/dev-transform-vendor-registry.js +145 -0
  130. package/src/dev/transform-server/index.d.ts +6 -0
  131. package/src/dev/transform-server/index.js +4 -0
  132. package/src/dev/transform-server/types.d.ts +15 -0
  133. package/src/dev/transform-server/types.js +1 -0
  134. package/src/dev-toolbar/README.md +51 -0
  135. package/src/dev-toolbar/define-dev-tool.d.ts +21 -0
  136. package/src/dev-toolbar/define-dev-tool.js +9 -0
  137. package/src/dev-toolbar/dev-toolbar-config.d.ts +25 -0
  138. package/src/dev-toolbar/dev-toolbar-config.js +19 -0
  139. package/src/dev-toolbar/dev-toolbar-host.d.ts +49 -0
  140. package/src/dev-toolbar/dev-toolbar-host.js +117 -0
  141. package/src/dev-toolbar/dev-toolbar-html-response.d.ts +11 -0
  142. package/src/dev-toolbar/dev-toolbar-html-response.js +14 -0
  143. package/src/dev-toolbar/dev-toolbar-manifest.d.ts +15 -0
  144. package/src/dev-toolbar/dev-toolbar-manifest.js +52 -0
  145. package/src/dev-toolbar/dev-toolbar-package.d.ts +31 -0
  146. package/src/dev-toolbar/dev-toolbar-package.js +72 -0
  147. package/src/dev-toolbar/dev-toolbar-runtime-paths.d.ts +10 -0
  148. package/src/dev-toolbar/dev-toolbar-runtime-paths.js +13 -0
  149. package/src/diagnostics/request-build-dedupe.js +34 -28
  150. package/src/diagnostics/startup-trace.d.ts +10 -1
  151. package/src/diagnostics/startup-trace.js +269 -241
  152. package/src/eco/README.md +6 -0
  153. package/src/eco/eco-declared-component.js +19 -14
  154. package/src/eco/eco.browser.js +56 -69
  155. package/src/eco/eco.js +156 -96
  156. package/src/eco/eco.types.js +5 -0
  157. package/src/eco/eco.utils.js +1 -10
  158. package/src/eco/global-injector-map.js +55 -41
  159. package/src/eco/lazy-injector-map.js +90 -60
  160. package/src/eco/module-dependencies.js +52 -44
  161. package/src/eco/page-layout-normalization.js +59 -49
  162. package/src/errors/http-error.js +50 -49
  163. package/src/errors/index.js +3 -12
  164. package/src/errors/locals-access-error.js +5 -8
  165. package/src/errors/undeclared-component-dependency-error.js +15 -15
  166. package/src/global/app-logger.js +3 -6
  167. package/src/hmr/README.md +10 -0
  168. package/src/hmr/client/hmr-runtime.js +117 -113
  169. package/src/hmr/client/module-update.d.ts +13 -0
  170. package/src/hmr/client/module-update.js +39 -0
  171. package/src/hmr/hmr-asset-paths.d.ts +14 -0
  172. package/src/hmr/hmr-asset-paths.js +22 -0
  173. package/src/hmr/hmr-entrypoint-output.d.ts +11 -39
  174. package/src/hmr/hmr-entrypoint-output.js +28 -54
  175. package/src/hmr/hmr-file-change-prep.js +13 -16
  176. package/src/hmr/hmr-registered-entrypoints-context.d.ts +7 -0
  177. package/src/hmr/hmr-registered-entrypoints-context.js +1 -0
  178. package/src/hmr/hmr-runtime-paths.d.ts +10 -0
  179. package/src/hmr/hmr-runtime-paths.js +13 -0
  180. package/src/hmr/hmr-strategy.d.ts +5 -11
  181. package/src/hmr/hmr-strategy.js +113 -61
  182. package/src/hmr/strategies/default-hmr-strategy.js +55 -33
  183. package/src/hmr/strategies/js-hmr-strategy.d.ts +6 -95
  184. package/src/hmr/strategies/js-hmr-strategy.js +104 -217
  185. package/src/hmr/strategies/server-rendered-template-hmr-strategy.js +33 -27
  186. package/src/index.browser.js +1 -4
  187. package/src/index.d.ts +1 -0
  188. package/src/index.js +6 -47
  189. package/src/integrations/ghtml/ghtml-renderer.js +12 -7
  190. package/src/integrations/ghtml/ghtml.constants.js +1 -4
  191. package/src/integrations/ghtml/ghtml.plugin.js +9 -12
  192. package/src/islands/README.md +25 -0
  193. package/src/islands/island-host.d.ts +37 -0
  194. package/src/islands/island-host.js +55 -0
  195. package/src/plugins/alias-resolver-cache.js +114 -97
  196. package/src/plugins/alias-resolver-plugin.js +33 -47
  197. package/src/plugins/define-integration.js +18 -17
  198. package/src/plugins/eco-component-meta-plugin.js +387 -145
  199. package/src/plugins/foreign-jsx-override-plugin.js +49 -35
  200. package/src/plugins/integration-plugin.js +212 -203
  201. package/src/plugins/jsx-import-source.utils.js +16 -21
  202. package/src/plugins/processor.js +159 -149
  203. package/src/plugins/runtime-capability.js +1 -0
  204. package/src/plugins/source-transform.js +109 -79
  205. package/src/plugins/tsconfig-import-resolver.d.ts +17 -0
  206. package/src/plugins/tsconfig-import-resolver.js +218 -142
  207. package/src/route-renderer/README.md +12 -1
  208. package/src/route-renderer/orchestration/document-shell/document-shell-render.service.js +143 -136
  209. package/src/route-renderer/orchestration/document-shell/layout-shell-props.service.js +33 -27
  210. package/src/route-renderer/orchestration/foreign-child/component-render-context.js +173 -126
  211. package/src/route-renderer/orchestration/foreign-child/foreign-child-output.utils.js +165 -154
  212. package/src/route-renderer/orchestration/foreign-child/foreign-subtree-execution.service.js +329 -341
  213. package/src/route-renderer/orchestration/foreign-child/owning-renderer-resolution.js +33 -32
  214. package/src/route-renderer/orchestration/integration-renderer.d.ts +1 -0
  215. package/src/route-renderer/orchestration/integration-renderer.js +731 -750
  216. package/src/route-renderer/orchestration/integration-renderer.test-fixtures.js +168 -172
  217. package/src/route-renderer/orchestration/ownership-graph/component-graph-collectors.js +119 -131
  218. package/src/route-renderer/orchestration/ownership-graph/component-graph.js +82 -91
  219. package/src/route-renderer/orchestration/ownership-graph/ownership-validation.service.js +82 -76
  220. package/src/route-renderer/orchestration/page-browser-graph/global-injector-assets.service.js +29 -31
  221. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-contribution.loader.js +10 -6
  222. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.js +31 -28
  223. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.d.ts +3 -0
  224. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.js +430 -413
  225. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph.service.js +191 -212
  226. package/src/route-renderer/orchestration/page-browser-graph/processed-asset-dedupe.js +20 -24
  227. package/src/route-renderer/orchestration/route-pipeline/integration-route-render-adapter.d.ts +4 -0
  228. package/src/route-renderer/orchestration/route-pipeline/integration-route-render-adapter.js +24 -24
  229. package/src/route-renderer/orchestration/route-pipeline/marker-artifact.utils.js +22 -24
  230. package/src/route-renderer/orchestration/route-pipeline/robots-meta.contribution.js +41 -34
  231. package/src/route-renderer/orchestration/route-pipeline/route-html-finalization.service.d.ts +5 -0
  232. package/src/route-renderer/orchestration/route-pipeline/route-html-finalization.service.js +42 -22
  233. package/src/route-renderer/orchestration/route-pipeline/route-prepared-options.builder.js +50 -48
  234. package/src/route-renderer/orchestration/route-pipeline/route-prepared-options.utils.js +29 -32
  235. package/src/route-renderer/orchestration/route-pipeline/route-render-orchestrator.js +150 -156
  236. package/src/route-renderer/orchestration/string-markup-renderer.js +48 -51
  237. package/src/route-renderer/page-loading/component-dependency-collection.js +120 -124
  238. package/src/route-renderer/page-loading/declared-asset-collection.js +66 -94
  239. package/src/route-renderer/page-loading/dependency-resolver.js +101 -104
  240. package/src/route-renderer/page-loading/ecopages-virtual-imports.js +68 -54
  241. package/src/route-renderer/page-loading/lazy-entry-collection.js +75 -98
  242. package/src/route-renderer/page-loading/lazy-trigger-planning.js +46 -36
  243. package/src/route-renderer/page-loading/module-declaration-aggregation.js +38 -31
  244. package/src/route-renderer/page-loading/module-declaration-scripts.js +10 -15
  245. package/src/route-renderer/page-loading/page-dependency-bundling.js +156 -113
  246. package/src/route-renderer/page-loading/page-module-loader.js +130 -124
  247. package/src/route-renderer/route-renderer.js +74 -76
  248. package/src/router/client/link-intent.js +55 -39
  249. package/src/router/client/link-intent.test.browser.js +49 -49
  250. package/src/router/client/link-navigation-policy.js +114 -93
  251. package/src/router/client/link-navigation-policy.test.browser.js +70 -80
  252. package/src/router/client/navigation-coordinator.js +235 -211
  253. package/src/router/server/route-registry.js +237 -242
  254. package/src/services/assets/asset-processing-service/asset-dependency-keys.js +64 -53
  255. package/src/services/assets/asset-processing-service/asset-processing.service.js +287 -303
  256. package/src/services/assets/asset-processing-service/asset.factory.js +78 -81
  257. package/src/services/assets/asset-processing-service/assets.types.js +1 -0
  258. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.js +57 -46
  259. package/src/services/assets/asset-processing-service/browser-runtime-entry-resolution.js +106 -69
  260. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.js +53 -53
  261. package/src/services/assets/asset-processing-service/finalize-processed-asset.js +4 -6
  262. package/src/services/assets/asset-processing-service/grouped-content-bundles.js +79 -80
  263. package/src/services/assets/asset-processing-service/index.js +8 -8
  264. package/src/services/assets/asset-processing-service/inline-content-script-body.js +22 -16
  265. package/src/services/assets/asset-processing-service/materialize-content-script-asset.js +21 -17
  266. package/src/services/assets/asset-processing-service/page-package.js +70 -72
  267. package/src/services/assets/asset-processing-service/processor.interface.js +7 -5
  268. package/src/services/assets/asset-processing-service/processor.registry.js +11 -14
  269. package/src/services/assets/asset-processing-service/processors/base/base-processor.js +59 -62
  270. package/src/services/assets/asset-processing-service/processors/base/base-script-processor.js +102 -117
  271. package/src/services/assets/asset-processing-service/processors/index.js +5 -5
  272. package/src/services/assets/asset-processing-service/processors/script/content-script.processor.js +110 -115
  273. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +95 -97
  274. package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +136 -135
  275. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +27 -29
  276. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +34 -40
  277. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.js +36 -35
  278. package/src/services/assets/asset-processing-service/resolve-integration-plugin.js +14 -16
  279. package/src/services/assets/asset-processing-service/ungrouped-dependency-processing.js +27 -39
  280. package/src/services/assets/browser-bundle.service.js +52 -47
  281. package/src/services/cache/cache.types.js +5 -0
  282. package/src/services/cache/index.js +6 -7
  283. package/src/services/cache/memory-cache-store.js +101 -89
  284. package/src/services/cache/page-cache-service.js +156 -145
  285. package/src/services/cache/page-request-cache-coordinator.service.js +102 -102
  286. package/src/services/html/html-rewriter-provider.service.js +61 -61
  287. package/src/services/html/html-transformer.service.js +249 -266
  288. package/src/services/invalidation/development-invalidation.service.js +252 -241
  289. package/src/services/module-loading/app-module-loader.service.js +1 -0
  290. package/src/services/module-loading/app-server-module-transpiler.service.js +107 -101
  291. package/src/services/module-loading/host-module-loader-registry.js +6 -11
  292. package/src/services/module-loading/module-loading-types.js +1 -0
  293. package/src/services/module-loading/page-module-import.service.js +235 -243
  294. package/src/services/module-loading/route-module-build-cache-registry.js +47 -33
  295. package/src/services/module-loading/route-module-build-cache.js +3 -38
  296. package/src/services/module-loading/route-module-build-cache.store.js +195 -200
  297. package/src/services/module-loading/route-module-build-manifest.js +61 -74
  298. package/src/services/module-loading/route-module-dependency-hasher.js +136 -110
  299. package/src/services/module-loading/server-module-transpiler.service.js +68 -62
  300. package/src/services/module-loading/source-module-support.js +15 -7
  301. package/src/services/runtime-state/dev-graph.service.js +165 -157
  302. package/src/services/runtime-state/entrypoint-dependency-graph.service.js +85 -76
  303. package/src/services/runtime-state/server-invalidation-state.service.js +38 -30
  304. package/src/services/validation/schema-validation-service.js +157 -95
  305. package/src/services/validation/standard-schema.types.js +1 -0
  306. package/src/services/validation/validate-standard-schema.js +13 -13
  307. package/src/static-site-generator/production-page-browser-graph-prebuild.js +35 -19
  308. package/src/static-site-generator/sitemap-routes.js +33 -28
  309. package/src/static-site-generator/sitemap.js +38 -29
  310. package/src/static-site-generator/static-build-invalidation.js +34 -56
  311. package/src/static-site-generator/static-export-context.js +1 -0
  312. package/src/static-site-generator/static-site-generator.js +517 -553
  313. package/src/types/internal-types.d.ts +19 -3
  314. package/src/types/internal-types.js +1 -0
  315. package/src/types/public-types.d.ts +16 -46
  316. package/src/types/public-types.js +1 -0
  317. package/src/utils/deep-merge.js +39 -26
  318. package/src/utils/ecopages-route-info.js +27 -23
  319. package/src/utils/hash.js +3 -6
  320. package/src/utils/html-escaping.js +21 -8
  321. package/src/utils/html.js +1 -4
  322. package/src/utils/invariant.js +11 -10
  323. package/src/utils/locals-utils.js +32 -22
  324. package/src/utils/parse-cli-args.js +73 -65
  325. package/src/utils/path-pattern.js +38 -23
  326. package/src/utils/path-utils.module.js +8 -11
  327. package/src/utils/resolve-entry-file.js +70 -38
  328. package/src/utils/resolve-work-dir.js +23 -27
  329. package/src/utils/runtime.js +24 -33
  330. package/src/utils/server-utils.module.js +60 -52
  331. package/src/utils/static-file-extensions.js +52 -46
  332. package/src/watchers/project-watcher-ignore.js +16 -15
  333. package/src/watchers/project-watcher.js +385 -366
  334. package/src/watchers/project-watcher.test-helpers.js +45 -50
  335. package/src/adapters/shared/hmr/hmr-entrypoint-registrar.d.ts +0 -56
  336. package/src/adapters/shared/hmr/hmr-entrypoint-registrar.js +0 -106
  337. package/src/build/cache/dev-browser-script-cache.d.ts +0 -34
  338. package/src/build/cache/dev-browser-script-cache.js +0 -90
@@ -1,591 +1,555 @@
1
- import path from "node:path";
2
- import { availableParallelism } from "node:os";
3
- import { appLogger } from "../global/app-logger.js";
4
- import { fileSystem } from "@ecopages/file-system";
5
- import { prepareExplicitStaticRender } from "../adapters/shared/http/explicit-static-render-preparation.js";
6
- import { createRouteModuleStaticRenderCacheContext } from "./static-build-invalidation.js";
7
- import {
8
- getServerModuleBuildCacheOutdir,
9
- getSharedRouteModuleBuildCache
10
- } from "../services/module-loading/route-module-build-cache-registry.js";
11
- import {
12
- ensurePagesUnifiedGraphBuilt,
13
- shouldBuildPagesUnifiedGraph
14
- } from "../build/cache/pages-unified-graph-build.js";
15
- import {
16
- clearProductionPageBrowserGraphSession,
17
- prebuildProductionPageBrowserGraphs,
18
- shouldPrebuildProductionPageBrowserGraphs
19
- } from "./production-page-browser-graph-prebuild.js";
20
- import { PageModuleLoaderService } from "../route-renderer/page-loading/page-module-loader.js";
21
- import { renderSitemap } from "./sitemap.js";
22
- import { resolveSitemapLocations } from "./sitemap-routes.js";
23
- import { toEcopagesRouteInfo } from "../utils/ecopages-route-info.js";
24
- import { normalizePathname } from "../utils/path-pattern.js";
25
- const STATIC_SITE_GENERATOR_ERRORS = {
26
- ROUTE_RENDERER_FACTORY_REQUIRED: "RouteRendererFactory is required for render strategy",
27
- unsupportedBodyType: (bodyType) => `Unsupported body type for static generation: ${bodyType}`,
28
- missingIntegration: (routePath) => `View at ${routePath} is missing __eco.integration. Ensure it's defined with eco.page().`,
29
- noRendererForIntegration: (integrationName) => `No renderer found for integration: ${integrationName}`,
30
- dynamicRouteRequiresStaticPaths: (routePath) => `Dynamic route ${routePath} requires staticPaths to be defined on the view.`
1
+ import path from 'node:path';
2
+ import { availableParallelism } from 'node:os';
3
+ import { appLogger } from '../global/app-logger.js';
4
+ import { fileSystem } from '@ecopages/file-system';
5
+ import { prepareExplicitStaticRender } from '../adapters/shared/http/explicit-static-render-preparation.js';
6
+ import { createRouteModuleStaticRenderCacheContext } from './static-build-invalidation.js';
7
+ import { getServerModuleBuildCacheOutdir, getSharedRouteModuleBuildCache, } from '../services/module-loading/route-module-build-cache-registry.js';
8
+ import { ensurePagesUnifiedGraphBuilt, shouldBuildPagesUnifiedGraph, } from '../build/cache/pages-unified-graph-build.js';
9
+ import { clearProductionPageBrowserGraphSession, prebuildProductionPageBrowserGraphs, shouldPrebuildProductionPageBrowserGraphs, } from './production-page-browser-graph-prebuild.js';
10
+ import { PageModuleLoaderService } from '../route-renderer/page-loading/page-module-loader.js';
11
+ import { renderSitemap } from './sitemap.js';
12
+ import { resolveSitemapLocations } from './sitemap-routes.js';
13
+ import { toEcopagesRouteInfo } from '../utils/ecopages-route-info.js';
14
+ import { normalizePathname } from '../utils/path-pattern.js';
15
+ export const STATIC_SITE_GENERATOR_ERRORS = {
16
+ ROUTE_RENDERER_FACTORY_REQUIRED: 'RouteRendererFactory is required for render strategy',
17
+ unsupportedBodyType: (bodyType) => `Unsupported body type for static generation: ${bodyType}`,
18
+ missingIntegration: (routePath) => `View at ${routePath} is missing __eco.integration. Ensure it's defined with eco.page().`,
19
+ noRendererForIntegration: (integrationName) => `No renderer found for integration: ${integrationName}`,
20
+ dynamicRouteRequiresStaticPaths: (routePath) => `Dynamic route ${routePath} requires staticPaths to be defined on the view.`,
31
21
  };
32
22
  function resolveStaticPageConcurrency() {
33
- return Math.max(1, availableParallelism() - 1);
23
+ return Math.max(1, availableParallelism() - 1);
34
24
  }
35
25
  async function runWithConcurrency(items, concurrency, worker) {
36
- if (items.length === 0) {
37
- return;
38
- }
39
- const limit = Math.max(1, concurrency);
40
- let nextIndex = 0;
41
- const runners = Array.from({ length: Math.min(limit, items.length) }, async () => {
42
- while (nextIndex < items.length) {
43
- const currentIndex = nextIndex++;
44
- await worker(items[currentIndex]);
26
+ if (items.length === 0) {
27
+ return;
45
28
  }
46
- });
47
- await Promise.all(runners);
29
+ const limit = Math.max(1, concurrency);
30
+ let nextIndex = 0;
31
+ const runners = Array.from({ length: Math.min(limit, items.length) }, async () => {
32
+ while (nextIndex < items.length) {
33
+ const currentIndex = nextIndex++;
34
+ await worker(items[currentIndex]);
35
+ }
36
+ });
37
+ await Promise.all(runners);
48
38
  }
49
- class StaticSiteGenerator {
50
- appConfig;
51
- routeModuleBuildCacheOverride;
52
- staticRenderCacheContext;
53
- forceFullStaticGeneration = false;
54
- pageModuleLoader;
55
- /**
56
- * Creates the static-site generator for one app config.
57
- */
58
- constructor({
59
- appConfig,
60
- routeModuleBuildCache
61
- }) {
62
- this.appConfig = appConfig;
63
- this.routeModuleBuildCacheOverride = routeModuleBuildCache;
64
- this.staticRenderCacheContext = createRouteModuleStaticRenderCacheContext(appConfig);
65
- }
66
- getRouteModuleBuildCache() {
67
- return this.routeModuleBuildCacheOverride ?? getSharedRouteModuleBuildCache(getServerModuleBuildCacheOutdir(this.appConfig), this.appConfig);
68
- }
69
- getExportDir() {
70
- return this.appConfig.absolutePaths?.distDir ?? path.join(this.appConfig.rootDir, this.appConfig.distDir);
71
- }
72
- async shouldSkipStaticPageFile(filePath, routeRendererFactory) {
73
- const pageModule = await routeRendererFactory.getPageRenderer(filePath).loadPageModule(filePath);
74
- const Page = pageModule.default;
75
- return Page.cache === "dynamic";
76
- }
77
- shouldSkipStaticView(_routePath, view) {
78
- return view.cache === "dynamic";
79
- }
80
- /**
81
- * Writes the robots.txt file declared by the app config.
82
- */
83
- generateRobotsTxt() {
84
- let data = "";
85
- const preferences = this.appConfig.robotsTxt.preferences;
86
- for (const userAgent in preferences) {
87
- data += `user-agent: ${userAgent}
88
- `;
89
- for (const path2 of preferences[userAgent]) {
90
- data += `disallow: ${path2}
91
- `;
92
- }
93
- data += "\n";
39
+ /**
40
+ * Generates static output files from the finalized app config and route graph.
41
+ *
42
+ * @remarks
43
+ * This class intentionally reuses the same routing, renderer, and server-module
44
+ * loading seams used by runtime rendering. Static generation should be a build
45
+ * loop over the normal app model, not a parallel rendering stack with different
46
+ * semantics.
47
+ */
48
+ export class StaticSiteGenerator {
49
+ appConfig;
50
+ routeModuleBuildCacheOverride;
51
+ staticRenderCacheContext;
52
+ forceFullStaticGeneration = false;
53
+ pageModuleLoader;
54
+ /**
55
+ * Creates the static-site generator for one app config.
56
+ */
57
+ constructor({ appConfig, routeModuleBuildCache, }) {
58
+ this.appConfig = appConfig;
59
+ this.routeModuleBuildCacheOverride = routeModuleBuildCache;
60
+ this.staticRenderCacheContext = createRouteModuleStaticRenderCacheContext(appConfig);
94
61
  }
95
- fileSystem.ensureDir(this.getExportDir());
96
- fileSystem.write(path.join(this.getExportDir(), "robots.txt"), data);
97
- }
98
- /**
99
- * Writes the configured sitemap file to the export directory.
100
- */
101
- generateSitemap(locations, sitemap) {
102
- const fileName = sitemap.fileName ?? "sitemap.xml";
103
- fileSystem.ensureDir(this.getExportDir());
104
- fileSystem.write(path.join(this.getExportDir(), fileName), renderSitemap(locations));
105
- }
106
- /**
107
- * Returns whether the input path points at the root directory.
108
- */
109
- isRootDir(path2) {
110
- const slashes = path2.match(/\//g);
111
- return slashes && slashes.length === 1;
112
- }
113
- /**
114
- * Collects parent directories that must exist for the generated route set.
115
- */
116
- getDirectories(routes) {
117
- const directories = /* @__PURE__ */ new Set();
118
- for (const route of routes) {
119
- const path2 = route.startsWith("http") ? new URL(route).pathname : route;
120
- const segments = path2.split("/");
121
- if (segments.length > 2) {
122
- directories.add(segments.slice(0, segments.length - 1).join("/"));
123
- }
62
+ getRouteModuleBuildCache() {
63
+ return (this.routeModuleBuildCacheOverride ??
64
+ getSharedRouteModuleBuildCache(getServerModuleBuildCacheOutdir(this.appConfig), this.appConfig));
124
65
  }
125
- return Array.from(directories);
126
- }
127
- writeStaticOutput(routePath, contents, directories = []) {
128
- const outputPath = this.getOutputPath(routePath, directories);
129
- fileSystem.ensureDir(path.dirname(outputPath));
130
- fileSystem.write(outputPath, contents);
131
- return outputPath;
132
- }
133
- async writeStaticPageArtifact(options) {
134
- const directories = options.directories ?? this.getDirectories([options.pathname]);
135
- const renderedOutputPath = this.getOutputPath(options.pathname, directories);
136
- const routeModuleBuildCache = this.getRouteModuleBuildCache();
137
- if (options.reuseRenderedOutput !== false && options.sourceFile && routeModuleBuildCache.canReuseStaticRender({
138
- sourceFile: options.sourceFile,
139
- pathname: options.pathname,
140
- renderedOutputPath,
141
- context: this.staticRenderCacheContext,
142
- force: this.forceFullStaticGeneration
143
- })) {
144
- options.activeStaticPathnames?.add(options.pathname);
145
- await options.onSuccessfulExport?.();
146
- appLogger.debug(`Skipped unchanged static page: ${options.debugLabel ?? options.pathname}`);
147
- return;
66
+ getExportDir() {
67
+ return this.appConfig.absolutePaths?.distDir ?? path.join(this.appConfig.rootDir, this.appConfig.distDir);
148
68
  }
149
- const contents = await options.createContents();
150
- if (contents === null) {
151
- return;
69
+ async shouldSkipStaticPageFile(filePath, routeRendererFactory) {
70
+ const pageModule = await routeRendererFactory.getPageRenderer(filePath).loadPageModule(filePath);
71
+ const Page = pageModule.default;
72
+ return Page.cache === 'dynamic';
152
73
  }
153
- const outputPath = this.writeStaticOutput(options.pathname, contents, directories);
154
- options.activeStaticPathnames?.add(options.pathname);
155
- if (options.sourceFile) {
156
- try {
157
- routeModuleBuildCache.recordStaticRender({
158
- filePath: options.sourceFile,
159
- pathname: options.pathname,
160
- sourceHash: fileSystem.hash(options.sourceFile),
161
- renderedOutputPath: outputPath,
162
- context: this.staticRenderCacheContext
163
- });
164
- } catch (error) {
165
- appLogger.debug(
166
- `Failed to record static render cache for ${options.pathname}`,
167
- error instanceof Error ? error.message : String(error)
168
- );
169
- }
74
+ shouldSkipStaticView(_routePath, view) {
75
+ return view.cache === 'dynamic';
170
76
  }
171
- await options.onSuccessfulExport?.();
172
- }
173
- /**
174
- * Marks a successfully exported pathname as sitemap-eligible when robots allow indexing.
175
- *
176
- * @remarks
177
- * Fail-closed: metadata resolution errors omit the URL. `robots.index === false`
178
- * also omits it. Eligibility is decided during generation, not by reloading modules
179
- * after `afterStaticExport`.
180
- */
181
- async considerSitemapEligibility(input) {
182
- if (!input.sitemapEligiblePathnames || !this.appConfig.sitemap?.enabled) {
183
- return;
77
+ /**
78
+ * Writes the robots.txt file declared by the app config.
79
+ */
80
+ generateRobotsTxt() {
81
+ let data = '';
82
+ const preferences = this.appConfig.robotsTxt.preferences;
83
+ for (const userAgent in preferences) {
84
+ data += `user-agent: ${userAgent}\n`;
85
+ for (const path of preferences[userAgent]) {
86
+ data += `disallow: ${path}\n`;
87
+ }
88
+ data += '\n';
89
+ }
90
+ fileSystem.ensureDir(this.getExportDir());
91
+ fileSystem.write(path.join(this.getExportDir(), 'robots.txt'), data);
184
92
  }
185
- try {
186
- const metadata = await input.resolveMetadata();
187
- if (metadata.robots?.index === false) {
188
- return;
189
- }
190
- input.sitemapEligiblePathnames.add(normalizePathname(input.pathname));
191
- } catch (error) {
192
- appLogger.debug(
193
- `Sitemap eligibility skipped for ${input.pathname}; metadata resolution failed`,
194
- error instanceof Error ? error.message : String(error)
195
- );
93
+ /**
94
+ * Writes the configured sitemap file to the export directory.
95
+ */
96
+ generateSitemap(locations, sitemap) {
97
+ const fileName = sitemap.fileName ?? 'sitemap.xml';
98
+ fileSystem.ensureDir(this.getExportDir());
99
+ fileSystem.write(path.join(this.getExportDir(), fileName), renderSitemap(locations));
196
100
  }
197
- }
198
- getPageModuleLoader(baseUrl) {
199
- this.pageModuleLoader ??= new PageModuleLoaderService(this.appConfig, baseUrl);
200
- return this.pageModuleLoader;
201
- }
202
- async resolveFilesystemPageMetadata(input) {
203
- const loader = this.getPageModuleLoader(input.baseUrl);
204
- const pageModule = await loader.resolvePageModule({
205
- file: input.filePath,
206
- importPageFileFn: (file) => input.routeRendererFactory.getPageRenderer(file).loadPageModule(file)
207
- });
208
- const { metadata } = await loader.resolvePageData({
209
- pageModule,
210
- routeOptions: {
211
- file: input.filePath,
212
- params: input.params
213
- }
214
- });
215
- return metadata;
216
- }
217
- async resolveExplicitViewMetadata(input) {
218
- if (!input.view.metadata) {
219
- return this.appConfig.defaultMetadata;
101
+ /**
102
+ * Returns whether the input path points at the root directory.
103
+ */
104
+ isRootDir(path) {
105
+ const slashes = path.match(/\//g);
106
+ return slashes && slashes.length === 1;
220
107
  }
221
- const dynamicMetadata = await input.view.metadata({
222
- params: input.params,
223
- query: {},
224
- props: input.props,
225
- appConfig: this.appConfig
226
- });
227
- return { ...this.appConfig.defaultMetadata, ...dynamicMetadata };
228
- }
229
- pruneStaleStaticOutputs(activeStaticPathnames) {
230
- const removedOutputPaths = this.getRouteModuleBuildCache().pruneStaleRenderedOutputs(activeStaticPathnames);
231
- for (const outputPath of removedOutputPaths) {
232
- const resolvedOutputPath = path.isAbsolute(outputPath) ? outputPath : path.resolve(outputPath);
233
- if (fileSystem.exists(resolvedOutputPath)) {
234
- fileSystem.remove(resolvedOutputPath);
235
- appLogger.debug(`Removed stale static output: ${resolvedOutputPath}`);
236
- }
108
+ /**
109
+ * Collects parent directories that must exist for the generated route set.
110
+ */
111
+ getDirectories(routes) {
112
+ const directories = new Set();
113
+ for (const route of routes) {
114
+ const path = route.startsWith('http') ? new URL(route).pathname : route;
115
+ const segments = path.split('/');
116
+ if (segments.length > 2) {
117
+ directories.add(segments.slice(0, segments.length - 1).join('/'));
118
+ }
119
+ }
120
+ return Array.from(directories);
237
121
  }
238
- }
239
- async createFilesystemStaticContents(input) {
240
- const {
241
- route: {
242
- templateRoute: { filePath },
243
- params
244
- },
245
- routeRendererFactory,
246
- skipped
247
- } = input;
248
- if (!routeRendererFactory) {
249
- throw new Error(STATIC_SITE_GENERATOR_ERRORS.ROUTE_RENDERER_FACTORY_REQUIRED);
122
+ writeStaticOutput(routePath, contents, directories = []) {
123
+ const outputPath = this.getOutputPath(routePath, directories);
124
+ fileSystem.ensureDir(path.dirname(outputPath));
125
+ fileSystem.write(outputPath, contents);
126
+ return outputPath;
250
127
  }
251
- if (await this.shouldSkipStaticPageFile(filePath, routeRendererFactory)) {
252
- skipped?.push(filePath);
253
- return null;
128
+ async writeStaticPageArtifact(options) {
129
+ const directories = options.directories ?? this.getDirectories([options.pathname]);
130
+ const renderedOutputPath = this.getOutputPath(options.pathname, directories);
131
+ const routeModuleBuildCache = this.getRouteModuleBuildCache();
132
+ if (options.reuseRenderedOutput !== false &&
133
+ options.sourceFile &&
134
+ routeModuleBuildCache.canReuseStaticRender({
135
+ sourceFile: options.sourceFile,
136
+ pathname: options.pathname,
137
+ renderedOutputPath,
138
+ context: this.staticRenderCacheContext,
139
+ force: this.forceFullStaticGeneration,
140
+ })) {
141
+ options.activeStaticPathnames?.add(options.pathname);
142
+ await options.onSuccessfulExport?.();
143
+ appLogger.debug(`Skipped unchanged static page: ${options.debugLabel ?? options.pathname}`);
144
+ return;
145
+ }
146
+ const contents = await options.createContents();
147
+ if (contents === null) {
148
+ return;
149
+ }
150
+ const outputPath = this.writeStaticOutput(options.pathname, contents, directories);
151
+ options.activeStaticPathnames?.add(options.pathname);
152
+ if (options.sourceFile) {
153
+ try {
154
+ routeModuleBuildCache.recordStaticRender({
155
+ filePath: options.sourceFile,
156
+ pathname: options.pathname,
157
+ sourceHash: fileSystem.hash(options.sourceFile),
158
+ renderedOutputPath: outputPath,
159
+ context: this.staticRenderCacheContext,
160
+ });
161
+ }
162
+ catch (error) {
163
+ appLogger.debug(`Failed to record static render cache for ${options.pathname}`, error instanceof Error ? error.message : String(error));
164
+ }
165
+ }
166
+ await options.onSuccessfulExport?.();
254
167
  }
255
- const renderer = routeRendererFactory.getPageRenderer(filePath);
256
- const result = await renderer.execute({
257
- file: filePath,
258
- params
259
- });
260
- const body = result.body;
261
- if (typeof body === "string" || Buffer.isBuffer(body)) {
262
- return body;
168
+ /**
169
+ * Marks a successfully exported pathname as sitemap-eligible when robots allow indexing.
170
+ *
171
+ * @remarks
172
+ * Fail-closed: metadata resolution errors omit the URL. `robots.index === false`
173
+ * also omits it. Eligibility is decided during generation, not by reloading modules
174
+ * after `afterStaticExport`.
175
+ */
176
+ async considerSitemapEligibility(input) {
177
+ if (!input.sitemapEligiblePathnames || !this.appConfig.sitemap?.enabled) {
178
+ return;
179
+ }
180
+ try {
181
+ const metadata = await input.resolveMetadata();
182
+ if (metadata.robots?.index === false) {
183
+ return;
184
+ }
185
+ input.sitemapEligiblePathnames.add(normalizePathname(input.pathname));
186
+ }
187
+ catch (error) {
188
+ appLogger.debug(`Sitemap eligibility skipped for ${input.pathname}; metadata resolution failed`, error instanceof Error ? error.message : String(error));
189
+ }
263
190
  }
264
- if (body instanceof ReadableStream) {
265
- return new Response(body).text();
191
+ getPageModuleLoader(baseUrl) {
192
+ this.pageModuleLoader ??= new PageModuleLoaderService(this.appConfig, baseUrl);
193
+ return this.pageModuleLoader;
266
194
  }
267
- throw new Error(STATIC_SITE_GENERATOR_ERRORS.unsupportedBodyType(typeof body));
268
- }
269
- /**
270
- * Generates static output for all filesystem-discovered routes.
271
- *
272
- * @remarks
273
- * Routes are rendered through the normal route renderer directly.
274
- */
275
- async generateStaticPages(input) {
276
- const { router, baseUrl, routeRendererFactory, skipped, activeStaticPathnames, sitemapEligiblePathnames } = input;
277
- const routes = input.preloadedRoutes ?? await router.listStaticGenerationRoutes({ runtimeOrigin: baseUrl });
278
- appLogger.debug(
279
- "Static Pages",
280
- routes.map((route) => route.requestUrl)
281
- );
282
- const directories = this.getDirectories(routes.map((route) => route.requestUrl));
283
- await runWithConcurrency(routes, resolveStaticPageConcurrency(), async (route) => {
284
- try {
285
- await this.writeStaticPageArtifact({
286
- pathname: route.pathname,
287
- sourceFile: route.templateRoute.filePath,
288
- directories,
289
- debugLabel: route.requestUrl,
290
- activeStaticPathnames,
291
- onSuccessfulExport: () => this.considerSitemapEligibility({
292
- pathname: route.pathname,
293
- sitemapEligiblePathnames,
294
- resolveMetadata: () => {
295
- if (!routeRendererFactory) {
296
- throw new Error(STATIC_SITE_GENERATOR_ERRORS.ROUTE_RENDERER_FACTORY_REQUIRED);
297
- }
298
- return this.resolveFilesystemPageMetadata({
299
- filePath: route.templateRoute.filePath,
300
- params: route.params,
301
- baseUrl,
302
- routeRendererFactory
303
- });
304
- }
305
- }),
306
- createContents: () => this.createFilesystemStaticContents({
307
- route,
308
- baseUrl,
309
- routeRendererFactory,
310
- skipped
311
- })
195
+ async resolveFilesystemPageMetadata(input) {
196
+ const loader = this.getPageModuleLoader(input.baseUrl);
197
+ const pageModule = await loader.resolvePageModule({
198
+ file: input.filePath,
199
+ importPageFileFn: (file) => input.routeRendererFactory.getPageRenderer(file).loadPageModule(file),
312
200
  });
313
- } catch (error) {
314
- appLogger.error(
315
- `Error generating static page for ${route.requestUrl}:`,
316
- error instanceof Error ? error : String(error)
317
- );
318
- }
319
- });
320
- }
321
- createStaticExportContext(input) {
322
- return {
323
- appConfig: this.appConfig,
324
- router: input.router,
325
- baseUrl: input.baseUrl,
326
- routeRendererFactory: input.routeRendererFactory,
327
- staticRoutes: input.staticRoutes,
328
- force: input.force,
329
- preserveExportDirectory: input.preserveExportDirectory,
330
- routes: input.routes
331
- };
332
- }
333
- async invokeStaticExportHook(hook, context) {
334
- for (const integration of this.appConfig.integrations) {
335
- const handler = integration[hook];
336
- if (typeof handler === "function") {
337
- await handler.call(integration, context);
338
- }
201
+ const { metadata } = await loader.resolvePageData({
202
+ pageModule,
203
+ routeOptions: {
204
+ file: input.filePath,
205
+ params: input.params,
206
+ },
207
+ });
208
+ return metadata;
339
209
  }
340
- }
341
- /**
342
- * Executes the full static-generation workflow for one app run.
343
- */
344
- async run({
345
- router,
346
- baseUrl,
347
- routeRendererFactory,
348
- staticRoutes,
349
- force = false,
350
- preserveExportDirectory = false
351
- }) {
352
- const skippedDynamicPages = [];
353
- const activeStaticPathnames = /* @__PURE__ */ new Set();
354
- const sitemapEligiblePathnames = this.appConfig.sitemap?.enabled ? /* @__PURE__ */ new Set() : void 0;
355
- this.forceFullStaticGeneration = force;
356
- this.staticRenderCacheContext = createRouteModuleStaticRenderCacheContext(this.appConfig);
357
- this.pageModuleLoader = void 0;
358
- if (!force) {
359
- this.getRouteModuleBuildCache().ensureIncrementalStaticGenerationContext(this.staticRenderCacheContext);
210
+ async resolveExplicitViewMetadata(input) {
211
+ if (!input.view.metadata) {
212
+ return this.appConfig.defaultMetadata;
213
+ }
214
+ const dynamicMetadata = await input.view.metadata({
215
+ params: input.params,
216
+ query: {},
217
+ props: input.props,
218
+ appConfig: this.appConfig,
219
+ });
220
+ return { ...this.appConfig.defaultMetadata, ...dynamicMetadata };
360
221
  }
361
- if (shouldPrebuildProductionPageBrowserGraphs() && force) {
362
- clearProductionPageBrowserGraphSession(this.appConfig);
222
+ pruneStaleStaticOutputs(activeStaticPathnames) {
223
+ const removedOutputPaths = this.getRouteModuleBuildCache().pruneStaleRenderedOutputs(activeStaticPathnames);
224
+ for (const outputPath of removedOutputPaths) {
225
+ const resolvedOutputPath = path.isAbsolute(outputPath) ? outputPath : path.resolve(outputPath);
226
+ if (fileSystem.exists(resolvedOutputPath)) {
227
+ fileSystem.remove(resolvedOutputPath);
228
+ appLogger.debug(`Removed stale static output: ${resolvedOutputPath}`);
229
+ }
230
+ }
363
231
  }
364
- const routes = await router.listStaticGenerationRoutes({ runtimeOrigin: baseUrl });
365
- const exportRoutes = routes.map((route) => toEcopagesRouteInfo(route));
366
- if (shouldBuildPagesUnifiedGraph()) {
367
- await ensurePagesUnifiedGraphBuilt({
368
- appConfig: this.appConfig,
369
- entryPaths: routes.map((route) => route.templateRoute.filePath),
370
- outdir: getServerModuleBuildCacheOutdir(this.appConfig),
371
- force
372
- });
232
+ async createFilesystemStaticContents(input) {
233
+ const { route: { templateRoute: { filePath }, params, }, routeRendererFactory, skipped, } = input;
234
+ if (!routeRendererFactory) {
235
+ throw new Error(STATIC_SITE_GENERATOR_ERRORS.ROUTE_RENDERER_FACTORY_REQUIRED);
236
+ }
237
+ if (await this.shouldSkipStaticPageFile(filePath, routeRendererFactory)) {
238
+ skipped?.push(filePath);
239
+ return null;
240
+ }
241
+ const renderer = routeRendererFactory.getPageRenderer(filePath);
242
+ const result = await renderer.execute({
243
+ file: filePath,
244
+ params: params,
245
+ });
246
+ const body = result.body;
247
+ if (typeof body === 'string' || Buffer.isBuffer(body)) {
248
+ return body;
249
+ }
250
+ if (body instanceof ReadableStream) {
251
+ return new Response(body).text();
252
+ }
253
+ throw new Error(STATIC_SITE_GENERATOR_ERRORS.unsupportedBodyType(typeof body));
373
254
  }
374
- const staticExportContext = this.createStaticExportContext({
375
- router,
376
- baseUrl,
377
- routeRendererFactory,
378
- staticRoutes,
379
- force,
380
- preserveExportDirectory,
381
- routes: exportRoutes
382
- });
383
- await this.invokeStaticExportHook("beforeStaticExport", staticExportContext);
384
- try {
385
- if (shouldPrebuildProductionPageBrowserGraphs() && routeRendererFactory) {
386
- await prebuildProductionPageBrowserGraphs(
387
- routes.map((route) => route.templateRoute.filePath),
388
- routeRendererFactory
389
- );
390
- }
391
- this.generateRobotsTxt();
392
- await this.generateStaticPages({
393
- router,
394
- baseUrl,
395
- routeRendererFactory,
396
- skipped: skippedDynamicPages,
397
- activeStaticPathnames,
398
- preloadedRoutes: routes,
399
- sitemapEligiblePathnames
400
- });
401
- if (staticRoutes && staticRoutes.length > 0 && routeRendererFactory) {
402
- await this.generateExplicitStaticPages({
403
- staticRoutes,
404
- routeRendererFactory,
405
- skipped: skippedDynamicPages,
406
- activeStaticPathnames,
407
- sitemapEligiblePathnames
255
+ /**
256
+ * Generates static output for all filesystem-discovered routes.
257
+ *
258
+ * @remarks
259
+ * Routes are rendered through the normal route renderer directly.
260
+ */
261
+ async generateStaticPages(input) {
262
+ const { router, baseUrl, routeRendererFactory, skipped, activeStaticPathnames, sitemapEligiblePathnames } = input;
263
+ const routes = input.preloadedRoutes ?? (await router.listStaticGenerationRoutes({ runtimeOrigin: baseUrl }));
264
+ appLogger.debug('Static Pages', routes.map((route) => route.requestUrl));
265
+ const directories = this.getDirectories(routes.map((route) => route.requestUrl));
266
+ await runWithConcurrency(routes, resolveStaticPageConcurrency(), async (route) => {
267
+ try {
268
+ await this.writeStaticPageArtifact({
269
+ pathname: route.pathname,
270
+ sourceFile: route.templateRoute.filePath,
271
+ directories,
272
+ debugLabel: route.requestUrl,
273
+ activeStaticPathnames,
274
+ onSuccessfulExport: () => this.considerSitemapEligibility({
275
+ pathname: route.pathname,
276
+ sitemapEligiblePathnames,
277
+ resolveMetadata: () => {
278
+ if (!routeRendererFactory) {
279
+ throw new Error(STATIC_SITE_GENERATOR_ERRORS.ROUTE_RENDERER_FACTORY_REQUIRED);
280
+ }
281
+ return this.resolveFilesystemPageMetadata({
282
+ filePath: route.templateRoute.filePath,
283
+ params: route.params,
284
+ baseUrl,
285
+ routeRendererFactory,
286
+ });
287
+ },
288
+ }),
289
+ createContents: () => this.createFilesystemStaticContents({
290
+ route,
291
+ baseUrl,
292
+ routeRendererFactory,
293
+ skipped,
294
+ }),
295
+ });
296
+ }
297
+ catch (error) {
298
+ appLogger.error(`Error generating static page for ${route.requestUrl}:`, error instanceof Error ? error : String(error));
299
+ }
408
300
  });
409
- }
410
- if (preserveExportDirectory) {
411
- this.pruneStaleStaticOutputs(activeStaticPathnames);
412
- }
413
- } catch (error) {
414
- if (shouldPrebuildProductionPageBrowserGraphs()) {
415
- clearProductionPageBrowserGraphSession(this.appConfig);
416
- }
417
- throw error;
418
- } finally {
419
- await this.invokeStaticExportHook("afterStaticExport", staticExportContext);
420
301
  }
421
- if (this.appConfig.sitemap?.enabled && sitemapEligiblePathnames) {
422
- const locations = resolveSitemapLocations({
423
- eligiblePathnames: [...sitemapEligiblePathnames],
424
- sitemap: this.appConfig.sitemap,
425
- baseUrl
426
- });
427
- this.generateSitemap(locations, this.appConfig.sitemap);
302
+ createStaticExportContext(input) {
303
+ return {
304
+ appConfig: this.appConfig,
305
+ router: input.router,
306
+ baseUrl: input.baseUrl,
307
+ routeRendererFactory: input.routeRendererFactory,
308
+ staticRoutes: input.staticRoutes,
309
+ force: input.force,
310
+ preserveExportDirectory: input.preserveExportDirectory,
311
+ routes: input.routes,
312
+ };
428
313
  }
429
- if (skippedDynamicPages.length > 0) {
430
- appLogger.debug(
431
- `Skipped ${skippedDynamicPages.length} page(s) with cache: 'dynamic' (not supported in static generation)`,
432
- skippedDynamicPages
433
- );
314
+ async invokeStaticExportHook(hook, context) {
315
+ for (const integration of this.appConfig.integrations) {
316
+ const handler = integration[hook];
317
+ if (typeof handler === 'function') {
318
+ await handler.call(integration, context);
319
+ }
320
+ }
434
321
  }
435
- }
436
- async generateExplicitStaticPages(input) {
437
- appLogger.debug(
438
- "Generating explicit static routes",
439
- input.staticRoutes.map((r) => r.path)
440
- );
441
- for (const route of input.staticRoutes) {
442
- try {
443
- const mod = await route.loader();
444
- const view = mod.default;
445
- if (this.shouldSkipStaticView(route.path, view)) {
446
- input.skipped?.push(route.path);
447
- continue;
448
- }
449
- await this.generateExplicitStaticRoute({
450
- routePath: route.path,
451
- view,
452
- routeRendererFactory: input.routeRendererFactory,
453
- activeStaticPathnames: input.activeStaticPathnames,
454
- sitemapEligiblePathnames: input.sitemapEligiblePathnames
322
+ /**
323
+ * Executes the full static-generation workflow for one app run.
324
+ */
325
+ async run({ router, baseUrl, routeRendererFactory, staticRoutes, force = false, preserveExportDirectory = false, }) {
326
+ const skippedDynamicPages = [];
327
+ const activeStaticPathnames = new Set();
328
+ const sitemapEligiblePathnames = this.appConfig.sitemap?.enabled ? new Set() : undefined;
329
+ this.forceFullStaticGeneration = force;
330
+ this.staticRenderCacheContext = createRouteModuleStaticRenderCacheContext(this.appConfig);
331
+ this.pageModuleLoader = undefined;
332
+ if (!force) {
333
+ this.getRouteModuleBuildCache().ensureIncrementalStaticGenerationContext(this.staticRenderCacheContext);
334
+ }
335
+ if (shouldPrebuildProductionPageBrowserGraphs() && force) {
336
+ clearProductionPageBrowserGraphSession(this.appConfig);
337
+ }
338
+ const routes = await router.listStaticGenerationRoutes({ runtimeOrigin: baseUrl });
339
+ const exportRoutes = routes.map((route) => toEcopagesRouteInfo(route));
340
+ if (shouldBuildPagesUnifiedGraph()) {
341
+ await ensurePagesUnifiedGraphBuilt({
342
+ appConfig: this.appConfig,
343
+ entryPaths: routes.map((route) => route.templateRoute.filePath),
344
+ outdir: getServerModuleBuildCacheOutdir(this.appConfig),
345
+ force,
346
+ });
347
+ }
348
+ const staticExportContext = this.createStaticExportContext({
349
+ router,
350
+ baseUrl,
351
+ routeRendererFactory,
352
+ staticRoutes,
353
+ force,
354
+ preserveExportDirectory,
355
+ routes: exportRoutes,
455
356
  });
456
- } catch (error) {
457
- appLogger.error(
458
- `Error generating explicit static page for ${route.path}:`,
459
- error instanceof Error ? error : String(error)
460
- );
461
- }
357
+ await this.invokeStaticExportHook('beforeStaticExport', staticExportContext);
358
+ try {
359
+ if (shouldPrebuildProductionPageBrowserGraphs() && routeRendererFactory) {
360
+ await prebuildProductionPageBrowserGraphs(routes.map((route) => route.templateRoute.filePath), routeRendererFactory);
361
+ }
362
+ this.generateRobotsTxt();
363
+ await this.generateStaticPages({
364
+ router,
365
+ baseUrl,
366
+ routeRendererFactory,
367
+ skipped: skippedDynamicPages,
368
+ activeStaticPathnames,
369
+ preloadedRoutes: routes,
370
+ sitemapEligiblePathnames,
371
+ });
372
+ if (staticRoutes && staticRoutes.length > 0 && routeRendererFactory) {
373
+ await this.generateExplicitStaticPages({
374
+ staticRoutes,
375
+ routeRendererFactory,
376
+ skipped: skippedDynamicPages,
377
+ activeStaticPathnames,
378
+ sitemapEligiblePathnames,
379
+ });
380
+ }
381
+ if (preserveExportDirectory) {
382
+ this.pruneStaleStaticOutputs(activeStaticPathnames);
383
+ }
384
+ }
385
+ catch (error) {
386
+ if (shouldPrebuildProductionPageBrowserGraphs()) {
387
+ clearProductionPageBrowserGraphSession(this.appConfig);
388
+ }
389
+ throw error;
390
+ }
391
+ finally {
392
+ await this.invokeStaticExportHook('afterStaticExport', staticExportContext);
393
+ }
394
+ if (this.appConfig.sitemap?.enabled && sitemapEligiblePathnames) {
395
+ const locations = resolveSitemapLocations({
396
+ eligiblePathnames: [...sitemapEligiblePathnames],
397
+ sitemap: this.appConfig.sitemap,
398
+ baseUrl,
399
+ });
400
+ this.generateSitemap(locations, this.appConfig.sitemap);
401
+ }
402
+ if (skippedDynamicPages.length > 0) {
403
+ appLogger.debug(`Skipped ${skippedDynamicPages.length} page(s) with cache: 'dynamic' (not supported in static generation)`, skippedDynamicPages);
404
+ }
405
+ }
406
+ async generateExplicitStaticPages(input) {
407
+ appLogger.debug('Generating explicit static routes', input.staticRoutes.map((r) => r.path));
408
+ for (const route of input.staticRoutes) {
409
+ try {
410
+ const mod = await route.loader();
411
+ const view = mod.default;
412
+ if (this.shouldSkipStaticView(route.path, view)) {
413
+ input.skipped?.push(route.path);
414
+ continue;
415
+ }
416
+ await this.generateExplicitStaticRoute({
417
+ routePath: route.path,
418
+ view,
419
+ routeRendererFactory: input.routeRendererFactory,
420
+ activeStaticPathnames: input.activeStaticPathnames,
421
+ sitemapEligiblePathnames: input.sitemapEligiblePathnames,
422
+ });
423
+ }
424
+ catch (error) {
425
+ appLogger.error(`Error generating explicit static page for ${route.path}:`, error instanceof Error ? error : String(error));
426
+ }
427
+ }
462
428
  }
463
- }
464
- resolveExplicitViewSourceFile(view) {
465
- const sourceFile = view.config?.__eco?.file;
466
- if (!sourceFile) {
467
- return void 0;
429
+ resolveExplicitViewSourceFile(view) {
430
+ const sourceFile = view.config?.__eco?.file;
431
+ if (!sourceFile) {
432
+ return undefined;
433
+ }
434
+ return path.isAbsolute(sourceFile) ? sourceFile : path.join(this.appConfig.rootDir, sourceFile);
468
435
  }
469
- return path.isAbsolute(sourceFile) ? sourceFile : path.join(this.appConfig.rootDir, sourceFile);
470
- }
471
- async generateExplicitStaticRoute(input) {
472
- const { routePath, view, routeRendererFactory, activeStaticPathnames, sitemapEligiblePathnames } = input;
473
- const { renderer, routeEntries } = await this.planExplicitStaticRoute({
474
- routePath,
475
- view,
476
- routeRendererFactory
477
- });
478
- const sourceFile = this.resolveExplicitViewSourceFile(view);
479
- for (const { pathname, params } of routeEntries) {
480
- await this.writeStaticPageArtifact({
481
- pathname,
482
- sourceFile,
483
- debugLabel: pathname,
484
- activeStaticPathnames,
485
- onSuccessfulExport: () => this.considerSitemapEligibility({
486
- pathname,
487
- sitemapEligiblePathnames,
488
- resolveMetadata: async () => {
489
- const { props } = await prepareExplicitStaticRender({
490
- routePath,
491
- view,
492
- params,
493
- appConfig: this.appConfig,
494
- runtimeOrigin: this.appConfig.baseUrl,
495
- routeRendererFactory,
496
- errors: STATIC_SITE_GENERATOR_ERRORS
436
+ async generateExplicitStaticRoute(input) {
437
+ const { routePath, view, routeRendererFactory, activeStaticPathnames, sitemapEligiblePathnames } = input;
438
+ const { renderer, routeEntries } = await this.planExplicitStaticRoute({
439
+ routePath,
440
+ view,
441
+ routeRendererFactory,
442
+ });
443
+ const sourceFile = this.resolveExplicitViewSourceFile(view);
444
+ for (const { pathname, params } of routeEntries) {
445
+ await this.writeStaticPageArtifact({
446
+ pathname,
447
+ sourceFile,
448
+ debugLabel: pathname,
449
+ activeStaticPathnames,
450
+ onSuccessfulExport: () => this.considerSitemapEligibility({
451
+ pathname,
452
+ sitemapEligiblePathnames,
453
+ resolveMetadata: async () => {
454
+ const { props } = await prepareExplicitStaticRender({
455
+ routePath,
456
+ view,
457
+ params,
458
+ appConfig: this.appConfig,
459
+ runtimeOrigin: this.appConfig.baseUrl,
460
+ routeRendererFactory,
461
+ errors: STATIC_SITE_GENERATOR_ERRORS,
462
+ });
463
+ return this.resolveExplicitViewMetadata({ view, params, props });
464
+ },
465
+ }),
466
+ createContents: () => this.createExplicitStaticContents({
467
+ routePath,
468
+ view,
469
+ params,
470
+ routeRendererFactory,
471
+ renderer,
472
+ }),
497
473
  });
498
- return this.resolveExplicitViewMetadata({ view, params, props });
499
- }
500
- }),
501
- createContents: () => this.createExplicitStaticContents({
502
- routePath,
503
- view,
504
- params,
505
- routeRendererFactory,
506
- renderer
507
- })
508
- });
509
- appLogger.debug(`Generated static page: ${pathname}`);
474
+ appLogger.debug(`Generated static page: ${pathname}`);
475
+ }
510
476
  }
511
- }
512
- async planExplicitStaticRoute(input) {
513
- const { renderer } = await prepareExplicitStaticRender({
514
- routePath: input.routePath,
515
- view: input.view,
516
- params: {},
517
- appConfig: this.appConfig,
518
- runtimeOrigin: this.appConfig.baseUrl,
519
- routeRendererFactory: input.routeRendererFactory,
520
- errors: STATIC_SITE_GENERATOR_ERRORS
521
- });
522
- return {
523
- renderer,
524
- routeEntries: await this.listExplicitStaticRouteEntries(input.routePath, input.view)
525
- };
526
- }
527
- async createExplicitStaticContents(input) {
528
- const { props, view: renderableView } = await prepareExplicitStaticRender({
529
- routePath: input.routePath,
530
- view: input.view,
531
- params: input.params,
532
- appConfig: this.appConfig,
533
- runtimeOrigin: this.appConfig.baseUrl,
534
- routeRendererFactory: input.routeRendererFactory,
535
- errors: STATIC_SITE_GENERATOR_ERRORS
536
- });
537
- const response = await input.renderer.renderToResponse(renderableView, props, {});
538
- return response.text();
539
- }
540
- async listExplicitStaticRouteEntries(routePath, view) {
541
- const isDynamic = routePath.includes(":") || routePath.includes("[");
542
- if (!isDynamic) {
543
- return [{ pathname: routePath, params: {} }];
477
+ async planExplicitStaticRoute(input) {
478
+ const { renderer } = await prepareExplicitStaticRender({
479
+ routePath: input.routePath,
480
+ view: input.view,
481
+ params: {},
482
+ appConfig: this.appConfig,
483
+ runtimeOrigin: this.appConfig.baseUrl,
484
+ routeRendererFactory: input.routeRendererFactory,
485
+ errors: STATIC_SITE_GENERATOR_ERRORS,
486
+ });
487
+ return {
488
+ renderer,
489
+ routeEntries: await this.listExplicitStaticRouteEntries(input.routePath, input.view),
490
+ };
544
491
  }
545
- if (!view.staticPaths) {
546
- throw new Error(STATIC_SITE_GENERATOR_ERRORS.dynamicRouteRequiresStaticPaths(routePath));
492
+ async createExplicitStaticContents(input) {
493
+ const { props, view: renderableView } = await prepareExplicitStaticRender({
494
+ routePath: input.routePath,
495
+ view: input.view,
496
+ params: input.params,
497
+ appConfig: this.appConfig,
498
+ runtimeOrigin: this.appConfig.baseUrl,
499
+ routeRendererFactory: input.routeRendererFactory,
500
+ errors: STATIC_SITE_GENERATOR_ERRORS,
501
+ });
502
+ const response = await input.renderer.renderToResponse(renderableView, props, {});
503
+ return response.text();
547
504
  }
548
- const { paths } = await view.staticPaths({
549
- appConfig: this.appConfig,
550
- runtimeOrigin: this.appConfig.baseUrl
551
- });
552
- return paths.map(({ params }) => ({
553
- pathname: this.resolveRoutePath(routePath, params),
554
- params
555
- }));
556
- }
557
- /**
558
- * Resolve a route path template with actual params.
559
- * Supports both :param and [param] syntax.
560
- */
561
- resolveRoutePath(routePath, params) {
562
- let resolved = routePath;
563
- for (const [key, value] of Object.entries(params)) {
564
- const paramValue = Array.isArray(value) ? value.join("/") : value;
565
- resolved = resolved.replace(`:${key}`, paramValue);
566
- resolved = resolved.replace(`[${key}]`, paramValue);
567
- resolved = resolved.replace(`[...${key}]`, paramValue);
505
+ async listExplicitStaticRouteEntries(routePath, view) {
506
+ const isDynamic = routePath.includes(':') || routePath.includes('[');
507
+ if (!isDynamic) {
508
+ return [{ pathname: routePath, params: {} }];
509
+ }
510
+ if (!view.staticPaths) {
511
+ throw new Error(STATIC_SITE_GENERATOR_ERRORS.dynamicRouteRequiresStaticPaths(routePath));
512
+ }
513
+ const { paths } = await view.staticPaths({
514
+ appConfig: this.appConfig,
515
+ runtimeOrigin: this.appConfig.baseUrl,
516
+ });
517
+ return paths.map(({ params }) => ({
518
+ pathname: this.resolveRoutePath(routePath, params),
519
+ params,
520
+ }));
521
+ }
522
+ /**
523
+ * Resolve a route path template with actual params.
524
+ * Supports both :param and [param] syntax.
525
+ */
526
+ resolveRoutePath(routePath, params) {
527
+ let resolved = routePath;
528
+ for (const [key, value] of Object.entries(params)) {
529
+ const paramValue = Array.isArray(value) ? value.join('/') : value;
530
+ resolved = resolved.replace(`:${key}`, paramValue);
531
+ resolved = resolved.replace(`[${key}]`, paramValue);
532
+ resolved = resolved.replace(`[...${key}]`, paramValue);
533
+ }
534
+ return resolved;
568
535
  }
569
- return resolved;
570
- }
571
- /**
572
- * Get the output file path for a given route.
573
- */
574
- getOutputPath(routePath, directories = []) {
575
- let outputName;
576
- if (routePath === "/") {
577
- outputName = "index.html";
578
- } else if (directories.includes(routePath)) {
579
- outputName = `${routePath}/index.html`;
580
- } else if (routePath.endsWith("/")) {
581
- outputName = `${routePath}index.html`;
582
- } else {
583
- outputName = `${routePath}.html`;
536
+ /**
537
+ * Get the output file path for a given route.
538
+ */
539
+ getOutputPath(routePath, directories = []) {
540
+ let outputName;
541
+ if (routePath === '/') {
542
+ outputName = 'index.html';
543
+ }
544
+ else if (directories.includes(routePath)) {
545
+ outputName = `${routePath}/index.html`;
546
+ }
547
+ else if (routePath.endsWith('/')) {
548
+ outputName = `${routePath}index.html`;
549
+ }
550
+ else {
551
+ outputName = `${routePath}.html`;
552
+ }
553
+ return path.join(this.getExportDir(), outputName);
584
554
  }
585
- return path.join(this.getExportDir(), outputName);
586
- }
587
555
  }
588
- export {
589
- STATIC_SITE_GENERATOR_ERRORS,
590
- StaticSiteGenerator
591
- };