@ecopages/core 0.2.0-beta.1 → 0.2.0-beta.11

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 (216) hide show
  1. package/package.json +107 -3
  2. package/src/adapters/abstract/application-adapter.d.ts +73 -1
  3. package/src/adapters/abstract/application-adapter.js +89 -0
  4. package/src/adapters/abstract/server-adapter.d.ts +3 -0
  5. package/src/adapters/abstract/ws-pattern-matcher.d.ts +60 -0
  6. package/src/adapters/abstract/ws-pattern-matcher.js +61 -0
  7. package/src/adapters/bun/client-bridge.d.ts +1 -0
  8. package/src/adapters/bun/client-bridge.js +3 -0
  9. package/src/adapters/bun/create-app.d.ts +5 -3
  10. package/src/adapters/bun/create-app.js +44 -39
  11. package/src/adapters/bun/hmr-manager.d.ts +2 -1
  12. package/src/adapters/bun/hmr-manager.js +2 -2
  13. package/src/adapters/bun/server-adapter.d.ts +81 -20
  14. package/src/adapters/bun/server-adapter.js +220 -78
  15. package/src/adapters/bun/static-preview-host.js +25 -20
  16. package/src/adapters/create-app.d.ts +13 -0
  17. package/src/adapters/create-app.js +10 -1
  18. package/src/adapters/node/create-app.d.ts +4 -0
  19. package/src/adapters/node/create-app.js +38 -9
  20. package/src/adapters/node/node-client-bridge.d.ts +1 -0
  21. package/src/adapters/node/node-client-bridge.js +3 -0
  22. package/src/adapters/node/node-hmr-manager.d.ts +2 -1
  23. package/src/adapters/node/node-hmr-manager.js +2 -2
  24. package/src/adapters/node/server-adapter-dependencies.js +2 -0
  25. package/src/adapters/node/server-adapter.d.ts +55 -19
  26. package/src/adapters/node/server-adapter.js +140 -132
  27. package/src/adapters/node/static-content-server.d.ts +1 -0
  28. package/src/adapters/node/static-content-server.js +10 -2
  29. package/src/adapters/node/static-preview-host.js +27 -12
  30. package/src/adapters/shared/bun-user-websocket-lifecycle.d.ts +22 -0
  31. package/src/adapters/shared/bun-user-websocket-lifecycle.js +96 -0
  32. package/src/adapters/shared/copy-runtime-public-dir.d.ts +6 -0
  33. package/src/adapters/shared/copy-runtime-public-dir.js +24 -0
  34. package/src/adapters/shared/hmr-html-response.d.ts +1 -1
  35. package/src/adapters/shared/hmr-html-response.js +4 -1
  36. package/src/adapters/shared/node-http-websocket-upgrades.d.ts +21 -0
  37. package/src/adapters/shared/node-http-websocket-upgrades.js +100 -0
  38. package/src/adapters/shared/port-manager.d.ts +116 -0
  39. package/src/adapters/shared/port-manager.js +196 -0
  40. package/src/adapters/shared/runtime-app-bootstrap.d.ts +2 -1
  41. package/src/adapters/shared/runtime-app-bootstrap.js +3 -6
  42. package/src/adapters/shared/runtime-server-lifecycle.d.ts +38 -0
  43. package/src/adapters/shared/runtime-server-lifecycle.js +39 -0
  44. package/src/adapters/shared/server-adapter.d.ts +2 -1
  45. package/src/adapters/shared/server-adapter.js +12 -4
  46. package/src/adapters/shared/server-route-handler.d.ts +4 -1
  47. package/src/adapters/shared/server-route-handler.js +5 -2
  48. package/src/adapters/shared/server-static-builder.d.ts +14 -29
  49. package/src/adapters/shared/server-static-builder.js +91 -70
  50. package/src/adapters/shared/shared-hmr-manager.d.ts +12 -0
  51. package/src/adapters/shared/shared-hmr-manager.js +64 -7
  52. package/src/adapters/shared/static-preview-host.d.ts +5 -0
  53. package/src/adapters/shared/websocket-lifecycle.d.ts +24 -0
  54. package/src/adapters/shared/websocket-lifecycle.js +55 -0
  55. package/src/build/README.md +77 -14
  56. package/src/build/app-build-manifest-runtime.d.ts +10 -0
  57. package/src/build/app-build-manifest-runtime.js +90 -0
  58. package/src/build/build-adapter.d.ts +17 -319
  59. package/src/build/build-adapter.js +28 -94
  60. package/src/build/build-contracts.d.ts +97 -0
  61. package/src/build/build-contracts.js +0 -0
  62. package/src/build/build-input-fingerprint.d.ts +28 -0
  63. package/src/build/build-input-fingerprint.js +31 -0
  64. package/src/build/build-profile-options.d.ts +7 -0
  65. package/src/build/build-profile-options.js +37 -0
  66. package/src/build/build-runtime.d.ts +24 -0
  67. package/src/build/build-runtime.js +65 -0
  68. package/src/build/deduping-build-executor.d.ts +28 -0
  69. package/src/build/deduping-build-executor.js +56 -0
  70. package/src/build/jsx-ownership-plugins.d.ts +24 -0
  71. package/src/build/jsx-ownership-plugins.js +41 -0
  72. package/src/build/pages-unified-graph-build.d.ts +31 -0
  73. package/src/build/pages-unified-graph-build.js +206 -0
  74. package/src/build/parallel-build-executor.d.ts +21 -0
  75. package/src/build/parallel-build-executor.js +52 -0
  76. package/src/build/production-build-cache.d.ts +20 -0
  77. package/src/build/production-build-cache.js +63 -0
  78. package/src/build/rolldown-adapter-helpers.d.ts +3 -6
  79. package/src/build/rolldown-adapter-helpers.js +71 -6
  80. package/src/build/rolldown-build-adapter.d.ts +2 -6
  81. package/src/build/rolldown-build-adapter.js +3 -3
  82. package/src/build/rolldown-build-invocation-metrics.d.ts +12 -0
  83. package/src/build/rolldown-build-invocation-metrics.js +43 -0
  84. package/src/build/rolldown-plugin-bridge.d.ts +15 -1
  85. package/src/build/rolldown-plugin-bridge.js +20 -5
  86. package/src/build/rolldown-source-transform-pass.d.ts +15 -0
  87. package/src/build/rolldown-source-transform-pass.js +58 -0
  88. package/src/build/runtime-build-executor.d.ts +8 -15
  89. package/src/build/runtime-build-executor.js +12 -15
  90. package/src/build/runtime-build-output-normalizer.d.ts +7 -0
  91. package/src/build/runtime-build-output-normalizer.js +94 -2
  92. package/src/build/server-entry-build-cache.d.ts +44 -0
  93. package/src/build/server-entry-build-cache.js +161 -0
  94. package/src/config/config-builder.d.ts +12 -7
  95. package/src/config/config-builder.js +12 -9
  96. package/src/dev/client-bridge-registry.d.ts +5 -0
  97. package/src/dev/client-bridge-registry.js +15 -0
  98. package/src/dev/dev-client-ownership.d.ts +7 -0
  99. package/src/dev/dev-client-ownership.js +6 -0
  100. package/src/dev/host-runtime.d.ts +3 -0
  101. package/src/dev/host-runtime.js +7 -0
  102. package/src/hmr/client/hmr-runtime.js +5 -2
  103. package/src/hmr/strategies/server-rendered-template-hmr-strategy.d.ts +17 -0
  104. package/src/hmr/strategies/server-rendered-template-hmr-strategy.js +29 -0
  105. package/src/integrations/ghtml/ghtml-renderer.d.ts +2 -6
  106. package/src/integrations/ghtml/ghtml-renderer.js +2 -51
  107. package/src/integrations/ghtml/ghtml.plugin.d.ts +3 -16
  108. package/src/integrations/ghtml/ghtml.plugin.js +7 -14
  109. package/src/plugins/define-integration.d.ts +26 -0
  110. package/src/plugins/define-integration.js +19 -0
  111. package/src/plugins/eco-component-meta-plugin.js +5 -8
  112. package/src/plugins/foreign-jsx-override-plugin.js +4 -6
  113. package/src/plugins/integration-plugin.d.ts +19 -9
  114. package/src/plugins/integration-plugin.js +7 -4
  115. package/src/plugins/jsx-import-source.utils.d.ts +8 -0
  116. package/src/plugins/jsx-import-source.utils.js +26 -0
  117. package/src/plugins/processor.d.ts +19 -4
  118. package/src/plugins/processor.js +22 -4
  119. package/src/plugins/source-transform.d.ts +31 -0
  120. package/src/plugins/source-transform.js +23 -1
  121. package/src/route-renderer/GRAPH.md +8 -8
  122. package/src/route-renderer/README.md +30 -28
  123. package/src/route-renderer/orchestration/component-graph-collectors.d.ts +10 -0
  124. package/src/route-renderer/orchestration/component-graph-collectors.js +143 -0
  125. package/src/route-renderer/orchestration/component-graph.d.ts +36 -0
  126. package/src/route-renderer/orchestration/component-graph.js +90 -0
  127. package/src/route-renderer/orchestration/component-render-context.d.ts +3 -10
  128. package/src/route-renderer/orchestration/component-render-context.js +3 -1
  129. package/src/route-renderer/orchestration/declared-ownership-graph.d.ts +1 -18
  130. package/src/route-renderer/orchestration/declared-ownership-graph.js +5 -31
  131. package/src/route-renderer/orchestration/document-shell-render.service.d.ts +54 -0
  132. package/src/route-renderer/orchestration/document-shell-render.service.js +54 -0
  133. package/src/route-renderer/orchestration/foreign-subtree-execution.service.d.ts +59 -29
  134. package/src/route-renderer/orchestration/foreign-subtree-execution.service.js +164 -35
  135. package/src/route-renderer/orchestration/global-injector-assets.service.d.ts +7 -0
  136. package/src/route-renderer/orchestration/global-injector-assets.service.js +32 -0
  137. package/src/route-renderer/orchestration/integration-renderer.d.ts +18 -54
  138. package/src/route-renderer/orchestration/integration-renderer.js +108 -194
  139. package/src/route-renderer/orchestration/integration-route-render-adapter.d.ts +38 -0
  140. package/src/route-renderer/orchestration/integration-route-render-adapter.js +27 -0
  141. package/src/route-renderer/orchestration/ownership-validation.service.d.ts +4 -0
  142. package/src/route-renderer/orchestration/ownership-validation.service.js +11 -1
  143. package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.d.ts +9 -0
  144. package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.js +7 -0
  145. package/src/route-renderer/orchestration/page-browser-graph.service.d.ts +25 -0
  146. package/src/route-renderer/orchestration/page-browser-graph.service.js +174 -0
  147. package/src/route-renderer/orchestration/render-output.utils.d.ts +2 -0
  148. package/src/route-renderer/orchestration/render-output.utils.js +4 -0
  149. package/src/route-renderer/orchestration/route-html-finalization.service.d.ts +17 -0
  150. package/src/route-renderer/orchestration/route-html-finalization.service.js +26 -0
  151. package/src/route-renderer/orchestration/route-prepared-options.builder.d.ts +24 -0
  152. package/src/route-renderer/orchestration/route-prepared-options.builder.js +52 -0
  153. package/src/route-renderer/orchestration/route-prepared-options.utils.d.ts +4 -0
  154. package/src/route-renderer/orchestration/route-prepared-options.utils.js +33 -0
  155. package/src/route-renderer/orchestration/route-render-orchestrator.d.ts +5 -38
  156. package/src/route-renderer/orchestration/route-render-orchestrator.js +51 -459
  157. package/src/route-renderer/orchestration/string-markup-renderer.d.ts +14 -0
  158. package/src/route-renderer/orchestration/string-markup-renderer.js +54 -0
  159. package/src/router/client/navigation-coordinator.d.ts +1 -1
  160. package/src/router/client/navigation-coordinator.js +12 -2
  161. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.d.ts +1 -0
  162. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.js +1 -0
  163. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.d.ts +4 -2
  164. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.js +27 -7
  165. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +32 -14
  166. package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +17 -3
  167. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.d.ts +0 -3
  168. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +2 -31
  169. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.d.ts +0 -3
  170. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +2 -30
  171. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.d.ts +2 -0
  172. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.js +43 -0
  173. package/src/services/assets/browser-bundle.service.d.ts +3 -1
  174. package/src/services/assets/browser-bundle.service.js +17 -5
  175. package/src/services/html/html-transformer.service.d.ts +1 -1
  176. package/src/services/html/html-transformer.service.js +4 -4
  177. package/src/services/invalidation/development-invalidation.service.d.ts +10 -1
  178. package/src/services/invalidation/development-invalidation.service.js +31 -2
  179. package/src/services/module-loading/README.md +49 -0
  180. package/src/services/module-loading/app-server-module-transpiler.service.js +5 -17
  181. package/src/services/module-loading/page-module-import.service.d.ts +4 -1
  182. package/src/services/module-loading/page-module-import.service.js +61 -41
  183. package/src/services/module-loading/route-module-build-cache-registry.d.ts +24 -0
  184. package/src/services/module-loading/route-module-build-cache-registry.js +37 -0
  185. package/src/services/module-loading/route-module-build-cache.d.ts +4 -0
  186. package/src/services/module-loading/route-module-build-cache.js +38 -0
  187. package/src/services/module-loading/route-module-build-cache.store.d.ts +57 -0
  188. package/src/services/module-loading/route-module-build-cache.store.js +207 -0
  189. package/src/services/module-loading/route-module-build-manifest.d.ts +61 -0
  190. package/src/services/module-loading/route-module-build-manifest.js +116 -0
  191. package/src/services/module-loading/route-module-dependency-hasher.d.ts +81 -0
  192. package/src/services/module-loading/route-module-dependency-hasher.js +121 -0
  193. package/src/services/module-loading/server-module-transpiler.service.js +1 -1
  194. package/src/static-site-generator/README.md +33 -0
  195. package/src/static-site-generator/static-build-invalidation.d.ts +10 -0
  196. package/src/static-site-generator/static-build-invalidation.js +59 -0
  197. package/src/static-site-generator/static-export-context.d.ts +22 -0
  198. package/src/static-site-generator/static-export-context.js +0 -0
  199. package/src/static-site-generator/static-site-generator.d.ts +17 -20
  200. package/src/static-site-generator/static-site-generator.js +188 -85
  201. package/src/types/internal-types.d.ts +12 -9
  202. package/src/types/public-types.d.ts +87 -22
  203. package/src/utils/parse-cli-args.d.ts +1 -0
  204. package/src/utils/parse-cli-args.js +3 -0
  205. package/src/watchers/project-watcher.d.ts +14 -2
  206. package/src/watchers/project-watcher.js +50 -20
  207. package/src/build/rolldown-dev-build-adapter.d.ts +0 -82
  208. package/src/build/rolldown-dev-build-adapter.js +0 -166
  209. package/src/hmr/hmr.postcss.test.e2e.d.ts +0 -1
  210. package/src/hmr/hmr.postcss.test.e2e.js +0 -31
  211. package/src/hmr/hmr.test.e2e.d.ts +0 -1
  212. package/src/hmr/hmr.test.e2e.js +0 -43
  213. package/src/route-renderer/orchestration/ownership-planning.service.d.ts +0 -24
  214. package/src/route-renderer/orchestration/ownership-planning.service.js +0 -63
  215. package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.d.ts +0 -91
  216. package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.js +0 -170
