@ecopages/core 0.2.0-beta.2 → 0.2.0-beta.20

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 (273) hide show
  1. package/README.md +1 -1
  2. package/package.json +163 -3
  3. package/src/adapters/abstract/application-adapter.d.ts +104 -5
  4. package/src/adapters/abstract/application-adapter.js +125 -1
  5. package/src/adapters/abstract/server-adapter.d.ts +6 -2
  6. package/src/adapters/abstract/ws-pattern-matcher.d.ts +60 -0
  7. package/src/adapters/abstract/ws-pattern-matcher.js +61 -0
  8. package/src/adapters/bun/client-bridge.d.ts +1 -0
  9. package/src/adapters/bun/client-bridge.js +3 -0
  10. package/src/adapters/bun/create-app.d.ts +6 -4
  11. package/src/adapters/bun/create-app.js +65 -43
  12. package/src/adapters/bun/hmr-manager.d.ts +2 -1
  13. package/src/adapters/bun/hmr-manager.js +2 -2
  14. package/src/adapters/bun/server-adapter.d.ts +90 -22
  15. package/src/adapters/bun/server-adapter.js +265 -84
  16. package/src/adapters/bun/static-preview-host.js +25 -20
  17. package/src/adapters/create-app.d.ts +13 -11
  18. package/src/adapters/create-app.js +2 -51
  19. package/src/adapters/index.d.ts +1 -1
  20. package/src/adapters/index.js +1 -2
  21. package/src/adapters/node/create-app.d.ts +5 -1
  22. package/src/adapters/node/create-app.js +51 -12
  23. package/src/adapters/node/node-client-bridge.d.ts +1 -0
  24. package/src/adapters/node/node-client-bridge.js +3 -0
  25. package/src/adapters/node/node-hmr-manager.d.ts +2 -1
  26. package/src/adapters/node/node-hmr-manager.js +2 -2
  27. package/src/adapters/node/server-adapter-dependencies.js +4 -0
  28. package/src/adapters/node/server-adapter.d.ts +62 -20
  29. package/src/adapters/node/server-adapter.js +161 -131
  30. package/src/adapters/node/static-content-server.d.ts +1 -0
  31. package/src/adapters/node/static-content-server.js +10 -2
  32. package/src/adapters/node/static-preview-host.js +27 -12
  33. package/src/adapters/shared/bun-user-websocket-lifecycle.d.ts +22 -0
  34. package/src/adapters/shared/bun-user-websocket-lifecycle.js +96 -0
  35. package/src/adapters/shared/copy-runtime-public-dir.d.ts +6 -0
  36. package/src/adapters/shared/copy-runtime-public-dir.js +24 -0
  37. package/src/adapters/shared/fs-server-response-matcher.d.ts +2 -0
  38. package/src/adapters/shared/fs-server-response-matcher.js +40 -18
  39. package/src/adapters/shared/hmr-entrypoint-registrar.d.ts +0 -2
  40. package/src/adapters/shared/hmr-entrypoint-registrar.js +3 -26
  41. package/src/adapters/shared/hmr-html-response.d.ts +1 -1
  42. package/src/adapters/shared/hmr-html-response.js +4 -1
  43. package/src/adapters/shared/node-http-websocket-upgrades.d.ts +21 -0
  44. package/src/adapters/shared/node-http-websocket-upgrades.js +100 -0
  45. package/src/adapters/shared/port-manager.d.ts +116 -0
  46. package/src/adapters/shared/port-manager.js +196 -0
  47. package/src/adapters/shared/runtime-app-bootstrap.d.ts +2 -1
  48. package/src/adapters/shared/runtime-app-bootstrap.js +3 -6
  49. package/src/adapters/shared/runtime-server-lifecycle.d.ts +38 -0
  50. package/src/adapters/shared/runtime-server-lifecycle.js +43 -0
  51. package/src/adapters/shared/server-adapter.d.ts +2 -1
  52. package/src/adapters/shared/server-adapter.js +13 -6
  53. package/src/adapters/shared/server-route-handler.d.ts +4 -1
  54. package/src/adapters/shared/server-route-handler.js +5 -2
  55. package/src/adapters/shared/server-static-builder.d.ts +23 -32
  56. package/src/adapters/shared/server-static-builder.js +97 -73
  57. package/src/adapters/shared/shared-hmr-manager.d.ts +28 -0
  58. package/src/adapters/shared/shared-hmr-manager.js +119 -9
  59. package/src/adapters/shared/static-preview-host.d.ts +5 -0
  60. package/src/adapters/shared/websocket-lifecycle.d.ts +24 -0
  61. package/src/adapters/shared/websocket-lifecycle.js +55 -0
  62. package/src/build/README.md +77 -14
  63. package/src/build/app-build-manifest-runtime.d.ts +10 -0
  64. package/src/build/app-build-manifest-runtime.js +90 -0
  65. package/src/build/build-adapter.d.ts +17 -319
  66. package/src/build/build-adapter.js +28 -94
  67. package/src/build/build-contracts.d.ts +97 -0
  68. package/src/build/build-contracts.js +0 -0
  69. package/src/build/build-input-fingerprint.d.ts +28 -0
  70. package/src/build/build-input-fingerprint.js +31 -0
  71. package/src/build/build-profile-options.d.ts +7 -0
  72. package/src/build/build-profile-options.js +37 -0
  73. package/src/build/build-runtime.d.ts +24 -0
  74. package/src/build/build-runtime.js +65 -0
  75. package/src/build/deduping-build-executor.d.ts +28 -0
  76. package/src/build/deduping-build-executor.js +56 -0
  77. package/src/build/jsx-ownership-plugins.d.ts +24 -0
  78. package/src/build/jsx-ownership-plugins.js +41 -0
  79. package/src/build/pages-unified-graph-build.d.ts +31 -0
  80. package/src/build/pages-unified-graph-build.js +206 -0
  81. package/src/build/parallel-build-executor.d.ts +21 -0
  82. package/src/build/parallel-build-executor.js +52 -0
  83. package/src/build/production-build-cache.d.ts +20 -0
  84. package/src/build/production-build-cache.js +63 -0
  85. package/src/build/rolldown-adapter-helpers.d.ts +3 -6
  86. package/src/build/rolldown-adapter-helpers.js +71 -6
  87. package/src/build/rolldown-build-adapter.d.ts +2 -6
  88. package/src/build/rolldown-build-adapter.js +3 -3
  89. package/src/build/rolldown-build-invocation-metrics.d.ts +12 -0
  90. package/src/build/rolldown-build-invocation-metrics.js +43 -0
  91. package/src/build/rolldown-plugin-bridge.d.ts +15 -1
  92. package/src/build/rolldown-plugin-bridge.js +20 -5
  93. package/src/build/rolldown-source-transform-pass.d.ts +15 -0
  94. package/src/build/rolldown-source-transform-pass.js +58 -0
  95. package/src/build/runtime-build-executor.d.ts +8 -15
  96. package/src/build/runtime-build-executor.js +12 -15
  97. package/src/build/runtime-build-output-normalizer.d.ts +7 -0
  98. package/src/build/runtime-build-output-normalizer.js +94 -2
  99. package/src/build/server-entry-build-cache.d.ts +44 -0
  100. package/src/build/server-entry-build-cache.js +161 -0
  101. package/src/client/navigation-scripts/classification.d.ts +16 -0
  102. package/src/client/navigation-scripts/classification.js +41 -0
  103. package/src/client/navigation-scripts/index.d.ts +3 -0
  104. package/src/client/navigation-scripts/index.js +23 -0
  105. package/src/client/navigation-scripts/navigation-scripts.test.browser.js +65 -0
  106. package/src/client/navigation-scripts/registry.d.ts +11 -0
  107. package/src/client/navigation-scripts/registry.js +12 -0
  108. package/src/client/navigation-scripts/rerun-queue.d.ts +37 -0
  109. package/src/client/navigation-scripts/rerun-queue.js +73 -0
  110. package/src/client/scroll.d.ts +17 -0
  111. package/src/client/scroll.js +25 -0
  112. package/src/client/scroll.test.browser.js +37 -0
  113. package/src/client/view-transitions.d.ts +16 -0
  114. package/src/client/view-transitions.js +63 -0
  115. package/src/config/config-builder.d.ts +12 -7
  116. package/src/config/config-builder.js +12 -9
  117. package/src/dev/client-bridge-registry.d.ts +5 -0
  118. package/src/dev/client-bridge-registry.js +15 -0
  119. package/src/dev/dev-client-ownership.d.ts +7 -0
  120. package/src/dev/dev-client-ownership.js +6 -0
  121. package/src/dev/hmr-manager-registry.d.ts +8 -0
  122. package/src/dev/hmr-manager-registry.js +15 -0
  123. package/src/dev/host-runtime.d.ts +3 -0
  124. package/src/dev/host-runtime.js +7 -0
  125. package/src/dev/runtime-server-started-message.d.ts +2 -0
  126. package/src/dev/runtime-server-started-message.js +6 -0
  127. package/src/eco/README.md +11 -0
  128. package/src/eco/eco-declared-component.d.ts +14 -0
  129. package/src/eco/eco-declared-component.js +16 -0
  130. package/src/eco/eco.browser.js +5 -6
  131. package/src/eco/eco.js +5 -6
  132. package/src/eco/eco.types.d.ts +2 -2
  133. package/src/eco/page-layout-normalization.d.ts +21 -0
  134. package/src/eco/page-layout-normalization.js +59 -0
  135. package/src/errors/index.d.ts +1 -0
  136. package/src/errors/index.js +7 -1
  137. package/src/errors/undeclared-component-dependency-error.d.ts +11 -0
  138. package/src/errors/undeclared-component-dependency-error.js +17 -0
  139. package/src/hmr/client/hmr-runtime.js +5 -2
  140. package/src/hmr/hmr-entrypoint-output.d.ts +28 -0
  141. package/src/hmr/hmr-entrypoint-output.js +58 -0
  142. package/src/hmr/strategies/js-hmr-strategy.d.ts +3 -0
  143. package/src/hmr/strategies/js-hmr-strategy.js +68 -18
  144. package/src/hmr/strategies/server-rendered-template-hmr-strategy.d.ts +17 -0
  145. package/src/hmr/strategies/server-rendered-template-hmr-strategy.js +29 -0
  146. package/src/integrations/ghtml/ghtml-renderer.d.ts +2 -6
  147. package/src/integrations/ghtml/ghtml-renderer.js +2 -51
  148. package/src/integrations/ghtml/ghtml.plugin.d.ts +3 -16
  149. package/src/integrations/ghtml/ghtml.plugin.js +7 -14
  150. package/src/plugins/define-integration.d.ts +26 -0
  151. package/src/plugins/define-integration.js +19 -0
  152. package/src/plugins/eco-component-meta-plugin.js +5 -8
  153. package/src/plugins/foreign-jsx-override-plugin.js +4 -6
  154. package/src/plugins/integration-plugin.d.ts +19 -9
  155. package/src/plugins/integration-plugin.js +7 -4
  156. package/src/plugins/jsx-import-source.utils.d.ts +8 -0
  157. package/src/plugins/jsx-import-source.utils.js +26 -0
  158. package/src/plugins/processor.d.ts +19 -4
  159. package/src/plugins/processor.js +22 -4
  160. package/src/plugins/source-transform.d.ts +31 -0
  161. package/src/plugins/source-transform.js +23 -1
  162. package/src/route-renderer/GRAPH.md +8 -8
  163. package/src/route-renderer/README.md +30 -28
  164. package/src/route-renderer/orchestration/component-graph-collectors.d.ts +10 -0
  165. package/src/route-renderer/orchestration/component-graph-collectors.js +143 -0
  166. package/src/route-renderer/orchestration/component-graph.d.ts +36 -0
  167. package/src/route-renderer/orchestration/component-graph.js +98 -0
  168. package/src/route-renderer/orchestration/component-render-context.d.ts +3 -10
  169. package/src/route-renderer/orchestration/component-render-context.js +3 -1
  170. package/src/route-renderer/orchestration/declared-ownership-graph.d.ts +1 -18
  171. package/src/route-renderer/orchestration/declared-ownership-graph.js +5 -31
  172. package/src/route-renderer/orchestration/document-shell-render.service.d.ts +90 -0
  173. package/src/route-renderer/orchestration/document-shell-render.service.js +120 -0
  174. package/src/route-renderer/orchestration/foreign-subtree-execution.service.d.ts +59 -29
  175. package/src/route-renderer/orchestration/foreign-subtree-execution.service.js +164 -35
  176. package/src/route-renderer/orchestration/global-injector-assets.service.d.ts +7 -0
  177. package/src/route-renderer/orchestration/global-injector-assets.service.js +32 -0
  178. package/src/route-renderer/orchestration/integration-renderer.d.ts +18 -54
  179. package/src/route-renderer/orchestration/integration-renderer.js +113 -195
  180. package/src/route-renderer/orchestration/integration-route-render-adapter.d.ts +38 -0
  181. package/src/route-renderer/orchestration/integration-route-render-adapter.js +27 -0
  182. package/src/route-renderer/orchestration/layout-shell-props.service.d.ts +20 -0
  183. package/src/route-renderer/orchestration/layout-shell-props.service.js +31 -0
  184. package/src/route-renderer/orchestration/ownership-validation.service.d.ts +5 -0
  185. package/src/route-renderer/orchestration/ownership-validation.service.js +29 -1
  186. package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.d.ts +9 -0
  187. package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.js +7 -0
  188. package/src/route-renderer/orchestration/page-browser-graph.service.d.ts +25 -0
  189. package/src/route-renderer/orchestration/page-browser-graph.service.js +174 -0
  190. package/src/route-renderer/orchestration/render-output.utils.d.ts +2 -0
  191. package/src/route-renderer/orchestration/render-output.utils.js +4 -0
  192. package/src/route-renderer/orchestration/route-html-finalization.service.d.ts +17 -0
  193. package/src/route-renderer/orchestration/route-html-finalization.service.js +26 -0
  194. package/src/route-renderer/orchestration/route-prepared-options.builder.d.ts +26 -0
  195. package/src/route-renderer/orchestration/route-prepared-options.builder.js +54 -0
  196. package/src/route-renderer/orchestration/route-prepared-options.utils.d.ts +4 -0
  197. package/src/route-renderer/orchestration/route-prepared-options.utils.js +33 -0
  198. package/src/route-renderer/orchestration/route-render-orchestrator.d.ts +8 -39
  199. package/src/route-renderer/orchestration/route-render-orchestrator.js +53 -461
  200. package/src/route-renderer/orchestration/string-markup-renderer.d.ts +14 -0
  201. package/src/route-renderer/orchestration/string-markup-renderer.js +55 -0
  202. package/src/route-renderer/page-loading/component-dependency-collection.js +5 -2
  203. package/src/router/client/link-intent.d.ts +11 -21
  204. package/src/router/client/link-intent.js +10 -0
  205. package/src/router/client/link-intent.test.browser.js +12 -1
  206. package/src/router/client/link-navigation-policy.d.ts +45 -0
  207. package/src/router/client/link-navigation-policy.js +101 -0
  208. package/src/router/client/link-navigation-policy.test.browser.d.ts +1 -0
  209. package/src/router/client/link-navigation-policy.test.browser.js +92 -0
  210. package/src/router/client/navigation-coordinator.d.ts +1 -1
  211. package/src/router/client/navigation-coordinator.js +12 -2
  212. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.d.ts +1 -0
  213. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.js +1 -0
  214. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.d.ts +4 -2
  215. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.js +27 -7
  216. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +36 -14
  217. package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +17 -3
  218. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.d.ts +0 -3
  219. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +2 -31
  220. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.d.ts +0 -3
  221. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +2 -30
  222. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.d.ts +2 -0
  223. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.js +43 -0
  224. package/src/services/assets/browser-bundle.service.d.ts +3 -1
  225. package/src/services/assets/browser-bundle.service.js +17 -5
  226. package/src/services/html/html-transformer.service.d.ts +1 -1
  227. package/src/services/html/html-transformer.service.js +4 -4
  228. package/src/services/invalidation/development-invalidation.service.d.ts +20 -2
  229. package/src/services/invalidation/development-invalidation.service.js +56 -2
  230. package/src/services/module-loading/README.md +49 -0
  231. package/src/services/module-loading/app-server-module-transpiler.service.js +5 -17
  232. package/src/services/module-loading/page-module-import.service.d.ts +5 -1
  233. package/src/services/module-loading/page-module-import.service.js +97 -45
  234. package/src/services/module-loading/route-module-build-cache-registry.d.ts +24 -0
  235. package/src/services/module-loading/route-module-build-cache-registry.js +37 -0
  236. package/src/services/module-loading/route-module-build-cache.d.ts +4 -0
  237. package/src/services/module-loading/route-module-build-cache.js +38 -0
  238. package/src/services/module-loading/route-module-build-cache.store.d.ts +57 -0
  239. package/src/services/module-loading/route-module-build-cache.store.js +207 -0
  240. package/src/services/module-loading/route-module-build-manifest.d.ts +61 -0
  241. package/src/services/module-loading/route-module-build-manifest.js +116 -0
  242. package/src/services/module-loading/route-module-dependency-hasher.d.ts +81 -0
  243. package/src/services/module-loading/route-module-dependency-hasher.js +121 -0
  244. package/src/services/module-loading/server-module-transpiler.service.js +1 -1
  245. package/src/static-site-generator/README.md +33 -0
  246. package/src/static-site-generator/static-build-invalidation.d.ts +10 -0
  247. package/src/static-site-generator/static-build-invalidation.js +59 -0
  248. package/src/static-site-generator/static-export-context.d.ts +22 -0
  249. package/src/static-site-generator/static-export-context.js +0 -0
  250. package/src/static-site-generator/static-site-generator.d.ts +17 -20
  251. package/src/static-site-generator/static-site-generator.js +188 -85
  252. package/src/types/internal-types.d.ts +16 -9
  253. package/src/types/public-types.d.ts +138 -26
  254. package/src/utils/parse-cli-args.d.ts +2 -0
  255. package/src/utils/parse-cli-args.js +6 -0
  256. package/src/utils/server-utils.module.d.ts +3 -2
  257. package/src/utils/server-utils.module.js +5 -6
  258. package/src/utils/static-file-extensions.d.ts +12 -0
  259. package/src/utils/static-file-extensions.js +48 -0
  260. package/src/watchers/project-watcher-ignore.d.ts +11 -0
  261. package/src/watchers/project-watcher-ignore.js +16 -0
  262. package/src/watchers/project-watcher.d.ts +25 -2
  263. package/src/watchers/project-watcher.js +130 -23
  264. package/src/build/rolldown-dev-build-adapter.d.ts +0 -82
  265. package/src/build/rolldown-dev-build-adapter.js +0 -166
  266. package/src/hmr/hmr.postcss.test.e2e.js +0 -31
  267. package/src/hmr/hmr.test.e2e.js +0 -43
  268. package/src/route-renderer/orchestration/ownership-planning.service.d.ts +0 -24
  269. package/src/route-renderer/orchestration/ownership-planning.service.js +0 -63
  270. package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.d.ts +0 -91
  271. package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.js +0 -170
  272. /package/src/{hmr/hmr.postcss.test.e2e.d.ts → client/navigation-scripts/navigation-scripts.test.browser.d.ts} +0 -0
  273. /package/src/{hmr/hmr.test.e2e.d.ts → client/scroll.test.browser.d.ts} +0 -0
