@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,226 +1,113 @@
1
- import path from "node:path";
2
- import { fileSystem } from "@ecopages/file-system";
3
- import { HmrStrategy, HmrStrategyType } from "../hmr-strategy.js";
4
- import { appLogger } from "../../global/app-logger.js";
5
- import {
6
- removeStaleHmrEntrypointOutput,
7
- resolveHmrEntrypointOutputPaths,
8
- isRegisteredScriptEntrypoint,
9
- isHmrOutputFresh,
10
- isHmrOutputOlderThanSource
11
- } from "../hmr-entrypoint-output.js";
12
- class JsHmrStrategy extends HmrStrategy {
13
- type = HmrStrategyType.SCRIPT;
14
- context;
15
- constructor(context) {
16
- super();
17
- this.context = context;
18
- }
19
- /**
20
- * Determines if the file is a JS/TS file that could affect registered entrypoints.
21
- *
22
- * Matches if:
23
- * 1. There are registered entrypoints to rebuild
24
- * 2. The changed file is a JS/TS file in the src directory
25
- * 3. Registered entrypoints always match, even when they share an integration template extension
26
- *
27
- * @param filePath - Absolute path to the changed file
28
- * @returns True if this file should trigger entrypoint rebuilds
29
- */
30
- matches(filePath) {
31
- const watchedFiles = this.context.getWatchedFiles();
32
- const resolvedPath = path.resolve(filePath);
33
- const isJsTs = /\.(ts|tsx|js|jsx)$/.test(resolvedPath);
34
- const srcDir = path.resolve(this.context.getSrcDir());
35
- const isInSrc = resolvedPath.startsWith(`${srcDir}${path.sep}`) || resolvedPath === srcDir;
36
- const isIntegrationTemplate = this.context.getTemplateExtensions().some((extension) => resolvedPath.endsWith(extension));
37
- if (watchedFiles.size === 0) {
38
- return false;
1
+ /**
2
+ * JavaScript HMR Strategy
3
+ *
4
+ * Handles hot module replacement for JavaScript and TypeScript entrypoints.
5
+ * Invalidates dev-transform modules and decides whether the browser can hot-accept
6
+ * the change or must reload.
7
+ *
8
+ * @module
9
+ */
10
+ import path from 'node:path';
11
+ import { HmrStrategy, HmrStrategyType } from '../hmr-strategy.js';
12
+ import { appLogger } from '../../global/app-logger.js';
13
+ import { isRegisteredDevTransformEntrypoint, isRegisteredScriptEntrypoint } from '../hmr-entrypoint-output.js';
14
+ import { isDevTransformModuleUrl } from '../hmr-asset-paths.js';
15
+ /**
16
+ * Strategy for handling JavaScript/TypeScript file changes with hot reloading.
17
+ */
18
+ export class JsHmrStrategy extends HmrStrategy {
19
+ type = HmrStrategyType.SCRIPT;
20
+ context;
21
+ constructor(context) {
22
+ super();
23
+ this.context = context;
39
24
  }
40
- if (!isJsTs || !isInSrc) {
41
- return false;
42
- }
43
- if (watchedFiles.has(resolvedPath)) {
44
- return true;
45
- }
46
- if (isIntegrationTemplate) {
47
- return false;
48
- }
49
- return true;
50
- }
51
- /**
52
- * Processes a file change by rebuilding affected entrypoints.
53
- *
54
- * @param filePath - Absolute path to the changed file
55
- *
56
- * @remarks
57
- * If runtime-specific dependency graph hooks are unavailable, this strategy
58
- * falls back to rebuilding all watched entrypoints.
59
- * When multiple entrypoints are impacted they are bundled in a single
60
- * invocation to share AST parsing and chunk deduplication.
61
- * @returns Action to broadcast update events
62
- */
63
- async process(filePath) {
64
- appLogger.debug(`[JsHmrStrategy] Processing ${filePath}`);
65
- const watchedFiles = this.context.getWatchedFiles();
66
- const resolvedChanged = path.resolve(filePath);
67
- const isRegisteredEntrypointEdit = isRegisteredScriptEntrypoint(watchedFiles, resolvedChanged);
68
- if (watchedFiles.size === 0) {
69
- appLogger.debug(`[JsHmrStrategy] No watched files to rebuild`);
70
- return { type: "none" };
71
- }
72
- const dependencyHits = this.context.getEntrypointDependencyGraph().getDependencyEntrypoints(filePath);
73
- const hasDependencyHit = dependencyHits.size > 0;
74
- const impactedEntrypoints = isRegisteredEntrypointEdit ? [resolvedChanged] : hasDependencyHit ? Array.from(dependencyHits).filter((entrypoint) => watchedFiles.has(path.resolve(entrypoint))) : Array.from(watchedFiles.keys());
75
- const buildableEntrypoints = impactedEntrypoints.filter(
76
- (entrypoint) => this.context.shouldProcessEntrypoint?.(entrypoint) ?? true
77
- );
78
- if (!hasDependencyHit && !isRegisteredEntrypointEdit) {
79
- appLogger.debug("[JsHmrStrategy] Dependency graph miss, rebuilding all watched entrypoints");
80
- }
81
- if (buildableEntrypoints.length === 0) {
82
- return { type: "none" };
83
- }
84
- for (const entrypoint of buildableEntrypoints) {
85
- this.removeStaleEntrypointOutput(this.resolveEntrypointOutputPath(entrypoint));
86
- }
87
- const buildResult = await this.bundleEntrypoints(buildableEntrypoints);
88
- if (!buildResult.success) {
89
- return { type: "none" };
90
- }
91
- const updates = [];
92
- let reloadRequired = false;
93
- for (const entrypoint of buildableEntrypoints) {
94
- const resolvedEntrypoint = path.resolve(entrypoint);
95
- const derivedOutput = resolveHmrEntrypointOutputPaths(
96
- this.context.getSrcDir(),
97
- this.context.getDistDir(),
98
- resolvedEntrypoint
99
- );
100
- const outputUrl = watchedFiles.get(resolvedEntrypoint) ?? derivedOutput.outputUrl;
101
- const outputPath = this.resolveEntrypointOutputPath(resolvedEntrypoint);
102
- if (buildResult.dependencies) {
103
- const entrypointDeps = buildResult.dependencies.get(resolvedEntrypoint) ?? [];
104
- this.context.getEntrypointDependencyGraph().setEntrypointDependencies(resolvedEntrypoint, entrypointDeps);
105
- }
106
- const result = await this.processOutput(outputPath, outputUrl, resolvedEntrypoint);
107
- if (result.success) {
108
- updates.push(outputUrl);
109
- if (result.requiresReload) {
110
- reloadRequired = true;
25
+ matches(filePath) {
26
+ const watchedFiles = this.context.getWatchedFiles();
27
+ const resolvedPath = path.resolve(filePath);
28
+ const isJsTs = /\.(ts|tsx|js|jsx)$/.test(resolvedPath);
29
+ const srcDir = path.resolve(this.context.getSrcDir());
30
+ const isInSrc = resolvedPath.startsWith(`${srcDir}${path.sep}`) || resolvedPath === srcDir;
31
+ const isIntegrationTemplate = this.context
32
+ .getTemplateExtensions()
33
+ .some((extension) => resolvedPath.endsWith(extension));
34
+ if (watchedFiles.size === 0) {
35
+ return false;
111
36
  }
112
- }
113
- }
114
- if (updates.length > 0) {
115
- if (reloadRequired) {
116
- appLogger.debug(`[JsHmrStrategy] Full reload required (no HMR accept found)`);
117
- return {
118
- type: "broadcast",
119
- events: [{ type: "reload" }]
120
- };
121
- }
122
- return {
123
- type: "broadcast",
124
- events: updates.map((p) => ({
125
- type: "update",
126
- path: p,
127
- timestamp: Date.now()
128
- }))
129
- };
130
- }
131
- return { type: "none" };
132
- }
133
- resolveEntrypointOutputPath(entrypointPath) {
134
- return resolveHmrEntrypointOutputPaths(this.context.getSrcDir(), this.context.getDistDir(), entrypointPath).outputPath;
135
- }
136
- removeStaleEntrypointOutput(outputPath) {
137
- removeStaleHmrEntrypointOutput(outputPath, "JsHmrStrategy");
138
- }
139
- /**
140
- * Bundles one or more entrypoints in a single build invocation.
141
- * Uses the source directory as the output base so that the directory structure
142
- * is preserved under the HMR dist folder.
143
- */
144
- async bundleEntrypoints(entrypoints) {
145
- try {
146
- if (entrypoints.length === 1) {
147
- const entrypoint = entrypoints[0];
148
- const outputPath = this.resolveEntrypointOutputPath(entrypoint);
149
- const naming = path.relative(this.context.getDistDir(), outputPath).split(path.sep).join("/");
150
- const result2 = await this.context.getBrowserBundleService().bundle({
151
- profile: "hmr-entrypoint",
152
- entrypoints: [entrypoint],
153
- outdir: this.context.getDistDir(),
154
- naming,
155
- minify: false
156
- });
157
- if (!result2.success) {
158
- appLogger.error("[JsHmrStrategy] Entrypoint build failed:", result2.logs);
159
- return { success: false };
37
+ if (!isJsTs || !isInSrc) {
38
+ return false;
160
39
  }
161
- const dependencies2 = /* @__PURE__ */ new Map();
162
- if (result2.dependencyGraph?.entrypoints) {
163
- for (const [resolvedEntrypoint, deps] of Object.entries(result2.dependencyGraph.entrypoints)) {
164
- dependencies2.set(path.resolve(resolvedEntrypoint), deps);
165
- }
40
+ if (watchedFiles.has(resolvedPath)) {
41
+ return true;
166
42
  }
167
- return { success: true, dependencies: dependencies2 };
168
- }
169
- const result = await this.context.getBrowserBundleService().bundle({
170
- profile: "hmr-entrypoint",
171
- entrypoints,
172
- outdir: this.context.getDistDir(),
173
- outbase: this.context.getSrcDir(),
174
- naming: "[dir]/[name]",
175
- minify: false
176
- });
177
- if (!result.success) {
178
- appLogger.error("[JsHmrStrategy] Batched build failed:", result.logs);
179
- return { success: false };
180
- }
181
- const dependencies = /* @__PURE__ */ new Map();
182
- if (result.dependencyGraph?.entrypoints) {
183
- for (const [entrypoint, deps] of Object.entries(result.dependencyGraph.entrypoints)) {
184
- dependencies.set(path.resolve(entrypoint), deps);
43
+ if (isIntegrationTemplate) {
44
+ return false;
185
45
  }
186
- }
187
- return { success: true, dependencies };
188
- } catch (error) {
189
- appLogger.error("[JsHmrStrategy] Error in batched build:", error);
190
- return { success: false };
46
+ return true;
191
47
  }
192
- }
193
- /**
194
- * Processes bundled output and determines whether the browser can hot-accept
195
- * the update or must fall back to a full reload.
196
- *
197
- * @param filepath - Path to the bundled output file
198
- * @param url - URL path for the bundled file
199
- * @returns True if processing was successful and update should be broadcast
200
- */
201
- async processOutput(filepath, url, sourcePath) {
202
- try {
203
- if (sourcePath && !isHmrOutputFresh(filepath, sourcePath)) {
204
- if (isHmrOutputOlderThanSource(filepath, sourcePath)) {
205
- appLogger.warn(
206
- `[JsHmrStrategy] HMR output is older than source after rebuild; skipping broadcast for ${url}`
207
- );
48
+ async process(filePath) {
49
+ appLogger.debug(`[JsHmrStrategy] Processing ${filePath}`);
50
+ const watchedFiles = this.context.getWatchedFiles();
51
+ const resolvedChanged = path.resolve(filePath);
52
+ const registeredEntrypoints = this.context.getRegisteredEntrypoints();
53
+ const isRegisteredEntrypointEdit = isRegisteredDevTransformEntrypoint(registeredEntrypoints, resolvedChanged);
54
+ if (watchedFiles.size === 0) {
55
+ appLogger.debug(`[JsHmrStrategy] No watched files to rebuild`);
56
+ return { type: 'none' };
57
+ }
58
+ const dependencyHits = this.context.getEntrypointDependencyGraph().getDependencyEntrypoints(filePath);
59
+ const hasDependencyHit = dependencyHits.size > 0;
60
+ const impactedEntrypoints = isRegisteredEntrypointEdit
61
+ ? [resolvedChanged]
62
+ : hasDependencyHit
63
+ ? Array.from(dependencyHits).filter((entrypoint) => watchedFiles.has(path.resolve(entrypoint)))
64
+ : Array.from(watchedFiles.keys());
65
+ const buildableEntrypoints = impactedEntrypoints.filter((entrypoint) => this.context.shouldProcessEntrypoint?.(entrypoint) ?? true);
66
+ if (!hasDependencyHit && !isRegisteredEntrypointEdit) {
67
+ appLogger.debug('[JsHmrStrategy] Dependency graph miss, rebuilding all watched entrypoints');
68
+ }
69
+ if (buildableEntrypoints.length === 0) {
70
+ return { type: 'none' };
71
+ }
72
+ const devTransformUpdates = [];
73
+ let devTransformReloadRequired = false;
74
+ for (const entrypoint of buildableEntrypoints) {
75
+ const resolvedEntrypoint = path.resolve(entrypoint);
76
+ const outputUrl = watchedFiles.get(resolvedEntrypoint);
77
+ if (!outputUrl || !isDevTransformModuleUrl(outputUrl)) {
78
+ appLogger.debug(`[JsHmrStrategy] Skipping non-dev-transform entrypoint ${resolvedEntrypoint}: ${outputUrl ?? 'unregistered'}`);
79
+ continue;
80
+ }
81
+ this.context.invalidateDevTransformSource?.(resolvedEntrypoint);
82
+ if (isRegisteredScriptEntrypoint(registeredEntrypoints, resolvedEntrypoint)) {
83
+ if (this.context.consumeRegisteredScriptReloadRequired?.(resolvedEntrypoint)) {
84
+ devTransformReloadRequired = true;
85
+ }
86
+ else {
87
+ devTransformUpdates.push(outputUrl);
88
+ }
89
+ }
90
+ else {
91
+ devTransformUpdates.push(outputUrl);
92
+ }
93
+ }
94
+ if (devTransformReloadRequired) {
95
+ appLogger.debug(`[JsHmrStrategy] Full reload required (no HMR accept found)`);
96
+ return {
97
+ type: 'broadcast',
98
+ events: [{ type: 'reload' }],
99
+ };
100
+ }
101
+ if (devTransformUpdates.length > 0) {
102
+ return {
103
+ type: 'broadcast',
104
+ events: devTransformUpdates.map((p) => ({
105
+ type: 'update',
106
+ path: p,
107
+ timestamp: Date.now(),
108
+ })),
109
+ };
208
110
  }
209
- return { success: false, requiresReload: false };
210
- }
211
- const code = await fileSystem.readFile(filepath);
212
- if (code.includes("/* [ecopages] hmr */")) {
213
- return { success: true, requiresReload: !code.includes("import.meta.hot.accept") };
214
- }
215
- appLogger.debug(`[JsHmrStrategy] Processed ${url}`);
216
- const hasHmrAccept = code.includes("import.meta.hot.accept");
217
- return { success: true, requiresReload: !hasHmrAccept };
218
- } catch (error) {
219
- appLogger.error(`[JsHmrStrategy] Error processing output for ${url}:`, error);
220
- return { success: false, requiresReload: false };
111
+ return { type: 'none' };
221
112
  }
222
- }
223
113
  }
224
- export {
225
- JsHmrStrategy
226
- };
@@ -1,29 +1,35 @@
1
- import { HmrStrategy, HmrStrategyType } from "../hmr-strategy.js";
1
+ /**
2
+ * HMR strategy for server-rendered include templates and explicit server views.
3
+ *
4
+ * These files affect SSR output (head, layout shell, explicit views) but are not
5
+ * client HMR entrypoints. Broadcast a layout-update so the active navigation
6
+ * runtime can refresh the current page without a full document reload.
7
+ */
8
+ import { HmrStrategy, HmrStrategyType } from '../hmr-strategy.js';
9
+ /** Outranks integration-owned strategies that may also match shared dependencies. */
2
10
  const SERVER_RENDERED_TEMPLATE_PRIORITY_OFFSET = 50;
3
- class ServerRenderedTemplateHmrStrategy extends HmrStrategy {
4
- constructor(invalidationService) {
5
- super();
6
- this.invalidationService = invalidationService;
7
- }
8
- invalidationService;
9
- type = HmrStrategyType.INTEGRATION;
10
- priorityOffset = SERVER_RENDERED_TEMPLATE_PRIORITY_OFFSET;
11
- matches(filePath) {
12
- return this.invalidationService.isIncludeSourceFile(filePath) || this.invalidationService.isExplicitServerViewFile(filePath);
13
- }
14
- async process(filePath) {
15
- return {
16
- type: "broadcast",
17
- events: [
18
- {
19
- type: "layout-update",
20
- path: filePath,
21
- timestamp: Date.now()
22
- }
23
- ]
24
- };
25
- }
11
+ export class ServerRenderedTemplateHmrStrategy extends HmrStrategy {
12
+ invalidationService;
13
+ type = HmrStrategyType.INTEGRATION;
14
+ priorityOffset = SERVER_RENDERED_TEMPLATE_PRIORITY_OFFSET;
15
+ constructor(invalidationService) {
16
+ super();
17
+ this.invalidationService = invalidationService;
18
+ }
19
+ matches(filePath) {
20
+ return (this.invalidationService.isIncludeSourceFile(filePath) ||
21
+ this.invalidationService.isExplicitServerViewFile(filePath));
22
+ }
23
+ async process(filePath) {
24
+ return {
25
+ type: 'broadcast',
26
+ events: [
27
+ {
28
+ type: 'layout-update',
29
+ path: filePath,
30
+ timestamp: Date.now(),
31
+ },
32
+ ],
33
+ };
34
+ }
26
35
  }
27
- export {
28
- ServerRenderedTemplateHmrStrategy
29
- };
@@ -1,4 +1 @@
1
- import { eco } from "./eco/eco.browser.js";
2
- export {
3
- eco
4
- };
1
+ export { eco } from './eco/eco.browser.js';
package/src/index.d.ts CHANGED
@@ -5,3 +5,4 @@ export { defineApiHandler, defineGroupHandler, defineGet, definePost, definePut,
5
5
  export { createEcoBuildPluginFromSourceTransform, createVitePluginsFromAppSourceTransforms, getAppSourceTransforms, createVitePluginFromSourceTransform, normalizeTransformId, type EcoSourceTransform, type EcoSourceTransformResult, type EcoViteCompatiblePlugin, } from './plugins/source-transform.js';
6
6
  export { createEcoComponentMetaTransform } from './plugins/eco-component-meta-plugin.js';
7
7
  export { SchemaError, validateStandardSchema } from './services/validation/validate-standard-schema.js';
8
+ export { buildIslandHostAttributes, ECO_ISLAND_HOST_ATTRIBUTE, ECO_ISLAND_INTEGRATION_ATTRIBUTE, finalizeIslandComponentRender, isIslandHostElement, mergeIslandHostAttributes, } from './islands/island-host.js';
package/src/index.js CHANGED
@@ -1,47 +1,6 @@
1
- import { eco } from "./eco/eco.js";
2
- import {
3
- defineApiHandler,
4
- defineGroupHandler,
5
- defineGet,
6
- definePost,
7
- definePut,
8
- defineDelete,
9
- definePatch,
10
- defineOptions,
11
- defineHead,
12
- json,
13
- html,
14
- redirect
15
- } from "./adapters/shared/http/define-api-handler.js";
16
- import {
17
- createEcoBuildPluginFromSourceTransform,
18
- createVitePluginsFromAppSourceTransforms,
19
- getAppSourceTransforms,
20
- createVitePluginFromSourceTransform,
21
- normalizeTransformId
22
- } from "./plugins/source-transform.js";
23
- import { createEcoComponentMetaTransform } from "./plugins/eco-component-meta-plugin.js";
24
- import { SchemaError, validateStandardSchema } from "./services/validation/validate-standard-schema.js";
25
- export {
26
- SchemaError,
27
- createEcoBuildPluginFromSourceTransform,
28
- createEcoComponentMetaTransform,
29
- createVitePluginFromSourceTransform,
30
- createVitePluginsFromAppSourceTransforms,
31
- defineApiHandler,
32
- defineDelete,
33
- defineGet,
34
- defineGroupHandler,
35
- defineHead,
36
- defineOptions,
37
- definePatch,
38
- definePost,
39
- definePut,
40
- eco,
41
- getAppSourceTransforms,
42
- html,
43
- json,
44
- normalizeTransformId,
45
- redirect,
46
- validateStandardSchema
47
- };
1
+ export { eco } from './eco/eco.js';
2
+ export { defineApiHandler, defineGroupHandler, defineGet, definePost, definePut, defineDelete, definePatch, defineOptions, defineHead, json, html, redirect, } from './adapters/shared/http/define-api-handler.js';
3
+ export { createEcoBuildPluginFromSourceTransform, createVitePluginsFromAppSourceTransforms, getAppSourceTransforms, createVitePluginFromSourceTransform, normalizeTransformId, } from './plugins/source-transform.js';
4
+ export { createEcoComponentMetaTransform } from './plugins/eco-component-meta-plugin.js';
5
+ export { SchemaError, validateStandardSchema } from './services/validation/validate-standard-schema.js';
6
+ export { buildIslandHostAttributes, ECO_ISLAND_HOST_ATTRIBUTE, ECO_ISLAND_INTEGRATION_ATTRIBUTE, finalizeIslandComponentRender, isIslandHostElement, mergeIslandHostAttributes, } from './islands/island-host.js';
@@ -1,8 +1,13 @@
1
- import { StringMarkupRenderer } from "../../route-renderer/orchestration/string-markup-renderer.js";
2
- import { GHTML_PLUGIN_NAME } from "./ghtml.constants.js";
3
- class GhtmlRenderer extends StringMarkupRenderer {
4
- name = GHTML_PLUGIN_NAME;
1
+ /**
2
+ * This module contains the ghtml renderer
3
+ * @module
4
+ */
5
+ import { StringMarkupRenderer } from '../../route-renderer/orchestration/string-markup-renderer.js';
6
+ import { GHTML_PLUGIN_NAME } from './ghtml.constants.js';
7
+ /**
8
+ * A renderer for the ghtml integration.
9
+ * It renders a page using the HtmlTemplate and Page components.
10
+ */
11
+ export class GhtmlRenderer extends StringMarkupRenderer {
12
+ name = GHTML_PLUGIN_NAME;
5
13
  }
6
- export {
7
- GhtmlRenderer
8
- };
@@ -1,4 +1 @@
1
- const GHTML_PLUGIN_NAME = "ghtml";
2
- export {
3
- GHTML_PLUGIN_NAME
4
- };
1
+ export const GHTML_PLUGIN_NAME = 'ghtml';
@@ -1,13 +1,10 @@
1
- import { defineIntegration } from "../../plugins/define-integration.js";
2
- import { GhtmlRenderer } from "./ghtml-renderer.js";
3
- import { GHTML_PLUGIN_NAME } from "./ghtml.constants.js";
4
- const ghtmlPlugin = defineIntegration({
5
- name: GHTML_PLUGIN_NAME,
6
- extensions: [".ghtml.ts", ".ghtml.tsx", ".ghtml"],
7
- renderer: GhtmlRenderer
1
+ import { defineIntegration } from '../../plugins/define-integration.js';
2
+ import { GhtmlRenderer } from './ghtml-renderer.js';
3
+ import { GHTML_PLUGIN_NAME } from './ghtml.constants.js';
4
+ export const ghtmlPlugin = defineIntegration({
5
+ name: GHTML_PLUGIN_NAME,
6
+ extensions: ['.ghtml.ts', '.ghtml.tsx', '.ghtml'],
7
+ renderer: GhtmlRenderer,
8
8
  });
9
- const GhtmlPlugin = ghtmlPlugin.Plugin;
10
- export {
11
- GhtmlPlugin,
12
- ghtmlPlugin
13
- };
9
+ /** @deprecated Use {@link ghtmlPlugin.Plugin} or {@link ghtmlPlugin} instead. */
10
+ export const GhtmlPlugin = ghtmlPlugin.Plugin;
@@ -0,0 +1,25 @@
1
+ # Island host contract
2
+
3
+ Hydratable component instances stamp a small, integration-agnostic attribute set on their SSR root so devtools (and future runtime features) can discover islands without integration-specific selectors.
4
+
5
+ ## Attributes
6
+
7
+ | Attribute | Purpose |
8
+ | ----------------------------- | ---------------------------------------------------- |
9
+ | `data-eco-island` | Marks the element as an interactive island host |
10
+ | `data-eco-island-integration` | Owning integration (`react`, `lit`, `kitajs`, …) |
11
+ | `data-eco-component-id` | Stable instance id from the render pipeline |
12
+ | `data-eco-component-key` | Optional module key used by client hydration (React) |
13
+ | `data-eco-props` | Optional base64 JSON props snapshot for hydration |
14
+
15
+ React may still replace the SSR host with `<eco-island>` after hydration; the marker is applied at SSR time on the component root.
16
+
17
+ ## Where stamping happens
18
+
19
+ `finalizeIslandComponentRender()` in `island-host.ts` is called from integration renderers when:
20
+
21
+ 1. `integrationContext.componentInstanceId` is present
22
+ 2. The render result can attach attributes to a single root element
23
+ 3. The render emits at least one client `script` asset
24
+
25
+ Integrations with custom island metadata (for example React's component key) should build on `buildIslandHostAttributes()` rather than inventing per-demo selectors.
@@ -0,0 +1,37 @@
1
+ export declare const ECO_ISLAND_HOST_ATTRIBUTE = "data-eco-island";
2
+ export declare const ECO_ISLAND_INTEGRATION_ATTRIBUTE = "data-eco-island-integration";
3
+ export declare const ECO_ISLAND_COMPONENT_ID_ATTRIBUTE = "data-eco-component-id";
4
+ export declare const ECO_ISLAND_COMPONENT_KEY_ATTRIBUTE = "data-eco-component-key";
5
+ export declare const ECO_ISLAND_PROPS_ATTRIBUTE = "data-eco-props";
6
+ export type IslandHostAttributesInput = {
7
+ integrationName: string;
8
+ componentInstanceId: string;
9
+ componentKey?: string;
10
+ props?: Record<string, unknown>;
11
+ existing?: Record<string, string>;
12
+ };
13
+ /**
14
+ * @remarks Presence marker for hydratable component hosts, similar in spirit to Astro's `astro-island`.
15
+ */
16
+ export declare function mergeIslandHostAttributes(input: {
17
+ integrationName: string;
18
+ componentInstanceId: string;
19
+ existing?: Record<string, string>;
20
+ }): Record<string, string>;
21
+ export declare function buildIslandHostAttributes(input: IslandHostAttributesInput): Record<string, string>;
22
+ export declare function isIslandHostElement(element: Element): boolean;
23
+ export declare function componentRenderHasIslandBootstrap(assets: Array<{
24
+ kind: string;
25
+ }> | undefined): boolean;
26
+ export declare function finalizeIslandComponentRender<T extends {
27
+ canAttachAttributes: boolean;
28
+ integrationName: string;
29
+ rootAttributes?: Record<string, string>;
30
+ assets?: Array<{
31
+ kind: string;
32
+ }>;
33
+ }>(input: {
34
+ integrationContext?: {
35
+ componentInstanceId?: string;
36
+ };
37
+ }, result: T): T;
@@ -0,0 +1,55 @@
1
+ export const ECO_ISLAND_HOST_ATTRIBUTE = 'data-eco-island';
2
+ export const ECO_ISLAND_INTEGRATION_ATTRIBUTE = 'data-eco-island-integration';
3
+ export const ECO_ISLAND_COMPONENT_ID_ATTRIBUTE = 'data-eco-component-id';
4
+ export const ECO_ISLAND_COMPONENT_KEY_ATTRIBUTE = 'data-eco-component-key';
5
+ export const ECO_ISLAND_PROPS_ATTRIBUTE = 'data-eco-props';
6
+ /**
7
+ * @remarks Presence marker for hydratable component hosts, similar in spirit to Astro's `astro-island`.
8
+ */
9
+ export function mergeIslandHostAttributes(input) {
10
+ return {
11
+ ...input.existing,
12
+ [ECO_ISLAND_HOST_ATTRIBUTE]: '',
13
+ [ECO_ISLAND_COMPONENT_ID_ATTRIBUTE]: input.existing?.[ECO_ISLAND_COMPONENT_ID_ATTRIBUTE] ?? input.componentInstanceId,
14
+ [ECO_ISLAND_INTEGRATION_ATTRIBUTE]: input.integrationName,
15
+ };
16
+ }
17
+ export function buildIslandHostAttributes(input) {
18
+ const attributes = mergeIslandHostAttributes(input);
19
+ if (input.componentKey) {
20
+ attributes[ECO_ISLAND_COMPONENT_KEY_ATTRIBUTE] = input.componentKey;
21
+ }
22
+ if (input.props !== undefined) {
23
+ attributes[ECO_ISLAND_PROPS_ATTRIBUTE] = btoa(JSON.stringify(input.props));
24
+ }
25
+ return attributes;
26
+ }
27
+ export function isIslandHostElement(element) {
28
+ if (element.hasAttribute(ECO_ISLAND_HOST_ATTRIBUTE)) {
29
+ return true;
30
+ }
31
+ if (element.hasAttribute(ECO_ISLAND_COMPONENT_KEY_ATTRIBUTE)) {
32
+ return true;
33
+ }
34
+ if (element.tagName.toLowerCase() === 'eco-island') {
35
+ return true;
36
+ }
37
+ return element.hasAttribute(ECO_ISLAND_COMPONENT_ID_ATTRIBUTE) && element.hasAttribute(ECO_ISLAND_PROPS_ATTRIBUTE);
38
+ }
39
+ export function componentRenderHasIslandBootstrap(assets) {
40
+ return (assets ?? []).some((asset) => asset.kind === 'script');
41
+ }
42
+ export function finalizeIslandComponentRender(input, result) {
43
+ const componentInstanceId = input.integrationContext?.componentInstanceId;
44
+ if (!componentInstanceId || !result.canAttachAttributes || !componentRenderHasIslandBootstrap(result.assets)) {
45
+ return result;
46
+ }
47
+ return {
48
+ ...result,
49
+ rootAttributes: mergeIslandHostAttributes({
50
+ integrationName: result.integrationName,
51
+ componentInstanceId,
52
+ existing: result.rootAttributes,
53
+ }),
54
+ };
55
+ }