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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/README.md +1 -1
  2. package/package.json +163 -3
  3. package/src/adapters/abstract/application-adapter.d.ts +104 -5
  4. package/src/adapters/abstract/application-adapter.js +125 -1
  5. package/src/adapters/abstract/server-adapter.d.ts +6 -2
  6. package/src/adapters/abstract/ws-pattern-matcher.d.ts +60 -0
  7. package/src/adapters/abstract/ws-pattern-matcher.js +61 -0
  8. package/src/adapters/bun/client-bridge.d.ts +1 -0
  9. package/src/adapters/bun/client-bridge.js +3 -0
  10. package/src/adapters/bun/create-app.d.ts +6 -4
  11. package/src/adapters/bun/create-app.js +65 -43
  12. package/src/adapters/bun/hmr-manager.d.ts +2 -1
  13. package/src/adapters/bun/hmr-manager.js +2 -2
  14. package/src/adapters/bun/server-adapter.d.ts +90 -22
  15. package/src/adapters/bun/server-adapter.js +265 -84
  16. package/src/adapters/bun/static-preview-host.js +25 -20
  17. package/src/adapters/create-app.d.ts +13 -11
  18. package/src/adapters/create-app.js +2 -51
  19. package/src/adapters/index.d.ts +1 -1
  20. package/src/adapters/index.js +1 -2
  21. package/src/adapters/node/create-app.d.ts +5 -1
  22. package/src/adapters/node/create-app.js +51 -12
  23. package/src/adapters/node/node-client-bridge.d.ts +1 -0
  24. package/src/adapters/node/node-client-bridge.js +3 -0
  25. package/src/adapters/node/node-hmr-manager.d.ts +2 -1
  26. package/src/adapters/node/node-hmr-manager.js +2 -2
  27. package/src/adapters/node/server-adapter-dependencies.js +4 -0
  28. package/src/adapters/node/server-adapter.d.ts +62 -20
  29. package/src/adapters/node/server-adapter.js +161 -131
  30. package/src/adapters/node/static-content-server.d.ts +1 -0
  31. package/src/adapters/node/static-content-server.js +10 -2
  32. package/src/adapters/node/static-preview-host.js +27 -12
  33. package/src/adapters/shared/bun-user-websocket-lifecycle.d.ts +22 -0
  34. package/src/adapters/shared/bun-user-websocket-lifecycle.js +96 -0
  35. package/src/adapters/shared/copy-runtime-public-dir.d.ts +6 -0
  36. package/src/adapters/shared/copy-runtime-public-dir.js +24 -0
  37. package/src/adapters/shared/fs-server-response-matcher.d.ts +2 -0
  38. package/src/adapters/shared/fs-server-response-matcher.js +40 -18
  39. package/src/adapters/shared/hmr-entrypoint-registrar.d.ts +0 -2
  40. package/src/adapters/shared/hmr-entrypoint-registrar.js +3 -26
  41. package/src/adapters/shared/hmr-html-response.d.ts +1 -1
  42. package/src/adapters/shared/hmr-html-response.js +4 -1
  43. package/src/adapters/shared/node-http-websocket-upgrades.d.ts +21 -0
  44. package/src/adapters/shared/node-http-websocket-upgrades.js +100 -0
  45. package/src/adapters/shared/port-manager.d.ts +116 -0
  46. package/src/adapters/shared/port-manager.js +196 -0
  47. package/src/adapters/shared/runtime-app-bootstrap.d.ts +2 -1
  48. package/src/adapters/shared/runtime-app-bootstrap.js +3 -6
  49. package/src/adapters/shared/runtime-server-lifecycle.d.ts +38 -0
  50. package/src/adapters/shared/runtime-server-lifecycle.js +43 -0
  51. package/src/adapters/shared/server-adapter.d.ts +2 -1
  52. package/src/adapters/shared/server-adapter.js +13 -6
  53. package/src/adapters/shared/server-route-handler.d.ts +4 -1
  54. package/src/adapters/shared/server-route-handler.js +5 -2
  55. package/src/adapters/shared/server-static-builder.d.ts +23 -32
  56. package/src/adapters/shared/server-static-builder.js +97 -73
  57. package/src/adapters/shared/shared-hmr-manager.d.ts +28 -0
  58. package/src/adapters/shared/shared-hmr-manager.js +119 -9
  59. package/src/adapters/shared/static-preview-host.d.ts +5 -0
  60. package/src/adapters/shared/websocket-lifecycle.d.ts +24 -0
  61. package/src/adapters/shared/websocket-lifecycle.js +55 -0
  62. package/src/build/README.md +77 -14
  63. package/src/build/app-build-manifest-runtime.d.ts +10 -0
  64. package/src/build/app-build-manifest-runtime.js +90 -0
  65. package/src/build/build-adapter.d.ts +17 -319
  66. package/src/build/build-adapter.js +28 -94
  67. package/src/build/build-contracts.d.ts +97 -0
  68. package/src/build/build-contracts.js +0 -0
  69. package/src/build/build-input-fingerprint.d.ts +28 -0
  70. package/src/build/build-input-fingerprint.js +31 -0
  71. package/src/build/build-profile-options.d.ts +7 -0
  72. package/src/build/build-profile-options.js +37 -0
  73. package/src/build/build-runtime.d.ts +24 -0
  74. package/src/build/build-runtime.js +65 -0
  75. package/src/build/deduping-build-executor.d.ts +28 -0
  76. package/src/build/deduping-build-executor.js +56 -0
  77. package/src/build/jsx-ownership-plugins.d.ts +24 -0
  78. package/src/build/jsx-ownership-plugins.js +41 -0
  79. package/src/build/pages-unified-graph-build.d.ts +31 -0
  80. package/src/build/pages-unified-graph-build.js +206 -0
  81. package/src/build/parallel-build-executor.d.ts +21 -0
  82. package/src/build/parallel-build-executor.js +52 -0
  83. package/src/build/production-build-cache.d.ts +20 -0
  84. package/src/build/production-build-cache.js +63 -0
  85. package/src/build/rolldown-adapter-helpers.d.ts +3 -6
  86. package/src/build/rolldown-adapter-helpers.js +71 -6
  87. package/src/build/rolldown-build-adapter.d.ts +2 -6
  88. package/src/build/rolldown-build-adapter.js +3 -3
  89. package/src/build/rolldown-build-invocation-metrics.d.ts +12 -0
  90. package/src/build/rolldown-build-invocation-metrics.js +43 -0
  91. package/src/build/rolldown-plugin-bridge.d.ts +15 -1
  92. package/src/build/rolldown-plugin-bridge.js +20 -5
  93. package/src/build/rolldown-source-transform-pass.d.ts +15 -0
  94. package/src/build/rolldown-source-transform-pass.js +58 -0
  95. package/src/build/runtime-build-executor.d.ts +8 -15
  96. package/src/build/runtime-build-executor.js +12 -15
  97. package/src/build/runtime-build-output-normalizer.d.ts +7 -0
  98. package/src/build/runtime-build-output-normalizer.js +94 -2
  99. package/src/build/server-entry-build-cache.d.ts +44 -0
  100. package/src/build/server-entry-build-cache.js +161 -0
  101. package/src/client/navigation-scripts/classification.d.ts +16 -0
  102. package/src/client/navigation-scripts/classification.js +41 -0
  103. package/src/client/navigation-scripts/index.d.ts +3 -0
  104. package/src/client/navigation-scripts/index.js +23 -0
  105. package/src/client/navigation-scripts/navigation-scripts.test.browser.js +65 -0
  106. package/src/client/navigation-scripts/registry.d.ts +11 -0
  107. package/src/client/navigation-scripts/registry.js +12 -0
  108. package/src/client/navigation-scripts/rerun-queue.d.ts +37 -0
  109. package/src/client/navigation-scripts/rerun-queue.js +73 -0
  110. package/src/client/scroll.d.ts +17 -0
  111. package/src/client/scroll.js +25 -0
  112. package/src/client/scroll.test.browser.js +37 -0
  113. package/src/client/view-transitions.d.ts +16 -0
  114. package/src/client/view-transitions.js +63 -0
  115. package/src/config/config-builder.d.ts +12 -7
  116. package/src/config/config-builder.js +12 -9
  117. package/src/dev/client-bridge-registry.d.ts +5 -0
  118. package/src/dev/client-bridge-registry.js +15 -0
  119. package/src/dev/dev-client-ownership.d.ts +7 -0
  120. package/src/dev/dev-client-ownership.js +6 -0
  121. package/src/dev/hmr-manager-registry.d.ts +8 -0
  122. package/src/dev/hmr-manager-registry.js +15 -0
  123. package/src/dev/host-runtime.d.ts +3 -0
  124. package/src/dev/host-runtime.js +7 -0
  125. package/src/dev/runtime-server-started-message.d.ts +2 -0
  126. package/src/dev/runtime-server-started-message.js +6 -0
  127. package/src/eco/README.md +11 -0
  128. package/src/eco/eco-declared-component.d.ts +14 -0
  129. package/src/eco/eco-declared-component.js +16 -0
  130. package/src/eco/eco.browser.js +5 -6
  131. package/src/eco/eco.js +5 -6
  132. package/src/eco/eco.types.d.ts +2 -2
  133. package/src/eco/page-layout-normalization.d.ts +21 -0
  134. package/src/eco/page-layout-normalization.js +59 -0
  135. package/src/errors/index.d.ts +1 -0
  136. package/src/errors/index.js +7 -1
  137. package/src/errors/undeclared-component-dependency-error.d.ts +11 -0
  138. package/src/errors/undeclared-component-dependency-error.js +17 -0
  139. package/src/hmr/client/hmr-runtime.js +5 -2
  140. package/src/hmr/hmr-entrypoint-output.d.ts +28 -0
  141. package/src/hmr/hmr-entrypoint-output.js +58 -0
  142. package/src/hmr/strategies/js-hmr-strategy.d.ts +3 -0
  143. package/src/hmr/strategies/js-hmr-strategy.js +68 -18
  144. package/src/hmr/strategies/server-rendered-template-hmr-strategy.d.ts +17 -0
  145. package/src/hmr/strategies/server-rendered-template-hmr-strategy.js +29 -0
  146. package/src/integrations/ghtml/ghtml-renderer.d.ts +2 -6
  147. package/src/integrations/ghtml/ghtml-renderer.js +2 -51
  148. package/src/integrations/ghtml/ghtml.plugin.d.ts +3 -16
  149. package/src/integrations/ghtml/ghtml.plugin.js +7 -14
  150. package/src/plugins/define-integration.d.ts +26 -0
  151. package/src/plugins/define-integration.js +19 -0
  152. package/src/plugins/eco-component-meta-plugin.js +5 -8
  153. package/src/plugins/foreign-jsx-override-plugin.js +4 -6
  154. package/src/plugins/integration-plugin.d.ts +19 -9
  155. package/src/plugins/integration-plugin.js +7 -4
  156. package/src/plugins/jsx-import-source.utils.d.ts +8 -0
  157. package/src/plugins/jsx-import-source.utils.js +26 -0
  158. package/src/plugins/processor.d.ts +19 -4
  159. package/src/plugins/processor.js +22 -4
  160. package/src/plugins/source-transform.d.ts +31 -0
  161. package/src/plugins/source-transform.js +23 -1
  162. package/src/route-renderer/GRAPH.md +8 -8
  163. package/src/route-renderer/README.md +30 -28
  164. package/src/route-renderer/orchestration/component-graph-collectors.d.ts +10 -0
  165. package/src/route-renderer/orchestration/component-graph-collectors.js +143 -0
  166. package/src/route-renderer/orchestration/component-graph.d.ts +36 -0
  167. package/src/route-renderer/orchestration/component-graph.js +98 -0
  168. package/src/route-renderer/orchestration/component-render-context.d.ts +3 -10
  169. package/src/route-renderer/orchestration/component-render-context.js +3 -1
  170. package/src/route-renderer/orchestration/declared-ownership-graph.d.ts +1 -18
  171. package/src/route-renderer/orchestration/declared-ownership-graph.js +5 -31
  172. package/src/route-renderer/orchestration/document-shell-render.service.d.ts +90 -0
  173. package/src/route-renderer/orchestration/document-shell-render.service.js +120 -0
  174. package/src/route-renderer/orchestration/foreign-subtree-execution.service.d.ts +59 -29
  175. package/src/route-renderer/orchestration/foreign-subtree-execution.service.js +164 -35
  176. package/src/route-renderer/orchestration/global-injector-assets.service.d.ts +7 -0
  177. package/src/route-renderer/orchestration/global-injector-assets.service.js +32 -0
  178. package/src/route-renderer/orchestration/integration-renderer.d.ts +18 -54
  179. package/src/route-renderer/orchestration/integration-renderer.js +113 -195
  180. package/src/route-renderer/orchestration/integration-route-render-adapter.d.ts +38 -0
  181. package/src/route-renderer/orchestration/integration-route-render-adapter.js +27 -0
  182. package/src/route-renderer/orchestration/layout-shell-props.service.d.ts +20 -0
  183. package/src/route-renderer/orchestration/layout-shell-props.service.js +31 -0
  184. package/src/route-renderer/orchestration/ownership-validation.service.d.ts +5 -0
  185. package/src/route-renderer/orchestration/ownership-validation.service.js +29 -1
  186. package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.d.ts +9 -0
  187. package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.js +7 -0
  188. package/src/route-renderer/orchestration/page-browser-graph.service.d.ts +25 -0
  189. package/src/route-renderer/orchestration/page-browser-graph.service.js +174 -0
  190. package/src/route-renderer/orchestration/render-output.utils.d.ts +2 -0
  191. package/src/route-renderer/orchestration/render-output.utils.js +4 -0
  192. package/src/route-renderer/orchestration/route-html-finalization.service.d.ts +17 -0
  193. package/src/route-renderer/orchestration/route-html-finalization.service.js +26 -0
  194. package/src/route-renderer/orchestration/route-prepared-options.builder.d.ts +26 -0
  195. package/src/route-renderer/orchestration/route-prepared-options.builder.js +54 -0
  196. package/src/route-renderer/orchestration/route-prepared-options.utils.d.ts +4 -0
  197. package/src/route-renderer/orchestration/route-prepared-options.utils.js +33 -0
  198. package/src/route-renderer/orchestration/route-render-orchestrator.d.ts +8 -39
  199. package/src/route-renderer/orchestration/route-render-orchestrator.js +53 -461
  200. package/src/route-renderer/orchestration/string-markup-renderer.d.ts +14 -0
  201. package/src/route-renderer/orchestration/string-markup-renderer.js +55 -0
  202. package/src/route-renderer/page-loading/component-dependency-collection.js +5 -2
  203. package/src/router/client/link-intent.d.ts +11 -21
  204. package/src/router/client/link-intent.js +10 -0
  205. package/src/router/client/link-intent.test.browser.js +12 -1
  206. package/src/router/client/link-navigation-policy.d.ts +45 -0
  207. package/src/router/client/link-navigation-policy.js +101 -0
  208. package/src/router/client/link-navigation-policy.test.browser.d.ts +1 -0
  209. package/src/router/client/link-navigation-policy.test.browser.js +92 -0
  210. package/src/router/client/navigation-coordinator.d.ts +1 -1
  211. package/src/router/client/navigation-coordinator.js +12 -2
  212. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.d.ts +1 -0
  213. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.js +1 -0
  214. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.d.ts +4 -2
  215. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.js +27 -7
  216. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +36 -14
  217. package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +17 -3
  218. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.d.ts +0 -3
  219. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +2 -31
  220. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.d.ts +0 -3
  221. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +2 -30
  222. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.d.ts +2 -0
  223. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.js +43 -0
  224. package/src/services/assets/browser-bundle.service.d.ts +3 -1
  225. package/src/services/assets/browser-bundle.service.js +17 -5
  226. package/src/services/html/html-transformer.service.d.ts +1 -1
  227. package/src/services/html/html-transformer.service.js +4 -4
  228. package/src/services/invalidation/development-invalidation.service.d.ts +20 -2
  229. package/src/services/invalidation/development-invalidation.service.js +56 -2
  230. package/src/services/module-loading/README.md +49 -0
  231. package/src/services/module-loading/app-server-module-transpiler.service.js +5 -17
  232. package/src/services/module-loading/page-module-import.service.d.ts +5 -1
  233. package/src/services/module-loading/page-module-import.service.js +97 -45
  234. package/src/services/module-loading/route-module-build-cache-registry.d.ts +24 -0
  235. package/src/services/module-loading/route-module-build-cache-registry.js +37 -0
  236. package/src/services/module-loading/route-module-build-cache.d.ts +4 -0
  237. package/src/services/module-loading/route-module-build-cache.js +38 -0
  238. package/src/services/module-loading/route-module-build-cache.store.d.ts +57 -0
  239. package/src/services/module-loading/route-module-build-cache.store.js +207 -0
  240. package/src/services/module-loading/route-module-build-manifest.d.ts +61 -0
  241. package/src/services/module-loading/route-module-build-manifest.js +116 -0
  242. package/src/services/module-loading/route-module-dependency-hasher.d.ts +81 -0
  243. package/src/services/module-loading/route-module-dependency-hasher.js +121 -0
  244. package/src/services/module-loading/server-module-transpiler.service.js +1 -1
  245. package/src/static-site-generator/README.md +33 -0
  246. package/src/static-site-generator/static-build-invalidation.d.ts +10 -0
  247. package/src/static-site-generator/static-build-invalidation.js +59 -0
  248. package/src/static-site-generator/static-export-context.d.ts +22 -0
  249. package/src/static-site-generator/static-export-context.js +0 -0
  250. package/src/static-site-generator/static-site-generator.d.ts +17 -20
  251. package/src/static-site-generator/static-site-generator.js +188 -85
  252. package/src/types/internal-types.d.ts +16 -9
  253. package/src/types/public-types.d.ts +138 -26
  254. package/src/utils/parse-cli-args.d.ts +2 -0
  255. package/src/utils/parse-cli-args.js +6 -0
  256. package/src/utils/server-utils.module.d.ts +3 -2
  257. package/src/utils/server-utils.module.js +5 -6
  258. package/src/utils/static-file-extensions.d.ts +12 -0
  259. package/src/utils/static-file-extensions.js +48 -0
  260. package/src/watchers/project-watcher-ignore.d.ts +11 -0
  261. package/src/watchers/project-watcher-ignore.js +16 -0
  262. package/src/watchers/project-watcher.d.ts +25 -2
  263. package/src/watchers/project-watcher.js +130 -23
  264. package/src/build/rolldown-dev-build-adapter.d.ts +0 -82
  265. package/src/build/rolldown-dev-build-adapter.js +0 -166
  266. package/src/hmr/hmr.postcss.test.e2e.js +0 -31
  267. package/src/hmr/hmr.test.e2e.js +0 -43
  268. package/src/route-renderer/orchestration/ownership-planning.service.d.ts +0 -24
  269. package/src/route-renderer/orchestration/ownership-planning.service.js +0 -63
  270. package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.d.ts +0 -91
  271. package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.js +0 -170
  272. /package/src/{hmr/hmr.postcss.test.e2e.d.ts → client/navigation-scripts/navigation-scripts.test.browser.d.ts} +0 -0
  273. /package/src/{hmr/hmr.test.e2e.d.ts → client/scroll.test.browser.d.ts} +0 -0
