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

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 (343) hide show
  1. package/README.md +1 -0
  2. package/package.json +90 -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/README.md +23 -0
  249. package/src/router/client/link-intent.js +55 -39
  250. package/src/router/client/link-intent.test.browser.js +49 -49
  251. package/src/router/client/link-navigation-policy.js +114 -93
  252. package/src/router/client/link-navigation-policy.test.browser.js +70 -80
  253. package/src/router/client/navigation-coordinator.js +235 -211
  254. package/src/router/client/navigation-lifecycle.d.ts +55 -0
  255. package/src/router/client/navigation-lifecycle.js +50 -0
  256. package/src/router/client/navigation-lifecycle.test.browser.d.ts +1 -0
  257. package/src/router/client/navigation-lifecycle.test.browser.js +118 -0
  258. package/src/router/server/route-registry.js +237 -242
  259. package/src/services/assets/asset-processing-service/asset-dependency-keys.js +64 -53
  260. package/src/services/assets/asset-processing-service/asset-processing.service.js +287 -303
  261. package/src/services/assets/asset-processing-service/asset.factory.js +78 -81
  262. package/src/services/assets/asset-processing-service/assets.types.js +1 -0
  263. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.js +57 -46
  264. package/src/services/assets/asset-processing-service/browser-runtime-entry-resolution.js +106 -69
  265. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.js +53 -53
  266. package/src/services/assets/asset-processing-service/finalize-processed-asset.js +4 -6
  267. package/src/services/assets/asset-processing-service/grouped-content-bundles.js +79 -80
  268. package/src/services/assets/asset-processing-service/index.js +8 -8
  269. package/src/services/assets/asset-processing-service/inline-content-script-body.js +22 -16
  270. package/src/services/assets/asset-processing-service/materialize-content-script-asset.js +21 -17
  271. package/src/services/assets/asset-processing-service/page-package.js +70 -72
  272. package/src/services/assets/asset-processing-service/processor.interface.js +7 -5
  273. package/src/services/assets/asset-processing-service/processor.registry.js +11 -14
  274. package/src/services/assets/asset-processing-service/processors/base/base-processor.js +59 -62
  275. package/src/services/assets/asset-processing-service/processors/base/base-script-processor.js +102 -117
  276. package/src/services/assets/asset-processing-service/processors/index.js +5 -5
  277. package/src/services/assets/asset-processing-service/processors/script/content-script.processor.js +110 -115
  278. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +95 -97
  279. package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +136 -135
  280. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +27 -29
  281. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +34 -40
  282. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.js +36 -35
  283. package/src/services/assets/asset-processing-service/resolve-integration-plugin.js +14 -16
  284. package/src/services/assets/asset-processing-service/ungrouped-dependency-processing.js +27 -39
  285. package/src/services/assets/browser-bundle.service.js +52 -47
  286. package/src/services/cache/cache.types.js +5 -0
  287. package/src/services/cache/index.js +6 -7
  288. package/src/services/cache/memory-cache-store.js +101 -89
  289. package/src/services/cache/page-cache-service.js +156 -145
  290. package/src/services/cache/page-request-cache-coordinator.service.js +102 -102
  291. package/src/services/html/html-rewriter-provider.service.js +61 -61
  292. package/src/services/html/html-transformer.service.js +249 -266
  293. package/src/services/invalidation/development-invalidation.service.js +252 -241
  294. package/src/services/module-loading/app-module-loader.service.js +1 -0
  295. package/src/services/module-loading/app-server-module-transpiler.service.js +107 -101
  296. package/src/services/module-loading/host-module-loader-registry.js +6 -11
  297. package/src/services/module-loading/module-loading-types.js +1 -0
  298. package/src/services/module-loading/page-module-import.service.js +235 -243
  299. package/src/services/module-loading/route-module-build-cache-registry.js +47 -33
  300. package/src/services/module-loading/route-module-build-cache.js +3 -38
  301. package/src/services/module-loading/route-module-build-cache.store.js +195 -200
  302. package/src/services/module-loading/route-module-build-manifest.js +61 -74
  303. package/src/services/module-loading/route-module-dependency-hasher.js +136 -110
  304. package/src/services/module-loading/server-module-transpiler.service.js +68 -62
  305. package/src/services/module-loading/source-module-support.js +15 -7
  306. package/src/services/runtime-state/dev-graph.service.js +165 -157
  307. package/src/services/runtime-state/entrypoint-dependency-graph.service.js +85 -76
  308. package/src/services/runtime-state/server-invalidation-state.service.js +38 -30
  309. package/src/services/validation/schema-validation-service.js +157 -95
  310. package/src/services/validation/standard-schema.types.js +1 -0
  311. package/src/services/validation/validate-standard-schema.js +13 -13
  312. package/src/static-site-generator/production-page-browser-graph-prebuild.js +35 -19
  313. package/src/static-site-generator/sitemap-routes.js +33 -28
  314. package/src/static-site-generator/sitemap.js +38 -29
  315. package/src/static-site-generator/static-build-invalidation.js +34 -56
  316. package/src/static-site-generator/static-export-context.js +1 -0
  317. package/src/static-site-generator/static-site-generator.js +517 -553
  318. package/src/types/internal-types.d.ts +19 -3
  319. package/src/types/internal-types.js +1 -0
  320. package/src/types/public-types.d.ts +16 -46
  321. package/src/types/public-types.js +1 -0
  322. package/src/utils/deep-merge.js +39 -26
  323. package/src/utils/ecopages-route-info.js +27 -23
  324. package/src/utils/hash.js +3 -6
  325. package/src/utils/html-escaping.js +21 -8
  326. package/src/utils/html.js +1 -4
  327. package/src/utils/invariant.js +11 -10
  328. package/src/utils/locals-utils.js +32 -22
  329. package/src/utils/parse-cli-args.js +73 -65
  330. package/src/utils/path-pattern.js +38 -23
  331. package/src/utils/path-utils.module.js +8 -11
  332. package/src/utils/resolve-entry-file.js +70 -38
  333. package/src/utils/resolve-work-dir.js +23 -27
  334. package/src/utils/runtime.js +24 -33
  335. package/src/utils/server-utils.module.js +60 -52
  336. package/src/utils/static-file-extensions.js +52 -46
  337. package/src/watchers/project-watcher-ignore.js +16 -15
  338. package/src/watchers/project-watcher.js +385 -366
  339. package/src/watchers/project-watcher.test-helpers.js +45 -50
  340. package/src/adapters/shared/hmr/hmr-entrypoint-registrar.d.ts +0 -56
  341. package/src/adapters/shared/hmr/hmr-entrypoint-registrar.js +0 -106
  342. package/src/build/cache/dev-browser-script-cache.d.ts +0 -34
  343. package/src/build/cache/dev-browser-script-cache.js +0 -90
