@ecopages/lit 0.2.0-beta.4 → 0.2.0-beta.40
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 +11 -3
- package/src/STATIC_RENDER.md +69 -0
- package/src/console.js +12 -6
- package/src/dom-shim.js +13 -12
- package/src/lit-element-hydrate.js +1 -4
- package/src/lit-renderer.d.ts +27 -5
- package/src/lit-renderer.js +215 -224
- package/src/lit-ssr-lazy-preloader.d.ts +1 -1
- package/src/lit-ssr-lazy-preloader.js +155 -153
- package/src/lit-static-render-protocol.d.ts +35 -0
- package/src/lit-static-render-protocol.js +1 -0
- package/src/lit-static-render-session.d.ts +63 -0
- package/src/lit-static-render-session.js +119 -0
- package/src/lit-static-render-worker-client.d.ts +26 -0
- package/src/lit-static-render-worker-client.js +109 -0
- package/src/lit-static-render-worker.d.ts +1 -0
- package/src/lit-static-render-worker.js +80 -0
- package/src/lit.constants.js +1 -4
- package/src/lit.plugin.d.ts +14 -1
- package/src/lit.plugin.js +113 -48
- package/src/styled-mixin.js +20 -12
- package/src/utils/lit-html-rendering.js +39 -46
package/src/lit-renderer.js
CHANGED
|
@@ -1,236 +1,227 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
/**
|
|
2
|
+
* This module contains the Lit renderer
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
import './dom-shim.js';
|
|
6
|
+
import { IntegrationRenderer, } from '@ecopages/core/route-renderer/orchestration/integration-renderer';
|
|
7
|
+
import { getForeignSubtreeResolutionContextKey, resolveOwningIntegrationRenderer, } from '@ecopages/core/route-renderer/orchestration/foreign-child/owning-renderer-resolution';
|
|
8
|
+
import { LitSsrLazyPreloader } from './lit-ssr-lazy-preloader.js';
|
|
9
|
+
import { LIT_PLUGIN_NAME } from './lit.constants.js';
|
|
10
|
+
import { injectLitRenderedChildren, LIT_COMPONENT_CHILDREN_SLOT_MARKER, normalizeLitHtml, renderLitValueToString, } from './utils/lit-html-rendering.js';
|
|
11
|
+
/**
|
|
12
|
+
* A renderer for the Lit integration.
|
|
13
|
+
*/
|
|
14
|
+
export class LitRenderer extends IntegrationRenderer {
|
|
15
|
+
name = LIT_PLUGIN_NAME;
|
|
16
|
+
getRenderSession;
|
|
17
|
+
constructor(options) {
|
|
18
|
+
const { getRenderSession, ...rendererOptions } = options;
|
|
19
|
+
super(rendererOptions);
|
|
20
|
+
this.getRenderSession = getRenderSession;
|
|
19
21
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Renders a Lit page route, preferring the plugin worker when available.
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* Activates the integration runtime before reading {@link getRenderSession}
|
|
27
|
+
* so a renderer constructed before plugin setup still sees the session.
|
|
28
|
+
* When `options.locals` is present, rendering stays in-process: request locals
|
|
29
|
+
* are not structured-cloned across the worker boundary.
|
|
30
|
+
*/
|
|
31
|
+
async execute(options) {
|
|
32
|
+
await this.ensureIntegrationRuntimeActivated();
|
|
33
|
+
const renderSession = this.getRenderSession?.();
|
|
34
|
+
if (renderSession && options.locals === undefined) {
|
|
35
|
+
const params = options.params ?? {};
|
|
36
|
+
const result = await renderSession.renderPageInWorker({
|
|
37
|
+
filePath: options.file,
|
|
38
|
+
params,
|
|
39
|
+
query: options.query,
|
|
40
|
+
});
|
|
41
|
+
return {
|
|
42
|
+
body: result.html,
|
|
43
|
+
cacheStrategy: result.cacheStrategy,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return super.execute(options);
|
|
22
47
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
resolveToken
|
|
45
|
-
);
|
|
46
|
-
if (typeof renderedChildren !== "string") {
|
|
47
|
-
return {
|
|
48
|
-
assets: [],
|
|
49
|
-
children: renderedChildren
|
|
50
|
-
};
|
|
48
|
+
isFunctionComponent(component) {
|
|
49
|
+
return typeof component === 'function';
|
|
50
|
+
}
|
|
51
|
+
async resolveQueuedForeignSubtreeChildren(children, queuedResolutionsByToken, resolveToken) {
|
|
52
|
+
if (children === undefined) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
if (typeof children !== 'string') {
|
|
56
|
+
return children;
|
|
57
|
+
}
|
|
58
|
+
let renderedChildren = children;
|
|
59
|
+
renderedChildren = await this.foreignSubtreeExecutionService.resolveQueuedTokens(renderedChildren, queuedResolutionsByToken, resolveToken);
|
|
60
|
+
return renderedChildren;
|
|
61
|
+
}
|
|
62
|
+
async renderLitQueuedForeignSubtreeChildren(children, queuedResolutionsByToken, resolveToken) {
|
|
63
|
+
const renderedChildren = await this.resolveQueuedForeignSubtreeChildren(children, queuedResolutionsByToken, resolveToken);
|
|
64
|
+
if (typeof renderedChildren !== 'string') {
|
|
65
|
+
return {
|
|
66
|
+
assets: [],
|
|
67
|
+
children: renderedChildren,
|
|
68
|
+
};
|
|
51
69
|
}
|
|
52
70
|
return {
|
|
53
|
-
|
|
54
|
-
|
|
71
|
+
assets: [],
|
|
72
|
+
html: renderedChildren,
|
|
55
73
|
};
|
|
56
|
-
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Renders a Lit component for component-level orchestration.
|
|
77
|
+
*
|
|
78
|
+
* SSR-eligible lazy scripts are preloaded first so custom elements registered
|
|
79
|
+
* by the component can render their server markup even when the Lit renderer is
|
|
80
|
+
* entered through cross-integration foreign-child handoff.
|
|
81
|
+
*
|
|
82
|
+
* Includes component-scoped dependency assets when declared.
|
|
83
|
+
*/
|
|
84
|
+
async renderComponent(input) {
|
|
85
|
+
await this.preloadSsrLazyScripts([input.component]);
|
|
86
|
+
if (!this.isFunctionComponent(input.component)) {
|
|
87
|
+
throw new TypeError('Lit renderer expected a callable component.');
|
|
88
|
+
}
|
|
89
|
+
const component = input.component;
|
|
90
|
+
let renderedChildren;
|
|
91
|
+
if (input.children !== undefined) {
|
|
92
|
+
renderedChildren =
|
|
93
|
+
typeof input.children === 'string' ? input.children : await renderLitValueToString(input.children);
|
|
94
|
+
}
|
|
95
|
+
let props = input.props;
|
|
96
|
+
if (renderedChildren !== undefined) {
|
|
97
|
+
props = {
|
|
98
|
+
...input.props,
|
|
99
|
+
children: LIT_COMPONENT_CHILDREN_SLOT_MARKER,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const content = await component(props);
|
|
103
|
+
const renderedHtml = await renderLitValueToString(content);
|
|
104
|
+
const html = renderedChildren === undefined ? renderedHtml : injectLitRenderedChildren(renderedHtml, renderedChildren);
|
|
105
|
+
const queuedForeignSubtreeResolution = await this.foreignSubtreeExecutionService.resolveQueuedHtml({
|
|
106
|
+
currentIntegrationName: this.name,
|
|
107
|
+
html,
|
|
108
|
+
runtimeContext: this.foreignSubtreeExecutionService.getQueuedRuntimeContext(input, getForeignSubtreeResolutionContextKey(this.name)),
|
|
109
|
+
queueLabel: 'Lit',
|
|
110
|
+
getOwningRenderer: (integrationName, rendererCache) => resolveOwningIntegrationRenderer({
|
|
111
|
+
appConfig: this.appConfig,
|
|
112
|
+
runtimeOrigin: this.runtimeOrigin,
|
|
113
|
+
currentIntegrationName: this.name,
|
|
114
|
+
currentRenderer: this,
|
|
115
|
+
integrationName,
|
|
116
|
+
cache: rendererCache,
|
|
117
|
+
}),
|
|
118
|
+
applyAttributesToFirstElement: (resolvedHtml, attributes) => this.htmlTransformer.applyAttributesToFirstElement(resolvedHtml, attributes),
|
|
119
|
+
dedupeProcessedAssets: (assets) => this.htmlTransformer.dedupeProcessedAssets(assets),
|
|
120
|
+
renderQueuedChildren: (children, _runtimeContext, queuedResolutionsByToken, resolveToken) => this.renderLitQueuedForeignSubtreeChildren(children, queuedResolutionsByToken, resolveToken),
|
|
121
|
+
});
|
|
122
|
+
const hasDependencies = Boolean(input.component.config?.dependencies);
|
|
123
|
+
const canResolveAssets = typeof this.assetProcessingService?.processDependencies === 'function';
|
|
124
|
+
const assets = hasDependencies && canResolveAssets
|
|
125
|
+
? await this.processComponentDependencies([input.component])
|
|
126
|
+
: undefined;
|
|
127
|
+
return this.finalizeIslandComponentRender(input, {
|
|
128
|
+
html: queuedForeignSubtreeResolution.html,
|
|
129
|
+
canAttachAttributes: true,
|
|
130
|
+
rootTag: this.getRootTagName(queuedForeignSubtreeResolution.html),
|
|
131
|
+
integrationName: this.name,
|
|
132
|
+
assets: this.htmlTransformer.dedupeProcessedAssets([
|
|
133
|
+
...(assets ?? []),
|
|
134
|
+
...queuedForeignSubtreeResolution.assets,
|
|
135
|
+
]),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
ssrLazyPreloader = new LitSsrLazyPreloader({
|
|
139
|
+
resolveDependencyPath: this.resolveDependencyPath.bind(this),
|
|
140
|
+
processDependencies: this.assetProcessingService?.processDependencies?.bind(this.assetProcessingService),
|
|
57
141
|
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
isLitManagedComponent(component) {
|
|
67
|
-
return component?.config?.integration === this.name || component?.config?.__eco?.integration === this.name;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Renders a Lit component for component-level orchestration.
|
|
71
|
-
*
|
|
72
|
-
* SSR-eligible lazy scripts are preloaded first so custom elements registered
|
|
73
|
-
* by the component can render their server markup even when the Lit renderer is
|
|
74
|
-
* entered through cross-integration foreign-child handoff.
|
|
75
|
-
*
|
|
76
|
-
* Includes component-scoped dependency assets when declared.
|
|
77
|
-
*/
|
|
78
|
-
async renderComponent(input) {
|
|
79
|
-
await this.preloadSsrLazyScripts([input.component]);
|
|
80
|
-
if (!this.isFunctionComponent(input.component)) {
|
|
81
|
-
throw new TypeError("Lit renderer expected a callable component.");
|
|
142
|
+
/**
|
|
143
|
+
* Detects preload failures that are expected for browser-only modules.
|
|
144
|
+
*
|
|
145
|
+
* These errors are treated as non-fatal during SSR preload because some
|
|
146
|
+
* lazy client scripts intentionally depend on browser globals.
|
|
147
|
+
*/
|
|
148
|
+
isExpectedSsrPreloadError(error) {
|
|
149
|
+
return this.ssrLazyPreloader.isExpectedSsrPreloadError(error);
|
|
82
150
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Collects lazy script file paths eligible for SSR preloading.
|
|
153
|
+
*
|
|
154
|
+
* Only per-entry lazy script dependencies with `ssr: true` are collected.
|
|
155
|
+
* File-backed entries are required (`src` must be present);
|
|
156
|
+
* inline content lazy entries are intentionally skipped.
|
|
157
|
+
*/
|
|
158
|
+
collectSsrPreloadScripts(components) {
|
|
159
|
+
return this.ssrLazyPreloader.collectSsrPreloadScripts(components);
|
|
87
160
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Preloads SSR-eligible lazy scripts to register custom elements before render.
|
|
163
|
+
*/
|
|
164
|
+
async preloadSsrLazyScripts(components) {
|
|
165
|
+
const renderSession = this.getRenderSession?.();
|
|
166
|
+
if (renderSession) {
|
|
167
|
+
await renderSession.preloadSsrLazyScripts(components);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
await this.ssrLazyPreloader.preloadSsrLazyScripts(components);
|
|
94
171
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
const canResolveAssets = typeof this.assetProcessingService?.processDependencies === "function";
|
|
104
|
-
const assets = hasDependencies && canResolveAssets ? await this.processComponentDependencies([input.component]) : void 0;
|
|
105
|
-
return {
|
|
106
|
-
html: queuedForeignSubtreeResolution.html,
|
|
107
|
-
canAttachAttributes: true,
|
|
108
|
-
rootTag: this.getRootTagName(queuedForeignSubtreeResolution.html),
|
|
109
|
-
integrationName: this.name,
|
|
110
|
-
assets: this.htmlTransformer.dedupeProcessedAssets([
|
|
111
|
-
...assets ?? [],
|
|
112
|
-
...queuedForeignSubtreeResolution.assets ?? []
|
|
113
|
-
])
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
ssrLazyPreloader = new LitSsrLazyPreloader({
|
|
117
|
-
resolveDependencyPath: this.resolveDependencyPath.bind(this),
|
|
118
|
-
processDependencies: this.assetProcessingService?.processDependencies?.bind(this.assetProcessingService)
|
|
119
|
-
});
|
|
120
|
-
/**
|
|
121
|
-
* Detects preload failures that are expected for browser-only modules.
|
|
122
|
-
*
|
|
123
|
-
* These errors are treated as non-fatal during SSR preload because some
|
|
124
|
-
* lazy client scripts intentionally depend on browser globals.
|
|
125
|
-
*/
|
|
126
|
-
isExpectedSsrPreloadError(error) {
|
|
127
|
-
return this.ssrLazyPreloader.isExpectedSsrPreloadError(error);
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Collects lazy script file paths eligible for SSR preloading.
|
|
131
|
-
*
|
|
132
|
-
* Only per-entry lazy script dependencies with `ssr: true` are collected.
|
|
133
|
-
* File-backed entries are required (`src` must be present);
|
|
134
|
-
* inline content lazy entries are intentionally skipped.
|
|
135
|
-
*/
|
|
136
|
-
collectSsrPreloadScripts(components) {
|
|
137
|
-
return this.ssrLazyPreloader.collectSsrPreloadScripts(components);
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Preloads SSR-eligible lazy scripts to register custom elements before render.
|
|
141
|
-
*/
|
|
142
|
-
async preloadSsrLazyScripts(components) {
|
|
143
|
-
await this.ssrLazyPreloader.preloadSsrLazyScripts(components);
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Resolves the concrete JS entrypoint used for SSR preloading.
|
|
147
|
-
*
|
|
148
|
-
* Scripts are passed through the asset pipeline so preload imports can use
|
|
149
|
-
* the same processed output shape as runtime dependencies.
|
|
150
|
-
*/
|
|
151
|
-
async resolveSsrPreloadEntrypoint(scriptPath) {
|
|
152
|
-
return this.ssrLazyPreloader.resolveSsrPreloadEntrypoint(scriptPath);
|
|
153
|
-
}
|
|
154
|
-
async render({
|
|
155
|
-
params,
|
|
156
|
-
query,
|
|
157
|
-
props,
|
|
158
|
-
locals,
|
|
159
|
-
metadata,
|
|
160
|
-
Page,
|
|
161
|
-
Layout,
|
|
162
|
-
HtmlTemplate
|
|
163
|
-
}) {
|
|
164
|
-
try {
|
|
165
|
-
await this.preloadSsrLazyScripts([Page, Layout]);
|
|
166
|
-
return await this.renderPageWithDocumentShell({
|
|
167
|
-
page: {
|
|
168
|
-
component: Page,
|
|
169
|
-
props: {
|
|
170
|
-
params,
|
|
171
|
-
query,
|
|
172
|
-
...props,
|
|
173
|
-
locals
|
|
174
|
-
}
|
|
175
|
-
},
|
|
176
|
-
layout: Layout ? {
|
|
177
|
-
component: Layout,
|
|
178
|
-
props: locals ? { locals } : {}
|
|
179
|
-
} : void 0,
|
|
180
|
-
htmlTemplate: HtmlTemplate,
|
|
181
|
-
metadata,
|
|
182
|
-
pageProps: props || {},
|
|
183
|
-
transformDocumentHtml: normalizeLitHtml
|
|
184
|
-
});
|
|
185
|
-
} catch (error) {
|
|
186
|
-
throw this.createRenderError("Error rendering page", error);
|
|
172
|
+
/**
|
|
173
|
+
* Resolves the concrete JS entrypoint used for SSR preloading.
|
|
174
|
+
*
|
|
175
|
+
* Scripts are passed through the asset pipeline so preload imports can use
|
|
176
|
+
* the same processed output shape as runtime dependencies.
|
|
177
|
+
*/
|
|
178
|
+
async resolveSsrPreloadEntrypoint(scriptPath) {
|
|
179
|
+
return this.ssrLazyPreloader.resolveSsrPreloadEntrypoint(scriptPath);
|
|
187
180
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
181
|
+
async render({ params, query, props, locals, metadata, Page, Layout, HtmlTemplate, }) {
|
|
182
|
+
try {
|
|
183
|
+
await this.preloadSsrLazyScripts([Page, Layout]);
|
|
184
|
+
return await this.renderPageWithDocumentShell({
|
|
185
|
+
page: {
|
|
186
|
+
component: Page,
|
|
187
|
+
props: {
|
|
188
|
+
params,
|
|
189
|
+
query,
|
|
190
|
+
...props,
|
|
191
|
+
locals,
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
layout: Layout
|
|
195
|
+
? {
|
|
196
|
+
component: Layout,
|
|
197
|
+
props: locals ? { locals } : {},
|
|
198
|
+
}
|
|
199
|
+
: undefined,
|
|
200
|
+
htmlTemplate: HtmlTemplate,
|
|
201
|
+
metadata,
|
|
202
|
+
pageProps: props || {},
|
|
203
|
+
transformDocumentHtml: normalizeLitHtml,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
throw this.createRenderError('Error rendering page', error);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async renderToResponse(view, props, ctx) {
|
|
211
|
+
try {
|
|
212
|
+
const layouts = view.config?.layouts;
|
|
213
|
+
const Layout = layouts?.[layouts.length - 1];
|
|
214
|
+
await this.preloadSsrLazyScripts([view, Layout]);
|
|
215
|
+
return await this.renderViewWithDocumentShell({
|
|
216
|
+
view,
|
|
217
|
+
props,
|
|
218
|
+
ctx,
|
|
219
|
+
layout: Layout,
|
|
220
|
+
transformDocumentHtml: normalizeLitHtml,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
throw this.createRenderError('Error rendering view', error);
|
|
225
|
+
}
|
|
231
226
|
}
|
|
232
|
-
}
|
|
233
227
|
}
|
|
234
|
-
export {
|
|
235
|
-
LitRenderer
|
|
236
|
-
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type EcoComponent } from '@ecopages/core';
|
|
2
2
|
import type { AssetDefinition } from '@ecopages/core/services/asset-processing-service';
|
|
3
3
|
type ProcessDependencies = (dependencies: AssetDefinition[], integrationName: string) => Promise<Array<{
|
|
4
4
|
filepath?: string;
|