@ecopages/lit 0.2.0-beta.3 → 0.2.0-beta.31
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/lit-renderer.d.ts +27 -5
- package/src/lit-renderer.js +86 -76
- package/src/lit-ssr-lazy-preloader.js +2 -2
- package/src/lit-static-render-protocol.d.ts +35 -0
- package/src/lit-static-render-protocol.js +0 -0
- package/src/lit-static-render-session.d.ts +63 -0
- package/src/lit-static-render-session.js +105 -0
- package/src/lit-static-render-worker-client.d.ts +26 -0
- package/src/lit-static-render-worker-client.js +113 -0
- package/src/lit-static-render-worker.d.ts +1 -0
- package/src/lit-static-render-worker.js +75 -0
- package/src/lit.plugin.d.ts +14 -1
- package/src/lit.plugin.js +53 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecopages/lit",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.31",
|
|
4
4
|
"description": "Ecopages lit integration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ecopages",
|
|
@@ -20,18 +20,26 @@
|
|
|
20
20
|
"default": "./src/styled-mixin.js",
|
|
21
21
|
"types": "./src/styled-mixin.d.ts"
|
|
22
22
|
},
|
|
23
|
+
"./static-render-worker": {
|
|
24
|
+
"default": "./src/lit-static-render-worker.js",
|
|
25
|
+
"types": "./src/lit-static-render-worker.d.ts"
|
|
26
|
+
},
|
|
23
27
|
"./styled-mixin.ts": {
|
|
24
28
|
"default": "./src/styled-mixin.js",
|
|
25
29
|
"types": "./src/styled-mixin.d.ts"
|
|
30
|
+
},
|
|
31
|
+
"./static-render-worker.ts": {
|
|
32
|
+
"default": "./src/lit-static-render-worker.js",
|
|
33
|
+
"types": "./src/lit-static-render-worker.d.ts"
|
|
26
34
|
}
|
|
27
35
|
},
|
|
28
36
|
"repository": {
|
|
29
37
|
"type": "git",
|
|
30
|
-
"url": "https://github.com/ecopages/ecopages.git",
|
|
38
|
+
"url": "git+https://github.com/ecopages/ecopages.git",
|
|
31
39
|
"directory": "packages/integrations/lit"
|
|
32
40
|
},
|
|
33
41
|
"peerDependencies": {
|
|
34
|
-
"@ecopages/core": "0.2.0-beta.
|
|
42
|
+
"@ecopages/core": "0.2.0-beta.31",
|
|
35
43
|
"@lit-labs/ssr": "^3.3.0",
|
|
36
44
|
"@lit-labs/ssr-client": "^1.1.7",
|
|
37
45
|
"lit": "^3.2.1"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Lit static render worker
|
|
2
|
+
|
|
3
|
+
Lit-first page routes (`.lit.tsx`) render through a dedicated worker thread during static export and runtime when the Lit plugin is active.
|
|
4
|
+
|
|
5
|
+
## Flow
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Main thread Worker thread
|
|
9
|
+
─────────── ─────────────
|
|
10
|
+
LitPlugin.setup()
|
|
11
|
+
└─ LitStaticRenderSession.ensureWorker()
|
|
12
|
+
└─ LitStaticRenderWorkerClient.start()
|
|
13
|
+
└─ postMessage(init) ──► setupAppRuntimePlugins()
|
|
14
|
+
installBuildRuntime()
|
|
15
|
+
RouteRendererFactory
|
|
16
|
+
|
|
17
|
+
LitPlugin.initializeRenderer()
|
|
18
|
+
└─ LitRenderer({ getRenderSession }) lazy accessor (not constructor snapshot)
|
|
19
|
+
|
|
20
|
+
LitRenderer.execute() (main)
|
|
21
|
+
└─ ensureIntegrationRuntimeActivated()
|
|
22
|
+
└─ getRenderSession()
|
|
23
|
+
├─ locals present ──────────► super.execute() (in-process)
|
|
24
|
+
└─ session.renderPageInWorker()
|
|
25
|
+
└─ postMessage(render) ─────► RouteRendererFactory.execute()
|
|
26
|
+
(no active session in worker)
|
|
27
|
+
|
|
28
|
+
beforeStaticExport
|
|
29
|
+
└─ ensureWorker() only when `renderSession` is still null
|
|
30
|
+
└─ preloadStaticRoutes()
|
|
31
|
+
└─ preload SSR lazy scripts for Lit pages
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`ECOPAGES_LIT_STATIC_RENDER_WORKER=true` in the worker environment prevents nested worker creation during `LitPlugin.setup()` and skips processor `.setup()` inside `setupAppRuntimePlugins` (main thread already prepared artifacts).
|
|
35
|
+
|
|
36
|
+
## Ownership
|
|
37
|
+
|
|
38
|
+
| Concern | Owner |
|
|
39
|
+
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
40
|
+
| Session + worker lifecycle | `LitPlugin` (`renderSession`) |
|
|
41
|
+
| Lazy session lookup | `LitRenderer` via `getRenderSession` |
|
|
42
|
+
| Worker identity | First `{ configModulePath, runtimeOrigin }` passed to `ensureWorker`; same identity is sticky; a later `ensureWorker` with a different identity disposes and recreates |
|
|
43
|
+
|
|
44
|
+
Renderers created before plugin `setup()` still use the worker after setup assigns the session, because `execute` activates the integration runtime then reads the accessor.
|
|
45
|
+
|
|
46
|
+
`beforeStaticExport` does not re-call `ensureWorker` when setup already created the session, so a different `context.baseUrl` at export time will not recreate an existing worker.
|
|
47
|
+
|
|
48
|
+
## Locals policy
|
|
49
|
+
|
|
50
|
+
When `RouteRendererOptions.locals` is present, `LitRenderer.execute` forces the in-process path (`super.execute`). Request locals are not sent through the worker protocol (structured-clone risk and request-scoped data). Static export and routes without locals continue through the worker.
|
|
51
|
+
|
|
52
|
+
## Files
|
|
53
|
+
|
|
54
|
+
| File | Role |
|
|
55
|
+
| ------------------------------------ | --------------------------------------------------------------- |
|
|
56
|
+
| `lit-static-render-session.ts` | Plugin-runtime-scoped session: worker lifecycle + SSR preload |
|
|
57
|
+
| `lit-static-render-worker-client.ts` | Message protocol client |
|
|
58
|
+
| `lit-static-render-worker.ts` | Worker entry (exported as `@ecopages/lit/static-render-worker`) |
|
|
59
|
+
| `lit-static-render-protocol.ts` | Request/response message types |
|
|
60
|
+
|
|
61
|
+
## Protocol
|
|
62
|
+
|
|
63
|
+
| Message | Direction | Purpose |
|
|
64
|
+
| ------------------ | ------------- | ----------------------------------------------------------------------------- |
|
|
65
|
+
| `init` | main → worker | Load `eco.config.ts`, bootstrap runtime (`configModulePath`, `runtimeOrigin`) |
|
|
66
|
+
| `ready` | worker → main | Worker initialized |
|
|
67
|
+
| `render` | main → worker | Render one Lit page route (`filePath`, `PageParams`, optional `PageQuery`) |
|
|
68
|
+
| `result` / `error` | worker → main | HTML body plus optional `cacheStrategy`, or an error message |
|
|
69
|
+
| `shutdown` | main → worker | Exit worker thread |
|
package/src/lit-renderer.d.ts
CHANGED
|
@@ -2,19 +2,41 @@
|
|
|
2
2
|
* This module contains the Lit renderer
|
|
3
3
|
* @module
|
|
4
4
|
*/
|
|
5
|
-
import type { ComponentRenderInput, ComponentRenderResult, EcoComponent, EcoPagesElement, IntegrationRendererRenderOptions, RouteRendererBody } from '@ecopages/core';
|
|
5
|
+
import type { ComponentRenderInput, ComponentRenderResult, EcoComponent, EcoPagesElement, IntegrationRendererRenderOptions, RouteRenderResult, RouteRendererBody, RouteRendererOptions } from '@ecopages/core';
|
|
6
6
|
import './dom-shim.js';
|
|
7
|
-
import { IntegrationRenderer, type RenderToResponseContext } from '@ecopages/core/route-renderer/integration-renderer';
|
|
7
|
+
import { IntegrationRenderer, type RenderToResponseContext } from '@ecopages/core/route-renderer/orchestration/integration-renderer';
|
|
8
|
+
import type { LitStaticRenderSession } from './lit-static-render-session.js';
|
|
9
|
+
export type LitRendererOptions = ConstructorParameters<typeof IntegrationRenderer>[0] & {
|
|
10
|
+
/**
|
|
11
|
+
* Lazy session lookup owned by {@link LitPlugin}.
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* Must not snapshot the session at construction time. Plugin `setup()` may
|
|
15
|
+
* assign the session after the renderer is created; `execute` activates the
|
|
16
|
+
* integration runtime then reads this accessor so worker dispatch still works.
|
|
17
|
+
*/
|
|
18
|
+
getRenderSession?: () => LitStaticRenderSession | undefined;
|
|
19
|
+
};
|
|
8
20
|
/**
|
|
9
21
|
* A renderer for the Lit integration.
|
|
10
22
|
*/
|
|
11
23
|
export declare class LitRenderer extends IntegrationRenderer<EcoPagesElement> {
|
|
12
24
|
name: string;
|
|
25
|
+
private readonly getRenderSession?;
|
|
26
|
+
constructor(options: LitRendererOptions);
|
|
27
|
+
/**
|
|
28
|
+
* Renders a Lit page route, preferring the plugin worker when available.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* Activates the integration runtime before reading {@link getRenderSession}
|
|
32
|
+
* so a renderer constructed before plugin setup still sees the session.
|
|
33
|
+
* When `options.locals` is present, rendering stays in-process: request locals
|
|
34
|
+
* are not structured-cloned across the worker boundary.
|
|
35
|
+
*/
|
|
36
|
+
execute(options: RouteRendererOptions): Promise<RouteRenderResult>;
|
|
13
37
|
private isFunctionComponent;
|
|
14
38
|
private resolveQueuedForeignSubtreeChildren;
|
|
15
|
-
private
|
|
16
|
-
protected shouldRenderPageComponent(): boolean;
|
|
17
|
-
private isLitManagedComponent;
|
|
39
|
+
private renderLitQueuedForeignSubtreeChildren;
|
|
18
40
|
/**
|
|
19
41
|
* Renders a Lit component for component-level orchestration.
|
|
20
42
|
*
|
package/src/lit-renderer.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import "./dom-shim.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
IntegrationRenderer
|
|
4
|
+
} from "@ecopages/core/route-renderer/orchestration/integration-renderer";
|
|
5
|
+
import {
|
|
6
|
+
getForeignSubtreeResolutionContextKey,
|
|
7
|
+
resolveOwningIntegrationRenderer
|
|
8
|
+
} from "@ecopages/core/route-renderer/orchestration/foreign-child/owning-renderer-resolution";
|
|
3
9
|
import { LitSsrLazyPreloader } from "./lit-ssr-lazy-preloader.js";
|
|
4
10
|
import { LIT_PLUGIN_NAME } from "./lit.constants.js";
|
|
5
11
|
import {
|
|
@@ -10,6 +16,38 @@ import {
|
|
|
10
16
|
} from "./utils/lit-html-rendering.js";
|
|
11
17
|
class LitRenderer extends IntegrationRenderer {
|
|
12
18
|
name = LIT_PLUGIN_NAME;
|
|
19
|
+
getRenderSession;
|
|
20
|
+
constructor(options) {
|
|
21
|
+
const { getRenderSession, ...rendererOptions } = options;
|
|
22
|
+
super(rendererOptions);
|
|
23
|
+
this.getRenderSession = getRenderSession;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Renders a Lit page route, preferring the plugin worker when available.
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
29
|
+
* Activates the integration runtime before reading {@link getRenderSession}
|
|
30
|
+
* so a renderer constructed before plugin setup still sees the session.
|
|
31
|
+
* When `options.locals` is present, rendering stays in-process: request locals
|
|
32
|
+
* are not structured-cloned across the worker boundary.
|
|
33
|
+
*/
|
|
34
|
+
async execute(options) {
|
|
35
|
+
await this.ensureIntegrationRuntimeActivated();
|
|
36
|
+
const renderSession = this.getRenderSession?.();
|
|
37
|
+
if (renderSession && options.locals === void 0) {
|
|
38
|
+
const params = options.params ?? {};
|
|
39
|
+
const result = await renderSession.renderPageInWorker({
|
|
40
|
+
filePath: options.file,
|
|
41
|
+
params,
|
|
42
|
+
query: options.query
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
body: result.html,
|
|
46
|
+
cacheStrategy: result.cacheStrategy
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return super.execute(options);
|
|
50
|
+
}
|
|
13
51
|
isFunctionComponent(component) {
|
|
14
52
|
return typeof component === "function";
|
|
15
53
|
}
|
|
@@ -28,44 +66,23 @@ class LitRenderer extends IntegrationRenderer {
|
|
|
28
66
|
);
|
|
29
67
|
return renderedChildren;
|
|
30
68
|
}
|
|
31
|
-
async
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
queuedResolutionsByToken,
|
|
44
|
-
resolveToken
|
|
45
|
-
);
|
|
46
|
-
if (typeof renderedChildren !== "string") {
|
|
47
|
-
return {
|
|
48
|
-
assets: [],
|
|
49
|
-
children: renderedChildren
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
assets: [],
|
|
54
|
-
html: renderedChildren
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
});
|
|
69
|
+
async renderLitQueuedForeignSubtreeChildren(children, queuedResolutionsByToken, resolveToken) {
|
|
70
|
+
const renderedChildren = await this.resolveQueuedForeignSubtreeChildren(
|
|
71
|
+
children,
|
|
72
|
+
queuedResolutionsByToken,
|
|
73
|
+
resolveToken
|
|
74
|
+
);
|
|
75
|
+
if (typeof renderedChildren !== "string") {
|
|
76
|
+
return {
|
|
77
|
+
assets: [],
|
|
78
|
+
children: renderedChildren
|
|
79
|
+
};
|
|
80
|
+
}
|
|
58
81
|
return {
|
|
59
|
-
|
|
60
|
-
|
|
82
|
+
assets: [],
|
|
83
|
+
html: renderedChildren
|
|
61
84
|
};
|
|
62
85
|
}
|
|
63
|
-
shouldRenderPageComponent() {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
isLitManagedComponent(component) {
|
|
67
|
-
return component?.config?.integration === this.name || component?.config?.__eco?.integration === this.name;
|
|
68
|
-
}
|
|
69
86
|
/**
|
|
70
87
|
* Renders a Lit component for component-level orchestration.
|
|
71
88
|
*
|
|
@@ -95,10 +112,26 @@ class LitRenderer extends IntegrationRenderer {
|
|
|
95
112
|
const content = await component(props);
|
|
96
113
|
const renderedHtml = await renderLitValueToString(content);
|
|
97
114
|
const html = renderedChildren === void 0 ? renderedHtml : injectLitRenderedChildren(renderedHtml, renderedChildren);
|
|
98
|
-
const queuedForeignSubtreeResolution = await this.
|
|
115
|
+
const queuedForeignSubtreeResolution = await this.foreignSubtreeExecutionService.resolveQueuedHtml({
|
|
116
|
+
currentIntegrationName: this.name,
|
|
99
117
|
html,
|
|
100
|
-
this.
|
|
101
|
-
|
|
118
|
+
runtimeContext: this.foreignSubtreeExecutionService.getQueuedRuntimeContext(
|
|
119
|
+
input,
|
|
120
|
+
getForeignSubtreeResolutionContextKey(this.name)
|
|
121
|
+
),
|
|
122
|
+
queueLabel: "Lit",
|
|
123
|
+
getOwningRenderer: (integrationName, rendererCache) => resolveOwningIntegrationRenderer({
|
|
124
|
+
appConfig: this.appConfig,
|
|
125
|
+
runtimeOrigin: this.runtimeOrigin,
|
|
126
|
+
currentIntegrationName: this.name,
|
|
127
|
+
currentRenderer: this,
|
|
128
|
+
integrationName,
|
|
129
|
+
cache: rendererCache
|
|
130
|
+
}),
|
|
131
|
+
applyAttributesToFirstElement: (resolvedHtml, attributes) => this.htmlTransformer.applyAttributesToFirstElement(resolvedHtml, attributes),
|
|
132
|
+
dedupeProcessedAssets: (assets2) => this.htmlTransformer.dedupeProcessedAssets(assets2),
|
|
133
|
+
renderQueuedChildren: (children, _runtimeContext, queuedResolutionsByToken, resolveToken) => this.renderLitQueuedForeignSubtreeChildren(children, queuedResolutionsByToken, resolveToken)
|
|
134
|
+
});
|
|
102
135
|
const hasDependencies = Boolean(input.component.config?.dependencies);
|
|
103
136
|
const canResolveAssets = typeof this.assetProcessingService?.processDependencies === "function";
|
|
104
137
|
const assets = hasDependencies && canResolveAssets ? await this.processComponentDependencies([input.component]) : void 0;
|
|
@@ -109,7 +142,7 @@ class LitRenderer extends IntegrationRenderer {
|
|
|
109
142
|
integrationName: this.name,
|
|
110
143
|
assets: this.htmlTransformer.dedupeProcessedAssets([
|
|
111
144
|
...assets ?? [],
|
|
112
|
-
...queuedForeignSubtreeResolution.assets
|
|
145
|
+
...queuedForeignSubtreeResolution.assets
|
|
113
146
|
])
|
|
114
147
|
};
|
|
115
148
|
}
|
|
@@ -140,6 +173,11 @@ class LitRenderer extends IntegrationRenderer {
|
|
|
140
173
|
* Preloads SSR-eligible lazy scripts to register custom elements before render.
|
|
141
174
|
*/
|
|
142
175
|
async preloadSsrLazyScripts(components) {
|
|
176
|
+
const renderSession = this.getRenderSession?.();
|
|
177
|
+
if (renderSession) {
|
|
178
|
+
await renderSession.preloadSsrLazyScripts(components);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
143
181
|
await this.ssrLazyPreloader.preloadSsrLazyScripts(components);
|
|
144
182
|
}
|
|
145
183
|
/**
|
|
@@ -188,44 +226,16 @@ class LitRenderer extends IntegrationRenderer {
|
|
|
188
226
|
}
|
|
189
227
|
async renderToResponse(view, props, ctx) {
|
|
190
228
|
try {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
view,
|
|
194
|
-
props,
|
|
195
|
-
ctx,
|
|
196
|
-
transformHtml: normalizeLitHtml
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
const viewConfig = view.config;
|
|
200
|
-
const Layout = viewConfig?.layout;
|
|
201
|
-
const HtmlTemplate = await this.getHtmlTemplate();
|
|
202
|
-
const metadata = await this.resolveViewMetadata(view, props);
|
|
203
|
-
const normalizedProps = props ?? {};
|
|
229
|
+
const layouts = view.config?.layouts;
|
|
230
|
+
const Layout = layouts?.[layouts.length - 1];
|
|
204
231
|
await this.preloadSsrLazyScripts([view, Layout]);
|
|
205
|
-
await this.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
component: Layout,
|
|
212
|
-
props: {},
|
|
213
|
-
children: pageRender.html
|
|
214
|
-
}) : void 0;
|
|
215
|
-
const documentRender = await this.renderComponentWithForeignChildren({
|
|
216
|
-
component: HtmlTemplate,
|
|
217
|
-
props: {
|
|
218
|
-
metadata,
|
|
219
|
-
pageProps: normalizedProps
|
|
220
|
-
},
|
|
221
|
-
children: layoutRender?.html ?? pageRender.html
|
|
222
|
-
});
|
|
223
|
-
this.appendProcessedDependencies(pageRender.assets, layoutRender?.assets, documentRender.assets);
|
|
224
|
-
const body = await this.finalizeResolvedHtml({
|
|
225
|
-
html: `${this.DOC_TYPE}${normalizeLitHtml(documentRender.html)}`,
|
|
226
|
-
partial: false
|
|
232
|
+
return await this.renderViewWithDocumentShell({
|
|
233
|
+
view,
|
|
234
|
+
props,
|
|
235
|
+
ctx,
|
|
236
|
+
layout: Layout,
|
|
237
|
+
transformDocumentHtml: normalizeLitHtml
|
|
227
238
|
});
|
|
228
|
-
return this.createHtmlResponse(body, ctx);
|
|
229
239
|
} catch (error) {
|
|
230
240
|
throw this.createRenderError("Error rendering view", error);
|
|
231
241
|
}
|
|
@@ -60,8 +60,8 @@ class LitSsrLazyPreloader {
|
|
|
60
60
|
scriptPaths.add(this.resolveDependencyPath(componentDir, script.src));
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
|
|
64
|
-
collect(
|
|
63
|
+
for (const layout of config.layouts ?? []) {
|
|
64
|
+
collect(layout);
|
|
65
65
|
}
|
|
66
66
|
for (const nestedComponent of config.dependencies?.components || []) {
|
|
67
67
|
collect(nestedComponent);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { PageParams, PageQuery, RouteRenderResult } from '@ecopages/core';
|
|
2
|
+
export type LitStaticRenderCacheStrategy = RouteRenderResult['cacheStrategy'];
|
|
3
|
+
export type LitStaticRenderWorkerInitMessage = {
|
|
4
|
+
type: 'init';
|
|
5
|
+
configModulePath: string;
|
|
6
|
+
runtimeOrigin: string;
|
|
7
|
+
};
|
|
8
|
+
export type LitStaticRenderWorkerRenderMessage = {
|
|
9
|
+
type: 'render';
|
|
10
|
+
id: string;
|
|
11
|
+
filePath: string;
|
|
12
|
+
/** Route params; multi-value keys stay as `string[]`. */
|
|
13
|
+
params: PageParams;
|
|
14
|
+
/** Serializable route query; preserves multi-value keys as `string[]`. */
|
|
15
|
+
query?: PageQuery;
|
|
16
|
+
};
|
|
17
|
+
export type LitStaticRenderWorkerShutdownMessage = {
|
|
18
|
+
type: 'shutdown';
|
|
19
|
+
};
|
|
20
|
+
export type LitStaticRenderWorkerRequestMessage = LitStaticRenderWorkerInitMessage | LitStaticRenderWorkerRenderMessage | LitStaticRenderWorkerShutdownMessage;
|
|
21
|
+
export type LitStaticRenderWorkerReadyMessage = {
|
|
22
|
+
type: 'ready';
|
|
23
|
+
};
|
|
24
|
+
export type LitStaticRenderWorkerResultMessage = {
|
|
25
|
+
type: 'result';
|
|
26
|
+
id: string;
|
|
27
|
+
html: string;
|
|
28
|
+
cacheStrategy?: LitStaticRenderCacheStrategy;
|
|
29
|
+
};
|
|
30
|
+
export type LitStaticRenderWorkerErrorMessage = {
|
|
31
|
+
type: 'error';
|
|
32
|
+
id?: string;
|
|
33
|
+
message: string;
|
|
34
|
+
};
|
|
35
|
+
export type LitStaticRenderWorkerResponseMessage = LitStaticRenderWorkerReadyMessage | LitStaticRenderWorkerResultMessage | LitStaticRenderWorkerErrorMessage;
|
|
File without changes
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { EcoComponent, PageParams, PageQuery } from '@ecopages/core';
|
|
2
|
+
import type { StaticExportContext } from '@ecopages/core/plugins/integration-plugin';
|
|
3
|
+
import type { AssetDefinition } from '@ecopages/core/services/asset-processing-service';
|
|
4
|
+
import { LitStaticRenderWorkerClient } from './lit-static-render-worker-client.js';
|
|
5
|
+
import type { LitStaticRenderCacheStrategy } from './lit-static-render-protocol.js';
|
|
6
|
+
type LitStaticRenderWorkerClientContract = Pick<LitStaticRenderWorkerClient, 'start' | 'renderPage' | 'dispose'>;
|
|
7
|
+
type LitStaticRenderSessionOptions = {
|
|
8
|
+
resolveDependencyPath: (componentDir: string, sourcePath: string) => string;
|
|
9
|
+
processDependencies?: (dependencies: AssetDefinition[], integrationName: string) => Promise<Array<{
|
|
10
|
+
filepath?: string;
|
|
11
|
+
}>>;
|
|
12
|
+
preferSourceImports?: boolean;
|
|
13
|
+
createWorkerClient?: (input: {
|
|
14
|
+
configModulePath: string;
|
|
15
|
+
runtimeOrigin: string;
|
|
16
|
+
}) => LitStaticRenderWorkerClientContract;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Plugin-runtime-scoped Lit server render session.
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* Owns worker lifecycle and SSR preload for Lit page routes during static export
|
|
23
|
+
* and runtime/dev requests. Injected into `LitRenderer` via a lazy accessor from
|
|
24
|
+
* `LitPlugin`; there is no process-global coordinator.
|
|
25
|
+
*
|
|
26
|
+
* Worker init identity is sticky for the first `{ configModulePath, runtimeOrigin }`.
|
|
27
|
+
* Later `ensureWorker` calls with the same identity are no-ops; a different identity
|
|
28
|
+
* recreates the worker so stale config/origin is never silently reused.
|
|
29
|
+
*/
|
|
30
|
+
export declare class LitStaticRenderSession {
|
|
31
|
+
private readonly preloader;
|
|
32
|
+
private readonly createWorkerClient;
|
|
33
|
+
private workerClient;
|
|
34
|
+
private workerIdentity;
|
|
35
|
+
constructor(options: LitStaticRenderSessionOptions);
|
|
36
|
+
/**
|
|
37
|
+
* Ensures the static render worker is running for the given identity.
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* First successful start sticks that identity. Matching re-entry is a no-op.
|
|
41
|
+
* A different `configModulePath` or `runtimeOrigin` disposes and recreates.
|
|
42
|
+
*/
|
|
43
|
+
ensureWorker(input: {
|
|
44
|
+
configModulePath: string;
|
|
45
|
+
runtimeOrigin: string;
|
|
46
|
+
}): Promise<void>;
|
|
47
|
+
preloadStaticRoutes(context: StaticExportContext): Promise<void>;
|
|
48
|
+
preloadSsrLazyScripts(components: Array<EcoComponent | undefined>): Promise<void>;
|
|
49
|
+
collectSsrPreloadScripts(components: Array<EcoComponent | undefined>): string[];
|
|
50
|
+
renderPageInWorker(input: {
|
|
51
|
+
filePath: string;
|
|
52
|
+
params: PageParams;
|
|
53
|
+
query?: PageQuery;
|
|
54
|
+
}): Promise<{
|
|
55
|
+
html: string;
|
|
56
|
+
cacheStrategy?: LitStaticRenderCacheStrategy;
|
|
57
|
+
}>;
|
|
58
|
+
isExpectedSsrPreloadError(error: unknown): boolean;
|
|
59
|
+
dispose(): Promise<void>;
|
|
60
|
+
private preloadLitRoutes;
|
|
61
|
+
private isLitRoute;
|
|
62
|
+
}
|
|
63
|
+
export {};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { LitSsrLazyPreloader } from "./lit-ssr-lazy-preloader.js";
|
|
3
|
+
import { LIT_PLUGIN_NAME } from "./lit.constants.js";
|
|
4
|
+
import { LitStaticRenderWorkerClient } from "./lit-static-render-worker-client.js";
|
|
5
|
+
class LitStaticRenderSession {
|
|
6
|
+
preloader;
|
|
7
|
+
createWorkerClient;
|
|
8
|
+
workerClient = null;
|
|
9
|
+
workerIdentity = null;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.preloader = new LitSsrLazyPreloader({
|
|
12
|
+
resolveDependencyPath: options.resolveDependencyPath,
|
|
13
|
+
processDependencies: options.processDependencies,
|
|
14
|
+
preferSourceImports: options.preferSourceImports
|
|
15
|
+
});
|
|
16
|
+
this.createWorkerClient = options.createWorkerClient ?? ((input) => new LitStaticRenderWorkerClient({
|
|
17
|
+
configModulePath: input.configModulePath,
|
|
18
|
+
runtimeOrigin: input.runtimeOrigin
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Ensures the static render worker is running for the given identity.
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* First successful start sticks that identity. Matching re-entry is a no-op.
|
|
26
|
+
* A different `configModulePath` or `runtimeOrigin` disposes and recreates.
|
|
27
|
+
*/
|
|
28
|
+
async ensureWorker(input) {
|
|
29
|
+
if (this.workerClient && this.workerIdentity && this.workerIdentity.configModulePath === input.configModulePath && this.workerIdentity.runtimeOrigin === input.runtimeOrigin) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (this.workerClient) {
|
|
33
|
+
await this.dispose();
|
|
34
|
+
}
|
|
35
|
+
this.workerIdentity = {
|
|
36
|
+
configModulePath: input.configModulePath,
|
|
37
|
+
runtimeOrigin: input.runtimeOrigin
|
|
38
|
+
};
|
|
39
|
+
this.workerClient = this.createWorkerClient({
|
|
40
|
+
configModulePath: input.configModulePath,
|
|
41
|
+
runtimeOrigin: input.runtimeOrigin
|
|
42
|
+
});
|
|
43
|
+
await this.workerClient.start();
|
|
44
|
+
}
|
|
45
|
+
async preloadStaticRoutes(context) {
|
|
46
|
+
await this.preloadLitRoutes(context);
|
|
47
|
+
}
|
|
48
|
+
async preloadSsrLazyScripts(components) {
|
|
49
|
+
await this.preloader.preloadSsrLazyScripts(components);
|
|
50
|
+
}
|
|
51
|
+
collectSsrPreloadScripts(components) {
|
|
52
|
+
return this.preloader.collectSsrPreloadScripts(components);
|
|
53
|
+
}
|
|
54
|
+
async renderPageInWorker(input) {
|
|
55
|
+
if (!this.workerClient) {
|
|
56
|
+
throw new Error("Lit static render worker is not active");
|
|
57
|
+
}
|
|
58
|
+
return this.workerClient.renderPage(input);
|
|
59
|
+
}
|
|
60
|
+
isExpectedSsrPreloadError(error) {
|
|
61
|
+
return this.preloader.isExpectedSsrPreloadError(error);
|
|
62
|
+
}
|
|
63
|
+
async dispose() {
|
|
64
|
+
await this.workerClient?.dispose();
|
|
65
|
+
this.workerClient = null;
|
|
66
|
+
this.workerIdentity = null;
|
|
67
|
+
}
|
|
68
|
+
async preloadLitRoutes(context) {
|
|
69
|
+
if (!context.routeRendererFactory) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const routes = await context.router.listStaticGenerationRoutes({ runtimeOrigin: context.baseUrl });
|
|
73
|
+
const components = [];
|
|
74
|
+
for (const route of routes) {
|
|
75
|
+
const filePath = route.templateRoute.filePath;
|
|
76
|
+
if (!this.isLitRoute(filePath, context.appConfig)) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const pageRenderer = context.routeRendererFactory.getPageRenderer(filePath);
|
|
80
|
+
const pageModule = await pageRenderer.loadPageModule(filePath);
|
|
81
|
+
const page = pageModule.default;
|
|
82
|
+
if (page) {
|
|
83
|
+
components.push(page);
|
|
84
|
+
for (const layout of page.config?.layouts ?? []) {
|
|
85
|
+
components.push(layout);
|
|
86
|
+
}
|
|
87
|
+
for (const nestedComponent of page.config?.dependencies?.components ?? []) {
|
|
88
|
+
components.push(nestedComponent);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
await this.preloadSsrLazyScripts(components);
|
|
93
|
+
}
|
|
94
|
+
isLitRoute(filePath, appConfig) {
|
|
95
|
+
const litIntegration = appConfig.integrations.find((plugin) => plugin.name === LIT_PLUGIN_NAME);
|
|
96
|
+
if (!litIntegration) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const normalizedPath = path.normalize(filePath);
|
|
100
|
+
return litIntegration.extensions.some((extension) => normalizedPath.endsWith(extension));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export {
|
|
104
|
+
LitStaticRenderSession
|
|
105
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { PageParams, PageQuery } from '@ecopages/core';
|
|
2
|
+
import type { LitStaticRenderCacheStrategy } from './lit-static-render-protocol.js';
|
|
3
|
+
export declare class LitStaticRenderWorkerClient {
|
|
4
|
+
private readonly configModulePath;
|
|
5
|
+
private readonly runtimeOrigin;
|
|
6
|
+
private worker;
|
|
7
|
+
private readyPromise;
|
|
8
|
+
private pending;
|
|
9
|
+
private nextRenderId;
|
|
10
|
+
constructor({ configModulePath, runtimeOrigin }: {
|
|
11
|
+
configModulePath: string;
|
|
12
|
+
runtimeOrigin: string;
|
|
13
|
+
});
|
|
14
|
+
private rejectPending;
|
|
15
|
+
start(): Promise<void>;
|
|
16
|
+
renderPage(input: {
|
|
17
|
+
filePath: string;
|
|
18
|
+
params: PageParams;
|
|
19
|
+
query?: PageQuery;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
html: string;
|
|
22
|
+
cacheStrategy?: LitStaticRenderCacheStrategy;
|
|
23
|
+
}>;
|
|
24
|
+
dispose(): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export declare const litStaticRenderWorkerEntryPath: string;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Worker } from "node:worker_threads";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
class LitStaticRenderWorkerClient {
|
|
4
|
+
configModulePath;
|
|
5
|
+
runtimeOrigin;
|
|
6
|
+
worker = null;
|
|
7
|
+
readyPromise = null;
|
|
8
|
+
pending = /* @__PURE__ */ new Map();
|
|
9
|
+
nextRenderId = 0;
|
|
10
|
+
constructor({ configModulePath, runtimeOrigin }) {
|
|
11
|
+
this.configModulePath = configModulePath;
|
|
12
|
+
this.runtimeOrigin = runtimeOrigin;
|
|
13
|
+
}
|
|
14
|
+
rejectPending(error) {
|
|
15
|
+
for (const pending of this.pending.values()) {
|
|
16
|
+
pending.reject(error);
|
|
17
|
+
}
|
|
18
|
+
this.pending.clear();
|
|
19
|
+
}
|
|
20
|
+
async start() {
|
|
21
|
+
if (this.readyPromise) {
|
|
22
|
+
return this.readyPromise;
|
|
23
|
+
}
|
|
24
|
+
this.readyPromise = new Promise((resolveReady, rejectReady) => {
|
|
25
|
+
const worker = new Worker(new URL("./lit-static-render-worker.js", import.meta.url), {
|
|
26
|
+
execArgv: [...process.execArgv],
|
|
27
|
+
env: {
|
|
28
|
+
...process.env,
|
|
29
|
+
ECOPAGES_LIT_STATIC_RENDER_WORKER: "true"
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
this.worker = worker;
|
|
33
|
+
worker.on("message", (message) => {
|
|
34
|
+
if (message.type === "ready") {
|
|
35
|
+
resolveReady();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (message.type === "error") {
|
|
39
|
+
if (message.id && this.pending.has(message.id)) {
|
|
40
|
+
const pending = this.pending.get(message.id);
|
|
41
|
+
this.pending.delete(message.id);
|
|
42
|
+
pending.reject(new Error(message.message));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
rejectReady(new Error(message.message));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (message.type === "result") {
|
|
49
|
+
const pending = this.pending.get(message.id);
|
|
50
|
+
if (!pending) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
this.pending.delete(message.id);
|
|
54
|
+
pending.resolve({ html: message.html, cacheStrategy: message.cacheStrategy });
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
worker.on("error", (error) => {
|
|
58
|
+
rejectReady(error instanceof Error ? error : new Error(String(error)));
|
|
59
|
+
});
|
|
60
|
+
worker.on("exit", (code) => {
|
|
61
|
+
const exitError = new Error(
|
|
62
|
+
code === 0 ? "Lit static render worker exited" : `Lit static render worker exited with code ${code}`
|
|
63
|
+
);
|
|
64
|
+
this.rejectPending(exitError);
|
|
65
|
+
if (code !== 0) {
|
|
66
|
+
rejectReady(exitError);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const initMessage = {
|
|
70
|
+
type: "init",
|
|
71
|
+
configModulePath: this.configModulePath,
|
|
72
|
+
runtimeOrigin: this.runtimeOrigin
|
|
73
|
+
};
|
|
74
|
+
worker.postMessage(initMessage);
|
|
75
|
+
});
|
|
76
|
+
return this.readyPromise;
|
|
77
|
+
}
|
|
78
|
+
async renderPage(input) {
|
|
79
|
+
await this.start();
|
|
80
|
+
const worker = this.worker;
|
|
81
|
+
if (!worker) {
|
|
82
|
+
throw new Error("Lit static render worker is not available");
|
|
83
|
+
}
|
|
84
|
+
const id = String(++this.nextRenderId);
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
this.pending.set(id, { resolve, reject });
|
|
87
|
+
const renderMessage = {
|
|
88
|
+
type: "render",
|
|
89
|
+
id,
|
|
90
|
+
filePath: input.filePath,
|
|
91
|
+
params: input.params,
|
|
92
|
+
query: input.query
|
|
93
|
+
};
|
|
94
|
+
worker.postMessage(renderMessage);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async dispose() {
|
|
98
|
+
if (!this.worker) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
this.rejectPending(new Error("Lit static render worker was disposed"));
|
|
102
|
+
const worker = this.worker;
|
|
103
|
+
this.worker = null;
|
|
104
|
+
this.readyPromise = null;
|
|
105
|
+
worker.postMessage({ type: "shutdown" });
|
|
106
|
+
await worker.terminate();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const litStaticRenderWorkerEntryPath = fileURLToPath(new URL("./lit-static-render-worker.js", import.meta.url));
|
|
110
|
+
export {
|
|
111
|
+
LitStaticRenderWorkerClient,
|
|
112
|
+
litStaticRenderWorkerEntryPath
|
|
113
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './dom-shim.js';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { parentPort } from "node:worker_threads";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import { setupAppRuntimePlugins } from "@ecopages/core/build/build-adapter";
|
|
4
|
+
import { installBuildRuntime } from "@ecopages/core/build/build-runtime";
|
|
5
|
+
import { RouteRendererFactory } from "@ecopages/core/route-renderer/route-renderer";
|
|
6
|
+
import "./dom-shim.js";
|
|
7
|
+
let appConfig = null;
|
|
8
|
+
let routeRendererFactory = null;
|
|
9
|
+
async function initializeWorker(configModulePath, runtimeOrigin) {
|
|
10
|
+
const configModule = await import(
|
|
11
|
+
/* @vite-ignore */
|
|
12
|
+
pathToFileURL(configModulePath).href
|
|
13
|
+
);
|
|
14
|
+
appConfig = configModule.default ?? configModule;
|
|
15
|
+
await setupAppRuntimePlugins({
|
|
16
|
+
appConfig,
|
|
17
|
+
runtimeOrigin
|
|
18
|
+
});
|
|
19
|
+
installBuildRuntime(appConfig);
|
|
20
|
+
routeRendererFactory = new RouteRendererFactory({
|
|
21
|
+
appConfig,
|
|
22
|
+
runtimeOrigin
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async function renderPage(filePath, params, query) {
|
|
26
|
+
if (!routeRendererFactory) {
|
|
27
|
+
throw new Error("Lit static render worker is not initialized");
|
|
28
|
+
}
|
|
29
|
+
const result = await routeRendererFactory.getPageRenderer(filePath).execute({
|
|
30
|
+
file: filePath,
|
|
31
|
+
params,
|
|
32
|
+
query
|
|
33
|
+
});
|
|
34
|
+
const body = result.body;
|
|
35
|
+
if (typeof body === "string") {
|
|
36
|
+
return { html: body, cacheStrategy: result.cacheStrategy };
|
|
37
|
+
}
|
|
38
|
+
if (Buffer.isBuffer(body)) {
|
|
39
|
+
return { html: body.toString("utf8"), cacheStrategy: result.cacheStrategy };
|
|
40
|
+
}
|
|
41
|
+
if (body instanceof ReadableStream) {
|
|
42
|
+
return { html: await new Response(body).text(), cacheStrategy: result.cacheStrategy };
|
|
43
|
+
}
|
|
44
|
+
throw new TypeError(`Unsupported Lit static render body type: ${typeof body}`);
|
|
45
|
+
}
|
|
46
|
+
function postMessage(message) {
|
|
47
|
+
parentPort?.postMessage(message);
|
|
48
|
+
}
|
|
49
|
+
if (!parentPort) {
|
|
50
|
+
throw new Error("Lit static render worker must be started as a worker thread");
|
|
51
|
+
}
|
|
52
|
+
parentPort.on("message", async (message) => {
|
|
53
|
+
try {
|
|
54
|
+
if (message.type === "init") {
|
|
55
|
+
await initializeWorker(message.configModulePath, message.runtimeOrigin);
|
|
56
|
+
postMessage({ type: "ready" });
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (message.type === "shutdown") {
|
|
60
|
+
process.exit(0);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (message.type === "render") {
|
|
64
|
+
const result = await renderPage(message.filePath, message.params, message.query);
|
|
65
|
+
postMessage({ type: "result", id: message.id, ...result });
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
69
|
+
if (message.type === "render") {
|
|
70
|
+
postMessage({ type: "error", id: message.id, message: errorMessage });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
postMessage({ type: "error", message: errorMessage });
|
|
74
|
+
}
|
|
75
|
+
});
|
package/src/lit.plugin.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module
|
|
4
4
|
*/
|
|
5
5
|
import './console.js';
|
|
6
|
-
import { IntegrationPlugin, type IntegrationPluginConfig } from '@ecopages/core/plugins/integration-plugin';
|
|
6
|
+
import { IntegrationPlugin, type IntegrationPluginConfig, type StaticExportContext } from '@ecopages/core/plugins/integration-plugin';
|
|
7
7
|
import { type AssetDefinition } from '@ecopages/core/services/asset-processing-service';
|
|
8
8
|
import { LitRenderer } from './lit-renderer.js';
|
|
9
9
|
/**
|
|
@@ -13,11 +13,24 @@ export declare const PLUGIN_NAME = "lit";
|
|
|
13
13
|
/**
|
|
14
14
|
* The Lit plugin class
|
|
15
15
|
* This plugin provides support for Lit components in Ecopages
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* Owns the {@link LitStaticRenderSession} for worker SSR. Renderers receive a
|
|
19
|
+
* lazy `getRenderSession` accessor so construction before {@link setup} still
|
|
20
|
+
* observes the session once setup assigns it.
|
|
16
21
|
*/
|
|
17
22
|
export declare class LitPlugin extends IntegrationPlugin {
|
|
18
23
|
renderer: typeof LitRenderer;
|
|
24
|
+
private renderSession;
|
|
19
25
|
constructor(options?: Omit<IntegrationPluginConfig, 'name'>);
|
|
20
26
|
getDependencies(): AssetDefinition[];
|
|
27
|
+
private createRenderSession;
|
|
28
|
+
initializeRenderer(options?: {
|
|
29
|
+
rendererModules?: unknown;
|
|
30
|
+
}): LitRenderer;
|
|
31
|
+
setup(): Promise<void>;
|
|
32
|
+
teardown(): Promise<void>;
|
|
33
|
+
beforeStaticExport(context: StaticExportContext): Promise<void>;
|
|
21
34
|
}
|
|
22
35
|
/**
|
|
23
36
|
* Factory function to create a Lit plugin instance
|
package/src/lit.plugin.js
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import "./console.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
IntegrationPlugin
|
|
4
|
+
} from "@ecopages/core/plugins/integration-plugin";
|
|
5
|
+
import { isLitStaticRenderWorkerThread } from "@ecopages/core/build/lit-static-render-worker-context";
|
|
6
|
+
import path from "node:path";
|
|
3
7
|
import { AssetFactory } from "@ecopages/core/services/asset-processing-service";
|
|
4
8
|
import { litElementHydrateScript } from "./lit-element-hydrate.js";
|
|
5
9
|
import { LIT_PLUGIN_NAME } from "./lit.constants.js";
|
|
6
10
|
import { LitRenderer } from "./lit-renderer.js";
|
|
11
|
+
import { LitStaticRenderSession } from "./lit-static-render-session.js";
|
|
7
12
|
const PLUGIN_NAME = LIT_PLUGIN_NAME;
|
|
8
13
|
class LitPlugin extends IntegrationPlugin {
|
|
9
14
|
renderer = LitRenderer;
|
|
15
|
+
renderSession = null;
|
|
10
16
|
constructor(options) {
|
|
11
17
|
super({
|
|
12
18
|
name: PLUGIN_NAME,
|
|
13
19
|
extensions: [".lit.tsx"],
|
|
14
|
-
staticBuildStep: "fetch",
|
|
15
20
|
...options
|
|
16
21
|
});
|
|
17
22
|
this.integrationDependencies.unshift(...this.getDependencies());
|
|
@@ -40,6 +45,52 @@ class LitPlugin extends IntegrationPlugin {
|
|
|
40
45
|
})
|
|
41
46
|
];
|
|
42
47
|
}
|
|
48
|
+
createRenderSession() {
|
|
49
|
+
if (!this.appConfig || !this.assetProcessingService) {
|
|
50
|
+
throw new Error("Lit plugin must be initialized before starting the render worker");
|
|
51
|
+
}
|
|
52
|
+
return new LitStaticRenderSession({
|
|
53
|
+
resolveDependencyPath: (componentDir, sourcePath) => path.join(componentDir, sourcePath),
|
|
54
|
+
processDependencies: this.assetProcessingService.processDependencies.bind(this.assetProcessingService),
|
|
55
|
+
preferSourceImports: typeof Bun !== "undefined"
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
initializeRenderer(options) {
|
|
59
|
+
const renderer = new this.renderer({
|
|
60
|
+
...this.createRendererOptions(options),
|
|
61
|
+
getRenderSession: () => this.renderSession ?? void 0
|
|
62
|
+
});
|
|
63
|
+
return this.attachRendererRuntimeServices(renderer);
|
|
64
|
+
}
|
|
65
|
+
async setup() {
|
|
66
|
+
await super.setup();
|
|
67
|
+
if (!this.appConfig?.absolutePaths?.config) {
|
|
68
|
+
throw new Error("[ecopages][lit] Lit server rendering requires appConfig.absolutePaths.config");
|
|
69
|
+
}
|
|
70
|
+
if (isLitStaticRenderWorkerThread()) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
this.renderSession = this.createRenderSession();
|
|
74
|
+
await this.renderSession.ensureWorker({
|
|
75
|
+
configModulePath: this.appConfig.absolutePaths.config,
|
|
76
|
+
runtimeOrigin: this.runtimeOrigin
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async teardown() {
|
|
80
|
+
await this.renderSession?.dispose();
|
|
81
|
+
this.renderSession = null;
|
|
82
|
+
await super.teardown();
|
|
83
|
+
}
|
|
84
|
+
async beforeStaticExport(context) {
|
|
85
|
+
if (!this.renderSession) {
|
|
86
|
+
this.renderSession = this.createRenderSession();
|
|
87
|
+
await this.renderSession.ensureWorker({
|
|
88
|
+
configModulePath: context.appConfig.absolutePaths.config,
|
|
89
|
+
runtimeOrigin: context.baseUrl
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
await this.renderSession.preloadStaticRoutes(context);
|
|
93
|
+
}
|
|
43
94
|
}
|
|
44
95
|
function litPlugin(options) {
|
|
45
96
|
return new LitPlugin(options);
|