@@ -1,64 +1,23 @@
1
- import { createRequire } from "node:module";
2
- import path from "node:path";
3
- import { fileSystem } from "@ecopages/file-system";
4
- import {
5
- AssetFactory,
6
- createPagePackage
7
- } from "../../services/assets/asset-processing-service/index.js";
8
- import { buildGlobalInjectorBootstrapContent, buildGlobalInjectorMapScript } from "../../eco/global-injector-map.js";
9
- import { LocalsAccessError } from "../../errors/locals-access-error.js";
10
- import { appLogger } from "../../global/app-logger.js";
11
1
  import { inspectUnresolvedMarkerArtifactHtml } from "./render-output.utils.js";
12
- import { OwnershipValidationService } from "./ownership-validation.service.js";
13
- import { OwnershipPlanningService } from "./ownership-planning.service.js";
14
- import { dedupeProcessedAssets } from "./processed-asset-dedupe.js";
15
- function createPageLocalsProxy(filePath) {
16
- const errorMessage = `[ecopages] Request locals are only available during request-time rendering with cache: 'dynamic'. Page: ${filePath}. If you meant to use locals here, set cache: 'dynamic' and provide locals from route middleware/handlers.`;
17
- return new Proxy(
18
- {},
19
- {
20
- get: () => {
21
- throw new LocalsAccessError(errorMessage);
22
- },
23
- set: () => {
24
- throw new LocalsAccessError(errorMessage);
25
- },
26
- has: () => {
27
- throw new LocalsAccessError(errorMessage);
28
- },
29
- ownKeys: () => {
30
- throw new LocalsAccessError(errorMessage);
31
- },
32
- deleteProperty: () => {
33
- throw new LocalsAccessError(errorMessage);
34
- },
35
- defineProperty: () => {
36
- throw new LocalsAccessError(errorMessage);
37
- },
38
- getOwnPropertyDescriptor: () => {
39
- throw new LocalsAccessError(errorMessage);
40
- }
41
- }
42
- );
43
- }
44
- function isGroupedContentScriptAsset(asset) {
45
- return asset.kind === "script" && asset.source === "content" && Boolean(asset.groupedBundle?.id);
46
- }
47
- function getGroupedBundleAssetKey(groupedBundle) {
48
- return `${groupedBundle.id}:${groupedBundle.entryName}`;
49
- }
2
+ import { OwnershipValidationService, throwIfOwnershipInvalid } from "./ownership-validation.service.js";
3
+ import {
4
+ buildEagerSsrLazyAssetsFromGraph,
5
+ collectResolvedLazyTriggersFromGraph,
6
+ collectUsedIntegrationDependenciesFromGraph
7
+ } from "./component-graph-collectors.js";
8
+ import { buildGlobalInjectorAssets } from "./global-injector-assets.service.js";
9
+ import { PageBrowserGraphService } from "./page-browser-graph.service.js";
10
+ import { buildPreparedRenderOptions } from "./route-prepared-options.builder.js";
50
11
  class RouteRenderOrchestrator {
51
12
  appConfig;
52
13
  assetProcessingService;
53
- ownershipPlanningService;
54
14
  ownershipValidationService;
55
- pageBrowserGraphCache = /* @__PURE__ */ new Map();
56
- groupedPageBrowserGraphCache = /* @__PURE__ */ new Map();
15
+ pageBrowserGraphService;
57
16
  constructor(appConfig, assetProcessingService, dependencies = {}) {
58
17
  this.appConfig = appConfig;
59
18
  this.assetProcessingService = assetProcessingService;
60
- this.ownershipPlanningService = dependencies.ownershipPlanningService ?? new OwnershipPlanningService();
61
19
  this.ownershipValidationService = dependencies.ownershipValidationService ?? new OwnershipValidationService(appConfig);
20
+ this.pageBrowserGraphService = dependencies.pageBrowserGraphService ?? new PageBrowserGraphService(appConfig, assetProcessingService);
62
21
  }
63
22
  /**
64
23
  * Builds normalized route render options before the integration render runs.
@@ -69,259 +28,58 @@ class RouteRenderOrchestrator {
69
28
  */
70
29
  async prepareRenderOptions(routeOptions, adapter) {
71
30
  const resolvedInputs = await adapter.resolveRouteRenderInputs(routeOptions);
72
- const { Page, HtmlTemplate, Layout, props, metadata, integrationSpecificProps } = resolvedInputs;
31
+ const { Page, HtmlTemplate, Layouts, Layout } = resolvedInputs;
73
32
  const validationErrors = this.ownershipValidationService.validate({
74
33
  currentIntegrationName: adapter.name,
75
34
  roots: [
76
35
  { component: HtmlTemplate, source: "html-template" },
77
- ...Layout ? [{ component: Layout, source: "layout" }] : [],
36
+ ...Layouts.map((layout) => ({ component: layout, source: "layout" })),
78
37
  { component: Page, source: "page" }
79
38
  ]
80
39
  });
81
- const ownershipPlan = this.ownershipPlanningService.buildPlan({
82
- routeFile: routeOptions.file,
83
- currentIntegrationName: adapter.name,
84
- HtmlTemplate,
85
- Layout,
86
- Page,
87
- validationErrors
88
- });
89
- const componentsToResolve = Layout ? [HtmlTemplate, Layout, Page] : [HtmlTemplate, Page];
90
- const { resolvedDependencies } = await adapter.resolveRouteDependencies({
91
- components: componentsToResolve
92
- });
93
- const pageBrowserGraph = await this.resolvePageBrowserGraph({
94
- routeFile: routeOptions.file,
95
- integrationName: adapter.name,
96
- collectContribution: async (routeFile) => await adapter.collectPageBrowserGraphContribution(routeFile)
97
- });
98
- const usedIntegrationDependencies = this.collectUsedIntegrationDependencies(componentsToResolve, adapter.name);
99
- const allDependencies = [...resolvedDependencies, ...usedIntegrationDependencies];
100
- const componentRender = await adapter.resolveRoutePageComponentRender({
101
- Page,
102
- Layout,
103
- props,
104
- routeOptions
105
- });
40
+ throwIfOwnershipInvalid(validationErrors);
41
+ const componentsToResolve = [HtmlTemplate, ...Layouts, Page];
42
+ const [{ resolvedDependencies }, pageBrowserGraph, componentRender] = await Promise.all([
43
+ adapter.resolveRouteDependencies({
44
+ components: componentsToResolve
45
+ }),
46
+ this.pageBrowserGraphService.resolvePageBrowserGraph({
47
+ routeFile: routeOptions.file,
48
+ integrationName: adapter.name,
49
+ collectContribution: async (routeFile) => await adapter.collectPageBrowserGraphContribution(routeFile)
50
+ }),
51
+ adapter.resolveRoutePageComponentRender({
52
+ Page,
53
+ Layout,
54
+ props: resolvedInputs.props,
55
+ routeOptions
56
+ })
57
+ ]);
58
+ const allDependencies = [
59
+ ...resolvedDependencies,
60
+ ...collectUsedIntegrationDependenciesFromGraph(this.appConfig, componentsToResolve, adapter.name)
61
+ ];
106
62
  if (componentRender?.assets?.length) {
107
63
  allDependencies.push(...componentRender.assets);
108
64
  }
109
- const triggers = this.collectResolvedTriggers(componentsToResolve);
110
- if (triggers.length > 0) {
111
- const globalAssets = await this.buildGlobalInjectorAssets(triggers, adapter.name);
112
- allDependencies.push(...globalAssets);
113
- }
114
- const eagerSsrLazyAssets = await this.buildEagerSsrLazyAssets(componentsToResolve, adapter.name);
115
- if (eagerSsrLazyAssets.length > 0) {
116
- allDependencies.push(...eagerSsrLazyAssets);
117
- }
118
- const dedupedDependencies = dedupeProcessedAssets(allDependencies);
119
- const pagePackage = createPagePackage(dedupedDependencies, { pageBrowserGraph });
120
- const pageProps = {
121
- ...props,
122
- params: routeOptions.params || {},
123
- query: routeOptions.query || {}
124
- };
125
- const cacheStrategy = Page.cache;
126
- const defaultCacheStrategy = this.appConfig.cache?.defaultStrategy ?? "static";
127
- const effectiveCacheStrategy = cacheStrategy ?? defaultCacheStrategy;
128
- const localsAvailable = effectiveCacheStrategy === "dynamic" && routeOptions.locals !== void 0;
129
- const pageLocals = localsAvailable ? routeOptions.locals : createPageLocalsProxy(routeOptions.file);
130
- const locals = localsAvailable ? routeOptions.locals : void 0;
131
- const preparedOptions = {
132
- ...routeOptions,
65
+ const triggers = collectResolvedLazyTriggersFromGraph(componentsToResolve, adapter.name);
66
+ const [globalAssets, eagerSsrLazyAssets] = await Promise.all([
67
+ triggers.length > 0 ? buildGlobalInjectorAssets(this.appConfig, this.assetProcessingService, triggers, adapter.name) : Promise.resolve([]),
68
+ buildEagerSsrLazyAssetsFromGraph(this.assetProcessingService, componentsToResolve, adapter.name)
69
+ ]);
70
+ allDependencies.push(...globalAssets, ...eagerSsrLazyAssets);
71
+ return buildPreparedRenderOptions({
72
+ routeOptions,
73
+ resolvedInputs,
133
74
  resolvedDependencies,
134
- pagePackage,
75
+ allDependencies,
76
+ pageBrowserGraph,
135
77
  componentRender,
136
- HtmlTemplate,
137
- Layout,
138
- props,
139
- Page,
140
- metadata,
141
- params: routeOptions.params || {},
142
- query: routeOptions.query || {},
143
- pageProps,
144
- locals,
145
- pageLocals,
146
- cacheStrategy,
147
- ownershipPlan
148
- };
149
- return {
150
- ...integrationSpecificProps,
151
- ...preparedOptions
152
- };
153
- }
154
- async resolveDeclaredPageBrowserGraph(input) {
155
- return await this.resolvePageBrowserGraph(input);
156
- }
157
- async resolvePageBrowserGraph(input) {
158
- if (this.isHmrEnabled()) {
159
- return await this.buildPageBrowserGraph(input);
160
- }
161
- const cacheKey = `${input.integrationName}:${input.routeFile}`;
162
- const cachedGraph = this.pageBrowserGraphCache.get(cacheKey);
163
- if (cachedGraph) {
164
- return await cachedGraph;
165
- }
166
- const pendingGraph = this.buildPageBrowserGraph(input).catch((error) => {
167
- this.pageBrowserGraphCache.delete(cacheKey);
168
- throw error;
78
+ appConfig: this.appConfig
169
79
  });
170
- this.pageBrowserGraphCache.set(cacheKey, pendingGraph);
171
- return await pendingGraph;
172
80
  }
173
- isHmrEnabled() {
174
- return typeof this.assetProcessingService.getHmrManager === "function" && this.assetProcessingService.getHmrManager()?.isEnabled() === true;
175
- }
176
- async buildPageBrowserGraph(input) {
177
- const contribution = await input.collectContribution(input.routeFile);
178
- if (!contribution) {
179
- return void 0;
180
- }
181
- const groupedDependencies = (contribution.dependencies ?? []).filter((dep) => isGroupedContentScriptAsset(dep));
182
- const ungroupedDependencies = (contribution.dependencies ?? []).filter(
183
- (dep) => !isGroupedContentScriptAsset(dep)
184
- );
185
- const groupedAssets = groupedDependencies.length ? (await this.resolveGroupedPageBrowserAssets(input, contribution)).get(input.routeFile) ?? [] : [];
186
- const processedDependencies = ungroupedDependencies.length ? await this.assetProcessingService.processDependencies(
187
- ungroupedDependencies,
188
- `${input.integrationName}:${input.routeFile}`
189
- ) : [];
190
- const resolvedAssets = [...processedDependencies, ...groupedAssets, ...contribution.assets ?? []];
191
- return this.partitionPageBrowserGraphAssets(resolvedAssets);
192
- }
193
- /**
194
- * Resolves grouped page-browser assets for all routes owned by one integration.
195
- *
196
- * @remarks
197
- * This keeps one shared browser-build result available across sibling routes so
198
- * navigation can reuse the same emitted client graph instead of rebuilding one
199
- * page entry at a time. HMR bypasses this cache because the grouped build must
200
- * reflect the latest source on every request.
201
- */
202
- async resolveGroupedPageBrowserAssets(input, currentContribution) {
203
- if (this.isHmrEnabled()) {
204
- const result = await this.buildGroupedPageBrowserAssets(input, currentContribution);
205
- return result.assetsByRoute;
206
- }
207
- const cacheKey = input.integrationName;
208
- const cached = this.groupedPageBrowserGraphCache.get(cacheKey);
209
- if (cached) {
210
- return await cached;
211
- }
212
- const pendingGroupedAssets = this.buildGroupedPageBrowserAssets(input, currentContribution).then((result) => {
213
- if (result.hasCollectionFailures) {
214
- this.groupedPageBrowserGraphCache.delete(cacheKey);
215
- }
216
- return result.assetsByRoute;
217
- }).catch((error) => {
218
- this.groupedPageBrowserGraphCache.delete(cacheKey);
219
- throw error;
220
- });
221
- this.groupedPageBrowserGraphCache.set(cacheKey, pendingGroupedAssets);
222
- return await pendingGroupedAssets;
223
- }
224
- /**
225
- * Builds the shared grouped page-browser asset map for one integration.
226
- *
227
- * @remarks
228
- * Each route can declare grouped content-script dependencies that should be
229
- * emitted together. This method collects those declarations across the owning
230
- * integration, runs the grouped processor once, and then maps the emitted
231
- * assets back onto the routes that reference them.
232
- */
233
- async buildGroupedPageBrowserAssets(input, currentContribution) {
234
- const routeFiles = await this.listIntegrationRouteFiles(input.integrationName);
235
- const currentRouteGroupedDependencies = (currentContribution.dependencies ?? []).filter(
236
- (dep) => isGroupedContentScriptAsset(dep)
237
- );
238
- const groupedDependencies = [...currentRouteGroupedDependencies];
239
- const groupedAssetKeysByRoute = /* @__PURE__ */ new Map();
240
- let hasCollectionFailures = false;
241
- if (currentRouteGroupedDependencies.length > 0) {
242
- groupedAssetKeysByRoute.set(
243
- input.routeFile,
244
- new Set(currentRouteGroupedDependencies.map((dep) => getGroupedBundleAssetKey(dep.groupedBundle)))
245
- );
246
- }
247
- for (const routeFile of routeFiles) {
248
- if (routeFile === input.routeFile) {
249
- continue;
250
- }
251
- let contribution;
252
- try {
253
- contribution = await input.collectContribution(routeFile);
254
- } catch (error) {
255
- hasCollectionFailures = true;
256
- appLogger.warn(
257
- `Skipping grouped page-browser contribution for ${routeFile}: ${error instanceof Error ? error.message : String(error)}`
258
- );
259
- continue;
260
- }
261
- if (!contribution?.dependencies?.length) {
262
- continue;
263
- }
264
- const routeGroupedDependencies = contribution.dependencies.filter(
265
- (dep) => isGroupedContentScriptAsset(dep)
266
- );
267
- if (routeGroupedDependencies.length === 0) {
268
- continue;
269
- }
270
- groupedDependencies.push(...routeGroupedDependencies);
271
- groupedAssetKeysByRoute.set(
272
- routeFile,
273
- new Set(routeGroupedDependencies.map((dep) => getGroupedBundleAssetKey(dep.groupedBundle)))
274
- );
275
- }
276
- if (groupedDependencies.length === 0) {
277
- return {
278
- assetsByRoute: /* @__PURE__ */ new Map(),
279
- hasCollectionFailures
280
- };
281
- }
282
- const processedGroupedDependencies = await this.assetProcessingService.processDependencies(
283
- groupedDependencies,
284
- `${input.integrationName}:grouped-page-browser-graph`
285
- );
286
- const groupedAssetsByRoute = /* @__PURE__ */ new Map();
287
- for (const [routeFile, groupedAssetKeys] of groupedAssetKeysByRoute) {
288
- groupedAssetsByRoute.set(
289
- routeFile,
290
- processedGroupedDependencies.filter((asset) => {
291
- if (!asset.groupedBundle) {
292
- return false;
293
- }
294
- return groupedAssetKeys.has(getGroupedBundleAssetKey(asset.groupedBundle));
295
- })
296
- );
297
- }
298
- return {
299
- assetsByRoute: groupedAssetsByRoute,
300
- hasCollectionFailures
301
- };
302
- }
303
- async listIntegrationRouteFiles(integrationName) {
304
- const integration = this.appConfig.integrations.find((plugin) => plugin.name === integrationName);
305
- if (!integration) {
306
- return [];
307
- }
308
- const scannedFiles = await fileSystem.glob(
309
- integration.extensions.map((extension) => `**/*${extension}`),
310
- { cwd: this.appConfig.absolutePaths.pagesDir }
311
- );
312
- return scannedFiles.filter((file) => !file.includes(".ecopages-node.")).map((file) => path.join(this.appConfig.absolutePaths.pagesDir, file)).sort((left, right) => left.localeCompare(right));
313
- }
314
- partitionPageBrowserGraphAssets(assets) {
315
- const entryAssets = [];
316
- const chunkAssets = [];
317
- for (const asset of assets) {
318
- if (asset.packageRole === "dynamic-chunk") {
319
- chunkAssets.push(asset);
320
- continue;
321
- }
322
- entryAssets.push(asset);
323
- }
324
- return { entryAssets, chunkAssets };
81
+ async resolveDeclaredPageBrowserGraph(input) {
82
+ return await this.pageBrowserGraphService.resolvePageBrowserGraph(input);
325
83
  }
326
84
  /**
327
85
  * Captures one route render body as HTML while preserving a replayable body value.
@@ -362,7 +120,8 @@ class RouteRenderOrchestrator {
362
120
  "Content-Type": "text/html"
363
121
  }
364
122
  }),
365
- htmlFinalization.htmlContributions
123
+ htmlFinalization.htmlContributions,
124
+ renderOptions.pagePackage
366
125
  );
367
126
  return {
368
127
  body: body2,
@@ -376,7 +135,8 @@ class RouteRenderOrchestrator {
376
135
  "Content-Type": "text/html"
377
136
  }
378
137
  }),
379
- htmlFinalization.htmlContributions
138
+ htmlFinalization.htmlContributions,
139
+ renderOptions.pagePackage
380
140
  );
381
141
  return {
382
142
  body,
@@ -403,174 +163,6 @@ class RouteRenderOrchestrator {
403
163
  html: await new Response(capturedBody).text()
404
164
  };
405
165
  }
406
- collectResolvedTriggers(components, seen = /* @__PURE__ */ new Set()) {
407
- const triggers = [];
408
- for (const comp of components) {
409
- if (!comp) {
410
- continue;
411
- }
412
- const ecoComp = comp;
413
- if (seen.has(ecoComp)) {
414
- continue;
415
- }
416
- seen.add(ecoComp);
417
- const ownTriggers = ecoComp.config?._resolvedLazyTriggers;
418
- if (ownTriggers?.length) {
419
- triggers.push(...ownTriggers);
420
- }
421
- const nested = ecoComp.config?.dependencies?.components;
422
- if (nested?.length) {
423
- triggers.push(...this.collectResolvedTriggers(nested, seen));
424
- }
425
- }
426
- return triggers;
427
- }
428
- collectUsedIntegrationDependencies(components, currentIntegrationName) {
429
- const integrationNames = this.collectIntegrationNames(components);
430
- const dependencies = [];
431
- for (const integrationName of integrationNames) {
432
- if (integrationName === currentIntegrationName) {
433
- continue;
434
- }
435
- const integrationPlugin = this.appConfig.integrations.find(
436
- (integration) => integration.name === integrationName
437
- );
438
- if (!integrationPlugin || typeof integrationPlugin.getResolvedIntegrationDependencies !== "function") {
439
- continue;
440
- }
441
- dependencies.push(...integrationPlugin.getResolvedIntegrationDependencies());
442
- }
443
- return dependencies;
444
- }
445
- collectIntegrationNames(components, seen = /* @__PURE__ */ new Set()) {
446
- const integrationNames = /* @__PURE__ */ new Set();
447
- for (const comp of components) {
448
- if (!comp) {
449
- continue;
450
- }
451
- const ecoComp = comp;
452
- if (seen.has(ecoComp)) {
453
- continue;
454
- }
455
- seen.add(ecoComp);
456
- const integrationName = ecoComp.config?.integration ?? ecoComp.config?.__eco?.integration;
457
- if (integrationName) {
458
- integrationNames.add(integrationName);
459
- }
460
- const nested = ecoComp.config?.dependencies?.components;
461
- if (nested?.length) {
462
- const nestedNames = this.collectIntegrationNames(nested, seen);
463
- for (const nestedName of nestedNames) {
464
- integrationNames.add(nestedName);
465
- }
466
- }
467
- }
468
- return integrationNames;
469
- }
470
- async buildGlobalInjectorAssets(triggers, currentIntegrationName) {
471
- const appProjectDir = this.appConfig.rootDir ?? this.appConfig.absolutePaths?.projectDir ?? process.cwd();
472
- const appPackageRequire = createRequire(path.join(appProjectDir, "package.json"));
473
- const corePackageEntryPath = appPackageRequire.resolve("@ecopages/core");
474
- const globalInjectorImportPath = createRequire(corePackageEntryPath).resolve(
475
- "@ecopages/scripts-injector/global"
476
- );
477
- const mapScript = AssetFactory.createInlineContentScript({
478
- position: "head",
479
- name: "ecopages-global-injector-map",
480
- content: buildGlobalInjectorMapScript(triggers),
481
- attributes: { type: "ecopages/global-injector-map" },
482
- packageRole: "keep-separate",
483
- bundle: false
484
- });
485
- const bootstrapInlineScript = AssetFactory.createInlineContentScript({
486
- position: "head",
487
- name: "ecopages-global-injector-bootstrap",
488
- content: buildGlobalInjectorBootstrapContent(globalInjectorImportPath),
489
- attributes: { type: "module" },
490
- packageRole: "keep-separate",
491
- bundle: true
492
- });
493
- return this.assetProcessingService.processDependencies(
494
- [mapScript, bootstrapInlineScript],
495
- currentIntegrationName
496
- );
497
- }
498
- async buildEagerSsrLazyAssets(components, currentIntegrationName) {
499
- const dependencies = this.collectEagerSsrLazyDependencies(components);
500
- if (dependencies.length === 0) {
501
- return [];
502
- }
503
- return this.assetProcessingService.processDependencies(dependencies, `${currentIntegrationName}:ssr-lazy`);
504
- }
505
- collectEagerSsrLazyDependencies(components) {
506
- const dependencies = [];
507
- const visitedConfigs = /* @__PURE__ */ new Set();
508
- const seenKeys = /* @__PURE__ */ new Set();
509
- const normalizeAttributes = (attributes) => ({
510
- type: "module",
511
- defer: "",
512
- ...attributes ?? {}
513
- });
514
- const collect = (config) => {
515
- if (!config || visitedConfigs.has(config)) {
516
- return;
517
- }
518
- visitedConfigs.add(config);
519
- const componentFile = config.__eco?.file;
520
- if (componentFile) {
521
- const componentDir = path.dirname(componentFile);
522
- for (const script of config.dependencies?.scripts ?? []) {
523
- if (typeof script === "string" || !script.lazy || script.ssr !== true) {
524
- continue;
525
- }
526
- const attributes = normalizeAttributes(script.attributes);
527
- if (script.content) {
528
- const key2 = `content:${script.content}:${JSON.stringify(attributes)}`;
529
- if (seenKeys.has(key2)) {
530
- continue;
531
- }
532
- seenKeys.add(key2);
533
- dependencies.push(
534
- AssetFactory.createContentScript({
535
- position: "head",
536
- content: script.content,
537
- attributes,
538
- packageRole: "dynamic-chunk"
539
- })
540
- );
541
- continue;
542
- }
543
- if (!script.src) {
544
- continue;
545
- }
546
- const resolvedPath = path.resolve(componentDir, script.src);
547
- const key = `file:${resolvedPath}:${JSON.stringify(attributes)}`;
548
- if (seenKeys.has(key)) {
549
- continue;
550
- }
551
- seenKeys.add(key);
552
- dependencies.push(
553
- AssetFactory.createFileScript({
554
- filepath: resolvedPath,
555
- position: "head",
556
- attributes,
557
- packageRole: "dynamic-chunk"
558
- })
559
- );
560
- }
561
- }
562
- if (config.layout?.config) {
563
- collect(config.layout.config);
564
- }
565
- for (const nestedComponent of config.dependencies?.components ?? []) {
566
- collect(nestedComponent?.config);
567
- }
568
- };
569
- for (const component of components) {
570
- collect(component.config);
571
- }
572
- return dependencies;
573
- }
574
166
  }
575
167
  export {
576
168
  RouteRenderOrchestrator
@@ -0,0 +1,14 @@
1
+ import type { ComponentRenderInput, ComponentRenderResult, EcoComponent, EcoPagesElement, IntegrationRendererRenderOptions, RouteRendererBody } from '../../types/public-types.js';
2
+ import { IntegrationRenderer, type RenderToResponseContext } from './integration-renderer.js';
3
+ /**
4
+ * Base renderer for integrations whose page output is string HTML markup.
5
+ *
6
+ * @remarks
7
+ * Ghtml, KitaJS, and MDX share the same document-shell rendering path. Subclasses
8
+ * only need to set `name` and override hooks for integration-specific behavior.
9
+ */
10
+ export declare abstract class StringMarkupRenderer extends IntegrationRenderer<EcoPagesElement> {
11
+ renderComponent(input: ComponentRenderInput): Promise<ComponentRenderResult>;
12
+ render({ params, query, props, locals, pageLocals, metadata, Page, Layout, HtmlTemplate, pageProps, }: IntegrationRendererRenderOptions): Promise<RouteRendererBody>;
13
+ renderToResponse<P = Record<string, unknown>>(view: EcoComponent<P>, props: P, ctx: RenderToResponseContext): Promise<Response>;
14
+ }
@@ -0,0 +1,55 @@
1
+ import { IntegrationRenderer } from "./integration-renderer.js";
2
+ import { resolveInnermostPageLayout } from "./layout-shell-props.service.js";
3
+ class StringMarkupRenderer extends IntegrationRenderer {
4
+ async renderComponent(input) {
5
+ if (typeof input.component !== "function") {
6
+ throw new TypeError(`${this.name} renderer expected a callable component.`);
7
+ }
8
+ return this.renderStringComponentWithQueuedForeignSubtrees(input, input.component);
9
+ }
10
+ async render({
11
+ params,
12
+ query,
13
+ props,
14
+ locals,
15
+ pageLocals,
16
+ metadata,
17
+ Page,
18
+ Layout,
19
+ HtmlTemplate,
20
+ pageProps
21
+ }) {
22
+ try {
23
+ return await this.renderPageWithDocumentShell({
24
+ page: {
25
+ component: Page,
26
+ props: { params, query, ...props, locals: pageLocals }
27
+ },
28
+ layout: Layout ? {
29
+ component: Layout,
30
+ props: locals ? { locals } : {}
31
+ } : void 0,
32
+ htmlTemplate: HtmlTemplate,
33
+ metadata,
34
+ pageProps: pageProps ?? props ?? {}
35
+ });
36
+ } catch (error) {
37
+ throw this.createRenderError("Error rendering page", error);
38
+ }
39
+ }
40
+ async renderToResponse(view, props, ctx) {
41
+ try {
42
+ return await this.renderViewWithDocumentShell({
43
+ view,
44
+ props,
45
+ ctx,
46
+ layout: resolveInnermostPageLayout(view.config?.layouts)
47
+ });
48
+ } catch (error) {
49
+ throw this.createRenderError("Error rendering view", error);
50
+ }
51
+ }
52
+ }
53
+ export {
54
+ StringMarkupRenderer
55
+ };
@@ -1,4 +1,5 @@
1
1
  import path from "node:path";
2
+ import { assertEcoDeclaredComponent } from "../../eco/eco-declared-component.js";
2
3
  import { AssetFactory } from "../../services/assets/asset-processing-service/index.js";
3
4
  import { extractEcopagesVirtualImports } from "./ecopages-virtual-imports.js";
4
5
  import { collectDeclaredAssetEntries } from "./declared-asset-collection.js";
@@ -103,9 +104,11 @@ function collectComponentDependencies(options) {
103
104
  });
104
105
  if (dependenciesConfig?.components) {
105
106
  for (const nestedComponent of dependenciesConfig.components) {
106
- if (nestedComponent?.config) {
107
- collect(nestedComponent.config);
107
+ if (!nestedComponent) {
108
+ continue;
108
109
  }
110
+ assertEcoDeclaredComponent(nestedComponent, { parentComponentFile: file });
111
+ collect(nestedComponent.config);
109
112
  }
110
113
  }
111
114
  };