@ecopages/lit 0.2.1-beta.0 → 0.3.0

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.
@@ -1,236 +1,116 @@
1
- import "./dom-shim.js";
2
- import { IntegrationRenderer } from "@ecopages/core/route-renderer/integration-renderer";
3
- import { LitSsrLazyPreloader } from "./lit-ssr-lazy-preloader.js";
4
- import { LIT_PLUGIN_NAME } from "./lit.constants.js";
5
- import {
6
- injectLitRenderedChildren,
7
- LIT_COMPONENT_CHILDREN_SLOT_MARKER,
8
- normalizeLitHtml,
9
- renderLitValueToString
10
- } from "./utils/lit-html-rendering.js";
11
- class LitRenderer extends IntegrationRenderer {
12
- name = LIT_PLUGIN_NAME;
13
- isFunctionComponent(component) {
14
- return typeof component === "function";
15
- }
16
- async resolveQueuedForeignSubtreeChildren(children, queuedResolutionsByToken, resolveToken) {
17
- if (children === void 0) {
18
- return void 0;
19
- }
20
- if (typeof children !== "string") {
21
- return children;
22
- }
23
- let renderedChildren = children;
24
- renderedChildren = await this.foreignSubtreeExecutionService.resolveQueuedTokens(
25
- renderedChildren,
26
- queuedResolutionsByToken,
27
- resolveToken
28
- );
29
- return renderedChildren;
30
- }
31
- async resolveQueuedForeignSubtreeHtml(html, runtimeContext) {
32
- const queuedForeignSubtreeResolution = await this.foreignSubtreeExecutionService.resolveQueuedHtml({
33
- currentIntegrationName: this.name,
34
- html,
35
- runtimeContext,
36
- queueLabel: "Lit",
37
- getOwningRenderer: (integrationName, rendererCache) => this.getIntegrationRendererForName(integrationName, rendererCache),
38
- applyAttributesToFirstElement: (resolvedHtml, attributes) => this.htmlTransformer.applyAttributesToFirstElement(resolvedHtml, attributes),
39
- dedupeProcessedAssets: (assets) => this.htmlTransformer.dedupeProcessedAssets(assets),
40
- renderQueuedChildren: async (children, _runtimeContext, queuedResolutionsByToken, resolveToken) => {
41
- const renderedChildren = await this.resolveQueuedForeignSubtreeChildren(
42
- children,
43
- queuedResolutionsByToken,
44
- resolveToken
45
- );
46
- if (typeof renderedChildren !== "string") {
47
- return {
48
- assets: [],
49
- children: renderedChildren
50
- };
51
- }
1
+ /**
2
+ * This module contains the Lit renderer hooks.
3
+ * @module
4
+ */
5
+ import { createIntegrationRenderer } from '@ecopages/core/integration';
6
+ import { resolveQueuedTokens } from '@ecopages/core/integration';
7
+ import { CUSTOM_ELEMENT_SSR_PRELOAD_CACHE_SCOPES, CustomElementScriptPreloader, } from '@ecopages/core/integration';
8
+ import { requireAppRuntime } from '@ecopages/core/integration';
9
+ import { createCustomElementServerModuleImporter } from '@ecopages/core/integration';
10
+ import { ensureLitDomShim } from './dom-shim.js';
11
+ import { LIT_PLUGIN_NAME } from './lit.constants.js';
12
+ import { injectLitRenderedChildren, LIT_COMPONENT_CHILDREN_SLOT_MARKER, normalizeLitHtml, renderLitValueToString, } from './utils/lit-html-rendering.js';
13
+ /**
14
+ * Creates the Lit renderer class with a lazy worker-session lookup.
15
+ *
16
+ * @remarks
17
+ * The plugin may create its renderer before `setup()` assigns the static render session, so hooks read the
18
+ * session when they run instead of capturing its construction-time value.
19
+ */
20
+ export function createLitRenderer(getRenderSession = () => undefined) {
21
+ return createIntegrationRenderer(LIT_PLUGIN_NAME, ({ appConfig, resolveComponentDependencies, resolveDependencyPath }) => {
22
+ const ssrScriptPreloader = new CustomElementScriptPreloader({
23
+ cacheScope: CUSTOM_ELEMENT_SSR_PRELOAD_CACHE_SCOPES.lit,
24
+ preloadScopes: requireAppRuntime(appConfig).customElementSsrPreloadScopes,
25
+ logLabel: 'lit',
26
+ requireLazyScriptEntry: true,
27
+ resolveDependencyPath,
28
+ importServerModule: createCustomElementServerModuleImporter(appConfig, '.lit-ssr'),
29
+ });
30
+ const preloadSsrScripts = async (components) => {
31
+ ensureLitDomShim();
32
+ const renderSession = getRenderSession();
33
+ if (renderSession) {
34
+ await renderSession.preloadSsrScripts(components);
35
+ return;
36
+ }
37
+ await ssrScriptPreloader.preloadSsrScripts(components);
38
+ };
52
39
  return {
53
- assets: [],
54
- html: renderedChildren
40
+ beforeRender: async (renderOptions) => preloadSsrScripts([
41
+ renderOptions.Page,
42
+ renderOptions.Layout,
43
+ ...(renderOptions.pageDependencyComponents ?? []),
44
+ ]),
45
+ transformDocumentHtml: normalizeLitHtml,
46
+ renderRemote: (routeOptions) => renderLitPageInWorker(routeOptions, getRenderSession),
47
+ renderComponent: async (input) => {
48
+ await preloadSsrScripts([input.component]);
49
+ if (!isFunctionComponent(input.component)) {
50
+ throw new TypeError('Lit renderer expected a callable component.');
51
+ }
52
+ const component = input.component;
53
+ let renderedChildren;
54
+ if (input.children !== undefined) {
55
+ renderedChildren =
56
+ typeof input.children === 'string'
57
+ ? input.children
58
+ : await renderLitValueToString(input.children);
59
+ }
60
+ const props = renderedChildren === undefined
61
+ ? input.props
62
+ : {
63
+ ...input.props,
64
+ children: LIT_COMPONENT_CHILDREN_SLOT_MARKER,
65
+ };
66
+ const content = await component(props);
67
+ const renderedHtml = await renderLitValueToString(content);
68
+ const html = renderedChildren === undefined
69
+ ? renderedHtml
70
+ : injectLitRenderedChildren(renderedHtml, renderedChildren);
71
+ const assets = input.component.config?.dependencies
72
+ ? await resolveComponentDependencies([input.component])
73
+ : undefined;
74
+ return {
75
+ html,
76
+ canAttachAttributes: true,
77
+ integrationName: LIT_PLUGIN_NAME,
78
+ assets,
79
+ };
80
+ },
81
+ renderQueuedChildren: async (children, _runtimeContext, queuedResolutionsByToken, resolveToken) => {
82
+ if (children === undefined) {
83
+ return {};
84
+ }
85
+ if (typeof children !== 'string') {
86
+ return { children };
87
+ }
88
+ return { html: await resolveQueuedTokens(children, queuedResolutionsByToken, resolveToken) };
89
+ },
55
90
  };
56
- }
57
91
  });
58
- return {
59
- html: queuedForeignSubtreeResolution.html,
60
- assets: queuedForeignSubtreeResolution.assets.length > 0 ? queuedForeignSubtreeResolution.assets : void 0
61
- };
62
- }
63
- shouldRenderPageComponent() {
64
- return false;
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.");
82
- }
83
- const component = input.component;
84
- let renderedChildren;
85
- if (input.children !== void 0) {
86
- renderedChildren = typeof input.children === "string" ? input.children : await renderLitValueToString(input.children);
87
- }
88
- let props = input.props;
89
- if (renderedChildren !== void 0) {
90
- props = {
91
- ...input.props,
92
- children: LIT_COMPONENT_CHILDREN_SLOT_MARKER
93
- };
94
- }
95
- const content = await component(props);
96
- const renderedHtml = await renderLitValueToString(content);
97
- const html = renderedChildren === void 0 ? renderedHtml : injectLitRenderedChildren(renderedHtml, renderedChildren);
98
- const queuedForeignSubtreeResolution = await this.resolveQueuedForeignSubtreeHtml(
99
- html,
100
- this.getQueuedForeignSubtreeResolutionContext(input)
101
- );
102
- const hasDependencies = Boolean(input.component.config?.dependencies);
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);
187
- }
188
- }
189
- async renderToResponse(view, props, ctx) {
190
- try {
191
- if (ctx.partial) {
192
- return this.renderPartialViewResponse({
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 ?? {};
204
- await this.preloadSsrLazyScripts([view, Layout]);
205
- await this.prepareViewDependencies(view, Layout);
206
- const pageRender = await this.renderComponentWithForeignChildren({
207
- component: view,
208
- props: normalizedProps
209
- });
210
- const layoutRender = Layout ? await this.renderComponentWithForeignChildren({
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
227
- });
228
- return this.createHtmlResponse(body, ctx);
229
- } catch (error) {
230
- throw this.createRenderError("Error rendering view", error);
92
+ }
93
+ export const LitRenderer = createLitRenderer();
94
+ function isFunctionComponent(component) {
95
+ return typeof component === 'function';
96
+ }
97
+ /**
98
+ * Renders a Lit page route in its build-scoped worker when request data can cross the worker boundary.
99
+ *
100
+ * @remarks
101
+ * Locals and explicit props stay in-process: locals are request objects, and the worker re-imports the route module.
102
+ */
103
+ async function renderLitPageInWorker(options, getRenderSession) {
104
+ const renderSession = getRenderSession();
105
+ if (!renderSession || options.locals !== undefined || options.props !== undefined) {
106
+ return undefined;
231
107
  }
232
- }
108
+ const params = options.params ?? {};
109
+ const result = await renderSession.renderPageInWorker({
110
+ filePath: options.file,
111
+ params,
112
+ query: options.query,
113
+ });
114
+ const { html, ...metadata } = result;
115
+ return { body: html, ...metadata };
233
116
  }
234
- export {
235
- LitRenderer
236
- };
@@ -0,0 +1,40 @@
1
+ import type { PageParams, PageQuery } from '@ecopages/core';
2
+ import type { RouteRenderResult } from '@ecopages/core/integration';
3
+ /** A rendered page, its resolved metadata, and what the main thread needs to cache it. */
4
+ export type LitStaticRenderResult = Pick<RouteRenderResult, 'cacheStrategy' | 'sourceDependencyPaths' | 'metadata'> & {
5
+ html: string;
6
+ };
7
+ export type LitStaticRenderWorkerInitMessage = {
8
+ type: 'init';
9
+ configModulePath: string;
10
+ runtimeOrigin: string;
11
+ };
12
+ export type LitStaticRenderWorkerRenderMessage = {
13
+ type: 'render';
14
+ id: string;
15
+ filePath: string;
16
+ /** Route params; multi-value keys stay as `string[]`. */
17
+ params: PageParams;
18
+ /** Serializable route query; preserves multi-value keys as `string[]`. */
19
+ query?: PageQuery;
20
+ };
21
+ export type LitStaticRenderWorkerShutdownMessage = {
22
+ type: 'shutdown';
23
+ };
24
+ export type LitStaticRenderWorkerRequestMessage = LitStaticRenderWorkerInitMessage | LitStaticRenderWorkerRenderMessage | LitStaticRenderWorkerShutdownMessage;
25
+ export type LitStaticRenderWorkerReadyMessage = {
26
+ type: 'ready';
27
+ };
28
+ export type LitStaticRenderWorkerResultMessage = {
29
+ type: 'result';
30
+ id: string;
31
+ } & LitStaticRenderResult;
32
+ export type LitStaticRenderWorkerErrorMessage = {
33
+ type: 'error';
34
+ id?: string;
35
+ message: string;
36
+ };
37
+ export type LitStaticRenderWorkerShutdownCompleteMessage = {
38
+ type: 'shutdown-complete';
39
+ };
40
+ export type LitStaticRenderWorkerResponseMessage = LitStaticRenderWorkerReadyMessage | LitStaticRenderWorkerResultMessage | LitStaticRenderWorkerErrorMessage | LitStaticRenderWorkerShutdownCompleteMessage;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { RouteRenderResult } from '@ecopages/core/integration';
2
+ import type { LitStaticRenderResult } from './lit-static-render-protocol.js';
3
+ /**
4
+ * Turns a route render into the message the worker posts back to the main thread.
5
+ *
6
+ * @remarks
7
+ * The resolved `metadata` travels with the HTML so the main thread can decide sitemap eligibility
8
+ * from the render instead of loading the page again.
9
+ */
10
+ export declare function toLitStaticRenderResult(result: RouteRenderResult): Promise<LitStaticRenderResult>;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Turns a route render into the message the worker posts back to the main thread.
3
+ *
4
+ * @remarks
5
+ * The resolved `metadata` travels with the HTML so the main thread can decide sitemap eligibility
6
+ * from the render instead of loading the page again.
7
+ */
8
+ export async function toLitStaticRenderResult(result) {
9
+ const { body, cacheStrategy, sourceDependencyPaths, metadata } = result;
10
+ const rendered = { cacheStrategy, sourceDependencyPaths, metadata };
11
+ if (typeof body === 'string') {
12
+ return { html: body, ...rendered };
13
+ }
14
+ if (Buffer.isBuffer(body)) {
15
+ return { html: body.toString('utf8'), ...rendered };
16
+ }
17
+ if (body instanceof ReadableStream) {
18
+ return { html: await new Response(body).text(), ...rendered };
19
+ }
20
+ throw new TypeError(`Unsupported Lit static render body type: ${typeof body}`);
21
+ }
@@ -0,0 +1,65 @@
1
+ import type { PageParams, PageQuery } from '@ecopages/core';
2
+ import type { StaticExportContext } from '@ecopages/core/integration';
3
+ import { type CustomElementSsrPreloadComponent } from '@ecopages/core/integration';
4
+ import type { CustomElementScriptPreloadScopeState } from '@ecopages/core/integration';
5
+ import { LitStaticRenderWorkerClient } from './lit-static-render-worker-client.js';
6
+ import type { LitStaticRenderResult } from './lit-static-render-protocol.js';
7
+ import type { AssetDefinition, AssetProcessingIntegration, ProcessedAsset } from '@ecopages/core/integration';
8
+ type LitStaticRenderWorkerClientContract = Pick<LitStaticRenderWorkerClient, 'start' | 'renderPage' | 'dispose'>;
9
+ type LitStaticRenderSessionOptions = {
10
+ preloadScopes: Map<string, CustomElementScriptPreloadScopeState>;
11
+ integration?: AssetProcessingIntegration;
12
+ resolveDependencyPath: (componentDir: string, sourcePath: string) => string;
13
+ processDependencies: (dependencies: AssetDefinition[], integration?: AssetProcessingIntegration) => Promise<ProcessedAsset[]>;
14
+ getInvalidationVersion?: () => number;
15
+ createWorkerClient?: (input: {
16
+ configModulePath: string;
17
+ runtimeOrigin: string;
18
+ }) => LitStaticRenderWorkerClientContract;
19
+ };
20
+ /**
21
+ * Plugin-runtime-scoped Lit server render session.
22
+ *
23
+ * @remarks
24
+ * Owns worker lifecycle and SSR preload for Lit page routes during static export
25
+ * and runtime/dev requests. Injected into `LitRenderer` via a lazy accessor from
26
+ * `LitPlugin`; there is no process-global coordinator.
27
+ *
28
+ * Worker init identity is sticky for the first `{ configModulePath, runtimeOrigin }`.
29
+ * Later `ensureWorker` calls with the same identity are no-ops; a different identity
30
+ * recreates the worker so stale config/origin is never silently reused.
31
+ */
32
+ export declare class LitStaticRenderSession {
33
+ private readonly preloader;
34
+ private readonly createWorkerClient;
35
+ private workerClient;
36
+ private workerIdentity;
37
+ private readonly getInvalidationVersion;
38
+ private workerInvalidationVersion;
39
+ private renderChain;
40
+ constructor(options: LitStaticRenderSessionOptions);
41
+ /**
42
+ * Ensures the static render worker is running for the given identity.
43
+ *
44
+ * @remarks
45
+ * First successful start sticks that identity. Matching re-entry is a no-op.
46
+ * A different `configModulePath` or `runtimeOrigin` disposes and recreates.
47
+ */
48
+ ensureWorker(input: {
49
+ configModulePath: string;
50
+ runtimeOrigin: string;
51
+ }): Promise<void>;
52
+ preloadStaticRoutes(context: StaticExportContext): Promise<void>;
53
+ preloadSsrScripts(components: Array<CustomElementSsrPreloadComponent | undefined>): Promise<void>;
54
+ collectSsrPreloadScripts(components: Array<CustomElementSsrPreloadComponent | undefined>): string[];
55
+ renderPageInWorker(input: {
56
+ filePath: string;
57
+ params: PageParams;
58
+ query?: PageQuery;
59
+ }): Promise<LitStaticRenderResult>;
60
+ isExpectedSsrPreloadError(error: unknown): boolean;
61
+ dispose(): Promise<void>;
62
+ private preloadLitRoutes;
63
+ private isLitRoute;
64
+ }
65
+ export {};
@@ -0,0 +1,145 @@
1
+ import path from 'node:path';
2
+ import { ensureLitDomShim } from './dom-shim.js';
3
+ import { CUSTOM_ELEMENT_SSR_PRELOAD_CACHE_SCOPES, CustomElementScriptPreloader, } from '@ecopages/core/integration';
4
+ import { LIT_PLUGIN_NAME } from './lit.constants.js';
5
+ import { createCustomElementSsrPipelineImporter } from '@ecopages/core/integration';
6
+ import { LitStaticRenderWorkerClient } from './lit-static-render-worker-client.js';
7
+ /**
8
+ * Plugin-runtime-scoped Lit server render session.
9
+ *
10
+ * @remarks
11
+ * Owns worker lifecycle and SSR preload for Lit page routes during static export
12
+ * and runtime/dev requests. Injected into `LitRenderer` via a lazy accessor from
13
+ * `LitPlugin`; there is no process-global coordinator.
14
+ *
15
+ * Worker init identity is sticky for the first `{ configModulePath, runtimeOrigin }`.
16
+ * Later `ensureWorker` calls with the same identity are no-ops; a different identity
17
+ * recreates the worker so stale config/origin is never silently reused.
18
+ */
19
+ export class LitStaticRenderSession {
20
+ preloader;
21
+ createWorkerClient;
22
+ workerClient = null;
23
+ workerIdentity = null;
24
+ getInvalidationVersion;
25
+ workerInvalidationVersion = 0;
26
+ renderChain = Promise.resolve();
27
+ constructor(options) {
28
+ this.getInvalidationVersion = options.getInvalidationVersion ?? (() => 0);
29
+ this.preloader = new CustomElementScriptPreloader({
30
+ cacheScope: CUSTOM_ELEMENT_SSR_PRELOAD_CACHE_SCOPES.lit,
31
+ preloadScopes: options.preloadScopes,
32
+ logLabel: 'lit',
33
+ requireLazyScriptEntry: true,
34
+ resolveDependencyPath: options.resolveDependencyPath,
35
+ importServerModule: createCustomElementSsrPipelineImporter({
36
+ integration: options.integration,
37
+ processDependencies: options.processDependencies,
38
+ }),
39
+ });
40
+ this.createWorkerClient =
41
+ options.createWorkerClient ??
42
+ ((input) => new LitStaticRenderWorkerClient({
43
+ configModulePath: input.configModulePath,
44
+ runtimeOrigin: input.runtimeOrigin,
45
+ }));
46
+ }
47
+ /**
48
+ * Ensures the static render worker is running for the given identity.
49
+ *
50
+ * @remarks
51
+ * First successful start sticks that identity. Matching re-entry is a no-op.
52
+ * A different `configModulePath` or `runtimeOrigin` disposes and recreates.
53
+ */
54
+ async ensureWorker(input) {
55
+ if (this.workerClient &&
56
+ this.workerIdentity &&
57
+ this.workerIdentity.configModulePath === input.configModulePath &&
58
+ this.workerIdentity.runtimeOrigin === input.runtimeOrigin) {
59
+ return;
60
+ }
61
+ if (this.workerClient) {
62
+ await this.dispose();
63
+ }
64
+ this.workerIdentity = {
65
+ configModulePath: input.configModulePath,
66
+ runtimeOrigin: input.runtimeOrigin,
67
+ };
68
+ this.workerClient = this.createWorkerClient({
69
+ configModulePath: input.configModulePath,
70
+ runtimeOrigin: input.runtimeOrigin,
71
+ });
72
+ await this.workerClient.start();
73
+ this.workerInvalidationVersion = this.getInvalidationVersion();
74
+ }
75
+ async preloadStaticRoutes(context) {
76
+ await this.preloadLitRoutes(context);
77
+ }
78
+ async preloadSsrScripts(components) {
79
+ ensureLitDomShim();
80
+ await this.preloader.preloadSsrScripts(components);
81
+ }
82
+ collectSsrPreloadScripts(components) {
83
+ return this.preloader.collectSsrPreloadScripts(components);
84
+ }
85
+ async renderPageInWorker(input) {
86
+ if (!this.workerClient) {
87
+ throw new Error('Lit static render worker is not active');
88
+ }
89
+ const render = this.renderChain.then(async () => {
90
+ const version = this.getInvalidationVersion();
91
+ if (version !== this.workerInvalidationVersion && this.workerIdentity) {
92
+ const identity = this.workerIdentity;
93
+ await this.dispose();
94
+ await this.ensureWorker(identity);
95
+ }
96
+ if (!this.workerClient)
97
+ throw new Error('Lit static render worker is not active');
98
+ return this.workerClient.renderPage(input);
99
+ });
100
+ this.renderChain = render.then(() => undefined, () => undefined);
101
+ return render;
102
+ }
103
+ isExpectedSsrPreloadError(error) {
104
+ return this.preloader.isExpectedSsrPreloadError(error);
105
+ }
106
+ async dispose() {
107
+ await this.workerClient?.dispose();
108
+ this.workerClient = null;
109
+ this.workerIdentity = null;
110
+ }
111
+ async preloadLitRoutes(context) {
112
+ if (!context.routeRendererFactory) {
113
+ return;
114
+ }
115
+ const routes = await context.router.listStaticGenerationRoutes({ runtimeOrigin: context.baseUrl });
116
+ const components = [];
117
+ for (const route of routes) {
118
+ const filePath = route.templateRoute.filePath;
119
+ if (!this.isLitRoute(filePath, context.appConfig)) {
120
+ continue;
121
+ }
122
+ const pageRenderer = context.routeRendererFactory.getPageRenderer(filePath);
123
+ const pageModule = await pageRenderer.loadPageModule(filePath);
124
+ const page = pageModule.default;
125
+ if (page) {
126
+ components.push(page);
127
+ for (const layout of page.config?.layouts ?? []) {
128
+ components.push(layout);
129
+ }
130
+ for (const nestedComponent of page.config?.dependencies?.components ?? []) {
131
+ components.push(nestedComponent);
132
+ }
133
+ }
134
+ }
135
+ await this.preloadSsrScripts(components);
136
+ }
137
+ isLitRoute(filePath, appConfig) {
138
+ const litIntegration = appConfig.integrations.find((plugin) => plugin.name === LIT_PLUGIN_NAME);
139
+ if (!litIntegration) {
140
+ return false;
141
+ }
142
+ const normalizedPath = path.normalize(filePath);
143
+ return litIntegration.extensions.some((extension) => normalizedPath.endsWith(extension));
144
+ }
145
+ }
@@ -0,0 +1,26 @@
1
+ import type { PageParams, PageQuery } from '@ecopages/core';
2
+ import type { LitStaticRenderResult } from './lit-static-render-protocol.js';
3
+ export declare class LitStaticRenderWorkerClient {
4
+ private readonly configModulePath;
5
+ private readonly runtimeOrigin;
6
+ private worker;
7
+ private workerExitPromise;
8
+ private workerExitCode;
9
+ private readyPromise;
10
+ private pending;
11
+ private nextRenderId;
12
+ private shutdownHandler;
13
+ constructor({ configModulePath, runtimeOrigin }: {
14
+ configModulePath: string;
15
+ runtimeOrigin: string;
16
+ });
17
+ private rejectPending;
18
+ start(): Promise<void>;
19
+ renderPage(input: {
20
+ filePath: string;
21
+ params: PageParams;
22
+ query?: PageQuery;
23
+ }): Promise<LitStaticRenderResult>;
24
+ dispose(): Promise<void>;
25
+ }
26
+ export declare const litStaticRenderWorkerEntryPath: string;