@@ -0,0 +1,39 @@
1
+ import path from "node:path";
2
+ import { fileSystem } from "@ecopages/file-system";
3
+ import { RESOLVED_ASSETS_DIR } from "../../config/constants.js";
4
+ import { disposeAppBuildRuntime } from "../../build/build-runtime.js";
5
+ import { getAppBrowserBuildPlugins } from "../../build/build-adapter.js";
6
+ import { copyRuntimePublicDirIfChanged } from "./copy-runtime-public-dir.js";
7
+ import { injectHmrRuntimeIntoHtmlResponse, isHtmlResponse, shouldInjectHmrHtmlResponse } from "./hmr-html-response.js";
8
+ function prepareRuntimePublicDir(appConfig) {
9
+ const srcPublicDir = path.join(appConfig.rootDir, appConfig.srcDir, appConfig.publicDir);
10
+ if (fileSystem.exists(srcPublicDir)) {
11
+ copyRuntimePublicDirIfChanged(srcPublicDir, path.join(appConfig.rootDir, appConfig.distDir));
12
+ }
13
+ fileSystem.ensureDir(path.join(appConfig.absolutePaths.distDir, RESOLVED_ASSETS_DIR));
14
+ }
15
+ function wireIntegrationHmrManagers(appConfig, hmrManager) {
16
+ hmrManager.setPlugins(getAppBrowserBuildPlugins(appConfig));
17
+ for (const integration of appConfig.integrations) {
18
+ integration.setHmrManager(hmrManager);
19
+ }
20
+ }
21
+ async function maybeInjectAdapterHmrHtmlResponse(response, options) {
22
+ if (shouldInjectHmrHtmlResponse(options.watch, options.hmrManager, options.hostOwnsDevClient) && isHtmlResponse(response)) {
23
+ return injectHmrRuntimeIntoHtmlResponse(response);
24
+ }
25
+ return response;
26
+ }
27
+ async function disposeDevResources(options) {
28
+ await options.projectWatcher?.close();
29
+ await disposeAppBuildRuntime(options.appConfig);
30
+ options.hmrManager?.stop();
31
+ options.bridge?.destroy();
32
+ await options.previewHost.stop();
33
+ }
34
+ export {
35
+ disposeDevResources,
36
+ maybeInjectAdapterHmrHtmlResponse,
37
+ prepareRuntimePublicDir,
38
+ wireIntegrationHmrManagers
39
+ };
@@ -21,7 +21,8 @@ export declare abstract class SharedServerAdapter<TOptions extends ServerAdapter
21
21
  protected routeHandler: ServerRouteHandler;
