@ecopages/lit 0.2.0-rc.6 → 0.2.0-rc.8
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 +11 -2
- package/package.json +2 -2
- package/src/STATIC_RENDER.md +2 -0
- package/src/dom-shim.js +40 -6
- 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 +37 -30
- 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 +7 -9
- package/src/lit-static-render-session.js +17 -7
- package/src/lit.plugin.d.ts +12 -0
- package/src/lit.plugin.js +25 -14
- package/src/lit-server-module-loader.d.ts +0 -6
- package/src/lit-server-module-loader.js +0 -18
- package/src/lit-ssr-lazy-preloader.d.ts +0 -47
- package/src/lit-ssr-lazy-preloader.js +0 -198
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
|
|
|
@@ -57,10 +65,11 @@ Important:
|
|
|
57
65
|
|
|
58
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.
|
|
61
70
|
|
|
62
71
|
## Discovered assets and lazy custom elements
|
|
63
72
|
|
|
64
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.
|
|
65
74
|
|
|
66
|
-
Keep custom-element registration explicit with `scripts: [{ src: './counter.script.ts', ssr: true, lazy: { 'on:visible': true } }]`.
|
|
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.8",
|
|
4
4
|
"description": "Ecopages lit integration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ecopages",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"directory": "packages/integrations/lit"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@ecopages/core": "0.2.0-rc.
|
|
45
|
+
"@ecopages/core": "0.2.0-rc.8",
|
|
46
46
|
"@lit-labs/ssr": "^3.3.0",
|
|
47
47
|
"@lit-labs/ssr-client": "^1.1.7",
|
|
48
48
|
"lit": "^3.2.1"
|
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/dom-shim.js
CHANGED
|
@@ -2,15 +2,49 @@ 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
4
|
export function ensureLitDomShim() {
|
|
5
|
-
if (typeof globalThis.
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
if (typeof globalThis.window === 'undefined') {
|
|
6
|
+
installWindowOnGlobal();
|
|
7
|
+
}
|
|
8
|
+
if (typeof globalThis.document !== 'undefined' && typeof globalThis.document.createTreeWalker !== 'function') {
|
|
9
|
+
Object.defineProperty(globalThis.document, 'createTreeWalker', {
|
|
10
|
+
configurable: true,
|
|
11
|
+
writable: true,
|
|
12
|
+
value: () => ({}),
|
|
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
|
+
});
|
|
8
33
|
}
|
|
9
|
-
|
|
10
|
-
Object.defineProperty(
|
|
34
|
+
if (typeof target.prototype.attachShadow !== 'function') {
|
|
35
|
+
Object.defineProperty(target.prototype, 'attachShadow', {
|
|
11
36
|
configurable: true,
|
|
12
37
|
writable: true,
|
|
13
|
-
value: (
|
|
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
|
+
},
|
|
14
48
|
});
|
|
15
49
|
}
|
|
16
50
|
}
|
|
@@ -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
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* This module contains the Lit renderer
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
3
5
|
import { IntegrationRenderer, } from '@ecopages/core/route-renderer/orchestration/integration-renderer';
|
|
4
6
|
import { resolveDocumentShellLayouts } from '@ecopages/core/route-renderer/orchestration/document-shell/layout-shell-props.service';
|
|
5
7
|
import { getForeignSubtreeResolutionContextKey, resolveOwningIntegrationRenderer, } from '@ecopages/core/route-renderer/orchestration/foreign-child/owning-renderer-resolution';
|
|
6
|
-
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';
|
|
7
11
|
import { LIT_PLUGIN_NAME } from './lit.constants.js';
|
|
8
12
|
import { injectLitRenderedChildren, LIT_COMPONENT_CHILDREN_SLOT_MARKER, normalizeLitHtml, renderLitValueToString, } from './utils/lit-html-rendering.js';
|
|
9
13
|
/**
|
|
@@ -12,10 +16,18 @@ import { injectLitRenderedChildren, LIT_COMPONENT_CHILDREN_SLOT_MARKER, normaliz
|
|
|
12
16
|
export class LitRenderer extends IntegrationRenderer {
|
|
13
17
|
name = LIT_PLUGIN_NAME;
|
|
14
18
|
getRenderSession;
|
|
19
|
+
ssrScriptPreloader;
|
|
15
20
|
constructor(options) {
|
|
16
21
|
const { getRenderSession, ...rendererOptions } = options;
|
|
17
22
|
super(rendererOptions);
|
|
18
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
|
+
});
|
|
19
31
|
}
|
|
20
32
|
/**
|
|
21
33
|
* Renders a Lit page route, preferring the plugin worker when available.
|
|
@@ -73,14 +85,14 @@ export class LitRenderer extends IntegrationRenderer {
|
|
|
73
85
|
/**
|
|
74
86
|
* Renders a Lit component for component-level orchestration.
|
|
75
87
|
*
|
|
76
|
-
* SSR-eligible
|
|
88
|
+
* SSR-eligible scripts are preloaded first so custom elements registered
|
|
77
89
|
* by the component can render their server markup even when the Lit renderer is
|
|
78
90
|
* entered through cross-integration foreign-child handoff.
|
|
79
91
|
*
|
|
80
92
|
* Includes component-scoped dependency assets when declared.
|
|
81
93
|
*/
|
|
82
94
|
async renderComponent(input) {
|
|
83
|
-
await this.
|
|
95
|
+
await this.preloadSsrScripts([input.component]);
|
|
84
96
|
if (!this.isFunctionComponent(input.component)) {
|
|
85
97
|
throw new TypeError('Lit renderer expected a callable component.');
|
|
86
98
|
}
|
|
@@ -133,11 +145,6 @@ export class LitRenderer extends IntegrationRenderer {
|
|
|
133
145
|
]),
|
|
134
146
|
});
|
|
135
147
|
}
|
|
136
|
-
ssrLazyPreloader = new LitSsrLazyPreloader({
|
|
137
|
-
importServerModule: createLitServerModuleImporter(this.appConfig),
|
|
138
|
-
resolveDependencyPath: this.resolveDependencyPath.bind(this),
|
|
139
|
-
processDependencies: this.assetProcessingService?.processDependencies?.bind(this.assetProcessingService),
|
|
140
|
-
});
|
|
141
148
|
/**
|
|
142
149
|
* Detects preload failures that are expected for browser-only modules.
|
|
143
150
|
*
|
|
@@ -145,41 +152,40 @@ export class LitRenderer extends IntegrationRenderer {
|
|
|
145
152
|
* lazy client scripts intentionally depend on browser globals.
|
|
146
153
|
*/
|
|
147
154
|
isExpectedSsrPreloadError(error) {
|
|
148
|
-
return this.
|
|
155
|
+
return this.ssrScriptPreloader.isExpectedSsrPreloadError(error);
|
|
149
156
|
}
|
|
150
157
|
/**
|
|
151
|
-
* Collects
|
|
158
|
+
* Collects script file paths eligible for SSR preloading.
|
|
152
159
|
*
|
|
153
|
-
*
|
|
160
|
+
* Per-entry script dependencies with `ssr: true` are collected (eager or lazy).
|
|
154
161
|
* File-backed entries are required (`src` must be present);
|
|
155
162
|
* inline content lazy entries are intentionally skipped.
|
|
156
163
|
*/
|
|
157
164
|
collectSsrPreloadScripts(components) {
|
|
158
|
-
return this.
|
|
165
|
+
return this.ssrScriptPreloader.collectSsrPreloadScripts(components);
|
|
159
166
|
}
|
|
160
167
|
/**
|
|
161
|
-
* 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`.
|
|
162
176
|
*/
|
|
163
|
-
async
|
|
177
|
+
async preloadSsrScripts(components) {
|
|
178
|
+
ensureLitDomShim();
|
|
164
179
|
const renderSession = this.getRenderSession?.();
|
|
165
180
|
if (renderSession) {
|
|
166
|
-
await renderSession.
|
|
181
|
+
await renderSession.preloadSsrScripts(components);
|
|
167
182
|
return;
|
|
168
183
|
}
|
|
169
|
-
await this.
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Resolves the concrete JS entrypoint used for SSR preloading.
|
|
173
|
-
*
|
|
174
|
-
* Scripts are passed through the asset pipeline so preload imports can use
|
|
175
|
-
* the same processed output shape as runtime dependencies.
|
|
176
|
-
*/
|
|
177
|
-
async resolveSsrPreloadEntrypoint(scriptPath) {
|
|
178
|
-
return this.ssrLazyPreloader.resolveSsrPreloadEntrypoint(scriptPath);
|
|
184
|
+
await this.ssrScriptPreloader.preloadSsrScripts(components);
|
|
179
185
|
}
|
|
180
|
-
async render({ params, query, props, locals, metadata, Page, Layout, layoutEntries, HtmlTemplate, }) {
|
|
186
|
+
async render({ params, query, props, locals, metadata, Page, Layout, layoutEntries, HtmlTemplate, resolvedPageDependencyComponents, }) {
|
|
181
187
|
try {
|
|
182
|
-
await this.
|
|
188
|
+
await this.preloadSsrScripts([Page, Layout, ...(resolvedPageDependencyComponents ?? [])]);
|
|
183
189
|
return await this.renderPageWithDocumentShell({
|
|
184
190
|
page: {
|
|
185
191
|
component: Page,
|
|
@@ -200,6 +206,7 @@ export class LitRenderer extends IntegrationRenderer {
|
|
|
200
206
|
htmlTemplate: HtmlTemplate,
|
|
201
207
|
metadata,
|
|
202
208
|
pageProps: props || {},
|
|
209
|
+
foreignChildRoots: resolvedPageDependencyComponents,
|
|
203
210
|
transformDocumentHtml: normalizeLitHtml,
|
|
204
211
|
});
|
|
205
212
|
}
|
|
@@ -211,7 +218,7 @@ export class LitRenderer extends IntegrationRenderer {
|
|
|
211
218
|
try {
|
|
212
219
|
const layouts = view.config?.layouts;
|
|
213
220
|
const Layout = layouts?.[layouts.length - 1];
|
|
214
|
-
await this.
|
|
221
|
+
await this.preloadSsrScripts([view, Layout]);
|
|
215
222
|
return await this.renderViewWithDocumentShell({
|
|
216
223
|
view,
|
|
217
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,17 +1,15 @@
|
|
|
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
|
|
4
|
-
import { type LitSsrLazyPreloaderOptions } from './lit-ssr-lazy-preloader.js';
|
|
3
|
+
import { type CustomElementScriptPreloaderOptions, type CustomElementSsrPreloadComponent } from '@ecopages/core/route-renderer/orchestration/custom-element-scripts/custom-element-script-preloader';
|
|
5
4
|
import { LitStaticRenderWorkerClient } from './lit-static-render-worker-client.js';
|
|
6
5
|
import type { LitStaticRenderCacheStrategy } from './lit-static-render-protocol.js';
|
|
6
|
+
import type { AssetDefinition, ProcessedAsset } from '@ecopages/core/services/asset-processing-service';
|
|
7
7
|
type LitStaticRenderWorkerClientContract = Pick<LitStaticRenderWorkerClient, 'start' | 'renderPage' | 'dispose'>;
|
|
8
8
|
type LitStaticRenderSessionOptions = {
|
|
9
9
|
resolveDependencyPath: (componentDir: string, sourcePath: string) => string;
|
|
10
|
-
processDependencies?: (dependencies: AssetDefinition[], integrationName: string) => Promise<Array<{
|
|
11
|
-
filepath?: string;
|
|
12
|
-
}>>;
|
|
13
10
|
preferSourceImports?: boolean;
|
|
14
|
-
|
|
11
|
+
processDependencies?: (dependencies: AssetDefinition[], integrationName: string) => Promise<ProcessedAsset[]>;
|
|
12
|
+
importServerModule?: CustomElementScriptPreloaderOptions['importServerModule'];
|
|
15
13
|
getInvalidationVersion?: () => number;
|
|
16
14
|
createWorkerClient?: (input: {
|
|
17
15
|
configModulePath: string;
|
|
@@ -51,8 +49,8 @@ export declare class LitStaticRenderSession {
|
|
|
51
49
|
runtimeOrigin: string;
|
|
52
50
|
}): Promise<void>;
|
|
53
51
|
preloadStaticRoutes(context: StaticExportContext): Promise<void>;
|
|
54
|
-
|
|
55
|
-
collectSsrPreloadScripts(components: Array<
|
|
52
|
+
preloadSsrScripts(components: Array<CustomElementSsrPreloadComponent | undefined>): Promise<void>;
|
|
53
|
+
collectSsrPreloadScripts(components: Array<CustomElementSsrPreloadComponent | undefined>): string[];
|
|
56
54
|
renderPageInWorker(input: {
|
|
57
55
|
filePath: string;
|
|
58
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.
|
|
@@ -24,10 +26,17 @@ export class LitStaticRenderSession {
|
|
|
24
26
|
renderChain = Promise.resolve();
|
|
25
27
|
constructor(options) {
|
|
26
28
|
this.getInvalidationVersion = options.getInvalidationVersion ?? (() => 0);
|
|
27
|
-
|
|
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,
|
|
28
34
|
resolveDependencyPath: options.resolveDependencyPath,
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
preferSourceImports,
|
|
36
|
+
resolvePreloadEntrypoint: createLitSsrPreloadEntrypointResolver({
|
|
37
|
+
preferSourceImports,
|
|
38
|
+
processDependencies: options.processDependencies,
|
|
39
|
+
}),
|
|
31
40
|
importServerModule: options.importServerModule,
|
|
32
41
|
});
|
|
33
42
|
this.createWorkerClient =
|
|
@@ -68,8 +77,9 @@ export class LitStaticRenderSession {
|
|
|
68
77
|
async preloadStaticRoutes(context) {
|
|
69
78
|
await this.preloadLitRoutes(context);
|
|
70
79
|
}
|
|
71
|
-
async
|
|
72
|
-
|
|
80
|
+
async preloadSsrScripts(components) {
|
|
81
|
+
ensureLitDomShim();
|
|
82
|
+
await this.preloader.preloadSsrScripts(components);
|
|
73
83
|
}
|
|
74
84
|
collectSsrPreloadScripts(components) {
|
|
75
85
|
return this.preloader.collectSsrPreloadScripts(components);
|
|
@@ -124,7 +134,7 @@ export class LitStaticRenderSession {
|
|
|
124
134
|
}
|
|
125
135
|
}
|
|
126
136
|
}
|
|
127
|
-
await this.
|
|
137
|
+
await this.preloadSsrScripts(components);
|
|
128
138
|
}
|
|
129
139
|
isLitRoute(filePath, appConfig) {
|
|
130
140
|
const litIntegration = appConfig.integrations.find((plugin) => plugin.name === LIT_PLUGIN_NAME);
|
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,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
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';
|
|
2
3
|
/**
|
|
3
4
|
* This module contains the Lit plugin
|
|
4
5
|
* @module
|
|
@@ -36,20 +37,18 @@ export class LitPlugin extends IntegrationPlugin {
|
|
|
36
37
|
});
|
|
37
38
|
this.integrationDependencies.unshift(...this.getDependencies());
|
|
38
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
|
+
*/
|
|
39
50
|
getDependencies() {
|
|
40
51
|
return [
|
|
41
|
-
/**
|
|
42
|
-
* BUG ALERT
|
|
43
|
-
* Due to an issue appeared in Bun 1.2.2, we need to use a workaround to import the hydrate script.
|
|
44
|
-
* This is a temporary solution until the issue is resolved.
|
|
45
|
-
* The litElementHydrateScript is the same file built on Bun 1.1.45.
|
|
46
|
-
* https://github.com/oven-sh/bun/issues/17180
|
|
47
|
-
*
|
|
48
|
-
* AssetFactory.createNodeModuleScript({
|
|
49
|
-
* position: 'head',
|
|
50
|
-
* importPath: '@lit-labs/ssr-client/lit-element-hydrate-support.js'
|
|
51
|
-
* })
|
|
52
|
-
*/
|
|
53
52
|
AssetFactory.createInlineContentScript({
|
|
54
53
|
position: 'head',
|
|
55
54
|
content: `(() => {${litElementHydrateScript}})();`,
|
|
@@ -68,7 +67,7 @@ export class LitPlugin extends IntegrationPlugin {
|
|
|
68
67
|
resolveDependencyPath: (componentDir, sourcePath) => path.join(componentDir, sourcePath),
|
|
69
68
|
processDependencies: this.assetProcessingService.processDependencies.bind(this.assetProcessingService),
|
|
70
69
|
getInvalidationVersion: () => this.appConfig?.runtime?.serverInvalidationState?.getServerInvalidationVersion() ?? 0,
|
|
71
|
-
importServerModule:
|
|
70
|
+
importServerModule: createCustomElementServerModuleImporter(this.appConfig, '.lit-ssr'),
|
|
72
71
|
preferSourceImports: typeof Bun !== 'undefined',
|
|
73
72
|
});
|
|
74
73
|
}
|
|
@@ -79,6 +78,18 @@ export class LitPlugin extends IntegrationPlugin {
|
|
|
79
78
|
});
|
|
80
79
|
return this.attachRendererRuntimeServices(renderer);
|
|
81
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
|
+
}
|
|
82
93
|
async setup() {
|
|
83
94
|
await super.setup();
|
|
84
95
|
if (!this.appConfig?.absolutePaths?.config) {
|
|
@@ -1,6 +0,0 @@
|
|
|
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>;
|
|
@@ -1,18 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,47 +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
|
-
importServerModule?: (scriptPath: string, registryKey: string) => Promise<unknown>;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Encapsulates SSR lazy script preload behavior for Lit components.
|
|
14
|
-
*
|
|
15
|
-
* Collects `dependencies.scripts` lazy entries with `ssr: true`, resolves the
|
|
16
|
-
* processed entrypoint through the asset pipeline, and imports the module so
|
|
17
|
-
* custom elements are registered before SSR rendering.
|
|
18
|
-
*/
|
|
19
|
-
export declare class LitSsrLazyPreloader {
|
|
20
|
-
private readonly resolveDependencyPath;
|
|
21
|
-
private readonly processDependencies?;
|
|
22
|
-
private readonly preferSourceImports;
|
|
23
|
-
private readonly importServerModule?;
|
|
24
|
-
private activeRegistry;
|
|
25
|
-
private readonly ssrPreloadedScripts;
|
|
26
|
-
private readonly ssrPreloadFailedScripts;
|
|
27
|
-
private readonly ssrPreloadEntrypointCache;
|
|
28
|
-
constructor({ resolveDependencyPath, processDependencies, preferSourceImports, importServerModule, }: LitSsrLazyPreloaderOptions);
|
|
29
|
-
private getErrorCode;
|
|
30
|
-
/**
|
|
31
|
-
* Detects preload failures that are expected for browser-only modules.
|
|
32
|
-
*/
|
|
33
|
-
isExpectedSsrPreloadError(error: unknown): boolean;
|
|
34
|
-
/**
|
|
35
|
-
* Collects lazy script file paths eligible for SSR preloading.
|
|
36
|
-
*/
|
|
37
|
-
collectSsrPreloadScripts(components: Array<EcoComponent | undefined>): string[];
|
|
38
|
-
/**
|
|
39
|
-
* Preloads SSR-eligible lazy scripts to register custom elements before render.
|
|
40
|
-
*/
|
|
41
|
-
preloadSsrLazyScripts(components: Array<EcoComponent | undefined>): Promise<void>;
|
|
42
|
-
/**
|
|
43
|
-
* Resolves the concrete JS entrypoint used for SSR preloading.
|
|
44
|
-
*/
|
|
45
|
-
resolveSsrPreloadEntrypoint(scriptPath: string): Promise<string | null>;
|
|
46
|
-
}
|
|
47
|
-
export {};
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { ensureLitDomShim } from './dom-shim.js';
|
|
2
|
-
import { getComponentIdentity } from '@ecopages/core';
|
|
3
|
-
import { AssetFactory } from '@ecopages/core/services/asset-processing-service';
|
|
4
|
-
import path from 'node:path';
|
|
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
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Encapsulates SSR lazy script preload behavior for Lit components.
|
|
18
|
-
*
|
|
19
|
-
* Collects `dependencies.scripts` lazy entries with `ssr: true`, resolves the
|
|
20
|
-
* processed entrypoint through the asset pipeline, and imports the module so
|
|
21
|
-
* custom elements are registered before SSR rendering.
|
|
22
|
-
*/
|
|
23
|
-
export class LitSsrLazyPreloader {
|
|
24
|
-
resolveDependencyPath;
|
|
25
|
-
processDependencies;
|
|
26
|
-
preferSourceImports;
|
|
27
|
-
importServerModule;
|
|
28
|
-
activeRegistry;
|
|
29
|
-
ssrPreloadedScripts = new Set();
|
|
30
|
-
ssrPreloadFailedScripts = new Set();
|
|
31
|
-
ssrPreloadEntrypointCache = new Map();
|
|
32
|
-
constructor({ resolveDependencyPath, processDependencies, preferSourceImports, importServerModule, }) {
|
|
33
|
-
this.importServerModule = importServerModule;
|
|
34
|
-
this.resolveDependencyPath = resolveDependencyPath;
|
|
35
|
-
this.processDependencies = processDependencies;
|
|
36
|
-
this.preferSourceImports = preferSourceImports ?? typeof Bun !== 'undefined';
|
|
37
|
-
}
|
|
38
|
-
getErrorCode(error) {
|
|
39
|
-
if (typeof error !== 'object' || error === null) {
|
|
40
|
-
return '';
|
|
41
|
-
}
|
|
42
|
-
const code = Reflect.get(error, 'code');
|
|
43
|
-
return code === undefined ? '' : String(code);
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Detects preload failures that are expected for browser-only modules.
|
|
47
|
-
*/
|
|
48
|
-
isExpectedSsrPreloadError(error) {
|
|
49
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
50
|
-
const errorCode = this.getErrorCode(error);
|
|
51
|
-
if (errorCode === 'ERR_UNKNOWN_FILE_EXTENSION') {
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
return (errorMessage.includes(`reading 'Element'`) ||
|
|
55
|
-
errorMessage.includes('window is not defined') ||
|
|
56
|
-
errorMessage.includes('document is not defined') ||
|
|
57
|
-
errorMessage.includes('navigator is not defined'));
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Collects lazy script file paths eligible for SSR preloading.
|
|
61
|
-
*/
|
|
62
|
-
collectSsrPreloadScripts(components) {
|
|
63
|
-
const scriptPaths = new Set();
|
|
64
|
-
const visitedConfigs = new Set();
|
|
65
|
-
const collect = (component) => {
|
|
66
|
-
const config = component?.config;
|
|
67
|
-
if (!config || visitedConfigs.has(config)) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
visitedConfigs.add(config);
|
|
71
|
-
const scriptEntries = config.dependencies?.scripts ?? [];
|
|
72
|
-
const componentFile = getComponentIdentity(component)?.file;
|
|
73
|
-
if (componentFile) {
|
|
74
|
-
const componentDir = path.dirname(componentFile);
|
|
75
|
-
for (const script of scriptEntries) {
|
|
76
|
-
if (typeof script === 'string') {
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
if (!script.lazy || script.ssr !== true) {
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
if (!script.src) {
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
scriptPaths.add(this.resolveDependencyPath(componentDir, script.src));
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
for (const layout of config.layouts ?? []) {
|
|
89
|
-
collect(layout);
|
|
90
|
-
}
|
|
91
|
-
for (const nestedComponent of config.dependencies?.components || []) {
|
|
92
|
-
collect(nestedComponent);
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
for (const component of components) {
|
|
96
|
-
collect(component);
|
|
97
|
-
}
|
|
98
|
-
return Array.from(scriptPaths);
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Preloads SSR-eligible lazy scripts to register custom elements before render.
|
|
102
|
-
*/
|
|
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
|
-
}
|
|
111
|
-
const scripts = this.collectSsrPreloadScripts(components);
|
|
112
|
-
if (scripts.length === 0) {
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
await Promise.all(scripts
|
|
116
|
-
.filter((scriptPath) => {
|
|
117
|
-
if (this.ssrPreloadedScripts.has(scriptPath)) {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
if (this.ssrPreloadFailedScripts.has(scriptPath)) {
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
return true;
|
|
124
|
-
})
|
|
125
|
-
.map(async (scriptPath) => {
|
|
126
|
-
try {
|
|
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
|
-
}
|
|
140
|
-
this.ssrPreloadedScripts.add(scriptPath);
|
|
141
|
-
}
|
|
142
|
-
catch (error) {
|
|
143
|
-
this.ssrPreloadFailedScripts.add(scriptPath);
|
|
144
|
-
if (this.isExpectedSsrPreloadError(error)) {
|
|
145
|
-
if (process.env.ECOPAGES_DEBUG === 'true') {
|
|
146
|
-
console.warn(`[ecopages][lit] Skipping SSR preload for browser-only lazy script: ${scriptPath}`);
|
|
147
|
-
}
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
console.warn(`[ecopages][lit] Failed to preload lazy SSR script: ${scriptPath}`, error);
|
|
151
|
-
}
|
|
152
|
-
}));
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Resolves the concrete JS entrypoint used for SSR preloading.
|
|
156
|
-
*/
|
|
157
|
-
async resolveSsrPreloadEntrypoint(scriptPath) {
|
|
158
|
-
const cachedEntrypoint = this.ssrPreloadEntrypointCache.get(scriptPath);
|
|
159
|
-
if (cachedEntrypoint) {
|
|
160
|
-
return cachedEntrypoint;
|
|
161
|
-
}
|
|
162
|
-
if (this.preferSourceImports) {
|
|
163
|
-
this.ssrPreloadEntrypointCache.set(scriptPath, scriptPath);
|
|
164
|
-
return scriptPath;
|
|
165
|
-
}
|
|
166
|
-
if (!this.processDependencies) {
|
|
167
|
-
this.ssrPreloadEntrypointCache.set(scriptPath, scriptPath);
|
|
168
|
-
return scriptPath;
|
|
169
|
-
}
|
|
170
|
-
try {
|
|
171
|
-
const processed = await this.processDependencies([
|
|
172
|
-
AssetFactory.createInlineFileScript({
|
|
173
|
-
filepath: scriptPath,
|
|
174
|
-
excludeFromHtml: true,
|
|
175
|
-
position: 'head',
|
|
176
|
-
bundle: true,
|
|
177
|
-
attributes: {
|
|
178
|
-
type: 'module',
|
|
179
|
-
defer: '',
|
|
180
|
-
},
|
|
181
|
-
}),
|
|
182
|
-
], `lit-ssr-preload:${scriptPath}`);
|
|
183
|
-
const entrypoint = processed[0]?.filepath;
|
|
184
|
-
if (!entrypoint) {
|
|
185
|
-
return scriptPath;
|
|
186
|
-
}
|
|
187
|
-
this.ssrPreloadEntrypointCache.set(scriptPath, entrypoint);
|
|
188
|
-
return entrypoint;
|
|
189
|
-
}
|
|
190
|
-
catch (error) {
|
|
191
|
-
if (process.env.ECOPAGES_DEBUG === 'true') {
|
|
192
|
-
console.warn(`[ecopages][lit] Failed to resolve SSR preload entrypoint for: ${scriptPath}`, error);
|
|
193
|
-
}
|
|
194
|
-
this.ssrPreloadEntrypointCache.set(scriptPath, scriptPath);
|
|
195
|
-
return scriptPath;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|