@flightdev/ui 2.0.1 → 4.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.
Files changed (120) hide show
  1. package/README.md +283 -68
  2. package/dist/{chunk-XTDK7ME5.js → chunk-S4DTUQII.js} +246 -19
  3. package/dist/chunk-S4DTUQII.js.map +1 -0
  4. package/dist/core/index.d.ts +423 -3
  5. package/dist/core/index.js +23 -2
  6. package/dist/core/index.js.map +1 -0
  7. package/dist/index.d.ts +2 -3
  8. package/dist/index.js +29 -5
  9. package/dist/index.js.map +1 -0
  10. package/package.json +7 -183
  11. package/.turbo/turbo-build.log +0 -81
  12. package/.turbo/turbo-lint.log +0 -40
  13. package/.turbo/turbo-typecheck.log +0 -4
  14. package/TESTING.md +0 -124
  15. package/dist/adapter-MMD-iHNx.d.ts +0 -424
  16. package/dist/adapters/tier-1/angular.d.ts +0 -60
  17. package/dist/adapters/tier-1/angular.js +0 -2
  18. package/dist/adapters/tier-1/index.d.ts +0 -7
  19. package/dist/adapters/tier-1/index.js +0 -7
  20. package/dist/adapters/tier-1/qwik.d.ts +0 -55
  21. package/dist/adapters/tier-1/qwik.js +0 -2
  22. package/dist/adapters/tier-1/react.d.ts +0 -67
  23. package/dist/adapters/tier-1/react.js +0 -2
  24. package/dist/adapters/tier-1/solid.d.ts +0 -45
  25. package/dist/adapters/tier-1/solid.js +0 -2
  26. package/dist/adapters/tier-1/svelte.d.ts +0 -48
  27. package/dist/adapters/tier-1/svelte.js +0 -2
  28. package/dist/adapters/tier-1/vue.d.ts +0 -47
  29. package/dist/adapters/tier-1/vue.js +0 -2
  30. package/dist/adapters/tier-2/index.d.ts +0 -7
  31. package/dist/adapters/tier-2/index.js +0 -7
  32. package/dist/adapters/tier-2/inferno.d.ts +0 -31
  33. package/dist/adapters/tier-2/inferno.js +0 -2
  34. package/dist/adapters/tier-2/lit.d.ts +0 -34
  35. package/dist/adapters/tier-2/lit.js +0 -2
  36. package/dist/adapters/tier-2/marko.d.ts +0 -59
  37. package/dist/adapters/tier-2/marko.js +0 -2
  38. package/dist/adapters/tier-2/mithril.d.ts +0 -31
  39. package/dist/adapters/tier-2/mithril.js +0 -2
  40. package/dist/adapters/tier-2/preact.d.ts +0 -33
  41. package/dist/adapters/tier-2/preact.js +0 -2
  42. package/dist/adapters/tier-2/stencil.d.ts +0 -52
  43. package/dist/adapters/tier-2/stencil.js +0 -2
  44. package/dist/adapters/tier-3/alpine.d.ts +0 -73
  45. package/dist/adapters/tier-3/alpine.js +0 -2
  46. package/dist/adapters/tier-3/hotwire.d.ts +0 -71
  47. package/dist/adapters/tier-3/hotwire.js +0 -2
  48. package/dist/adapters/tier-3/htmx.d.ts +0 -88
  49. package/dist/adapters/tier-3/htmx.js +0 -2
  50. package/dist/adapters/tier-3/index.d.ts +0 -7
  51. package/dist/adapters/tier-3/index.js +0 -7
  52. package/dist/adapters/tier-3/petite-vue.d.ts +0 -56
  53. package/dist/adapters/tier-3/petite-vue.js +0 -2
  54. package/dist/adapters/tier-3/stimulus.d.ts +0 -63
  55. package/dist/adapters/tier-3/stimulus.js +0 -2
  56. package/dist/adapters/tier-3/vanilla.d.ts +0 -63
  57. package/dist/adapters/tier-3/vanilla.js +0 -2
  58. package/dist/chunk-2SNQ6PTM.js +0 -217
  59. package/dist/chunk-3D4XMIZI.js +0 -136
  60. package/dist/chunk-3HU6GSQ4.js +0 -125
  61. package/dist/chunk-4PZDNFL7.js +0 -148
  62. package/dist/chunk-5IBLFTYL.js +0 -114
  63. package/dist/chunk-64JZJ7OK.js +0 -142
  64. package/dist/chunk-7ZJI3QU2.js +0 -132
  65. package/dist/chunk-CE4FJHQJ.js +0 -133
  66. package/dist/chunk-DTCAUBH5.js +0 -87
  67. package/dist/chunk-NTASPOHG.js +0 -106
  68. package/dist/chunk-OI2AMQLG.js +0 -152
  69. package/dist/chunk-Q7HUE44H.js +0 -106
  70. package/dist/chunk-QH3LOWXU.js +0 -155
  71. package/dist/chunk-QIVAK6BH.js +0 -103
  72. package/dist/chunk-V34XPVGK.js +0 -103
  73. package/dist/chunk-VK7ZPMO7.js +0 -221
  74. package/dist/chunk-X6CNUW6T.js +0 -136
  75. package/dist/chunk-YFGSHW5S.js +0 -121
  76. package/dist/chunk-ZAJVSE7J.js +0 -90
  77. package/docs/ADAPTERS.md +0 -946
  78. package/docs/PATTERNS.md +0 -836
  79. package/src/adapters/tier-1/angular.ts +0 -223
  80. package/src/adapters/tier-1/index.ts +0 -12
  81. package/src/adapters/tier-1/qwik.ts +0 -177
  82. package/src/adapters/tier-1/react.ts +0 -330
  83. package/src/adapters/tier-1/solid.ts +0 -222
  84. package/src/adapters/tier-1/svelte.ts +0 -211
  85. package/src/adapters/tier-1/vue.ts +0 -234
  86. package/src/adapters/tier-2/index.ts +0 -12
  87. package/src/adapters/tier-2/inferno.ts +0 -149
  88. package/src/adapters/tier-2/lit.ts +0 -191
  89. package/src/adapters/tier-2/marko.ts +0 -199
  90. package/src/adapters/tier-2/mithril.ts +0 -152
  91. package/src/adapters/tier-2/preact.ts +0 -133
  92. package/src/adapters/tier-2/stencil.ts +0 -214
  93. package/src/adapters/tier-3/alpine.ts +0 -218
  94. package/src/adapters/tier-3/hotwire.ts +0 -254
  95. package/src/adapters/tier-3/htmx.ts +0 -263
  96. package/src/adapters/tier-3/index.ts +0 -12
  97. package/src/adapters/tier-3/petite-vue.ts +0 -163
  98. package/src/adapters/tier-3/stimulus.ts +0 -233
  99. package/src/adapters/tier-3/vanilla.ts +0 -252
  100. package/src/ambient.d.ts +0 -310
  101. package/src/core/adapter.ts +0 -366
  102. package/src/core/index.ts +0 -56
  103. package/src/core/registry.ts +0 -518
  104. package/src/core/types.ts +0 -461
  105. package/src/htmx.ts +0 -134
  106. package/src/index.ts +0 -263
  107. package/test/__mocks__/stencil-core.ts +0 -19
  108. package/test/__mocks__/stencil-hydrate.ts +0 -15
  109. package/test/adapters/tier-1.test.ts +0 -206
  110. package/test/adapters/tier-2.test.ts +0 -175
  111. package/test/adapters/tier-3.test.ts +0 -284
  112. package/test/contracts/adapter.contract.ts +0 -293
  113. package/test/core/core.test.ts +0 -310
  114. package/test/errors/error-handling.test.ts +0 -454
  115. package/test/integration/htmx.integration.test.ts +0 -246
  116. package/test/integration/react.integration.test.ts +0 -271
  117. package/test/integration/registry.integration.test.ts +0 -308
  118. package/tsconfig.json +0 -22
  119. package/tsup.config.ts +0 -93
  120. package/vitest.config.ts +0 -101
@@ -1,191 +0,0 @@
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;
@@ -1,199 +0,0 @@
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;
@@ -1,152 +0,0 @@
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;
@@ -1,133 +0,0 @@
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;