22
22
  protected staticSiteGenerator: StaticSiteGenerator;
23
23
  protected staticBuilder: ServerStaticBuilder;
24
- protected readonly schemaValidator: SchemaValidationService;
24
+ protected schemaValidator: SchemaValidationService;
25
+ protected hostOwnsDevClient: boolean;
25
26
  protected initializeSharedRouteHandling(options: {
26
27
  staticRoutes: StaticRoute[];
27
28
  hmrManager?: any;
@@ -27,6 +27,7 @@ class SharedServerAdapter extends AbstractServerAdapter {
27
27
  staticSiteGenerator;
28
28
  staticBuilder;
29
29
  schemaValidator = new SchemaValidationService();
30
+ hostOwnsDevClient = false;
30
31
  async initializeSharedRouteHandling(options) {
31
32
  await this.initSharedRouter();
32
33
  this.configureSharedResponseHandlers(options.staticRoutes, options.hmrManager);
@@ -76,7 +77,7 @@ class SharedServerAdapter extends AbstractServerAdapter {
76
77
  const module = await serverModuleTranspiler.importModule({
77
78
  filePath,
78
79
  outdir: path.join(resolveInternalExecutionDir(this.appConfig), ".server-route-modules"),
79
- externalPackages: false,
80
+ externalPackages: true,
80
81
  transpileErrorMessage: (details) => `Error transpiling route module: ${details}`,
81
82
  noOutputMessage: (targetFilePath) => `No transpiled output generated for route module: ${targetFilePath}`
82
83
  });
@@ -115,7 +116,8 @@ class SharedServerAdapter extends AbstractServerAdapter {
115
116
  fileSystemResponseMatcher: this.fileSystemResponseMatcher,
116
117
  explicitStaticRouteMatcher,
117
118
  watch: !!this.options?.watch,
118
- hmrManager
119
+ hmrManager,
120
+ hostOwnsDevClient: this.hostOwnsDevClient
119
121
  });
120
122
  }
121
123
  createSharedResponseHandlerDependencies(staticRoutes) {
@@ -380,7 +382,10 @@ class SharedServerAdapter extends AbstractServerAdapter {
380
382
  const runtimePath = context.hmrManager.getRuntimePath();
381
383
  if (fileSystem.exists(runtimePath)) {
382
384
  return new Response(fileSystem.readFileAsBuffer(runtimePath), {
383
- headers: { "Content-Type": "application/javascript" }
385
+ headers: {
386
+ "Content-Type": "application/javascript",
387
+ "Cache-Control": "no-store, must-revalidate"
388
+ }
384
389
  });
385
390
  }
386
391
  }
@@ -389,7 +394,10 @@ class SharedServerAdapter extends AbstractServerAdapter {
389
394
  const assetPath = path.join(context.hmrManager.getDistDir(), relativePath);
390
395
  if (fileSystem.exists(assetPath)) {
391
396
  return new Response(fileSystem.readFileAsBuffer(assetPath), {
392
- headers: { "Content-Type": "application/javascript" }
397
+ headers: {
398
+ "Content-Type": "application/javascript",
399
+ "Cache-Control": "no-store, must-revalidate"
400
+ }
393
401
  });
394
402
  }
395
403
  }
@@ -16,6 +16,8 @@ export interface ServerRouteHandlerParams {
16
16
  watch?: boolean;
17
17
  /** HMR manager for broadcasting hot module reload events in development. */
18
18
  hmrManager?: IHmrManager;
19
+ /** When true, the host dev server owns browser dev-client bootstrap. */
20
+ hostOwnsDevClient?: boolean;
19
21
  }
20
22
  /**
21
23
  * Handles HTTP requests and routing for the server.
@@ -33,12 +35,13 @@ export declare class ServerRouteHandler {
33
35
  private readonly explicitStaticRouteMatcher?;
34
36
  private readonly watch;
35
37
  private readonly hmrManager?;
38
+ private readonly hostOwnsDevClient;
36
39
  /**
37
40
  * Creates a new ServerRouteHandler instance.
38
41
  *
39
42
  * @param params - Configuration parameters
40
43
  */
41
- constructor({ router, fileSystemResponseMatcher, explicitStaticRouteMatcher, watch, hmrManager, }: ServerRouteHandlerParams);
44
+ constructor({ router, fileSystemResponseMatcher, explicitStaticRouteMatcher, watch, hmrManager, hostOwnsDevClient, }: ServerRouteHandlerParams);
42
45
  /**
43
46
  * Determines if HMR script should be injected.
44
47
  *
@@ -7,6 +7,7 @@ class ServerRouteHandler {
7
7
  explicitStaticRouteMatcher;
8
8
  watch;
9
9
  hmrManager;
10
+ hostOwnsDevClient;
10
11
  /**
11
12
  * Creates a new ServerRouteHandler instance.
12
13
  *
@@ -17,13 +18,15 @@ class ServerRouteHandler {
17
18
  fileSystemResponseMatcher,
18
19
  explicitStaticRouteMatcher,
19
20
  watch = false,
20
- hmrManager
21
+ hmrManager,
22
+ hostOwnsDevClient = false
21
23
  }) {
22
24
  this.router = router;
23
25
  this.fileSystemResponseMatcher = fileSystemResponseMatcher;
24
26
  this.explicitStaticRouteMatcher = explicitStaticRouteMatcher;
25
27
  this.watch = watch;
26
28
  this.hmrManager = hmrManager;
29
+ this.hostOwnsDevClient = hostOwnsDevClient;
27
30
  }
28
31
  /**
29
32
  * Determines if HMR script should be injected.
@@ -31,7 +34,7 @@ class ServerRouteHandler {
31
34
  * @returns true if in watch mode and HMR manager is enabled
32
35
  */
33
36
  shouldInjectHmrScript() {
34
- return shouldInjectHmrHtmlResponse(this.watch, this.hmrManager);
37
+ return shouldInjectHmrHtmlResponse(this.watch, this.hmrManager, this.hostOwnsDevClient);
35
38
  }
36
39
  /**
37
40
  * Checks if a response contains HTML content.
@@ -1,11 +1,12 @@
1
- import type { EcoPagesAppConfig } from '../../types/internal-types.js';
1
+ import type { EcoPagesAppConfig, IHmrManager } from '../../types/internal-types.js';
2
+ import type { EcoBuildPlugin } from '../../build/build-types.js';
2
3
  import type { ApiHandler, StaticRoute } from '../../types/public-types.js';
3
4
  import type { RouteRegistry } from '../../router/server/route-registry.js';
4
5
  import type { StaticSiteGenerator } from '../../static-site-generator/static-site-generator.js';
5
6
  import type { StaticGenerationRendererResolver } from '../../route-renderer/route-renderer.js';
6
7
  export interface StaticBuildOptions {
7
- preview?: boolean;
8
8
  baseUrl?: string;
9
+ force?: boolean;
9
10
  }
10
11
  export interface ServeOptions {
11
12
  hostname?: string;
@@ -15,10 +16,12 @@ export interface ServerStaticBuilderParams {
15
16
  appConfig: EcoPagesAppConfig;
16
17
  staticSiteGenerator: StaticSiteGenerator;
17
18
  serveOptions: ServeOptions;
19
+ runtimeOrigin: string;
18
20
  apiHandlers?: ApiHandler[];
19
21
  logger?: ServerStaticBuilderLogger;
20
- previewServerFactory?: ServerStaticPreviewServerFactory;
21
22
  entryFile?: string;
23
+ hmrManager?: IHmrManager;
24
+ onRuntimePlugin?: (plugin: EcoBuildPlugin) => void;
22
25
  }
23
26
  /**
24
27
  * Minimal logger dependency used by the static builder.
@@ -29,50 +32,33 @@ export interface ServerStaticBuilderLogger {
29
32
  error(message: string): unknown;
30
33
  }
31
34
  /**
32
- * Preview server factory dependency used when static preview mode is enabled.
33
- */
34
- export interface ServerStaticPreviewServerFactory {
35
- createServer(args: {
36
- appConfig: EcoPagesAppConfig;
37
- options: {
38
- port: number;
39
- };
40
- }): {
41
- server?: {
42
- port?: number;
43
- } | null;
44
- };
45
- }
46
- /**
47
- * Handles static site generation and previews.
35
+ * Handles static site generation.
48
36
  */
49
37
  export declare class ServerStaticBuilder {
50
38
  private readonly appConfig;
51
39
  private readonly staticSiteGenerator;
52
40
  private readonly serveOptions;
41
+ private readonly runtimeOrigin;
53
42
  private readonly apiHandlers;
54
43
  private readonly logger;
55
- private readonly previewServerFactory;
56
44
  private readonly entryFile;
57
- constructor({ appConfig, staticSiteGenerator, serveOptions, apiHandlers, logger, previewServerFactory, entryFile, }: ServerStaticBuilderParams);
58
- private warnApiHandlersUnavailableInStaticMode;
45
+ private readonly hmrManager?;
46
+ private readonly onRuntimePlugin?;
47
+ constructor({ appConfig, staticSiteGenerator, serveOptions, runtimeOrigin, apiHandlers, logger, entryFile, hmrManager, onRuntimePlugin, }: ServerStaticBuilderParams);
59
48
  private prepareExportDirectory;
60
49
  private refreshRuntimeAssets;
61
50
  /**
62
51
  * Bundles the server entry file for production use.
63
52
  *
64
53
  * @remarks
65
- * When the project has API endpoints, the entry file must be bundled
66
- * into a single JS file so the production server can start without
67
- * on-the-fly TypeScript transpilation. The bundle is output to
68
- * `dist/{SERVER_BUNDLE_DIR}/{SERVER_BUNDLE_FILENAME}` and used by
69
- * `ecopages start` in production.
54
+ * Every production build emits a runnable server entry for `ecopages start`,
55
+ * including static-only and websocket-only apps. Incremental builds may skip
56
+ * Rolldown when the `.eco/.server-entry` cache is still valid.
70
57
  *
71
58
  * Package imports remain external so native addons and runtime-owned
72
59
  * dependencies continue to load through the app's installed
73
60
  * `node_modules` tree. Only the app entry graph is bundled.
74
61
  *
75
- * Skips silently when no API endpoints are registered (static-only site).
76
62
  * Throws if the build adapter is unavailable, is owned by a host
77
63
  * runtime, or bundling fails.
78
64
  *
@@ -81,7 +67,6 @@ export declare class ServerStaticBuilder {
81
67
  private bundleServerEntry;
82
68
  /**
83
69
  * Generates a static build of the site for deployment.
84
- * @param options.preview - If true, starts a preview server after build
85
70
  * @param dependencies.router - The initialized router
86
71
  * @param dependencies.routeRendererFactory - The route renderer factory
87
72
  * @param dependencies.staticRoutes - Explicit static routes registered via app.static()
@@ -1,53 +1,65 @@
1
1
  import path from "node:path";
2
2
  import { fileSystem } from "@ecopages/file-system";
3
3
  import { DEFAULT_ECOPAGES_HOSTNAME, DEFAULT_ECOPAGES_PORT } from "../../config/constants.js";
4
- import { StaticContentServer } from "../../dev/sc-server.js";
5
4
  import { appLogger } from "../../global/app-logger.js";
6
- import { getAppBuildAdapter } from "../../build/build-adapter.js";
7
- import { resolveEntryFile, SERVER_BUNDLE_DIR, SERVER_BUNDLE_FILENAME } from "../../utils/resolve-entry-file.js";
5
+ import { build, getAppBuildAdapter, setupAppRuntimePlugins } from "../../build/build-adapter.js";
6
+ import { resolveBuildProfileOptions } from "../../build/build-profile-options.js";
7
+ import {
8
+ getServerBundleOutputPaths,
9
+ lookupServerEntryBuildCache,
10
+ recordServerEntryBuildCache,
11
+ writeServerBundleDeployManifest
12
+ } from "../../build/server-entry-build-cache.js";
13
+ import { getInstalledServerEntryBuildExecutor } from "../../build/runtime-build-executor.js";
14
+ import {
15
+ clearProductionBuildCaches,
16
+ shouldResetStaticExportDirectory
17
+ } from "../../static-site-generator/static-build-invalidation.js";
18
+ import { resolveEntryFile, SERVER_BUNDLE_FILENAME } from "../../utils/resolve-entry-file.js";
8
19
  class ServerStaticBuilder {
9
20
  appConfig;
10
21
  staticSiteGenerator;
11
22
  serveOptions;
23
+ runtimeOrigin;
12
24
  apiHandlers;
13
25
  logger;
14
- previewServerFactory;
15
26
  entryFile;
27
+ hmrManager;
28
+ onRuntimePlugin;
16
29
  constructor({
17
30
  appConfig,
18
31
  staticSiteGenerator,
19
32
  serveOptions,
33
+ runtimeOrigin,
20
34
  apiHandlers,
21
35
  logger,
22
- previewServerFactory,
23
- entryFile
36
+ entryFile,
37
+ hmrManager,
38
+ onRuntimePlugin
24
39
  }) {
25
40
  this.appConfig = appConfig;
26
41
  this.staticSiteGenerator = staticSiteGenerator;
27
42
  this.serveOptions = serveOptions;
43
+ this.runtimeOrigin = runtimeOrigin;
28
44
  this.apiHandlers = apiHandlers ?? [];
29
45
  this.logger = logger ?? appLogger;
30
- this.previewServerFactory = previewServerFactory ?? StaticContentServer;
31
46
  this.entryFile = resolveEntryFile({ entryFile });
47
+ this.hmrManager = hmrManager;
48
+ this.onRuntimePlugin = onRuntimePlugin;
32
49
  }
33
- warnApiHandlersUnavailableInStaticMode() {
34
- if (this.apiHandlers.length === 0) {
35
- return;
50
+ prepareExportDirectory(force) {
51
+ if (force) {
52
+ clearProductionBuildCaches(this.appConfig);
36
53
  }
37
- const uniqueHandlers = Array.from(
38
- new Set(this.apiHandlers.map((handler) => `${handler.method} ${handler.path}`))
39
- );
40
- const visibleHandlers = uniqueHandlers.slice(0, 5).join(", ");
41
- const remainingCount = uniqueHandlers.length - Math.min(uniqueHandlers.length, 5);
42
- const summary = remainingCount > 0 ? `${visibleHandlers}, +${remainingCount} more` : visibleHandlers;
43
- this.logger.warn(
44
- "Registered API endpoints are not available in static build or preview modes because no server runtime is started. They are excluded from the generated output.\n",
45
- `\u27A4 ${summary}`
46
- );
47
- }
48
- prepareExportDirectory() {
49
54
  const exportDir = this.appConfig.absolutePaths?.distDir ?? path.join(this.appConfig.rootDir, this.appConfig.distDir);
50
- fileSystem.ensureDir(exportDir, true);
55
+ const shouldCleanDist = shouldResetStaticExportDirectory(this.appConfig, force);
56
+ fileSystem.ensureDir(exportDir, shouldCleanDist);
57
+ if (shouldCleanDist) {
58
+ this.appConfig.runtime = {
59
+ ...this.appConfig.runtime ?? {},
60
+ runtimeAssetsPrepared: false
61
+ };
62
+ }
51
63
  const srcPublicDir = path.join(
52
64
  this.appConfig.rootDir,
53
65
  this.appConfig.srcDir ?? "src",
@@ -56,39 +68,39 @@ class ServerStaticBuilder {
56
68
  if (fileSystem.exists(srcPublicDir)) {
57
69
  fileSystem.copyDir(srcPublicDir, exportDir);
58
70
  }
71
+ return !shouldCleanDist;
59
72
  }
60
73
  async refreshRuntimeAssets() {
61
- for (const processor of this.appConfig.processors.values()) {
62
- await processor.setup();
63
- }
64
- for (const integration of this.appConfig.integrations) {
65
- await integration.setup();
74
+ appLogger.debugTime("refreshRuntimeAssets");
75
+ try {
76
+ await setupAppRuntimePlugins({
77
+ appConfig: this.appConfig,
78
+ runtimeOrigin: this.runtimeOrigin,
79
+ hmrManager: this.hmrManager,
80
+ onRuntimePlugin: this.onRuntimePlugin
81
+ });
82
+ } finally {
83
+ appLogger.debugTimeEnd("refreshRuntimeAssets");
66
84
  }
67
85
  }
68
86
  /**
69
87
  * Bundles the server entry file for production use.
70
88
  *
71
89
  * @remarks
72
- * When the project has API endpoints, the entry file must be bundled
73
- * into a single JS file so the production server can start without
74
- * on-the-fly TypeScript transpilation. The bundle is output to
75
- * `dist/{SERVER_BUNDLE_DIR}/{SERVER_BUNDLE_FILENAME}` and used by
76
- * `ecopages start` in production.
90
+ * Every production build emits a runnable server entry for `ecopages start`,
91
+ * including static-only and websocket-only apps. Incremental builds may skip
92
+ * Rolldown when the `.eco/.server-entry` cache is still valid.
77
93
  *
78
94
  * Package imports remain external so native addons and runtime-owned
79
95
  * dependencies continue to load through the app's installed
80
96
  * `node_modules` tree. Only the app entry graph is bundled.
81
97
  *
82
- * Skips silently when no API endpoints are registered (static-only site).
83
98
  * Throws if the build adapter is unavailable, is owned by a host
84
99
  * runtime, or bundling fails.
85
100
  *
86
101
  * @throws If the build adapter is unavailable, is host-owned, or bundling fails.
87
102
  */
88
- async bundleServerEntry() {
89
- if (this.apiHandlers.length === 0) {
90
- return;
91
- }
103
+ async bundleServerEntry(options) {
92
104
  const buildAdapter = getAppBuildAdapter(this.appConfig);
93
105
  if (buildAdapter.ownership === "vite-host") {
94
106
  throw new Error(
@@ -101,60 +113,69 @@ class ServerStaticBuilder {
101
113
  `Cannot bundle server entry: file "${this.entryFile}" not found in "${this.appConfig.rootDir}".`
102
114
  );
103
115
  }
104
- const distDir = this.appConfig.absolutePaths?.distDir ?? path.join(this.appConfig.rootDir, this.appConfig.distDir);
105
- const serverOutdir = path.join(distDir, SERVER_BUNDLE_DIR);
106
- this.logger.info("Bundling server entry file...");
107
- const result = await buildAdapter.build({
108
- entrypoints: [entryPath],
109
- outdir: serverOutdir,
110
- naming: SERVER_BUNDLE_FILENAME,
111
- target: "node",
112
- format: "esm",
113
- sourcemap: "hidden",
114
- externalPackages: true,
115
- root: this.appConfig.rootDir
116
+ const { serverOutdir, serverEntryPath } = getServerBundleOutputPaths(this.appConfig);
117
+ const cached = lookupServerEntryBuildCache({
118
+ appConfig: this.appConfig,
119
+ entryPath,
120
+ force: options?.force
116
121
  });
122
+ if (cached) {
123
+ const hasBundle = cached.outputPaths.some(
124
+ (outputPath) => path.resolve(outputPath) === path.resolve(serverEntryPath) && fileSystem.exists(outputPath)
125
+ );
126
+ if (hasBundle) {
127
+ this.logger.info("Reusing cached server entry bundle");
128
+ writeServerBundleDeployManifest(this.appConfig, serverEntryPath);
129
+ return;
130
+ }
131
+ }
132
+ this.logger.info("Bundling server entry file...");
133
+ const buildOptions = {
134
+ ...resolveBuildProfileOptions("server-entry", this.appConfig, {
135
+ entrypoints: [entryPath],
136
+ outdir: serverOutdir,
137
+ naming: SERVER_BUNDLE_FILENAME,
138
+ sourcemap: "hidden"
139
+ }),
140
+ entrypoints: [entryPath]
141
+ };
142
+ const result = await build(buildOptions, getInstalledServerEntryBuildExecutor(this.appConfig));
117
143
  if (!result.success) {
118
144
  const errorMessages = result.logs.map((log) => log.message).join("\n");
119
145
  throw new Error(`Failed to bundle server entry file:
120
146
  ${errorMessages}`);
121
147
  }
148
+ const outputPaths = result.outputs.length > 0 ? result.outputs.map((output) => output.path) : fileSystem.exists(serverEntryPath) ? [serverEntryPath] : [];
149
+ recordServerEntryBuildCache({
150
+ appConfig: this.appConfig,
151
+ entryPath,
152
+ buildResult: result,
153
+ outputPaths
154
+ });
155
+ writeServerBundleDeployManifest(this.appConfig, serverEntryPath);
122
156
  this.logger.info("Server entry file bundled successfully");
123
157
  }
124
158
  /**
125
159
  * Generates a static build of the site for deployment.
126
- * @param options.preview - If true, starts a preview server after build
127
160
  * @param dependencies.router - The initialized router
128
161
  * @param dependencies.routeRendererFactory - The route renderer factory
129
162
  * @param dependencies.staticRoutes - Explicit static routes registered via app.static()
130
163
  */
131
164
  async build(options, dependencies) {
132
- const { preview = false, baseUrl: explicitBaseUrl } = options ?? {};
165
+ const { baseUrl: explicitBaseUrl, force = false } = options ?? {};
133
166
  const baseUrl = explicitBaseUrl ?? `http://${this.serveOptions.hostname || DEFAULT_ECOPAGES_HOSTNAME}:${this.serveOptions.port || DEFAULT_ECOPAGES_PORT}`;
134
- this.warnApiHandlersUnavailableInStaticMode();
135
- this.prepareExportDirectory();
167
+ const preserveExportDirectory = this.prepareExportDirectory(force);
136
168
  await this.refreshRuntimeAssets();
137
- await this.bundleServerEntry();
169
+ await this.bundleServerEntry({ force });
138
170
  await this.staticSiteGenerator.run({
139
171
  router: dependencies.router,
140
172
  baseUrl,
141
173
  routeRendererFactory: dependencies.routeRendererFactory,
142
- staticRoutes: dependencies.staticRoutes
174
+ staticRoutes: dependencies.staticRoutes,
175
+ force,
176
+ preserveExportDirectory
143
177
  });
144
- if (!preview) {
145
- this.logger.info("Build completed");
146
- return;
147
- }
148
- const previewPort = this.serveOptions.port || DEFAULT_ECOPAGES_PORT;
149
- const { server } = this.previewServerFactory.createServer({
150
- appConfig: this.appConfig,
151
- options: { port: Number(previewPort) }
152
- });
153
- if (server?.port) {
154
- this.logger.info(`Preview running at http://localhost:${server.port}`);
155
- } else {
156
- this.logger.error("Failed to start preview server");
157
- }
178
+ this.logger.info("Build completed");
158
179
  }
159
180
  }
160
181
  export {
@@ -10,6 +10,7 @@ import type { ServerModuleTranspiler } from '../../services/module-loading/serve
10
10
  type HandleFileChangeOptions = {
11
11
  broadcast?: boolean;
12
12
  };
13
+ export declare function resolveHmrRegistrationTimeoutMs(explicitTimeoutMs?: number): number;
13
14
  type SharedHmrManagerParams = {
14
15
  appConfig: EcoPagesAppConfig;
15
16
  bridge: IClientBridge;
@@ -45,6 +46,16 @@ export declare abstract class SharedHmrManager implements IHmrManager {
45
46
  broadcast(event: ClientBridgeEvent): void;
46
47
  handleFileChange(filePath: string, options?: HandleFileChangeOptions): Promise<void>;
47
48
  getOutputUrl(entrypointPath: string): string | undefined;
49
+ /**
50
+ * Returns the emitted HMR script output when the artifact already exists on disk.
51
+ *
52
+ * SSR must not block on entrypoint registration when a previous build already
53
+ * produced the browser bundle.
54
+ */
55
+ getResolvedScriptOutput(entrypointPath: string): {
56
+ outputUrl: string;
57
+ outputPath: string;
58
+ } | undefined;
48
59
  getWatchedFiles(): Map<string, string>;
49
60
  getDistDir(): string;
50
61
  getPlugins(): EcoBuildPlugin[];
@@ -55,5 +66,6 @@ export declare abstract class SharedHmrManager implements IHmrManager {
55
66
  protected emitStrictEntrypoint(entrypointPath: string): Promise<void>;
56
67
  protected emitScriptEntrypoint(entrypointPath: string, outputPath: string): Promise<void>;
57
68
  stop(): void;
69
+ [Symbol.dispose](): void;
58
70
  }
59
71
  export {};
@@ -2,11 +2,13 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { RESOLVED_ASSETS_DIR } from "../../config/constants.js";
5
- import { getAppBuildExecutor } from "../../build/build-adapter.js";
5
+ import { requireBuildRuntime } from "../../build/build-runtime.js";
6
6
  import { fileSystem } from "@ecopages/file-system";
7
7
  import { HmrStrategyType } from "../../hmr/hmr-strategy.js";
8
8
  import { DefaultHmrStrategy } from "../../hmr/strategies/default-hmr-strategy.js";
9
9
  import { JsHmrStrategy } from "../../hmr/strategies/js-hmr-strategy.js";
10
+ import { ServerRenderedTemplateHmrStrategy } from "../../hmr/strategies/server-rendered-template-hmr-strategy.js";
11
+ import { DevelopmentInvalidationService } from "../../services/invalidation/development-invalidation.service.js";
10
12
  import { appLogger } from "../../global/app-logger.js";
11
13
  import { HmrEntrypointRegistrar } from "./hmr-entrypoint-registrar.js";
12
14
  import { BrowserBundleService } from "../../services/assets/browser-bundle.service.js";
@@ -16,6 +18,24 @@ import {
16
18
  setAppEntrypointDependencyGraph
17
19
  } from "../../services/runtime-state/entrypoint-dependency-graph.service.js";
18
20
  import { resolveInternalExecutionDir, resolveInternalWorkDir } from "../../utils/resolve-work-dir.js";
21
+ const DEFAULT_HMR_REGISTRATION_TIMEOUT_MS = 4e3;
22
+ const DEVELOPMENT_HMR_REGISTRATION_TIMEOUT_MS = 15e3;
23
+ function resolveHmrRegistrationTimeoutMs(explicitTimeoutMs) {
24
+ if (explicitTimeoutMs !== void 0) {
25
+ return explicitTimeoutMs;
26
+ }
27
+ const envTimeoutMs = process.env.ECOPAGES_HMR_REGISTRATION_TIMEOUT_MS;
28
+ if (envTimeoutMs !== void 0 && envTimeoutMs !== "") {
29
+ const parsedTimeoutMs = Number(envTimeoutMs);
30
+ if (Number.isFinite(parsedTimeoutMs) && parsedTimeoutMs > 0) {
31
+ return parsedTimeoutMs;
32
+ }
33
+ }
34
+ if (process.env.NODE_ENV === "development") {
35
+ return DEVELOPMENT_HMR_REGISTRATION_TIMEOUT_MS;
36
+ }
37
+ return DEFAULT_HMR_REGISTRATION_TIMEOUT_MS;
38
+ }
19
39
  class SharedHmrManager {
20
40
  appConfig;
21
41
  bridge;
@@ -24,13 +44,13 @@ class SharedHmrManager {
24
44
  entrypointRegistrations = /* @__PURE__ */ new Map();
25
45
  distDir;
26
46
  plugins = [];
27
- enabled = true;
47
+ enabled = false;
28
48
  strategies = [];
29
49
  entrypointRegistrar;
30
50
  browserBundleService;
31
51
  entrypointDependencyGraph;
32
52
  serverModuleTranspiler;
33
- constructor({ appConfig, bridge, registrationTimeoutMs = 4e3 }) {
53
+ constructor({ appConfig, bridge, registrationTimeoutMs }) {
34
54
  this.appConfig = appConfig;
35
55
  this.bridge = bridge;
36
56
  this.distDir = path.join(resolveInternalWorkDir(this.appConfig), RESOLVED_ASSETS_DIR, "_hmr");
@@ -40,7 +60,7 @@ class SharedHmrManager {
40
60
  entrypointRegistrations: this.entrypointRegistrations,
41
61
  watchedFiles: this.watchedFiles,
42
62
  clearFailedRegistration: (entrypointPath) => this.clearFailedEntrypointRegistration(entrypointPath),
43
- registrationTimeoutMs
63
+ registrationTimeoutMs: resolveHmrRegistrationTimeoutMs(registrationTimeoutMs)
44
64
  });
45
65
  this.browserBundleService = new BrowserBundleService(appConfig);
46
66
  this.entrypointDependencyGraph = this.createEntrypointDependencyGraph(
@@ -86,7 +106,12 @@ class SharedHmrManager {
86
106
  getEntrypointDependencyGraph: () => this.entrypointDependencyGraph,
87
107
  shouldProcessEntrypoint: (entrypointPath) => this.shouldJsStrategyProcessEntrypoint(entrypointPath)
88
108
  };
89
- this.strategies = [new JsHmrStrategy(jsContext), new DefaultHmrStrategy()];
109
+ const invalidationService = new DevelopmentInvalidationService(this.appConfig);
110
+ this.strategies = [
111
+ new JsHmrStrategy(jsContext),
112
+ new ServerRenderedTemplateHmrStrategy(invalidationService),
113
+ new DefaultHmrStrategy()
114
+ ];
90
115
  }
91
116
  registerStrategy(strategy) {
92
117
  this.strategies.push(strategy);
@@ -158,6 +183,24 @@ class SharedHmrManager {
158
183
  getOutputUrl(entrypointPath) {
159
184
  return this.watchedFiles.get(entrypointPath);
160
185
  }
186
+ /**
187
+ * Returns the emitted HMR script output when the artifact already exists on disk.
188
+ *
189
+ * SSR must not block on entrypoint registration when a previous build already
190
+ * produced the browser bundle.
191
+ */
192
+ getResolvedScriptOutput(entrypointPath) {
193
+ const normalizedEntrypoint = path.resolve(entrypointPath);
194
+ const relativePath = path.relative(this.appConfig.absolutePaths.srcDir, normalizedEntrypoint);
195
+ const relativePathJs = relativePath.replace(/\.(tsx?|jsx?|mdx?)$/, ".js").replace(/\[([^\]]+)\]/g, "_$1_");
196
+ const urlPath = relativePathJs.split(path.sep).join("/");
197
+ const outputPath = path.join(this.distDir, urlPath);
198
+ if (!fileSystem.exists(outputPath)) {
199
+ return void 0;
200
+ }
201
+ const outputUrl = this.watchedFiles.get(normalizedEntrypoint) ?? `/${path.join(RESOLVED_ASSETS_DIR, "_hmr", urlPath).split(path.sep).join("/")}`;
202
+ return { outputUrl, outputPath };
203
+ }
161
204
  getWatchedFiles() {
162
205
  return this.watchedFiles;
163
206
  }
@@ -175,7 +218,7 @@ class SharedHmrManager {
175
218
  getSrcDir: () => this.appConfig.absolutePaths.srcDir,
176
219
  getLayoutsDir: () => this.appConfig.absolutePaths.layoutsDir,
177
220
  getPagesDir: () => this.appConfig.absolutePaths.pagesDir,
178
- getBuildExecutor: () => getAppBuildExecutor(this.appConfig),
221
+ getBuildExecutor: () => requireBuildRuntime(this.appConfig).getProfile("browser-hmr"),
179
222
  getBrowserBundleService: () => this.browserBundleService,
180
223
  getEntrypointDependencyGraph: () => this.entrypointDependencyGraph,
181
224
  importServerModule: async (filePath) => await this.serverModuleTranspiler.importModule({
@@ -220,6 +263,16 @@ class SharedHmrManager {
220
263
  appLogger.error(`[HMR] Generic script entrypoint build failed for ${entrypointPath}:`, buildResult.logs);
221
264
  return;
222
265
  }
266
+ if (!fileSystem.exists(outputPath) && buildResult.outputs.length > 0) {
267
+ const resolvedOutputPath = path.resolve(outputPath);
268
+ const emittedOutput = buildResult.outputs.find((output) => path.resolve(output.path) === resolvedOutputPath)?.path ?? buildResult.outputs.find((output) => path.basename(output.path) === path.basename(outputPath))?.path;
269
+ if (emittedOutput && fileSystem.exists(emittedOutput)) {
270
+ fileSystem.ensureDir(path.dirname(outputPath));
271
+ if (path.resolve(emittedOutput) !== resolvedOutputPath) {
272
+ fileSystem.copyFile(emittedOutput, outputPath);
273
+ }
274
+ }
275
+ }
223
276
  const entrypointDependencies = buildResult.dependencyGraph?.entrypoints?.[entrypointPath];
224
277
  if (entrypointDependencies) {
225
278
  this.entrypointDependencyGraph.setEntrypointDependencies(entrypointPath, entrypointDependencies);
@@ -235,7 +288,11 @@ class SharedHmrManager {
235
288
  this.entrypointDependencyGraph.reset();
236
289
  this.plugins = [];
237
290
  }
291
+ [Symbol.dispose]() {
292
+ this.stop();
293
+ }
238
294
  }
239
295
  export {
240
- SharedHmrManager
296
+ SharedHmrManager,
297
+ resolveHmrRegistrationTimeoutMs
241
298
  };