@@ -7,6 +7,8 @@ function getEmbeddedRuntimeCommandOptions() {
7
7
  build: false,
8
8
  start: !isDevelopment,
9
9
  dev: isDevelopment,
10
+ force: false,
11
+ serveOnly: false,
10
12
  port: void 0,
11
13
  hostname: void 0,
12
14
  reactFastRefresh: void 0
@@ -25,6 +27,8 @@ function parseCliArgs(options = {}) {
25
27
  dev: { type: "boolean" },
26
28
  preview: { type: "boolean" },
27
29
  build: { type: "boolean" },
30
+ force: { type: "boolean" },
31
+ "serve-only": { type: "boolean" },
28
32
  port: { type: "string" },
29
33
  hostname: { type: "string" },
30
34
  "react-fast-refresh": { type: "boolean" }
@@ -48,6 +52,8 @@ function parseCliArgs(options = {}) {
48
52
  build: isBuildCommand,
49
53
  start: isStartCommand,
50
54
  dev: isDevCommand,
55
+ force: !!values.force,
56
+ serveOnly: !!values["serve-only"] || process.env.ECOPAGES_PREVIEW_SERVE_ONLY === "true",
51
57
  port: values.port ? Number(values.port) : void 0,
52
58
  hostname: values.hostname,
53
59
  reactFastRefresh: values["react-fast-refresh"]
@@ -1,3 +1,4 @@
1
+ import { hasKnownStaticExtension } from './static-file-extensions.js';
1
2
  /**
2
3
  * Get the content type of a file based on its extension.
3
4
  * @param file - The file name.
@@ -9,11 +10,11 @@ export declare const getContentType: (file: string) => string;
9
10
  * @param file - The file name or path.
10
11
  * @returns true if the extension is recognized.
11
12
  */
12
- export declare const hasKnownExtension: (file: string) => boolean;
13
+ export { hasKnownStaticExtension } from './static-file-extensions.js';
13
14
  /**
14
15
  * A module for server utilities.
15
16
  */
16
17
  export declare const ServerUtils: {
17
18
  getContentType: (file: string) => string;
18
- hasKnownExtension: (file: string) => boolean;
19
+ hasKnownStaticExtension: typeof hasKnownStaticExtension;
19
20
  };
@@ -1,3 +1,4 @@
1
+ import { hasKnownStaticExtension } from "./static-file-extensions.js";
1
2
  const ContentTypeMap = /* @__PURE__ */ new Map([
2
3
  ["jpg", "image/jpeg"],
3
4
  ["jpeg", "image/jpeg"],
@@ -17,6 +18,7 @@ const ContentTypeMap = /* @__PURE__ */ new Map([
17
18
  ["ogv", "video/ogg"],
18
19
  ["mov", "video/quicktime"],
19
20
  ["txt", "text/plain"],
21
+ ["md", "text/markdown"],
20
22
  ["html", "text/html"],
21
23
  ["css", "text/css"],
22
24
  ["js", "text/javascript"],
@@ -41,16 +43,13 @@ const getContentType = (file) => {
41
43
  const extension = file.split(".").pop() || "txt";
42
44
  return ContentTypeMap.get(extension) || "text/plain";
43
45
  };
44
- const hasKnownExtension = (file) => {
45
- const extension = file.split(".").pop();
46
- return extension !== void 0 && ContentTypeMap.has(extension);
47
- };
46
+ import { hasKnownStaticExtension as hasKnownStaticExtension2 } from "./static-file-extensions.js";
48
47
  const ServerUtils = {
49
48
  getContentType,
50
- hasKnownExtension
49
+ hasKnownStaticExtension
51
50
  };
52
51
  export {
53
52
  ServerUtils,
54
53
  getContentType,
55
- hasKnownExtension
54
+ hasKnownStaticExtension2 as hasKnownStaticExtension
56
55
  };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Known static asset extensions served directly from disk.
3
+ *
4
+ * @remarks
5
+ * Keep this list aligned with server static-file handling and client routers
6
+ * that must bypass SPA navigation for same-origin asset links.
7
+ */
8
+ export declare const STATIC_FILE_EXTENSIONS: Set<string>;
9
+ /**
10
+ * Returns whether a path or filename ends with a known static asset extension.
11
+ */
12
+ export declare function hasKnownStaticExtension(fileOrPath: string): boolean;
@@ -0,0 +1,48 @@
1
+ const STATIC_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
2
+ "jpg",
3
+ "jpeg",
4
+ "png",
5
+ "gif",
6
+ "bmp",
7
+ "svg",
8
+ "tiff",
9
+ "webp",
10
+ "avif",
11
+ "ico",
12
+ "mp3",
13
+ "ogg",
14
+ "wav",
15
+ "mp4",
16
+ "webm",
17
+ "ogv",
18
+ "mov",
19
+ "txt",
20
+ "md",
21
+ "html",
22
+ "css",
23
+ "js",
24
+ "mjs",
25
+ "json",
26
+ "map",
27
+ "xml",
28
+ "webmanifest",
29
+ "wasm",
30
+ "csv",
31
+ "ttf",
32
+ "woff",
33
+ "woff2",
34
+ "otf",
35
+ "eot",
36
+ "gz",
37
+ "zip",
38
+ "pdf",
39
+ "doc"
40
+ ]);
41
+ function hasKnownStaticExtension(fileOrPath) {
42
+ const extension = fileOrPath.split(".").pop();
43
+ return extension !== void 0 && STATIC_FILE_EXTENSIONS.has(extension);
44
+ }
45
+ export {
46
+ STATIC_FILE_EXTENSIONS,
47
+ hasKnownStaticExtension
48
+ };
@@ -0,0 +1,11 @@
1
+ export type ProjectWatcherIgnorePaths = {
2
+ workDir: string;
3
+ distDir: string;
4
+ };
5
+ /**
6
+ * @remarks
7
+ * chokidar v4+ no longer treats glob strings in `ignored` as patterns. A path
8
+ * predicate keeps node_modules, VCS metadata, and scoped artifact dirs out of
9
+ * the watch tree without descending into symlinked monorepo node_modules.
10
+ */
11
+ export declare function createProjectWatcherIgnorePredicate(absolutePaths: ProjectWatcherIgnorePaths): (watchedPath: string) => boolean;
@@ -0,0 +1,16 @@
1
+ import path from "node:path";
2
+ function createProjectWatcherIgnorePredicate(absolutePaths) {
3
+ const ignoredPrefixes = [absolutePaths.workDir, absolutePaths.distDir];
4
+ return (watchedPath) => {
5
+ const segments = watchedPath.split(path.sep);
6
+ if (segments.includes("node_modules") || segments.includes(".git")) {
7
+ return true;
8
+ }
9
+ return ignoredPrefixes.some(
10
+ (prefix) => watchedPath === prefix || watchedPath.startsWith(`${prefix}${path.sep}`)
11
+ );
12
+ };
13
+ }
14
+ export {
15
+ createProjectWatcherIgnorePredicate
16
+ };
@@ -13,6 +13,10 @@ export interface ProjectWatcherConfig {
13
13
  refreshRouterRoutesCallback: () => Promise<void>;
14
14
  hmrManager: IHmrManager;
15
15
  bridge: IClientBridge;
16
+ /** When true, the host dev server owns browser dev-client bootstrap. */
17
+ hostOwnsDevClient?: boolean;
18
+ /** Delay before a change event is processed; 0 disables debouncing. */
19
+ changeDebounceMs?: number;
16
20
  }
17
21
  /**
18
22
  * ProjectWatcher handles file system changes for hot module replacement (HMR).
@@ -42,11 +46,14 @@ export declare class ProjectWatcher {
42
46
  private refreshRouterRoutesCallback;
43
47
  private hmrManager;
44
48
  private bridge;
49
+ private readonly hostOwnsDevClient;
45
50
  private readonly invalidationService;
51
+ private readonly changeDebounceMs;
46
52
  private watcher;
47
53
  private lastHandledChange;
54
+ private pendingChangeTimers;
48
55
  private changeQueue;
49
- constructor({ config, refreshRouterRoutesCallback, hmrManager, bridge }: ProjectWatcherConfig);
56
+ constructor({ config, refreshRouterRoutesCallback, hmrManager, bridge, hostOwnsDevClient, changeDebounceMs, }: ProjectWatcherConfig);
50
57
  /**
51
58
  * Uncaches modules in the source directory to ensure fresh imports.
52
59
  * This is necessary for hot module replacement to work correctly.
@@ -55,6 +62,7 @@ export declare class ProjectWatcher {
55
62
  private uncacheModules;
56
63
  private isRouteSourceFile;
57
64
  private isIncludeSourceFile;
65
+ private requestBrowserReload;
58
66
  /**
59
67
  * Handles public directory file changes by copying only the changed file.
60
68
  * @param filePath - Absolute path of the changed file
@@ -70,7 +78,7 @@ export declare class ProjectWatcher {
70
78
  * Follows 5-rule priority:
71
79
  * 0. Public directory match? -> copy file and reload
72
80
  * 1. additionalWatchPaths match? -> reload
73
- * 2. Include template source? -> reload after processor notifications
81
+ * 2. Include template source? -> current-page refresh via HMR after processor notifications are deferred
74
82
  * 3. Processor-owned asset? -> processor already handled it via notification, skip HMR
75
83
  * 4. Otherwise -> HMR strategies
76
84
  *
@@ -84,6 +92,13 @@ export declare class ProjectWatcher {
84
92
  * @param event - The type of file system event
85
93
  */
86
94
  private handleFileChange;
95
+ private processFileChange;
96
+ /**
97
+ * Re-imports server modules before HMR broadcast so reload/refetch does not
98
+ * race stale in-memory imports or custom-element registry state.
99
+ */
100
+ private prewarmBeforeHmr;
101
+ private prewarmServerModuleImports;
87
102
  /**
88
103
  * Notifies all processors whose watch config matches the given file extension.
89
104
  * This is called before checking processor ownership so that dependency-only
@@ -133,4 +148,12 @@ export declare class ProjectWatcher {
133
148
  * rapid file changes efficiently.
134
149
  */
135
150
  createWatcherSubscription(): Promise<FSWatcher>;
151
+ /**
152
+ * Closes the active filesystem watcher subscription.
153
+ *
154
+ * @remarks
155
+ * Safe to call multiple times. Used when tearing down dev servers in tests
156
+ * and other short-lived Ecopages runtimes.
157
+ */
158
+ close(): Promise<void>;
136
159
  }
@@ -2,7 +2,12 @@ import path from "node:path";
2
2
  import chokidar, {} from "chokidar";
3
3
  import { fileSystem } from "@ecopages/file-system";
4
4
  import { appLogger } from "../global/app-logger.js";
5
- import { DevelopmentInvalidationService } from "../services/invalidation/development-invalidation.service.js";
5
+ import {
6
+ DevelopmentInvalidationService
7
+ } from "../services/invalidation/development-invalidation.service.js";
8
+ import { isRegisteredScriptEntrypoint } from "../hmr/hmr-entrypoint-output.js";
9
+ import { resolveInternalExecutionDir } from "../utils/resolve-work-dir.js";
10
+ import { createProjectWatcherIgnorePredicate } from "./project-watcher-ignore.js";
6
11
  class ProjectWatcher {
7
12
  /**
8
13
  * Duplicate identical watcher events within this window are ignored.
@@ -16,19 +21,33 @@ class ProjectWatcher {
16
21
  refreshRouterRoutesCallback;
17
22
  hmrManager;
18
23
  bridge;
24
+ hostOwnsDevClient;
19
25
  invalidationService;
26
+ changeDebounceMs;
20
27
  watcher = null;
21
28
  lastHandledChange = /* @__PURE__ */ new Map();
29
+ pendingChangeTimers = /* @__PURE__ */ new Map();
22
30
  changeQueue = Promise.resolve();
23
- constructor({ config, refreshRouterRoutesCallback, hmrManager, bridge }) {
31
+ constructor({
32
+ config,
33
+ refreshRouterRoutesCallback,
34
+ hmrManager,
35
+ bridge,
36
+ hostOwnsDevClient,
37
+ changeDebounceMs
38
+ }) {
24
39
  this.appConfig = config;
25
40
  this.refreshRouterRoutesCallback = refreshRouterRoutesCallback;
26
41
  this.hmrManager = hmrManager;
27
42
  this.bridge = bridge;
43
+ this.hostOwnsDevClient = hostOwnsDevClient === true;
44
+ const envDebounceMs = process.env.ECOPAGES_WATCH_CHANGE_DEBOUNCE_MS;
45
+ this.changeDebounceMs = changeDebounceMs ?? (envDebounceMs !== void 0 && envDebounceMs !== "" ? Number(envDebounceMs) : void 0) ?? ProjectWatcher.duplicateChangeWindowMs;
28
46
  this.invalidationService = new DevelopmentInvalidationService(config);
29
47
  this.triggerRouterRefresh = this.triggerRouterRefresh.bind(this);
30
48
  this.handleError = this.handleError.bind(this);
31
49
  this.handleFileChange = this.handleFileChange.bind(this);
50
+ this.processFileChange = this.processFileChange.bind(this);
32
51
  }
33
52
  /**
34
53
  * Uncaches modules in the source directory to ensure fresh imports.
@@ -51,6 +70,12 @@ class ProjectWatcher {
51
70
  isIncludeSourceFile(filePath) {
52
71
  return this.invalidationService.isIncludeSourceFile(filePath);
53
72
  }
73
+ requestBrowserReload() {
74
+ if (this.hostOwnsDevClient) {
75
+ return;
76
+ }
77
+ this.bridge.reload();
78
+ }
54
79
  /**
55
80
  * Handles public directory file changes by copying only the changed file.
56
81
  * @param filePath - Absolute path of the changed file
@@ -64,10 +89,10 @@ class ProjectWatcher {
64
89
  fileSystem.ensureDir(destDir);
65
90
  await fileSystem.copyFileAsync(filePath, destPath);
66
91
  }
67
- this.bridge.reload();
92
+ this.requestBrowserReload();
68
93
  } catch (error) {
69
94
  appLogger.error(`Failed to copy public file: ${error instanceof Error ? error.message : String(error)}`);
70
- this.bridge.reload();
95
+ this.requestBrowserReload();
71
96
  }
72
97
  }
73
98
  /**
@@ -77,13 +102,14 @@ class ProjectWatcher {
77
102
  enqueueChange(task) {
78
103
  const queuedTask = this.changeQueue.then(task, task);
79
104
  this.changeQueue = queuedTask.catch(() => void 0);
105
+ return queuedTask;
80
106
  }
81
107
  /**
82
108
  * Handles file changes by uncaching modules, refreshing routes, and delegating appropriately.
83
109
  * Follows 5-rule priority:
84
110
  * 0. Public directory match? -> copy file and reload
85
111
  * 1. additionalWatchPaths match? -> reload
86
- * 2. Include template source? -> reload after processor notifications
112
+ * 2. Include template source? -> current-page refresh via HMR after processor notifications are deferred
87
113
  * 3. Processor-owned asset? -> processor already handled it via notification, skip HMR
88
114
  * 4. Otherwise -> HMR strategies
89
115
  *
@@ -96,8 +122,23 @@ class ProjectWatcher {
96
122
  * @param rawPath - Path of the changed file
97
123
  * @param event - The type of file system event
98
124
  */
99
- async handleFileChange(rawPath, event = "change") {
125
+ handleFileChange(rawPath, event = "change") {
100
126
  const filePath = path.resolve(rawPath);
127
+ if (this.changeDebounceMs === 0) {
128
+ return this.enqueueChange(() => this.processFileChange(filePath, event));
129
+ }
130
+ const existing = this.pendingChangeTimers.get(filePath);
131
+ if (existing) {
132
+ clearTimeout(existing);
133
+ }
134
+ const timer = setTimeout(() => {
135
+ this.pendingChangeTimers.delete(filePath);
136
+ this.enqueueChange(() => this.processFileChange(filePath, event));
137
+ }, this.changeDebounceMs);
138
+ this.pendingChangeTimers.set(filePath, timer);
139
+ return Promise.resolve();
140
+ }
141
+ async processFileChange(filePath, event) {
101
142
  const now = Date.now();
102
143
  const lastHandledAt = this.lastHandledChange.get(filePath);
103
144
  if (lastHandledAt !== void 0 && now - lastHandledAt < ProjectWatcher.duplicateChangeWindowMs) {
@@ -111,21 +152,32 @@ class ProjectWatcher {
111
152
  return;
112
153
  }
113
154
  this.uncacheModules();
155
+ const resolvedFilePath = path.resolve(filePath);
156
+ const isRegisteredScriptEdit = isRegisteredScriptEntrypoint(
157
+ this.hmrManager.getWatchedFiles(),
158
+ resolvedFilePath
159
+ );
114
160
  if (plan.invalidateServerModules) {
115
161
  this.invalidationService.invalidateServerModules([filePath]);
162
+ if (isRegisteredScriptEdit) {
163
+ await this.invalidationService.notifyRegisteredScriptEntrypointChange(resolvedFilePath);
164
+ }
116
165
  }
117
166
  if (plan.refreshRoutes) {
118
167
  await this.refreshRouterRoutesCallback();
119
168
  }
120
- if (plan.category === "additional-watch") {
121
- this.bridge.reload();
169
+ if (plan.reloadBrowser) {
170
+ this.requestBrowserReload();
122
171
  return;
123
172
  }
124
- await this.notifyProcessors(filePath, event);
125
- if (plan.category === "include-source") {
126
- this.bridge.reload();
173
+ const deferProcessorNotifications = plan.category === "include-source" || plan.category === "explicit-server-view" || isRegisteredScriptEdit;
174
+ if (deferProcessorNotifications && plan.delegateToHmr) {
175
+ await this.prewarmBeforeHmr(resolvedFilePath, plan, isRegisteredScriptEdit);
176
+ await this.hmrManager.handleFileChange(filePath);
177
+ void this.notifyProcessors(filePath, event);
127
178
  return;
128
179
  }
180
+ await this.notifyProcessors(filePath, event);
129
181
  if (plan.processorHandledAsset) {
130
182
  return;
131
183
  }
@@ -139,6 +191,46 @@ class ProjectWatcher {
139
191
  }
140
192
  }
141
193
  }
194
+ /**
195
+ * Re-imports server modules before HMR broadcast so reload/refetch does not
196
+ * race stale in-memory imports or custom-element registry state.
197
+ */
198
+ async prewarmBeforeHmr(filePath, plan, isRegisteredScriptEdit) {
199
+ if (isRegisteredScriptEdit) {
200
+ await this.prewarmServerModuleImports([filePath], { bypassCache: true, scope: "registered script" });
201
+ return;
202
+ }
203
+ if (plan.category !== "include-source" && plan.category !== "explicit-server-view") {
204
+ return;
205
+ }
206
+ const modulePaths = plan.category === "include-source" ? [this.appConfig.absolutePaths.htmlTemplatePath] : [path.resolve(filePath)];
207
+ await this.prewarmServerModuleImports(modulePaths, { scope: "server template" });
208
+ }
209
+ async prewarmServerModuleImports(modulePaths, options) {
210
+ const appModuleLoader = this.appConfig.runtime?.appModuleLoader;
211
+ if (!appModuleLoader) {
212
+ return;
213
+ }
214
+ const outdir = path.join(resolveInternalExecutionDir(this.appConfig), ".server-modules");
215
+ for (const modulePath of modulePaths) {
216
+ if (!modulePath) {
217
+ continue;
218
+ }
219
+ try {
220
+ await appModuleLoader.importModule({
221
+ filePath: modulePath,
222
+ rootDir: this.appConfig.rootDir,
223
+ outdir,
224
+ externalPackages: true,
225
+ bypassCache: options.bypassCache
226
+ });
227
+ } catch (error) {
228
+ appLogger.error(
229
+ `Failed to prewarm ${options.scope} ${modulePath}: ${error instanceof Error ? error.message : String(error)}`
230
+ );
231
+ }
232
+ }
233
+ }
142
234
  /**
143
235
  * Notifies all processors whose watch config matches the given file extension.
144
236
  * This is called before checking processor ownership so that dependency-only
@@ -231,36 +323,37 @@ class ProjectWatcher {
231
323
  if (this.watcher) {
232
324
  return this.watcher;
233
325
  }
234
- const processorPaths = [];
326
+ const processorPaths = /* @__PURE__ */ new Set();
235
327
  for (const processor of this.appConfig.processors.values()) {
236
328
  const watchConfig = processor.getWatchConfig();
237
329
  if (!watchConfig) continue;
238
- processorPaths.push(...watchConfig.paths);
330
+ for (const watchPath of watchConfig.paths) {
331
+ processorPaths.add(watchPath);
332
+ }
239
333
  }
240
334
  if (fileSystem.exists(this.appConfig.absolutePaths.includesDir)) {
241
- processorPaths.push(this.appConfig.absolutePaths.includesDir);
335
+ processorPaths.add(this.appConfig.absolutePaths.includesDir);
242
336
  }
243
337
  if (fileSystem.exists(this.appConfig.absolutePaths.srcDir)) {
244
- processorPaths.push(this.appConfig.absolutePaths.srcDir);
245
- }
246
- if (fileSystem.exists(this.appConfig.absolutePaths.pagesDir)) {
247
- processorPaths.push(this.appConfig.absolutePaths.pagesDir);
338
+ processorPaths.add(this.appConfig.absolutePaths.srcDir);
248
339
  }
249
340
  if (fileSystem.exists(this.appConfig.absolutePaths.publicDir)) {
250
- processorPaths.push(this.appConfig.absolutePaths.publicDir);
341
+ processorPaths.add(this.appConfig.absolutePaths.publicDir);
251
342
  }
252
- if (this.appConfig.additionalWatchPaths.length) {
253
- processorPaths.push(...this.appConfig.additionalWatchPaths);
343
+ for (const watchPath of this.appConfig.additionalWatchPaths) {
344
+ processorPaths.add(watchPath);
254
345
  }
255
- this.watcher = chokidar.watch(processorPaths, {
346
+ const ignored = createProjectWatcherIgnorePredicate(this.appConfig.absolutePaths);
347
+ this.watcher = chokidar.watch(Array.from(processorPaths), {
256
348
  ignoreInitial: true,
257
349
  ignorePermissionErrors: true,
350
+ ignored,
258
351
  awaitWriteFinish: {
259
352
  stabilityThreshold: 50,
260
353
  pollInterval: 50
261
354
  }
262
355
  });
263
- this.watcher.on("change", (p) => this.enqueueChange(() => this.handleFileChange(p, "change"))).on("add", (p) => this.enqueueChange(() => this.handleFileChange(p, "add"))).on("addDir", (p) => this.enqueueChange(() => this.triggerRouterRefresh(p))).on("unlink", (p) => this.enqueueChange(() => this.handleFileChange(p, "unlink"))).on("unlinkDir", (p) => this.enqueueChange(() => this.triggerRouterRefresh(p))).on("error", (error) => this.handleError(error));
356
+ this.watcher.on("change", (p) => this.handleFileChange(p, "change")).on("add", (p) => this.handleFileChange(p, "add")).on("addDir", (p) => this.enqueueChange(() => this.triggerRouterRefresh(p))).on("unlink", (p) => this.handleFileChange(p, "unlink")).on("unlinkDir", (p) => this.enqueueChange(() => this.triggerRouterRefresh(p))).on("error", (error) => this.handleError(error));
264
357
  for (const processor of this.appConfig.processors.values()) {
265
358
  const watchConfig = processor.getWatchConfig();
266
359
  if (watchConfig?.onError) {
@@ -269,6 +362,20 @@ class ProjectWatcher {
269
362
  }
270
363
  return this.watcher;
271
364
  }
365
+ /**
366
+ * Closes the active filesystem watcher subscription.
367
+ *
368
+ * @remarks
369
+ * Safe to call multiple times. Used when tearing down dev servers in tests
370
+ * and other short-lived Ecopages runtimes.
371
+ */
372
+ async close() {
373
+ if (!this.watcher) {
374
+ return;
375
+ }
376
+ await this.watcher.close();
377
+ this.watcher = null;
378
+ }
272
379
  }
273
380
  export {
274
381
  ProjectWatcher
@@ -1,82 +0,0 @@
1
- /**
2
- * DevEngine-backed build adapter for cached incremental rebuilds.
3
- *
4
- * @remarks
5
- * Wraps Rolldown's experimental `DevEngine` to provide cached incremental
6
- * rebuilds. Unlike the standard `RolldownBuildAdapter` which creates a new
7
- * bundler for every build, this adapter keeps a `DevEngine` instance alive
8
- * and reuses the internal module graph, resolver cache, and transform cache
9
- * across builds.
10
- *
11
- * The adapter is designed for the HMR rebuild path where the same entrypoints
12
- * are rebuilt repeatedly with small changes. For one-off production builds,
13
- * use `RolldownBuildAdapter` instead.
14
- *
15
- * The first build creates the engine and runs it. Subsequent builds with the
16
- * same plugin/target/format triple reuse the cached engine and call
17
- * `triggerFullBuild()`. The cache key incorporates the context root, plugin
18
- * names, target, and format; when any of those change, the cached engine is
19
- * closed and a new one is created.
20
- *
21
- * Call `close()` to release the cached engine and free resources.
22
- *
23
- * @module
24
- */
25
- import type { BuildAdapter, BuildOptions, BuildResult, BuildTranspileOptions, BuildTranspileProfile } from './build-adapter.js';
26
- /**
27
- * Build adapter backed by Rolldown's DevEngine for cached incremental rebuilds.
28
- */
29
- export declare class RolldownDevBuildAdapter implements BuildAdapter {
30
- /**
31
- * `'rolldown-dev'` (not `'rolldown'`) so {@link getBuildAdapterOwnership}
32
- * round-trips correctly and `createBuildAdapter({ ownership })` can
33
- * re-select this class.
34
- */
35
- readonly ownership: "rolldown-dev";
36
- private cachedEngine;
37
- private engineInstanceCount;
38
- private readonly pendingBuilds;
39
- private readonly appRootRequireCache;
40
- /**
41
- * Stable key over the inputs that affect the engine configuration.
42
- * Plugin order is intentionally not part of the key: two plugin
43
- * sets with the same names are interchangeable for caching.
44
- */
45
- private getCacheKey;
46
- /**
47
- * Returns the cached engine, creating a new one when the cache
48
- * key changes. On a cache miss the prior engine is closed and
49
- * {@link engineInstanceCount} is incremented.
50
- */
51
- private getOrCreateEngine;
52
- /**
53
- * Triggers a build on the cached engine and returns the next
54
- * `RolldownOutput` delivered to `onOutput`.
55
- *
56
- * First call after engine creation invokes `engine.run`; subsequent
57
- * calls invoke `engine.triggerFullBuild` and reuse the cached
58
- * module graph, resolver, and transform caches.
59
- *
60
- * If the trigger itself throws, the queued build is removed so
61
- * the next `build()` does not consume a stale slot.
62
- */
63
- private runBuild;
64
- /** Throws on error; see {@link build} for the no-throw variant. */
65
- buildOrThrow(options: BuildOptions): Promise<BuildResult>;
66
- build(options: BuildOptions): Promise<BuildResult>;
67
- resolve(importPath: string, rootDir: string): string;
68
- getTranspileOptions(profile: BuildTranspileProfile): BuildTranspileOptions;
69
- /**
70
- * Closes the cached DevEngine and rejects any in-flight builds
71
- * with `'DevBuildAdapter closed'`.
72
- *
73
- * @remarks
74
- * Not on the {@link BuildAdapter} interface, so it is only
75
- * reachable through the concrete class. The adapter is meant to
76
- * live for the process lifetime; this is for explicit teardown.
77
- */
78
- close(): Promise<void>;
79
- /** Test-only: number of engines created over the adapter's lifetime. */
80
- getEngineInstanceCountForTests(): number;
81
- }
82
- export declare function createRolldownDevBuildAdapter(): RolldownDevBuildAdapter;