@ecopages/core 0.2.0-beta.20 → 0.2.0-beta.21
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.
- package/package.json +2 -2
- package/src/adapters/bun/hmr-manager.d.ts +1 -2
- package/src/adapters/bun/hmr-manager.js +2 -2
- package/src/adapters/bun/server-adapter.js +5 -4
- package/src/adapters/node/node-hmr-manager.d.ts +1 -2
- package/src/adapters/node/node-hmr-manager.js +2 -2
- package/src/adapters/node/server-adapter.js +5 -6
- package/src/adapters/shared/hmr-entrypoint-registrar.d.ts +21 -28
- package/src/adapters/shared/hmr-entrypoint-registrar.js +49 -31
- package/src/adapters/shared/runtime-server-lifecycle.d.ts +9 -1
- package/src/adapters/shared/runtime-server-lifecycle.js +10 -3
- package/src/adapters/shared/server-static-builder.js +4 -1
- package/src/adapters/shared/shared-hmr-manager.d.ts +29 -23
- package/src/adapters/shared/shared-hmr-manager.js +144 -108
- package/src/build/app-build-manifest-runtime.d.ts +1 -2
- package/src/build/app-build-manifest-runtime.js +0 -3
- package/src/hmr/hmr-entrypoint-output.d.ts +18 -3
- package/src/hmr/hmr-entrypoint-output.js +6 -11
- package/src/hmr/hmr-strategy.d.ts +15 -0
- package/src/hmr/hmr-strategy.js +19 -0
- package/src/hmr/strategies/js-hmr-strategy.d.ts +0 -5
- package/src/hmr/strategies/js-hmr-strategy.js +0 -2
- package/src/services/assets/asset-processing-service/processors/script/file-script.processor.d.ts +0 -1
- package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +13 -32
- package/src/services/assets/browser-bundle.service.js +1 -1
- package/src/services/module-loading/page-module-import.service.js +0 -1
- package/src/services/module-loading/route-module-dependency-hasher.js +1 -0
- package/src/types/public-types.d.ts +10 -18
- package/src/watchers/project-watcher.d.ts +2 -2
- package/src/watchers/project-watcher.js +23 -28
- package/src/watchers/project-watcher.test-helpers.js +5 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecopages/core",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.21",
|
|
4
4
|
"description": "Core package for Ecopages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ecopages",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"directory": "packages/core"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@ecopages/file-system": "0.2.0-beta.
|
|
20
|
+
"@ecopages/file-system": "0.2.0-beta.21",
|
|
21
21
|
"@ecopages/logger": "^0.2.3",
|
|
22
22
|
"@ecopages/scripts-injector": "^0.1.5",
|
|
23
23
|
"@oxc-project/runtime": "0.134.0",
|
|
@@ -7,7 +7,6 @@ type BunSocketHandler = WebSocketHandler<unknown>;
|
|
|
7
7
|
export interface HmrManagerParams {
|
|
8
8
|
appConfig: EcoPagesAppConfig;
|
|
9
9
|
bridge: ClientBridge;
|
|
10
|
-
registrationTimeoutMs?: number;
|
|
11
10
|
}
|
|
12
11
|
/**
|
|
13
12
|
* Bun development HMR manager.
|
|
@@ -27,7 +26,7 @@ export declare class HmrManager extends SharedHmrManager {
|
|
|
27
26
|
* generation to `SharedHmrManager`. The Bun subclass only supplies the
|
|
28
27
|
* transport-specific dependency graph policy and websocket hook surface.
|
|
29
28
|
*/
|
|
30
|
-
constructor({ appConfig, bridge
|
|
29
|
+
constructor({ appConfig, bridge }: HmrManagerParams);
|
|
31
30
|
/**
|
|
32
31
|
* Reuses the shared in-memory dependency graph when possible and otherwise
|
|
33
32
|
* creates the Bun-compatible default graph implementation.
|
|
@@ -13,8 +13,8 @@ class HmrManager extends SharedHmrManager {
|
|
|
13
13
|
* generation to `SharedHmrManager`. The Bun subclass only supplies the
|
|
14
14
|
* transport-specific dependency graph policy and websocket hook surface.
|
|
15
15
|
*/
|
|
16
|
-
constructor({ appConfig, bridge
|
|
17
|
-
super({ appConfig, bridge
|
|
16
|
+
constructor({ appConfig, bridge }) {
|
|
17
|
+
super({ appConfig, bridge });
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Reuses the shared in-memory dependency graph when possible and otherwise
|
|
@@ -17,10 +17,10 @@ import { createBunUserWebSocketLifecycle } from "../shared/bun-user-websocket-li
|
|
|
17
17
|
import { createEcopagesSocket } from "../shared/websocket-lifecycle.js";
|
|
18
18
|
import { resolveServeRuntimeOrigin } from "../shared/runtime-app-bootstrap.js";
|
|
19
19
|
import {
|
|
20
|
+
attachHmrToIntegrations,
|
|
20
21
|
disposeDevResources,
|
|
21
22
|
maybeInjectAdapterHmrHtmlResponse,
|
|
22
|
-
prepareRuntimePublicDir
|
|
23
|
-
wireIntegrationHmrManagers
|
|
23
|
+
prepareRuntimePublicDir
|
|
24
24
|
} from "../shared/runtime-server-lifecycle.js";
|
|
25
25
|
import { ClientBridge } from "./client-bridge.js";
|
|
26
26
|
import { setAppDevClientBridge } from "../../dev/client-bridge-registry.js";
|
|
@@ -193,12 +193,13 @@ class BunServerAdapter extends SharedServerAdapter {
|
|
|
193
193
|
await setupAppRuntimePlugins({
|
|
194
194
|
appConfig: this.appConfig,
|
|
195
195
|
runtimeOrigin: this.runtimeOrigin,
|
|
196
|
-
hmrManager: this.hmrManager,
|
|
197
196
|
onRuntimePlugin: (plugin) => {
|
|
198
197
|
this.registerBunRuntimePlugin(plugin);
|
|
199
198
|
}
|
|
200
199
|
});
|
|
201
|
-
|
|
200
|
+
if (options?.watch) {
|
|
201
|
+
attachHmrToIntegrations(this.appConfig, this.hmrManager);
|
|
202
|
+
}
|
|
202
203
|
} catch (error) {
|
|
203
204
|
appLogger.error(`Failed to initialize plugins: ${error instanceof Error ? error.message : String(error)}`);
|
|
204
205
|
throw error;
|
|
@@ -4,7 +4,6 @@ import { SharedHmrManager } from '../shared/shared-hmr-manager.js';
|
|
|
4
4
|
export interface NodeHmrManagerParams {
|
|
5
5
|
appConfig: EcoPagesAppConfig;
|
|
6
6
|
bridge: IClientBridge;
|
|
7
|
-
registrationTimeoutMs?: number;
|
|
8
7
|
}
|
|
9
8
|
/**
|
|
10
9
|
* Node development HMR manager.
|
|
@@ -29,7 +28,7 @@ export declare class NodeHmrManager extends SharedHmrManager {
|
|
|
29
28
|
* actually differs: dependency-graph storage, missing-file tolerance, and how
|
|
30
29
|
* runtime bundle failures disable HMR.
|
|
31
30
|
*/
|
|
32
|
-
constructor({ appConfig, bridge
|
|
31
|
+
constructor({ appConfig, bridge }: NodeHmrManagerParams);
|
|
33
32
|
/**
|
|
34
33
|
* Reuses the shared in-memory dependency graph when the app already has one and
|
|
35
34
|
* otherwise creates the default Node development graph.
|
|
@@ -13,8 +13,8 @@ class NodeHmrManager extends SharedHmrManager {
|
|
|
13
13
|
* actually differs: dependency-graph storage, missing-file tolerance, and how
|
|
14
14
|
* runtime bundle failures disable HMR.
|
|
15
15
|
*/
|
|
16
|
-
constructor({ appConfig, bridge
|
|
17
|
-
super({ appConfig, bridge
|
|
16
|
+
constructor({ appConfig, bridge }) {
|
|
17
|
+
super({ appConfig, bridge });
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Reuses the shared in-memory dependency graph when the app already has one and
|
|
@@ -15,10 +15,10 @@ import { SharedServerAdapter } from "../shared/server-adapter.js";
|
|
|
15
15
|
import { ServerStaticBuilder } from "../shared/server-static-builder.js";
|
|
16
16
|
import { DEFAULT_ECOPAGES_HOSTNAME, DEFAULT_ECOPAGES_PORT } from "../../config/constants.js";
|
|
17
17
|
import {
|
|
18
|
+
attachHmrToIntegrations,
|
|
19
|
+
disposeDevResources,
|
|
18
20
|
maybeInjectAdapterHmrHtmlResponse,
|
|
19
|
-
prepareRuntimePublicDir
|
|
20
|
-
wireIntegrationHmrManagers,
|
|
21
|
-
disposeDevResources
|
|
21
|
+
prepareRuntimePublicDir
|
|
22
22
|
} from "../shared/runtime-server-lifecycle.js";
|
|
23
23
|
import { resolveServeRuntimeOrigin } from "../shared/runtime-app-bootstrap.js";
|
|
24
24
|
import { NodeClientAbortError, NodeHttpRequestBridge } from "./http-request-bridge.js";
|
|
@@ -119,8 +119,7 @@ class NodeServerAdapter extends SharedServerAdapter {
|
|
|
119
119
|
if (!this.deferRuntimeAssetSetup) {
|
|
120
120
|
await setupAppRuntimePlugins({
|
|
121
121
|
appConfig: this.appConfig,
|
|
122
|
-
runtimeOrigin: this.runtimeOrigin
|
|
123
|
-
hmrManager: this.hmrManager ?? void 0
|
|
122
|
+
runtimeOrigin: this.runtimeOrigin
|
|
124
123
|
});
|
|
125
124
|
}
|
|
126
125
|
await this.initializeSharedRouteHandling({
|
|
@@ -298,7 +297,7 @@ class NodeServerAdapter extends SharedServerAdapter {
|
|
|
298
297
|
}
|
|
299
298
|
});
|
|
300
299
|
}
|
|
301
|
-
|
|
300
|
+
attachHmrToIntegrations(this.appConfig, this.hmrManager);
|
|
302
301
|
this.configureSharedResponseHandlers(this.staticRoutes, this.hmrManager);
|
|
303
302
|
const watcher = new ProjectWatcher({
|
|
304
303
|
config: this.appConfig,
|
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Shared runtime state used while registering HMR-owned entrypoints.
|
|
3
|
-
*/
|
|
4
|
-
export interface HmrEntrypointRegistrarOptions {
|
|
5
|
-
/** Absolute source directory used to derive the emitted HMR path. */
|
|
6
|
-
srcDir: string;
|
|
7
|
-
/** Absolute distribution directory where HMR outputs are written. */
|
|
8
|
-
distDir: string;
|
|
9
|
-
/** In-flight registrations keyed by normalized absolute entrypoint path. */
|
|
10
|
-
entrypointRegistrations: Map<string, Promise<string>>;
|
|
11
|
-
/** Stable entrypoint-to-output mapping retained once an entrypoint is registered. */
|
|
12
|
-
watchedFiles: Map<string, string>;
|
|
13
|
-
/** Runtime-specific cleanup invoked when an entrypoint registration fails. */
|
|
14
|
-
clearFailedRegistration?: (entrypointPath: string) => void;
|
|
15
|
-
/** Development-only guardrail for integrations that never finish producing output. */
|
|
16
|
-
registrationTimeoutMs: number;
|
|
17
|
-
}
|
|
1
|
+
import { type ResolvedHmrEntrypoint } from '../../hmr/hmr-entrypoint-output.js';
|
|
18
2
|
/**
|
|
19
3
|
* Runtime-specific hooks required to materialize a single HMR entrypoint.
|
|
20
4
|
*/
|
|
@@ -28,26 +12,35 @@ export interface HmrEntrypointRegistrationOptions {
|
|
|
28
12
|
*/
|
|
29
13
|
getMissingOutputError(entrypointPath: string, outputPath: string): Error;
|
|
30
14
|
}
|
|
15
|
+
export interface HmrEntrypointRegistrarOptions {
|
|
16
|
+
/** Absolute source directory used to derive the emitted HMR path. */
|
|
17
|
+
srcDir: string;
|
|
18
|
+
/** Absolute distribution directory where HMR outputs are written. */
|
|
19
|
+
distDir: string;
|
|
20
|
+
/** Runtime-specific cleanup invoked when an entrypoint registration fails. */
|
|
21
|
+
clearFailedRegistration?: (entrypointPath: string) => void;
|
|
22
|
+
}
|
|
31
23
|
/**
|
|
32
24
|
* Coordinates the shared HMR entrypoint registration lifecycle for both Node and Bun managers.
|
|
33
25
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* Runtime-specific managers remain responsible for the actual emit step and any cleanup outside
|
|
38
|
-
* this shared registration flow.
|
|
26
|
+
* @remarks
|
|
27
|
+
* The registrar owns in-flight deduplication and the registered entrypoint table.
|
|
28
|
+
* A source path is committed only after emit succeeds and the expected output exists on disk.
|
|
39
29
|
*/
|
|
40
30
|
export declare class HmrEntrypointRegistrar {
|
|
41
31
|
private readonly options;
|
|
32
|
+
private readonly inFlight;
|
|
33
|
+
private readonly registered;
|
|
42
34
|
constructor(options: HmrEntrypointRegistrarOptions);
|
|
35
|
+
getRegistered(): ReadonlyMap<string, ResolvedHmrEntrypoint>;
|
|
36
|
+
getWatchedFiles(): Map<string, string>;
|
|
37
|
+
clearRegistration(entrypointPath: string): void;
|
|
38
|
+
clearAll(): void;
|
|
43
39
|
/**
|
|
44
|
-
* Registers a single source entrypoint and returns
|
|
40
|
+
* Registers a single source entrypoint and returns its verified HMR artifact.
|
|
45
41
|
*
|
|
46
|
-
* Concurrent requests for the same normalized entrypoint share the same in-flight promise
|
|
47
|
-
* integration only builds once per registration cycle.
|
|
42
|
+
* Concurrent requests for the same normalized entrypoint share the same in-flight promise.
|
|
48
43
|
*/
|
|
49
|
-
registerEntrypoint(entrypointPath: string, registrationOptions: HmrEntrypointRegistrationOptions): Promise<
|
|
44
|
+
registerEntrypoint(entrypointPath: string, registrationOptions: HmrEntrypointRegistrationOptions): Promise<ResolvedHmrEntrypoint>;
|
|
50
45
|
private registerEntrypointInternal;
|
|
51
|
-
private awaitEntrypointRegistration;
|
|
52
|
-
private getEntrypointOutput;
|
|
53
46
|
}
|
|
@@ -1,62 +1,80 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { fileSystem } from "@ecopages/file-system";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
removeStaleHmrEntrypointOutput,
|
|
5
|
+
resolveHmrEntrypointOutputPaths
|
|
6
|
+
} from "../../hmr/hmr-entrypoint-output.js";
|
|
4
7
|
class HmrEntrypointRegistrar {
|
|
5
8
|
options;
|
|
9
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
10
|
+
registered = /* @__PURE__ */ new Map();
|
|
6
11
|
constructor(options) {
|
|
7
12
|
this.options = options;
|
|
8
13
|
}
|
|
14
|
+
getRegistered() {
|
|
15
|
+
return this.registered;
|
|
16
|
+
}
|
|
17
|
+
getWatchedFiles() {
|
|
18
|
+
const watchedFiles = /* @__PURE__ */ new Map();
|
|
19
|
+
for (const [sourcePath, entrypoint] of this.registered) {
|
|
20
|
+
watchedFiles.set(sourcePath, entrypoint.outputUrl);
|
|
21
|
+
}
|
|
22
|
+
return watchedFiles;
|
|
23
|
+
}
|
|
24
|
+
clearRegistration(entrypointPath) {
|
|
25
|
+
this.registered.delete(path.resolve(entrypointPath));
|
|
26
|
+
}
|
|
27
|
+
clearAll() {
|
|
28
|
+
this.inFlight.clear();
|
|
29
|
+
this.registered.clear();
|
|
30
|
+
}
|
|
9
31
|
/**
|
|
10
|
-
* Registers a single source entrypoint and returns
|
|
32
|
+
* Registers a single source entrypoint and returns its verified HMR artifact.
|
|
11
33
|
*
|
|
12
|
-
* Concurrent requests for the same normalized entrypoint share the same in-flight promise
|
|
13
|
-
* integration only builds once per registration cycle.
|
|
34
|
+
* Concurrent requests for the same normalized entrypoint share the same in-flight promise.
|
|
14
35
|
*/
|
|
15
36
|
async registerEntrypoint(entrypointPath, registrationOptions) {
|
|
16
37
|
const normalizedEntrypoint = path.resolve(entrypointPath);
|
|
17
|
-
const
|
|
38
|
+
const existing = this.registered.get(normalizedEntrypoint);
|
|
39
|
+
if (existing && fileSystem.exists(existing.outputPath)) {
|
|
40
|
+
return existing;
|
|
41
|
+
}
|
|
42
|
+
const existingRegistration = this.inFlight.get(normalizedEntrypoint);
|
|
18
43
|
if (existingRegistration) {
|
|
19
|
-
return await
|
|
44
|
+
return await existingRegistration;
|
|
20
45
|
}
|
|
21
46
|
const registration = this.registerEntrypointInternal(normalizedEntrypoint, registrationOptions);
|
|
22
|
-
this.
|
|
47
|
+
this.inFlight.set(normalizedEntrypoint, registration);
|
|
23
48
|
try {
|
|
24
|
-
return await
|
|
49
|
+
return await registration;
|
|
25
50
|
} catch (error) {
|
|
26
51
|
this.options.clearFailedRegistration?.(normalizedEntrypoint);
|
|
52
|
+
this.registered.delete(normalizedEntrypoint);
|
|
27
53
|
throw error;
|
|
28
54
|
} finally {
|
|
29
|
-
this.
|
|
55
|
+
if (this.inFlight.get(normalizedEntrypoint) === registration) {
|
|
56
|
+
this.inFlight.delete(normalizedEntrypoint);
|
|
57
|
+
}
|
|
30
58
|
}
|
|
31
59
|
}
|
|
32
60
|
async registerEntrypointInternal(entrypointPath, registrationOptions) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
61
|
+
const { outputPath, outputUrl } = resolveHmrEntrypointOutputPaths(
|
|
62
|
+
this.options.srcDir,
|
|
63
|
+
this.options.distDir,
|
|
64
|
+
entrypointPath
|
|
65
|
+
);
|
|
38
66
|
removeStaleHmrEntrypointOutput(outputPath, "HMR");
|
|
39
67
|
await registrationOptions.emit(entrypointPath, outputPath);
|
|
40
68
|
if (!fileSystem.exists(outputPath)) {
|
|
41
69
|
throw registrationOptions.getMissingOutputError(entrypointPath, outputPath);
|
|
42
70
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return
|
|
50
|
-
registration,
|
|
51
|
-
new Promise((_, reject) => {
|
|
52
|
-
setTimeout(() => {
|
|
53
|
-
reject(new Error(`[HMR] Timed out registering entrypoint: ${entrypointPath}`));
|
|
54
|
-
}, this.options.registrationTimeoutMs);
|
|
55
|
-
})
|
|
56
|
-
]);
|
|
57
|
-
}
|
|
58
|
-
getEntrypointOutput(entrypointPath) {
|
|
59
|
-
return resolveHmrEntrypointOutputPaths(this.options.srcDir, this.options.distDir, entrypointPath);
|
|
71
|
+
const resolved = {
|
|
72
|
+
sourcePath: entrypointPath,
|
|
73
|
+
outputPath,
|
|
74
|
+
outputUrl
|
|
75
|
+
};
|
|
76
|
+
this.registered.set(entrypointPath, resolved);
|
|
77
|
+
return resolved;
|
|
60
78
|
}
|
|
61
79
|
}
|
|
62
80
|
export {
|
|
@@ -6,7 +6,15 @@ import type { ProjectWatcher } from '../../watchers/project-watcher.js';
|
|
|
6
6
|
*/
|
|
7
7
|
export declare function prepareRuntimePublicDir(appConfig: EcoPagesAppConfig): void;
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Attaches the shared HMR manager to every integration exactly once per app config.
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* Runtime plugin setup must not call `integration.setHmrManager()` directly. Adapters invoke
|
|
13
|
+
* this after the manager is enabled so late plugin init cannot double-bind integrations.
|
|
14
|
+
*/
|
|
15
|
+
export declare function attachHmrToIntegrations(appConfig: EcoPagesAppConfig, hmrManager: IHmrManager): void;
|
|
16
|
+
/**
|
|
17
|
+
* @deprecated Use {@link attachHmrToIntegrations} instead.
|
|
10
18
|
*/
|
|
11
19
|
export declare function wireIntegrationHmrManagers(appConfig: EcoPagesAppConfig, hmrManager: IHmrManager): void;
|
|
12
20
|
/**
|
|
@@ -2,7 +2,6 @@ import path from "node:path";
|
|
|
2
2
|
import { fileSystem } from "@ecopages/file-system";
|
|
3
3
|
import { RESOLVED_ASSETS_DIR } from "../../config/constants.js";
|
|
4
4
|
import { disposeAppBuildRuntime } from "../../build/build-runtime.js";
|
|
5
|
-
import { getAppBrowserBuildPlugins } from "../../build/build-adapter.js";
|
|
6
5
|
import { copyRuntimePublicDirIfChanged } from "./copy-runtime-public-dir.js";
|
|
7
6
|
import { clearAppDevClientBridge } from "../../dev/client-bridge-registry.js";
|
|
8
7
|
import { clearAppHmrManager } from "../../dev/hmr-manager-registry.js";
|
|
@@ -14,12 +13,19 @@ function prepareRuntimePublicDir(appConfig) {
|
|
|
14
13
|
}
|
|
15
14
|
fileSystem.ensureDir(path.join(appConfig.absolutePaths.distDir, RESOLVED_ASSETS_DIR));
|
|
16
15
|
}
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
const attachedHmrApps = /* @__PURE__ */ new WeakSet();
|
|
17
|
+
function attachHmrToIntegrations(appConfig, hmrManager) {
|
|
18
|
+
if (attachedHmrApps.has(appConfig)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
attachedHmrApps.add(appConfig);
|
|
19
22
|
for (const integration of appConfig.integrations) {
|
|
20
23
|
integration.setHmrManager(hmrManager);
|
|
21
24
|
}
|
|
22
25
|
}
|
|
26
|
+
function wireIntegrationHmrManagers(appConfig, hmrManager) {
|
|
27
|
+
attachHmrToIntegrations(appConfig, hmrManager);
|
|
28
|
+
}
|
|
23
29
|
async function maybeInjectAdapterHmrHtmlResponse(response, options) {
|
|
24
30
|
if (shouldInjectHmrHtmlResponse(options.watch, options.hmrManager, options.hostOwnsDevClient) && isHtmlResponse(response)) {
|
|
25
31
|
return injectHmrRuntimeIntoHtmlResponse(response);
|
|
@@ -36,6 +42,7 @@ async function disposeDevResources(options) {
|
|
|
36
42
|
await options.previewHost.stop();
|
|
37
43
|
}
|
|
38
44
|
export {
|
|
45
|
+
attachHmrToIntegrations,
|
|
39
46
|
disposeDevResources,
|
|
40
47
|
maybeInjectAdapterHmrHtmlResponse,
|
|
41
48
|
prepareRuntimePublicDir,
|
|
@@ -3,6 +3,7 @@ import { fileSystem } from "@ecopages/file-system";
|
|
|
3
3
|
import { DEFAULT_ECOPAGES_HOSTNAME, DEFAULT_ECOPAGES_PORT } from "../../config/constants.js";
|
|
4
4
|
import { appLogger } from "../../global/app-logger.js";
|
|
5
5
|
import { build, getAppBuildAdapter, setupAppRuntimePlugins } from "../../build/build-adapter.js";
|
|
6
|
+
import { attachHmrToIntegrations } from "./runtime-server-lifecycle.js";
|
|
6
7
|
import { resolveBuildProfileOptions } from "../../build/build-profile-options.js";
|
|
7
8
|
import {
|
|
8
9
|
getServerBundleOutputPaths,
|
|
@@ -76,9 +77,11 @@ class ServerStaticBuilder {
|
|
|
76
77
|
await setupAppRuntimePlugins({
|
|
77
78
|
appConfig: this.appConfig,
|
|
78
79
|
runtimeOrigin: this.runtimeOrigin,
|
|
79
|
-
hmrManager: this.hmrManager,
|
|
80
80
|
onRuntimePlugin: this.onRuntimePlugin
|
|
81
81
|
});
|
|
82
|
+
if (this.hmrManager?.isEnabled()) {
|
|
83
|
+
attachHmrToIntegrations(this.appConfig, this.hmrManager);
|
|
84
|
+
}
|
|
82
85
|
} finally {
|
|
83
86
|
appLogger.debugTimeEnd("refreshRuntimeAssets");
|
|
84
87
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import { type ResolvedHmrEntrypoint } from '../../hmr/hmr-entrypoint-output.js';
|
|
2
3
|
import type { DefaultHmrContext, EcoPagesAppConfig, IHmrManager, IClientBridge } from '../../types/internal-types.js';
|
|
3
|
-
import type { EcoBuildPlugin } from '../../build/build-types.js';
|
|
4
4
|
import { type HmrStrategy } from '../../hmr/hmr-strategy.js';
|
|
5
|
+
import { DevelopmentInvalidationService } from '../../services/invalidation/development-invalidation.service.js';
|
|
5
6
|
import type { ClientBridgeEvent } from '../../types/public-types.js';
|
|
6
7
|
import { HmrEntrypointRegistrar } from './hmr-entrypoint-registrar.js';
|
|
7
8
|
import { BrowserBundleService } from '../../services/assets/browser-bundle.service.js';
|
|
@@ -10,28 +11,25 @@ import type { ServerModuleTranspiler } from '../../services/module-loading/serve
|
|
|
10
11
|
type HandleFileChangeOptions = {
|
|
11
12
|
broadcast?: boolean;
|
|
12
13
|
};
|
|
13
|
-
export declare function resolveHmrRegistrationTimeoutMs(explicitTimeoutMs?: number): number;
|
|
14
14
|
type SharedHmrManagerParams = {
|
|
15
15
|
appConfig: EcoPagesAppConfig;
|
|
16
16
|
bridge: IClientBridge;
|
|
17
|
-
registrationTimeoutMs?: number;
|
|
18
17
|
};
|
|
19
18
|
export declare abstract class SharedHmrManager implements IHmrManager {
|
|
20
19
|
readonly appConfig: EcoPagesAppConfig;
|
|
21
20
|
protected readonly bridge: IClientBridge;
|
|
22
21
|
protected watchers: Map<string, fs.FSWatcher>;
|
|
23
|
-
protected watchedFiles: Map<string, string>;
|
|
24
|
-
protected entrypointRegistrations: Map<string, Promise<string>>;
|
|
25
22
|
protected distDir: string;
|
|
26
|
-
protected plugins: EcoBuildPlugin[];
|
|
27
23
|
protected enabled: boolean;
|
|
28
24
|
protected strategies: HmrStrategy[];
|
|
29
25
|
protected readonly entrypointRegistrar: HmrEntrypointRegistrar;
|
|
30
26
|
protected readonly browserBundleService: BrowserBundleService;
|
|
27
|
+
protected readonly invalidationService: DevelopmentInvalidationService;
|
|
31
28
|
protected readonly entrypointDependencyGraph: EntrypointDependencyGraph;
|
|
32
29
|
protected readonly serverModuleTranspiler: ServerModuleTranspiler;
|
|
33
30
|
private runtimeBuildPromise;
|
|
34
|
-
|
|
31
|
+
private runtimeReady;
|
|
32
|
+
constructor({ appConfig, bridge }: SharedHmrManagerParams);
|
|
35
33
|
protected abstract createEntrypointDependencyGraph(existingEntrypointDependencyGraph: EntrypointDependencyGraph): EntrypointDependencyGraph;
|
|
36
34
|
protected shouldSkipMissingFileChange(_filePath: string): boolean;
|
|
37
35
|
protected onRuntimeBundleFailure(error: unknown): void;
|
|
@@ -39,26 +37,39 @@ export declare abstract class SharedHmrManager implements IHmrManager {
|
|
|
39
37
|
protected shouldJsStrategyProcessEntrypoint(entrypointPath: string): boolean;
|
|
40
38
|
protected initializeStrategies(): void;
|
|
41
39
|
registerStrategy(strategy: HmrStrategy): void;
|
|
42
|
-
setPlugins(plugins: EcoBuildPlugin[]): void;
|
|
43
40
|
setEnabled(enabled: boolean): void;
|
|
44
41
|
isEnabled(): boolean;
|
|
45
42
|
isRuntimeReady(): boolean;
|
|
46
43
|
/**
|
|
47
|
-
* Builds the browser HMR runtime once and reuses the in-flight build for concurrent callers.
|
|
44
|
+
* Builds the browser HMR runtime once per manager session and reuses the in-flight build for concurrent callers.
|
|
48
45
|
*/
|
|
49
46
|
ensureRuntimeReady(): Promise<boolean>;
|
|
50
47
|
buildRuntime(): Promise<void>;
|
|
51
48
|
getRuntimePath(): string;
|
|
52
49
|
private buildRuntimeInternal;
|
|
50
|
+
broadcast(event: ClientBridgeEvent): void;
|
|
51
|
+
handleFileChange(filePath: string, options?: HandleFileChangeOptions): Promise<void>;
|
|
52
|
+
private getStrategiesByPriority;
|
|
53
|
+
private selectChangeStrategy;
|
|
54
|
+
private selectEntrypointEmitter;
|
|
55
|
+
protected createUnownedEntrypointError(entrypointPath: string): Error;
|
|
53
56
|
/**
|
|
57
|
+
* Materializes one integration-owned page entrypoint during cold registration.
|
|
58
|
+
*
|
|
54
59
|
* @remarks
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
60
|
+
* Registration uses {@link HmrStrategy.canEmitEntrypoint} / {@link HmrStrategy.emitEntrypoint},
|
|
61
|
+
* not {@link HmrStrategy.matches} / {@link HmrStrategy.process}. File-change handling stays
|
|
62
|
+
* on `handleFileChange()`.
|
|
58
63
|
*/
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
emitIntegrationEntrypoint(entrypointPath: string, outputPath: string): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Runs server invalidation and integration hooks before rebuilding a registered script entrypoint.
|
|
67
|
+
*
|
|
68
|
+
* @remarks
|
|
69
|
+
* Browser-only `*.script.ts` entrypoints skip server module invalidation and bypass-cache
|
|
70
|
+
* import; only integration change handlers and the downstream strategy rebuild run.
|
|
71
|
+
*/
|
|
72
|
+
private prepareRegisteredScriptChange;
|
|
62
73
|
getOutputUrl(entrypointPath: string): string | undefined;
|
|
63
74
|
/**
|
|
64
75
|
* Returns the emitted HMR script output when the entrypoint is already registered
|
|
@@ -68,20 +79,15 @@ export declare abstract class SharedHmrManager implements IHmrManager {
|
|
|
68
79
|
* Disk artifacts alone are not enough: a fresh dev session must still register
|
|
69
80
|
* the entrypoint so file watchers can rebuild it on change.
|
|
70
81
|
*/
|
|
71
|
-
getResolvedScriptOutput(entrypointPath: string):
|
|
72
|
-
outputUrl: string;
|
|
73
|
-
outputPath: string;
|
|
74
|
-
} | undefined;
|
|
82
|
+
getResolvedScriptOutput(entrypointPath: string): ResolvedHmrEntrypoint | undefined;
|
|
75
83
|
getWatchedFiles(): Map<string, string>;
|
|
76
84
|
getDistDir(): string;
|
|
77
|
-
getPlugins(): EcoBuildPlugin[];
|
|
78
85
|
getDefaultContext(): DefaultHmrContext;
|
|
86
|
+
stop(): void;
|
|
79
87
|
protected clearFailedEntrypointRegistration(entrypointPath: string): void;
|
|
80
88
|
registerEntrypoint(entrypointPath: string): Promise<string>;
|
|
81
|
-
registerScriptEntrypoint(entrypointPath: string): Promise<
|
|
82
|
-
protected emitStrictEntrypoint(entrypointPath: string): Promise<void>;
|
|
89
|
+
registerScriptEntrypoint(entrypointPath: string): Promise<ResolvedHmrEntrypoint>;
|
|
83
90
|
protected emitScriptEntrypoint(entrypointPath: string, outputPath: string): Promise<void>;
|
|
84
|
-
stop(): void;
|
|
85
91
|
[Symbol.dispose](): void;
|
|
86
92
|
}
|
|
87
93
|
export {};
|