@ecopages/lit 0.2.1 → 0.3.1
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 +35 -17
- package/package.json +15 -4
- package/src/console.d.ts +0 -1
- package/src/console.js +12 -6
- package/src/dom-shim.d.ts +11 -0
- package/src/dom-shim.js +60 -0
- package/src/lit-element-hydrate.d.ts +10 -0
- package/src/lit-element-hydrate.js +11 -4
- package/src/lit-renderer.d.ts +13 -60
- package/src/lit-renderer.js +106 -278
- package/src/lit-static-render-error.d.ts +17 -0
- package/src/lit-static-render-error.js +34 -0
- package/src/lit-static-render-protocol.d.ts +46 -0
- package/src/lit-static-render-protocol.js +1 -0
- package/src/lit-static-render-result.d.ts +10 -0
- package/src/lit-static-render-result.js +21 -0
- package/src/lit-static-render-session.d.ts +63 -0
- package/src/lit-static-render-session.js +139 -0
- package/src/lit-static-render-worker-client.d.ts +26 -0
- package/src/lit-static-render-worker-client.js +182 -0
- package/src/lit-static-render-worker.d.ts +1 -0
- package/src/lit-static-render-worker.js +104 -0
- package/src/lit.constants.d.ts +1 -0
- package/src/lit.constants.js +1 -0
- package/src/lit.plugin.d.ts +36 -3
- package/src/lit.plugin.js +138 -47
- package/src/styled-mixin.js +20 -12
- package/src/utils/lit-html-rendering.d.ts +5 -0
- package/src/utils/lit-html-rendering.js +45 -0
- package/CHANGELOG.md +0 -19
- package/src/lit-ssr-lazy-preloader.d.ts +0 -43
- package/src/lit-ssr-lazy-preloader.js +0 -160
|
@@ -0,0 +1,139 @@
|
|
|
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
|
+
async renderPageInWorker(input) {
|
|
83
|
+
if (!this.workerClient) {
|
|
84
|
+
throw new Error('Lit static render worker is not active');
|
|
85
|
+
}
|
|
86
|
+
const render = this.renderChain.then(async () => {
|
|
87
|
+
const version = this.getInvalidationVersion();
|
|
88
|
+
if (version !== this.workerInvalidationVersion && this.workerIdentity) {
|
|
89
|
+
const identity = this.workerIdentity;
|
|
90
|
+
await this.dispose();
|
|
91
|
+
await this.ensureWorker(identity);
|
|
92
|
+
}
|
|
93
|
+
if (!this.workerClient)
|
|
94
|
+
throw new Error('Lit static render worker is not active');
|
|
95
|
+
return this.workerClient.renderPage(input);
|
|
96
|
+
});
|
|
97
|
+
this.renderChain = render.then(() => undefined, () => undefined);
|
|
98
|
+
return render;
|
|
99
|
+
}
|
|
100
|
+
async dispose() {
|
|
101
|
+
await this.workerClient?.dispose();
|
|
102
|
+
this.workerClient = null;
|
|
103
|
+
this.workerIdentity = null;
|
|
104
|
+
}
|
|
105
|
+
async preloadLitRoutes(context) {
|
|
106
|
+
if (!context.routeRendererFactory) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const routes = await context.router.listStaticGenerationRoutes({ runtimeOrigin: context.baseUrl });
|
|
110
|
+
const components = [];
|
|
111
|
+
for (const route of routes) {
|
|
112
|
+
const filePath = route.templateRoute.filePath;
|
|
113
|
+
if (!this.isLitRoute(filePath, context.appConfig)) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const pageRenderer = context.routeRendererFactory.getPageRenderer(filePath);
|
|
117
|
+
const pageModule = await pageRenderer.loadPageModule(filePath);
|
|
118
|
+
const page = pageModule.default;
|
|
119
|
+
if (page) {
|
|
120
|
+
components.push(page);
|
|
121
|
+
for (const layout of page.config?.layouts ?? []) {
|
|
122
|
+
components.push(layout);
|
|
123
|
+
}
|
|
124
|
+
for (const nestedComponent of page.config?.dependencies?.components ?? []) {
|
|
125
|
+
components.push(nestedComponent);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
await this.preloadSsrScripts(components);
|
|
130
|
+
}
|
|
131
|
+
isLitRoute(filePath, appConfig) {
|
|
132
|
+
const litIntegration = appConfig.integrations.find((plugin) => plugin.name === LIT_PLUGIN_NAME);
|
|
133
|
+
if (!litIntegration) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const normalizedPath = path.normalize(filePath);
|
|
137
|
+
return litIntegration.extensions.some((extension) => normalizedPath.endsWith(extension));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -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;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { Worker } from 'node:worker_threads';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { fromLitStaticRenderErrorMessage } from './lit-static-render-error.js';
|
|
4
|
+
const workerShutdownTimeoutMs = 30_000;
|
|
5
|
+
const workerExitTimeoutMs = 1_000;
|
|
6
|
+
export class LitStaticRenderWorkerClient {
|
|
7
|
+
configModulePath;
|
|
8
|
+
runtimeOrigin;
|
|
9
|
+
worker = null;
|
|
10
|
+
workerExitPromise = null;
|
|
11
|
+
workerExitCode = null;
|
|
12
|
+
readyPromise = null;
|
|
13
|
+
pending = new Map();
|
|
14
|
+
nextRenderId = 0;
|
|
15
|
+
shutdownHandler = null;
|
|
16
|
+
constructor({ configModulePath, runtimeOrigin }) {
|
|
17
|
+
this.configModulePath = configModulePath;
|
|
18
|
+
this.runtimeOrigin = runtimeOrigin;
|
|
19
|
+
}
|
|
20
|
+
rejectPending(error) {
|
|
21
|
+
for (const pending of this.pending.values()) {
|
|
22
|
+
pending.reject(error);
|
|
23
|
+
}
|
|
24
|
+
this.pending.clear();
|
|
25
|
+
}
|
|
26
|
+
async start() {
|
|
27
|
+
if (this.readyPromise) {
|
|
28
|
+
return this.readyPromise;
|
|
29
|
+
}
|
|
30
|
+
this.readyPromise = new Promise((resolveReady, rejectReady) => {
|
|
31
|
+
const worker = new Worker(new URL('./lit-static-render-worker.js', import.meta.url), {
|
|
32
|
+
execArgv: [...process.execArgv],
|
|
33
|
+
env: {
|
|
34
|
+
...process.env,
|
|
35
|
+
ECOPAGES_LIT_STATIC_RENDER_WORKER: 'true',
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
this.worker = worker;
|
|
39
|
+
this.workerExitCode = null;
|
|
40
|
+
this.workerExitPromise = new Promise((resolveExit) => {
|
|
41
|
+
worker.once('exit', resolveExit);
|
|
42
|
+
});
|
|
43
|
+
worker.on('message', (message) => {
|
|
44
|
+
if (message.type === 'ready') {
|
|
45
|
+
resolveReady();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (message.type === 'shutdown-complete') {
|
|
49
|
+
this.shutdownHandler?.resolve();
|
|
50
|
+
this.shutdownHandler = null;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (message.type === 'error') {
|
|
54
|
+
if (message.id && this.pending.has(message.id)) {
|
|
55
|
+
const pending = this.pending.get(message.id);
|
|
56
|
+
this.pending.delete(message.id);
|
|
57
|
+
pending.reject(fromLitStaticRenderErrorMessage(message));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const error = new Error(message.message);
|
|
61
|
+
this.shutdownHandler?.reject(error);
|
|
62
|
+
this.shutdownHandler = null;
|
|
63
|
+
rejectReady(error);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (message.type === 'result') {
|
|
67
|
+
const pending = this.pending.get(message.id);
|
|
68
|
+
if (!pending) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
this.pending.delete(message.id);
|
|
72
|
+
pending.resolve({
|
|
73
|
+
html: message.html,
|
|
74
|
+
cacheStrategy: message.cacheStrategy,
|
|
75
|
+
sourceDependencyPaths: message.sourceDependencyPaths,
|
|
76
|
+
metadata: message.metadata,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
worker.on('error', (error) => {
|
|
81
|
+
rejectReady(error instanceof Error ? error : new Error(String(error)));
|
|
82
|
+
});
|
|
83
|
+
worker.on('exit', (code) => {
|
|
84
|
+
this.workerExitCode = code;
|
|
85
|
+
const exitError = new Error(code === 0
|
|
86
|
+
? 'Lit static render worker exited'
|
|
87
|
+
: `Lit static render worker exited with code ${code}`);
|
|
88
|
+
this.rejectPending(exitError);
|
|
89
|
+
this.shutdownHandler?.reject(exitError);
|
|
90
|
+
this.shutdownHandler = null;
|
|
91
|
+
if (code !== 0) {
|
|
92
|
+
rejectReady(exitError);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
const initMessage = {
|
|
96
|
+
type: 'init',
|
|
97
|
+
configModulePath: this.configModulePath,
|
|
98
|
+
runtimeOrigin: this.runtimeOrigin,
|
|
99
|
+
};
|
|
100
|
+
worker.postMessage(initMessage);
|
|
101
|
+
});
|
|
102
|
+
return this.readyPromise;
|
|
103
|
+
}
|
|
104
|
+
async renderPage(input) {
|
|
105
|
+
await this.start();
|
|
106
|
+
const worker = this.worker;
|
|
107
|
+
if (!worker) {
|
|
108
|
+
throw new Error('Lit static render worker is not available');
|
|
109
|
+
}
|
|
110
|
+
const id = String(++this.nextRenderId);
|
|
111
|
+
return new Promise((resolve, reject) => {
|
|
112
|
+
this.pending.set(id, { resolve, reject });
|
|
113
|
+
const renderMessage = {
|
|
114
|
+
type: 'render',
|
|
115
|
+
id,
|
|
116
|
+
filePath: input.filePath,
|
|
117
|
+
params: input.params,
|
|
118
|
+
query: input.query,
|
|
119
|
+
};
|
|
120
|
+
worker.postMessage(renderMessage);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
async dispose() {
|
|
124
|
+
if (!this.worker) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
this.rejectPending(new Error('Lit static render worker was disposed'));
|
|
128
|
+
const worker = this.worker;
|
|
129
|
+
const workerExit = this.workerExitPromise ?? Promise.resolve(this.workerExitCode ?? 0);
|
|
130
|
+
this.worker = null;
|
|
131
|
+
this.workerExitPromise = null;
|
|
132
|
+
this.readyPromise = null;
|
|
133
|
+
if (this.workerExitCode !== null) {
|
|
134
|
+
if (this.workerExitCode !== 0) {
|
|
135
|
+
throw new Error(`Lit static render worker exited with code ${this.workerExitCode}`);
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
let shutdownTimeout;
|
|
140
|
+
const shutdownComplete = new Promise((resolve, reject) => {
|
|
141
|
+
shutdownTimeout = setTimeout(() => reject(new Error('Lit static render worker shutdown timed out')), workerShutdownTimeoutMs);
|
|
142
|
+
this.shutdownHandler = {
|
|
143
|
+
resolve: () => {
|
|
144
|
+
if (shutdownTimeout)
|
|
145
|
+
clearTimeout(shutdownTimeout);
|
|
146
|
+
resolve();
|
|
147
|
+
},
|
|
148
|
+
reject: (error) => {
|
|
149
|
+
if (shutdownTimeout)
|
|
150
|
+
clearTimeout(shutdownTimeout);
|
|
151
|
+
reject(error);
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
worker.postMessage({ type: 'shutdown' });
|
|
156
|
+
let shutdownError;
|
|
157
|
+
try {
|
|
158
|
+
await shutdownComplete;
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
shutdownError = error instanceof Error ? error : new Error(String(error));
|
|
162
|
+
}
|
|
163
|
+
if (shutdownTimeout)
|
|
164
|
+
clearTimeout(shutdownTimeout);
|
|
165
|
+
const exitResult = await new Promise((resolve) => {
|
|
166
|
+
const exitTimeout = setTimeout(() => resolve({ timedOut: true }), workerExitTimeoutMs);
|
|
167
|
+
void workerExit.then((code) => {
|
|
168
|
+
clearTimeout(exitTimeout);
|
|
169
|
+
resolve({ code });
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
if ('timedOut' in exitResult) {
|
|
173
|
+
await worker.terminate();
|
|
174
|
+
}
|
|
175
|
+
else if (exitResult.code !== 0) {
|
|
176
|
+
shutdownError ??= new Error(`Lit static render worker exited with code ${exitResult.code}`);
|
|
177
|
+
}
|
|
178
|
+
if (shutdownError)
|
|
179
|
+
throw shutdownError;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export const litStaticRenderWorkerEntryPath = fileURLToPath(new URL('./lit-static-render-worker.js', import.meta.url));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './dom-shim.js';
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { parentPort } from 'node:worker_threads';
|
|
2
|
+
import { loadEcoPagesConfig } from '@ecopages/core/config';
|
|
3
|
+
import { setupAppRuntimeIntegrations, setupAppRuntimePlugins, teardownAppRuntimeIntegrations, } from '@ecopages/core/build';
|
|
4
|
+
import { requireAppRuntime } from '@ecopages/core/integration';
|
|
5
|
+
import { RouteRendererFactory } from '@ecopages/core/integration';
|
|
6
|
+
import { toLitStaticRenderErrorMessage } from './lit-static-render-error.js';
|
|
7
|
+
import { toLitStaticRenderResult } from './lit-static-render-result.js';
|
|
8
|
+
import './dom-shim.js';
|
|
9
|
+
let appConfig = null;
|
|
10
|
+
let routeRendererFactory = null;
|
|
11
|
+
let activeRenders = 0;
|
|
12
|
+
let shutdownRequested = false;
|
|
13
|
+
let resolveRendersIdle;
|
|
14
|
+
/**
|
|
15
|
+
* Loads app config in the worker isolate and bootstraps rendering.
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* Processor `.setup()` is skipped here because the main thread already prepared disk artifacts
|
|
19
|
+
* before the Lit worker started (`ECOPAGES_LIT_STATIC_RENDER_WORKER`). Integrations still need
|
|
20
|
+
* setup in this isolate before route renderers are created.
|
|
21
|
+
*/
|
|
22
|
+
async function initializeWorker(configModulePath, runtimeOrigin) {
|
|
23
|
+
appConfig = await loadEcoPagesConfig({ configFile: configModulePath });
|
|
24
|
+
const runtime = requireAppRuntime(appConfig);
|
|
25
|
+
runtime.origin = runtimeOrigin;
|
|
26
|
+
await setupAppRuntimePlugins({
|
|
27
|
+
appConfig,
|
|
28
|
+
});
|
|
29
|
+
await setupAppRuntimeIntegrations({ appConfig });
|
|
30
|
+
routeRendererFactory = new RouteRendererFactory({
|
|
31
|
+
appConfig,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async function renderPage(filePath, params, query) {
|
|
35
|
+
if (!routeRendererFactory) {
|
|
36
|
+
throw new Error('Lit static render worker is not initialized');
|
|
37
|
+
}
|
|
38
|
+
const result = await routeRendererFactory.getPageRenderer(filePath).execute({
|
|
39
|
+
file: filePath,
|
|
40
|
+
params,
|
|
41
|
+
query,
|
|
42
|
+
});
|
|
43
|
+
return toLitStaticRenderResult(result);
|
|
44
|
+
}
|
|
45
|
+
async function shutdownWorker() {
|
|
46
|
+
shutdownRequested = true;
|
|
47
|
+
if (activeRenders > 0) {
|
|
48
|
+
await new Promise((resolve) => {
|
|
49
|
+
resolveRendersIdle = resolve;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
if (appConfig) {
|
|
54
|
+
await teardownAppRuntimeIntegrations({ appConfig });
|
|
55
|
+
}
|
|
56
|
+
postMessage({ type: 'shutdown-complete' });
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
60
|
+
postMessage({ type: 'error', message: errorMessage });
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
parentPort?.close();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function postMessage(message) {
|
|
67
|
+
parentPort?.postMessage(message);
|
|
68
|
+
}
|
|
69
|
+
if (!parentPort) {
|
|
70
|
+
throw new Error('Lit static render worker must be started as a worker thread');
|
|
71
|
+
}
|
|
72
|
+
parentPort.on('message', async (message) => {
|
|
73
|
+
try {
|
|
74
|
+
if (message.type === 'init') {
|
|
75
|
+
await initializeWorker(message.configModulePath, message.runtimeOrigin);
|
|
76
|
+
postMessage({ type: 'ready' });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (message.type === 'shutdown') {
|
|
80
|
+
await shutdownWorker();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (message.type === 'render') {
|
|
84
|
+
if (shutdownRequested) {
|
|
85
|
+
throw new Error('Lit static render worker is shutting down');
|
|
86
|
+
}
|
|
87
|
+
activeRenders += 1;
|
|
88
|
+
try {
|
|
89
|
+
const result = await renderPage(message.filePath, message.params, message.query);
|
|
90
|
+
postMessage({ type: 'result', id: message.id, ...result });
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
activeRenders -= 1;
|
|
94
|
+
if (activeRenders === 0) {
|
|
95
|
+
resolveRendersIdle?.();
|
|
96
|
+
resolveRendersIdle = undefined;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
postMessage(toLitStaticRenderErrorMessage(error, message.type === 'render' ? message.id : undefined));
|
|
103
|
+
}
|
|
104
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const LIT_PLUGIN_NAME = "lit";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const LIT_PLUGIN_NAME = 'lit';
|
package/src/lit.plugin.d.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* @module
|
|
4
4
|
*/
|
|
5
5
|
import './console.js';
|
|
6
|
-
import { IntegrationPlugin, type IntegrationPluginConfig } from '@ecopages/core/
|
|
7
|
-
import { type AssetDefinition } from '@ecopages/core/
|
|
6
|
+
import { IntegrationPlugin, type IntegrationRendererBase, type IntegrationRuntimeContext, type IntegrationPluginConfig, type StaticExportContext, type StaticExportRelease } from '@ecopages/core/integration';
|
|
7
|
+
import { type AssetDefinition } from '@ecopages/core/integration';
|
|
8
8
|
import { LitRenderer } from './lit-renderer.js';
|
|
9
9
|
/**
|
|
10
10
|
* The name of the Lit plugin
|
|
@@ -13,11 +13,44 @@ 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
|
-
renderer:
|
|
23
|
+
renderer: new (options: import("@ecopages/core/integration").IntegrationRendererOptions) => import("@ecopages/core/integration").IntegrationRenderer<import("@ecopages/core").EcoPagesElement> & {
|
|
24
|
+
name: string;
|
|
25
|
+
};
|
|
26
|
+
private renderSession;
|
|
19
27
|
constructor(options?: Omit<IntegrationPluginConfig, 'name'>);
|
|
28
|
+
/**
|
|
29
|
+
* Returns the global integration dependencies required in the browser.
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* Injects Lit's hydration support script into the document head before any
|
|
33
|
+
* custom element connects. While `@lit-labs/ssr-client/lit-element-hydrate-support.js`
|
|
34
|
+
* can be resolved directly in modern Bun and Node environments, an inline self-contained
|
|
35
|
+
* script is currently emitted to ensure synchronous availability before custom-element
|
|
36
|
+
* registration without requiring an import map for `@lit-labs/ssr-client` subpath imports.
|
|
37
|
+
*/
|
|
20
38
|
getDependencies(): AssetDefinition[];
|
|
39
|
+
private createRenderSession;
|
|
40
|
+
createRenderer(base: IntegrationRendererBase): LitRenderer;
|
|
41
|
+
hmr(context: IntegrationRuntimeContext): void;
|
|
42
|
+
setup(context: IntegrationRuntimeContext): Promise<void>;
|
|
43
|
+
teardown(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Reconciles the render worker with the export origin and preloads Lit routes.
|
|
46
|
+
*
|
|
47
|
+
* @remarks
|
|
48
|
+
* `setup()` can start the session with the runtime origin before static export resolves its own base URL. The
|
|
49
|
+
* session reuses the worker when the identities match and restarts it when the export origin differs. The returned
|
|
50
|
+
* release stops the worker once the export ends, so an in-process export leaves no worker thread running. A later
|
|
51
|
+
* render in the same process falls back to rendering in-process.
|
|
52
|
+
*/
|
|
53
|
+
staticExport(context: StaticExportContext): Promise<StaticExportRelease>;
|
|
21
54
|
}
|
|
22
55
|
/**
|
|
23
56
|
* Factory function to create a Lit plugin instance
|