@ecopages/lit 0.2.0-rc.1 → 0.2.0-rc.10
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 +17 -2
- package/package.json +6 -3
- package/src/STATIC_RENDER.md +2 -0
- package/src/console.d.ts +1 -0
- package/src/dom-shim.d.ts +2 -0
- package/src/dom-shim.js +40 -2
- package/src/lit-element-hydrate.d.ts +10 -0
- package/src/lit-element-hydrate.js +10 -0
- package/src/lit-renderer.d.ts +15 -15
- package/src/lit-renderer.js +33 -28
- package/src/lit-ssr-preload-entrypoint.d.ts +1 -0
- package/src/lit-ssr-preload-entrypoint.js +1 -0
- package/src/lit-static-render-session.d.ts +11 -7
- package/src/lit-static-render-session.js +36 -8
- package/src/lit-static-render-worker.js +2 -3
- package/src/lit.plugin.d.ts +12 -0
- package/src/lit.plugin.js +26 -12
- package/src/lit-ssr-lazy-preloader.d.ts +0 -44
- package/src/lit-ssr-lazy-preloader.js +0 -169
package/README.md
CHANGED
|
@@ -47,7 +47,15 @@ This setup lets Kita own the page shell while Lit owns the nested Lit component
|
|
|
47
47
|
|
|
48
48
|
- `.lit.tsx` route files.
|
|
49
49
|
- Nested Lit component boundaries rendered inside pages owned by other integrations.
|
|
50
|
-
- The Lit hydration support script required for SSR custom elements and declarative shadow DOM.
|
|
50
|
+
- The Lit hydration support script required for SSR custom elements and declarative shadow DOM. Injected synchronously into `document.head` so `globalThis.litElementHydrateSupport` is available before any custom element connects.
|
|
51
|
+
|
|
52
|
+
## Hydration Architecture
|
|
53
|
+
|
|
54
|
+
Lit component SSR reuses existing declarative shadow roots rather than replacing host nodes. When hydrated:
|
|
55
|
+
|
|
56
|
+
1. The server renders declarative shadow roots (`<template shadowrootmode="open">`) and stamps `defer-hydration` on nodes needing client hydration.
|
|
57
|
+
2. The global `lit-hydrate-support` script initializes `globalThis.litElementHydrateSupport` before custom element definitions execute.
|
|
58
|
+
3. Custom elements register via their client scripts, remove `defer-hydration`, and perform in-place hydration against the preserved SSR shadow roots.
|
|
51
59
|
|
|
52
60
|
## Mixed Rendering
|
|
53
61
|
|
|
@@ -55,6 +63,13 @@ When a non-Lit render pass reaches a Lit-owned foreign child, Ecopages hands tha
|
|
|
55
63
|
|
|
56
64
|
Important:
|
|
57
65
|
|
|
58
|
-
- Components
|
|
66
|
+
- 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
67
|
- Ecopages validates ownership from declared dependencies during render preparation instead of relying on post-render HTML discovery.
|
|
68
|
+
- Route-level dependency resolvers can supply Components at request time; Lit includes those resolved roots when deciding whether a Foreign Subtree needs handoff.
|
|
60
69
|
- Lit keeps slot transport, shadow-root handling, and SSR preload behavior inside the Lit renderer.
|
|
70
|
+
|
|
71
|
+
## Discovered assets and lazy custom elements
|
|
72
|
+
|
|
73
|
+
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.
|
|
74
|
+
|
|
75
|
+
Keep custom-element registration explicit with `scripts: [{ src: './counter.script.ts', ssr: true, lazy: { 'on:visible': true } }]`. Entries with `ssr: true` execute on the server before rendering (eager or lazy) and load in the browser according to `lazy`. `ssr: true` does not emit 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. Lit-owned page routes preload those scripts in the static-render worker through the app module loader so `@lit-labs/ssr` sees the registered custom 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.10",
|
|
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.10",
|
|
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/STATIC_RENDER.md
CHANGED
|
@@ -24,6 +24,8 @@ LitRenderer.execute() (main)
|
|
|
24
24
|
└─ session.renderPageInWorker()
|
|
25
25
|
└─ postMessage(render) ─────► RouteRendererFactory.execute()
|
|
26
26
|
(no active session in worker)
|
|
27
|
+
LitRenderer.ssrScriptPreloader uses
|
|
28
|
+
importServerModule (no entrypoint resolver)
|
|
27
29
|
|
|
28
30
|
beforeStaticExport
|
|
29
31
|
└─ ensureWorker() only when `renderSession` is still null
|
package/src/console.d.ts
CHANGED
package/src/dom-shim.d.ts
CHANGED
package/src/dom-shim.js
CHANGED
|
@@ -1,14 +1,52 @@
|
|
|
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
|
-
|
|
3
|
+
/** Repairs the minimal Lit DOM surface after another Integration installs its SSR globals. */
|
|
4
|
+
export function ensureLitDomShim() {
|
|
4
5
|
if (typeof globalThis.window === 'undefined') {
|
|
5
6
|
installWindowOnGlobal();
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
+
if (typeof globalThis.document !== 'undefined' && typeof globalThis.document.createTreeWalker !== 'function') {
|
|
8
9
|
Object.defineProperty(globalThis.document, 'createTreeWalker', {
|
|
9
10
|
configurable: true,
|
|
10
11
|
writable: true,
|
|
11
12
|
value: () => ({}),
|
|
12
13
|
});
|
|
13
14
|
}
|
|
15
|
+
for (const target of [globalThis.Element, globalThis.HTMLElement]) {
|
|
16
|
+
if (typeof target === 'undefined' || !target?.prototype) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (!('attributes' in target.prototype)) {
|
|
20
|
+
Object.defineProperty(target.prototype, 'attributes', {
|
|
21
|
+
configurable: true,
|
|
22
|
+
enumerable: true,
|
|
23
|
+
get() {
|
|
24
|
+
if (typeof this.getAttributeNames === 'function') {
|
|
25
|
+
return this.getAttributeNames().map((name) => ({
|
|
26
|
+
name,
|
|
27
|
+
value: this.getAttribute(name),
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
return [];
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if (typeof target.prototype.attachShadow !== 'function') {
|
|
35
|
+
Object.defineProperty(target.prototype, 'attachShadow', {
|
|
36
|
+
configurable: true,
|
|
37
|
+
writable: true,
|
|
38
|
+
value: function attachShadow(init = { mode: 'open' }) {
|
|
39
|
+
const shadowRoot = {
|
|
40
|
+
mode: init?.mode ?? 'open',
|
|
41
|
+
host: this,
|
|
42
|
+
children: [],
|
|
43
|
+
childNodes: [],
|
|
44
|
+
};
|
|
45
|
+
this.shadowRoot = shadowRoot;
|
|
46
|
+
return shadowRoot;
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
14
51
|
}
|
|
52
|
+
ensureLitDomShim();
|
|
@@ -1 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-bundled Lit SSR hydration client runtime.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Contains the compiled `@lit-labs/ssr-client/lit-element-hydrate-support.js`
|
|
6
|
+
* and `@lit-labs/ssr-client/lib/hydrate-lit-html.js` runtime. It defines
|
|
7
|
+
* `globalThis.litElementHydrateSupport`, which installs the `defer-hydration`
|
|
8
|
+
* observer and hooks into `LitElement.prototype.update` to hydrate existing
|
|
9
|
+
* server-rendered shadow roots instead of replacing them.
|
|
10
|
+
*/
|
|
1
11
|
export declare const litElementHydrateScript = "var k=globalThis,y=k.trustedTypes,p=y?y.createPolicy(\"lit-html\",{createHTML:(q)=>q}):void 0;var P=`lit$${Math.random().toFixed(9).slice(2)}$`,v=\"?\"+P,Kq=`<${v}>`,x=document,C=()=>x.createComment(\"\"),S=(q)=>q===null||typeof q!=\"object\"&&typeof q!=\"function\",L=Array.isArray,h=(q)=>L(q)||typeof q?.[Symbol.iterator]==\"function\";var U=/<(?:(!--|\\/[^a-zA-Z])|(\\/?[a-zA-Z][^>\\s]*)|(\\/?$))/g,m=/-->/g,a=/>/g,$=/>|[ \\t\\n\\f\\r](?:([^\\s\"'>=\\/]+)([ \\t\\n\\f\\r]*=[ \\t\\n\\f\\r]*(?:[^ \\t\\n\\f\\r\"'\\`<>=]|(\"|')|))|$)/g,c=/'/g,l=/\"/g,r=/^(?:script|style|textarea|title)$/i,M=(q)=>(D,...F)=>({_$litType$:q,strings:D,values:F}),Aq=M(1),Pq=M(2),Vq=M(3),V=Symbol.for(\"lit-noChange\"),B=Symbol.for(\"lit-nothing\"),n=new WeakMap,w=x.createTreeWalker(x,129);function o(q,D){if(!L(q)||!q.hasOwnProperty(\"raw\"))throw Error(\"invalid template strings array\");return p!==void 0?p.createHTML(D):D}var e=(q,D)=>{let F=q.length-1,J=[],G,O=D===2?\"<svg>\":D===3?\"<math>\":\"\",K=U;for(let W=0;W<F;W++){let Q=q[W],Y,X,b=-1,A=0;while(A<Q.length&&(K.lastIndex=A,X=K.exec(Q),X!==null))A=K.lastIndex,K===U?X[1]===\"!--\"?K=m:X[1]!==void 0?K=a:X[2]!==void 0?(r.test(X[2])&&(G=RegExp(\"</\"+X[2],\"g\")),K=$):X[3]!==void 0&&(K=$):K===$?X[0]===\">\"?(K=G??U,b=-1):X[1]===void 0?b=-2:(b=K.lastIndex-X[2].length,Y=X[1],K=X[3]===void 0?$:X[3]==='\"'?l:c):K===l||K===c?K=$:K===m||K===a?K=U:(K=$,G=void 0);const _=K===$&&q[W+1].startsWith(\"/>\")?\" \":\"\";O+=K===U?Q+Kq:b>=0?(J.push(Y),Q.slice(0,b)+\"$lit$\"+Q.slice(b)+P+_):Q+P+(b===-2?W:_)}return[o(q,O+(q[F]||\"<?>\")+(D===2?\"</svg>\":D===3?\"</math>\":\"\")),J]};class g{constructor({strings:q,_$litType$:D},F){let J;this.parts=[];let G=0,O=0,K=q.length-1,W=this.parts,[Q,Y]=e(q,D);if(this.el=g.createElement(Q,F),w.currentNode=this.el.content,D===2||D===3){const X=this.el.content.firstChild;X.replaceWith(...X.childNodes)}while((J=w.nextNode())!==null&&W.length<K){if(J.nodeType===1){if(J.hasAttributes())for(const X of J.getAttributeNames())if(X.endsWith(\"$lit$\")){const b=Y[O++],A=J.getAttribute(X).split(P),_=/([.?@])?(.*)/.exec(b);W.push({type:1,index:G,name:_[2],strings:A,ctor:_[1]===\".\"?R:_[1]===\"?\"?E:_[1]===\"@\"?d:I}),J.removeAttribute(X)}else X.startsWith(P)&&(W.push({type:6,index:G}),J.removeAttribute(X));if(r.test(J.tagName)){const X=J.textContent.split(P),b=X.length-1;if(b>0){J.textContent=y?y.emptyScript:\"\";for(let A=0;A<b;A++)J.append(X[A],C()),w.nextNode(),W.push({type:2,index:++G});J.append(X[b],C())}}}else if(J.nodeType===8)if(J.data===v)W.push({type:2,index:G});else{let X=-1;while((X=J.data.indexOf(P,X+1))!==-1)W.push({type:7,index:G}),X+=P.length-1}G++}}static createElement(q,D){const F=x.createElement(\"template\");return F.innerHTML=q,F}}function z(q,D,F=q,J){if(D===V)return D;let G=J!==void 0?F._$Co?.[J]:F._$Cl,O=S(D)?void 0:D._$litDirective$;return G?.constructor!==O&&(G?._$AO?.(!1),O===void 0?G=void 0:(G=new O(q),G._$AT(q,F,J)),J!==void 0?(F._$Co??=[])[J]=G:F._$Cl=G),G!==void 0&&(D=z(q,G._$AS(q,D.values),G,J)),D}class j{constructor(q,D){this._$AV=[],this._$AN=void 0,this._$AD=q,this._$AM=D}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(q){const{el:{content:D},parts:F}=this._$AD,J=(q?.creationScope??x).importNode(D,!0);w.currentNode=J;let G=w.nextNode(),O=0,K=0,W=F[0];while(W!==void 0){if(O===W.index){let Q;W.type===2?Q=new H(G,G.nextSibling,this,q):W.type===1?Q=new W.ctor(G,W.name,W.strings,this,q):W.type===6&&(Q=new f(G,this,q)),this._$AV.push(Q),W=F[++K]}O!==W?.index&&(G=w.nextNode(),O++)}return w.currentNode=x,J}p(q){let D=0;for(const F of this._$AV)F!==void 0&&(F.strings!==void 0?(F._$AI(q,F,D),D+=F.strings.length-2):F._$AI(q[D])),D++}}class H{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(q,D,F,J){this.type=2,this._$AH=B,this._$AN=void 0,this._$AA=q,this._$AB=D,this._$AM=F,this.options=J,this._$Cv=J?.isConnected??!0}get parentNode(){let q=this._$AA.parentNode,D=this._$AM;return D!==void 0&&q?.nodeType===11&&(q=D.parentNode),q}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(q,D=this){q=z(this,q,D),S(q)?q===B||q==null||q===\"\"?(this._$AH!==B&&this._$AR(),this._$AH=B):q!==this._$AH&&q!==V&&this._(q):q._$litType$!==void 0?this.$(q):q.nodeType!==void 0?this.T(q):h(q)?this.k(q):this._(q)}O(q){return this._$AA.parentNode.insertBefore(q,this._$AB)}T(q){this._$AH!==q&&(this._$AR(),this._$AH=this.O(q))}_(q){this._$AH!==B&&S(this._$AH)?this._$AA.nextSibling.data=q:this.T(x.createTextNode(q)),this._$AH=q}$(q){const{values:D,_$litType$:F}=q,J=typeof F==\"number\"?this._$AC(q):(F.el===void 0&&(F.el=g.createElement(o(F.h,F.h[0]),this.options)),F);if(this._$AH?._$AD===J)this._$AH.p(D);else{const G=new j(J,this),O=G.u(this.options);G.p(D),this.T(O),this._$AH=G}}_$AC(q){let D=n.get(q.strings);return D===void 0&&n.set(q.strings,D=new g(q)),D}k(q){L(this._$AH)||(this._$AH=[],this._$AR());let D=this._$AH,F,J=0;for(const G of q)J===D.length?D.push(F=new H(this.O(C()),this.O(C()),this,this.options)):F=D[J],F._$AI(G),J++;J<D.length&&(this._$AR(F&&F._$AB.nextSibling,J),D.length=J)}_$AR(q=this._$AA.nextSibling,D){for(this._$AP?.(!1,!0,D);q&&q!==this._$AB;){const F=q.nextSibling;q.remove(),q=F}}setConnected(q){this._$AM===void 0&&(this._$Cv=q,this._$AP?.(q))}}class I{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(q,D,F,J,G){this.type=1,this._$AH=B,this._$AN=void 0,this.element=q,this.name=D,this._$AM=J,this.options=G,F.length>2||F[0]!==\"\"||F[1]!==\"\"?(this._$AH=Array(F.length-1).fill(new String),this.strings=F):this._$AH=B}_$AI(q,D=this,F,J){let G=this.strings,O=!1;if(G===void 0)q=z(this,q,D,0),O=!S(q)||q!==this._$AH&&q!==V,O&&(this._$AH=q);else{let K=q,W,Q;for(q=G[0],W=0;W<G.length-1;W++)Q=z(this,K[F+W],D,W),Q===V&&(Q=this._$AH[W]),O||=!S(Q)||Q!==this._$AH[W],Q===B?q=B:q!==B&&(q+=(Q??\"\")+G[W+1]),this._$AH[W]=Q}O&&!J&&this.j(q)}j(q){q===B?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,q??\"\")}}class R extends I{constructor(){super(...arguments),this.type=3}j(q){this.element[this.name]=q===B?void 0:q}}class E extends I{constructor(){super(...arguments),this.type=4}j(q){this.element.toggleAttribute(this.name,!!q&&q!==B)}}class d extends I{constructor(q,D,F,J,G){super(q,D,F,J,G),this.type=5}_$AI(q,D=this){if((q=z(this,q,D,0)??B)===V)return;const F=this._$AH,J=q===B&&F!==B||q.capture!==F.capture||q.once!==F.once||q.passive!==F.passive,G=q!==B&&(F===B||J);J&&this.element.removeEventListener(this.name,this,F),G&&this.element.addEventListener(this.name,this,q),this._$AH=q}handleEvent(q){typeof this._$AH==\"function\"?this._$AH.call(this.options?.host??this.element,q):this._$AH.handleEvent(q)}}class f{constructor(q,D,F){this.element=q,this.type=6,this._$AN=void 0,this._$AM=D,this.options=F}get _$AU(){return this._$AM._$AU}_$AI(q){z(this,q)}}var Z={M:\"$lit$\",P,A:v,C:1,L:e,R:j,D:h,V:z,I:H,H:I,N:E,U:d,B:R,F:f},Oq=k.litHtmlPolyfillSupport;Oq?.(g,H),(k.litHtmlVersions??=[]).push(\"3.2.1\");var i=(q,D,F)=>{let J=F?.renderBefore??D,G=J._$litPart$;if(G===void 0){const O=F?.renderBefore??null;J._$litPart$=G=new H(D.insertBefore(C(),O),O,void 0,F??{})}return G._$AI(q),G};var u=null,s={boundAttributeSuffix:Z.M,marker:Z.P,markerMatch:Z.A,HTML_RESULT:Z.C,getTemplateHtml:Z.L,overrideDirectiveResolve:(q,D)=>class extends q{_$AS(F,J){return D(this,J)}},patchDirectiveResolve:(q,D)=>{if(q.prototype._$AS!==D){u??=q.prototype._$AS.name;for(let F=q.prototype;F!==Object.prototype;F=Object.getPrototypeOf(F))if(F.hasOwnProperty(u))return void(F[u]=D);throw Error(\"Internal error: It is possible that both dev mode and production mode Lit was mixed together during SSR. Please comment on the issue: https://github.com/lit/lit/issues/4527\")}},setDirectiveClass(q,D){q._$litDirective$=D},getAttributePartCommittedValue:(q,D,F)=>{let J=V;return q.j=(G)=>J=G,q._$AI(D,q,F),J},connectedDisconnectable:(q)=>({...q,_$AU:!0}),resolveDirective:Z.V,AttributePart:Z.H,PropertyPart:Z.B,BooleanAttributePart:Z.N,EventPart:Z.U,ElementPart:Z.F,TemplateInstance:Z.R,isIterable:Z.D,ChildPart:Z.I};var T={ATTRIBUTE:1,CHILD:2,PROPERTY:3,BOOLEAN_ATTRIBUTE:4,EVENT:5,ELEMENT:6};var t=(q)=>q===null||typeof q!=\"object\"&&typeof q!=\"function\";var qq=(q,D)=>D===void 0?q?._$litType$!==void 0:q?._$litType$===D,Dq=(q)=>q?._$litType$?.h!=null;var Fq=(q)=>q.strings===void 0;var{TemplateInstance:Qq,isIterable:Wq,resolveDirective:Gq,ChildPart:N,ElementPart:Xq}=s,Jq=(q,D,F={})=>{if(D._$litPart$!==void 0)throw Error(\"container already contains a live render\");let J,G,O,K=[],W=document.createTreeWalker(D,NodeFilter.SHOW_COMMENT),Q;while((Q=W.nextNode())!==null){const Y=Q.data;if(Y.startsWith(\"lit-part\")){if(K.length===0&&J!==void 0)throw Error(`There must be only one root part per container. Found a part marker (${Q}) when we already have a root part marker (${G})`);O=Yq(q,Q,K,F),J===void 0&&(J=O),G??=Q}else if(Y.startsWith(\"lit-node\"))Zq(Q,K,F);else if(Y.startsWith(\"/lit-part\")){if(K.length===1&&O!==J)throw Error(\"internal error\");O=Bq(Q,O,K)}}if(J===void 0){const Y=D instanceof ShadowRoot?\"{container.host.localName}'s shadow root\":D instanceof DocumentFragment?\"DocumentFragment\":D.localName;console.error(`There should be exactly one root part in a render container, but we didn't find any in ${Y}.`)}D._$litPart$=J},Yq=(q,D,F,J)=>{let G,O;if(F.length===0)O=new N(D,null,void 0,J),G=q;else{const K=F[F.length-1];if(K.type===\"template-instance\")O=new N(D,null,K.instance,J),K.instance._$AV.push(O),G=K.result.values[K.instancePartIndex++],K.templatePartIndex++;else if(K.type===\"iterable\"){O=new N(D,null,K.part,J);const W=K.iterator.next();if(W.done)throw G=void 0,K.done=!0,Error(\"Unhandled shorter than expected iterable\");G=W.value,K.part._$AH.push(O)}else O=new N(D,null,K.part,J)}if(G=Gq(O,G),G===V)F.push({part:O,type:\"leaf\"});else if(t(G))F.push({part:O,type:\"leaf\"}),O._$AH=G;else if(qq(G)){if(Dq(G))throw Error(\"compiled templates are not supported\");const K=\"lit-part \"+bq(G);if(D.data!==K)throw Error(\"Hydration value mismatch: Unexpected TemplateResult rendered to part\");{const W=N.prototype._$AC(G),Q=new Qq(W,O);F.push({type:\"template-instance\",instance:Q,part:O,templatePartIndex:0,instancePartIndex:0,result:G}),O._$AH=Q}}else Wq(G)?(F.push({part:O,type:\"iterable\",value:G,iterator:G[Symbol.iterator](),done:!1}),O._$AH=[]):(F.push({part:O,type:\"leaf\"}),O._$AH=G??\"\");return O},Bq=(q,D,F)=>{if(D===void 0)throw Error(\"unbalanced part marker\");D._$AB=q;const J=F.pop();if(J.type===\"iterable\"&&!J.iterator.next().done)throw Error(\"unexpected longer than expected iterable\");if(F.length>0)return F[F.length-1].part},Zq=(q,D,F)=>{const J=/lit-node (\\d+)/.exec(q.data),G=Number.parseInt(J[1]),O=q.nextElementSibling;if(O===null)throw Error(\"could not find node for attribute parts\");O.removeAttribute(\"defer-hydration\");const K=D[D.length-1];if(K.type!==\"template-instance\")throw Error(\"Hydration value mismatch: Primitive found where TemplateResult expected. This usually occurs due to conditional rendering that resulted in a different value or template being rendered between the server and client.\");{const W=K.instance;for(;;){const Q=W._$AD.parts[K.templatePartIndex];if(Q===void 0||Q.type!==T.ATTRIBUTE&&Q.type!==T.ELEMENT||Q.index!==G)break;if(Q.type===T.ATTRIBUTE){const Y=new Q.ctor(O,Q.name,Q.strings,K.instance,F),X=Fq(Y)?K.result.values[K.instancePartIndex]:K.result.values,b=!(Y.type===T.EVENT||Y.type===T.PROPERTY);Y._$AI(X,Y,K.instancePartIndex,b),K.instancePartIndex+=Q.strings.length-1,W._$AV.push(Y)}else{const Y=new Xq(O,K.instance,F);Gq(Y,K.result.values[K.instancePartIndex++]),W._$AV.push(Y)}K.templatePartIndex++}}},bq=(q)=>{const D=new Uint32Array(2).fill(5381);for(const J of q.strings)for(let G=0;G<J.length;G++)D[G%2]=33*D[G%2]^J.charCodeAt(G);const F=String.fromCharCode(...new Uint8Array(D.buffer));return btoa(F)};globalThis.litElementHydrateSupport=({LitElement:q})=>{const D=Object.getOwnPropertyDescriptor(Object.getPrototypeOf(q),\"observedAttributes\").get;Object.defineProperty(q,\"observedAttributes\",{get(){return[...D.call(this),\"defer-hydration\"]}});const F=q.prototype.attributeChangedCallback;q.prototype.attributeChangedCallback=function(K,W,Q){K===\"defer-hydration\"&&Q===null&&J.call(this),F.call(this,K,W,Q)};const J=q.prototype.connectedCallback;q.prototype.connectedCallback=function(){this.hasAttribute(\"defer-hydration\")||J.call(this)};const G=q.prototype.createRenderRoot;q.prototype.createRenderRoot=function(){return this.shadowRoot?(this._$AG=!0,this.shadowRoot):G.call(this)};const O=Object.getPrototypeOf(q.prototype).update;q.prototype.update=function(K){const W=this.render();if(O.call(this,K),this._$AG){this._$AG=!1;for(let Q=0;Q<this.attributes.length;Q++){const Y=this.attributes[Q];if(Y.name.startsWith(\"hydrate-internals-\")){const X=Y.name.slice(18);this.removeAttribute(X),this.removeAttribute(Y.name)}}Jq(W,this.renderRoot,this.renderOptions)}else i(W,this.renderRoot,this.renderOptions)}};";
|
|
@@ -1 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-bundled Lit SSR hydration client runtime.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Contains the compiled `@lit-labs/ssr-client/lit-element-hydrate-support.js`
|
|
6
|
+
* and `@lit-labs/ssr-client/lib/hydrate-lit-html.js` runtime. It defines
|
|
7
|
+
* `globalThis.litElementHydrateSupport`, which installs the `defer-hydration`
|
|
8
|
+
* observer and hooks into `LitElement.prototype.update` to hydrate existing
|
|
9
|
+
* server-rendered shadow roots instead of replacing them.
|
|
10
|
+
*/
|
|
1
11
|
export const litElementHydrateScript = 'var k=globalThis,y=k.trustedTypes,p=y?y.createPolicy("lit-html",{createHTML:(q)=>q}):void 0;var P=`lit$${Math.random().toFixed(9).slice(2)}$`,v="?"+P,Kq=`<${v}>`,x=document,C=()=>x.createComment(""),S=(q)=>q===null||typeof q!="object"&&typeof q!="function",L=Array.isArray,h=(q)=>L(q)||typeof q?.[Symbol.iterator]=="function";var U=/<(?:(!--|\\/[^a-zA-Z])|(\\/?[a-zA-Z][^>\\s]*)|(\\/?$))/g,m=/-->/g,a=/>/g,$=/>|[ \\t\\n\\f\\r](?:([^\\s"\'>=\\/]+)([ \\t\\n\\f\\r]*=[ \\t\\n\\f\\r]*(?:[^ \\t\\n\\f\\r"\'\\`<>=]|("|\')|))|$)/g,c=/\'/g,l=/"/g,r=/^(?:script|style|textarea|title)$/i,M=(q)=>(D,...F)=>({_$litType$:q,strings:D,values:F}),Aq=M(1),Pq=M(2),Vq=M(3),V=Symbol.for("lit-noChange"),B=Symbol.for("lit-nothing"),n=new WeakMap,w=x.createTreeWalker(x,129);function o(q,D){if(!L(q)||!q.hasOwnProperty("raw"))throw Error("invalid template strings array");return p!==void 0?p.createHTML(D):D}var e=(q,D)=>{let F=q.length-1,J=[],G,O=D===2?"<svg>":D===3?"<math>":"",K=U;for(let W=0;W<F;W++){let Q=q[W],Y,X,b=-1,A=0;while(A<Q.length&&(K.lastIndex=A,X=K.exec(Q),X!==null))A=K.lastIndex,K===U?X[1]==="!--"?K=m:X[1]!==void 0?K=a:X[2]!==void 0?(r.test(X[2])&&(G=RegExp("</"+X[2],"g")),K=$):X[3]!==void 0&&(K=$):K===$?X[0]===">"?(K=G??U,b=-1):X[1]===void 0?b=-2:(b=K.lastIndex-X[2].length,Y=X[1],K=X[3]===void 0?$:X[3]===\'"\'?l:c):K===l||K===c?K=$:K===m||K===a?K=U:(K=$,G=void 0);const _=K===$&&q[W+1].startsWith("/>")?" ":"";O+=K===U?Q+Kq:b>=0?(J.push(Y),Q.slice(0,b)+"$lit$"+Q.slice(b)+P+_):Q+P+(b===-2?W:_)}return[o(q,O+(q[F]||"<?>")+(D===2?"</svg>":D===3?"</math>":"")),J]};class g{constructor({strings:q,_$litType$:D},F){let J;this.parts=[];let G=0,O=0,K=q.length-1,W=this.parts,[Q,Y]=e(q,D);if(this.el=g.createElement(Q,F),w.currentNode=this.el.content,D===2||D===3){const X=this.el.content.firstChild;X.replaceWith(...X.childNodes)}while((J=w.nextNode())!==null&&W.length<K){if(J.nodeType===1){if(J.hasAttributes())for(const X of J.getAttributeNames())if(X.endsWith("$lit$")){const b=Y[O++],A=J.getAttribute(X).split(P),_=/([.?@])?(.*)/.exec(b);W.push({type:1,index:G,name:_[2],strings:A,ctor:_[1]==="."?R:_[1]==="?"?E:_[1]==="@"?d:I}),J.removeAttribute(X)}else X.startsWith(P)&&(W.push({type:6,index:G}),J.removeAttribute(X));if(r.test(J.tagName)){const X=J.textContent.split(P),b=X.length-1;if(b>0){J.textContent=y?y.emptyScript:"";for(let A=0;A<b;A++)J.append(X[A],C()),w.nextNode(),W.push({type:2,index:++G});J.append(X[b],C())}}}else if(J.nodeType===8)if(J.data===v)W.push({type:2,index:G});else{let X=-1;while((X=J.data.indexOf(P,X+1))!==-1)W.push({type:7,index:G}),X+=P.length-1}G++}}static createElement(q,D){const F=x.createElement("template");return F.innerHTML=q,F}}function z(q,D,F=q,J){if(D===V)return D;let G=J!==void 0?F._$Co?.[J]:F._$Cl,O=S(D)?void 0:D._$litDirective$;return G?.constructor!==O&&(G?._$AO?.(!1),O===void 0?G=void 0:(G=new O(q),G._$AT(q,F,J)),J!==void 0?(F._$Co??=[])[J]=G:F._$Cl=G),G!==void 0&&(D=z(q,G._$AS(q,D.values),G,J)),D}class j{constructor(q,D){this._$AV=[],this._$AN=void 0,this._$AD=q,this._$AM=D}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(q){const{el:{content:D},parts:F}=this._$AD,J=(q?.creationScope??x).importNode(D,!0);w.currentNode=J;let G=w.nextNode(),O=0,K=0,W=F[0];while(W!==void 0){if(O===W.index){let Q;W.type===2?Q=new H(G,G.nextSibling,this,q):W.type===1?Q=new W.ctor(G,W.name,W.strings,this,q):W.type===6&&(Q=new f(G,this,q)),this._$AV.push(Q),W=F[++K]}O!==W?.index&&(G=w.nextNode(),O++)}return w.currentNode=x,J}p(q){let D=0;for(const F of this._$AV)F!==void 0&&(F.strings!==void 0?(F._$AI(q,F,D),D+=F.strings.length-2):F._$AI(q[D])),D++}}class H{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(q,D,F,J){this.type=2,this._$AH=B,this._$AN=void 0,this._$AA=q,this._$AB=D,this._$AM=F,this.options=J,this._$Cv=J?.isConnected??!0}get parentNode(){let q=this._$AA.parentNode,D=this._$AM;return D!==void 0&&q?.nodeType===11&&(q=D.parentNode),q}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(q,D=this){q=z(this,q,D),S(q)?q===B||q==null||q===""?(this._$AH!==B&&this._$AR(),this._$AH=B):q!==this._$AH&&q!==V&&this._(q):q._$litType$!==void 0?this.$(q):q.nodeType!==void 0?this.T(q):h(q)?this.k(q):this._(q)}O(q){return this._$AA.parentNode.insertBefore(q,this._$AB)}T(q){this._$AH!==q&&(this._$AR(),this._$AH=this.O(q))}_(q){this._$AH!==B&&S(this._$AH)?this._$AA.nextSibling.data=q:this.T(x.createTextNode(q)),this._$AH=q}$(q){const{values:D,_$litType$:F}=q,J=typeof F=="number"?this._$AC(q):(F.el===void 0&&(F.el=g.createElement(o(F.h,F.h[0]),this.options)),F);if(this._$AH?._$AD===J)this._$AH.p(D);else{const G=new j(J,this),O=G.u(this.options);G.p(D),this.T(O),this._$AH=G}}_$AC(q){let D=n.get(q.strings);return D===void 0&&n.set(q.strings,D=new g(q)),D}k(q){L(this._$AH)||(this._$AH=[],this._$AR());let D=this._$AH,F,J=0;for(const G of q)J===D.length?D.push(F=new H(this.O(C()),this.O(C()),this,this.options)):F=D[J],F._$AI(G),J++;J<D.length&&(this._$AR(F&&F._$AB.nextSibling,J),D.length=J)}_$AR(q=this._$AA.nextSibling,D){for(this._$AP?.(!1,!0,D);q&&q!==this._$AB;){const F=q.nextSibling;q.remove(),q=F}}setConnected(q){this._$AM===void 0&&(this._$Cv=q,this._$AP?.(q))}}class I{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(q,D,F,J,G){this.type=1,this._$AH=B,this._$AN=void 0,this.element=q,this.name=D,this._$AM=J,this.options=G,F.length>2||F[0]!==""||F[1]!==""?(this._$AH=Array(F.length-1).fill(new String),this.strings=F):this._$AH=B}_$AI(q,D=this,F,J){let G=this.strings,O=!1;if(G===void 0)q=z(this,q,D,0),O=!S(q)||q!==this._$AH&&q!==V,O&&(this._$AH=q);else{let K=q,W,Q;for(q=G[0],W=0;W<G.length-1;W++)Q=z(this,K[F+W],D,W),Q===V&&(Q=this._$AH[W]),O||=!S(Q)||Q!==this._$AH[W],Q===B?q=B:q!==B&&(q+=(Q??"")+G[W+1]),this._$AH[W]=Q}O&&!J&&this.j(q)}j(q){q===B?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,q??"")}}class R extends I{constructor(){super(...arguments),this.type=3}j(q){this.element[this.name]=q===B?void 0:q}}class E extends I{constructor(){super(...arguments),this.type=4}j(q){this.element.toggleAttribute(this.name,!!q&&q!==B)}}class d extends I{constructor(q,D,F,J,G){super(q,D,F,J,G),this.type=5}_$AI(q,D=this){if((q=z(this,q,D,0)??B)===V)return;const F=this._$AH,J=q===B&&F!==B||q.capture!==F.capture||q.once!==F.once||q.passive!==F.passive,G=q!==B&&(F===B||J);J&&this.element.removeEventListener(this.name,this,F),G&&this.element.addEventListener(this.name,this,q),this._$AH=q}handleEvent(q){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,q):this._$AH.handleEvent(q)}}class f{constructor(q,D,F){this.element=q,this.type=6,this._$AN=void 0,this._$AM=D,this.options=F}get _$AU(){return this._$AM._$AU}_$AI(q){z(this,q)}}var Z={M:"$lit$",P,A:v,C:1,L:e,R:j,D:h,V:z,I:H,H:I,N:E,U:d,B:R,F:f},Oq=k.litHtmlPolyfillSupport;Oq?.(g,H),(k.litHtmlVersions??=[]).push("3.2.1");var i=(q,D,F)=>{let J=F?.renderBefore??D,G=J._$litPart$;if(G===void 0){const O=F?.renderBefore??null;J._$litPart$=G=new H(D.insertBefore(C(),O),O,void 0,F??{})}return G._$AI(q),G};var u=null,s={boundAttributeSuffix:Z.M,marker:Z.P,markerMatch:Z.A,HTML_RESULT:Z.C,getTemplateHtml:Z.L,overrideDirectiveResolve:(q,D)=>class extends q{_$AS(F,J){return D(this,J)}},patchDirectiveResolve:(q,D)=>{if(q.prototype._$AS!==D){u??=q.prototype._$AS.name;for(let F=q.prototype;F!==Object.prototype;F=Object.getPrototypeOf(F))if(F.hasOwnProperty(u))return void(F[u]=D);throw Error("Internal error: It is possible that both dev mode and production mode Lit was mixed together during SSR. Please comment on the issue: https://github.com/lit/lit/issues/4527")}},setDirectiveClass(q,D){q._$litDirective$=D},getAttributePartCommittedValue:(q,D,F)=>{let J=V;return q.j=(G)=>J=G,q._$AI(D,q,F),J},connectedDisconnectable:(q)=>({...q,_$AU:!0}),resolveDirective:Z.V,AttributePart:Z.H,PropertyPart:Z.B,BooleanAttributePart:Z.N,EventPart:Z.U,ElementPart:Z.F,TemplateInstance:Z.R,isIterable:Z.D,ChildPart:Z.I};var T={ATTRIBUTE:1,CHILD:2,PROPERTY:3,BOOLEAN_ATTRIBUTE:4,EVENT:5,ELEMENT:6};var t=(q)=>q===null||typeof q!="object"&&typeof q!="function";var qq=(q,D)=>D===void 0?q?._$litType$!==void 0:q?._$litType$===D,Dq=(q)=>q?._$litType$?.h!=null;var Fq=(q)=>q.strings===void 0;var{TemplateInstance:Qq,isIterable:Wq,resolveDirective:Gq,ChildPart:N,ElementPart:Xq}=s,Jq=(q,D,F={})=>{if(D._$litPart$!==void 0)throw Error("container already contains a live render");let J,G,O,K=[],W=document.createTreeWalker(D,NodeFilter.SHOW_COMMENT),Q;while((Q=W.nextNode())!==null){const Y=Q.data;if(Y.startsWith("lit-part")){if(K.length===0&&J!==void 0)throw Error(`There must be only one root part per container. Found a part marker (${Q}) when we already have a root part marker (${G})`);O=Yq(q,Q,K,F),J===void 0&&(J=O),G??=Q}else if(Y.startsWith("lit-node"))Zq(Q,K,F);else if(Y.startsWith("/lit-part")){if(K.length===1&&O!==J)throw Error("internal error");O=Bq(Q,O,K)}}if(J===void 0){const Y=D instanceof ShadowRoot?"{container.host.localName}\'s shadow root":D instanceof DocumentFragment?"DocumentFragment":D.localName;console.error(`There should be exactly one root part in a render container, but we didn\'t find any in ${Y}.`)}D._$litPart$=J},Yq=(q,D,F,J)=>{let G,O;if(F.length===0)O=new N(D,null,void 0,J),G=q;else{const K=F[F.length-1];if(K.type==="template-instance")O=new N(D,null,K.instance,J),K.instance._$AV.push(O),G=K.result.values[K.instancePartIndex++],K.templatePartIndex++;else if(K.type==="iterable"){O=new N(D,null,K.part,J);const W=K.iterator.next();if(W.done)throw G=void 0,K.done=!0,Error("Unhandled shorter than expected iterable");G=W.value,K.part._$AH.push(O)}else O=new N(D,null,K.part,J)}if(G=Gq(O,G),G===V)F.push({part:O,type:"leaf"});else if(t(G))F.push({part:O,type:"leaf"}),O._$AH=G;else if(qq(G)){if(Dq(G))throw Error("compiled templates are not supported");const K="lit-part "+bq(G);if(D.data!==K)throw Error("Hydration value mismatch: Unexpected TemplateResult rendered to part");{const W=N.prototype._$AC(G),Q=new Qq(W,O);F.push({type:"template-instance",instance:Q,part:O,templatePartIndex:0,instancePartIndex:0,result:G}),O._$AH=Q}}else Wq(G)?(F.push({part:O,type:"iterable",value:G,iterator:G[Symbol.iterator](),done:!1}),O._$AH=[]):(F.push({part:O,type:"leaf"}),O._$AH=G??"");return O},Bq=(q,D,F)=>{if(D===void 0)throw Error("unbalanced part marker");D._$AB=q;const J=F.pop();if(J.type==="iterable"&&!J.iterator.next().done)throw Error("unexpected longer than expected iterable");if(F.length>0)return F[F.length-1].part},Zq=(q,D,F)=>{const J=/lit-node (\\d+)/.exec(q.data),G=Number.parseInt(J[1]),O=q.nextElementSibling;if(O===null)throw Error("could not find node for attribute parts");O.removeAttribute("defer-hydration");const K=D[D.length-1];if(K.type!=="template-instance")throw Error("Hydration value mismatch: Primitive found where TemplateResult expected. This usually occurs due to conditional rendering that resulted in a different value or template being rendered between the server and client.");{const W=K.instance;for(;;){const Q=W._$AD.parts[K.templatePartIndex];if(Q===void 0||Q.type!==T.ATTRIBUTE&&Q.type!==T.ELEMENT||Q.index!==G)break;if(Q.type===T.ATTRIBUTE){const Y=new Q.ctor(O,Q.name,Q.strings,K.instance,F),X=Fq(Y)?K.result.values[K.instancePartIndex]:K.result.values,b=!(Y.type===T.EVENT||Y.type===T.PROPERTY);Y._$AI(X,Y,K.instancePartIndex,b),K.instancePartIndex+=Q.strings.length-1,W._$AV.push(Y)}else{const Y=new Xq(O,K.instance,F);Gq(Y,K.result.values[K.instancePartIndex++]),W._$AV.push(Y)}K.templatePartIndex++}}},bq=(q)=>{const D=new Uint32Array(2).fill(5381);for(const J of q.strings)for(let G=0;G<J.length;G++)D[G%2]=33*D[G%2]^J.charCodeAt(G);const F=String.fromCharCode(...new Uint8Array(D.buffer));return btoa(F)};globalThis.litElementHydrateSupport=({LitElement:q})=>{const D=Object.getOwnPropertyDescriptor(Object.getPrototypeOf(q),"observedAttributes").get;Object.defineProperty(q,"observedAttributes",{get(){return[...D.call(this),"defer-hydration"]}});const F=q.prototype.attributeChangedCallback;q.prototype.attributeChangedCallback=function(K,W,Q){K==="defer-hydration"&&Q===null&&J.call(this),F.call(this,K,W,Q)};const J=q.prototype.connectedCallback;q.prototype.connectedCallback=function(){this.hasAttribute("defer-hydration")||J.call(this)};const G=q.prototype.createRenderRoot;q.prototype.createRenderRoot=function(){return this.shadowRoot?(this._$AG=!0,this.shadowRoot):G.call(this)};const O=Object.getPrototypeOf(q.prototype).update;q.prototype.update=function(K){const W=this.render();if(O.call(this,K),this._$AG){this._$AG=!1;for(let Q=0;Q<this.attributes.length;Q++){const Y=this.attributes[Q];if(Y.name.startsWith("hydrate-internals-")){const X=Y.name.slice(18);this.removeAttribute(X),this.removeAttribute(Y.name)}}Jq(W,this.renderRoot,this.renderOptions)}else i(W,this.renderRoot,this.renderOptions)}};';
|
package/src/lit-renderer.d.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* @module
|
|
4
4
|
*/
|
|
5
5
|
import type { ComponentRenderInput, ComponentRenderResult, EcoComponent, EcoPagesElement, IntegrationRendererRenderOptions, RouteRenderResult, RouteRendererBody, RouteRendererOptions } from '@ecopages/core';
|
|
6
|
-
import './dom-shim.js';
|
|
7
6
|
import { IntegrationRenderer, type RenderToResponseContext } from '@ecopages/core/route-renderer/orchestration/integration-renderer';
|
|
7
|
+
import { type CustomElementSsrPreloadComponent } from '@ecopages/core/route-renderer/orchestration/custom-element-scripts/custom-element-script-preloader';
|
|
8
8
|
import type { LitStaticRenderSession } from './lit-static-render-session.js';
|
|
9
9
|
export type LitRendererOptions = ConstructorParameters<typeof IntegrationRenderer>[0] & {
|
|
10
10
|
/**
|
|
@@ -23,6 +23,7 @@ export type LitRendererOptions = ConstructorParameters<typeof IntegrationRendere
|
|
|
23
23
|
export declare class LitRenderer extends IntegrationRenderer<EcoPagesElement> {
|
|
24
24
|
name: string;
|
|
25
25
|
private readonly getRenderSession?;
|
|
26
|
+
private readonly ssrScriptPreloader;
|
|
26
27
|
constructor(options: LitRendererOptions);
|
|
27
28
|
/**
|
|
28
29
|
* Renders a Lit page route, preferring the plugin worker when available.
|
|
@@ -40,14 +41,13 @@ export declare class LitRenderer extends IntegrationRenderer<EcoPagesElement> {
|
|
|
40
41
|
/**
|
|
41
42
|
* Renders a Lit component for component-level orchestration.
|
|
42
43
|
*
|
|
43
|
-
* SSR-eligible
|
|
44
|
+
* SSR-eligible scripts are preloaded first so custom elements registered
|
|
44
45
|
* by the component can render their server markup even when the Lit renderer is
|
|
45
46
|
* entered through cross-integration foreign-child handoff.
|
|
46
47
|
*
|
|
47
48
|
* Includes component-scoped dependency assets when declared.
|
|
48
49
|
*/
|
|
49
50
|
renderComponent(input: ComponentRenderInput): Promise<ComponentRenderResult>;
|
|
50
|
-
private readonly ssrLazyPreloader;
|
|
51
51
|
/**
|
|
52
52
|
* Detects preload failures that are expected for browser-only modules.
|
|
53
53
|
*
|
|
@@ -56,24 +56,24 @@ export declare class LitRenderer extends IntegrationRenderer<EcoPagesElement> {
|
|
|
56
56
|
*/
|
|
57
57
|
protected isExpectedSsrPreloadError(error: unknown): boolean;
|
|
58
58
|
/**
|
|
59
|
-
* Collects
|
|
59
|
+
* Collects script file paths eligible for SSR preloading.
|
|
60
60
|
*
|
|
61
|
-
*
|
|
61
|
+
* Per-entry script dependencies with `ssr: true` are collected (eager or lazy).
|
|
62
62
|
* File-backed entries are required (`src` must be present);
|
|
63
63
|
* inline content lazy entries are intentionally skipped.
|
|
64
64
|
*/
|
|
65
|
-
protected collectSsrPreloadScripts(components: Array<
|
|
65
|
+
protected collectSsrPreloadScripts(components: Array<CustomElementSsrPreloadComponent | undefined>): string[];
|
|
66
66
|
/**
|
|
67
|
-
* Preloads SSR-eligible
|
|
68
|
-
*/
|
|
69
|
-
protected preloadSsrLazyScripts(components: Array<EcoComponent | undefined>): Promise<void>;
|
|
70
|
-
/**
|
|
71
|
-
* Resolves the concrete JS entrypoint used for SSR preloading.
|
|
67
|
+
* Preloads SSR-eligible scripts to register custom elements before render.
|
|
72
68
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
69
|
+
* @remarks
|
|
70
|
+
* When a render session exists (main thread), preload uses the session so
|
|
71
|
+
* Bun source imports and the Node asset pipeline stay on that isolate.
|
|
72
|
+
* With no session (static-render worker), preload uses this renderer's
|
|
73
|
+
* app-module importer. A native source import in the worker leaves custom
|
|
74
|
+
* elements undefined for `@lit-labs/ssr`.
|
|
75
75
|
*/
|
|
76
|
-
protected
|
|
77
|
-
render({ params, query, props, locals, metadata, Page, Layout, layoutEntries, HtmlTemplate, }: IntegrationRendererRenderOptions): Promise<RouteRendererBody>;
|
|
76
|
+
protected preloadSsrScripts(components: Array<CustomElementSsrPreloadComponent | undefined>): Promise<void>;
|
|
77
|
+
render({ params, query, props, locals, metadata, Page, Layout, layoutEntries, HtmlTemplate, resolvedPageDependencyComponents, }: IntegrationRendererRenderOptions): Promise<RouteRendererBody>;
|
|
78
78
|
renderToResponse<P = Record<string, unknown>>(view: EcoComponent<P>, props: P, ctx: RenderToResponseContext): Promise<Response>;
|
|
79
79
|
}
|
package/src/lit-renderer.js
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* This module contains the Lit renderer
|
|
3
3
|
* @module
|
|
4
4
|
*/
|
|
5
|
-
import './dom-shim.js';
|
|
6
5
|
import { IntegrationRenderer, } from '@ecopages/core/route-renderer/orchestration/integration-renderer';
|
|
7
6
|
import { resolveDocumentShellLayouts } from '@ecopages/core/route-renderer/orchestration/document-shell/layout-shell-props.service';
|
|
8
7
|
import { getForeignSubtreeResolutionContextKey, resolveOwningIntegrationRenderer, } from '@ecopages/core/route-renderer/orchestration/foreign-child/owning-renderer-resolution';
|
|
9
|
-
import {
|
|
8
|
+
import { ensureLitDomShim } from './dom-shim.js';
|
|
9
|
+
import { CUSTOM_ELEMENT_SSR_PRELOAD_CACHE_SCOPES, CustomElementScriptPreloader, } from '@ecopages/core/route-renderer/orchestration/custom-element-scripts/custom-element-script-preloader';
|
|
10
|
+
import { createCustomElementServerModuleImporter } from '@ecopages/core/route-renderer/orchestration/custom-element-scripts/custom-element-server-module-importer';
|
|
10
11
|
import { LIT_PLUGIN_NAME } from './lit.constants.js';
|
|
11
12
|
import { injectLitRenderedChildren, LIT_COMPONENT_CHILDREN_SLOT_MARKER, normalizeLitHtml, renderLitValueToString, } from './utils/lit-html-rendering.js';
|
|
12
13
|
/**
|
|
@@ -15,10 +16,18 @@ import { injectLitRenderedChildren, LIT_COMPONENT_CHILDREN_SLOT_MARKER, normaliz
|
|
|
15
16
|
export class LitRenderer extends IntegrationRenderer {
|
|
16
17
|
name = LIT_PLUGIN_NAME;
|
|
17
18
|
getRenderSession;
|
|
19
|
+
ssrScriptPreloader;
|
|
18
20
|
constructor(options) {
|
|
19
21
|
const { getRenderSession, ...rendererOptions } = options;
|
|
20
22
|
super(rendererOptions);
|
|
21
23
|
this.getRenderSession = getRenderSession;
|
|
24
|
+
this.ssrScriptPreloader = new CustomElementScriptPreloader({
|
|
25
|
+
cacheScope: CUSTOM_ELEMENT_SSR_PRELOAD_CACHE_SCOPES.lit,
|
|
26
|
+
logLabel: 'lit',
|
|
27
|
+
requireLazyScriptEntry: true,
|
|
28
|
+
resolveDependencyPath: this.resolveDependencyPath.bind(this),
|
|
29
|
+
importServerModule: createCustomElementServerModuleImporter(this.appConfig, '.lit-ssr'),
|
|
30
|
+
});
|
|
22
31
|
}
|
|
23
32
|
/**
|
|
24
33
|
* Renders a Lit page route, preferring the plugin worker when available.
|
|
@@ -76,14 +85,14 @@ export class LitRenderer extends IntegrationRenderer {
|
|
|
76
85
|
/**
|
|
77
86
|
* Renders a Lit component for component-level orchestration.
|
|
78
87
|
*
|
|
79
|
-
* SSR-eligible
|
|
88
|
+
* SSR-eligible scripts are preloaded first so custom elements registered
|
|
80
89
|
* by the component can render their server markup even when the Lit renderer is
|
|
81
90
|
* entered through cross-integration foreign-child handoff.
|
|
82
91
|
*
|
|
83
92
|
* Includes component-scoped dependency assets when declared.
|
|
84
93
|
*/
|
|
85
94
|
async renderComponent(input) {
|
|
86
|
-
await this.
|
|
95
|
+
await this.preloadSsrScripts([input.component]);
|
|
87
96
|
if (!this.isFunctionComponent(input.component)) {
|
|
88
97
|
throw new TypeError('Lit renderer expected a callable component.');
|
|
89
98
|
}
|
|
@@ -136,10 +145,6 @@ export class LitRenderer extends IntegrationRenderer {
|
|
|
136
145
|
]),
|
|
137
146
|
});
|
|
138
147
|
}
|
|
139
|
-
ssrLazyPreloader = new LitSsrLazyPreloader({
|
|
140
|
-
resolveDependencyPath: this.resolveDependencyPath.bind(this),
|
|
141
|
-
processDependencies: this.assetProcessingService?.processDependencies?.bind(this.assetProcessingService),
|
|
142
|
-
});
|
|
143
148
|
/**
|
|
144
149
|
* Detects preload failures that are expected for browser-only modules.
|
|
145
150
|
*
|
|
@@ -147,41 +152,40 @@ export class LitRenderer extends IntegrationRenderer {
|
|
|
147
152
|
* lazy client scripts intentionally depend on browser globals.
|
|
148
153
|
*/
|
|
149
154
|
isExpectedSsrPreloadError(error) {
|
|
150
|
-
return this.
|
|
155
|
+
return this.ssrScriptPreloader.isExpectedSsrPreloadError(error);
|
|
151
156
|
}
|
|
152
157
|
/**
|
|
153
|
-
* Collects
|
|
158
|
+
* Collects script file paths eligible for SSR preloading.
|
|
154
159
|
*
|
|
155
|
-
*
|
|
160
|
+
* Per-entry script dependencies with `ssr: true` are collected (eager or lazy).
|
|
156
161
|
* File-backed entries are required (`src` must be present);
|
|
157
162
|
* inline content lazy entries are intentionally skipped.
|
|
158
163
|
*/
|
|
159
164
|
collectSsrPreloadScripts(components) {
|
|
160
|
-
return this.
|
|
165
|
+
return this.ssrScriptPreloader.collectSsrPreloadScripts(components);
|
|
161
166
|
}
|
|
162
167
|
/**
|
|
163
|
-
* Preloads SSR-eligible
|
|
168
|
+
* Preloads SSR-eligible scripts to register custom elements before render.
|
|
169
|
+
*
|
|
170
|
+
* @remarks
|
|
171
|
+
* When a render session exists (main thread), preload uses the session so
|
|
172
|
+
* Bun source imports and the Node asset pipeline stay on that isolate.
|
|
173
|
+
* With no session (static-render worker), preload uses this renderer's
|
|
174
|
+
* app-module importer. A native source import in the worker leaves custom
|
|
175
|
+
* elements undefined for `@lit-labs/ssr`.
|
|
164
176
|
*/
|
|
165
|
-
async
|
|
177
|
+
async preloadSsrScripts(components) {
|
|
178
|
+
ensureLitDomShim();
|
|
166
179
|
const renderSession = this.getRenderSession?.();
|
|
167
180
|
if (renderSession) {
|
|
168
|
-
await renderSession.
|
|
181
|
+
await renderSession.preloadSsrScripts(components);
|
|
169
182
|
return;
|
|
170
183
|
}
|
|
171
|
-
await this.
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Resolves the concrete JS entrypoint used for SSR preloading.
|
|
175
|
-
*
|
|
176
|
-
* Scripts are passed through the asset pipeline so preload imports can use
|
|
177
|
-
* the same processed output shape as runtime dependencies.
|
|
178
|
-
*/
|
|
179
|
-
async resolveSsrPreloadEntrypoint(scriptPath) {
|
|
180
|
-
return this.ssrLazyPreloader.resolveSsrPreloadEntrypoint(scriptPath);
|
|
184
|
+
await this.ssrScriptPreloader.preloadSsrScripts(components);
|
|
181
185
|
}
|
|
182
|
-
async render({ params, query, props, locals, metadata, Page, Layout, layoutEntries, HtmlTemplate, }) {
|
|
186
|
+
async render({ params, query, props, locals, metadata, Page, Layout, layoutEntries, HtmlTemplate, resolvedPageDependencyComponents, }) {
|
|
183
187
|
try {
|
|
184
|
-
await this.
|
|
188
|
+
await this.preloadSsrScripts([Page, Layout, ...(resolvedPageDependencyComponents ?? [])]);
|
|
185
189
|
return await this.renderPageWithDocumentShell({
|
|
186
190
|
page: {
|
|
187
191
|
component: Page,
|
|
@@ -202,6 +206,7 @@ export class LitRenderer extends IntegrationRenderer {
|
|
|
202
206
|
htmlTemplate: HtmlTemplate,
|
|
203
207
|
metadata,
|
|
204
208
|
pageProps: props || {},
|
|
209
|
+
foreignChildRoots: resolvedPageDependencyComponents,
|
|
205
210
|
transformDocumentHtml: normalizeLitHtml,
|
|
206
211
|
});
|
|
207
212
|
}
|
|
@@ -213,7 +218,7 @@ export class LitRenderer extends IntegrationRenderer {
|
|
|
213
218
|
try {
|
|
214
219
|
const layouts = view.config?.layouts;
|
|
215
220
|
const Layout = layouts?.[layouts.length - 1];
|
|
216
|
-
await this.
|
|
221
|
+
await this.preloadSsrScripts([view, Layout]);
|
|
217
222
|
return await this.renderViewWithDocumentShell({
|
|
218
223
|
view,
|
|
219
224
|
props,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createCustomElementSsrPreloadEntrypointResolver as createLitSsrPreloadEntrypointResolver, type CustomElementSsrPreloadEntrypointResolverOptions as LitSsrPreloadEntrypointResolverOptions, } from '@ecopages/core/route-renderer/orchestration/custom-element-scripts/custom-element-ssr-preload-entrypoint';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createCustomElementSsrPreloadEntrypointResolver as createLitSsrPreloadEntrypointResolver, } from '@ecopages/core/route-renderer/orchestration/custom-element-scripts/custom-element-ssr-preload-entrypoint';
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PageParams, PageQuery } from '@ecopages/core';
|
|
2
2
|
import type { StaticExportContext } from '@ecopages/core/plugins/integration-plugin';
|
|
3
|
-
import type
|
|
3
|
+
import { type CustomElementScriptPreloaderOptions, type CustomElementSsrPreloadComponent } from '@ecopages/core/route-renderer/orchestration/custom-element-scripts/custom-element-script-preloader';
|
|
4
4
|
import { LitStaticRenderWorkerClient } from './lit-static-render-worker-client.js';
|
|
5
5
|
import type { LitStaticRenderCacheStrategy } from './lit-static-render-protocol.js';
|
|
6
|
+
import type { AssetDefinition, ProcessedAsset } from '@ecopages/core/services/asset-processing-service';
|
|
6
7
|
type LitStaticRenderWorkerClientContract = Pick<LitStaticRenderWorkerClient, 'start' | 'renderPage' | 'dispose'>;
|
|
7
8
|
type LitStaticRenderSessionOptions = {
|
|
8
9
|
resolveDependencyPath: (componentDir: string, sourcePath: string) => string;
|
|
9
|
-
processDependencies?: (dependencies: AssetDefinition[], integrationName: string) => Promise<Array<{
|
|
10
|
-
filepath?: string;
|
|
11
|
-
}>>;
|
|
12
10
|
preferSourceImports?: boolean;
|
|
11
|
+
processDependencies?: (dependencies: AssetDefinition[], integrationName: string) => Promise<ProcessedAsset[]>;
|
|
12
|
+
importServerModule?: CustomElementScriptPreloaderOptions['importServerModule'];
|
|
13
|
+
getInvalidationVersion?: () => number;
|
|
13
14
|
createWorkerClient?: (input: {
|
|
14
15
|
configModulePath: string;
|
|
15
16
|
runtimeOrigin: string;
|
|
@@ -32,6 +33,9 @@ export declare class LitStaticRenderSession {
|
|
|
32
33
|
private readonly createWorkerClient;
|
|
33
34
|
private workerClient;
|
|
34
35
|
private workerIdentity;
|
|
36
|
+
private readonly getInvalidationVersion;
|
|
37
|
+
private workerInvalidationVersion;
|
|
38
|
+
private renderChain;
|
|
35
39
|
constructor(options: LitStaticRenderSessionOptions);
|
|
36
40
|
/**
|
|
37
41
|
* Ensures the static render worker is running for the given identity.
|
|
@@ -45,8 +49,8 @@ export declare class LitStaticRenderSession {
|
|
|
45
49
|
runtimeOrigin: string;
|
|
46
50
|
}): Promise<void>;
|
|
47
51
|
preloadStaticRoutes(context: StaticExportContext): Promise<void>;
|
|
48
|
-
|
|
49
|
-
collectSsrPreloadScripts(components: Array<
|
|
52
|
+
preloadSsrScripts(components: Array<CustomElementSsrPreloadComponent | undefined>): Promise<void>;
|
|
53
|
+
collectSsrPreloadScripts(components: Array<CustomElementSsrPreloadComponent | undefined>): string[];
|
|
50
54
|
renderPageInWorker(input: {
|
|
51
55
|
filePath: string;
|
|
52
56
|
params: PageParams;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { ensureLitDomShim } from './dom-shim.js';
|
|
3
|
+
import { CUSTOM_ELEMENT_SSR_PRELOAD_CACHE_SCOPES, CustomElementScriptPreloader, } from '@ecopages/core/route-renderer/orchestration/custom-element-scripts/custom-element-script-preloader';
|
|
3
4
|
import { LIT_PLUGIN_NAME } from './lit.constants.js';
|
|
5
|
+
import { createLitSsrPreloadEntrypointResolver } from './lit-ssr-preload-entrypoint.js';
|
|
4
6
|
import { LitStaticRenderWorkerClient } from './lit-static-render-worker-client.js';
|
|
5
7
|
/**
|
|
6
8
|
* Plugin-runtime-scoped Lit server render session.
|
|
@@ -19,11 +21,23 @@ export class LitStaticRenderSession {
|
|
|
19
21
|
createWorkerClient;
|
|
20
22
|
workerClient = null;
|
|
21
23
|
workerIdentity = null;
|
|
24
|
+
getInvalidationVersion;
|
|
25
|
+
workerInvalidationVersion = 0;
|
|
26
|
+
renderChain = Promise.resolve();
|
|
22
27
|
constructor(options) {
|
|
23
|
-
this.
|
|
28
|
+
this.getInvalidationVersion = options.getInvalidationVersion ?? (() => 0);
|
|
29
|
+
const preferSourceImports = options.preferSourceImports ?? typeof Bun !== 'undefined';
|
|
30
|
+
this.preloader = new CustomElementScriptPreloader({
|
|
31
|
+
cacheScope: CUSTOM_ELEMENT_SSR_PRELOAD_CACHE_SCOPES.lit,
|
|
32
|
+
logLabel: 'lit',
|
|
33
|
+
requireLazyScriptEntry: true,
|
|
24
34
|
resolveDependencyPath: options.resolveDependencyPath,
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
preferSourceImports,
|
|
36
|
+
resolvePreloadEntrypoint: createLitSsrPreloadEntrypointResolver({
|
|
37
|
+
preferSourceImports,
|
|
38
|
+
processDependencies: options.processDependencies,
|
|
39
|
+
}),
|
|
40
|
+
importServerModule: options.importServerModule,
|
|
27
41
|
});
|
|
28
42
|
this.createWorkerClient =
|
|
29
43
|
options.createWorkerClient ??
|
|
@@ -58,12 +72,14 @@ export class LitStaticRenderSession {
|
|
|
58
72
|
runtimeOrigin: input.runtimeOrigin,
|
|
59
73
|
});
|
|
60
74
|
await this.workerClient.start();
|
|
75
|
+
this.workerInvalidationVersion = this.getInvalidationVersion();
|
|
61
76
|
}
|
|
62
77
|
async preloadStaticRoutes(context) {
|
|
63
78
|
await this.preloadLitRoutes(context);
|
|
64
79
|
}
|
|
65
|
-
async
|
|
66
|
-
|
|
80
|
+
async preloadSsrScripts(components) {
|
|
81
|
+
ensureLitDomShim();
|
|
82
|
+
await this.preloader.preloadSsrScripts(components);
|
|
67
83
|
}
|
|
68
84
|
collectSsrPreloadScripts(components) {
|
|
69
85
|
return this.preloader.collectSsrPreloadScripts(components);
|
|
@@ -72,7 +88,19 @@ export class LitStaticRenderSession {
|
|
|
72
88
|
if (!this.workerClient) {
|
|
73
89
|
throw new Error('Lit static render worker is not active');
|
|
74
90
|
}
|
|
75
|
-
|
|
91
|
+
const render = this.renderChain.then(async () => {
|
|
92
|
+
const version = this.getInvalidationVersion();
|
|
93
|
+
if (version !== this.workerInvalidationVersion && this.workerIdentity) {
|
|
94
|
+
const identity = this.workerIdentity;
|
|
95
|
+
await this.dispose();
|
|
96
|
+
await this.ensureWorker(identity);
|
|
97
|
+
}
|
|
98
|
+
if (!this.workerClient)
|
|
99
|
+
throw new Error('Lit static render worker is not active');
|
|
100
|
+
return this.workerClient.renderPage(input);
|
|
101
|
+
});
|
|
102
|
+
this.renderChain = render.then(() => undefined, () => undefined);
|
|
103
|
+
return render;
|
|
76
104
|
}
|
|
77
105
|
isExpectedSsrPreloadError(error) {
|
|
78
106
|
return this.preloader.isExpectedSsrPreloadError(error);
|
|
@@ -106,7 +134,7 @@ export class LitStaticRenderSession {
|
|
|
106
134
|
}
|
|
107
135
|
}
|
|
108
136
|
}
|
|
109
|
-
await this.
|
|
137
|
+
await this.preloadSsrScripts(components);
|
|
110
138
|
}
|
|
111
139
|
isLitRoute(filePath, appConfig) {
|
|
112
140
|
const litIntegration = appConfig.integrations.find((plugin) => plugin.name === LIT_PLUGIN_NAME);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parentPort } from 'node:worker_threads';
|
|
2
|
-
import {
|
|
2
|
+
import { loadEcoPagesConfig } from '@ecopages/core/config';
|
|
3
3
|
import { setupAppRuntimePlugins } from '@ecopages/core/build/build-adapter';
|
|
4
4
|
import { installBuildRuntime } from '@ecopages/core/build/build-runtime';
|
|
5
5
|
import { RouteRendererFactory } from '@ecopages/core/route-renderer/route-renderer';
|
|
@@ -14,8 +14,7 @@ let routeRendererFactory = null;
|
|
|
14
14
|
* before the Lit worker started (`ECOPAGES_LIT_STATIC_RENDER_WORKER`).
|
|
15
15
|
*/
|
|
16
16
|
async function initializeWorker(configModulePath, runtimeOrigin) {
|
|
17
|
-
|
|
18
|
-
appConfig = (configModule.default ?? configModule);
|
|
17
|
+
appConfig = await loadEcoPagesConfig({ configFile: configModulePath });
|
|
19
18
|
await setupAppRuntimePlugins({
|
|
20
19
|
appConfig,
|
|
21
20
|
runtimeOrigin,
|
package/src/lit.plugin.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IHmrManager } from '@ecopages/core';
|
|
1
2
|
/**
|
|
2
3
|
* This module contains the Lit plugin
|
|
3
4
|
* @module
|
|
@@ -23,11 +24,22 @@ export declare class LitPlugin extends IntegrationPlugin {
|
|
|
23
24
|
renderer: typeof LitRenderer;
|
|
24
25
|
private renderSession;
|
|
25
26
|
constructor(options?: Omit<IntegrationPluginConfig, 'name'>);
|
|
27
|
+
/**
|
|
28
|
+
* Returns the global integration dependencies required in the browser.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* Injects Lit's hydration support script into the document head before any
|
|
32
|
+
* custom element connects. While `@lit-labs/ssr-client/lit-element-hydrate-support.js`
|
|
33
|
+
* can be resolved directly in modern Bun and Node environments, an inline self-contained
|
|
34
|
+
* script is currently emitted to ensure synchronous availability before custom-element
|
|
35
|
+
* registration without requiring an import map for `@lit-labs/ssr-client` subpath imports.
|
|
36
|
+
*/
|
|
26
37
|
getDependencies(): AssetDefinition[];
|
|
27
38
|
private createRenderSession;
|
|
28
39
|
initializeRenderer(options?: {
|
|
29
40
|
rendererModules?: unknown;
|
|
30
41
|
}): LitRenderer;
|
|
42
|
+
setHmrManager(hmrManager: IHmrManager): void;
|
|
31
43
|
setup(): Promise<void>;
|
|
32
44
|
teardown(): Promise<void>;
|
|
33
45
|
beforeStaticExport(context: StaticExportContext): Promise<void>;
|
package/src/lit.plugin.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createCustomElementServerModuleImporter } from '@ecopages/core/route-renderer/orchestration/custom-element-scripts/custom-element-server-module-importer';
|
|
2
|
+
import { CUSTOM_ELEMENT_SSR_PRELOAD_CACHE_SCOPES, invalidateCustomElementScriptPreload, } from '@ecopages/core/route-renderer/orchestration/custom-element-scripts/custom-element-script-preloader';
|
|
1
3
|
/**
|
|
2
4
|
* This module contains the Lit plugin
|
|
3
5
|
* @module
|
|
@@ -35,20 +37,18 @@ export class LitPlugin extends IntegrationPlugin {
|
|
|
35
37
|
});
|
|
36
38
|
this.integrationDependencies.unshift(...this.getDependencies());
|
|
37
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns the global integration dependencies required in the browser.
|
|
42
|
+
*
|
|
43
|
+
* @remarks
|
|
44
|
+
* Injects Lit's hydration support script into the document head before any
|
|
45
|
+
* custom element connects. While `@lit-labs/ssr-client/lit-element-hydrate-support.js`
|
|
46
|
+
* can be resolved directly in modern Bun and Node environments, an inline self-contained
|
|
47
|
+
* script is currently emitted to ensure synchronous availability before custom-element
|
|
48
|
+
* registration without requiring an import map for `@lit-labs/ssr-client` subpath imports.
|
|
49
|
+
*/
|
|
38
50
|
getDependencies() {
|
|
39
51
|
return [
|
|
40
|
-
/**
|
|
41
|
-
* BUG ALERT
|
|
42
|
-
* Due to an issue appeared in Bun 1.2.2, we need to use a workaround to import the hydrate script.
|
|
43
|
-
* This is a temporary solution until the issue is resolved.
|
|
44
|
-
* The litElementHydrateScript is the same file built on Bun 1.1.45.
|
|
45
|
-
* https://github.com/oven-sh/bun/issues/17180
|
|
46
|
-
*
|
|
47
|
-
* AssetFactory.createNodeModuleScript({
|
|
48
|
-
* position: 'head',
|
|
49
|
-
* importPath: '@lit-labs/ssr-client/lit-element-hydrate-support.js'
|
|
50
|
-
* })
|
|
51
|
-
*/
|
|
52
52
|
AssetFactory.createInlineContentScript({
|
|
53
53
|
position: 'head',
|
|
54
54
|
content: `(() => {${litElementHydrateScript}})();`,
|
|
@@ -66,6 +66,8 @@ export class LitPlugin extends IntegrationPlugin {
|
|
|
66
66
|
return new LitStaticRenderSession({
|
|
67
67
|
resolveDependencyPath: (componentDir, sourcePath) => path.join(componentDir, sourcePath),
|
|
68
68
|
processDependencies: this.assetProcessingService.processDependencies.bind(this.assetProcessingService),
|
|
69
|
+
getInvalidationVersion: () => this.appConfig?.runtime?.serverInvalidationState?.getServerInvalidationVersion() ?? 0,
|
|
70
|
+
importServerModule: createCustomElementServerModuleImporter(this.appConfig, '.lit-ssr'),
|
|
69
71
|
preferSourceImports: typeof Bun !== 'undefined',
|
|
70
72
|
});
|
|
71
73
|
}
|
|
@@ -76,6 +78,18 @@ export class LitPlugin extends IntegrationPlugin {
|
|
|
76
78
|
});
|
|
77
79
|
return this.attachRendererRuntimeServices(renderer);
|
|
78
80
|
}
|
|
81
|
+
setHmrManager(hmrManager) {
|
|
82
|
+
super.setHmrManager(hmrManager);
|
|
83
|
+
if (!this.appConfig) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const runtime = this.appConfig.runtime ?? {};
|
|
87
|
+
this.appConfig.runtime = runtime;
|
|
88
|
+
runtime.registeredScriptEntrypointChangeHandlers ??= [];
|
|
89
|
+
runtime.registeredScriptEntrypointChangeHandlers.push((scriptPath) => {
|
|
90
|
+
invalidateCustomElementScriptPreload(scriptPath, CUSTOM_ELEMENT_SSR_PRELOAD_CACHE_SCOPES.lit);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
79
93
|
async setup() {
|
|
80
94
|
await super.setup();
|
|
81
95
|
if (!this.appConfig?.absolutePaths?.config) {
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { type EcoComponent } from '@ecopages/core';
|
|
2
|
-
import type { AssetDefinition } from '@ecopages/core/services/asset-processing-service';
|
|
3
|
-
type ProcessDependencies = (dependencies: AssetDefinition[], integrationName: string) => Promise<Array<{
|
|
4
|
-
filepath?: string;
|
|
5
|
-
}>>;
|
|
6
|
-
export interface LitSsrLazyPreloaderOptions {
|
|
7
|
-
resolveDependencyPath: (componentDir: string, sourcePath: string) => string;
|
|
8
|
-
processDependencies?: ProcessDependencies;
|
|
9
|
-
preferSourceImports?: boolean;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Encapsulates SSR lazy script preload behavior for Lit components.
|
|
13
|
-
*
|
|
14
|
-
* Collects `dependencies.scripts` lazy entries with `ssr: true`, resolves the
|
|
15
|
-
* processed entrypoint through the asset pipeline, and imports the module so
|
|
16
|
-
* custom elements are registered before SSR rendering.
|
|
17
|
-
*/
|
|
18
|
-
export declare class LitSsrLazyPreloader {
|
|
19
|
-
private readonly resolveDependencyPath;
|
|
20
|
-
private readonly processDependencies?;
|
|
21
|
-
private readonly preferSourceImports;
|
|
22
|
-
private readonly ssrPreloadedScripts;
|
|
23
|
-
private readonly ssrPreloadFailedScripts;
|
|
24
|
-
private readonly ssrPreloadEntrypointCache;
|
|
25
|
-
constructor({ resolveDependencyPath, processDependencies, preferSourceImports }: LitSsrLazyPreloaderOptions);
|
|
26
|
-
private getErrorCode;
|
|
27
|
-
/**
|
|
28
|
-
* Detects preload failures that are expected for browser-only modules.
|
|
29
|
-
*/
|
|
30
|
-
isExpectedSsrPreloadError(error: unknown): boolean;
|
|
31
|
-
/**
|
|
32
|
-
* Collects lazy script file paths eligible for SSR preloading.
|
|
33
|
-
*/
|
|
34
|
-
collectSsrPreloadScripts(components: Array<EcoComponent | undefined>): string[];
|
|
35
|
-
/**
|
|
36
|
-
* Preloads SSR-eligible lazy scripts to register custom elements before render.
|
|
37
|
-
*/
|
|
38
|
-
preloadSsrLazyScripts(components: Array<EcoComponent | undefined>): Promise<void>;
|
|
39
|
-
/**
|
|
40
|
-
* Resolves the concrete JS entrypoint used for SSR preloading.
|
|
41
|
-
*/
|
|
42
|
-
resolveSsrPreloadEntrypoint(scriptPath: string): Promise<string | null>;
|
|
43
|
-
}
|
|
44
|
-
export {};
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { getComponentIdentity } from '@ecopages/core';
|
|
2
|
-
import { AssetFactory } from '@ecopages/core/services/asset-processing-service';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
5
|
-
/**
|
|
6
|
-
* Encapsulates SSR lazy script preload behavior for Lit components.
|
|
7
|
-
*
|
|
8
|
-
* Collects `dependencies.scripts` lazy entries with `ssr: true`, resolves the
|
|
9
|
-
* processed entrypoint through the asset pipeline, and imports the module so
|
|
10
|
-
* custom elements are registered before SSR rendering.
|
|
11
|
-
*/
|
|
12
|
-
export class LitSsrLazyPreloader {
|
|
13
|
-
resolveDependencyPath;
|
|
14
|
-
processDependencies;
|
|
15
|
-
preferSourceImports;
|
|
16
|
-
ssrPreloadedScripts = new Set();
|
|
17
|
-
ssrPreloadFailedScripts = new Set();
|
|
18
|
-
ssrPreloadEntrypointCache = new Map();
|
|
19
|
-
constructor({ resolveDependencyPath, processDependencies, preferSourceImports }) {
|
|
20
|
-
this.resolveDependencyPath = resolveDependencyPath;
|
|
21
|
-
this.processDependencies = processDependencies;
|
|
22
|
-
this.preferSourceImports = preferSourceImports ?? typeof Bun !== 'undefined';
|
|
23
|
-
}
|
|
24
|
-
getErrorCode(error) {
|
|
25
|
-
if (typeof error !== 'object' || error === null) {
|
|
26
|
-
return '';
|
|
27
|
-
}
|
|
28
|
-
const code = Reflect.get(error, 'code');
|
|
29
|
-
return code === undefined ? '' : String(code);
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Detects preload failures that are expected for browser-only modules.
|
|
33
|
-
*/
|
|
34
|
-
isExpectedSsrPreloadError(error) {
|
|
35
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
36
|
-
const errorCode = this.getErrorCode(error);
|
|
37
|
-
if (errorCode === 'ERR_UNKNOWN_FILE_EXTENSION') {
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
return (errorMessage.includes(`reading 'Element'`) ||
|
|
41
|
-
errorMessage.includes('window is not defined') ||
|
|
42
|
-
errorMessage.includes('document is not defined') ||
|
|
43
|
-
errorMessage.includes('navigator is not defined'));
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Collects lazy script file paths eligible for SSR preloading.
|
|
47
|
-
*/
|
|
48
|
-
collectSsrPreloadScripts(components) {
|
|
49
|
-
const scriptPaths = new Set();
|
|
50
|
-
const visitedConfigs = new Set();
|
|
51
|
-
const collect = (component) => {
|
|
52
|
-
const config = component?.config;
|
|
53
|
-
if (!config || visitedConfigs.has(config)) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
visitedConfigs.add(config);
|
|
57
|
-
const scriptEntries = config.dependencies?.scripts ?? [];
|
|
58
|
-
const componentFile = getComponentIdentity(component)?.file;
|
|
59
|
-
if (componentFile) {
|
|
60
|
-
const componentDir = path.dirname(componentFile);
|
|
61
|
-
for (const script of scriptEntries) {
|
|
62
|
-
if (typeof script === 'string') {
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
if (!script.lazy || script.ssr !== true) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
if (!script.src) {
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
scriptPaths.add(this.resolveDependencyPath(componentDir, script.src));
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
for (const layout of config.layouts ?? []) {
|
|
75
|
-
collect(layout);
|
|
76
|
-
}
|
|
77
|
-
for (const nestedComponent of config.dependencies?.components || []) {
|
|
78
|
-
collect(nestedComponent);
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
for (const component of components) {
|
|
82
|
-
collect(component);
|
|
83
|
-
}
|
|
84
|
-
return Array.from(scriptPaths);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Preloads SSR-eligible lazy scripts to register custom elements before render.
|
|
88
|
-
*/
|
|
89
|
-
async preloadSsrLazyScripts(components) {
|
|
90
|
-
const scripts = this.collectSsrPreloadScripts(components);
|
|
91
|
-
if (scripts.length === 0) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
await Promise.all(scripts
|
|
95
|
-
.filter((scriptPath) => {
|
|
96
|
-
if (this.ssrPreloadedScripts.has(scriptPath)) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
if (this.ssrPreloadFailedScripts.has(scriptPath)) {
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
return true;
|
|
103
|
-
})
|
|
104
|
-
.map(async (scriptPath) => {
|
|
105
|
-
const preloadEntrypoint = await this.resolveSsrPreloadEntrypoint(scriptPath);
|
|
106
|
-
if (!preloadEntrypoint) {
|
|
107
|
-
this.ssrPreloadFailedScripts.add(scriptPath);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
try {
|
|
111
|
-
await import(/* @vite-ignore */ pathToFileURL(preloadEntrypoint).href);
|
|
112
|
-
this.ssrPreloadedScripts.add(scriptPath);
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
this.ssrPreloadFailedScripts.add(scriptPath);
|
|
116
|
-
if (this.isExpectedSsrPreloadError(error)) {
|
|
117
|
-
if (process.env.ECOPAGES_DEBUG === 'true') {
|
|
118
|
-
console.warn(`[ecopages][lit] Skipping SSR preload for browser-only lazy script: ${scriptPath}`);
|
|
119
|
-
}
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
console.warn(`[ecopages][lit] Failed to preload lazy SSR script: ${scriptPath}`, error);
|
|
123
|
-
}
|
|
124
|
-
}));
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Resolves the concrete JS entrypoint used for SSR preloading.
|
|
128
|
-
*/
|
|
129
|
-
async resolveSsrPreloadEntrypoint(scriptPath) {
|
|
130
|
-
const cachedEntrypoint = this.ssrPreloadEntrypointCache.get(scriptPath);
|
|
131
|
-
if (cachedEntrypoint) {
|
|
132
|
-
return cachedEntrypoint;
|
|
133
|
-
}
|
|
134
|
-
if (this.preferSourceImports) {
|
|
135
|
-
this.ssrPreloadEntrypointCache.set(scriptPath, scriptPath);
|
|
136
|
-
return scriptPath;
|
|
137
|
-
}
|
|
138
|
-
if (!this.processDependencies) {
|
|
139
|
-
this.ssrPreloadEntrypointCache.set(scriptPath, scriptPath);
|
|
140
|
-
return scriptPath;
|
|
141
|
-
}
|
|
142
|
-
try {
|
|
143
|
-
const processed = await this.processDependencies([
|
|
144
|
-
AssetFactory.createInlineFileScript({
|
|
145
|
-
filepath: scriptPath,
|
|
146
|
-
position: 'head',
|
|
147
|
-
bundle: true,
|
|
148
|
-
attributes: {
|
|
149
|
-
type: 'module',
|
|
150
|
-
defer: '',
|
|
151
|
-
},
|
|
152
|
-
}),
|
|
153
|
-
], `lit-ssr-preload:${scriptPath}`);
|
|
154
|
-
const entrypoint = processed[0]?.filepath;
|
|
155
|
-
if (!entrypoint) {
|
|
156
|
-
return scriptPath;
|
|
157
|
-
}
|
|
158
|
-
this.ssrPreloadEntrypointCache.set(scriptPath, entrypoint);
|
|
159
|
-
return entrypoint;
|
|
160
|
-
}
|
|
161
|
-
catch (error) {
|
|
162
|
-
if (process.env.ECOPAGES_DEBUG === 'true') {
|
|
163
|
-
console.warn(`[ecopages][lit] Failed to resolve SSR preload entrypoint for: ${scriptPath}`, error);
|
|
164
|
-
}
|
|
165
|
-
this.ssrPreloadEntrypointCache.set(scriptPath, scriptPath);
|
|
166
|
-
return scriptPath;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|