@@ -1,172 +1,414 @@
1
- import { cachedParseSync } from "../cache/module-parse-cache.js";
2
- import { prependJsxImportSourceIfMissing } from "./jsx-import-source.utils.js";
3
- import { rapidhash } from "../utils/hash.js";
4
- import {
5
- createEcoBuildPluginFromSourceTransform,
6
- createVitePluginFromSourceTransform
7
- } from "./source-transform.js";
1
+ /**
2
+ * Bun plugin that auto-injects `__eco` metadata into EcoComponent config objects.
3
+ *
4
+ * This plugin uses AST parsing (via oxc-parser) to reliably inject the `__eco` property
5
+ * into EcoComponent config objects at import time. The injected metadata contains:
6
+ * - `dir`: The directory path of the component file (used for dependency resolution)
7
+ * - `integration`: The integration type (e.g., 'react', 'kitajs', 'ghtml', 'lit')
8
+ *
9
+ * The plugin intercepts file loading for all configured integration extensions and
10
+ * transforms component configs before they are executed.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * // Before transformation:
15
+ * export default eco.page({
16
+ * render: () => '<div>Hello</div>',
17
+ * });
18
+ *
19
+ * // After transformation:
20
+ * export default eco.page({
21
+ * __eco: { id: "<hash>", file: "/path/to/pages/index.tsx", integration: "react" },
22
+ * render: () => '<div>Hello</div>',
23
+ * });
24
+ * ```
25
+ *
26
+ * @module eco-component-meta-plugin
27
+ */
28
+ import { cachedParseSync } from '../cache/module-parse-cache.js';
29
+ import { prependJsxImportSourceIfMissing } from './jsx-import-source.utils.js';
30
+ import { rapidhash } from '../utils/hash.js';
31
+ import { createEcoBuildPluginFromSourceTransform, createVitePluginFromSourceTransform, } from './source-transform.js';
32
+ /**
33
+ * Pattern to match regex special characters that need escaping.
34
+ * Used when building the file extension filter pattern.
35
+ */
8
36
  const REGEX_SPECIAL_CHARS = /[.*+?^${}()|[\]\\]/g;
