@ecopages/core 0.2.0-beta.2 → 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/README.md +1 -1
- package/package.json +163 -3
- package/src/adapters/abstract/application-adapter.d.ts +104 -5
- package/src/adapters/abstract/application-adapter.js +125 -1
- package/src/adapters/abstract/server-adapter.d.ts +6 -2
- package/src/adapters/abstract/ws-pattern-matcher.d.ts +60 -0
- package/src/adapters/abstract/ws-pattern-matcher.js +61 -0
- package/src/adapters/bun/client-bridge.d.ts +1 -0
- package/src/adapters/bun/client-bridge.js +3 -0
- package/src/adapters/bun/create-app.d.ts +6 -4
- package/src/adapters/bun/create-app.js +65 -43
- package/src/adapters/bun/server-adapter.d.ts +90 -22
- package/src/adapters/bun/server-adapter.js +266 -84
- package/src/adapters/bun/static-preview-host.js +25 -20
- package/src/adapters/create-app.d.ts +13 -11
- package/src/adapters/create-app.js +2 -51
- package/src/adapters/index.d.ts +1 -1
- package/src/adapters/index.js +1 -2
- package/src/adapters/node/create-app.d.ts +5 -1
- package/src/adapters/node/create-app.js +51 -12
- package/src/adapters/node/node-client-bridge.d.ts +1 -0
- package/src/adapters/node/node-client-bridge.js +3 -0
- package/src/adapters/node/server-adapter-dependencies.js +4 -0
- package/src/adapters/node/server-adapter.d.ts +62 -20
- package/src/adapters/node/server-adapter.js +160 -131
- package/src/adapters/node/static-content-server.d.ts +1 -0
- package/src/adapters/node/static-content-server.js +10 -2
- package/src/adapters/node/static-preview-host.js +27 -12
- package/src/adapters/shared/bun-user-websocket-lifecycle.d.ts +22 -0
- package/src/adapters/shared/bun-user-websocket-lifecycle.js +96 -0
- package/src/adapters/shared/copy-runtime-public-dir.d.ts +6 -0
- package/src/adapters/shared/copy-runtime-public-dir.js +24 -0
- package/src/adapters/shared/fs-server-response-matcher.d.ts +2 -0
- package/src/adapters/shared/fs-server-response-matcher.js +40 -18
- package/src/adapters/shared/hmr-entrypoint-registrar.d.ts +21 -30
- package/src/adapters/shared/hmr-entrypoint-registrar.js +49 -54
- package/src/adapters/shared/hmr-html-response.d.ts +1 -1
- package/src/adapters/shared/hmr-html-response.js +4 -1
- package/src/adapters/shared/node-http-websocket-upgrades.d.ts +21 -0
- package/src/adapters/shared/node-http-websocket-upgrades.js +100 -0
- package/src/adapters/shared/port-manager.d.ts +116 -0
- package/src/adapters/shared/port-manager.js +196 -0
- package/src/adapters/shared/runtime-app-bootstrap.d.ts +2 -1
- package/src/adapters/shared/runtime-app-bootstrap.js +3 -6
- package/src/adapters/shared/runtime-server-lifecycle.d.ts +46 -0
- package/src/adapters/shared/runtime-server-lifecycle.js +50 -0
- package/src/adapters/shared/server-adapter.d.ts +2 -1
- package/src/adapters/shared/server-adapter.js +13 -6
- package/src/adapters/shared/server-route-handler.d.ts +4 -1
- package/src/adapters/shared/server-route-handler.js +5 -2
- package/src/adapters/shared/server-static-builder.d.ts +23 -32
- package/src/adapters/shared/server-static-builder.js +100 -73
- package/src/adapters/shared/shared-hmr-manager.d.ts +45 -11
- package/src/adapters/shared/shared-hmr-manager.js +204 -58
- package/src/adapters/shared/static-preview-host.d.ts +5 -0
- package/src/adapters/shared/websocket-lifecycle.d.ts +24 -0
- package/src/adapters/shared/websocket-lifecycle.js +55 -0
- package/src/build/README.md +77 -14
- package/src/build/app-build-manifest-runtime.d.ts +9 -0
- package/src/build/app-build-manifest-runtime.js +87 -0
- package/src/build/build-adapter.d.ts +17 -319
- package/src/build/build-adapter.js +28 -94
- package/src/build/build-contracts.d.ts +97 -0
- package/src/build/build-contracts.js +0 -0
- package/src/build/build-input-fingerprint.d.ts +28 -0
- package/src/build/build-input-fingerprint.js +31 -0
- package/src/build/build-profile-options.d.ts +7 -0
- package/src/build/build-profile-options.js +37 -0
- package/src/build/build-runtime.d.ts +24 -0
- package/src/build/build-runtime.js +65 -0
- package/src/build/deduping-build-executor.d.ts +28 -0
- package/src/build/deduping-build-executor.js +56 -0
- package/src/build/jsx-ownership-plugins.d.ts +24 -0
- package/src/build/jsx-ownership-plugins.js +41 -0
- package/src/build/pages-unified-graph-build.d.ts +31 -0
- package/src/build/pages-unified-graph-build.js +206 -0
- package/src/build/parallel-build-executor.d.ts +21 -0
- package/src/build/parallel-build-executor.js +52 -0
- package/src/build/production-build-cache.d.ts +20 -0
- package/src/build/production-build-cache.js +63 -0
- package/src/build/rolldown-adapter-helpers.d.ts +3 -6
- package/src/build/rolldown-adapter-helpers.js +71 -6
- package/src/build/rolldown-build-adapter.d.ts +2 -6
- package/src/build/rolldown-build-adapter.js +3 -3
- package/src/build/rolldown-build-invocation-metrics.d.ts +12 -0
- package/src/build/rolldown-build-invocation-metrics.js +43 -0
- package/src/build/rolldown-plugin-bridge.d.ts +15 -1
- package/src/build/rolldown-plugin-bridge.js +20 -5
- package/src/build/rolldown-source-transform-pass.d.ts +15 -0
- package/src/build/rolldown-source-transform-pass.js +58 -0
- package/src/build/runtime-build-executor.d.ts +8 -15
- package/src/build/runtime-build-executor.js +12 -15
- package/src/build/runtime-build-output-normalizer.d.ts +7 -0
- package/src/build/runtime-build-output-normalizer.js +94 -2
- package/src/build/server-entry-build-cache.d.ts +44 -0
- package/src/build/server-entry-build-cache.js +161 -0
- package/src/client/navigation-scripts/classification.d.ts +16 -0
- package/src/client/navigation-scripts/classification.js +41 -0
- package/src/client/navigation-scripts/index.d.ts +3 -0
- package/src/client/navigation-scripts/index.js +23 -0
- package/src/client/navigation-scripts/navigation-scripts.test.browser.js +65 -0
- package/src/client/navigation-scripts/registry.d.ts +11 -0
- package/src/client/navigation-scripts/registry.js +12 -0
- package/src/client/navigation-scripts/rerun-queue.d.ts +37 -0
- package/src/client/navigation-scripts/rerun-queue.js +73 -0
- package/src/client/scroll.d.ts +17 -0
- package/src/client/scroll.js +25 -0
- package/src/client/scroll.test.browser.js +37 -0
- package/src/client/view-transitions.d.ts +16 -0
- package/src/client/view-transitions.js +63 -0
- package/src/config/config-builder.d.ts +12 -7
- package/src/config/config-builder.js +12 -9
- package/src/dev/client-bridge-registry.d.ts +5 -0
- package/src/dev/client-bridge-registry.js +15 -0
- package/src/dev/dev-client-ownership.d.ts +7 -0
- package/src/dev/dev-client-ownership.js +6 -0
- package/src/dev/hmr-manager-registry.d.ts +8 -0
- package/src/dev/hmr-manager-registry.js +15 -0
- package/src/dev/host-runtime.d.ts +3 -0
- package/src/dev/host-runtime.js +7 -0
- package/src/dev/runtime-server-started-message.d.ts +2 -0
- package/src/dev/runtime-server-started-message.js +6 -0
- package/src/eco/README.md +11 -0
- package/src/eco/eco-declared-component.d.ts +14 -0
- package/src/eco/eco-declared-component.js +16 -0
- package/src/eco/eco.browser.js +5 -6
- package/src/eco/eco.js +5 -6
- package/src/eco/eco.types.d.ts +2 -2
- package/src/eco/page-layout-normalization.d.ts +21 -0
- package/src/eco/page-layout-normalization.js +59 -0
- package/src/errors/index.d.ts +1 -0
- package/src/errors/index.js +7 -1
- package/src/errors/undeclared-component-dependency-error.d.ts +11 -0
- package/src/errors/undeclared-component-dependency-error.js +17 -0
- package/src/hmr/client/hmr-runtime.js +5 -2
- package/src/hmr/hmr-entrypoint-output.d.ts +43 -0
- package/src/hmr/hmr-entrypoint-output.js +53 -0
- 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 +3 -5
- package/src/hmr/strategies/js-hmr-strategy.js +67 -19
- package/src/hmr/strategies/server-rendered-template-hmr-strategy.d.ts +17 -0
- package/src/hmr/strategies/server-rendered-template-hmr-strategy.js +29 -0
- package/src/integrations/ghtml/ghtml-renderer.d.ts +2 -6
- package/src/integrations/ghtml/ghtml-renderer.js +2 -51
- package/src/integrations/ghtml/ghtml.plugin.d.ts +3 -16
- package/src/integrations/ghtml/ghtml.plugin.js +7 -14
- package/src/plugins/define-integration.d.ts +26 -0
- package/src/plugins/define-integration.js +19 -0
- package/src/plugins/eco-component-meta-plugin.js +5 -8
- package/src/plugins/foreign-jsx-override-plugin.js +4 -6
- package/src/plugins/integration-plugin.d.ts +19 -9
- package/src/plugins/integration-plugin.js +7 -4
- package/src/plugins/jsx-import-source.utils.d.ts +8 -0
- package/src/plugins/jsx-import-source.utils.js +26 -0
- package/src/plugins/processor.d.ts +19 -4
- package/src/plugins/processor.js +22 -4
- package/src/plugins/source-transform.d.ts +31 -0
- package/src/plugins/source-transform.js +23 -1
- package/src/route-renderer/GRAPH.md +8 -8
- package/src/route-renderer/README.md +30 -28
- package/src/route-renderer/orchestration/component-graph-collectors.d.ts +10 -0
- package/src/route-renderer/orchestration/component-graph-collectors.js +143 -0
- package/src/route-renderer/orchestration/component-graph.d.ts +36 -0
- package/src/route-renderer/orchestration/component-graph.js +98 -0
- package/src/route-renderer/orchestration/component-render-context.d.ts +3 -10
- package/src/route-renderer/orchestration/component-render-context.js +3 -1
- package/src/route-renderer/orchestration/declared-ownership-graph.d.ts +1 -18
- package/src/route-renderer/orchestration/declared-ownership-graph.js +5 -31
- package/src/route-renderer/orchestration/document-shell-render.service.d.ts +90 -0
- package/src/route-renderer/orchestration/document-shell-render.service.js +120 -0
- package/src/route-renderer/orchestration/foreign-subtree-execution.service.d.ts +59 -29
- package/src/route-renderer/orchestration/foreign-subtree-execution.service.js +164 -35
- package/src/route-renderer/orchestration/global-injector-assets.service.d.ts +7 -0
- package/src/route-renderer/orchestration/global-injector-assets.service.js +32 -0
- package/src/route-renderer/orchestration/integration-renderer.d.ts +18 -54
- package/src/route-renderer/orchestration/integration-renderer.js +113 -195
- package/src/route-renderer/orchestration/integration-route-render-adapter.d.ts +38 -0
- package/src/route-renderer/orchestration/integration-route-render-adapter.js +27 -0
- package/src/route-renderer/orchestration/layout-shell-props.service.d.ts +20 -0
- package/src/route-renderer/orchestration/layout-shell-props.service.js +31 -0
- package/src/route-renderer/orchestration/ownership-validation.service.d.ts +5 -0
- package/src/route-renderer/orchestration/ownership-validation.service.js +29 -1
- package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.d.ts +9 -0
- package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.js +7 -0
- package/src/route-renderer/orchestration/page-browser-graph.service.d.ts +25 -0
- package/src/route-renderer/orchestration/page-browser-graph.service.js +174 -0
- package/src/route-renderer/orchestration/render-output.utils.d.ts +2 -0
- package/src/route-renderer/orchestration/render-output.utils.js +4 -0
- package/src/route-renderer/orchestration/route-html-finalization.service.d.ts +17 -0
- package/src/route-renderer/orchestration/route-html-finalization.service.js +26 -0
- package/src/route-renderer/orchestration/route-prepared-options.builder.d.ts +26 -0
- package/src/route-renderer/orchestration/route-prepared-options.builder.js +54 -0
- package/src/route-renderer/orchestration/route-prepared-options.utils.d.ts +4 -0
- package/src/route-renderer/orchestration/route-prepared-options.utils.js +33 -0
- package/src/route-renderer/orchestration/route-render-orchestrator.d.ts +8 -39
- package/src/route-renderer/orchestration/route-render-orchestrator.js +53 -461
- package/src/route-renderer/orchestration/string-markup-renderer.d.ts +14 -0
- package/src/route-renderer/orchestration/string-markup-renderer.js +55 -0
- package/src/route-renderer/page-loading/component-dependency-collection.js +5 -2
- package/src/router/client/link-intent.d.ts +11 -21
- package/src/router/client/link-intent.js +10 -0
- package/src/router/client/link-intent.test.browser.js +12 -1
- package/src/router/client/link-navigation-policy.d.ts +45 -0
- package/src/router/client/link-navigation-policy.js +101 -0
- package/src/router/client/link-navigation-policy.test.browser.d.ts +1 -0
- package/src/router/client/link-navigation-policy.test.browser.js +92 -0
- package/src/router/client/navigation-coordinator.d.ts +1 -1
- package/src/router/client/navigation-coordinator.js +12 -2
- package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.d.ts +1 -0
- package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.js +1 -0
- package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.d.ts +4 -2
- package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.js +27 -7
- 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 +18 -15
- package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +17 -3
- package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.d.ts +0 -3
- package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +2 -31
- package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.d.ts +0 -3
- package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +2 -30
- package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.d.ts +2 -0
- package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.js +43 -0
- package/src/services/assets/browser-bundle.service.d.ts +3 -1
- package/src/services/assets/browser-bundle.service.js +17 -5
- package/src/services/html/html-transformer.service.d.ts +1 -1
- package/src/services/html/html-transformer.service.js +4 -4
- package/src/services/invalidation/development-invalidation.service.d.ts +20 -2
- package/src/services/invalidation/development-invalidation.service.js +56 -2
- package/src/services/module-loading/README.md +49 -0
- package/src/services/module-loading/app-server-module-transpiler.service.js +5 -17
- package/src/services/module-loading/page-module-import.service.d.ts +5 -1
- package/src/services/module-loading/page-module-import.service.js +96 -45
- package/src/services/module-loading/route-module-build-cache-registry.d.ts +24 -0
- package/src/services/module-loading/route-module-build-cache-registry.js +37 -0
- package/src/services/module-loading/route-module-build-cache.d.ts +4 -0
- package/src/services/module-loading/route-module-build-cache.js +38 -0
- package/src/services/module-loading/route-module-build-cache.store.d.ts +57 -0
- package/src/services/module-loading/route-module-build-cache.store.js +207 -0
- package/src/services/module-loading/route-module-build-manifest.d.ts +61 -0
- package/src/services/module-loading/route-module-build-manifest.js +116 -0
- package/src/services/module-loading/route-module-dependency-hasher.d.ts +81 -0
- package/src/services/module-loading/route-module-dependency-hasher.js +122 -0
- package/src/services/module-loading/server-module-transpiler.service.js +1 -1
- package/src/static-site-generator/README.md +33 -0
- package/src/static-site-generator/static-build-invalidation.d.ts +10 -0
- package/src/static-site-generator/static-build-invalidation.js +59 -0
- package/src/static-site-generator/static-export-context.d.ts +22 -0
- package/src/static-site-generator/static-export-context.js +0 -0
- package/src/static-site-generator/static-site-generator.d.ts +17 -20
- package/src/static-site-generator/static-site-generator.js +188 -85
- package/src/types/internal-types.d.ts +16 -9
- package/src/types/public-types.d.ts +144 -40
- package/src/utils/parse-cli-args.d.ts +2 -0
- package/src/utils/parse-cli-args.js +6 -0
- package/src/utils/server-utils.module.d.ts +3 -2
- package/src/utils/server-utils.module.js +5 -6
- package/src/utils/static-file-extensions.d.ts +12 -0
- package/src/utils/static-file-extensions.js +48 -0
- package/src/watchers/project-watcher-ignore.d.ts +11 -0
- package/src/watchers/project-watcher-ignore.js +16 -0
- package/src/watchers/project-watcher.d.ts +26 -3
- package/src/watchers/project-watcher.js +132 -30
- package/src/watchers/project-watcher.test-helpers.js +5 -5
- package/src/build/rolldown-dev-build-adapter.d.ts +0 -82
- package/src/build/rolldown-dev-build-adapter.js +0 -166
- package/src/hmr/hmr.postcss.test.e2e.js +0 -31
- package/src/hmr/hmr.test.e2e.js +0 -43
- package/src/route-renderer/orchestration/ownership-planning.service.d.ts +0 -24
- package/src/route-renderer/orchestration/ownership-planning.service.js +0 -63
- package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.d.ts +0 -91
- package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.js +0 -170
- /package/src/{hmr/hmr.postcss.test.e2e.d.ts → client/navigation-scripts/navigation-scripts.test.browser.d.ts} +0 -0
- /package/src/{hmr/hmr.test.e2e.d.ts → client/scroll.test.browser.d.ts} +0 -0
package/src/eco/eco.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
interceptForeignChild
|
|
5
5
|
} from "../route-renderer/orchestration/component-render-context.js";
|
|
6
6
|
import { isThenable } from "../route-renderer/orchestration/render-output.utils.js";
|
|
7
|
+
import { applyPageLayoutConfig, mergeLayoutDependencies, normalizePageLayouts } from "./page-layout-normalization.js";
|
|
7
8
|
function createComponentFactory(options) {
|
|
8
9
|
const integrationName = options.integration ?? options.__eco?.integration;
|
|
9
10
|
const comp = ((props) => {
|
|
@@ -74,18 +75,16 @@ function layout(options) {
|
|
|
74
75
|
}
|
|
75
76
|
function page(options) {
|
|
76
77
|
const { layout: layout2, dependencies, render, staticPaths: staticPaths2, staticProps: staticProps2, metadata: metadata2, cache, requires, middleware } = options;
|
|
78
|
+
const layoutEntries = normalizePageLayouts(layout2);
|
|
77
79
|
const componentOptions = {
|
|
78
80
|
__eco: options.__eco,
|
|
79
81
|
integration: options.integration,
|
|
80
|
-
dependencies:
|
|
81
|
-
...dependencies,
|
|
82
|
-
components: [...dependencies?.components || [], layout2]
|
|
83
|
-
} : dependencies,
|
|
82
|
+
dependencies: mergeLayoutDependencies(dependencies, layoutEntries),
|
|
84
83
|
render
|
|
85
84
|
};
|
|
86
85
|
const pageComponent = createComponentFactory(componentOptions);
|
|
87
|
-
if (
|
|
88
|
-
pageComponent.config
|
|
86
|
+
if (pageComponent.config) {
|
|
87
|
+
applyPageLayoutConfig(pageComponent.config, layoutEntries);
|
|
89
88
|
}
|
|
90
89
|
if (staticPaths2) pageComponent.staticPaths = staticPaths2;
|
|
91
90
|
if (staticProps2) pageComponent.staticProps = staticProps2;
|
package/src/eco/eco.types.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Type definitions for the eco namespace API
|
|
3
3
|
* @module
|
|
4
4
|
*/
|
|
5
|
-
import type { DependencyLazyTrigger, EcoComponent, EcoComponentDependencies, EcoHtmlComponent, EcoInjectedMeta, EcoLayoutComponent,
|
|
5
|
+
import type { DependencyLazyTrigger, EcoComponent, EcoComponentDependencies, EcoHtmlComponent, EcoInjectedMeta, EcoLayoutComponent, EcoPageLayouts, EcoPagesElement, FileRouteMiddleware, GetMetadata, GetStaticPaths, GetStaticProps, HtmlTemplateProps, LayoutProps, RequestLocals, RequestPageContext } from '../types/public-types.js';
|
|
6
6
|
import type { CacheStrategy } from '../services/cache/cache.types.js';
|
|
7
7
|
/**
|
|
8
8
|
* Extracts the props type from one eco component.
|
|
@@ -81,7 +81,7 @@ export interface PageOptionsBase<T, E = EcoPagesElement> {
|
|
|
81
81
|
__eco?: EcoInjectedMeta;
|
|
82
82
|
integration?: string;
|
|
83
83
|
dependencies?: EcoComponentDependencies;
|
|
84
|
-
layout?:
|
|
84
|
+
layout?: EcoPageLayouts<E>;
|
|
85
85
|
/**
|
|
86
86
|
* Define static paths for dynamic routes (e.g., [slug].tsx).
|
|
87
87
|
* Returns all possible paths that should be pre-rendered at build time.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { EcoComponentConfig, EcoComponentDependencies, EcoPageLayoutEntry, EcoPageLayouts } from '../types/public-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes `layout` page options to an outer→inner stack of layout entries.
|
|
4
|
+
*/
|
|
5
|
+
export declare function normalizePageLayouts<E>(layout?: EcoPageLayouts<E>): EcoPageLayoutEntry<E>[];
|
|
6
|
+
/**
|
|
7
|
+
* Merges layout components into `dependencies.components` without duplicates.
|
|
8
|
+
*/
|
|
9
|
+
export declare function mergeLayoutDependencies(dependencies: EcoComponentDependencies | undefined, layoutEntries: EcoPageLayoutEntry[]): EcoComponentDependencies | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Writes normalized layout metadata onto a page `config`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function applyPageLayoutConfig(pageConfig: EcoComponentConfig, layoutEntries: EcoPageLayoutEntry[]): void;
|
|
14
|
+
/**
|
|
15
|
+
* Migrates MDX-exported `config.layout` values onto normalized layout metadata.
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* `eco.page({ layout })` already normalizes at factory time. MDX modules that
|
|
19
|
+
* export `config.layout` directly still need this import-time migration.
|
|
20
|
+
*/
|
|
21
|
+
export declare function ensurePageConfigLayouts(config: EcoComponentConfig | undefined): EcoComponentConfig | undefined;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
function isLayoutSpecObject(spec) {
|
|
2
|
+
return typeof spec === "object" && spec !== null && "component" in spec;
|
|
3
|
+
}
|
|
4
|
+
function normalizePageLayouts(layout) {
|
|
5
|
+
if (!layout) {
|
|
6
|
+
return [];
|
|
7
|
+
}
|
|
8
|
+
const specs = Array.isArray(layout) ? layout : [layout];
|
|
9
|
+
return specs.map((spec) => {
|
|
10
|
+
if (isLayoutSpecObject(spec)) {
|
|
11
|
+
return {
|
|
12
|
+
component: spec.component,
|
|
13
|
+
props: spec.props
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
component: spec
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function mergeLayoutDependencies(dependencies, layoutEntries) {
|
|
22
|
+
if (layoutEntries.length === 0) {
|
|
23
|
+
return dependencies;
|
|
24
|
+
}
|
|
25
|
+
const mergedComponents = [...dependencies?.components ?? []];
|
|
26
|
+
for (const entry of layoutEntries) {
|
|
27
|
+
if (!mergedComponents.includes(entry.component)) {
|
|
28
|
+
mergedComponents.push(entry.component);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
...dependencies,
|
|
33
|
+
components: mergedComponents
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function applyPageLayoutConfig(pageConfig, layoutEntries) {
|
|
37
|
+
if (layoutEntries.length === 0) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
pageConfig.layouts = layoutEntries.map((entry) => entry.component);
|
|
41
|
+
pageConfig.layoutEntries = layoutEntries;
|
|
42
|
+
}
|
|
43
|
+
function ensurePageConfigLayouts(config) {
|
|
44
|
+
if (!config || config.layouts && config.layouts.length > 0) {
|
|
45
|
+
return config;
|
|
46
|
+
}
|
|
47
|
+
const legacyLayout = config.layout;
|
|
48
|
+
if (!legacyLayout) {
|
|
49
|
+
return config;
|
|
50
|
+
}
|
|
51
|
+
applyPageLayoutConfig(config, normalizePageLayouts(legacyLayout));
|
|
52
|
+
return config;
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
applyPageLayoutConfig,
|
|
56
|
+
ensurePageConfigLayouts,
|
|
57
|
+
mergeLayoutDependencies,
|
|
58
|
+
normalizePageLayouts
|
|
59
|
+
};
|
package/src/errors/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { HttpError } from './http-error.js';
|
|
2
2
|
export type { HttpErrorDetails, HttpErrorJson } from './http-error.js';
|
|
3
3
|
export { LocalsAccessError } from './locals-access-error.js';
|
|
4
|
+
export { UndeclaredComponentDependencyError, getUndeclaredComponentDependencyMessage, } from './undeclared-component-dependency-error.js';
|
package/src/errors/index.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { HttpError } from "./http-error.js";
|
|
2
2
|
import { LocalsAccessError } from "./locals-access-error.js";
|
|
3
|
+
import {
|
|
4
|
+
UndeclaredComponentDependencyError,
|
|
5
|
+
getUndeclaredComponentDependencyMessage
|
|
6
|
+
} from "./undeclared-component-dependency-error.js";
|
|
3
7
|
export {
|
|
4
8
|
HttpError,
|
|
5
|
-
LocalsAccessError
|
|
9
|
+
LocalsAccessError,
|
|
10
|
+
UndeclaredComponentDependencyError,
|
|
11
|
+
getUndeclaredComponentDependencyMessage
|
|
6
12
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function getUndeclaredComponentDependencyMessage(parentComponentFile?: string): string;
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when `dependencies.components` contains a plain function or component
|
|
4
|
+
* without plugin-injected `config.__eco` metadata.
|
|
5
|
+
*/
|
|
6
|
+
export declare class UndeclaredComponentDependencyError extends Error {
|
|
7
|
+
name: string;
|
|
8
|
+
/** Parent component file path when the invalid entry was declared. */
|
|
9
|
+
readonly parentComponentFile?: string;
|
|
10
|
+
constructor(parentComponentFile?: string);
|
|
11
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
function getUndeclaredComponentDependencyMessage(parentComponentFile) {
|
|
2
|
+
const parentHint = parentComponentFile ? ` (declared by ${parentComponentFile})` : "";
|
|
3
|
+
return `[ecopages] dependencies.components entries must be eco.component(), eco.layout(), or eco.html() components with __eco metadata${parentHint}. Plain functions and untyped components are not valid dependency entries.`;
|
|
4
|
+
}
|
|
5
|
+
class UndeclaredComponentDependencyError extends Error {
|
|
6
|
+
name = "UndeclaredComponentDependencyError";
|
|
7
|
+
/** Parent component file path when the invalid entry was declared. */
|
|
8
|
+
parentComponentFile;
|
|
9
|
+
constructor(parentComponentFile) {
|
|
10
|
+
super(getUndeclaredComponentDependencyMessage(parentComponentFile));
|
|
11
|
+
this.parentComponentFile = parentComponentFile;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
UndeclaredComponentDependencyError,
|
|
16
|
+
getUndeclaredComponentDependencyMessage
|
|
17
|
+
};
|
|
@@ -12,7 +12,7 @@ import { getEcoNavigationRuntime } from "../../router/client/navigation-coordina
|
|
|
12
12
|
socket.addEventListener("message", async (event) => {
|
|
13
13
|
try {
|
|
14
14
|
const payload = JSON.parse(event.data);
|
|
15
|
-
handleMessage(payload);
|
|
15
|
+
await handleMessage(payload);
|
|
16
16
|
} catch (e) {
|
|
17
17
|
console.error("[ecopages] Invalid HMR message:", e);
|
|
18
18
|
}
|
|
@@ -29,12 +29,15 @@ import { getEcoNavigationRuntime } from "../../router/client/navigation-coordina
|
|
|
29
29
|
switch (payload.type) {
|
|
30
30
|
case "reload":
|
|
31
31
|
await waitForNavigationToSettle(navigationRuntime);
|
|
32
|
+
if (window.__ECOPAGES_HOST_OWNS_RELOAD__) {
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
32
35
|
location.reload();
|
|
33
36
|
break;
|
|
34
37
|
case "layout-update": {
|
|
35
38
|
await waitForNavigationToSettle(navigationRuntime);
|
|
36
39
|
if (await navigationRuntime.reloadCurrentPage({ clearCache: true, moduleUrl: getActiveHmrModuleUrl() })) {
|
|
37
|
-
} else {
|
|
40
|
+
} else if (!window.__ECOPAGES_HOST_OWNS_RELOAD__) {
|
|
38
41
|
location.reload();
|
|
39
42
|
}
|
|
40
43
|
break;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verified HMR entrypoint artifact produced by registration.
|
|
3
|
+
*/
|
|
4
|
+
export interface ResolvedHmrEntrypoint {
|
|
5
|
+
sourcePath: string;
|
|
6
|
+
outputPath: string;
|
|
7
|
+
outputUrl: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Returns whether a source path is a registered client script HMR entrypoint.
|
|
11
|
+
*/
|
|
12
|
+
export declare function isRegisteredScriptEntrypoint(registered: ReadonlyMap<string, unknown>, filePath: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Returns whether a registered script entrypoint is browser-only and must not be
|
|
15
|
+
* server-imported during HMR invalidation.
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* Declared `*.script.ts` modules run only in the browser bundle. Re-importing them
|
|
19
|
+
* on the server during `prepareRegisteredScriptChange()` executes DOM globals and
|
|
20
|
+
* aborts the watcher before the client artifact rebuilds. Radiant `*.script.tsx`
|
|
21
|
+
* entrypoints are server-rendered and remain on the invalidation path.
|
|
22
|
+
*/
|
|
23
|
+
export declare function isBrowserOnlyRegisteredScriptEntrypoint(filePath: string): boolean;
|
|
24
|
+
export declare function encodeHmrDynamicSegments(filepath: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Resolves the on-disk and browser URL targets for one HMR script entrypoint.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveHmrEntrypointOutputPaths(srcDir: string, distDir: string, entrypointPath: string): {
|
|
29
|
+
outputPath: string;
|
|
30
|
+
outputUrl: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* @remarks
|
|
34
|
+
* Rebuilds must delete the previous artifact so browser-hmr does not reuse a
|
|
35
|
+
* stale bundle when the source file changed.
|
|
36
|
+
*/
|
|
37
|
+
export declare function removeStaleHmrEntrypointOutput(outputPath: string, scope: string): void;
|
|
38
|
+
/**
|
|
39
|
+
* @remarks
|
|
40
|
+
* Guards against broadcasting reload/update before the bundler has flushed a
|
|
41
|
+
* fresher artifact than the edited source file.
|
|
42
|
+
*/
|
|
43
|
+
export declare function isHmrOutputFresh(outputPath: string, sourcePath: string): boolean;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileSystem } from "@ecopages/file-system";
|
|
4
|
+
import { RESOLVED_ASSETS_DIR } from "../config/constants.js";
|
|
5
|
+
import { appLogger } from "../global/app-logger.js";
|
|
6
|
+
function isRegisteredScriptEntrypoint(registered, filePath) {
|
|
7
|
+
return registered.has(path.resolve(filePath));
|
|
8
|
+
}
|
|
9
|
+
function isBrowserOnlyRegisteredScriptEntrypoint(filePath) {
|
|
10
|
+
return /\.script\.ts$/u.test(path.resolve(filePath));
|
|
11
|
+
}
|
|
12
|
+
function encodeHmrDynamicSegments(filepath) {
|
|
13
|
+
return filepath.replace(/\[([^\]]+)\]/g, "_$1_");
|
|
14
|
+
}
|
|
15
|
+
function resolveHmrEntrypointOutputPaths(srcDir, distDir, entrypointPath) {
|
|
16
|
+
const normalizedEntrypoint = path.resolve(entrypointPath);
|
|
17
|
+
const relativePath = path.relative(srcDir, normalizedEntrypoint);
|
|
18
|
+
const relativePathJs = relativePath.replace(/\.(tsx?|jsx?|mdx?)$/, ".js");
|
|
19
|
+
const encodedPathJs = encodeHmrDynamicSegments(relativePathJs);
|
|
20
|
+
const urlPath = encodedPathJs.split(path.sep).join("/");
|
|
21
|
+
return {
|
|
22
|
+
outputUrl: `/${path.join(RESOLVED_ASSETS_DIR, "_hmr", urlPath).split(path.sep).join("/")}`,
|
|
23
|
+
outputPath: path.join(distDir, urlPath)
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function removeStaleHmrEntrypointOutput(outputPath, scope) {
|
|
27
|
+
if (!fileSystem.exists(outputPath)) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
fileSystem.remove(outputPath);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
appLogger.warn(
|
|
34
|
+
`[${scope}] Failed to remove stale entrypoint output ${outputPath}: ${error instanceof Error ? error.message : String(error)}`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function isHmrOutputFresh(outputPath, sourcePath) {
|
|
39
|
+
if (!fileSystem.exists(outputPath) || !fileSystem.exists(sourcePath)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const outputMtimeMs = fs.statSync(outputPath).mtimeMs;
|
|
43
|
+
const sourceMtimeMs = fs.statSync(sourcePath).mtimeMs;
|
|
44
|
+
return outputMtimeMs >= sourceMtimeMs;
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
encodeHmrDynamicSegments,
|
|
48
|
+
isBrowserOnlyRegisteredScriptEntrypoint,
|
|
49
|
+
isHmrOutputFresh,
|
|
50
|
+
isRegisteredScriptEntrypoint,
|
|
51
|
+
removeStaleHmrEntrypointOutput,
|
|
52
|
+
resolveHmrEntrypointOutputPaths
|
|
53
|
+
};
|
|
@@ -133,6 +133,21 @@ export declare abstract class HmrStrategy {
|
|
|
133
133
|
* ```
|
|
134
134
|
*/
|
|
135
135
|
abstract matches(filePath: string): boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Returns whether this strategy owns cold registration for the entrypoint.
|
|
138
|
+
*
|
|
139
|
+
* @remarks
|
|
140
|
+
* Registration dispatch uses only integration strategies that claim the
|
|
141
|
+
* entrypoint. File-change dispatch continues to use {@link matches}.
|
|
142
|
+
*/
|
|
143
|
+
canEmitEntrypoint(_entrypointPath: string): boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Materializes one integration-owned HMR entrypoint during registration.
|
|
146
|
+
*
|
|
147
|
+
* @remarks
|
|
148
|
+
* Must write the canonical output path and must not broadcast client events.
|
|
149
|
+
*/
|
|
150
|
+
emitEntrypoint(_entrypointPath: string, _outputPath: string): Promise<void>;
|
|
136
151
|
/**
|
|
137
152
|
* Processes a file change and returns the action to take.
|
|
138
153
|
*
|
package/src/hmr/hmr-strategy.js
CHANGED
|
@@ -37,6 +37,25 @@ class HmrStrategy {
|
|
|
37
37
|
get priority() {
|
|
38
38
|
return this.type + this.priorityOffset;
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns whether this strategy owns cold registration for the entrypoint.
|
|
42
|
+
*
|
|
43
|
+
* @remarks
|
|
44
|
+
* Registration dispatch uses only integration strategies that claim the
|
|
45
|
+
* entrypoint. File-change dispatch continues to use {@link matches}.
|
|
46
|
+
*/
|
|
47
|
+
canEmitEntrypoint(_entrypointPath) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Materializes one integration-owned HMR entrypoint during registration.
|
|
52
|
+
*
|
|
53
|
+
* @remarks
|
|
54
|
+
* Must write the canonical output path and must not broadcast client events.
|
|
55
|
+
*/
|
|
56
|
+
async emitEntrypoint(_entrypointPath, _outputPath) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
40
59
|
}
|
|
41
60
|
export {
|
|
42
61
|
HmrStrategy,
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
import { HmrStrategy, type HmrAction } from '../hmr-strategy.js';
|
|
11
|
-
import type { EcoBuildPlugin } from '../../build/build-types.js';
|
|
12
11
|
import type { BrowserBundleExecutor } from '../../services/assets/browser-bundle.service.js';
|
|
13
12
|
import type { EntrypointDependencyGraph } from '../../services/runtime-state/entrypoint-dependency-graph.service.js';
|
|
14
13
|
/**
|
|
@@ -25,10 +24,6 @@ export interface JsHmrContext {
|
|
|
25
24
|
* Directory where HMR bundles are written.
|
|
26
25
|
*/
|
|
27
26
|
getDistDir(): string;
|
|
28
|
-
/**
|
|
29
|
-
* Build plugins to use during bundling.
|
|
30
|
-
*/
|
|
31
|
-
getPlugins(): EcoBuildPlugin[];
|
|
32
27
|
/**
|
|
33
28
|
* Absolute path to the source directory.
|
|
34
29
|
*/
|
|
@@ -98,6 +93,7 @@ export declare class JsHmrStrategy extends HmrStrategy {
|
|
|
98
93
|
* Matches if:
|
|
99
94
|
* 1. There are registered entrypoints to rebuild
|
|
100
95
|
* 2. The changed file is a JS/TS file in the src directory
|
|
96
|
+
* 3. Registered entrypoints always match, even when they share an integration template extension
|
|
101
97
|
*
|
|
102
98
|
* @param filePath - Absolute path to the changed file
|
|
103
99
|
* @returns True if this file should trigger entrypoint rebuilds
|
|
@@ -116,6 +112,8 @@ export declare class JsHmrStrategy extends HmrStrategy {
|
|
|
116
112
|
* @returns Action to broadcast update events
|
|
117
113
|
*/
|
|
118
114
|
process(filePath: string): Promise<HmrAction>;
|
|
115
|
+
private resolveEntrypointOutputPath;
|
|
116
|
+
private removeStaleEntrypointOutput;
|
|
119
117
|
/**
|
|
120
118
|
* Bundles one or more entrypoints in a single build invocation.
|
|
121
119
|
* Uses the source directory as the output base so that the directory structure
|
|
@@ -2,6 +2,12 @@ import path from "node:path";
|
|
|
2
2
|
import { fileSystem } from "@ecopages/file-system";
|
|
3
3
|
import { HmrStrategy, HmrStrategyType } from "../hmr-strategy.js";
|
|
4
4
|
import { appLogger } from "../../global/app-logger.js";
|
|
5
|
+
import {
|
|
6
|
+
removeStaleHmrEntrypointOutput,
|
|
7
|
+
resolveHmrEntrypointOutputPaths,
|
|
8
|
+
isRegisteredScriptEntrypoint,
|
|
9
|
+
isHmrOutputFresh
|
|
10
|
+
} from "../hmr-entrypoint-output.js";
|
|
5
11
|
class JsHmrStrategy extends HmrStrategy {
|
|
6
12
|
type = HmrStrategyType.SCRIPT;
|
|
7
13
|
context;
|
|
@@ -15,27 +21,30 @@ class JsHmrStrategy extends HmrStrategy {
|
|
|
15
21
|
* Matches if:
|
|
16
22
|
* 1. There are registered entrypoints to rebuild
|
|
17
23
|
* 2. The changed file is a JS/TS file in the src directory
|
|
24
|
+
* 3. Registered entrypoints always match, even when they share an integration template extension
|
|
18
25
|
*
|
|
19
26
|
* @param filePath - Absolute path to the changed file
|
|
20
27
|
* @returns True if this file should trigger entrypoint rebuilds
|
|
21
28
|
*/
|
|
22
29
|
matches(filePath) {
|
|
23
30
|
const watchedFiles = this.context.getWatchedFiles();
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
31
|
+
const resolvedPath = path.resolve(filePath);
|
|
32
|
+
const isJsTs = /\.(ts|tsx|js|jsx)$/.test(resolvedPath);
|
|
33
|
+
const srcDir = path.resolve(this.context.getSrcDir());
|
|
34
|
+
const isInSrc = resolvedPath.startsWith(`${srcDir}${path.sep}`) || resolvedPath === srcDir;
|
|
35
|
+
const isIntegrationTemplate = this.context.getTemplateExtensions().some((extension) => resolvedPath.endsWith(extension));
|
|
27
36
|
if (watchedFiles.size === 0) {
|
|
28
37
|
return false;
|
|
29
38
|
}
|
|
30
39
|
if (!isJsTs || !isInSrc) {
|
|
31
40
|
return false;
|
|
32
41
|
}
|
|
42
|
+
if (watchedFiles.has(resolvedPath)) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
33
45
|
if (isIntegrationTemplate) {
|
|
34
46
|
return false;
|
|
35
47
|
}
|
|
36
|
-
if (watchedFiles.has(filePath)) {
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
48
|
return true;
|
|
40
49
|
}
|
|
41
50
|
/**
|
|
@@ -53,22 +62,27 @@ class JsHmrStrategy extends HmrStrategy {
|
|
|
53
62
|
async process(filePath) {
|
|
54
63
|
appLogger.debug(`[JsHmrStrategy] Processing ${filePath}`);
|
|
55
64
|
const watchedFiles = this.context.getWatchedFiles();
|
|
65
|
+
const resolvedChanged = path.resolve(filePath);
|
|
66
|
+
const isRegisteredEntrypointEdit = isRegisteredScriptEntrypoint(watchedFiles, resolvedChanged);
|
|
56
67
|
if (watchedFiles.size === 0) {
|
|
57
68
|
appLogger.debug(`[JsHmrStrategy] No watched files to rebuild`);
|
|
58
69
|
return { type: "none" };
|
|
59
70
|
}
|
|
60
71
|
const dependencyHits = this.context.getEntrypointDependencyGraph().getDependencyEntrypoints(filePath);
|
|
61
72
|
const hasDependencyHit = dependencyHits.size > 0;
|
|
62
|
-
const impactedEntrypoints = hasDependencyHit ? Array.from(dependencyHits).filter((entrypoint) => watchedFiles.has(entrypoint)) : Array.from(watchedFiles.keys());
|
|
73
|
+
const impactedEntrypoints = isRegisteredEntrypointEdit ? [resolvedChanged] : hasDependencyHit ? Array.from(dependencyHits).filter((entrypoint) => watchedFiles.has(path.resolve(entrypoint))) : Array.from(watchedFiles.keys());
|
|
63
74
|
const buildableEntrypoints = impactedEntrypoints.filter(
|
|
64
75
|
(entrypoint) => this.context.shouldProcessEntrypoint?.(entrypoint) ?? true
|
|
65
76
|
);
|
|
66
|
-
if (!hasDependencyHit) {
|
|
77
|
+
if (!hasDependencyHit && !isRegisteredEntrypointEdit) {
|
|
67
78
|
appLogger.debug("[JsHmrStrategy] Dependency graph miss, rebuilding all watched entrypoints");
|
|
68
79
|
}
|
|
69
80
|
if (buildableEntrypoints.length === 0) {
|
|
70
81
|
return { type: "none" };
|
|
71
82
|
}
|
|
83
|
+
for (const entrypoint of buildableEntrypoints) {
|
|
84
|
+
this.removeStaleEntrypointOutput(this.resolveEntrypointOutputPath(entrypoint));
|
|
85
|
+
}
|
|
72
86
|
const buildResult = await this.bundleEntrypoints(buildableEntrypoints);
|
|
73
87
|
if (!buildResult.success) {
|
|
74
88
|
return { type: "none" };
|
|
@@ -76,17 +90,19 @@ class JsHmrStrategy extends HmrStrategy {
|
|
|
76
90
|
const updates = [];
|
|
77
91
|
let reloadRequired = false;
|
|
78
92
|
for (const entrypoint of buildableEntrypoints) {
|
|
79
|
-
const
|
|
80
|
-
|
|
93
|
+
const resolvedEntrypoint = path.resolve(entrypoint);
|
|
94
|
+
const derivedOutput = resolveHmrEntrypointOutputPaths(
|
|
95
|
+
this.context.getSrcDir(),
|
|
96
|
+
this.context.getDistDir(),
|
|
97
|
+
resolvedEntrypoint
|
|
98
|
+
);
|
|
99
|
+
const outputUrl = watchedFiles.get(resolvedEntrypoint) ?? derivedOutput.outputUrl;
|
|
100
|
+
const outputPath = this.resolveEntrypointOutputPath(resolvedEntrypoint);
|
|
81
101
|
if (buildResult.dependencies) {
|
|
82
|
-
const entrypointDeps = buildResult.dependencies.get(
|
|
83
|
-
this.context.getEntrypointDependencyGraph().setEntrypointDependencies(
|
|
102
|
+
const entrypointDeps = buildResult.dependencies.get(resolvedEntrypoint) ?? [];
|
|
103
|
+
this.context.getEntrypointDependencyGraph().setEntrypointDependencies(resolvedEntrypoint, entrypointDeps);
|
|
84
104
|
}
|
|
85
|
-
const
|
|
86
|
-
const relativePath = path.relative(srcDir, entrypoint);
|
|
87
|
-
const relativePathJs = relativePath.replace(/\.(tsx?|jsx?|mdx?)$/, ".js");
|
|
88
|
-
const outputPath = path.join(this.context.getDistDir(), relativePathJs);
|
|
89
|
-
const result = await this.processOutput(outputPath, outputUrl);
|
|
105
|
+
const result = await this.processOutput(outputPath, outputUrl, resolvedEntrypoint);
|
|
90
106
|
if (result.success) {
|
|
91
107
|
updates.push(outputUrl);
|
|
92
108
|
if (result.requiresReload) {
|
|
@@ -113,6 +129,12 @@ class JsHmrStrategy extends HmrStrategy {
|
|
|
113
129
|
}
|
|
114
130
|
return { type: "none" };
|
|
115
131
|
}
|
|
132
|
+
resolveEntrypointOutputPath(entrypointPath) {
|
|
133
|
+
return resolveHmrEntrypointOutputPaths(this.context.getSrcDir(), this.context.getDistDir(), entrypointPath).outputPath;
|
|
134
|
+
}
|
|
135
|
+
removeStaleEntrypointOutput(outputPath) {
|
|
136
|
+
removeStaleHmrEntrypointOutput(outputPath, "JsHmrStrategy");
|
|
137
|
+
}
|
|
116
138
|
/**
|
|
117
139
|
* Bundles one or more entrypoints in a single build invocation.
|
|
118
140
|
* Uses the source directory as the output base so that the directory structure
|
|
@@ -120,13 +142,35 @@ class JsHmrStrategy extends HmrStrategy {
|
|
|
120
142
|
*/
|
|
121
143
|
async bundleEntrypoints(entrypoints) {
|
|
122
144
|
try {
|
|
145
|
+
if (entrypoints.length === 1) {
|
|
146
|
+
const entrypoint = entrypoints[0];
|
|
147
|
+
const outputPath = this.resolveEntrypointOutputPath(entrypoint);
|
|
148
|
+
const naming = path.relative(this.context.getDistDir(), outputPath).split(path.sep).join("/");
|
|
149
|
+
const result2 = await this.context.getBrowserBundleService().bundle({
|
|
150
|
+
profile: "hmr-entrypoint",
|
|
151
|
+
entrypoints: [entrypoint],
|
|
152
|
+
outdir: this.context.getDistDir(),
|
|
153
|
+
naming,
|
|
154
|
+
minify: false
|
|
155
|
+
});
|
|
156
|
+
if (!result2.success) {
|
|
157
|
+
appLogger.error("[JsHmrStrategy] Entrypoint build failed:", result2.logs);
|
|
158
|
+
return { success: false };
|
|
159
|
+
}
|
|
160
|
+
const dependencies2 = /* @__PURE__ */ new Map();
|
|
161
|
+
if (result2.dependencyGraph?.entrypoints) {
|
|
162
|
+
for (const [resolvedEntrypoint, deps] of Object.entries(result2.dependencyGraph.entrypoints)) {
|
|
163
|
+
dependencies2.set(path.resolve(resolvedEntrypoint), deps);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return { success: true, dependencies: dependencies2 };
|
|
167
|
+
}
|
|
123
168
|
const result = await this.context.getBrowserBundleService().bundle({
|
|
124
169
|
profile: "hmr-entrypoint",
|
|
125
170
|
entrypoints,
|
|
126
171
|
outdir: this.context.getDistDir(),
|
|
127
172
|
outbase: this.context.getSrcDir(),
|
|
128
173
|
naming: "[dir]/[name]",
|
|
129
|
-
plugins: this.context.getPlugins(),
|
|
130
174
|
minify: false
|
|
131
175
|
});
|
|
132
176
|
if (!result.success) {
|
|
@@ -153,8 +197,12 @@ class JsHmrStrategy extends HmrStrategy {
|
|
|
153
197
|
* @param url - URL path for the bundled file
|
|
154
198
|
* @returns True if processing was successful and update should be broadcast
|
|
155
199
|
*/
|
|
156
|
-
async processOutput(filepath, url) {
|
|
200
|
+
async processOutput(filepath, url, sourcePath) {
|
|
157
201
|
try {
|
|
202
|
+
if (sourcePath && !isHmrOutputFresh(filepath, sourcePath)) {
|
|
203
|
+
appLogger.warn(`[JsHmrStrategy] Skipping broadcast for stale HMR output ${url}`);
|
|
204
|
+
return { success: false, requiresReload: false };
|
|
205
|
+
}
|
|
158
206
|
const code = await fileSystem.readFile(filepath);
|
|
159
207
|
if (code.includes("/* [ecopages] hmr */")) {
|
|
160
208
|
return { success: true, requiresReload: !code.includes("import.meta.hot.accept") };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMR strategy for server-rendered include templates and explicit server views.
|
|
3
|
+
*
|
|
4
|
+
* These files affect SSR output (head, layout shell, explicit views) but are not
|
|
5
|
+
* client HMR entrypoints. Broadcast a layout-update so the active navigation
|
|
6
|
+
* runtime can refresh the current page without a full document reload.
|
|
7
|
+
*/
|
|
8
|
+
import { HmrStrategy, type HmrAction } from '../hmr-strategy.js';
|
|
9
|
+
import type { DevelopmentInvalidationService } from '../../services/invalidation/development-invalidation.service.js';
|
|
10
|
+
export declare class ServerRenderedTemplateHmrStrategy extends HmrStrategy {
|
|
11
|
+
private readonly invalidationService;
|
|
12
|
+
readonly type: 100;
|
|
13
|
+
readonly priorityOffset = 50;
|
|
14
|
+
constructor(invalidationService: DevelopmentInvalidationService);
|
|
15
|
+
matches(filePath: string): boolean;
|
|
16
|
+
process(filePath: string): Promise<HmrAction>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { HmrStrategy, HmrStrategyType } from "../hmr-strategy.js";
|
|
2
|
+
const SERVER_RENDERED_TEMPLATE_PRIORITY_OFFSET = 50;
|
|
3
|
+
class ServerRenderedTemplateHmrStrategy extends HmrStrategy {
|
|
4
|
+
constructor(invalidationService) {
|
|
5
|
+
super();
|
|
6
|
+
this.invalidationService = invalidationService;
|
|
7
|
+
}
|
|
8
|
+
invalidationService;
|
|
9
|
+
type = HmrStrategyType.INTEGRATION;
|
|
10
|
+
priorityOffset = SERVER_RENDERED_TEMPLATE_PRIORITY_OFFSET;
|
|
11
|
+
matches(filePath) {
|
|
12
|
+
return this.invalidationService.isIncludeSourceFile(filePath) || this.invalidationService.isExplicitServerViewFile(filePath);
|
|
13
|
+
}
|
|
14
|
+
async process(filePath) {
|
|
15
|
+
return {
|
|
16
|
+
type: "broadcast",
|
|
17
|
+
events: [
|
|
18
|
+
{
|
|
19
|
+
type: "layout-update",
|
|
20
|
+
path: filePath,
|
|
21
|
+
timestamp: Date.now()
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
ServerRenderedTemplateHmrStrategy
|
|
29
|
+
};
|
|
@@ -2,15 +2,11 @@
|
|
|
2
2
|
* This module contains the ghtml renderer
|
|
3
3
|
* @module
|
|
4
4
|
*/
|
|
5
|
-
import
|
|
6
|
-
import { IntegrationRenderer, type RenderToResponseContext } from '../../route-renderer/orchestration/integration-renderer.js';
|
|
5
|
+
import { StringMarkupRenderer } from '../../route-renderer/orchestration/string-markup-renderer.js';
|
|
7
6
|
/**
|
|
8
7
|
* A renderer for the ghtml integration.
|
|
9
8
|
* It renders a page using the HtmlTemplate and Page components.
|
|
10
9
|
*/
|
|
11
|
-
export declare class GhtmlRenderer extends
|
|
10
|
+
export declare class GhtmlRenderer extends StringMarkupRenderer {
|
|
12
11
|
name: string;
|
|
13
|
-
renderComponent(input: ComponentRenderInput): Promise<ComponentRenderResult>;
|
|
14
|
-
render({ params, query, props, locals, pageLocals, metadata, Page, Layout, HtmlTemplate, }: IntegrationRendererRenderOptions): Promise<RouteRendererBody>;
|
|
15
|
-
renderToResponse<P = Record<string, unknown>>(view: EcoComponent<P>, props: P, ctx: RenderToResponseContext): Promise<Response>;
|
|
16
12
|
}
|