@qwik-custom-elements/adapter-lit 1.0.0
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/LICENSE +21 -0
- package/README.md +90 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.js +3 -0
- package/dist/client/lit-csr-client-setup.d.ts +11 -0
- package/dist/client/lit-csr-client-setup.js +58 -0
- package/dist/client/lit-csr-props.d.ts +1 -0
- package/dist/client/lit-csr-props.js +17 -0
- package/dist/client/lit-csr.d.ts +12 -0
- package/dist/client/lit-csr.js +140 -0
- package/dist/client/lit-ssr-client-setup.d.ts +12 -0
- package/dist/client/lit-ssr-client-setup.js +63 -0
- package/dist/generated-output.d.ts +38 -0
- package/dist/generated-output.js +226 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +34 -0
- package/dist/ssr/index.d.ts +66 -0
- package/dist/ssr/index.js +103 -0
- package/dist/ssr/lit-ssr.d.ts +22 -0
- package/dist/ssr/lit-ssr.js +171 -0
- package/dist/ssr-client.d.ts +14 -0
- package/dist/ssr-client.js +14 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dmitry A. Efimenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# @qwik-custom-elements/adapter-lit
|
|
2
|
+
|
|
3
|
+
Lit-specific adapter contract and SSR surface for generated Qwik custom-element integration.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
npm install @qwik-custom-elements/adapter-lit
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependencies:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
npm install @builder.io/qwik lit
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
For SSR support, also install the optional peers:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
npm install @lit-labs/ssr @lit-labs/ssr-client
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quickstart
|
|
24
|
+
|
|
25
|
+
Reference this adapter in your `qwik-custom-elements.config.json`:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"projects": [
|
|
30
|
+
{
|
|
31
|
+
"id": "my-lit-lib",
|
|
32
|
+
"adapter": "lit",
|
|
33
|
+
"adapterPackage": "@qwik-custom-elements/adapter-lit",
|
|
34
|
+
"source": {
|
|
35
|
+
"type": "PACKAGE_NAME",
|
|
36
|
+
"packageName": "my-lit-lib"
|
|
37
|
+
},
|
|
38
|
+
"adapterOptions": {
|
|
39
|
+
"runtime": {
|
|
40
|
+
"libraryImport": "my-lit-lib"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"outDir": "./src/generated/lit/csr"
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For SSR-capable generation, set `"adapterPackage"` to `"@qwik-custom-elements/adapter-lit/ssr"` and point `"outDir"` to a separate SSR folder.
|
|
50
|
+
|
|
51
|
+
Run `npx qwik-custom-elements` to generate Qwik wrappers from the Lit component library.
|
|
52
|
+
|
|
53
|
+
## Support Policy
|
|
54
|
+
|
|
55
|
+
This package follows semantic versioning. See [COMPATIBILITY.md](../../COMPATIBILITY.md) for tested combinations of adapter-lit, Qwik, Lit, `@lit-labs/ssr`, and Node.js.
|
|
56
|
+
|
|
57
|
+
Breaking changes always include an explicit `BREAKING` section in the release notes and require an update to `COMPATIBILITY.md` before merging.
|
|
58
|
+
|
|
59
|
+
## Ownership Boundary
|
|
60
|
+
|
|
61
|
+
`@qwik-custom-elements/adapter-lit` owns Lit-specific generation behavior.
|
|
62
|
+
|
|
63
|
+
That ownership includes:
|
|
64
|
+
|
|
65
|
+
- Lit capability metadata
|
|
66
|
+
- Lit SSR capability probing and SSR-specific output contracts
|
|
67
|
+
- adapter-owned generated barrels, wrapper modules, and any Lit-specific generated helper modules
|
|
68
|
+
- framework-specific output shape decisions that must not live in core
|
|
69
|
+
|
|
70
|
+
Core may orchestrate the run and pass typed parsed component metadata into the adapter, but core should not shape Lit-generated output directly.
|
|
71
|
+
|
|
72
|
+
## Current Exports
|
|
73
|
+
|
|
74
|
+
This package currently exposes:
|
|
75
|
+
|
|
76
|
+
- `@qwik-custom-elements/adapter-lit`
|
|
77
|
+
- Lit adapter metadata
|
|
78
|
+
- SSR capability probe wiring
|
|
79
|
+
- `@qwik-custom-elements/adapter-lit/ssr`
|
|
80
|
+
- Lit SSR placeholder probe and SSR markup contract
|
|
81
|
+
|
|
82
|
+
## Current Status
|
|
83
|
+
|
|
84
|
+
Lit support is still an adapter-contract surface rather than a complete generated-wrapper implementation.
|
|
85
|
+
|
|
86
|
+
Even so, the architectural boundary is already fixed: when Lit-generated wrappers or helper modules are emitted, they belong to this adapter rather than `@qwik-custom-elements/core`.
|
|
87
|
+
|
|
88
|
+
## Documentation Expectations
|
|
89
|
+
|
|
90
|
+
When Lit adapter contracts or generated output ownership change, update this README alongside the system decisions and findings logs.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type QRL } from '@builder.io/qwik';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a client-side setup hook for Lit CSR components.
|
|
4
|
+
*
|
|
5
|
+
* The returned hook registers a `readystatechange` listener that calls the
|
|
6
|
+
* provided QRL to load and define the custom elements. The setup is idempotent
|
|
7
|
+
* and runs at most once per page load, tracked via a `globalThis` marker.
|
|
8
|
+
*
|
|
9
|
+
* Call the returned hook inside a Qwik component to activate element loading.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createLitCSRClientSetup(importLibraryQrl?: QRL<() => Promise<void>>): () => void;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { $, useOnDocument } from '@builder.io/qwik';
|
|
2
|
+
const LIT_CSR_CLIENT_SETUP_DONE = '__qce_lit_csr_client_setup_done__';
|
|
3
|
+
function ensureLitCSRClientSetup(importLibraryQrl) {
|
|
4
|
+
const markerTarget = globalThis;
|
|
5
|
+
const existingMarker = markerTarget[LIT_CSR_CLIENT_SETUP_DONE];
|
|
6
|
+
if (existingMarker === true) {
|
|
7
|
+
return Promise.resolve();
|
|
8
|
+
}
|
|
9
|
+
if (typeof existingMarker === 'object' &&
|
|
10
|
+
existingMarker !== null &&
|
|
11
|
+
'then' in existingMarker &&
|
|
12
|
+
typeof existingMarker.then === 'function') {
|
|
13
|
+
return existingMarker;
|
|
14
|
+
}
|
|
15
|
+
const setupPromise = (async () => {
|
|
16
|
+
if (importLibraryQrl) {
|
|
17
|
+
const fn = await importLibraryQrl.resolve();
|
|
18
|
+
await fn();
|
|
19
|
+
}
|
|
20
|
+
})();
|
|
21
|
+
markerTarget[LIT_CSR_CLIENT_SETUP_DONE] = setupPromise;
|
|
22
|
+
return setupPromise
|
|
23
|
+
.then(() => {
|
|
24
|
+
markerTarget[LIT_CSR_CLIENT_SETUP_DONE] = true;
|
|
25
|
+
})
|
|
26
|
+
.catch((error) => {
|
|
27
|
+
delete markerTarget[LIT_CSR_CLIENT_SETUP_DONE];
|
|
28
|
+
throw error;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function executeLitCSRClientSetup(importLibraryQrl) {
|
|
32
|
+
await ensureLitCSRClientSetup(importLibraryQrl).catch((error) => {
|
|
33
|
+
console.error(error);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Creates a client-side setup hook for Lit CSR components.
|
|
38
|
+
*
|
|
39
|
+
* The returned hook registers a `readystatechange` listener that calls the
|
|
40
|
+
* provided QRL to load and define the custom elements. The setup is idempotent
|
|
41
|
+
* and runs at most once per page load, tracked via a `globalThis` marker.
|
|
42
|
+
*
|
|
43
|
+
* Call the returned hook inside a Qwik component to activate element loading.
|
|
44
|
+
*/
|
|
45
|
+
export function createLitCSRClientSetup(importLibraryQrl) {
|
|
46
|
+
const runSetup$ = $(async () => {
|
|
47
|
+
await executeLitCSRClientSetup(importLibraryQrl);
|
|
48
|
+
});
|
|
49
|
+
const useLitCSRClientSetup = () => {
|
|
50
|
+
useOnDocument('readystatechange', runSetup$);
|
|
51
|
+
// Also run immediately when mounted after the document is already past the
|
|
52
|
+
// loading state (client navigation or dev fast-refresh timing).
|
|
53
|
+
if (typeof document !== 'undefined' && document.readyState !== 'loading') {
|
|
54
|
+
void executeLitCSRClientSetup(importLibraryQrl);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
return useLitCSRClientSetup;
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function updateLitCSRHostProps(host: HTMLElement | undefined, props: Record<string, unknown> | undefined): void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const EVENT_PROP_PREFIX = 'on';
|
|
2
|
+
const QRL_PRIVATE_PREFIX = '$';
|
|
3
|
+
function isEventLikeProp(propName) {
|
|
4
|
+
return (propName.startsWith(EVENT_PROP_PREFIX) ||
|
|
5
|
+
propName.startsWith(QRL_PRIVATE_PREFIX));
|
|
6
|
+
}
|
|
7
|
+
export function updateLitCSRHostProps(host, props) {
|
|
8
|
+
if (!host || !props) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
for (const [propName, propValue] of Object.entries(props)) {
|
|
12
|
+
if (isEventLikeProp(propName)) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
host[propName] = propValue;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type QRL } from '@builder.io/qwik';
|
|
2
|
+
export declare function renderComponentCsrTag(options?: {
|
|
3
|
+
tagName?: unknown;
|
|
4
|
+
}): string | null;
|
|
5
|
+
export interface LitCSRProps {
|
|
6
|
+
tagName: string;
|
|
7
|
+
props?: Record<string, unknown>;
|
|
8
|
+
events?: Record<string, QRL<(...args: any[]) => void>>;
|
|
9
|
+
slots?: string[];
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
export declare function createLitCSRComponent(): import("@builder.io/qwik").Component<LitCSRProps>;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@builder.io/qwik/jsx-runtime";
|
|
2
|
+
import { Slot, component$, useSignal, useVisibleTask$, } from '@builder.io/qwik';
|
|
3
|
+
import { updateLitCSRHostProps } from './lit-csr-props.js';
|
|
4
|
+
const EVENT_QRL_IDS = new WeakMap();
|
|
5
|
+
let eventQrlIdCounter = 0;
|
|
6
|
+
const PROP_VALUE_IDS = new WeakMap();
|
|
7
|
+
let propValueIdCounter = 0;
|
|
8
|
+
function createContractError(code, message) {
|
|
9
|
+
const error = new Error(message);
|
|
10
|
+
error.code = code;
|
|
11
|
+
return error;
|
|
12
|
+
}
|
|
13
|
+
export function renderComponentCsrTag(options = {}) {
|
|
14
|
+
if (options.tagName == null) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
if (typeof options.tagName !== 'string' ||
|
|
18
|
+
options.tagName.trim().length === 0) {
|
|
19
|
+
throw createContractError('QCE_LIT_RUNTIME_TAGNAME_INVALID', 'Lit CSR render contract requires options.tagName to be a non-empty string when provided.');
|
|
20
|
+
}
|
|
21
|
+
return options.tagName.trim();
|
|
22
|
+
}
|
|
23
|
+
function requireLitTagName(tagName) {
|
|
24
|
+
const normalizedTagName = renderComponentCsrTag({ tagName });
|
|
25
|
+
if (normalizedTagName == null) {
|
|
26
|
+
throw createContractError('QCE_LIT_RUNTIME_TAGNAME_INVALID', 'Lit CSR render contract requires options.tagName to be a non-empty string when provided.');
|
|
27
|
+
}
|
|
28
|
+
return normalizedTagName;
|
|
29
|
+
}
|
|
30
|
+
function getEventQrlId(qrl) {
|
|
31
|
+
if (!qrl || (typeof qrl !== 'object' && typeof qrl !== 'function')) {
|
|
32
|
+
return -1;
|
|
33
|
+
}
|
|
34
|
+
const qrlObject = qrl;
|
|
35
|
+
const existing = EVENT_QRL_IDS.get(qrlObject);
|
|
36
|
+
if (existing) {
|
|
37
|
+
return existing;
|
|
38
|
+
}
|
|
39
|
+
const next = ++eventQrlIdCounter;
|
|
40
|
+
EVENT_QRL_IDS.set(qrlObject, next);
|
|
41
|
+
return next;
|
|
42
|
+
}
|
|
43
|
+
function getEventEntries(events) {
|
|
44
|
+
return Object.entries(events ?? {}).filter(([eventName, eventQrl]) => eventName.trim().length > 0 && Boolean(eventQrl));
|
|
45
|
+
}
|
|
46
|
+
function getEventsDependencyKey(events) {
|
|
47
|
+
return getEventEntries(events)
|
|
48
|
+
.map(([eventName, eventQrl]) => `${eventName}:${getEventQrlId(eventQrl)}`)
|
|
49
|
+
.sort()
|
|
50
|
+
.join('|');
|
|
51
|
+
}
|
|
52
|
+
function getPropValueDependencyKey(value) {
|
|
53
|
+
if (value == null) {
|
|
54
|
+
return String(value);
|
|
55
|
+
}
|
|
56
|
+
if (typeof value !== 'object' && typeof value !== 'function') {
|
|
57
|
+
return `${typeof value}:${String(value)}`;
|
|
58
|
+
}
|
|
59
|
+
const valueObject = value;
|
|
60
|
+
const existing = PROP_VALUE_IDS.get(valueObject);
|
|
61
|
+
if (existing) {
|
|
62
|
+
return `ref:${existing}`;
|
|
63
|
+
}
|
|
64
|
+
const next = ++propValueIdCounter;
|
|
65
|
+
PROP_VALUE_IDS.set(valueObject, next);
|
|
66
|
+
return `ref:${next}`;
|
|
67
|
+
}
|
|
68
|
+
function getPropsDependencyKey(props) {
|
|
69
|
+
return Object.entries(props ?? {})
|
|
70
|
+
.map(([propName, propValue]) => `${propName}:${getPropValueDependencyKey(propValue)}`)
|
|
71
|
+
.sort()
|
|
72
|
+
.join('|');
|
|
73
|
+
}
|
|
74
|
+
export function createLitCSRComponent() {
|
|
75
|
+
return component$((inputProps) => {
|
|
76
|
+
const hostRef = useSignal();
|
|
77
|
+
const { tagName, props, events, slots, ...restProps } = inputProps;
|
|
78
|
+
const namedSlots = slots ?? [];
|
|
79
|
+
const normalizedTagName = requireLitTagName(tagName);
|
|
80
|
+
useVisibleTask$(({ track, cleanup }) => {
|
|
81
|
+
const host = track(() => hostRef.value);
|
|
82
|
+
const latestTagName = track(() => inputProps.tagName);
|
|
83
|
+
track(() => getPropsDependencyKey(props));
|
|
84
|
+
if (!host) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
let disposed = false;
|
|
88
|
+
cleanup(() => {
|
|
89
|
+
disposed = true;
|
|
90
|
+
});
|
|
91
|
+
updateLitCSRHostProps(host, props);
|
|
92
|
+
const resolvedTagName = requireLitTagName(latestTagName);
|
|
93
|
+
void customElements.whenDefined(resolvedTagName).then(() => {
|
|
94
|
+
if (disposed) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
updateLitCSRHostProps(host, props);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
useVisibleTask$(({ track, cleanup }) => {
|
|
101
|
+
const host = track(() => hostRef.value);
|
|
102
|
+
const eventsDependencyKey = track(() => getEventsDependencyKey(events));
|
|
103
|
+
if (!host) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const eventEntries = getEventEntries(events);
|
|
107
|
+
if (eventEntries.length === 0 || eventsDependencyKey.length === 0) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
let disposed = false;
|
|
111
|
+
const listeners = [];
|
|
112
|
+
cleanup(() => {
|
|
113
|
+
disposed = true;
|
|
114
|
+
for (const { eventName, listener } of listeners) {
|
|
115
|
+
host.removeEventListener(eventName, listener);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
for (const [eventName, eventQrl] of eventEntries) {
|
|
119
|
+
const listener = (event) => {
|
|
120
|
+
const qrl = eventQrl;
|
|
121
|
+
const containerEl = host.closest('[q\\:container]');
|
|
122
|
+
if (containerEl) {
|
|
123
|
+
qrl.$setContainer$?.(containerEl);
|
|
124
|
+
}
|
|
125
|
+
const result = eventQrl(event, host);
|
|
126
|
+
void Promise.resolve(result).catch((error) => {
|
|
127
|
+
console.error(error);
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
if (disposed) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
host.addEventListener(eventName, listener);
|
|
134
|
+
listeners.push({ eventName, listener });
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
const ElementTag = normalizedTagName;
|
|
138
|
+
return (_jsxs(ElementTag, { ref: hostRef, ...restProps, children: [_jsx(Slot, {}), namedSlots.map((name) => (_jsx("span", { slot: name, style: { display: 'contents' }, children: _jsx(Slot, { name: name }) }, name)))] }));
|
|
139
|
+
});
|
|
140
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type QRL } from '@builder.io/qwik';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a client-side setup hook for Lit SSR components.
|
|
4
|
+
*
|
|
5
|
+
* The returned hook registers a `readystatechange` listener that:
|
|
6
|
+
* 1. Loads `@lit-labs/ssr-client/lit-element-hydrate-support.js` first so
|
|
7
|
+
* existing Declarative Shadow DOM roots are reused instead of duplicated.
|
|
8
|
+
* 2. Then calls the provided QRL to load and define the custom elements.
|
|
9
|
+
*
|
|
10
|
+
* Call the returned hook inside a Qwik component to activate hydration.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createLitSSRClientSetup(importLibraryQrl?: QRL<() => Promise<void>>): () => void;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { $, useOnDocument } from '@builder.io/qwik';
|
|
2
|
+
const LIT_SSR_CLIENT_SETUP_DONE = '__qce_lit_ssr_client_setup_done__';
|
|
3
|
+
function ensureLitSSRClientSetup(importLibraryQrl) {
|
|
4
|
+
const markerTarget = globalThis;
|
|
5
|
+
const existingMarker = markerTarget[LIT_SSR_CLIENT_SETUP_DONE];
|
|
6
|
+
if (existingMarker === true) {
|
|
7
|
+
return Promise.resolve();
|
|
8
|
+
}
|
|
9
|
+
if (typeof existingMarker === 'object' &&
|
|
10
|
+
existingMarker !== null &&
|
|
11
|
+
'then' in existingMarker &&
|
|
12
|
+
typeof existingMarker.then === 'function') {
|
|
13
|
+
return existingMarker;
|
|
14
|
+
}
|
|
15
|
+
const setupPromise = (async () => {
|
|
16
|
+
// Must load hydrate-support BEFORE custom elements are defined so that
|
|
17
|
+
// Lit can reuse the existing Declarative Shadow DOM rather than
|
|
18
|
+
// re-rendering and producing a duplicate shadow tree.
|
|
19
|
+
await import('@qwik-custom-elements/adapter-lit/ssr-client');
|
|
20
|
+
if (importLibraryQrl) {
|
|
21
|
+
const fn = await importLibraryQrl.resolve();
|
|
22
|
+
await fn();
|
|
23
|
+
}
|
|
24
|
+
})();
|
|
25
|
+
markerTarget[LIT_SSR_CLIENT_SETUP_DONE] = setupPromise;
|
|
26
|
+
return setupPromise
|
|
27
|
+
.then(() => {
|
|
28
|
+
markerTarget[LIT_SSR_CLIENT_SETUP_DONE] = true;
|
|
29
|
+
})
|
|
30
|
+
.catch((error) => {
|
|
31
|
+
delete markerTarget[LIT_SSR_CLIENT_SETUP_DONE];
|
|
32
|
+
throw error;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async function executeLitSSRClientSetup(importLibraryQrl) {
|
|
36
|
+
await ensureLitSSRClientSetup(importLibraryQrl).catch((error) => {
|
|
37
|
+
console.error(error);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Creates a client-side setup hook for Lit SSR components.
|
|
42
|
+
*
|
|
43
|
+
* The returned hook registers a `readystatechange` listener that:
|
|
44
|
+
* 1. Loads `@lit-labs/ssr-client/lit-element-hydrate-support.js` first so
|
|
45
|
+
* existing Declarative Shadow DOM roots are reused instead of duplicated.
|
|
46
|
+
* 2. Then calls the provided QRL to load and define the custom elements.
|
|
47
|
+
*
|
|
48
|
+
* Call the returned hook inside a Qwik component to activate hydration.
|
|
49
|
+
*/
|
|
50
|
+
export function createLitSSRClientSetup(importLibraryQrl) {
|
|
51
|
+
const runSetup$ = $(async () => {
|
|
52
|
+
await executeLitSSRClientSetup(importLibraryQrl);
|
|
53
|
+
});
|
|
54
|
+
const useLitSSRClientSetup = () => {
|
|
55
|
+
useOnDocument('readystatechange', runSetup$);
|
|
56
|
+
// Also run immediately when mounted after the document is already past the
|
|
57
|
+
// loading state (client navigation or dev fast-refresh timing).
|
|
58
|
+
if (typeof document !== 'undefined' && document.readyState !== 'loading') {
|
|
59
|
+
void executeLitSSRClientSetup(importLibraryQrl);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
return useLitSSRClientSetup;
|
|
63
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
interface CemComponentProp {
|
|
2
|
+
name: string;
|
|
3
|
+
type: string;
|
|
4
|
+
required: boolean;
|
|
5
|
+
}
|
|
6
|
+
interface CemComponentEvent {
|
|
7
|
+
name: string;
|
|
8
|
+
type: string;
|
|
9
|
+
}
|
|
10
|
+
interface CemComponentSlot {
|
|
11
|
+
name: string;
|
|
12
|
+
}
|
|
13
|
+
interface CemComponentDefinition {
|
|
14
|
+
tagName: string;
|
|
15
|
+
props: CemComponentProp[];
|
|
16
|
+
events: CemComponentEvent[];
|
|
17
|
+
slots: CemComponentSlot[];
|
|
18
|
+
}
|
|
19
|
+
interface CreateGeneratedOutputInput {
|
|
20
|
+
projectId?: string;
|
|
21
|
+
libraryName?: string;
|
|
22
|
+
componentDefinitions?: CemComponentDefinition[];
|
|
23
|
+
runtimeImports?: {
|
|
24
|
+
/**
|
|
25
|
+
* Import specifier for the Lit component library. When provided, the
|
|
26
|
+
* generated SSR runtime will include this import so custom element classes
|
|
27
|
+
* are registered before `@lit-labs/ssr` renders them server-side.
|
|
28
|
+
*/
|
|
29
|
+
libraryImport?: string;
|
|
30
|
+
};
|
|
31
|
+
ssrAvailable?: boolean;
|
|
32
|
+
}
|
|
33
|
+
type CreateLitPlannedWritesOptions = CreateGeneratedOutputInput;
|
|
34
|
+
export declare function createLitPlannedWrites({ projectId, libraryName, componentDefinitions, runtimeImports, ssrAvailable, }: CreateLitPlannedWritesOptions): Array<{
|
|
35
|
+
relativePath: string;
|
|
36
|
+
content: string;
|
|
37
|
+
}>;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
export function createLitPlannedWrites({ projectId, libraryName, componentDefinitions, runtimeImports, ssrAvailable, }) {
|
|
2
|
+
const componentTags = (componentDefinitions ?? []).map((componentDefinition) => componentDefinition.tagName);
|
|
3
|
+
const plannedWrites = [
|
|
4
|
+
{
|
|
5
|
+
relativePath: 'index.ts',
|
|
6
|
+
content: renderGeneratedIndex(projectId, componentTags, ssrAvailable),
|
|
7
|
+
},
|
|
8
|
+
];
|
|
9
|
+
if (ssrAvailable === true) {
|
|
10
|
+
plannedWrites.push({
|
|
11
|
+
relativePath: 'runtime.ts',
|
|
12
|
+
content: renderRuntimeBarrelModule(projectId, true),
|
|
13
|
+
}, {
|
|
14
|
+
relativePath: 'runtime-ssr.generated.ts',
|
|
15
|
+
content: renderServerRuntimeModule(projectId, libraryName, runtimeImports?.libraryImport),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
plannedWrites.push({
|
|
20
|
+
relativePath: 'runtime.ts',
|
|
21
|
+
content: renderRuntimeBarrelModule(projectId, false),
|
|
22
|
+
}, {
|
|
23
|
+
relativePath: 'runtime-csr.generated.ts',
|
|
24
|
+
content: renderClientRuntimeModule(projectId, libraryName, runtimeImports?.libraryImport),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
plannedWrites.push(...componentTags.map((componentTag) => ({
|
|
28
|
+
relativePath: renderComponentWrapperRelativePath(componentTag, ssrAvailable),
|
|
29
|
+
content: renderComponentWrapper({
|
|
30
|
+
projectId,
|
|
31
|
+
libraryName,
|
|
32
|
+
componentDefinition: normalizeComponentDefinition(componentTag, (componentDefinitions ?? []).find((definition) => definition.tagName === componentTag)),
|
|
33
|
+
ssrAvailable,
|
|
34
|
+
}),
|
|
35
|
+
})));
|
|
36
|
+
return plannedWrites;
|
|
37
|
+
}
|
|
38
|
+
function renderGeneratedIndex(projectId, componentTags, ssrAvailable) {
|
|
39
|
+
const mode = ssrAvailable === true ? 'ssr' : 'csr';
|
|
40
|
+
const exportLines = componentTags.map((componentTag) => `export { ${toWrapperName(componentTag)} } from './${componentTag}';`);
|
|
41
|
+
return [
|
|
42
|
+
`// Generated by @qwik-custom-elements/core. Project: ${projectId ?? 'unknown'}.`,
|
|
43
|
+
'// Do not edit this file directly. Use a manual extension layer.',
|
|
44
|
+
'',
|
|
45
|
+
`export const generatedComponentTags = ${JSON.stringify(componentTags)} as const;`,
|
|
46
|
+
`export const generatedMode = '${mode}' as const;`,
|
|
47
|
+
...exportLines,
|
|
48
|
+
'',
|
|
49
|
+
].join('\n');
|
|
50
|
+
}
|
|
51
|
+
function renderComponentWrapper(input) {
|
|
52
|
+
const { projectId, libraryName, componentDefinition, ssrAvailable } = input;
|
|
53
|
+
const { tagName: componentTag } = componentDefinition;
|
|
54
|
+
const wrapperName = toWrapperName(componentTag);
|
|
55
|
+
const ssrBridgeName = toBridgeComponentName(libraryName, 'SSR', 'GeneratedLitComponent');
|
|
56
|
+
const csrBridgeName = toBridgeComponentName(libraryName, 'CSR', 'GeneratedLitCSRComponent');
|
|
57
|
+
const bridgeName = ssrAvailable === true ? ssrBridgeName : csrBridgeName;
|
|
58
|
+
const lines = [
|
|
59
|
+
`// Generated by @qwik-custom-elements/core. Project: ${projectId ?? 'unknown'}.`,
|
|
60
|
+
'// Do not edit this file directly. Use a manual extension layer.',
|
|
61
|
+
];
|
|
62
|
+
lines.push('', "import { Slot, component$ } from '@builder.io/qwik';");
|
|
63
|
+
if (componentDefinition.events.length > 0) {
|
|
64
|
+
lines.push("import type { QRL } from '@builder.io/qwik';");
|
|
65
|
+
}
|
|
66
|
+
lines.push(`import { ${bridgeName} } from './runtime';`, '');
|
|
67
|
+
const propsTypeName = `${wrapperName}Props`;
|
|
68
|
+
const propLines = componentDefinition.props.map((prop) => {
|
|
69
|
+
const key = isValidIdentifier(prop.name) ? prop.name : `'${prop.name}'`;
|
|
70
|
+
const optionalToken = prop.required ? '' : '?';
|
|
71
|
+
return ` ${key}${optionalToken}: ${prop.type};`;
|
|
72
|
+
});
|
|
73
|
+
const eventPropLines = componentDefinition.events.map((event) => ` ${toEventPropName(event.name)}?: QRL<(event: ${event.type}) => void>;`);
|
|
74
|
+
propLines.push(...eventPropLines, ' [key: string]: unknown;');
|
|
75
|
+
const slotLines = [
|
|
76
|
+
' <Slot />',
|
|
77
|
+
...componentDefinition.slots.map((slot) => ` <span q:slot=${JSON.stringify(slot.name)} style={{ display: 'contents' }}>\n <Slot name=${JSON.stringify(slot.name)} />\n </span>`),
|
|
78
|
+
];
|
|
79
|
+
const slotListToken = componentDefinition.slots.length > 0
|
|
80
|
+
? JSON.stringify(componentDefinition.slots.map((slot) => slot.name))
|
|
81
|
+
: 'undefined';
|
|
82
|
+
const splitPropsLines = [
|
|
83
|
+
' const isEventBindingKey = (key: string) =>',
|
|
84
|
+
" /^on[A-Z].*\\$$/.test(key) || key.includes(':');",
|
|
85
|
+
' const eventProps: Record<string, unknown> = {};',
|
|
86
|
+
' const elementProps: Record<string, unknown> = {};',
|
|
87
|
+
'',
|
|
88
|
+
' for (const [key, value] of Object.entries(props as Record<string, unknown>)) {',
|
|
89
|
+
" if (key === 'children') {",
|
|
90
|
+
' continue;',
|
|
91
|
+
' }',
|
|
92
|
+
'',
|
|
93
|
+
' if (isEventBindingKey(key)) {',
|
|
94
|
+
' eventProps[key] = value;',
|
|
95
|
+
' continue;',
|
|
96
|
+
' }',
|
|
97
|
+
'',
|
|
98
|
+
' elementProps[key] = value;',
|
|
99
|
+
' }',
|
|
100
|
+
];
|
|
101
|
+
const mappedEventLines = componentDefinition.events.length > 0
|
|
102
|
+
? [
|
|
103
|
+
'',
|
|
104
|
+
` const mappedEventPropKeys = new Set(${JSON.stringify(componentDefinition.events.map((event) => toEventPropName(event.name)))});`,
|
|
105
|
+
' const passthroughEventProps = Object.fromEntries(',
|
|
106
|
+
' Object.entries(eventProps).filter(',
|
|
107
|
+
' ([key]) => !mappedEventPropKeys.has(key),',
|
|
108
|
+
' ),',
|
|
109
|
+
' );',
|
|
110
|
+
'',
|
|
111
|
+
' const events: Record<string, QRL<(...args: any[]) => void>> = {};',
|
|
112
|
+
...componentDefinition.events.map((event) => ` if (props.${toEventPropName(event.name)}) { events['${event.name}'] = props.${toEventPropName(event.name)}; }`),
|
|
113
|
+
' const mappedEvents = Object.keys(events).length > 0 ? events : undefined;',
|
|
114
|
+
]
|
|
115
|
+
: [
|
|
116
|
+
'',
|
|
117
|
+
' const passthroughEventProps = eventProps;',
|
|
118
|
+
' const mappedEvents = undefined;',
|
|
119
|
+
];
|
|
120
|
+
lines.push(`export interface ${propsTypeName} {`, ...propLines, '}', '', `export const ${wrapperName} = component$<${propsTypeName}>((props) => {`, ...splitPropsLines, ...mappedEventLines, '', ' return (', ` <${bridgeName}`, ` tagName=${JSON.stringify(componentTag)}`, ' props={elementProps}', ' events={mappedEvents}', ` slots={${slotListToken}}`, ' {...passthroughEventProps}', ' >', ...slotLines, ` </${bridgeName}>`, ' );', '});');
|
|
121
|
+
lines.push('');
|
|
122
|
+
return lines.join('\n');
|
|
123
|
+
}
|
|
124
|
+
function renderComponentWrapperRelativePath(componentTag, _ssrAvailable) {
|
|
125
|
+
return `${componentTag}.tsx`;
|
|
126
|
+
}
|
|
127
|
+
function renderRuntimeBarrelModule(projectId, ssrAvailable) {
|
|
128
|
+
const runtimeExportLine = ssrAvailable
|
|
129
|
+
? "export * from './runtime-ssr.generated';"
|
|
130
|
+
: "export * from './runtime-csr.generated';";
|
|
131
|
+
return [
|
|
132
|
+
`// Generated by @qwik-custom-elements/core. Project: ${projectId ?? 'unknown'}.`,
|
|
133
|
+
'// Do not edit this file directly. Use a manual extension layer.',
|
|
134
|
+
'',
|
|
135
|
+
runtimeExportLine,
|
|
136
|
+
'',
|
|
137
|
+
].join('\n');
|
|
138
|
+
}
|
|
139
|
+
function renderClientRuntimeModule(projectId, libraryName, libraryImport) {
|
|
140
|
+
const csrBridgeName = toBridgeComponentName(libraryName, 'CSR', 'GeneratedLitCSRComponent');
|
|
141
|
+
const clientSetupName = toCsrClientSetupHookName(libraryName);
|
|
142
|
+
const hasLibraryImport = libraryImport !== undefined && libraryImport.trim().length > 0;
|
|
143
|
+
const lines = [
|
|
144
|
+
`// Generated by @qwik-custom-elements/core. Project: ${projectId ?? 'unknown'}.`,
|
|
145
|
+
'// Do not edit this file directly. Use a manual extension layer.',
|
|
146
|
+
'',
|
|
147
|
+
];
|
|
148
|
+
if (hasLibraryImport) {
|
|
149
|
+
lines.push("import { $ } from '@builder.io/qwik';");
|
|
150
|
+
lines.push(`import { createLitCSRClientSetup, createLitCSRComponent } from '@qwik-custom-elements/adapter-lit/client';`);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
lines.push("import { createLitCSRComponent } from '@qwik-custom-elements/adapter-lit/client';");
|
|
154
|
+
}
|
|
155
|
+
if (hasLibraryImport) {
|
|
156
|
+
lines.push('', `const importLibrary = async (): Promise<void> => {`, ` await import('${libraryImport}');`, `};`, `const importLibraryQrl = $(importLibrary);`, `export const ${clientSetupName} = createLitCSRClientSetup(importLibraryQrl);`);
|
|
157
|
+
}
|
|
158
|
+
lines.push('', `export const ${csrBridgeName} = createLitCSRComponent();`, '');
|
|
159
|
+
return lines.join('\n');
|
|
160
|
+
}
|
|
161
|
+
function renderServerRuntimeModule(projectId, libraryName, libraryImport) {
|
|
162
|
+
const ssrBridgeName = toBridgeComponentName(libraryName, 'SSR', 'GeneratedLitComponent');
|
|
163
|
+
const clientSetupName = toClientSetupHookName(libraryName);
|
|
164
|
+
const hasLibraryImport = libraryImport !== undefined && libraryImport.trim().length > 0;
|
|
165
|
+
const lines = [
|
|
166
|
+
`// Generated by @qwik-custom-elements/core. Project: ${projectId ?? 'unknown'}.`,
|
|
167
|
+
'// Do not edit this file directly. Use a manual extension layer.',
|
|
168
|
+
'',
|
|
169
|
+
];
|
|
170
|
+
if (hasLibraryImport) {
|
|
171
|
+
lines.push("import { $ } from '@builder.io/qwik';");
|
|
172
|
+
lines.push("import { createLitSSRClientSetup } from '@qwik-custom-elements/adapter-lit/client';");
|
|
173
|
+
}
|
|
174
|
+
lines.push("import { createLitSSRComponent } from '@qwik-custom-elements/adapter-lit/ssr';");
|
|
175
|
+
if (hasLibraryImport) {
|
|
176
|
+
lines.push(`// Register Lit element classes for server-side rendering.`, `import '${libraryImport}';`, '', `const importLibrary = async (): Promise<void> => {`, ` await import('${libraryImport}');`, `};`, `const importLibraryQrl = $(importLibrary);`, `export const ${clientSetupName} = createLitSSRClientSetup(importLibraryQrl);`);
|
|
177
|
+
}
|
|
178
|
+
lines.push('', `export const ${ssrBridgeName} = createLitSSRComponent();`, '');
|
|
179
|
+
return lines.join('\n');
|
|
180
|
+
}
|
|
181
|
+
function toClientSetupHookName(libraryName) {
|
|
182
|
+
if (!libraryName || libraryName.trim().length === 0) {
|
|
183
|
+
return 'useLitSSRClientSetup';
|
|
184
|
+
}
|
|
185
|
+
return `use${toPascalCase(libraryName)}SSRClientSetup`;
|
|
186
|
+
}
|
|
187
|
+
function toCsrClientSetupHookName(libraryName) {
|
|
188
|
+
if (!libraryName || libraryName.trim().length === 0) {
|
|
189
|
+
return 'useLitCSRClientSetup';
|
|
190
|
+
}
|
|
191
|
+
return `use${toPascalCase(libraryName)}CSRClientSetup`;
|
|
192
|
+
}
|
|
193
|
+
function toPascalCase(name) {
|
|
194
|
+
return name
|
|
195
|
+
.split(/[-_]/)
|
|
196
|
+
.filter((p) => p.length > 0)
|
|
197
|
+
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
|
|
198
|
+
.join('');
|
|
199
|
+
}
|
|
200
|
+
function isValidIdentifier(value) {
|
|
201
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value);
|
|
202
|
+
}
|
|
203
|
+
function toEventPropName(eventName) {
|
|
204
|
+
return `on${toPascalCase(eventName)}$`;
|
|
205
|
+
}
|
|
206
|
+
function normalizeComponentDefinition(componentTag, input) {
|
|
207
|
+
return {
|
|
208
|
+
tagName: input?.tagName ?? componentTag,
|
|
209
|
+
props: input?.props ?? [],
|
|
210
|
+
events: input?.events ?? [],
|
|
211
|
+
slots: input?.slots ?? [],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function toBridgeComponentName(libraryName, mode, fallback) {
|
|
215
|
+
if (!libraryName || libraryName.trim().length === 0) {
|
|
216
|
+
return fallback;
|
|
217
|
+
}
|
|
218
|
+
return `${toPascalCase(libraryName)}${mode}BridgeComponent`;
|
|
219
|
+
}
|
|
220
|
+
function toWrapperName(componentTag) {
|
|
221
|
+
const parts = componentTag.split('-').filter((part) => part.length > 0);
|
|
222
|
+
const normalizedName = parts
|
|
223
|
+
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
|
|
224
|
+
.join('');
|
|
225
|
+
return `Qwik${normalizedName}`;
|
|
226
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const metadata: {
|
|
2
|
+
adapterId: string;
|
|
3
|
+
supportedSourceTypes: string[];
|
|
4
|
+
supportsSsrProbe: boolean;
|
|
5
|
+
ssrRuntimeSubpath: string;
|
|
6
|
+
};
|
|
7
|
+
interface ResolveRuntimeImportsInput {
|
|
8
|
+
source: {
|
|
9
|
+
type: 'CEM' | 'PACKAGE_NAME';
|
|
10
|
+
packageName?: string;
|
|
11
|
+
};
|
|
12
|
+
adapterOptions?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
export declare function resolveRuntimeImports({ source, adapterOptions, }: ResolveRuntimeImportsInput): Promise<{
|
|
15
|
+
libraryImport?: string;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function probeSSR(): Promise<{
|
|
18
|
+
available: boolean;
|
|
19
|
+
}>;
|
|
20
|
+
export declare function createGeneratedOutput(input: {
|
|
21
|
+
projectId?: string;
|
|
22
|
+
libraryName?: string;
|
|
23
|
+
componentDefinitions?: Array<{
|
|
24
|
+
tagName: string;
|
|
25
|
+
props: Array<{
|
|
26
|
+
name: string;
|
|
27
|
+
type: string;
|
|
28
|
+
required: boolean;
|
|
29
|
+
}>;
|
|
30
|
+
events: Array<{
|
|
31
|
+
name: string;
|
|
32
|
+
type: string;
|
|
33
|
+
}>;
|
|
34
|
+
slots: Array<{
|
|
35
|
+
name: string;
|
|
36
|
+
}>;
|
|
37
|
+
}>;
|
|
38
|
+
runtimeImports?: {
|
|
39
|
+
libraryImport?: string;
|
|
40
|
+
};
|
|
41
|
+
ssrAvailable?: boolean;
|
|
42
|
+
}): Array<{
|
|
43
|
+
relativePath: string;
|
|
44
|
+
content: string;
|
|
45
|
+
}>;
|
|
46
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createLitPlannedWrites } from './generated-output.js';
|
|
2
|
+
export const metadata = {
|
|
3
|
+
adapterId: 'lit',
|
|
4
|
+
supportedSourceTypes: ['CEM', 'PACKAGE_NAME'],
|
|
5
|
+
supportsSsrProbe: true,
|
|
6
|
+
ssrRuntimeSubpath: './ssr',
|
|
7
|
+
};
|
|
8
|
+
export async function resolveRuntimeImports({ source, adapterOptions, }) {
|
|
9
|
+
const runtime = typeof adapterOptions?.runtime === 'object' &&
|
|
10
|
+
adapterOptions.runtime !== null &&
|
|
11
|
+
!Array.isArray(adapterOptions.runtime)
|
|
12
|
+
? adapterOptions.runtime
|
|
13
|
+
: undefined;
|
|
14
|
+
const libraryImportOverride = typeof runtime?.libraryImport === 'string' &&
|
|
15
|
+
runtime.libraryImport.trim().length > 0
|
|
16
|
+
? runtime.libraryImport
|
|
17
|
+
: undefined;
|
|
18
|
+
if (source.type === 'CEM') {
|
|
19
|
+
return libraryImportOverride != null
|
|
20
|
+
? { libraryImport: libraryImportOverride }
|
|
21
|
+
: {};
|
|
22
|
+
}
|
|
23
|
+
const packageName = source.packageName;
|
|
24
|
+
if (typeof packageName !== 'string' || packageName.trim().length === 0) {
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
return { libraryImport: libraryImportOverride ?? packageName };
|
|
28
|
+
}
|
|
29
|
+
export async function probeSSR() {
|
|
30
|
+
return { available: false };
|
|
31
|
+
}
|
|
32
|
+
export function createGeneratedOutput(input) {
|
|
33
|
+
return createLitPlannedWrites(input);
|
|
34
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import '@lit-labs/ssr-client/lit-element-hydrate-support.js';
|
|
2
|
+
import { createLitSSRComponent } from './lit-ssr.js';
|
|
3
|
+
export declare const metadata: {
|
|
4
|
+
adapterId: string;
|
|
5
|
+
supportedSourceTypes: string[];
|
|
6
|
+
supportsSsrProbe: boolean;
|
|
7
|
+
ssrRuntimeSubpath: string;
|
|
8
|
+
};
|
|
9
|
+
interface ValidateProjectInput {
|
|
10
|
+
source: {
|
|
11
|
+
type: 'CEM' | 'PACKAGE_NAME';
|
|
12
|
+
packageName?: string;
|
|
13
|
+
};
|
|
14
|
+
adapterOptions?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
interface ResolveRuntimeImportsInput extends ValidateProjectInput {
|
|
17
|
+
runtimeResolution?: {
|
|
18
|
+
resolveImportSpecifier?: (specifier: string, packageRoot?: string) => string;
|
|
19
|
+
resolveSourcePackageRoot?: (packageName: string) => string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
interface ProbeSsrInput {
|
|
23
|
+
runtimeImports?: {
|
|
24
|
+
libraryImport?: unknown;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export type { LitSSRProps } from './lit-ssr.js';
|
|
28
|
+
export declare function validateProject({ source, adapterOptions, }: ValidateProjectInput): void;
|
|
29
|
+
export declare function resolveRuntimeImports({ source, adapterOptions, runtimeResolution, }: ResolveRuntimeImportsInput): Promise<{
|
|
30
|
+
libraryImport?: string;
|
|
31
|
+
}>;
|
|
32
|
+
export declare function probeSSR(_input?: ProbeSsrInput): Promise<{
|
|
33
|
+
available: boolean;
|
|
34
|
+
}>;
|
|
35
|
+
export declare function renderComponentSsrHtml(options?: {
|
|
36
|
+
tagName?: unknown;
|
|
37
|
+
props?: Record<string, unknown>;
|
|
38
|
+
}): string | null;
|
|
39
|
+
export type LitGeneratedSsrComponent = ReturnType<typeof createLitSSRComponent>;
|
|
40
|
+
export { createLitSSRComponent };
|
|
41
|
+
export declare function createGeneratedOutput(input: {
|
|
42
|
+
projectId?: string;
|
|
43
|
+
libraryName?: string;
|
|
44
|
+
componentDefinitions?: Array<{
|
|
45
|
+
tagName: string;
|
|
46
|
+
props: Array<{
|
|
47
|
+
name: string;
|
|
48
|
+
type: string;
|
|
49
|
+
required: boolean;
|
|
50
|
+
}>;
|
|
51
|
+
events: Array<{
|
|
52
|
+
name: string;
|
|
53
|
+
type: string;
|
|
54
|
+
}>;
|
|
55
|
+
slots: Array<{
|
|
56
|
+
name: string;
|
|
57
|
+
}>;
|
|
58
|
+
}>;
|
|
59
|
+
runtimeImports?: {
|
|
60
|
+
libraryImport?: string;
|
|
61
|
+
};
|
|
62
|
+
ssrAvailable?: boolean;
|
|
63
|
+
}): Array<{
|
|
64
|
+
relativePath: string;
|
|
65
|
+
content: string;
|
|
66
|
+
}>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import '@lit-labs/ssr-client/lit-element-hydrate-support.js';
|
|
2
|
+
import { createLitPlannedWrites } from '../generated-output.js';
|
|
3
|
+
import { createLitSSRComponent } from './lit-ssr.js';
|
|
4
|
+
export const metadata = {
|
|
5
|
+
adapterId: 'lit',
|
|
6
|
+
supportedSourceTypes: ['CEM', 'PACKAGE_NAME'],
|
|
7
|
+
supportsSsrProbe: true,
|
|
8
|
+
ssrRuntimeSubpath: './ssr',
|
|
9
|
+
};
|
|
10
|
+
export function validateProject({ source, adapterOptions, }) {
|
|
11
|
+
const runtime = isRecord(adapterOptions?.runtime)
|
|
12
|
+
? adapterOptions.runtime
|
|
13
|
+
: undefined;
|
|
14
|
+
validateOptionalRuntimeOverride(runtime, 'libraryImport', 'QCE_LIT_RUNTIME_LIBRARY_IMPORT_OVERRIDE_INVALID', `Lit ${source.type} projects must provide a non-empty adapterOptions.runtime.libraryImport override when the override is set.`);
|
|
15
|
+
}
|
|
16
|
+
export async function resolveRuntimeImports({ source, adapterOptions, runtimeResolution, }) {
|
|
17
|
+
const runtime = isRecord(adapterOptions?.runtime)
|
|
18
|
+
? adapterOptions.runtime
|
|
19
|
+
: undefined;
|
|
20
|
+
const libraryImportOverride = validateOptionalRuntimeOverride(runtime, 'libraryImport', 'QCE_LIT_RUNTIME_LIBRARY_IMPORT_OVERRIDE_INVALID', `Lit ${source.type} projects must provide a non-empty adapterOptions.runtime.libraryImport override when the override is set.`);
|
|
21
|
+
if (source.type === 'CEM') {
|
|
22
|
+
return libraryImportOverride != null
|
|
23
|
+
? { libraryImport: libraryImportOverride }
|
|
24
|
+
: {};
|
|
25
|
+
}
|
|
26
|
+
const packageName = source.packageName;
|
|
27
|
+
if (typeof packageName !== 'string' || packageName.trim().length === 0) {
|
|
28
|
+
throw createContractError('QCE_LIT_RUNTIME_LIBRARY_IMPORT_REQUIRED', 'Lit PACKAGE_NAME projects must provide source.packageName or adapterOptions.runtime.libraryImport.');
|
|
29
|
+
}
|
|
30
|
+
const rawLibraryImport = libraryImportOverride ?? packageName;
|
|
31
|
+
const libraryImport = runtimeResolution?.resolveImportSpecifier != null
|
|
32
|
+
? runtimeResolution.resolveImportSpecifier(rawLibraryImport, runtimeResolution.resolveSourcePackageRoot?.(packageName))
|
|
33
|
+
: rawLibraryImport;
|
|
34
|
+
return { libraryImport };
|
|
35
|
+
}
|
|
36
|
+
export async function probeSSR(_input = {}) {
|
|
37
|
+
// SSR is available whenever the Lit SSR adapter subpath can be loaded.
|
|
38
|
+
// @lit-labs/ssr-client/lit-element-hydrate-support.js is imported at module
|
|
39
|
+
// load time above — if this module resolves, the hydration infrastructure is
|
|
40
|
+
// ready. libraryImport is optional and only affects whether the library is
|
|
41
|
+
// pre-registered before server rendering.
|
|
42
|
+
return { available: true };
|
|
43
|
+
}
|
|
44
|
+
export function renderComponentSsrHtml(options = {}) {
|
|
45
|
+
// Keep fallback and hard-failure deterministic for contract tests.
|
|
46
|
+
if (options.tagName == null) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (typeof options.tagName !== 'string' ||
|
|
50
|
+
options.tagName.trim().length === 0) {
|
|
51
|
+
throw createContractError('QCE_LIT_RUNTIME_TAGNAME_INVALID', 'Lit SSR render contract requires options.tagName to be a non-empty string when provided.');
|
|
52
|
+
}
|
|
53
|
+
const tagName = options.tagName.trim();
|
|
54
|
+
const serializedProps = serializePropsForHtml(options.props ?? {});
|
|
55
|
+
const openingTag = serializedProps.length
|
|
56
|
+
? `<${tagName} ${serializedProps}>`
|
|
57
|
+
: `<${tagName}>`;
|
|
58
|
+
return `${openingTag}</${tagName}>`;
|
|
59
|
+
}
|
|
60
|
+
export { createLitSSRComponent };
|
|
61
|
+
function isRecord(value) {
|
|
62
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
63
|
+
}
|
|
64
|
+
function isNonEmptyString(value) {
|
|
65
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
66
|
+
}
|
|
67
|
+
function validateOptionalRuntimeOverride(runtime, field, errorCode, errorMessage) {
|
|
68
|
+
if (!isRecord(runtime) || !(field in runtime)) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
const value = runtime[field];
|
|
72
|
+
if (!isNonEmptyString(value)) {
|
|
73
|
+
throw createContractError(errorCode, errorMessage);
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
function createContractError(code, message) {
|
|
78
|
+
const error = new Error(message);
|
|
79
|
+
error.code = code;
|
|
80
|
+
return error;
|
|
81
|
+
}
|
|
82
|
+
export function createGeneratedOutput(input) {
|
|
83
|
+
return createLitPlannedWrites(input);
|
|
84
|
+
}
|
|
85
|
+
function serializePropsForHtml(props) {
|
|
86
|
+
return Object.entries(props)
|
|
87
|
+
.filter(([key, value]) => key.trim().length > 0 && isSerializableValue(value))
|
|
88
|
+
.map(([key, value]) => {
|
|
89
|
+
if (value === true) {
|
|
90
|
+
return key;
|
|
91
|
+
}
|
|
92
|
+
return `${key}=${JSON.stringify(String(value))}`;
|
|
93
|
+
})
|
|
94
|
+
.join(' ');
|
|
95
|
+
}
|
|
96
|
+
function isSerializableValue(value) {
|
|
97
|
+
if (value == null) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return (typeof value === 'string' ||
|
|
101
|
+
typeof value === 'number' ||
|
|
102
|
+
typeof value === 'boolean');
|
|
103
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type QRL } from '@builder.io/qwik';
|
|
2
|
+
export interface LitSSRProps {
|
|
3
|
+
tagName: string;
|
|
4
|
+
props?: Record<string, unknown>;
|
|
5
|
+
/**
|
|
6
|
+
* Optional custom-event names exposed by the Lit element.
|
|
7
|
+
* Keys are native event names; values are Qwik QRL handlers.
|
|
8
|
+
*/
|
|
9
|
+
events?: Record<string, QRL<(...args: any[]) => void>>;
|
|
10
|
+
/**
|
|
11
|
+
* Named slot names exposed by the Lit element (not including the default slot).
|
|
12
|
+
*/
|
|
13
|
+
slots?: string[];
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Creates a Qwik component that renders a Lit custom element with SSR support.
|
|
18
|
+
* On the server, uses `@lit-labs/ssr` to render the element with its shadow DOM
|
|
19
|
+
* as declarative shadow DOM (DSD). On the client, the element self-hydrates and
|
|
20
|
+
* Qwik wires props and events.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createLitSSRComponent(): import("@builder.io/qwik").Component<LitSSRProps>;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@builder.io/qwik/jsx-runtime";
|
|
2
|
+
import { $, component$, isBrowser, isServer, Slot, SSRRaw, SSRStream, useId, useOnDocument, useSignal, useTask$, } from '@builder.io/qwik';
|
|
3
|
+
import { updateLitCSRHostProps } from '../client/lit-csr-props.js';
|
|
4
|
+
const EVENT_QRL_IDS = new WeakMap();
|
|
5
|
+
let eventQrlIdCounter = 0;
|
|
6
|
+
function getEventQrlId(qrl) {
|
|
7
|
+
if (!qrl || (typeof qrl !== 'object' && typeof qrl !== 'function')) {
|
|
8
|
+
return -1;
|
|
9
|
+
}
|
|
10
|
+
const qrlObj = qrl;
|
|
11
|
+
const existing = EVENT_QRL_IDS.get(qrlObj);
|
|
12
|
+
if (existing) {
|
|
13
|
+
return existing;
|
|
14
|
+
}
|
|
15
|
+
const next = ++eventQrlIdCounter;
|
|
16
|
+
EVENT_QRL_IDS.set(qrlObj, next);
|
|
17
|
+
return next;
|
|
18
|
+
}
|
|
19
|
+
function getEventEntries(events) {
|
|
20
|
+
return Object.entries(events ?? {}).filter(([eventName, eventQrl]) => eventName.trim().length > 0 && Boolean(eventQrl));
|
|
21
|
+
}
|
|
22
|
+
function getEventsDependencyKey(events) {
|
|
23
|
+
return getEventEntries(events)
|
|
24
|
+
.map(([eventName, eventQrl]) => `${eventName}:${getEventQrlId(eventQrl)}`)
|
|
25
|
+
.sort()
|
|
26
|
+
.join('|');
|
|
27
|
+
}
|
|
28
|
+
function getWrapperElement(wrapperId) {
|
|
29
|
+
if (!isBrowser)
|
|
30
|
+
return undefined;
|
|
31
|
+
return (document.querySelector(`[data-lit-wrapper-id="${wrapperId}"]`) ?? undefined);
|
|
32
|
+
}
|
|
33
|
+
function getLitElement(wrapper, tagName) {
|
|
34
|
+
return wrapper?.querySelector(tagName) ?? undefined;
|
|
35
|
+
}
|
|
36
|
+
function serializePropsToAttributes(props) {
|
|
37
|
+
return Object.entries(props)
|
|
38
|
+
.filter(([key, value]) => key.trim().length > 0 && isSerializableAttributeValue(value))
|
|
39
|
+
.map(([key, value]) => {
|
|
40
|
+
if (value === true) {
|
|
41
|
+
return key;
|
|
42
|
+
}
|
|
43
|
+
return `${key}=${JSON.stringify(String(value))}`;
|
|
44
|
+
})
|
|
45
|
+
.join(' ');
|
|
46
|
+
}
|
|
47
|
+
function isSerializableAttributeValue(value) {
|
|
48
|
+
if (value == null) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return (typeof value === 'string' ||
|
|
52
|
+
typeof value === 'number' ||
|
|
53
|
+
typeof value === 'boolean');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Creates a Qwik component that renders a Lit custom element with SSR support.
|
|
57
|
+
* On the server, uses `@lit-labs/ssr` to render the element with its shadow DOM
|
|
58
|
+
* as declarative shadow DOM (DSD). On the client, the element self-hydrates and
|
|
59
|
+
* Qwik wires props and events.
|
|
60
|
+
*/
|
|
61
|
+
export function createLitSSRComponent() {
|
|
62
|
+
return component$(({ tagName, props, events, slots, ...restProps }) => {
|
|
63
|
+
const wrapperRef = useSignal(undefined);
|
|
64
|
+
const wrapperId = useId();
|
|
65
|
+
const clientReady = useSignal(false);
|
|
66
|
+
const namedSlots = slots ?? [];
|
|
67
|
+
useOnDocument('qinit', $(() => {
|
|
68
|
+
clientReady.value = true;
|
|
69
|
+
}));
|
|
70
|
+
// Sync props to the Lit element on the client whenever they change.
|
|
71
|
+
useTask$(({ track }) => {
|
|
72
|
+
const trackedProps = track(() => props);
|
|
73
|
+
if (!isBrowser)
|
|
74
|
+
return;
|
|
75
|
+
const wrapper = getWrapperElement(wrapperId) ?? wrapperRef.value;
|
|
76
|
+
const litEl = getLitElement(wrapper, tagName);
|
|
77
|
+
updateLitCSRHostProps(litEl, trackedProps);
|
|
78
|
+
});
|
|
79
|
+
// Wire event listeners after the client is ready (post-qinit).
|
|
80
|
+
useTask$(({ cleanup, track }) => {
|
|
81
|
+
const ready = track(() => clientReady.value);
|
|
82
|
+
const eventsDependencyKey = track(() => getEventsDependencyKey(events));
|
|
83
|
+
if (!isBrowser || !ready)
|
|
84
|
+
return;
|
|
85
|
+
const wrapper = getWrapperElement(wrapperId) ?? wrapperRef.value;
|
|
86
|
+
const litEl = getLitElement(wrapper, tagName);
|
|
87
|
+
const eventEntries = getEventEntries(events);
|
|
88
|
+
if (!litEl ||
|
|
89
|
+
eventEntries.length === 0 ||
|
|
90
|
+
eventsDependencyKey.length === 0) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
let disposed = false;
|
|
94
|
+
const listeners = [];
|
|
95
|
+
cleanup(() => {
|
|
96
|
+
disposed = true;
|
|
97
|
+
for (const { eventName, listener } of listeners) {
|
|
98
|
+
litEl.removeEventListener(eventName, listener);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
for (const [eventName, eventQrl] of eventEntries) {
|
|
102
|
+
const listener = (event) => {
|
|
103
|
+
const qrl = eventQrl;
|
|
104
|
+
const containerEl = litEl.closest('[q\\:container]');
|
|
105
|
+
if (containerEl) {
|
|
106
|
+
qrl.$setContainer$?.(containerEl);
|
|
107
|
+
}
|
|
108
|
+
void Promise.resolve(eventQrl(event, litEl)).catch(console.error);
|
|
109
|
+
};
|
|
110
|
+
if (disposed)
|
|
111
|
+
return;
|
|
112
|
+
litEl.addEventListener(eventName, listener);
|
|
113
|
+
listeners.push({ eventName, listener });
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
if (isServer) {
|
|
117
|
+
return (_jsx("div", { ref: wrapperRef, "data-lit-wrapper-id": wrapperId, style: { display: 'contents' }, ...restProps, children: _jsx(SSRStream, { children: async function* () {
|
|
118
|
+
// Dynamic imports ensure these server-only packages are not
|
|
119
|
+
// bundled into client code.
|
|
120
|
+
const { render } = await import('@lit-labs/ssr');
|
|
121
|
+
const { html } = await import('lit');
|
|
122
|
+
const propsHtml = serializePropsToAttributes(props ?? {});
|
|
123
|
+
const staticTemplate = propsHtml
|
|
124
|
+
? `<${tagName} ${propsHtml}></${tagName}>`
|
|
125
|
+
: `<${tagName}></${tagName}>`;
|
|
126
|
+
// Build a TemplateResult from a static string so @lit-labs/ssr
|
|
127
|
+
// can invoke registered element renderers and produce DSD.
|
|
128
|
+
// unsafeHTML() bypasses element renderer lookup; calling html()
|
|
129
|
+
// directly with a static-strings array does not.
|
|
130
|
+
const strings = Object.assign([staticTemplate], {
|
|
131
|
+
raw: [staticTemplate],
|
|
132
|
+
});
|
|
133
|
+
// Collect the full SSR output from @lit-labs/ssr.
|
|
134
|
+
let fullHtml = '';
|
|
135
|
+
for (const chunk of render(html(strings))) {
|
|
136
|
+
fullHtml += String(chunk);
|
|
137
|
+
}
|
|
138
|
+
// Inject Qwik slot content as light-DOM children of the element
|
|
139
|
+
// (before the closing tag) so the shadow DOM <slot> outlets
|
|
140
|
+
// can pick them up after client hydration.
|
|
141
|
+
const closingTag = `</${tagName}>`;
|
|
142
|
+
const closingTagIdx = fullHtml.lastIndexOf(closingTag);
|
|
143
|
+
if (closingTagIdx === -1) {
|
|
144
|
+
// No closing tag found — emit as-is and append slot content.
|
|
145
|
+
yield _jsx(SSRRaw, { data: fullHtml });
|
|
146
|
+
yield _jsx(Slot, {});
|
|
147
|
+
for (const slotName of namedSlots) {
|
|
148
|
+
yield (_jsx("span", { slot: slotName, style: { display: 'contents' }, children: _jsx(Slot, { name: slotName }) }));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
// Emit everything before the closing tag.
|
|
153
|
+
yield _jsx(SSRRaw, { data: fullHtml.slice(0, closingTagIdx) });
|
|
154
|
+
// Project default and named slot content.
|
|
155
|
+
yield _jsx(Slot, {});
|
|
156
|
+
for (const slotName of namedSlots) {
|
|
157
|
+
yield (_jsx("span", { slot: slotName, style: { display: 'contents' }, children: _jsx(Slot, { name: slotName }) }));
|
|
158
|
+
}
|
|
159
|
+
// Emit the closing tag.
|
|
160
|
+
yield _jsx(SSRRaw, { data: closingTag });
|
|
161
|
+
}
|
|
162
|
+
} }) }));
|
|
163
|
+
}
|
|
164
|
+
const HostTag = tagName;
|
|
165
|
+
// Client path: the Lit element is already present in the DOM from SSR and
|
|
166
|
+
// will self-hydrate using the declarative shadow DOM. Render the same host
|
|
167
|
+
// custom element shape as SSR so Qwik reconciliation keeps slot content
|
|
168
|
+
// inside the element across signal-driven rerenders.
|
|
169
|
+
return (_jsx("div", { ref: wrapperRef, "data-lit-wrapper-id": wrapperId, style: { display: 'contents' }, ...restProps, children: _jsxs(HostTag, { children: [_jsx(Slot, {}), namedSlots.map((name) => (_jsx("span", { slot: name, style: { display: 'contents' }, children: _jsx(Slot, { name: name }) }, name)))] }) }));
|
|
170
|
+
});
|
|
171
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side Lit SSR hydration support.
|
|
3
|
+
*
|
|
4
|
+
* Import this module (or ensure it is evaluated) **before** any Lit custom
|
|
5
|
+
* elements are defined via `customElements.define()`. It patches `LitElement`
|
|
6
|
+
* so that elements whose shadow root was already created from a Declarative
|
|
7
|
+
* Shadow DOM (DSD) template — produced by `@lit-labs/ssr` on the server — are
|
|
8
|
+
* hydrated in place rather than being re-rendered from scratch.
|
|
9
|
+
*
|
|
10
|
+
* Without this patch, Lit's first `performUpdate()` call appends a fresh render
|
|
11
|
+
* tree alongside the existing DSD content, producing a duplicate shadow DOM
|
|
12
|
+
* (e.g., two `.de-alert` divs visible to the user).
|
|
13
|
+
*/
|
|
14
|
+
import '@lit-labs/ssr-client/lit-element-hydrate-support.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side Lit SSR hydration support.
|
|
3
|
+
*
|
|
4
|
+
* Import this module (or ensure it is evaluated) **before** any Lit custom
|
|
5
|
+
* elements are defined via `customElements.define()`. It patches `LitElement`
|
|
6
|
+
* so that elements whose shadow root was already created from a Declarative
|
|
7
|
+
* Shadow DOM (DSD) template — produced by `@lit-labs/ssr` on the server — are
|
|
8
|
+
* hydrated in place rather than being re-rendered from scratch.
|
|
9
|
+
*
|
|
10
|
+
* Without this patch, Lit's first `performUpdate()` call appends a fresh render
|
|
11
|
+
* tree alongside the existing DSD content, producing a duplicate shadow DOM
|
|
12
|
+
* (e.g., two `.de-alert` divs visible to the user).
|
|
13
|
+
*/
|
|
14
|
+
import '@lit-labs/ssr-client/lit-element-hydrate-support.js';
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@qwik-custom-elements/adapter-lit",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"types": "./dist/index.d.ts",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./client": {
|
|
12
|
+
"types": "./dist/client/index.d.ts",
|
|
13
|
+
"import": "./dist/client/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./ssr": {
|
|
16
|
+
"types": "./dist/ssr/index.d.ts",
|
|
17
|
+
"import": "./dist/ssr/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./ssr-client": {
|
|
20
|
+
"types": "./dist/ssr-client.d.ts",
|
|
21
|
+
"import": "./dist/ssr-client.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@builder.io/qwik": "^1.19.2",
|
|
29
|
+
"@lit-labs/ssr": ">=4.0.0",
|
|
30
|
+
"@lit-labs/ssr-client": ">=1.1.0",
|
|
31
|
+
"lit": ">=3.0.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependenciesMeta": {
|
|
34
|
+
"@lit-labs/ssr": {
|
|
35
|
+
"optional": true
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@builder.io/qwik": "^1.19.2",
|
|
40
|
+
"@lit-labs/ssr": "^4.0.0",
|
|
41
|
+
"@lit-labs/ssr-client": "^1.1.8",
|
|
42
|
+
"lit": "^3.3.2",
|
|
43
|
+
"typescript": "^5.9.3",
|
|
44
|
+
"vitest": "^3.2.4"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc -p tsconfig.build.json",
|
|
51
|
+
"check-types": "tsc -p tsconfig.build.json --noEmit && tsc -p tsconfig.test.json --noEmit",
|
|
52
|
+
"dev": "tsc -p tsconfig.build.json --watch",
|
|
53
|
+
"format": "prettier --write . --config ../../.prettierrc.json --ignore-path ../../.prettierignore",
|
|
54
|
+
"format:check": "prettier --check . --config ../../.prettierrc.json --ignore-path ../../.prettierignore",
|
|
55
|
+
"lint": "eslint \"**/*.{js,mjs,cjs,ts,mts,cts,tsx}\" --no-error-on-unmatched-pattern",
|
|
56
|
+
"e2e": "node -e \"console.log('adapter-lit e2e noop')\"",
|
|
57
|
+
"test": "vitest run"
|
|
58
|
+
}
|
|
59
|
+
}
|