9
- const VALID_LOADER_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
37
+ /**
38
+ * Set of valid Bun loader extensions.
39
+ * Only files with these base extensions can be processed by Bun's loader system.
40
+ */
41
+ const VALID_LOADER_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx']);
42
+ /**
43
+ * Checks if an extension can be handled by a valid Bun loader.
44
+ *
45
+ * Compound extensions like `.kita.tsx` are valid because the final
46
+ * extension is `.tsx`, which Bun can process.
47
+ *
48
+ * @param ext - The file extension to check (e.g., '.tsx', '.kita.tsx')
49
+ * @returns `true` if the extension ends with a valid loader extension
50
+ */
10
51
  function hasValidLoaderExtension(ext) {
11
- for (const validExt of VALID_LOADER_EXTENSIONS) {
12
- if (ext.endsWith(validExt)) {
13
- return true;
52
+ for (const validExt of VALID_LOADER_EXTENSIONS) {
53
+ if (ext.endsWith(validExt)) {
54
+ return true;
55
+ }
14
56
  }
15
- }
16
- return false;
57
+ return false;
17
58
  }
18
59
  function buildExtensionToIntegrationMap(integrations) {
19
- const mapping = [];
20
- for (const integration of integrations) {
21
- for (const ext of integration.extensions) {
22
- mapping.push([
23
- ext,
24
- {
25
- name: integration.name,
26
- jsxImportSource: integration.jsxImportSource
60
+ const mapping = [];
61
+ for (const integration of integrations) {
62
+ for (const ext of integration.extensions) {
63
+ mapping.push([
64
+ ext,
65
+ {
66
+ name: integration.name,
67
+ jsxImportSource: integration.jsxImportSource,
68
+ },
69
+ ]);
27
70
  }
28
- ]);
29
71
  }
30
- }
31
- mapping.sort((a, b) => b[0].length - a[0].length);
32
- return mapping;
72
+ mapping.sort((a, b) => b[0].length - a[0].length);
73
+ return mapping;
33
74
  }
75
+ /**
76
+ * Detects the integration type for a file based on its extension.
77
+ *
78
+ * Uses the pre-sorted extension-to-integration map to find the most
79
+ * specific matching extension.
80
+ *
81
+ * @param filePath - Absolute path to the file
82
+ * @param extensionToIntegration - Pre-built extension mapping from buildExtensionToIntegrationMap
83
+ * @returns The integration identifier (e.g., 'react', 'kitajs', 'lit', 'ghtml')
84
+ */
34
85
  function detectIntegration(filePath, extensionToIntegration) {
35
- for (const [ext, integration] of extensionToIntegration) {
36
- if (filePath.endsWith(ext)) {
37
- return integration;
86
+ for (const [ext, integration] of extensionToIntegration) {
87
+ if (filePath.endsWith(ext)) {
88
+ return integration;
89
+ }
38
90
  }
39
- }
40
- return { name: "ghtml" };
91
+ return { name: 'ghtml' };
41
92
  }
93
+ /**
94
+ * Creates a RegExp pattern that matches files with any of the configured extensions.
95
+ *
96
+ * The pattern also matches optional query strings (e.g., `file.tsx?update=123`)
97
+ * which are used for cache-busting in development mode.
98
+ *
99
+ * @param extensions - Array of file extensions to match
100
+ * @returns RegExp pattern for use with Bun's onLoad filter
101
+ * @throws Error if no extensions are provided
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * const pattern = createExtensionPattern(['.tsx', '.kita.tsx']);
106
+ * pattern.test('component.tsx'); // true
107
+ * pattern.test('component.tsx?v=123'); // true
108
+ * pattern.test('component.ts'); // false
109
+ * ```
110
+ */
42
111
  function createExtensionPattern(extensions) {
43
- if (extensions.length === 0) {
44
- throw new Error("[eco-component-meta-plugin] No extensions configured. At least one integration is required.");
45
- }
46
- const uniqueExtensions = [...new Set(extensions)];
47
- const escaped = uniqueExtensions.map((ext) => ext.replace(REGEX_SPECIAL_CHARS, "\\$&"));
48
- return new RegExp(`(${escaped.join("|")})(\\?.*)?$`);
112
+ if (extensions.length === 0) {
113
+ throw new Error('[eco-component-meta-plugin] No extensions configured. At least one integration is required.');
114
+ }
115
+ const uniqueExtensions = [...new Set(extensions)];
116
+ const escaped = uniqueExtensions.map((ext) => ext.replace(REGEX_SPECIAL_CHARS, '\\$&'));
117
+ return new RegExp(`(${escaped.join('|')})(\\?.*)?$`);
49
118
  }
50
- function createEcoComponentMetaTransform(options) {
51
- const allExtensions = options.config.integrations.flatMap((integration) => integration.extensions).filter(hasValidLoaderExtension);
52
- if (allExtensions.length === 0) {
53
- throw new Error("[eco-component-meta-plugin] No extensions configured. At least one integration is required.");
54
- }
55
- const extensionPattern = createExtensionPattern(allExtensions);
56
- const extensionToIntegration = buildExtensionToIntegrationMap(options.config.integrations);
57
- return {
58
- name: "eco-component-meta-plugin",
59
- enforce: "pre",
60
- filter: extensionPattern,
61
- transform(code, id) {
62
- const integration = detectIntegration(id, extensionToIntegration);
63
- return {
64
- code: prependJsxImportSourceIfMissing(
65
- injectEcoMeta(code, id, integration.name),
66
- integration.jsxImportSource
67
- )
68
- };
119
+ /**
120
+ * Creates the bundler-neutral metadata transform used by Ecopages loaders and
121
+ * Vite-compatible adapters.
122
+ */
123
+ export function createEcoComponentMetaTransform(options) {
124
+ const allExtensions = options.config.integrations
125
+ .flatMap((integration) => integration.extensions)
126
+ .filter(hasValidLoaderExtension);
127
+ if (allExtensions.length === 0) {
128
+ throw new Error('[eco-component-meta-plugin] No extensions configured. At least one integration is required.');
69
129
  }
70
- };
130
+ const extensionPattern = createExtensionPattern(allExtensions);
131
+ const extensionToIntegration = buildExtensionToIntegrationMap(options.config.integrations);
132
+ return {
133
+ name: 'eco-component-meta-plugin',
134
+ enforce: 'pre',
135
+ filter: extensionPattern,
136
+ transform(code, id) {
137
+ const integration = detectIntegration(id, extensionToIntegration);
138
+ return {
139
+ code: prependJsxImportSourceIfMissing(injectEcoMeta(code, id, integration.name), integration.jsxImportSource),
140
+ };
141
+ },
142
+ };
71
143
  }
72
- function createEcoComponentMetaPlugin(options) {
73
- return createEcoBuildPluginFromSourceTransform(createEcoComponentMetaTransform(options));
144
+ /**
145
+ * Creates a build plugin that auto-injects `__eco` metadata into EcoComponent config objects.
146
+ *
147
+ * This plugin intercepts file loading for all integration-compatible files and:
148
+ * 1. Strips any query string from the file path (for dev mode cache-busting)
149
+ * 2. Reads the file contents
150
+ * 3. Parses the AST using oxc-parser to find injection points
151
+ * 4. Injects `__eco: { id: "...", file: "...", integration: "..." }` into config objects
152
+ * 5. Returns the transformed content with the appropriate loader
153
+ *
154
+ * Supported patterns:
155
+ * - `eco.page({ ... })` - Page component declarations
156
+ * - `eco.component({ ... })` - Reusable component declarations
157
+ * - `eco.html({ ... })` - HTML shell declarations
158
+ * - `eco.layout({ ... })` - Layout declarations
159
+ * - `Component.config = { ... }` - Config assignment pattern
160
+ * - `config: { ... }` - Config property in object literals
161
+ * - `export const config = { ... }` - Exported config declarations
162
+ *
163
+ * @param options - Plugin options containing the EcoPages config
164
+ * @returns A build plugin instance ready for registration
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * import { createEcoComponentMetaPlugin } from '@ecopages/core';
169
+ *
170
+ * const plugin = createEcoComponentMetaPlugin({ config: appConfig });
171
+ * appConfig.loaders.set(plugin.name, plugin);
172
+ * ```
173
+ */
174
+ export function createEcoComponentMetaPlugin(options) {
175
+ return createEcoBuildPluginFromSourceTransform(createEcoComponentMetaTransform(options));
74
176
  }
75
- function createEcoComponentMetaVitePlugin(options) {
76
- return createVitePluginFromSourceTransform(createEcoComponentMetaTransform(options));
177
+ /**
178
+ * Creates a Vite-compatible metadata injection plugin from the shared
179
+ * Ecopages source-transform primitive.
180
+ */
181
+ export function createEcoComponentMetaVitePlugin(options) {
182
+ return createVitePluginFromSourceTransform(createEcoComponentMetaTransform(options));
77
183
  }
184
+ /**
185
+ * Recursively walks the AST (Abstract Syntax Tree) to find all injection points for `__eco` metadata.
186
+ *
187
+ * ## What is an AST?
188
+ *
189
+ * An AST is a tree representation of source code. Instead of treating code as text,
190
+ * a parser breaks it down into a structured tree where each node represents a
191
+ * syntactic construct (variable, function call, object, etc.).
192
+ *
193
+ * For example, this code:
194
+ * ```typescript
195
+ * eco.page({ render: () => 'hi' })
196
+ * ```
197
+ *
198
+ * Becomes an AST like:
199
+ * ```
200
+ * CallExpression
201
+ * ├── callee: MemberExpression
202
+ * │ ├── object: Identifier (name: "eco")
203
+ * │ └── property: Identifier (name: "page")
204
+ * └── arguments: [
205
+ * └── ObjectExpression (start: 9) <-- We inject here at position 10 (after "{")
206
+ * └── properties: [...]
207
+ * ]
208
+ * ```
209
+ *
210
+ * ## How this function works
211
+ *
212
+ * 1. **Recursive traversal**: Visits every node in the tree, checking each one
213
+ * 2. **Pattern matching**: Checks if the current node matches one of our target patterns
214
+ * 3. **Position tracking**: When a match is found, records the `start` position of the
215
+ * config object (the character index in the original source where `{` appears)
216
+ * 4. **Insertion offset**: Adds +1 to insert right after the opening `{`
217
+ *
218
+ * ## Supported patterns
219
+ *
220
+ * | Pattern | AST Node Type | Example | File Types |
221
+ * |---------|---------------|---------|------------|
222
+ * | `eco.page({...})` | CallExpression | `export default eco.page({ render: () => 'hi' })` | All |
223
+ * | `eco.component({...})` | CallExpression | `export const Btn = eco.component({ render: () => '<button/>' })` | All |
224
+ * | `eco.html({...})` | CallExpression | `export default eco.html({ render: () => '<html />' })` | All |
225
+ * | `eco.layout({...})` | CallExpression | `export const MainLayout = eco.layout({ render: () => '<main />' })` | All |
226
+ * | `X.config = {...}` | AssignmentExpression | `MyComponent.config = { dependencies: [] }` | All |
227
+ * | `config: {...}` | ObjectProperty | `const X: EcoComponent = { config: {...} }` | EcoComponent-typed only |
228
+ *
229
+ * ## Why AST over regex?
230
+ *
231
+ * Regex would fail on edge cases like:
232
+ * - `eco.page<ComplexType<(arg: string) => void>>({...})` - generics with arrows
233
+ * - `// eco.page({ commented out })` - comments
234
+ * - `const str = "eco.page({ in a string })"` - string literals
235
+ * - Nested objects that look like config patterns
236
+ *
237
+ * AST parsing understands the actual code structure, not just text patterns.
238
+ *
239
+ * @param node - Current AST node being visited (starts with the root Program node)
240
+ * @param insertions - Array to collect insertion points (mutated by this function)
241
+ * @param injection - The injection text to insert at each point (e.g., ` __eco: {...},`)
242
+ * @param isInsideEcoComponent - Whether we're inside an EcoComponent-typed declaration
243
+ */
78
244
  function findInjectionPoints(node, insertions, injection, isInsideEcoComponent = false) {
79
- if (!node || typeof node !== "object") return;
80
- const n = node;
81
- if (n.type === "CallExpression") {
82
- const callee = n.callee;
83
- if (callee?.type === "MemberExpression" || callee?.type === "StaticMemberExpression") {
84
- const obj = callee.object;
85
- const prop = callee.property;
86
- if (obj?.type === "Identifier" && obj?.name === "eco" && (prop?.name === "page" || prop?.name === "component" || prop?.name === "html" || prop?.name === "layout")) {
87
- const args = n.arguments;
88
- const firstArg = args?.[0];
89
- if (firstArg?.type === "ObjectExpression") {
90
- const start = firstArg.start;
91
- if (typeof start === "number") {
92
- insertions.push({ position: start + 1, text: injection });
93
- }
245
+ if (!node || typeof node !== 'object')
246
+ return;
247
+ const n = node;
248
+ /**
249
+ * Pattern 1: eco.page({...}), eco.component({...}), eco.html({...}), or eco.layout({...})
250
+ * AST structure: CallExpression with MemberExpression callee where object is "eco"
251
+ */
252
+ if (n.type === 'CallExpression') {
253
+ const callee = n.callee;
254
+ /**
255
+ * MemberExpression represents "something.property" syntax.
256
+ * StaticMemberExpression is oxc's variant for computed vs non-computed access.
257
+ */
258
+ if (callee?.type === 'MemberExpression' || callee?.type === 'StaticMemberExpression') {
259
+ const obj = callee.object;
260
+ const prop = callee.property;
261
+ /** Check: is this `eco.page(...)`, `eco.component(...)`, `eco.html(...)`, or `eco.layout(...)`? */
262
+ if (obj?.type === 'Identifier' &&
263
+ obj?.name === 'eco' &&
264
+ (prop?.name === 'page' ||
265
+ prop?.name === 'component' ||
266
+ prop?.name === 'html' ||
267
+ prop?.name === 'layout')) {
268
+ /** Get the first argument - should be an object literal {...} */
269
+ const args = n.arguments;
270
+ const firstArg = args?.[0];
271
+ if (firstArg?.type === 'ObjectExpression') {
272
+ /**
273
+ * `start` is the character index where this object begins (the "{").
274
+ * Insert at position+1 to place content right after "{".
275
+ */
276
+ const start = firstArg.start;
277
+ if (typeof start === 'number') {
278
+ insertions.push({ position: start + 1, text: injection });
279
+ }
280
+ }
281
+ }
94
282
  }
95
- }
96
283
  }
97
- }
98
- if (n.type === "AssignmentExpression") {
99
- const left = n.left;
100
- const right = n.right;
101
- if (left?.type === "MemberExpression" || left?.type === "StaticMemberExpression") {
102
- const prop = left.property;
103
- if (prop?.name === "config" && right?.type === "ObjectExpression") {
104
- const start = right.start;
105
- if (typeof start === "number") {
106
- insertions.push({ position: start + 1, text: injection });
284
+ /**
285
+ * Pattern 2: Something.config = {...}
286
+ * AST structure: AssignmentExpression with MemberExpression left side ending in "config"
287
+ * This pattern is safe for all files because it requires a qualifier (e.g., MyComponent.config).
288
+ */
289
+ if (n.type === 'AssignmentExpression') {
290
+ const left = n.left;
291
+ const right = n.right;
292
+ /** Case: MyComponent.config = {...} */
293
+ if (left?.type === 'MemberExpression' || left?.type === 'StaticMemberExpression') {
294
+ const prop = left.property;
295
+ if (prop?.name === 'config' && right?.type === 'ObjectExpression') {
296
+ const start = right.start;
297
+ if (typeof start === 'number') {
298
+ insertions.push({ position: start + 1, text: injection });
299
+ }
300
+ }
107
301
  }
108
- }
109
302
  }
110
- }
111
- if (n.type === "ObjectProperty" || n.type === "Property") {
112
- const key = n.key;
113
- const value = n.value;
114
- if ((key?.type === "Identifier" || key?.type === "IdentifierName") && key?.name === "config" && value?.type === "ObjectExpression" && isInsideEcoComponent) {
115
- const start = value.start;
116
- if (typeof start === "number") {
117
- insertions.push({ position: start + 1, text: injection });
118
- }
303
+ /**
304
+ * Pattern 3: { config: {...} } - config as an object property inside EcoComponent
305
+ * AST structure: ObjectProperty/Property with key "config" and value as ObjectExpression
306
+ *
307
+ * This pattern is matched when the parent VariableDeclarator has a type annotation
308
+ * containing "EcoComponent", e.g., `const X: EcoComponent = { config: {...} }`
309
+ *
310
+ * We track whether we're inside an EcoComponent-typed object via the `isInsideEcoComponent` flag.
311
+ */
312
+ if (n.type === 'ObjectProperty' || n.type === 'Property') {
313
+ const key = n.key;
314
+ const value = n.value;
315
+ if ((key?.type === 'Identifier' || key?.type === 'IdentifierName') &&
316
+ key?.name === 'config' &&
317
+ value?.type === 'ObjectExpression' &&
318
+ isInsideEcoComponent) {
319
+ const start = value.start;
320
+ if (typeof start === 'number') {
321
+ insertions.push({ position: start + 1, text: injection });
322
+ }
323
+ }
119
324
  }
120
- }
121
- let childIsInsideEcoComponent = isInsideEcoComponent;
122
- if (n.type === "VariableDeclarator") {
123
- const id = n.id;
124
- const typeAnnotation = id?.typeAnnotation;
125
- if (typeAnnotation) {
126
- const typeStr = JSON.stringify(typeAnnotation);
127
- if (typeStr.includes("EcoComponent")) {
128
- childIsInsideEcoComponent = true;
129
- }
325
+ /**
326
+ * Check if we're entering an EcoComponent-typed variable declaration.
327
+ * This sets a flag for child nodes to know they're inside an EcoComponent.
328
+ *
329
+ * Type annotation is on the `id` (Identifier), not the VariableDeclarator directly.
330
+ * e.g., `const X: EcoComponent = {...}` has the annotation on the "X" Identifier.
331
+ */
332
+ let childIsInsideEcoComponent = isInsideEcoComponent;
333
+ if (n.type === 'VariableDeclarator') {
334
+ const id = n.id;
335
+ const typeAnnotation = id?.typeAnnotation;
336
+ if (typeAnnotation) {
337
+ const typeStr = JSON.stringify(typeAnnotation);
338
+ if (typeStr.includes('EcoComponent')) {
339
+ childIsInsideEcoComponent = true;
340
+ }
341
+ }
130
342
  }
131
- }
132
- for (const key in n) {
133
- if (key === "start" || key === "end" || key === "type") continue;
134
- const value = n[key];
135
- if (Array.isArray(value)) {
136
- for (const child of value) {
137
- findInjectionPoints(child, insertions, injection, childIsInsideEcoComponent);
138
- }
139
- } else if (typeof value === "object" && value !== null) {
140
- findInjectionPoints(value, insertions, injection, childIsInsideEcoComponent);
343
+ /**
344
+ * Recursive traversal: Visit all child nodes in the AST.
345
+ *
346
+ * This is how we "walk" the tree - for each property of the current node,
347
+ * if it's an array (like `body` containing statements) or an object (like `callee`),
348
+ * we recursively call findInjectionPoints on it.
349
+ * We skip metadata properties (start, end, type) that don't contain child nodes.
350
+ */
351
+ for (const key in n) {
352
+ if (key === 'start' || key === 'end' || key === 'type')
353
+ continue;
354
+ const value = n[key];
355
+ if (Array.isArray(value)) {
356
+ for (const child of value) {
357
+ findInjectionPoints(child, insertions, injection, childIsInsideEcoComponent);
358
+ }
359
+ }
360
+ else if (typeof value === 'object' && value !== null) {
361
+ findInjectionPoints(value, insertions, injection, childIsInsideEcoComponent);
362
+ }
141
363
  }
142
- }
143
364
  }
144
- function injectEcoMeta(contents, filePath, integration) {
145
- const result = cachedParseSync(filePath, contents);
146
- if (result.errors.length > 0) {
147
- console.warn(`[eco-component-meta-plugin] Parse errors in ${filePath}:`, result.errors);
148
- return contents;
149
- }
150
- const ast = result.program;
151
- const id = rapidhash(filePath).toString(36);
152
- const injection = ` __eco: { id: "${id}", file: "${filePath}", integration: "${integration}" },`;
153
- const insertions = [];
154
- findInjectionPoints(ast, insertions, injection);
155
- if (insertions.length === 0) {
156
- return contents;
157
- }
158
- insertions.sort((a, b) => b.position - a.position);
159
- let transformed = contents;
160
- for (const { position, text } of insertions) {
161
- transformed = transformed.slice(0, position) + text + transformed.slice(position);
162
- }
163
- return transformed;
365
+ /**
366
+ * Injects `__eco` metadata into EcoComponent config objects in file content.
367
+ *
368
+ * Uses oxc-parser for robust AST-based code analysis, which handles edge cases
369
+ * that regex-based approaches would miss (e.g., complex generics, nested objects,
370
+ * comments, string literals containing similar patterns).
371
+ *
372
+ * The injection is performed by:
373
+ * 1. Parsing the source code into an AST
374
+ * 2. Walking the AST to find all config object patterns
375
+ * 3. Collecting insertion points (sorted in reverse order to preserve positions)
376
+ * 4. Inserting the `__eco` property at each point
377
+ *
378
+ * @param contents - The source code content to transform
379
+ * @param filePath - Absolute path to the file (used to derive the directory)
380
+ * @param integration - The integration identifier for this file type
381
+ * @returns Transformed source code with `__eco` injected, or original if no patterns found
382
+ *
383
+ * @example
384
+ * ```typescript
385
+ * const result = injectEcoMeta(
386
+ * 'export default eco.page({ render: () => "<div>Hi</div>" });',
387
+ * '/app/src/pages/index.tsx',
388
+ * 'react'
389
+ * );
390
+ * // Result: 'export default eco.page({ __eco: { id: "<hash>", file: "/app/src/pages/index.tsx", integration: "react" }, render: () => "<div>Hi</div>" });'
391
+ * ```
392
+ */
393
+ export function injectEcoMeta(contents, filePath, integration) {
394
+ const result = cachedParseSync(filePath, contents);
395
+ if (result.errors.length > 0) {
396
+ console.warn(`[eco-component-meta-plugin] Parse errors in ${filePath}:`, result.errors);
397
+ return contents;
398
+ }
399
+ const ast = result.program;
400
+ const id = rapidhash(filePath).toString(36);
401
+ const injection = ` __eco: { id: "${id}", file: "${filePath}", integration: "${integration}" },`;
402
+ const insertions = [];
403
+ findInjectionPoints(ast, insertions, injection);
404
+ if (insertions.length === 0) {
405
+ return contents;
406
+ }
407
+ insertions.sort((a, b) => b.position - a.position);
408
+ let transformed = contents;
409
+ for (const { position, text } of insertions) {
410
+ transformed = transformed.slice(0, position) + text + transformed.slice(position);
411
+ }
412
+ return transformed;
164
413
  }
165
- var eco_component_meta_plugin_default = createEcoComponentMetaPlugin;
166
- export {
167
- createEcoComponentMetaPlugin,
168
- createEcoComponentMetaTransform,
169
- createEcoComponentMetaVitePlugin,
170
- eco_component_meta_plugin_default as default,
171
- injectEcoMeta
172
- };
414
+ export default createEcoComponentMetaPlugin;
@@ -1,39 +1,53 @@
1
- import { readFileSync } from "node:fs";
2
- import path from "node:path";
3
- import { createJsxImportSourcePragma, filterJsxSourceExtensions } from "./jsx-import-source.utils.js";
4
- function createForeignJsxOverridePlugin(options) {
5
- const extensions = filterJsxSourceExtensions(options.foreignExtensions);
6
- const excludedExtensions = filterJsxSourceExtensions(options.excludeExtensions ?? []);
7
- if (extensions.length === 0) {
8
- return {
9
- name: options.name ?? "foreign-jsx-override",
10
- setup() {
11
- }
12
- };
13
- }
14
- const pragma = createJsxImportSourcePragma(options.hostJsxImportSource);
15
- const filter = new RegExp(`(${extensions.map((e) => e.replace(".", "\\.")).join("|")})$`);
16
- return {
17
- name: options.name ?? "foreign-jsx-override",
18
- setup(build) {
19
- build.onLoad({ filter }, (args) => {
20
- if (excludedExtensions.some((extension) => args.path.endsWith(extension))) {
21
- return void 0;
22
- }
23
- const source = readFileSync(args.path, "utf-8");
24
- const loader = args.path.endsWith(".jsx") ? "jsx" : "tsx";
25
- if (source.includes("@jsxImportSource")) {
26
- return void 0;
27
- }
1
+ import { readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createJsxImportSourcePragma, filterJsxSourceExtensions } from './jsx-import-source.utils.js';
4
+ /**
5
+ * Build plugin that prepends a `@jsxImportSource` pragma to foreign integration
6
+ * files bundled into a host integration's client graph.
7
+ *
8
+ * When a host integration (e.g. React) bundles a component file that belongs to
9
+ * another JSX integration (e.g. `.kita.tsx`), that file inherits the project
10
+ * `tsconfig` JSX runtime which produces the wrong output (HTML strings instead
11
+ * of framework elements). This plugin rewrites the source to explicitly target
12
+ * the host's JSX factory so the bundler compiles every JSX expression into the
13
+ * correct element creation calls.
14
+ *
15
+ * The plugin is intentionally framework-agnostic: any integration that does
16
+ * client-side bundling can use it by passing its own `jsxImportSource` and the
17
+ * set of foreign extensions collected from the app config.
18
+ *
19
+ * When no JSX-bearing foreign extensions are present, the returned plugin is a
20
+ * no-op so integrations can register it unconditionally.
21
+ */
22
+ export function createForeignJsxOverridePlugin(options) {
23
+ const extensions = filterJsxSourceExtensions(options.foreignExtensions);
24
+ const excludedExtensions = filterJsxSourceExtensions(options.excludeExtensions ?? []);
25
+ if (extensions.length === 0) {
28
26
  return {
29
- contents: pragma + source,
30
- loader,
31
- resolveDir: path.dirname(args.path)
27
+ name: options.name ?? 'foreign-jsx-override',
28
+ setup() { },
32
29
  };
33
- });
34
30
  }
35
- };
31
+ const pragma = createJsxImportSourcePragma(options.hostJsxImportSource);
32
+ const filter = new RegExp(`(${extensions.map((e) => e.replace('.', '\\.')).join('|')})$`);
33
+ return {
34
+ name: options.name ?? 'foreign-jsx-override',
35
+ setup(build) {
36
+ build.onLoad({ filter }, (args) => {
37
+ if (excludedExtensions.some((extension) => args.path.endsWith(extension))) {
38
+ return undefined;
39
+ }
40
+ const source = readFileSync(args.path, 'utf-8');
41
+ const loader = args.path.endsWith('.jsx') ? 'jsx' : 'tsx';
42
+ if (source.includes('@jsxImportSource')) {
43
+ return undefined;
44
+ }
45
+ return {
46
+ contents: pragma + source,
47
+ loader,
48
+ resolveDir: path.dirname(args.path),
49
+ };
50
+ });
51
+ },
52
+ };
36
53
  }
37
- export {
38
- createForeignJsxOverridePlugin
39
- };