@ecopages/lit 0.2.0-alpha.5 → 0.2.0-alpha.50
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 +39 -7
- package/package.json +2 -2
- package/src/lit-renderer.d.ts +11 -1
- package/src/lit-renderer.js +147 -66
- package/src/lit-ssr-lazy-preloader.d.ts +4 -1
- package/src/lit-ssr-lazy-preloader.js +21 -3
- package/src/lit.constants.d.ts +1 -0
- package/src/lit.constants.js +4 -0
- package/src/lit.plugin.d.ts +1 -2
- package/src/lit.plugin.js +4 -5
- package/src/utils/lit-html-rendering.d.ts +5 -0
- package/src/utils/lit-html-rendering.js +52 -0
- package/CHANGELOG.md +0 -21
- package/src/console.ts +0 -19
- package/src/lit-element-hydrate.ts +0 -2
- package/src/lit-renderer.ts +0 -210
- package/src/lit-ssr-lazy-preloader.ts +0 -202
- package/src/lit.plugin.ts +0 -70
- package/src/styled-mixin.ts +0 -27
package/README.md
CHANGED
|
@@ -1,19 +1,35 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ecopages/lit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Integration plugin for [Lit](https://lit.dev/) in Ecopages. Use it when Lit should own `.lit.tsx` routes or when another integration needs Lit to render nested custom-element boundaries.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
bun add @ecopages/lit lit @lit-labs/ssr @lit-labs/ssr-client
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
`lit`, `@lit-labs/ssr`, and `@lit-labs/ssr-client` are required peer dependencies for this package.
|
|
12
|
+
|
|
11
13
|
## Usage
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
Register `litPlugin()` in your `eco.config.ts`.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { ConfigBuilder } from '@ecopages/core/config-builder';
|
|
19
|
+
import { litPlugin } from '@ecopages/lit';
|
|
20
|
+
|
|
21
|
+
const config = await new ConfigBuilder()
|
|
22
|
+
.setBaseUrl(import.meta.env.ECOPAGES_BASE_URL)
|
|
23
|
+
.setIntegrations([litPlugin()])
|
|
24
|
+
.build();
|
|
25
|
+
|
|
26
|
+
export default config;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Lit also works well alongside an HTML-first renderer such as `@ecopages/kitajs` when you want Lit to own only the nested custom elements:
|
|
14
30
|
|
|
15
31
|
```ts
|
|
16
|
-
import { ConfigBuilder } from '@ecopages/core';
|
|
32
|
+
import { ConfigBuilder } from '@ecopages/core/config-builder';
|
|
17
33
|
import { kitajsPlugin } from '@ecopages/kitajs';
|
|
18
34
|
import { litPlugin } from '@ecopages/lit';
|
|
19
35
|
|
|
@@ -25,4 +41,20 @@ const config = await new ConfigBuilder()
|
|
|
25
41
|
export default config;
|
|
26
42
|
```
|
|
27
43
|
|
|
28
|
-
|
|
44
|
+
This setup lets Kita own the page shell while Lit owns the nested Lit component boundaries.
|
|
45
|
+
|
|
46
|
+
## What This Integration Owns
|
|
47
|
+
|
|
48
|
+
- `.lit.tsx` route files.
|
|
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.
|
|
51
|
+
|
|
52
|
+
## Mixed Rendering
|
|
53
|
+
|
|
54
|
+
When a non-Lit render pass reaches a Lit-owned foreign child, Ecopages hands that foreign subtree to the Lit renderer. That keeps Lit SSR in charge of custom elements, declarative shadow DOM, and Lit-managed child content.
|
|
55
|
+
|
|
56
|
+
Important:
|
|
57
|
+
|
|
58
|
+
- Components that may render foreign children must declare those children in `config.dependencies.components`.
|
|
59
|
+
- Ecopages validates ownership from declared dependencies during render preparation instead of relying on post-render HTML discovery.
|
|
60
|
+
- Lit keeps slot transport, shadow-root handling, and SSR preload behavior inside the Lit renderer.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecopages/lit",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.50",
|
|
4
4
|
"description": "Ecopages lit integration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ecopages",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"directory": "packages/integrations/lit"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@ecopages/core": "0.2.0-alpha.
|
|
34
|
+
"@ecopages/core": "0.2.0-alpha.50",
|
|
35
35
|
"@lit-labs/ssr": "^3.3.0",
|
|
36
36
|
"@lit-labs/ssr-client": "^1.1.7",
|
|
37
37
|
"lit": "^3.2.1"
|
package/src/lit-renderer.d.ts
CHANGED
|
@@ -3,14 +3,24 @@
|
|
|
3
3
|
* @module
|
|
4
4
|
*/
|
|
5
5
|
import type { ComponentRenderInput, ComponentRenderResult, EcoComponent, EcoPagesElement, IntegrationRendererRenderOptions, RouteRendererBody } from '@ecopages/core';
|
|
6
|
+
import '@lit-labs/ssr/lib/install-global-dom-shim.js';
|
|
6
7
|
import { IntegrationRenderer, type RenderToResponseContext } from '@ecopages/core/route-renderer/integration-renderer';
|
|
7
8
|
/**
|
|
8
9
|
* A renderer for the Lit integration.
|
|
9
10
|
*/
|
|
10
11
|
export declare class LitRenderer extends IntegrationRenderer<EcoPagesElement> {
|
|
11
12
|
name: string;
|
|
13
|
+
private isFunctionComponent;
|
|
14
|
+
private resolveQueuedForeignSubtreeChildren;
|
|
15
|
+
private resolveQueuedForeignSubtreeHtml;
|
|
16
|
+
protected shouldRenderPageComponent(): boolean;
|
|
17
|
+
private isLitManagedComponent;
|
|
12
18
|
/**
|
|
13
|
-
* Renders a Lit component
|
|
19
|
+
* Renders a Lit component for component-level orchestration.
|
|
20
|
+
*
|
|
21
|
+
* SSR-eligible lazy scripts are preloaded first so custom elements registered
|
|
22
|
+
* by the component can render their server markup even when the Lit renderer is
|
|
23
|
+
* entered through cross-integration foreign-child handoff.
|
|
14
24
|
*
|
|
15
25
|
* Includes component-scoped dependency assets when declared.
|
|
16
26
|
*/
|
package/src/lit-renderer.js
CHANGED
|
@@ -1,30 +1,116 @@
|
|
|
1
|
+
import "@lit-labs/ssr/lib/install-global-dom-shim.js";
|
|
1
2
|
import { IntegrationRenderer } from "@ecopages/core/route-renderer/integration-renderer";
|
|
2
|
-
import { render } from "@lit-labs/ssr";
|
|
3
|
-
import { RenderResultReadable } from "@lit-labs/ssr/lib/render-result-readable.js";
|
|
4
|
-
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
5
3
|
import { LitSsrLazyPreloader } from "./lit-ssr-lazy-preloader.js";
|
|
6
|
-
import {
|
|
4
|
+
import { LIT_PLUGIN_NAME } from "./lit.constants.js";
|
|
5
|
+
import {
|
|
6
|
+
injectLitRenderedChildren,
|
|
7
|
+
LIT_COMPONENT_CHILDREN_SLOT_MARKER,
|
|
8
|
+
normalizeLitHtml,
|
|
9
|
+
renderLitValueToString
|
|
10
|
+
} from "./utils/lit-html-rendering.js";
|
|
7
11
|
class LitRenderer extends IntegrationRenderer {
|
|
8
|
-
name =
|
|
12
|
+
name = LIT_PLUGIN_NAME;
|
|
13
|
+
isFunctionComponent(component) {
|
|
14
|
+
return typeof component === "function";
|
|
15
|
+
}
|
|
16
|
+
async resolveQueuedForeignSubtreeChildren(children, queuedResolutionsByToken, resolveToken) {
|
|
17
|
+
if (children === void 0) {
|
|
18
|
+
return void 0;
|
|
19
|
+
}
|
|
20
|
+
if (typeof children !== "string") {
|
|
21
|
+
return children;
|
|
22
|
+
}
|
|
23
|
+
let renderedChildren = children;
|
|
24
|
+
renderedChildren = await this.foreignSubtreeExecutionService.resolveQueuedTokens(
|
|
25
|
+
renderedChildren,
|
|
26
|
+
queuedResolutionsByToken,
|
|
27
|
+
resolveToken
|
|
28
|
+
);
|
|
29
|
+
return renderedChildren;
|
|
30
|
+
}
|
|
31
|
+
async resolveQueuedForeignSubtreeHtml(html, runtimeContext) {
|
|
32
|
+
const queuedForeignSubtreeResolution = await this.foreignSubtreeExecutionService.resolveQueuedHtml({
|
|
33
|
+
currentIntegrationName: this.name,
|
|
34
|
+
html,
|
|
35
|
+
runtimeContext,
|
|
36
|
+
queueLabel: "Lit",
|
|
37
|
+
getOwningRenderer: (integrationName, rendererCache) => this.getIntegrationRendererForName(integrationName, rendererCache),
|
|
38
|
+
applyAttributesToFirstElement: (resolvedHtml, attributes) => this.htmlTransformer.applyAttributesToFirstElement(resolvedHtml, attributes),
|
|
39
|
+
dedupeProcessedAssets: (assets) => this.htmlTransformer.dedupeProcessedAssets(assets),
|
|
40
|
+
renderQueuedChildren: async (children, _runtimeContext, queuedResolutionsByToken, resolveToken) => {
|
|
41
|
+
const renderedChildren = await this.resolveQueuedForeignSubtreeChildren(
|
|
42
|
+
children,
|
|
43
|
+
queuedResolutionsByToken,
|
|
44
|
+
resolveToken
|
|
45
|
+
);
|
|
46
|
+
if (typeof renderedChildren !== "string") {
|
|
47
|
+
return {
|
|
48
|
+
assets: [],
|
|
49
|
+
children: renderedChildren
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
assets: [],
|
|
54
|
+
html: renderedChildren
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
html: queuedForeignSubtreeResolution.html,
|
|
60
|
+
assets: queuedForeignSubtreeResolution.assets.length > 0 ? queuedForeignSubtreeResolution.assets : void 0
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
shouldRenderPageComponent() {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
isLitManagedComponent(component) {
|
|
67
|
+
return component?.config?.integration === this.name || component?.config?.__eco?.integration === this.name;
|
|
68
|
+
}
|
|
9
69
|
/**
|
|
10
|
-
* Renders a Lit component
|
|
70
|
+
* Renders a Lit component for component-level orchestration.
|
|
71
|
+
*
|
|
72
|
+
* SSR-eligible lazy scripts are preloaded first so custom elements registered
|
|
73
|
+
* by the component can render their server markup even when the Lit renderer is
|
|
74
|
+
* entered through cross-integration foreign-child handoff.
|
|
11
75
|
*
|
|
12
76
|
* Includes component-scoped dependency assets when declared.
|
|
13
77
|
*/
|
|
14
78
|
async renderComponent(input) {
|
|
79
|
+
await this.preloadSsrLazyScripts([input.component]);
|
|
80
|
+
if (!this.isFunctionComponent(input.component)) {
|
|
81
|
+
throw new TypeError("Lit renderer expected a callable component.");
|
|
82
|
+
}
|
|
15
83
|
const component = input.component;
|
|
16
|
-
|
|
84
|
+
let renderedChildren;
|
|
85
|
+
if (input.children !== void 0) {
|
|
86
|
+
renderedChildren = typeof input.children === "string" ? input.children : await renderLitValueToString(input.children);
|
|
87
|
+
}
|
|
88
|
+
let props = input.props;
|
|
89
|
+
if (renderedChildren !== void 0) {
|
|
90
|
+
props = {
|
|
91
|
+
...input.props,
|
|
92
|
+
children: LIT_COMPONENT_CHILDREN_SLOT_MARKER
|
|
93
|
+
};
|
|
94
|
+
}
|
|
17
95
|
const content = await component(props);
|
|
18
|
-
const
|
|
96
|
+
const renderedHtml = await renderLitValueToString(content);
|
|
97
|
+
const html = renderedChildren === void 0 ? renderedHtml : injectLitRenderedChildren(renderedHtml, renderedChildren);
|
|
98
|
+
const queuedForeignSubtreeResolution = await this.resolveQueuedForeignSubtreeHtml(
|
|
99
|
+
html,
|
|
100
|
+
this.getQueuedForeignSubtreeResolutionContext(input)
|
|
101
|
+
);
|
|
19
102
|
const hasDependencies = Boolean(input.component.config?.dependencies);
|
|
20
103
|
const canResolveAssets = typeof this.assetProcessingService?.processDependencies === "function";
|
|
21
104
|
const assets = hasDependencies && canResolveAssets ? await this.processComponentDependencies([input.component]) : void 0;
|
|
22
105
|
return {
|
|
23
|
-
html,
|
|
106
|
+
html: queuedForeignSubtreeResolution.html,
|
|
24
107
|
canAttachAttributes: true,
|
|
25
|
-
rootTag: this.getRootTagName(html),
|
|
108
|
+
rootTag: this.getRootTagName(queuedForeignSubtreeResolution.html),
|
|
26
109
|
integrationName: this.name,
|
|
27
|
-
assets
|
|
110
|
+
assets: this.htmlTransformer.dedupeProcessedAssets([
|
|
111
|
+
...assets ?? [],
|
|
112
|
+
...queuedForeignSubtreeResolution.assets ?? []
|
|
113
|
+
])
|
|
28
114
|
};
|
|
29
115
|
}
|
|
30
116
|
ssrLazyPreloader = new LitSsrLazyPreloader({
|
|
@@ -77,74 +163,69 @@ class LitRenderer extends IntegrationRenderer {
|
|
|
77
163
|
}) {
|
|
78
164
|
try {
|
|
79
165
|
await this.preloadSsrLazyScripts([Page, Layout]);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
166
|
+
return await this.renderPageWithDocumentShell({
|
|
167
|
+
page: {
|
|
168
|
+
component: Page,
|
|
169
|
+
props: {
|
|
170
|
+
params,
|
|
171
|
+
query,
|
|
172
|
+
...props,
|
|
173
|
+
locals
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
layout: Layout ? {
|
|
177
|
+
component: Layout,
|
|
178
|
+
props: locals ? { locals } : {}
|
|
179
|
+
} : void 0,
|
|
180
|
+
htmlTemplate: HtmlTemplate,
|
|
88
181
|
metadata,
|
|
89
|
-
|
|
90
|
-
|
|
182
|
+
pageProps: props || {},
|
|
183
|
+
transformDocumentHtml: normalizeLitHtml
|
|
91
184
|
});
|
|
92
|
-
const [templateStart, templateEnd] = template.split("<--content-->");
|
|
93
|
-
const DOC_TYPE = this.DOC_TYPE;
|
|
94
|
-
function* streamBody() {
|
|
95
|
-
yield DOC_TYPE;
|
|
96
|
-
yield templateStart;
|
|
97
|
-
yield* render(unsafeHTML(children));
|
|
98
|
-
yield templateEnd;
|
|
99
|
-
}
|
|
100
|
-
return new RenderResultReadable(streamBody());
|
|
101
185
|
} catch (error) {
|
|
102
186
|
throw this.createRenderError("Error rendering page", error);
|
|
103
187
|
}
|
|
104
188
|
}
|
|
105
189
|
async renderToResponse(view, props, ctx) {
|
|
106
190
|
try {
|
|
107
|
-
const viewConfig = view.config;
|
|
108
|
-
const Layout = viewConfig?.layout;
|
|
109
|
-
await this.preloadSsrLazyScripts([view, Layout]);
|
|
110
|
-
const viewFn = view;
|
|
111
|
-
const pageContent = await viewFn(props);
|
|
112
191
|
if (ctx.partial) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
192
|
+
return this.renderPartialViewResponse({
|
|
193
|
+
view,
|
|
194
|
+
props,
|
|
195
|
+
ctx,
|
|
196
|
+
transformHtml: normalizeLitHtml
|
|
197
|
+
});
|
|
118
198
|
}
|
|
119
|
-
const
|
|
120
|
-
const
|
|
199
|
+
const viewConfig = view.config;
|
|
200
|
+
const Layout = viewConfig?.layout;
|
|
121
201
|
const HtmlTemplate = await this.getHtmlTemplate();
|
|
122
|
-
const metadata =
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
props,
|
|
126
|
-
appConfig: this.appConfig
|
|
127
|
-
}) : this.appConfig.defaultMetadata;
|
|
202
|
+
const metadata = await this.resolveViewMetadata(view, props);
|
|
203
|
+
const normalizedProps = props ?? {};
|
|
204
|
+
await this.preloadSsrLazyScripts([view, Layout]);
|
|
128
205
|
await this.prepareViewDependencies(view, Layout);
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
pageProps: props
|
|
206
|
+
const pageRender = await this.renderComponentWithForeignChildren({
|
|
207
|
+
component: view,
|
|
208
|
+
props: normalizedProps
|
|
133
209
|
});
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
);
|
|
147
|
-
|
|
210
|
+
const layoutRender = Layout ? await this.renderComponentWithForeignChildren({
|
|
211
|
+
component: Layout,
|
|
212
|
+
props: {},
|
|
213
|
+
children: pageRender.html
|
|
214
|
+
}) : void 0;
|
|
215
|
+
const documentRender = await this.renderComponentWithForeignChildren({
|
|
216
|
+
component: HtmlTemplate,
|
|
217
|
+
props: {
|
|
218
|
+
metadata,
|
|
219
|
+
pageProps: normalizedProps
|
|
220
|
+
},
|
|
221
|
+
children: layoutRender?.html ?? pageRender.html
|
|
222
|
+
});
|
|
223
|
+
this.appendProcessedDependencies(pageRender.assets, layoutRender?.assets, documentRender.assets);
|
|
224
|
+
const body = await this.finalizeResolvedHtml({
|
|
225
|
+
html: `${this.DOC_TYPE}${normalizeLitHtml(documentRender.html)}`,
|
|
226
|
+
partial: false
|
|
227
|
+
});
|
|
228
|
+
return this.createHtmlResponse(body, ctx);
|
|
148
229
|
} catch (error) {
|
|
149
230
|
throw this.createRenderError("Error rendering view", error);
|
|
150
231
|
}
|
|
@@ -6,6 +6,7 @@ type ProcessDependencies = (dependencies: AssetDefinition[], integrationName: st
|
|
|
6
6
|
export interface LitSsrLazyPreloaderOptions {
|
|
7
7
|
resolveDependencyPath: (componentDir: string, sourcePath: string) => string;
|
|
8
8
|
processDependencies?: ProcessDependencies;
|
|
9
|
+
preferSourceImports?: boolean;
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
12
|
* Encapsulates SSR lazy script preload behavior for Lit components.
|
|
@@ -17,10 +18,12 @@ export interface LitSsrLazyPreloaderOptions {
|
|
|
17
18
|
export declare class LitSsrLazyPreloader {
|
|
18
19
|
private readonly resolveDependencyPath;
|
|
19
20
|
private readonly processDependencies?;
|
|
21
|
+
private readonly preferSourceImports;
|
|
20
22
|
private readonly ssrPreloadedScripts;
|
|
21
23
|
private readonly ssrPreloadFailedScripts;
|
|
22
24
|
private readonly ssrPreloadEntrypointCache;
|
|
23
|
-
constructor({ resolveDependencyPath, processDependencies }: LitSsrLazyPreloaderOptions);
|
|
25
|
+
constructor({ resolveDependencyPath, processDependencies, preferSourceImports }: LitSsrLazyPreloaderOptions);
|
|
26
|
+
private getErrorCode;
|
|
24
27
|
/**
|
|
25
28
|
* Detects preload failures that are expected for browser-only modules.
|
|
26
29
|
*/
|
|
@@ -4,19 +4,28 @@ import { pathToFileURL } from "node:url";
|
|
|
4
4
|
class LitSsrLazyPreloader {
|
|
5
5
|
resolveDependencyPath;
|
|
6
6
|
processDependencies;
|
|
7
|
+
preferSourceImports;
|
|
7
8
|
ssrPreloadedScripts = /* @__PURE__ */ new Set();
|
|
8
9
|
ssrPreloadFailedScripts = /* @__PURE__ */ new Set();
|
|
9
10
|
ssrPreloadEntrypointCache = /* @__PURE__ */ new Map();
|
|
10
|
-
constructor({ resolveDependencyPath, processDependencies }) {
|
|
11
|
+
constructor({ resolveDependencyPath, processDependencies, preferSourceImports }) {
|
|
11
12
|
this.resolveDependencyPath = resolveDependencyPath;
|
|
12
13
|
this.processDependencies = processDependencies;
|
|
14
|
+
this.preferSourceImports = preferSourceImports ?? typeof Bun !== "undefined";
|
|
15
|
+
}
|
|
16
|
+
getErrorCode(error) {
|
|
17
|
+
if (typeof error !== "object" || error === null) {
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
const code = Reflect.get(error, "code");
|
|
21
|
+
return code === void 0 ? "" : String(code);
|
|
13
22
|
}
|
|
14
23
|
/**
|
|
15
24
|
* Detects preload failures that are expected for browser-only modules.
|
|
16
25
|
*/
|
|
17
26
|
isExpectedSsrPreloadError(error) {
|
|
18
27
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
19
|
-
const errorCode =
|
|
28
|
+
const errorCode = this.getErrorCode(error);
|
|
20
29
|
if (errorCode === "ERR_UNKNOWN_FILE_EXTENSION") {
|
|
21
30
|
return true;
|
|
22
31
|
}
|
|
@@ -87,7 +96,10 @@ class LitSsrLazyPreloader {
|
|
|
87
96
|
return;
|
|
88
97
|
}
|
|
89
98
|
try {
|
|
90
|
-
await import(
|
|
99
|
+
await import(
|
|
100
|
+
/* @vite-ignore */
|
|
101
|
+
pathToFileURL(preloadEntrypoint).href
|
|
102
|
+
);
|
|
91
103
|
this.ssrPreloadedScripts.add(scriptPath);
|
|
92
104
|
} catch (error) {
|
|
93
105
|
this.ssrPreloadFailedScripts.add(scriptPath);
|
|
@@ -112,7 +124,12 @@ class LitSsrLazyPreloader {
|
|
|
112
124
|
if (cachedEntrypoint) {
|
|
113
125
|
return cachedEntrypoint;
|
|
114
126
|
}
|
|
127
|
+
if (this.preferSourceImports) {
|
|
128
|
+
this.ssrPreloadEntrypointCache.set(scriptPath, scriptPath);
|
|
129
|
+
return scriptPath;
|
|
130
|
+
}
|
|
115
131
|
if (!this.processDependencies) {
|
|
132
|
+
this.ssrPreloadEntrypointCache.set(scriptPath, scriptPath);
|
|
116
133
|
return scriptPath;
|
|
117
134
|
}
|
|
118
135
|
try {
|
|
@@ -140,6 +157,7 @@ class LitSsrLazyPreloader {
|
|
|
140
157
|
if (process.env.ECOPAGES_DEBUG === "true") {
|
|
141
158
|
console.warn(`[ecopages][lit] Failed to resolve SSR preload entrypoint for: ${scriptPath}`, error);
|
|
142
159
|
}
|
|
160
|
+
this.ssrPreloadEntrypointCache.set(scriptPath, scriptPath);
|
|
143
161
|
return scriptPath;
|
|
144
162
|
}
|
|
145
163
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const LIT_PLUGIN_NAME = "lit";
|
package/src/lit.plugin.d.ts
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* This module contains the Lit plugin
|
|
3
3
|
* @module
|
|
4
4
|
*/
|
|
5
|
-
import '
|
|
6
|
-
import './console';
|
|
5
|
+
import './console.js';
|
|
7
6
|
import { IntegrationPlugin, type IntegrationPluginConfig } from '@ecopages/core/plugins/integration-plugin';
|
|
8
7
|
import { type AssetDefinition } from '@ecopages/core/services/asset-processing-service';
|
|
9
8
|
import { LitRenderer } from './lit-renderer.js';
|
package/src/lit.plugin.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import "
|
|
2
|
-
import "./console";
|
|
1
|
+
import "./console.js";
|
|
3
2
|
import { IntegrationPlugin } from "@ecopages/core/plugins/integration-plugin";
|
|
4
3
|
import { AssetFactory } from "@ecopages/core/services/asset-processing-service";
|
|
5
4
|
import { litElementHydrateScript } from "./lit-element-hydrate.js";
|
|
5
|
+
import { LIT_PLUGIN_NAME } from "./lit.constants.js";
|
|
6
6
|
import { LitRenderer } from "./lit-renderer.js";
|
|
7
|
-
const PLUGIN_NAME =
|
|
7
|
+
const PLUGIN_NAME = LIT_PLUGIN_NAME;
|
|
8
8
|
class LitPlugin extends IntegrationPlugin {
|
|
9
9
|
renderer = LitRenderer;
|
|
10
10
|
constructor(options) {
|
|
@@ -32,10 +32,9 @@ class LitPlugin extends IntegrationPlugin {
|
|
|
32
32
|
*/
|
|
33
33
|
AssetFactory.createInlineContentScript({
|
|
34
34
|
position: "head",
|
|
35
|
-
content: litElementHydrateScript
|
|
35
|
+
content: `(() => {${litElementHydrateScript}})();`,
|
|
36
36
|
bundle: false,
|
|
37
37
|
attributes: {
|
|
38
|
-
"data-eco-rerun": "true",
|
|
39
38
|
"data-eco-script-id": "lit-hydrate-support"
|
|
40
39
|
}
|
|
41
40
|
})
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const LIT_HTML_TEMPLATE_SLOT_MARKER = "<--content-->";
|
|
2
|
+
export declare const LIT_COMPONENT_CHILDREN_SLOT_MARKER = "<!--eco-lit-component-children-->";
|
|
3
|
+
export declare function normalizeLitHtml(markup: string): string;
|
|
4
|
+
export declare function injectLitRenderedChildren(template: string, renderedChildren: string): string;
|
|
5
|
+
export declare function renderLitValueToString(value: unknown): Promise<string>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { render } from "@lit-labs/ssr";
|
|
2
|
+
import { html as staticHtml, unsafeStatic } from "lit/static-html.js";
|
|
3
|
+
const LIT_HTML_TEMPLATE_SLOT_MARKER = "<--content-->";
|
|
4
|
+
const LIT_COMPONENT_CHILDREN_SLOT_MARKER = "<!--eco-lit-component-children-->";
|
|
5
|
+
const ESCAPED_COMPONENT_CHILDREN_SLOT_MARKER = "<!--eco-lit-component-children-->";
|
|
6
|
+
const DOUBLE_ESCAPED_COMPONENT_CHILDREN_SLOT_MARKER = "&lt;!--eco-lit-component-children--&gt;";
|
|
7
|
+
const DUPLICATE_DECLARATIVE_SHADOW_ROOT_ATTRIBUTE = /\sshadowroot=(['"])(open|closed)\1(?=\sshadowrootmode=\1\2\1)/g;
|
|
8
|
+
function normalizeLitHtml(markup) {
|
|
9
|
+
return markup.replace(DUPLICATE_DECLARATIVE_SHADOW_ROOT_ATTRIBUTE, "");
|
|
10
|
+
}
|
|
11
|
+
function injectLitRenderedChildren(template, renderedChildren) {
|
|
12
|
+
for (const marker of [
|
|
13
|
+
LIT_COMPONENT_CHILDREN_SLOT_MARKER,
|
|
14
|
+
ESCAPED_COMPONENT_CHILDREN_SLOT_MARKER,
|
|
15
|
+
DOUBLE_ESCAPED_COMPONENT_CHILDREN_SLOT_MARKER
|
|
16
|
+
]) {
|
|
17
|
+
if (template.includes(marker)) {
|
|
18
|
+
return template.split(marker).join(renderedChildren);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (template.includes(LIT_HTML_TEMPLATE_SLOT_MARKER)) {
|
|
22
|
+
return template.split(LIT_HTML_TEMPLATE_SLOT_MARKER).join(renderedChildren);
|
|
23
|
+
}
|
|
24
|
+
if (template.includes("</body>")) {
|
|
25
|
+
return template.replace("</body>", `${renderedChildren}</body>`);
|
|
26
|
+
}
|
|
27
|
+
if (template.includes("</html>")) {
|
|
28
|
+
return template.replace("</html>", `${renderedChildren}</html>`);
|
|
29
|
+
}
|
|
30
|
+
return `${template}${renderedChildren}`;
|
|
31
|
+
}
|
|
32
|
+
async function renderLitValueToString(value) {
|
|
33
|
+
if (typeof value === "string") {
|
|
34
|
+
let renderedHtml2 = "";
|
|
35
|
+
for (const chunk of render(staticHtml`${unsafeStatic(value)}`)) {
|
|
36
|
+
renderedHtml2 += chunk;
|
|
37
|
+
}
|
|
38
|
+
return normalizeLitHtml(renderedHtml2);
|
|
39
|
+
}
|
|
40
|
+
let renderedHtml = "";
|
|
41
|
+
for (const chunk of render(value)) {
|
|
42
|
+
renderedHtml += chunk;
|
|
43
|
+
}
|
|
44
|
+
return normalizeLitHtml(renderedHtml);
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
LIT_COMPONENT_CHILDREN_SLOT_MARKER,
|
|
48
|
+
LIT_HTML_TEMPLATE_SLOT_MARKER,
|
|
49
|
+
injectLitRenderedChildren,
|
|
50
|
+
normalizeLitHtml,
|
|
51
|
+
renderLitValueToString
|
|
52
|
+
};
|
package/CHANGELOG.md
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to `@ecopages/lit` are documented here.
|
|
4
|
-
|
|
5
|
-
> **Note:** Changelog tracking begins at version `0.2.0`. Changes prior to this release are not recorded here but are available in the git history.
|
|
6
|
-
|
|
7
|
-
## [UNRELEASED] — TBD
|
|
8
|
-
|
|
9
|
-
### Features
|
|
10
|
-
|
|
11
|
-
- Aligned Lit renderer with full orchestration mode — removed legacy rendering path (`fc07bdb0`).
|
|
12
|
-
- Integration updated to work with the new lazy dependency map and global injector rendering mode.
|
|
13
|
-
|
|
14
|
-
### Refactoring
|
|
15
|
-
|
|
16
|
-
- Ambient module declarations cleaned up (`5f46ecc5`).
|
|
17
|
-
- Updated test suite for esbuild adapter and Node.js runtime compatibility (`31a44458`).
|
|
18
|
-
|
|
19
|
-
### Tests
|
|
20
|
-
|
|
21
|
-
- Updated integration tests to the new orchestration model.
|
package/src/console.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
const originalConsoleWarn = console.warn;
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Suppresses the warning about CustomElementRegistry already being defined in ssr-dom-shim by lit
|
|
5
|
-
* https://github.com/lit/lit/blob/bd881370b83d366f7654dd510731242a68949a20/packages/labs/ssr-dom-shim/src/index.ts#L140
|
|
6
|
-
* Complete Error Log:
|
|
7
|
-
* 'CustomElementRegistry' already has "lit-counter" defined. This may have been caused by live reload or hot module replacement in which case it can be safely ignored.
|
|
8
|
-
* Make sure to test your application with a production build as repeat registrations will throw in production.
|
|
9
|
-
*/
|
|
10
|
-
console.warn = (...messages: any[]) => {
|
|
11
|
-
if (
|
|
12
|
-
messages.some(
|
|
13
|
-
(message) => typeof message === 'string' && message.includes("'CustomElementRegistry' already has"),
|
|
14
|
-
)
|
|
15
|
-
) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
originalConsoleWarn.apply(console, messages);
|
|
19
|
-
};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export const litElementHydrateScript =
|
|
2
|
-
'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.ts
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This module contains the Lit renderer
|
|
3
|
-
* @module
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
ComponentRenderInput,
|
|
8
|
-
ComponentRenderResult,
|
|
9
|
-
EcoComponent,
|
|
10
|
-
EcoPagesElement,
|
|
11
|
-
IntegrationRendererRenderOptions,
|
|
12
|
-
PageMetadataProps,
|
|
13
|
-
RouteRendererBody,
|
|
14
|
-
} from '@ecopages/core';
|
|
15
|
-
import { IntegrationRenderer, type RenderToResponseContext } from '@ecopages/core/route-renderer/integration-renderer';
|
|
16
|
-
import { render } from '@lit-labs/ssr';
|
|
17
|
-
import { RenderResultReadable } from '@lit-labs/ssr/lib/render-result-readable.js';
|
|
18
|
-
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
|
19
|
-
import { LitSsrLazyPreloader } from './lit-ssr-lazy-preloader.ts';
|
|
20
|
-
import { PLUGIN_NAME } from './lit.plugin.ts';
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* A renderer for the Lit integration.
|
|
24
|
-
*/
|
|
25
|
-
export class LitRenderer extends IntegrationRenderer<EcoPagesElement> {
|
|
26
|
-
override name = PLUGIN_NAME;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Renders a Lit component boundary for component-level orchestration.
|
|
30
|
-
*
|
|
31
|
-
* Includes component-scoped dependency assets when declared.
|
|
32
|
-
*/
|
|
33
|
-
override async renderComponent(input: ComponentRenderInput): Promise<ComponentRenderResult> {
|
|
34
|
-
const component = input.component as (
|
|
35
|
-
props: Record<string, unknown>,
|
|
36
|
-
) => Promise<EcoPagesElement> | EcoPagesElement;
|
|
37
|
-
const props = input.children === undefined ? input.props : { ...input.props, children: input.children };
|
|
38
|
-
const content = await component(props);
|
|
39
|
-
const html = String(content);
|
|
40
|
-
const hasDependencies = Boolean(input.component.config?.dependencies);
|
|
41
|
-
const canResolveAssets = typeof this.assetProcessingService?.processDependencies === 'function';
|
|
42
|
-
const assets =
|
|
43
|
-
hasDependencies && canResolveAssets
|
|
44
|
-
? await this.processComponentDependencies([input.component])
|
|
45
|
-
: undefined;
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
html,
|
|
49
|
-
canAttachAttributes: true,
|
|
50
|
-
rootTag: this.getRootTagName(html),
|
|
51
|
-
integrationName: this.name,
|
|
52
|
-
assets,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
private readonly ssrLazyPreloader = new LitSsrLazyPreloader({
|
|
57
|
-
resolveDependencyPath: this.resolveDependencyPath.bind(this),
|
|
58
|
-
processDependencies: this.assetProcessingService?.processDependencies?.bind(this.assetProcessingService),
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Detects preload failures that are expected for browser-only modules.
|
|
63
|
-
*
|
|
64
|
-
* These errors are treated as non-fatal during SSR preload because some
|
|
65
|
-
* lazy client scripts intentionally depend on browser globals.
|
|
66
|
-
*/
|
|
67
|
-
protected isExpectedSsrPreloadError(error: unknown): boolean {
|
|
68
|
-
return this.ssrLazyPreloader.isExpectedSsrPreloadError(error);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Collects lazy script file paths eligible for SSR preloading.
|
|
73
|
-
*
|
|
74
|
-
* Only per-entry lazy script dependencies with `ssr: true` are collected.
|
|
75
|
-
* File-backed entries are required (`src` must be present);
|
|
76
|
-
* inline content lazy entries are intentionally skipped.
|
|
77
|
-
*/
|
|
78
|
-
protected collectSsrPreloadScripts(components: Array<EcoComponent | undefined>): string[] {
|
|
79
|
-
return this.ssrLazyPreloader.collectSsrPreloadScripts(components);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Preloads SSR-eligible lazy scripts to register custom elements before render.
|
|
84
|
-
*/
|
|
85
|
-
protected async preloadSsrLazyScripts(components: Array<EcoComponent | undefined>): Promise<void> {
|
|
86
|
-
await this.ssrLazyPreloader.preloadSsrLazyScripts(components);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Resolves the concrete JS entrypoint used for SSR preloading.
|
|
91
|
-
*
|
|
92
|
-
* Scripts are passed through the asset pipeline so preload imports can use
|
|
93
|
-
* the same processed output shape as runtime dependencies.
|
|
94
|
-
*/
|
|
95
|
-
protected async resolveSsrPreloadEntrypoint(scriptPath: string): Promise<string | null> {
|
|
96
|
-
return this.ssrLazyPreloader.resolveSsrPreloadEntrypoint(scriptPath);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async render({
|
|
100
|
-
params,
|
|
101
|
-
query,
|
|
102
|
-
props,
|
|
103
|
-
locals,
|
|
104
|
-
metadata,
|
|
105
|
-
Page,
|
|
106
|
-
Layout,
|
|
107
|
-
HtmlTemplate,
|
|
108
|
-
}: IntegrationRendererRenderOptions): Promise<RouteRendererBody> {
|
|
109
|
-
try {
|
|
110
|
-
await this.preloadSsrLazyScripts([Page, Layout]);
|
|
111
|
-
|
|
112
|
-
const pageContent = await Page({ params, query, ...props, locals });
|
|
113
|
-
const children = Layout
|
|
114
|
-
? await (Layout as (props: { children: EcoPagesElement } & Record<string, unknown>) => EcoPagesElement)(
|
|
115
|
-
{
|
|
116
|
-
children: pageContent,
|
|
117
|
-
locals,
|
|
118
|
-
},
|
|
119
|
-
)
|
|
120
|
-
: pageContent;
|
|
121
|
-
|
|
122
|
-
const template = (await HtmlTemplate({
|
|
123
|
-
metadata,
|
|
124
|
-
children: '<--content-->',
|
|
125
|
-
pageProps: props || {},
|
|
126
|
-
})) as string;
|
|
127
|
-
|
|
128
|
-
const [templateStart, templateEnd] = template.split('<--content-->');
|
|
129
|
-
|
|
130
|
-
const DOC_TYPE = this.DOC_TYPE;
|
|
131
|
-
|
|
132
|
-
function* streamBody() {
|
|
133
|
-
yield DOC_TYPE;
|
|
134
|
-
yield templateStart;
|
|
135
|
-
yield* render(unsafeHTML(children));
|
|
136
|
-
yield templateEnd;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return new RenderResultReadable(streamBody());
|
|
140
|
-
} catch (error) {
|
|
141
|
-
throw this.createRenderError('Error rendering page', error);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
async renderToResponse<P = Record<string, unknown>>(
|
|
146
|
-
view: EcoComponent<P>,
|
|
147
|
-
props: P,
|
|
148
|
-
ctx: RenderToResponseContext,
|
|
149
|
-
): Promise<Response> {
|
|
150
|
-
try {
|
|
151
|
-
const viewConfig = view.config;
|
|
152
|
-
const Layout = viewConfig?.layout as
|
|
153
|
-
| ((props: { children: EcoPagesElement } & Record<string, unknown>) => EcoPagesElement)
|
|
154
|
-
| undefined;
|
|
155
|
-
|
|
156
|
-
await this.preloadSsrLazyScripts([view as unknown as EcoComponent, Layout as unknown as EcoComponent]);
|
|
157
|
-
|
|
158
|
-
const viewFn = view as (props: P) => Promise<EcoPagesElement>;
|
|
159
|
-
const pageContent = await viewFn(props);
|
|
160
|
-
|
|
161
|
-
if (ctx.partial) {
|
|
162
|
-
function* streamBody() {
|
|
163
|
-
yield* render(unsafeHTML(pageContent));
|
|
164
|
-
}
|
|
165
|
-
const readable = new RenderResultReadable(streamBody());
|
|
166
|
-
return this.createHtmlResponse(readable as unknown as BodyInit, ctx);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const DOC_TYPE = this.DOC_TYPE;
|
|
170
|
-
const children = Layout ? await Layout({ children: pageContent }) : pageContent;
|
|
171
|
-
|
|
172
|
-
const HtmlTemplate = await this.getHtmlTemplate();
|
|
173
|
-
const metadata: PageMetadataProps = view.metadata
|
|
174
|
-
? await view.metadata({
|
|
175
|
-
params: {},
|
|
176
|
-
query: {},
|
|
177
|
-
props: props as Record<string, unknown>,
|
|
178
|
-
appConfig: this.appConfig,
|
|
179
|
-
})
|
|
180
|
-
: this.appConfig.defaultMetadata;
|
|
181
|
-
|
|
182
|
-
await this.prepareViewDependencies(view, Layout as EcoComponent | undefined);
|
|
183
|
-
|
|
184
|
-
const template = (await HtmlTemplate({
|
|
185
|
-
metadata,
|
|
186
|
-
children: '<--content-->',
|
|
187
|
-
pageProps: props as Record<string, unknown>,
|
|
188
|
-
})) as string;
|
|
189
|
-
|
|
190
|
-
const [templateStart, templateEnd] = template.split('<--content-->');
|
|
191
|
-
|
|
192
|
-
function* streamBody() {
|
|
193
|
-
yield DOC_TYPE;
|
|
194
|
-
yield templateStart;
|
|
195
|
-
yield* render(unsafeHTML(children));
|
|
196
|
-
yield templateEnd;
|
|
197
|
-
}
|
|
198
|
-
const stream = new RenderResultReadable(streamBody());
|
|
199
|
-
const transformedResponse = await this.htmlTransformer.transform(
|
|
200
|
-
new Response(stream as any, {
|
|
201
|
-
headers: { 'Content-Type': 'text/html' },
|
|
202
|
-
}),
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
return this.createHtmlResponse(transformedResponse.body as BodyInit, ctx);
|
|
206
|
-
} catch (error) {
|
|
207
|
-
throw this.createRenderError('Error rendering view', error);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import type { EcoComponent, EcoComponentConfig } from '@ecopages/core';
|
|
2
|
-
import { AssetFactory } from '@ecopages/core/services/asset-processing-service';
|
|
3
|
-
import type { AssetDefinition } from '@ecopages/core/services/asset-processing-service';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { pathToFileURL } from 'node:url';
|
|
6
|
-
|
|
7
|
-
type ProcessDependencies = (
|
|
8
|
-
dependencies: AssetDefinition[],
|
|
9
|
-
integrationName: string,
|
|
10
|
-
) => Promise<Array<{ filepath?: string }>>;
|
|
11
|
-
|
|
12
|
-
export interface LitSsrLazyPreloaderOptions {
|
|
13
|
-
resolveDependencyPath: (componentDir: string, sourcePath: string) => string;
|
|
14
|
-
processDependencies?: ProcessDependencies;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Encapsulates SSR lazy script preload behavior for Lit components.
|
|
19
|
-
*
|
|
20
|
-
* Collects `dependencies.scripts` lazy entries with `ssr: true`, resolves the
|
|
21
|
-
* processed entrypoint through the asset pipeline, and imports the module so
|
|
22
|
-
* custom elements are registered before SSR rendering.
|
|
23
|
-
*/
|
|
24
|
-
export class LitSsrLazyPreloader {
|
|
25
|
-
private readonly resolveDependencyPath: (componentDir: string, sourcePath: string) => string;
|
|
26
|
-
private readonly processDependencies?: ProcessDependencies;
|
|
27
|
-
private readonly ssrPreloadedScripts = new Set<string>();
|
|
28
|
-
private readonly ssrPreloadFailedScripts = new Set<string>();
|
|
29
|
-
private readonly ssrPreloadEntrypointCache = new Map<string, string>();
|
|
30
|
-
|
|
31
|
-
constructor({ resolveDependencyPath, processDependencies }: LitSsrLazyPreloaderOptions) {
|
|
32
|
-
this.resolveDependencyPath = resolveDependencyPath;
|
|
33
|
-
this.processDependencies = processDependencies;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Detects preload failures that are expected for browser-only modules.
|
|
38
|
-
*/
|
|
39
|
-
isExpectedSsrPreloadError(error: unknown): boolean {
|
|
40
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
41
|
-
const errorCode =
|
|
42
|
-
typeof error === 'object' && error !== null && 'code' in error
|
|
43
|
-
? String((error as { code?: unknown }).code ?? '')
|
|
44
|
-
: '';
|
|
45
|
-
|
|
46
|
-
if (errorCode === 'ERR_UNKNOWN_FILE_EXTENSION') {
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
errorMessage.includes(`reading 'Element'`) ||
|
|
52
|
-
errorMessage.includes('window is not defined') ||
|
|
53
|
-
errorMessage.includes('document is not defined') ||
|
|
54
|
-
errorMessage.includes('navigator is not defined')
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Collects lazy script file paths eligible for SSR preloading.
|
|
60
|
-
*/
|
|
61
|
-
collectSsrPreloadScripts(components: Array<EcoComponent | undefined>): string[] {
|
|
62
|
-
const scriptPaths = new Set<string>();
|
|
63
|
-
const visitedConfigs = new Set<EcoComponentConfig>();
|
|
64
|
-
|
|
65
|
-
const collect = (component?: EcoComponent) => {
|
|
66
|
-
const config = component?.config;
|
|
67
|
-
if (!config || visitedConfigs.has(config)) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
visitedConfigs.add(config);
|
|
72
|
-
|
|
73
|
-
const scriptEntries = config.dependencies?.scripts ?? [];
|
|
74
|
-
const componentFile = config.__eco?.file;
|
|
75
|
-
|
|
76
|
-
if (componentFile) {
|
|
77
|
-
const componentDir = path.dirname(componentFile);
|
|
78
|
-
for (const script of scriptEntries) {
|
|
79
|
-
if (typeof script === 'string') {
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (!script.lazy || script.ssr !== true) {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (!script.src) {
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
scriptPaths.add(this.resolveDependencyPath(componentDir, script.src));
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (config.layout) {
|
|
96
|
-
collect(config.layout);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
for (const nestedComponent of config.dependencies?.components || []) {
|
|
100
|
-
collect(nestedComponent);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
for (const component of components) {
|
|
105
|
-
collect(component);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return Array.from(scriptPaths);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Preloads SSR-eligible lazy scripts to register custom elements before render.
|
|
113
|
-
*/
|
|
114
|
-
async preloadSsrLazyScripts(components: Array<EcoComponent | undefined>): Promise<void> {
|
|
115
|
-
const scripts = this.collectSsrPreloadScripts(components);
|
|
116
|
-
if (scripts.length === 0) {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
await Promise.all(
|
|
121
|
-
scripts
|
|
122
|
-
.filter((scriptPath) => {
|
|
123
|
-
if (this.ssrPreloadedScripts.has(scriptPath)) {
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
if (this.ssrPreloadFailedScripts.has(scriptPath)) {
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
return true;
|
|
130
|
-
})
|
|
131
|
-
.map(async (scriptPath) => {
|
|
132
|
-
const preloadEntrypoint = await this.resolveSsrPreloadEntrypoint(scriptPath);
|
|
133
|
-
if (!preloadEntrypoint) {
|
|
134
|
-
this.ssrPreloadFailedScripts.add(scriptPath);
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
await import(pathToFileURL(preloadEntrypoint).href);
|
|
140
|
-
this.ssrPreloadedScripts.add(scriptPath);
|
|
141
|
-
} catch (error) {
|
|
142
|
-
this.ssrPreloadFailedScripts.add(scriptPath);
|
|
143
|
-
|
|
144
|
-
if (this.isExpectedSsrPreloadError(error)) {
|
|
145
|
-
if (process.env.ECOPAGES_DEBUG === 'true') {
|
|
146
|
-
console.warn(
|
|
147
|
-
`[ecopages][lit] Skipping SSR preload for browser-only lazy script: ${scriptPath}`,
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
console.warn(`[ecopages][lit] Failed to preload lazy SSR script: ${scriptPath}`, error);
|
|
154
|
-
}
|
|
155
|
-
}),
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Resolves the concrete JS entrypoint used for SSR preloading.
|
|
161
|
-
*/
|
|
162
|
-
async resolveSsrPreloadEntrypoint(scriptPath: string): Promise<string | null> {
|
|
163
|
-
const cachedEntrypoint = this.ssrPreloadEntrypointCache.get(scriptPath);
|
|
164
|
-
if (cachedEntrypoint) {
|
|
165
|
-
return cachedEntrypoint;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (!this.processDependencies) {
|
|
169
|
-
return scriptPath;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
try {
|
|
173
|
-
const processed = await this.processDependencies(
|
|
174
|
-
[
|
|
175
|
-
AssetFactory.createInlineFileScript({
|
|
176
|
-
filepath: scriptPath,
|
|
177
|
-
position: 'head',
|
|
178
|
-
bundle: true,
|
|
179
|
-
attributes: {
|
|
180
|
-
type: 'module',
|
|
181
|
-
defer: '',
|
|
182
|
-
},
|
|
183
|
-
}),
|
|
184
|
-
],
|
|
185
|
-
`lit-ssr-preload:${scriptPath}`,
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
const entrypoint = processed[0]?.filepath;
|
|
189
|
-
if (!entrypoint) {
|
|
190
|
-
return scriptPath;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
this.ssrPreloadEntrypointCache.set(scriptPath, entrypoint);
|
|
194
|
-
return entrypoint;
|
|
195
|
-
} catch (error) {
|
|
196
|
-
if (process.env.ECOPAGES_DEBUG === 'true') {
|
|
197
|
-
console.warn(`[ecopages][lit] Failed to resolve SSR preload entrypoint for: ${scriptPath}`, error);
|
|
198
|
-
}
|
|
199
|
-
return scriptPath;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
package/src/lit.plugin.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This module contains the Lit plugin
|
|
3
|
-
* @module
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import '@lit-labs/ssr/lib/install-global-dom-shim.js';
|
|
7
|
-
import './console';
|
|
8
|
-
import { IntegrationPlugin, type IntegrationPluginConfig } from '@ecopages/core/plugins/integration-plugin';
|
|
9
|
-
import { type AssetDefinition, AssetFactory } from '@ecopages/core/services/asset-processing-service';
|
|
10
|
-
import { litElementHydrateScript } from './lit-element-hydrate.ts';
|
|
11
|
-
import { LitRenderer } from './lit-renderer.ts';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* The name of the Lit plugin
|
|
15
|
-
*/
|
|
16
|
-
export const PLUGIN_NAME = 'lit';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* The Lit plugin class
|
|
20
|
-
* This plugin provides support for Lit components in Ecopages
|
|
21
|
-
*/
|
|
22
|
-
export class LitPlugin extends IntegrationPlugin {
|
|
23
|
-
renderer = LitRenderer;
|
|
24
|
-
|
|
25
|
-
constructor(options?: Omit<IntegrationPluginConfig, 'name'>) {
|
|
26
|
-
super({
|
|
27
|
-
name: PLUGIN_NAME,
|
|
28
|
-
extensions: ['.lit.tsx'],
|
|
29
|
-
staticBuildStep: 'fetch',
|
|
30
|
-
...options,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
this.integrationDependencies.unshift(...this.getDependencies());
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
getDependencies(): AssetDefinition[] {
|
|
37
|
-
return [
|
|
38
|
-
/**
|
|
39
|
-
* BUG ALERT
|
|
40
|
-
* Due to an issue appeared in Bun 1.2.2, we need to use a workaround to import the hydrate script.
|
|
41
|
-
* This is a temporary solution until the issue is resolved.
|
|
42
|
-
* The litElementHydrateScript is the same file built on Bun 1.1.45.
|
|
43
|
-
* https://github.com/oven-sh/bun/issues/17180
|
|
44
|
-
*
|
|
45
|
-
* AssetFactory.createNodeModuleScript({
|
|
46
|
-
* position: 'head',
|
|
47
|
-
* importPath: '@lit-labs/ssr-client/lit-element-hydrate-support.js'
|
|
48
|
-
* })
|
|
49
|
-
*/
|
|
50
|
-
AssetFactory.createInlineContentScript({
|
|
51
|
-
position: 'head',
|
|
52
|
-
content: litElementHydrateScript,
|
|
53
|
-
bundle: false,
|
|
54
|
-
attributes: {
|
|
55
|
-
'data-eco-rerun': 'true',
|
|
56
|
-
'data-eco-script-id': 'lit-hydrate-support',
|
|
57
|
-
},
|
|
58
|
-
}),
|
|
59
|
-
];
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Factory function to create a Lit plugin instance
|
|
65
|
-
* @param options Configuration options for the Lit plugin
|
|
66
|
-
* @returns A new LitPlugin instance
|
|
67
|
-
*/
|
|
68
|
-
export function litPlugin(options?: Omit<IntegrationPluginConfig, 'name'>): LitPlugin {
|
|
69
|
-
return new LitPlugin(options);
|
|
70
|
-
}
|
package/src/styled-mixin.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This module contains StyledMixin
|
|
3
|
-
* It is used to add styles to a LitElement
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
import { type LitElement, unsafeCSS } from 'lit';
|
|
7
|
-
|
|
8
|
-
type Constructor<T> = new (...args: any[]) => T;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* A mixin to add styles to a LitElement
|
|
12
|
-
* @param superClass - The class to mix into
|
|
13
|
-
* @param css - The styles to add
|
|
14
|
-
* @returns The class with the styles added
|
|
15
|
-
*/
|
|
16
|
-
export const StyledMixin = <T extends Constructor<LitElement>>(
|
|
17
|
-
superClass: T,
|
|
18
|
-
css: string[] = [],
|
|
19
|
-
): Constructor<LitElement> & T => {
|
|
20
|
-
class StyledMixinClass extends superClass {
|
|
21
|
-
static styles = [
|
|
22
|
-
(superClass as unknown as typeof LitElement).styles ?? [],
|
|
23
|
-
...css.map((styles) => unsafeCSS(styles)),
|
|
24
|
-
];
|
|
25
|
-
}
|
|
26
|
-
return StyledMixinClass as Constructor<LitElement> & T;
|
|
27
|
-
};
|