@flightdev/ui 2.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/.turbo/turbo-build.log +81 -0
- package/.turbo/turbo-lint.log +40 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/LICENSE +21 -0
- package/README.md +92 -0
- package/TESTING.md +124 -0
- package/dist/adapter-MMD-iHNx.d.ts +424 -0
- package/dist/adapters/tier-1/angular.d.ts +60 -0
- package/dist/adapters/tier-1/angular.js +2 -0
- package/dist/adapters/tier-1/index.d.ts +7 -0
- package/dist/adapters/tier-1/index.js +7 -0
- package/dist/adapters/tier-1/qwik.d.ts +55 -0
- package/dist/adapters/tier-1/qwik.js +2 -0
- package/dist/adapters/tier-1/react.d.ts +67 -0
- package/dist/adapters/tier-1/react.js +2 -0
- package/dist/adapters/tier-1/solid.d.ts +45 -0
- package/dist/adapters/tier-1/solid.js +2 -0
- package/dist/adapters/tier-1/svelte.d.ts +48 -0
- package/dist/adapters/tier-1/svelte.js +2 -0
- package/dist/adapters/tier-1/vue.d.ts +47 -0
- package/dist/adapters/tier-1/vue.js +2 -0
- package/dist/adapters/tier-2/index.d.ts +7 -0
- package/dist/adapters/tier-2/index.js +7 -0
- package/dist/adapters/tier-2/inferno.d.ts +31 -0
- package/dist/adapters/tier-2/inferno.js +2 -0
- package/dist/adapters/tier-2/lit.d.ts +34 -0
- package/dist/adapters/tier-2/lit.js +2 -0
- package/dist/adapters/tier-2/marko.d.ts +59 -0
- package/dist/adapters/tier-2/marko.js +2 -0
- package/dist/adapters/tier-2/mithril.d.ts +31 -0
- package/dist/adapters/tier-2/mithril.js +2 -0
- package/dist/adapters/tier-2/preact.d.ts +33 -0
- package/dist/adapters/tier-2/preact.js +2 -0
- package/dist/adapters/tier-2/stencil.d.ts +52 -0
- package/dist/adapters/tier-2/stencil.js +2 -0
- package/dist/adapters/tier-3/alpine.d.ts +73 -0
- package/dist/adapters/tier-3/alpine.js +2 -0
- package/dist/adapters/tier-3/hotwire.d.ts +71 -0
- package/dist/adapters/tier-3/hotwire.js +2 -0
- package/dist/adapters/tier-3/htmx.d.ts +88 -0
- package/dist/adapters/tier-3/htmx.js +2 -0
- package/dist/adapters/tier-3/index.d.ts +7 -0
- package/dist/adapters/tier-3/index.js +7 -0
- package/dist/adapters/tier-3/petite-vue.d.ts +56 -0
- package/dist/adapters/tier-3/petite-vue.js +2 -0
- package/dist/adapters/tier-3/stimulus.d.ts +63 -0
- package/dist/adapters/tier-3/stimulus.js +2 -0
- package/dist/adapters/tier-3/vanilla.d.ts +63 -0
- package/dist/adapters/tier-3/vanilla.js +2 -0
- package/dist/chunk-2SNQ6PTM.js +217 -0
- package/dist/chunk-3D4XMIZI.js +136 -0
- package/dist/chunk-3HU6GSQ4.js +125 -0
- package/dist/chunk-4PZDNFL7.js +148 -0
- package/dist/chunk-5IBLFTYL.js +114 -0
- package/dist/chunk-64JZJ7OK.js +142 -0
- package/dist/chunk-7ZJI3QU2.js +132 -0
- package/dist/chunk-CE4FJHQJ.js +133 -0
- package/dist/chunk-DTCAUBH5.js +87 -0
- package/dist/chunk-NTASPOHG.js +106 -0
- package/dist/chunk-OI2AMQLG.js +152 -0
- package/dist/chunk-Q7HUE44H.js +106 -0
- package/dist/chunk-QH3LOWXU.js +155 -0
- package/dist/chunk-QIVAK6BH.js +103 -0
- package/dist/chunk-V34XPVGK.js +103 -0
- package/dist/chunk-VK7ZPMO7.js +221 -0
- package/dist/chunk-X6CNUW6T.js +136 -0
- package/dist/chunk-XTDK7ME5.js +382 -0
- package/dist/chunk-YFGSHW5S.js +121 -0
- package/dist/chunk-ZAJVSE7J.js +90 -0
- package/dist/core/index.d.ts +161 -0
- package/dist/core/index.js +2 -0
- package/dist/index.d.ts +103 -0
- package/dist/index.js +71 -0
- package/docs/ADAPTERS.md +946 -0
- package/docs/PATTERNS.md +836 -0
- package/package.json +229 -0
- package/src/adapters/tier-1/angular.ts +223 -0
- package/src/adapters/tier-1/index.ts +12 -0
- package/src/adapters/tier-1/qwik.ts +177 -0
- package/src/adapters/tier-1/react.ts +330 -0
- package/src/adapters/tier-1/solid.ts +222 -0
- package/src/adapters/tier-1/svelte.ts +211 -0
- package/src/adapters/tier-1/vue.ts +234 -0
- package/src/adapters/tier-2/index.ts +12 -0
- package/src/adapters/tier-2/inferno.ts +149 -0
- package/src/adapters/tier-2/lit.ts +191 -0
- package/src/adapters/tier-2/marko.ts +199 -0
- package/src/adapters/tier-2/mithril.ts +152 -0
- package/src/adapters/tier-2/preact.ts +133 -0
- package/src/adapters/tier-2/stencil.ts +214 -0
- package/src/adapters/tier-3/alpine.ts +218 -0
- package/src/adapters/tier-3/hotwire.ts +254 -0
- package/src/adapters/tier-3/htmx.ts +263 -0
- package/src/adapters/tier-3/index.ts +12 -0
- package/src/adapters/tier-3/petite-vue.ts +163 -0
- package/src/adapters/tier-3/stimulus.ts +233 -0
- package/src/adapters/tier-3/vanilla.ts +252 -0
- package/src/ambient.d.ts +310 -0
- package/src/core/adapter.ts +366 -0
- package/src/core/index.ts +56 -0
- package/src/core/registry.ts +518 -0
- package/src/core/types.ts +461 -0
- package/src/htmx.ts +134 -0
- package/src/index.ts +263 -0
- package/test/__mocks__/stencil-core.ts +19 -0
- package/test/__mocks__/stencil-hydrate.ts +15 -0
- package/test/adapters/tier-1.test.ts +206 -0
- package/test/adapters/tier-2.test.ts +175 -0
- package/test/adapters/tier-3.test.ts +284 -0
- package/test/contracts/adapter.contract.ts +293 -0
- package/test/core/core.test.ts +310 -0
- package/test/errors/error-handling.test.ts +454 -0
- package/test/integration/htmx.integration.test.ts +246 -0
- package/test/integration/react.integration.test.ts +271 -0
- package/test/integration/registry.integration.test.ts +308 -0
- package/tsconfig.json +22 -0
- package/tsup.config.ts +93 -0
- package/vitest.config.ts +101 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flightdev/ui - Lit Adapter (Tier 2)
|
|
3
|
+
*
|
|
4
|
+
* Lit Web Components adapter with SSR support.
|
|
5
|
+
*
|
|
6
|
+
* @module @flightdev/ui/lit
|
|
7
|
+
* @version 2.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { BaseUIAdapter } from '../../core/adapter.js';
|
|
11
|
+
import type {
|
|
12
|
+
AdapterCapabilities,
|
|
13
|
+
Component,
|
|
14
|
+
RenderContext,
|
|
15
|
+
RenderResult,
|
|
16
|
+
Island,
|
|
17
|
+
IslandOptions,
|
|
18
|
+
} from '../../core/types.js';
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Types
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
export interface LitAdapterOptions {
|
|
25
|
+
/** Enable declarative shadow DOM */
|
|
26
|
+
declarativeShadowDom?: boolean;
|
|
27
|
+
|
|
28
|
+
/** Defer hydration */
|
|
29
|
+
deferHydration?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// Lit Adapter
|
|
34
|
+
// ============================================================================
|
|
35
|
+
|
|
36
|
+
export class LitAdapter extends BaseUIAdapter {
|
|
37
|
+
readonly id = 'lit';
|
|
38
|
+
readonly name = 'Lit';
|
|
39
|
+
readonly framework = 'lit';
|
|
40
|
+
readonly frameworkVersion = '3+';
|
|
41
|
+
readonly tier = 'tier-2' as const;
|
|
42
|
+
|
|
43
|
+
override readonly capabilities: AdapterCapabilities = {
|
|
44
|
+
streaming: false,
|
|
45
|
+
partialHydration: true,
|
|
46
|
+
islands: true,
|
|
47
|
+
resumable: false,
|
|
48
|
+
ssg: true,
|
|
49
|
+
csr: true,
|
|
50
|
+
serverComponents: false,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
constructor(private options: LitAdapterOptions = {}) {
|
|
54
|
+
super();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async renderToString(
|
|
58
|
+
component: Component,
|
|
59
|
+
_context?: RenderContext
|
|
60
|
+
): Promise<RenderResult> {
|
|
61
|
+
const startTime = performance.now();
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const { render } = await import('@lit-labs/ssr');
|
|
65
|
+
const { html } = await import('lit');
|
|
66
|
+
const { collectResult } = await import('@lit-labs/ssr/lib/render-result.js');
|
|
67
|
+
|
|
68
|
+
// Component should be a Lit element tag or template
|
|
69
|
+
let template;
|
|
70
|
+
|
|
71
|
+
if (typeof component.component === 'string') {
|
|
72
|
+
// Tag name - create template
|
|
73
|
+
const tagName = component.component;
|
|
74
|
+
const props = component.props ?? {};
|
|
75
|
+
|
|
76
|
+
const attrs = Object.entries(props)
|
|
77
|
+
.map(([key, value]) => {
|
|
78
|
+
const attrName = key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
79
|
+
return `${attrName}="${this.escapeHtml(String(value))}"`;
|
|
80
|
+
})
|
|
81
|
+
.join(' ');
|
|
82
|
+
|
|
83
|
+
// Use lit html template
|
|
84
|
+
template = html`<${tagName} ${attrs}></${tagName}>`;
|
|
85
|
+
} else if (typeof component.component === 'function') {
|
|
86
|
+
// Template function
|
|
87
|
+
template = (component.component as (props: unknown) => unknown)(component.props ?? {});
|
|
88
|
+
} else {
|
|
89
|
+
template = component.component;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const ssrResult = render(template);
|
|
93
|
+
const htmlString = await collectResult(ssrResult);
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
html: htmlString,
|
|
97
|
+
hydrationData: {
|
|
98
|
+
props: component.props,
|
|
99
|
+
componentId: component.id ?? this.generateId(),
|
|
100
|
+
},
|
|
101
|
+
timing: this.createTiming(startTime),
|
|
102
|
+
};
|
|
103
|
+
} catch (error) {
|
|
104
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
105
|
+
|
|
106
|
+
if (message.includes("Cannot find module '@lit-labs/ssr'")) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
'[Flight/Lit] Lit SSR package not found.\n' +
|
|
109
|
+
'Install required packages:\n' +
|
|
110
|
+
' npm install lit @lit-labs/ssr'
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
override createIsland(
|
|
119
|
+
component: unknown,
|
|
120
|
+
props?: Record<string, unknown>,
|
|
121
|
+
options?: IslandOptions
|
|
122
|
+
): Island {
|
|
123
|
+
const tagName = component as string;
|
|
124
|
+
const id = this.generateId();
|
|
125
|
+
const deferHydration = this.options.deferHydration ?? false;
|
|
126
|
+
|
|
127
|
+
const attrs = Object.entries(props ?? {})
|
|
128
|
+
.map(([key, value]) => {
|
|
129
|
+
const attrName = key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
130
|
+
return `${attrName}="${this.escapeHtml(String(value))}"`;
|
|
131
|
+
})
|
|
132
|
+
.join(' ');
|
|
133
|
+
|
|
134
|
+
const placeholder = `
|
|
135
|
+
<${tagName}
|
|
136
|
+
${attrs}
|
|
137
|
+
data-flight-island="${id}"
|
|
138
|
+
${deferHydration ? 'defer-hydration' : ''}
|
|
139
|
+
></${tagName}>
|
|
140
|
+
`.trim();
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
id,
|
|
144
|
+
component,
|
|
145
|
+
props,
|
|
146
|
+
options: options ?? { hydrate: 'load' },
|
|
147
|
+
placeholder,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
override getHydrationScript(result: RenderResult): string {
|
|
152
|
+
const data = this.serializeProps(result.hydrationData);
|
|
153
|
+
return `
|
|
154
|
+
<script type="module">
|
|
155
|
+
window.__FLIGHT_DATA__ = ${data};
|
|
156
|
+
window.__FLIGHT_ADAPTER__ = 'lit';
|
|
157
|
+
|
|
158
|
+
// Lit components hydrate when their definition is loaded
|
|
159
|
+
// If using defer-hydration, call removeAttribute('defer-hydration')
|
|
160
|
+
document.querySelectorAll('[defer-hydration]').forEach(el => {
|
|
161
|
+
el.removeAttribute('defer-hydration');
|
|
162
|
+
});
|
|
163
|
+
</script>
|
|
164
|
+
`.trim();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
override getClientEntry(): string {
|
|
168
|
+
return `
|
|
169
|
+
// Lit Client Entry
|
|
170
|
+
// Lit components self-hydrate when their definitions are loaded
|
|
171
|
+
|
|
172
|
+
export function hydrate() {
|
|
173
|
+
// Remove defer-hydration attributes to trigger hydration
|
|
174
|
+
document.querySelectorAll('[defer-hydration]').forEach(el => {
|
|
175
|
+
el.removeAttribute('defer-hydration');
|
|
176
|
+
});
|
|
177
|
+
console.log('[Flight/Lit] Components hydrated');
|
|
178
|
+
}
|
|
179
|
+
`.trim();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ============================================================================
|
|
184
|
+
// Factory Function
|
|
185
|
+
// ============================================================================
|
|
186
|
+
|
|
187
|
+
export function lit(options?: LitAdapterOptions): LitAdapter {
|
|
188
|
+
return new LitAdapter(options);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export default lit;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flightdev/ui - Marko Adapter (Tier 2)
|
|
3
|
+
*
|
|
4
|
+
* Marko adapter with streaming SSR - pioneered by eBay.
|
|
5
|
+
*
|
|
6
|
+
* @module @flightdev/ui/marko
|
|
7
|
+
* @version 2.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { BaseUIAdapter } from '../../core/adapter.js';
|
|
11
|
+
import type {
|
|
12
|
+
AdapterCapabilities,
|
|
13
|
+
Component,
|
|
14
|
+
RenderContext,
|
|
15
|
+
RenderResult,
|
|
16
|
+
StreamingOptions,
|
|
17
|
+
StreamingRenderResult,
|
|
18
|
+
} from '../../core/types.js';
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Types
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Marko template interface
|
|
26
|
+
*/
|
|
27
|
+
export interface MarkoTemplate {
|
|
28
|
+
render(input: Record<string, unknown>): MarkoRenderResult;
|
|
29
|
+
renderToString(input: Record<string, unknown>): Promise<string>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface MarkoRenderResult {
|
|
33
|
+
toString(): Promise<string>;
|
|
34
|
+
pipe(stream: NodeJS.WritableStream): void;
|
|
35
|
+
on(event: 'data', callback: (chunk: Uint8Array) => void): void;
|
|
36
|
+
on(event: 'end', callback: () => void): void;
|
|
37
|
+
on(event: 'error', callback: (error: Error) => void): void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface MarkoAdapterOptions {
|
|
41
|
+
/** Enable streaming SSR */
|
|
42
|
+
streaming?: boolean;
|
|
43
|
+
|
|
44
|
+
/** Global template data */
|
|
45
|
+
globalData?: Record<string, unknown>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// Marko Adapter
|
|
50
|
+
// ============================================================================
|
|
51
|
+
|
|
52
|
+
export class MarkoAdapter extends BaseUIAdapter {
|
|
53
|
+
readonly id = 'marko';
|
|
54
|
+
readonly name = 'Marko';
|
|
55
|
+
readonly framework = 'marko';
|
|
56
|
+
readonly frameworkVersion = '5+';
|
|
57
|
+
readonly tier = 'tier-2' as const;
|
|
58
|
+
|
|
59
|
+
override readonly capabilities: AdapterCapabilities = {
|
|
60
|
+
streaming: true, // Marko pioneered streaming SSR!
|
|
61
|
+
partialHydration: true,
|
|
62
|
+
islands: true,
|
|
63
|
+
resumable: false,
|
|
64
|
+
ssg: true,
|
|
65
|
+
csr: true,
|
|
66
|
+
serverComponents: false,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
constructor(private options: MarkoAdapterOptions = {}) {
|
|
70
|
+
super();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async renderToString(
|
|
74
|
+
component: Component,
|
|
75
|
+
_context?: RenderContext
|
|
76
|
+
): Promise<RenderResult> {
|
|
77
|
+
const startTime = performance.now();
|
|
78
|
+
|
|
79
|
+
const template = component.component as MarkoTemplate;
|
|
80
|
+
const input = {
|
|
81
|
+
...this.options.globalData,
|
|
82
|
+
...component.props,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const html = await template.renderToString(input);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
html,
|
|
89
|
+
hydrationData: {
|
|
90
|
+
props: component.props,
|
|
91
|
+
componentId: component.id ?? this.generateId(),
|
|
92
|
+
},
|
|
93
|
+
timing: this.createTiming(startTime),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
renderToStream(
|
|
98
|
+
component: Component,
|
|
99
|
+
_context?: RenderContext,
|
|
100
|
+
options?: StreamingOptions
|
|
101
|
+
): StreamingRenderResult {
|
|
102
|
+
let aborted = false;
|
|
103
|
+
let resolvePromise: () => void;
|
|
104
|
+
let rejectPromise: (error: Error) => void;
|
|
105
|
+
|
|
106
|
+
const done = new Promise<void>((resolve, reject) => {
|
|
107
|
+
resolvePromise = resolve;
|
|
108
|
+
rejectPromise = reject;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const template = component.component as MarkoTemplate;
|
|
112
|
+
const input = {
|
|
113
|
+
...this.options.globalData,
|
|
114
|
+
...component.props,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const stream = new ReadableStream<Uint8Array>({
|
|
118
|
+
start(controller) {
|
|
119
|
+
const out = template.render(input);
|
|
120
|
+
|
|
121
|
+
options?.onShellReady?.();
|
|
122
|
+
|
|
123
|
+
out.on('data', (chunk: Uint8Array) => {
|
|
124
|
+
if (!aborted) {
|
|
125
|
+
controller.enqueue(new Uint8Array(chunk));
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
out.on('end', () => {
|
|
130
|
+
if (!aborted) {
|
|
131
|
+
controller.close();
|
|
132
|
+
resolvePromise();
|
|
133
|
+
options?.onAllReady?.();
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
out.on('error', (error: Error) => {
|
|
138
|
+
options?.onError?.(error);
|
|
139
|
+
controller.error(error);
|
|
140
|
+
rejectPromise(error);
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
stream,
|
|
147
|
+
done,
|
|
148
|
+
abort() {
|
|
149
|
+
aborted = true;
|
|
150
|
+
resolvePromise();
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
override getHydrationScript(result: RenderResult): string {
|
|
156
|
+
const data = this.serializeProps(result.hydrationData);
|
|
157
|
+
return `
|
|
158
|
+
<script type="module">
|
|
159
|
+
window.__FLIGHT_DATA__ = ${data};
|
|
160
|
+
window.__FLIGHT_ADAPTER__ = 'marko';
|
|
161
|
+
// Marko handles hydration via inline component scripts
|
|
162
|
+
</script>
|
|
163
|
+
`.trim();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
override getClientEntry(): string {
|
|
167
|
+
return `
|
|
168
|
+
// Marko Client Entry
|
|
169
|
+
// Marko components are self-hydrating
|
|
170
|
+
|
|
171
|
+
export function hydrate() {
|
|
172
|
+
// Marko components automatically hydrate themselves
|
|
173
|
+
// No additional client-side setup needed
|
|
174
|
+
console.log('[Flight/Marko] Components are self-hydrating');
|
|
175
|
+
}
|
|
176
|
+
`.trim();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ============================================================================
|
|
181
|
+
// Factory Function
|
|
182
|
+
// ============================================================================
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Create a Marko UI adapter.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* import { marko } from '@flightdev/ui/marko';
|
|
190
|
+
* import { defineUI } from '@flightdev/ui';
|
|
191
|
+
*
|
|
192
|
+
* export default defineUI(marko({ streaming: true }));
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
export function marko(options?: MarkoAdapterOptions): MarkoAdapter {
|
|
196
|
+
return new MarkoAdapter(options);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export default marko;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flightdev/ui - Mithril Adapter (Tier 2)
|
|
3
|
+
*
|
|
4
|
+
* Mithril.js adapter - ultra-lightweight (9kb) SPA framework.
|
|
5
|
+
*
|
|
6
|
+
* @module @flightdev/ui/mithril
|
|
7
|
+
* @version 2.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { BaseUIAdapter } from '../../core/adapter.js';
|
|
11
|
+
import type {
|
|
12
|
+
AdapterCapabilities,
|
|
13
|
+
Component,
|
|
14
|
+
RenderContext,
|
|
15
|
+
RenderResult,
|
|
16
|
+
} from '../../core/types.js';
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Types
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
export interface MithrilAdapterOptions {
|
|
23
|
+
/** Enable strict mode */
|
|
24
|
+
strict?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Mithril Adapter
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
export class MithrilAdapter extends BaseUIAdapter {
|
|
32
|
+
readonly id = 'mithril';
|
|
33
|
+
readonly name = 'Mithril';
|
|
34
|
+
readonly framework = 'mithril';
|
|
35
|
+
readonly frameworkVersion = '2+';
|
|
36
|
+
readonly tier = 'tier-2' as const;
|
|
37
|
+
|
|
38
|
+
override readonly capabilities: AdapterCapabilities = {
|
|
39
|
+
streaming: false,
|
|
40
|
+
partialHydration: false,
|
|
41
|
+
islands: false,
|
|
42
|
+
resumable: false,
|
|
43
|
+
ssg: true,
|
|
44
|
+
csr: true,
|
|
45
|
+
serverComponents: false,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
constructor(private options: MithrilAdapterOptions = {}) {
|
|
49
|
+
super();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async renderToString(
|
|
53
|
+
component: Component,
|
|
54
|
+
_context?: RenderContext
|
|
55
|
+
): Promise<RenderResult> {
|
|
56
|
+
const startTime = performance.now();
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
// mithril-node-render for SSR
|
|
60
|
+
const render = await import('mithril-node-render')
|
|
61
|
+
.then((m) => m.default || m);
|
|
62
|
+
const m = await import('mithril').then((mod) => mod.default || mod);
|
|
63
|
+
|
|
64
|
+
const MithrilComponent = component.component as mithril.Component;
|
|
65
|
+
const vnode = m(MithrilComponent, component.props ?? {});
|
|
66
|
+
|
|
67
|
+
const html = await render(vnode);
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
html,
|
|
71
|
+
hydrationData: {
|
|
72
|
+
props: component.props,
|
|
73
|
+
componentId: component.id ?? this.generateId(),
|
|
74
|
+
},
|
|
75
|
+
timing: this.createTiming(startTime),
|
|
76
|
+
};
|
|
77
|
+
} catch (error) {
|
|
78
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
79
|
+
|
|
80
|
+
if (message.includes("Cannot find module 'mithril-node-render'")) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
'[Flight/Mithril] Mithril SSR package not found.\n' +
|
|
83
|
+
'Install required packages:\n' +
|
|
84
|
+
' npm install mithril mithril-node-render\n' +
|
|
85
|
+
' npm install -D @types/mithril'
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
override getHydrationScript(result: RenderResult): string {
|
|
94
|
+
const data = this.serializeProps(result.hydrationData);
|
|
95
|
+
return `
|
|
96
|
+
<script type="module">
|
|
97
|
+
window.__FLIGHT_DATA__ = ${data};
|
|
98
|
+
window.__FLIGHT_ADAPTER__ = 'mithril';
|
|
99
|
+
|
|
100
|
+
import m from 'mithril';
|
|
101
|
+
|
|
102
|
+
const App = window.__FLIGHT_APP__;
|
|
103
|
+
const container = document.getElementById('app');
|
|
104
|
+
|
|
105
|
+
if (App && container) {
|
|
106
|
+
// Mithril mounts and diffs against existing DOM
|
|
107
|
+
m.mount(container, {
|
|
108
|
+
view: () => m(App, window.__FLIGHT_DATA__.props)
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
</script>
|
|
112
|
+
`.trim();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
override getClientEntry(): string {
|
|
116
|
+
return `
|
|
117
|
+
import m from 'mithril';
|
|
118
|
+
|
|
119
|
+
export function hydrate() {
|
|
120
|
+
const container = document.getElementById('app');
|
|
121
|
+
const App = window.__FLIGHT_APP__;
|
|
122
|
+
const data = window.__FLIGHT_DATA__ ?? {};
|
|
123
|
+
|
|
124
|
+
if (!container || !App) {
|
|
125
|
+
console.warn('[Flight/Mithril] Missing container or App');
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Mithril automatically diffs against existing DOM
|
|
130
|
+
m.mount(container, {
|
|
131
|
+
view: () => m(App, data.props)
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function render(App, container, props = {}) {
|
|
136
|
+
m.mount(container, {
|
|
137
|
+
view: () => m(App, props)
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
`.trim();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ============================================================================
|
|
145
|
+
// Factory Function
|
|
146
|
+
// ============================================================================
|
|
147
|
+
|
|
148
|
+
export function mithril(options?: MithrilAdapterOptions): MithrilAdapter {
|
|
149
|
+
return new MithrilAdapter(options);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export default mithril;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flightdev/ui - Preact Adapter (Tier 2)
|
|
3
|
+
*
|
|
4
|
+
* Preact adapter - lightweight React alternative (3kb).
|
|
5
|
+
*
|
|
6
|
+
* @module @flightdev/ui/preact
|
|
7
|
+
* @version 2.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { BaseUIAdapter } from '../../core/adapter.js';
|
|
11
|
+
import type {
|
|
12
|
+
AdapterCapabilities,
|
|
13
|
+
Component,
|
|
14
|
+
RenderContext,
|
|
15
|
+
RenderResult,
|
|
16
|
+
} from '../../core/types.js';
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Types
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
export interface PreactAdapterOptions {
|
|
23
|
+
/** Enable preact/compat for React compatibility */
|
|
24
|
+
compat?: boolean;
|
|
25
|
+
|
|
26
|
+
/** Pretty print HTML */
|
|
27
|
+
pretty?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Preact Adapter
|
|
32
|
+
// ============================================================================
|
|
33
|
+
|
|
34
|
+
export class PreactAdapter extends BaseUIAdapter {
|
|
35
|
+
readonly id = 'preact';
|
|
36
|
+
readonly name = 'Preact';
|
|
37
|
+
readonly framework = 'preact';
|
|
38
|
+
readonly frameworkVersion = '10+';
|
|
39
|
+
readonly tier = 'tier-2' as const;
|
|
40
|
+
|
|
41
|
+
override readonly capabilities: AdapterCapabilities = {
|
|
42
|
+
streaming: false,
|
|
43
|
+
partialHydration: true,
|
|
44
|
+
islands: true,
|
|
45
|
+
resumable: false,
|
|
46
|
+
ssg: true,
|
|
47
|
+
csr: true,
|
|
48
|
+
serverComponents: false,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
constructor(private options: PreactAdapterOptions = {}) {
|
|
52
|
+
super();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async renderToString(
|
|
56
|
+
component: Component,
|
|
57
|
+
_context?: RenderContext
|
|
58
|
+
): Promise<RenderResult> {
|
|
59
|
+
const startTime = performance.now();
|
|
60
|
+
|
|
61
|
+
const renderToString = await import('preact-render-to-string')
|
|
62
|
+
.then((m) => m.default || m.renderToString);
|
|
63
|
+
const { h } = await import('preact');
|
|
64
|
+
|
|
65
|
+
const PreactComponent = component.component as preact.FunctionComponent;
|
|
66
|
+
const element = h(PreactComponent, component.props ?? {});
|
|
67
|
+
|
|
68
|
+
const html = renderToString(element);
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
html,
|
|
72
|
+
hydrationData: {
|
|
73
|
+
props: component.props,
|
|
74
|
+
componentId: component.id ?? this.generateId(),
|
|
75
|
+
},
|
|
76
|
+
timing: this.createTiming(startTime),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
override getHydrationScript(result: RenderResult): string {
|
|
81
|
+
const data = this.serializeProps(result.hydrationData);
|
|
82
|
+
return `
|
|
83
|
+
<script type="module">
|
|
84
|
+
window.__FLIGHT_DATA__ = ${data};
|
|
85
|
+
window.__FLIGHT_ADAPTER__ = 'preact';
|
|
86
|
+
|
|
87
|
+
import { h, hydrate } from 'preact';
|
|
88
|
+
|
|
89
|
+
const App = window.__FLIGHT_APP__;
|
|
90
|
+
const container = document.getElementById('app');
|
|
91
|
+
|
|
92
|
+
if (App && container) {
|
|
93
|
+
hydrate(h(App, window.__FLIGHT_DATA__.props), container);
|
|
94
|
+
}
|
|
95
|
+
</script>
|
|
96
|
+
`.trim();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
override getClientEntry(): string {
|
|
100
|
+
return `
|
|
101
|
+
import { h, hydrate, render } from 'preact';
|
|
102
|
+
|
|
103
|
+
export function hydrateApp() {
|
|
104
|
+
const container = document.getElementById('app');
|
|
105
|
+
const App = window.__FLIGHT_APP__;
|
|
106
|
+
const data = window.__FLIGHT_DATA__ ?? {};
|
|
107
|
+
|
|
108
|
+
if (!container || !App) {
|
|
109
|
+
console.warn('[Flight/Preact] Missing container or App');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (container.innerHTML.trim()) {
|
|
114
|
+
hydrate(h(App, data.props), container);
|
|
115
|
+
} else {
|
|
116
|
+
render(h(App, data.props), container);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export { hydrateApp as hydrate };
|
|
121
|
+
`.trim();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ============================================================================
|
|
126
|
+
// Factory Function
|
|
127
|
+
// ============================================================================
|
|
128
|
+
|
|
129
|
+
export function preact(options?: PreactAdapterOptions): PreactAdapter {
|
|
130
|
+
return new PreactAdapter(options);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default preact;
|