@ecopages/lit 0.2.0-rc.4 → 0.2.0-rc.6
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 +7 -1
- package/package.json +6 -3
- package/src/console.d.ts +1 -0
- package/src/dom-shim.d.ts +2 -0
- package/src/dom-shim.js +14 -10
- package/src/lit-renderer.js +2 -4
- package/src/lit-server-module-loader.d.ts +6 -0
- package/src/lit-server-module-loader.js +18 -0
- package/src/lit-ssr-lazy-preloader.d.ts +4 -1
- package/src/lit-ssr-lazy-preloader.js +36 -7
- package/src/lit-static-render-session.d.ts +6 -0
- package/src/lit-static-render-session.js +19 -1
- package/src/lit.plugin.js +3 -0
package/README.md
CHANGED
|
@@ -55,6 +55,12 @@ When a non-Lit render pass reaches a Lit-owned foreign child, Ecopages hands tha
|
|
|
55
55
|
|
|
56
56
|
Important:
|
|
57
57
|
|
|
58
|
-
- Components
|
|
58
|
+
- Direct local imports of declared Eco Components contribute their Dependencies automatically, including Foreign Children and named `export { X } from` barrels. Use `dependencies.components` for `export *` barrels and package imports.
|
|
59
59
|
- Ecopages validates ownership from declared dependencies during render preparation instead of relying on post-render HTML discovery.
|
|
60
60
|
- Lit keeps slot transport, shadow-root handling, and SSR preload behavior inside the Lit renderer.
|
|
61
|
+
|
|
62
|
+
## Discovered assets and lazy custom elements
|
|
63
|
+
|
|
64
|
+
A Lit Component can import a relative stylesheet with `import './counter.css'`; core extracts it into the shared asset pipeline. Direct local imports of that Component make its Dependencies available to the Lit SSR preloader, including through Foreign Child rendering.
|
|
65
|
+
|
|
66
|
+
Keep custom-element registration explicit with `scripts: [{ src: './counter.script.ts', ssr: true, lazy: { 'on:visible': true } }]`. The module executes before server rendering and loads in the browser on visibility. `ssr: true` no longer emits an additional eager browser script. Omit `lazy` when eager browser loading is intended. SSR preload artifacts are excluded from HTML, and hydration support must be available before the browser registers the element.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecopages/lit",
|
|
3
|
-
"version": "0.2.0-rc.
|
|
3
|
+
"version": "0.2.0-rc.6",
|
|
4
4
|
"description": "Ecopages lit integration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ecopages",
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"main": "./src/lit.plugin.js",
|
|
12
12
|
"types": "./src/lit.plugin.d.ts",
|
|
13
13
|
"type": "module",
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
14
17
|
"exports": {
|
|
15
18
|
".": {
|
|
16
19
|
"default": "./src/lit.plugin.js",
|
|
@@ -39,9 +42,9 @@
|
|
|
39
42
|
"directory": "packages/integrations/lit"
|
|
40
43
|
},
|
|
41
44
|
"peerDependencies": {
|
|
42
|
-
"@ecopages/core": "0.2.0-rc.
|
|
45
|
+
"@ecopages/core": "0.2.0-rc.6",
|
|
43
46
|
"@lit-labs/ssr": "^3.3.0",
|
|
44
47
|
"@lit-labs/ssr-client": "^1.1.7",
|
|
45
48
|
"lit": "^3.2.1"
|
|
46
49
|
}
|
|
47
|
-
}
|
|
50
|
+
}
|
package/src/console.d.ts
CHANGED
package/src/dom-shim.d.ts
CHANGED
package/src/dom-shim.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { installWindowOnGlobal } from '@lit-labs/ssr/lib/dom-shim.js';
|
|
2
2
|
import '@lit-labs/ssr/lib/install-global-dom-shim.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
/** Repairs the minimal Lit DOM surface after another Integration installs its SSR globals. */
|
|
4
|
+
export function ensureLitDomShim() {
|
|
5
|
+
if (typeof globalThis.document?.createTreeWalker !== 'function') {
|
|
6
|
+
if (typeof globalThis.window === 'undefined') {
|
|
7
|
+
installWindowOnGlobal();
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
Object.defineProperty(globalThis.document, 'createTreeWalker', {
|
|
11
|
+
configurable: true,
|
|
12
|
+
writable: true,
|
|
13
|
+
value: () => ({}),
|
|
14
|
+
});
|
|
15
|
+
}
|
|
13
16
|
}
|
|
14
17
|
}
|
|
18
|
+
ensureLitDomShim();
|
package/src/lit-renderer.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* This module contains the Lit renderer
|
|
3
|
-
* @module
|
|
4
|
-
*/
|
|
1
|
+
import { createLitServerModuleImporter } from './lit-server-module-loader.js';
|
|
5
2
|
import './dom-shim.js';
|
|
6
3
|
import { IntegrationRenderer, } from '@ecopages/core/route-renderer/orchestration/integration-renderer';
|
|
7
4
|
import { resolveDocumentShellLayouts } from '@ecopages/core/route-renderer/orchestration/document-shell/layout-shell-props.service';
|
|
@@ -137,6 +134,7 @@ export class LitRenderer extends IntegrationRenderer {
|
|
|
137
134
|
});
|
|
138
135
|
}
|
|
139
136
|
ssrLazyPreloader = new LitSsrLazyPreloader({
|
|
137
|
+
importServerModule: createLitServerModuleImporter(this.appConfig),
|
|
140
138
|
resolveDependencyPath: this.resolveDependencyPath.bind(this),
|
|
141
139
|
processDependencies: this.assetProcessingService?.processDependencies?.bind(this.assetProcessingService),
|
|
142
140
|
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { EcoPagesAppConfig } from '@ecopages/core';
|
|
2
|
+
/**
|
|
3
|
+
* Uses server export conditions and isolates module evaluation by custom-element
|
|
4
|
+
* registry. Browser asset output must never be evaluated as the SSR implementation.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createLitServerModuleImporter(appConfig: EcoPagesAppConfig): (filePath: string, registryKey: string) => Promise<unknown>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
/**
|
|
3
|
+
* Uses server export conditions and isolates module evaluation by custom-element
|
|
4
|
+
* registry. Browser asset output must never be evaluated as the SSR implementation.
|
|
5
|
+
*/
|
|
6
|
+
export function createLitServerModuleImporter(appConfig) {
|
|
7
|
+
return async (filePath, registryKey) => {
|
|
8
|
+
const loader = appConfig.runtime?.appModuleLoader;
|
|
9
|
+
if (!loader)
|
|
10
|
+
throw new Error('Lit SSR requires the app server module loader');
|
|
11
|
+
return loader.importModule({
|
|
12
|
+
filePath,
|
|
13
|
+
rootDir: appConfig.rootDir,
|
|
14
|
+
outdir: path.join(appConfig.absolutePaths.workDir, '.lit-ssr', registryKey),
|
|
15
|
+
forceBuild: true,
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -7,6 +7,7 @@ export interface LitSsrLazyPreloaderOptions {
|
|
|
7
7
|
resolveDependencyPath: (componentDir: string, sourcePath: string) => string;
|
|
8
8
|
processDependencies?: ProcessDependencies;
|
|
9
9
|
preferSourceImports?: boolean;
|
|
10
|
+
importServerModule?: (scriptPath: string, registryKey: string) => Promise<unknown>;
|
|
10
11
|
}
|
|
11
12
|
/**
|
|
12
13
|
* Encapsulates SSR lazy script preload behavior for Lit components.
|
|
@@ -19,10 +20,12 @@ export declare class LitSsrLazyPreloader {
|
|
|
19
20
|
private readonly resolveDependencyPath;
|
|
20
21
|
private readonly processDependencies?;
|
|
21
22
|
private readonly preferSourceImports;
|
|
23
|
+
private readonly importServerModule?;
|
|
24
|
+
private activeRegistry;
|
|
22
25
|
private readonly ssrPreloadedScripts;
|
|
23
26
|
private readonly ssrPreloadFailedScripts;
|
|
24
27
|
private readonly ssrPreloadEntrypointCache;
|
|
25
|
-
constructor({ resolveDependencyPath, processDependencies, preferSourceImports }: LitSsrLazyPreloaderOptions);
|
|
28
|
+
constructor({ resolveDependencyPath, processDependencies, preferSourceImports, importServerModule, }: LitSsrLazyPreloaderOptions);
|
|
26
29
|
private getErrorCode;
|
|
27
30
|
/**
|
|
28
31
|
* Detects preload failures that are expected for browser-only modules.
|
|
@@ -1,7 +1,18 @@
|
|
|
1
|
+
import { ensureLitDomShim } from './dom-shim.js';
|
|
1
2
|
import { getComponentIdentity } from '@ecopages/core';
|
|
2
3
|
import { AssetFactory } from '@ecopages/core/services/asset-processing-service';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
7
|
+
const registryKeys = new WeakMap();
|
|
8
|
+
function getRegistryKey(registry) {
|
|
9
|
+
let key = registryKeys.get(registry);
|
|
10
|
+
if (!key) {
|
|
11
|
+
key = randomUUID();
|
|
12
|
+
registryKeys.set(registry, key);
|
|
13
|
+
}
|
|
14
|
+
return key;
|
|
15
|
+
}
|
|
5
16
|
/**
|
|
6
17
|
* Encapsulates SSR lazy script preload behavior for Lit components.
|
|
7
18
|
*
|
|
@@ -13,10 +24,13 @@ export class LitSsrLazyPreloader {
|
|
|
13
24
|
resolveDependencyPath;
|
|
14
25
|
processDependencies;
|
|
15
26
|
preferSourceImports;
|
|
27
|
+
importServerModule;
|
|
28
|
+
activeRegistry;
|
|
16
29
|
ssrPreloadedScripts = new Set();
|
|
17
30
|
ssrPreloadFailedScripts = new Set();
|
|
18
31
|
ssrPreloadEntrypointCache = new Map();
|
|
19
|
-
constructor({ resolveDependencyPath, processDependencies, preferSourceImports }) {
|
|
32
|
+
constructor({ resolveDependencyPath, processDependencies, preferSourceImports, importServerModule, }) {
|
|
33
|
+
this.importServerModule = importServerModule;
|
|
20
34
|
this.resolveDependencyPath = resolveDependencyPath;
|
|
21
35
|
this.processDependencies = processDependencies;
|
|
22
36
|
this.preferSourceImports = preferSourceImports ?? typeof Bun !== 'undefined';
|
|
@@ -87,6 +101,13 @@ export class LitSsrLazyPreloader {
|
|
|
87
101
|
* Preloads SSR-eligible lazy scripts to register custom elements before render.
|
|
88
102
|
*/
|
|
89
103
|
async preloadSsrLazyScripts(components) {
|
|
104
|
+
ensureLitDomShim();
|
|
105
|
+
const registry = globalThis.customElements;
|
|
106
|
+
if (this.activeRegistry !== registry) {
|
|
107
|
+
this.ssrPreloadedScripts.clear();
|
|
108
|
+
this.ssrPreloadFailedScripts.clear();
|
|
109
|
+
this.activeRegistry = registry;
|
|
110
|
+
}
|
|
90
111
|
const scripts = this.collectSsrPreloadScripts(components);
|
|
91
112
|
if (scripts.length === 0) {
|
|
92
113
|
return;
|
|
@@ -102,13 +123,20 @@ export class LitSsrLazyPreloader {
|
|
|
102
123
|
return true;
|
|
103
124
|
})
|
|
104
125
|
.map(async (scriptPath) => {
|
|
105
|
-
const preloadEntrypoint = await this.resolveSsrPreloadEntrypoint(scriptPath);
|
|
106
|
-
if (!preloadEntrypoint) {
|
|
107
|
-
this.ssrPreloadFailedScripts.add(scriptPath);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
126
|
try {
|
|
111
|
-
|
|
127
|
+
const registryKey = registry ? getRegistryKey(registry) : 'default';
|
|
128
|
+
if (this.importServerModule) {
|
|
129
|
+
await this.importServerModule(scriptPath, registryKey);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
const preloadEntrypoint = await this.resolveSsrPreloadEntrypoint(scriptPath);
|
|
133
|
+
if (!preloadEntrypoint)
|
|
134
|
+
throw new Error(`Cannot resolve SSR entry ${scriptPath}`);
|
|
135
|
+
const importUrl = pathToFileURL(preloadEntrypoint);
|
|
136
|
+
if (registry && typeof Bun === 'undefined')
|
|
137
|
+
importUrl.searchParams.set('eco-ssr-registry', registryKey);
|
|
138
|
+
await import(/* @vite-ignore */ importUrl.href);
|
|
139
|
+
}
|
|
112
140
|
this.ssrPreloadedScripts.add(scriptPath);
|
|
113
141
|
}
|
|
114
142
|
catch (error) {
|
|
@@ -143,6 +171,7 @@ export class LitSsrLazyPreloader {
|
|
|
143
171
|
const processed = await this.processDependencies([
|
|
144
172
|
AssetFactory.createInlineFileScript({
|
|
145
173
|
filepath: scriptPath,
|
|
174
|
+
excludeFromHtml: true,
|
|
146
175
|
position: 'head',
|
|
147
176
|
bundle: true,
|
|
148
177
|
attributes: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EcoComponent, PageParams, PageQuery } from '@ecopages/core';
|
|
2
2
|
import type { StaticExportContext } from '@ecopages/core/plugins/integration-plugin';
|
|
3
3
|
import type { AssetDefinition } from '@ecopages/core/services/asset-processing-service';
|
|
4
|
+
import { type LitSsrLazyPreloaderOptions } from './lit-ssr-lazy-preloader.js';
|
|
4
5
|
import { LitStaticRenderWorkerClient } from './lit-static-render-worker-client.js';
|
|
5
6
|
import type { LitStaticRenderCacheStrategy } from './lit-static-render-protocol.js';
|
|
6
7
|
type LitStaticRenderWorkerClientContract = Pick<LitStaticRenderWorkerClient, 'start' | 'renderPage' | 'dispose'>;
|
|
@@ -10,6 +11,8 @@ type LitStaticRenderSessionOptions = {
|
|
|
10
11
|
filepath?: string;
|
|
11
12
|
}>>;
|
|
12
13
|
preferSourceImports?: boolean;
|
|
14
|
+
importServerModule?: LitSsrLazyPreloaderOptions['importServerModule'];
|
|
15
|
+
getInvalidationVersion?: () => number;
|
|
13
16
|
createWorkerClient?: (input: {
|
|
14
17
|
configModulePath: string;
|
|
15
18
|
runtimeOrigin: string;
|
|
@@ -32,6 +35,9 @@ export declare class LitStaticRenderSession {
|
|
|
32
35
|
private readonly createWorkerClient;
|
|
33
36
|
private workerClient;
|
|
34
37
|
private workerIdentity;
|
|
38
|
+
private readonly getInvalidationVersion;
|
|
39
|
+
private workerInvalidationVersion;
|
|
40
|
+
private renderChain;
|
|
35
41
|
constructor(options: LitStaticRenderSessionOptions);
|
|
36
42
|
/**
|
|
37
43
|
* Ensures the static render worker is running for the given identity.
|
|
@@ -19,11 +19,16 @@ export class LitStaticRenderSession {
|
|
|
19
19
|
createWorkerClient;
|
|
20
20
|
workerClient = null;
|
|
21
21
|
workerIdentity = null;
|
|
22
|
+
getInvalidationVersion;
|
|
23
|
+
workerInvalidationVersion = 0;
|
|
24
|
+
renderChain = Promise.resolve();
|
|
22
25
|
constructor(options) {
|
|
26
|
+
this.getInvalidationVersion = options.getInvalidationVersion ?? (() => 0);
|
|
23
27
|
this.preloader = new LitSsrLazyPreloader({
|
|
24
28
|
resolveDependencyPath: options.resolveDependencyPath,
|
|
25
29
|
processDependencies: options.processDependencies,
|
|
26
30
|
preferSourceImports: options.preferSourceImports,
|
|
31
|
+
importServerModule: options.importServerModule,
|
|
27
32
|
});
|
|
28
33
|
this.createWorkerClient =
|
|
29
34
|
options.createWorkerClient ??
|
|
@@ -58,6 +63,7 @@ export class LitStaticRenderSession {
|
|
|
58
63
|
runtimeOrigin: input.runtimeOrigin,
|
|
59
64
|
});
|
|
60
65
|
await this.workerClient.start();
|
|
66
|
+
this.workerInvalidationVersion = this.getInvalidationVersion();
|
|
61
67
|
}
|
|
62
68
|
async preloadStaticRoutes(context) {
|
|
63
69
|
await this.preloadLitRoutes(context);
|
|
@@ -72,7 +78,19 @@ export class LitStaticRenderSession {
|
|
|
72
78
|
if (!this.workerClient) {
|
|
73
79
|
throw new Error('Lit static render worker is not active');
|
|
74
80
|
}
|
|
75
|
-
|
|
81
|
+
const render = this.renderChain.then(async () => {
|
|
82
|
+
const version = this.getInvalidationVersion();
|
|
83
|
+
if (version !== this.workerInvalidationVersion && this.workerIdentity) {
|
|
84
|
+
const identity = this.workerIdentity;
|
|
85
|
+
await this.dispose();
|
|
86
|
+
await this.ensureWorker(identity);
|
|
87
|
+
}
|
|
88
|
+
if (!this.workerClient)
|
|
89
|
+
throw new Error('Lit static render worker is not active');
|
|
90
|
+
return this.workerClient.renderPage(input);
|
|
91
|
+
});
|
|
92
|
+
this.renderChain = render.then(() => undefined, () => undefined);
|
|
93
|
+
return render;
|
|
76
94
|
}
|
|
77
95
|
isExpectedSsrPreloadError(error) {
|
|
78
96
|
return this.preloader.isExpectedSsrPreloadError(error);
|
package/src/lit.plugin.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createLitServerModuleImporter } from './lit-server-module-loader.js';
|
|
1
2
|
/**
|
|
2
3
|
* This module contains the Lit plugin
|
|
3
4
|
* @module
|
|
@@ -66,6 +67,8 @@ export class LitPlugin extends IntegrationPlugin {
|
|
|
66
67
|
return new LitStaticRenderSession({
|
|
67
68
|
resolveDependencyPath: (componentDir, sourcePath) => path.join(componentDir, sourcePath),
|
|
68
69
|
processDependencies: this.assetProcessingService.processDependencies.bind(this.assetProcessingService),
|
|
70
|
+
getInvalidationVersion: () => this.appConfig?.runtime?.serverInvalidationState?.getServerInvalidationVersion() ?? 0,
|
|
71
|
+
importServerModule: createLitServerModuleImporter(this.appConfig),
|
|
69
72
|
preferSourceImports: typeof Bun !== 'undefined',
|
|
70
73
|
});
|
|
71
74
|
}
|