@ecopages/core 0.2.0-alpha.25 → 0.2.0-alpha.27
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 +63 -7
- package/package.json +4 -47
- package/src/adapters/bun/create-app.ts +54 -2
- package/src/adapters/bun/hmr-manager.test.ts +0 -2
- package/src/adapters/bun/hmr-manager.ts +1 -24
- package/src/adapters/bun/server-adapter.ts +30 -4
- package/src/adapters/node/node-hmr-manager.test.ts +0 -2
- package/src/adapters/node/node-hmr-manager.ts +2 -25
- package/src/adapters/shared/explicit-static-render-preparation.ts +58 -0
- package/src/adapters/shared/explicit-static-route-matcher.test.ts +6 -6
- package/src/adapters/shared/explicit-static-route-matcher.ts +22 -31
- package/src/adapters/shared/file-route-middleware-pipeline.test.ts +5 -10
- package/src/adapters/shared/file-route-middleware-pipeline.ts +8 -17
- package/src/adapters/shared/fs-server-response-factory.test.ts +32 -43
- package/src/adapters/shared/fs-server-response-factory.ts +15 -37
- package/src/adapters/shared/fs-server-response-matcher.test.ts +65 -39
- package/src/adapters/shared/fs-server-response-matcher.ts +94 -43
- package/src/adapters/shared/hmr-manager.contract.test.ts +0 -4
- package/src/adapters/shared/render-context.ts +3 -3
- package/src/adapters/shared/server-adapter.test.ts +53 -0
- package/src/adapters/shared/server-adapter.ts +228 -159
- package/src/adapters/shared/server-route-handler.test.ts +6 -5
- package/src/adapters/shared/server-route-handler.ts +4 -4
- package/src/adapters/shared/server-static-builder.test.ts +4 -4
- package/src/adapters/shared/server-static-builder.ts +4 -4
- package/src/config/README.md +1 -1
- package/src/config/config-builder.test.ts +0 -1
- package/src/config/config-builder.ts +2 -7
- package/src/dev/host-runtime.ts +34 -0
- package/src/eco/eco.browser.test.ts +2 -2
- package/src/eco/eco.browser.ts +2 -2
- package/src/eco/eco.test.ts +6 -6
- package/src/eco/eco.ts +12 -12
- package/src/eco/eco.types.ts +3 -3
- package/src/errors/index.ts +1 -0
- package/src/hmr/client/hmr-runtime.ts +4 -2
- package/src/hmr/strategies/js-hmr-strategy.test.ts +0 -1
- package/src/hmr/strategies/js-hmr-strategy.ts +0 -6
- package/src/integrations/ghtml/ghtml-renderer.test.ts +7 -7
- package/src/integrations/ghtml/ghtml-renderer.ts +1 -11
- package/src/plugins/eco-component-meta-plugin.ts +0 -1
- package/src/plugins/integration-plugin.test.ts +9 -14
- package/src/plugins/integration-plugin.ts +34 -22
- package/src/plugins/processor.ts +17 -0
- package/src/route-renderer/GRAPH.md +81 -289
- package/src/route-renderer/README.md +67 -105
- package/src/route-renderer/orchestration/component-render-context.ts +45 -38
- package/src/route-renderer/orchestration/declared-ownership-graph.ts +62 -0
- package/src/route-renderer/orchestration/foreign-subtree-execution.service.ts +383 -0
- package/src/route-renderer/orchestration/integration-renderer.test.ts +118 -121
- package/src/route-renderer/orchestration/integration-renderer.ts +362 -403
- package/src/route-renderer/orchestration/ownership-planning.service.ts +97 -0
- package/src/route-renderer/orchestration/ownership-validation.service.ts +76 -0
- package/src/route-renderer/orchestration/processed-asset-dedupe.ts +1 -1
- package/src/route-renderer/orchestration/{queued-boundary-runtime.service.test.ts → queued-foreign-subtree-resolution.service.test.ts} +76 -71
- package/src/route-renderer/orchestration/{queued-boundary-runtime.service.ts → queued-foreign-subtree-resolution.service.ts} +68 -63
- package/src/route-renderer/orchestration/render-output.utils.ts +21 -13
- package/src/route-renderer/orchestration/{render-preparation.service.test.ts → route-render-orchestrator.prepare-render-options.test.ts} +160 -85
- package/src/route-renderer/orchestration/route-render-orchestrator.test.ts +265 -0
- package/src/route-renderer/orchestration/{render-preparation.service.ts → route-render-orchestrator.ts} +244 -160
- package/src/route-renderer/page-loading/component-dependency-collection.ts +9 -3
- package/src/route-renderer/page-loading/declared-asset-collection.ts +2 -5
- package/src/route-renderer/page-loading/dependency-resolver.test.ts +107 -11
- package/src/route-renderer/page-loading/dependency-resolver.ts +6 -12
- package/src/route-renderer/page-loading/ecopages-virtual-imports.ts +1 -1
- package/src/route-renderer/page-loading/lazy-entry-collection.ts +1 -1
- package/src/route-renderer/page-loading/lazy-trigger-planning.ts +1 -1
- package/src/route-renderer/page-loading/module-declaration-aggregation.ts +1 -1
- package/src/route-renderer/page-loading/module-declaration-scripts.ts +1 -1
- package/src/route-renderer/page-loading/page-dependency-bundling.ts +105 -66
- package/src/route-renderer/route-renderer.ts +28 -31
- package/src/router/README.md +16 -19
- package/src/router/server/route-registry.test.ts +176 -0
- package/src/router/server/route-registry.ts +382 -0
- package/src/services/README.md +1 -2
- package/src/services/assets/asset-processing-service/asset-dependency-keys.ts +1 -1
- package/src/services/assets/asset-processing-service/asset-processing.service.test.ts +1 -4
- package/src/services/assets/asset-processing-service/asset-processing.service.ts +1 -2
- package/src/services/assets/asset-processing-service/assets.types.ts +3 -0
- package/src/services/assets/asset-processing-service/grouped-content-bundles.ts +1 -1
- package/src/services/assets/asset-processing-service/index.ts +1 -0
- package/src/{route-renderer/orchestration/page-packaging.service.test.ts → services/assets/asset-processing-service/page-package.test.ts} +38 -14
- package/src/services/assets/asset-processing-service/page-package.ts +93 -0
- package/src/services/assets/asset-processing-service/processors/base/base-script-processor.ts +4 -5
- package/src/services/assets/asset-processing-service/processors/script/content-script.processor.test.ts +13 -10
- package/src/services/assets/asset-processing-service/processors/script/content-script.processor.ts +3 -0
- package/src/services/assets/asset-processing-service/processors/script/file-script.processor.ts +6 -0
- package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.ts +2 -0
- package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.ts +1 -0
- package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.ts +2 -0
- package/src/services/assets/asset-processing-service/ungrouped-dependency-processing.ts +1 -1
- package/src/services/html/html-transformer.service.test.ts +1 -4
- package/src/services/module-loading/app-server-module-transpiler.service.ts +1 -3
- package/src/services/module-loading/node-bootstrap-plugin.ts +17 -3
- package/src/services/module-loading/page-module-import.service.ts +0 -1
- package/src/services/module-loading/source-module-support.ts +1 -1
- package/src/static-site-generator/static-site-generator.test.ts +124 -32
- package/src/static-site-generator/static-site-generator.ts +168 -185
- package/src/types/internal-types.ts +13 -12
- package/src/types/public-types.ts +55 -39
- package/src/watchers/project-watcher.test-helpers.ts +4 -3
- package/src/route-renderer/orchestration/boundary-planning.service.ts +0 -146
- package/src/route-renderer/orchestration/page-packaging.service.ts +0 -85
- package/src/route-renderer/orchestration/render-execution.service.test.ts +0 -196
- package/src/route-renderer/orchestration/render-execution.service.ts +0 -182
- package/src/route-renderer/orchestration/route-shell-composer.service.ts +0 -162
- package/src/router/server/fs-router-scanner.test.ts +0 -83
- package/src/router/server/fs-router-scanner.ts +0 -224
- package/src/router/server/fs-router.test.ts +0 -214
- package/src/router/server/fs-router.ts +0 -122
- package/src/services/runtime-state/runtime-specifier-registry.service.ts +0 -96
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import type { ProcessedAsset } from '../../services/assets/asset-processing-service/index.ts';
|
|
2
|
+
import type {
|
|
3
|
+
BaseIntegrationContext,
|
|
4
|
+
ComponentRenderInput,
|
|
5
|
+
ComponentRenderResult,
|
|
6
|
+
EcoComponent,
|
|
7
|
+
ForeignSubtreeRenderPayload,
|
|
8
|
+
} from '../../types/public-types.ts';
|
|
9
|
+
import {
|
|
10
|
+
getComponentRenderContext,
|
|
11
|
+
runWithComponentRenderContext,
|
|
12
|
+
type ForeignChildRuntime,
|
|
13
|
+
} from './component-render-context.ts';
|
|
14
|
+
import {
|
|
15
|
+
QueuedForeignSubtreeResolutionService,
|
|
16
|
+
type QueuedForeignSubtreeResolution,
|
|
17
|
+
type QueuedForeignSubtreeResolutionContext,
|
|
18
|
+
} from './queued-foreign-subtree-resolution.service.ts';
|
|
19
|
+
|
|
20
|
+
export interface ForeignSubtreeExecutionOwningRenderer {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
renderComponentWithForeignChildren(input: ComponentRenderInput): Promise<ComponentRenderResult>;
|
|
23
|
+
renderForeignSubtree(input: ComponentRenderInput): Promise<ForeignSubtreeRenderPayload>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ForeignSubtreeExecutionDecisionInput {
|
|
27
|
+
currentIntegration: string;
|
|
28
|
+
targetIntegration?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ForeignSubtreeExecutionRenderOptions {
|
|
32
|
+
currentIntegrationName: string;
|
|
33
|
+
input: ComponentRenderInput;
|
|
34
|
+
renderComponent(input: ComponentRenderInput): Promise<ComponentRenderResult>;
|
|
35
|
+
normalizeComponentRenderOutput(result: ComponentRenderResult): ComponentRenderResult;
|
|
36
|
+
hasForeignChildDescendants(component: EcoComponent): boolean;
|
|
37
|
+
createForeignChildRuntime(options: {
|
|
38
|
+
renderInput: ComponentRenderInput;
|
|
39
|
+
rendererCache: Map<string, ForeignSubtreeExecutionOwningRenderer>;
|
|
40
|
+
}): ForeignChildRuntime;
|
|
41
|
+
getOwningRenderer(
|
|
42
|
+
integrationName: string,
|
|
43
|
+
rendererCache: Map<string, ForeignSubtreeExecutionOwningRenderer>,
|
|
44
|
+
): ForeignSubtreeExecutionOwningRenderer;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ForeignSubtreeQueuedRuntimeOptions<TContext extends QueuedForeignSubtreeResolutionContext> {
|
|
48
|
+
renderInput: ComponentRenderInput;
|
|
49
|
+
rendererCache: Map<string, ForeignSubtreeExecutionOwningRenderer>;
|
|
50
|
+
runtimeContextKey: string;
|
|
51
|
+
tokenPrefix: string;
|
|
52
|
+
createRuntimeContext?: (
|
|
53
|
+
integrationContext: BaseIntegrationContext & Record<string, unknown>,
|
|
54
|
+
rendererCache: Map<string, unknown>,
|
|
55
|
+
) => TContext;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface ForeignSubtreeStringQueuedHtmlOptions {
|
|
59
|
+
currentIntegrationName: string;
|
|
60
|
+
renderInput: ComponentRenderInput;
|
|
61
|
+
html: string;
|
|
62
|
+
runtimeContextKey: string;
|
|
63
|
+
queueLabel: string;
|
|
64
|
+
getOwningRenderer(
|
|
65
|
+
integrationName: string,
|
|
66
|
+
rendererCache: Map<string, ForeignSubtreeExecutionOwningRenderer>,
|
|
67
|
+
): ForeignSubtreeExecutionOwningRenderer;
|
|
68
|
+
applyAttributesToFirstElement(html: string, attributes: Record<string, string>): string;
|
|
69
|
+
dedupeProcessedAssets(assets: ProcessedAsset[]): ProcessedAsset[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface ForeignSubtreeQueuedHtmlOptions<TContext extends QueuedForeignSubtreeResolutionContext> {
|
|
73
|
+
currentIntegrationName: string;
|
|
74
|
+
html: string;
|
|
75
|
+
runtimeContext?: TContext;
|
|
76
|
+
queueLabel: string;
|
|
77
|
+
renderQueuedChildren(
|
|
78
|
+
children: unknown,
|
|
79
|
+
runtimeContext: TContext,
|
|
80
|
+
queuedResolutionsByToken: Map<string, QueuedForeignSubtreeResolution>,
|
|
81
|
+
resolveToken: (token: string) => Promise<string>,
|
|
82
|
+
): Promise<{ assets: ProcessedAsset[]; html?: string }>;
|
|
83
|
+
getOwningRenderer(
|
|
84
|
+
integrationName: string,
|
|
85
|
+
rendererCache: Map<string, ForeignSubtreeExecutionOwningRenderer>,
|
|
86
|
+
): ForeignSubtreeExecutionOwningRenderer;
|
|
87
|
+
applyAttributesToFirstElement(html: string, attributes: Record<string, string>): string;
|
|
88
|
+
dedupeProcessedAssets(assets: ProcessedAsset[]): ProcessedAsset[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Executes one component render tree under Foreign Child support.
|
|
93
|
+
*
|
|
94
|
+
* This service owns the execution policy for mixed-integration component trees:
|
|
95
|
+
* it decides when a child stays inline, when it must delegate to an owning
|
|
96
|
+
* renderer, how one render-pass renderer cache is reused, and how queued
|
|
97
|
+
* Foreign Subtree tokens resolve back into final HTML.
|
|
98
|
+
*/
|
|
99
|
+
export class ForeignSubtreeExecutionService {
|
|
100
|
+
private readonly queuedForeignSubtreeResolutionService: QueuedForeignSubtreeResolutionService;
|
|
101
|
+
|
|
102
|
+
constructor(queuedForeignSubtreeResolutionService = new QueuedForeignSubtreeResolutionService()) {
|
|
103
|
+
this.queuedForeignSubtreeResolutionService = queuedForeignSubtreeResolutionService;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Returns whether the current render pass must hand the child off to a foreign owner.
|
|
108
|
+
*/
|
|
109
|
+
shouldDelegateForeignChild(input: ForeignSubtreeExecutionDecisionInput): boolean {
|
|
110
|
+
return !!input.targetIntegration && input.targetIntegration !== input.currentIntegration;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Creates the base runtime used when a renderer has not supplied its own queueing runtime.
|
|
115
|
+
*
|
|
116
|
+
* The runtime allows same-integration children to continue inline and fails fast
|
|
117
|
+
* when execution crosses into a foreign owner without a renderer-owned handoff.
|
|
118
|
+
*/
|
|
119
|
+
createFailFastRuntime(rendererName: string): ForeignChildRuntime {
|
|
120
|
+
const interceptForeignChild = (input: ForeignSubtreeExecutionDecisionInput) => {
|
|
121
|
+
if (!this.shouldDelegateForeignChild(input)) {
|
|
122
|
+
return { kind: 'inline' as const };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
throw new Error(
|
|
126
|
+
`[ecopages] ${rendererName} renderer crossed into ${input.targetIntegration} without a renderer-owned foreign-child runtime. Override createForeignChildRuntime() to resolve foreign children inside the owning renderer.`,
|
|
127
|
+
);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
interceptForeignChild,
|
|
132
|
+
interceptForeignChildSync: interceptForeignChild,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
getQueuedRuntimeContext<TContext extends QueuedForeignSubtreeResolutionContext>(
|
|
137
|
+
input: ComponentRenderInput,
|
|
138
|
+
runtimeContextKey: string,
|
|
139
|
+
): TContext | undefined {
|
|
140
|
+
return this.queuedForeignSubtreeResolutionService.getRuntimeContext<TContext>(input, runtimeContextKey);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
createQueuedRuntime<TContext extends QueuedForeignSubtreeResolutionContext>(
|
|
144
|
+
options: ForeignSubtreeQueuedRuntimeOptions<TContext>,
|
|
145
|
+
): ForeignChildRuntime {
|
|
146
|
+
return this.queuedForeignSubtreeResolutionService.createRuntime<TContext>({
|
|
147
|
+
renderInput: options.renderInput,
|
|
148
|
+
rendererCache: options.rendererCache as Map<string, unknown>,
|
|
149
|
+
runtimeContextKey: options.runtimeContextKey,
|
|
150
|
+
tokenPrefix: options.tokenPrefix,
|
|
151
|
+
shouldQueueForeignChild: (input) => this.shouldDelegateForeignChild(input),
|
|
152
|
+
createRuntimeContext: options.createRuntimeContext,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Resolves a string-first renderer HTML fragment that may contain queued Foreign Subtree tokens.
|
|
158
|
+
*/
|
|
159
|
+
async resolveStringQueuedHtml<TContext extends QueuedForeignSubtreeResolutionContext>(
|
|
160
|
+
options: ForeignSubtreeStringQueuedHtmlOptions,
|
|
161
|
+
): Promise<{ assets: ProcessedAsset[]; html: string }> {
|
|
162
|
+
const runtimeContext = this.getQueuedRuntimeContext<TContext>(options.renderInput, options.runtimeContextKey);
|
|
163
|
+
|
|
164
|
+
return this.resolveQueuedHtml({
|
|
165
|
+
currentIntegrationName: options.currentIntegrationName,
|
|
166
|
+
html: options.html,
|
|
167
|
+
runtimeContext,
|
|
168
|
+
queueLabel: options.queueLabel,
|
|
169
|
+
renderQueuedChildren: async (children, _runtimeContext, queuedResolutionsByToken, resolveToken) => {
|
|
170
|
+
if (children === undefined) {
|
|
171
|
+
return { assets: [], html: undefined };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const html = await this.resolveQueuedTokens(
|
|
175
|
+
typeof children === 'string' ? children : String(children ?? ''),
|
|
176
|
+
queuedResolutionsByToken,
|
|
177
|
+
resolveToken,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
return { assets: [], html };
|
|
181
|
+
},
|
|
182
|
+
getOwningRenderer: options.getOwningRenderer,
|
|
183
|
+
applyAttributesToFirstElement: options.applyAttributesToFirstElement,
|
|
184
|
+
dedupeProcessedAssets: options.dedupeProcessedAssets,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async resolveQueuedHtml<TContext extends QueuedForeignSubtreeResolutionContext>(
|
|
189
|
+
options: ForeignSubtreeQueuedHtmlOptions<TContext>,
|
|
190
|
+
): Promise<{ assets: ProcessedAsset[]; html: string }> {
|
|
191
|
+
return this.queuedForeignSubtreeResolutionService.resolveQueuedHtml({
|
|
192
|
+
html: options.html,
|
|
193
|
+
runtimeContext: options.runtimeContext,
|
|
194
|
+
queueLabel: options.queueLabel,
|
|
195
|
+
renderQueuedChildren: options.renderQueuedChildren,
|
|
196
|
+
resolveForeignSubtree: (input, rendererCache) =>
|
|
197
|
+
this.resolveForeignSubtreeInOwningRenderer({
|
|
198
|
+
currentIntegrationName: options.currentIntegrationName,
|
|
199
|
+
input,
|
|
200
|
+
rendererCache: rendererCache as Map<string, ForeignSubtreeExecutionOwningRenderer>,
|
|
201
|
+
getOwningRenderer: options.getOwningRenderer,
|
|
202
|
+
}),
|
|
203
|
+
applyAttributesToFirstElement: options.applyAttributesToFirstElement,
|
|
204
|
+
dedupeProcessedAssets: options.dedupeProcessedAssets,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Executes one component render with Foreign Child support under the current integration.
|
|
210
|
+
*/
|
|
211
|
+
async executeComponentRender(options: ForeignSubtreeExecutionRenderOptions): Promise<ComponentRenderResult> {
|
|
212
|
+
const rendererCache =
|
|
213
|
+
this.getRendererCache(options.input.integrationContext) ??
|
|
214
|
+
new Map<string, ForeignSubtreeExecutionOwningRenderer>();
|
|
215
|
+
const delegatedForeignChildRender = await this.resolveForeignChildInOwningRenderer({
|
|
216
|
+
currentIntegrationName: options.currentIntegrationName,
|
|
217
|
+
input: options.input,
|
|
218
|
+
rendererCache,
|
|
219
|
+
getOwningRenderer: options.getOwningRenderer,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (delegatedForeignChildRender) {
|
|
223
|
+
return delegatedForeignChildRender;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const hasForeignChildren = options.hasForeignChildDescendants(options.input.component);
|
|
227
|
+
const activeRenderContext = getComponentRenderContext();
|
|
228
|
+
|
|
229
|
+
if (!hasForeignChildren) {
|
|
230
|
+
if (!activeRenderContext || activeRenderContext.currentIntegration === options.currentIntegrationName) {
|
|
231
|
+
return options.normalizeComponentRenderOutput(await options.renderComponent(options.input));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const sameIntegrationExecution = await runWithComponentRenderContext(
|
|
235
|
+
{
|
|
236
|
+
currentIntegration: options.currentIntegrationName,
|
|
237
|
+
},
|
|
238
|
+
async () => options.renderComponent(options.input),
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
return options.normalizeComponentRenderOutput(sameIntegrationExecution.value);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const execution = await runWithComponentRenderContext(
|
|
245
|
+
{
|
|
246
|
+
currentIntegration: options.currentIntegrationName,
|
|
247
|
+
foreignChildRuntime: options.createForeignChildRuntime({
|
|
248
|
+
renderInput: options.input,
|
|
249
|
+
rendererCache,
|
|
250
|
+
}),
|
|
251
|
+
},
|
|
252
|
+
async () => options.renderComponent(options.input),
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
return options.normalizeComponentRenderOutput(execution.value);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private getRendererCache(
|
|
259
|
+
integrationContext?: BaseIntegrationContext,
|
|
260
|
+
): Map<string, ForeignSubtreeExecutionOwningRenderer> | undefined {
|
|
261
|
+
if (integrationContext?.rendererCache instanceof Map) {
|
|
262
|
+
return integrationContext.rendererCache as Map<string, ForeignSubtreeExecutionOwningRenderer>;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private withRendererCache(
|
|
269
|
+
input: ComponentRenderInput,
|
|
270
|
+
rendererCache: Map<string, ForeignSubtreeExecutionOwningRenderer>,
|
|
271
|
+
): ComponentRenderInput {
|
|
272
|
+
const integrationContext = input.integrationContext;
|
|
273
|
+
const sharedRendererCache = rendererCache as BaseIntegrationContext['rendererCache'];
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
...input,
|
|
277
|
+
integrationContext: integrationContext
|
|
278
|
+
? { ...integrationContext, rendererCache: sharedRendererCache }
|
|
279
|
+
: { rendererCache: sharedRendererCache },
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Returns the delegatable owning renderer integration for one component.
|
|
285
|
+
*
|
|
286
|
+
* The pseudo `html` integration marks document-shell ownership only and does
|
|
287
|
+
* not participate in component-level foreign subtree execution.
|
|
288
|
+
*/
|
|
289
|
+
private getForeignOwnerIntegrationName(
|
|
290
|
+
component: EcoComponent,
|
|
291
|
+
currentIntegrationName: string,
|
|
292
|
+
): string | undefined {
|
|
293
|
+
const integrationName = component.config?.integration ?? component.config?.__eco?.integration;
|
|
294
|
+
if (!integrationName || integrationName === 'html' || integrationName === currentIntegrationName) {
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return integrationName;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private async resolveForeignChildInOwningRenderer(options: {
|
|
302
|
+
currentIntegrationName: string;
|
|
303
|
+
input: ComponentRenderInput;
|
|
304
|
+
rendererCache: Map<string, ForeignSubtreeExecutionOwningRenderer>;
|
|
305
|
+
getOwningRenderer(
|
|
306
|
+
integrationName: string,
|
|
307
|
+
rendererCache: Map<string, ForeignSubtreeExecutionOwningRenderer>,
|
|
308
|
+
): ForeignSubtreeExecutionOwningRenderer;
|
|
309
|
+
}): Promise<ComponentRenderResult | undefined> {
|
|
310
|
+
return await this.runInForeignOwningRenderer({
|
|
311
|
+
currentIntegrationName: options.currentIntegrationName,
|
|
312
|
+
input: options.input,
|
|
313
|
+
rendererCache: options.rendererCache,
|
|
314
|
+
getOwningRenderer: options.getOwningRenderer,
|
|
315
|
+
run: (owningRenderer, delegatedInput) => owningRenderer.renderComponentWithForeignChildren(delegatedInput),
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
private async resolveForeignSubtreeInOwningRenderer(options: {
|
|
320
|
+
currentIntegrationName: string;
|
|
321
|
+
input: ComponentRenderInput;
|
|
322
|
+
rendererCache: Map<string, ForeignSubtreeExecutionOwningRenderer>;
|
|
323
|
+
getOwningRenderer(
|
|
324
|
+
integrationName: string,
|
|
325
|
+
rendererCache: Map<string, ForeignSubtreeExecutionOwningRenderer>,
|
|
326
|
+
): ForeignSubtreeExecutionOwningRenderer;
|
|
327
|
+
}): Promise<ForeignSubtreeRenderPayload | undefined> {
|
|
328
|
+
return await this.runInForeignOwningRenderer({
|
|
329
|
+
currentIntegrationName: options.currentIntegrationName,
|
|
330
|
+
input: options.input,
|
|
331
|
+
rendererCache: options.rendererCache,
|
|
332
|
+
getOwningRenderer: options.getOwningRenderer,
|
|
333
|
+
run: (owningRenderer, delegatedInput) => owningRenderer.renderForeignSubtree(delegatedInput),
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
private async runInForeignOwningRenderer<TResult>(options: {
|
|
338
|
+
currentIntegrationName: string;
|
|
339
|
+
input: ComponentRenderInput;
|
|
340
|
+
rendererCache: Map<string, ForeignSubtreeExecutionOwningRenderer>;
|
|
341
|
+
getOwningRenderer(
|
|
342
|
+
integrationName: string,
|
|
343
|
+
rendererCache: Map<string, ForeignSubtreeExecutionOwningRenderer>,
|
|
344
|
+
): ForeignSubtreeExecutionOwningRenderer;
|
|
345
|
+
run(
|
|
346
|
+
owningRenderer: ForeignSubtreeExecutionOwningRenderer,
|
|
347
|
+
delegatedInput: ComponentRenderInput,
|
|
348
|
+
): Promise<TResult>;
|
|
349
|
+
}): Promise<TResult | undefined> {
|
|
350
|
+
const foreignOwnerIntegrationName = this.getForeignOwnerIntegrationName(
|
|
351
|
+
options.input.component,
|
|
352
|
+
options.currentIntegrationName,
|
|
353
|
+
);
|
|
354
|
+
if (!foreignOwnerIntegrationName) {
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const owningRenderer = options.getOwningRenderer(foreignOwnerIntegrationName, options.rendererCache);
|
|
359
|
+
if (owningRenderer.name === options.currentIntegrationName) {
|
|
360
|
+
return undefined;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return await options.run(owningRenderer, this.withRendererCache(options.input, options.rendererCache));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async resolveQueuedTokens(
|
|
367
|
+
html: string,
|
|
368
|
+
queuedResolutionsByToken: Map<string, QueuedForeignSubtreeResolution>,
|
|
369
|
+
resolveToken: (token: string) => Promise<string>,
|
|
370
|
+
): Promise<string> {
|
|
371
|
+
let resolvedHtml = html;
|
|
372
|
+
|
|
373
|
+
for (const token of queuedResolutionsByToken.keys()) {
|
|
374
|
+
if (!resolvedHtml.includes(token)) {
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
resolvedHtml = resolvedHtml.split(token).join(await resolveToken(token));
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return resolvedHtml;
|
|
382
|
+
}
|
|
383
|
+
}
|