@useavalon/avalon 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -54
- package/dist/mod.js +1 -0
- package/dist/src/build/integration-bundler-plugin.js +1 -0
- package/dist/src/build/integration-config.js +1 -0
- package/dist/src/build/integration-detection-plugin.js +1 -0
- package/dist/src/build/integration-resolver-plugin.js +1 -0
- package/dist/src/build/island-manifest.js +1 -0
- package/dist/src/build/island-types-generator.js +5 -0
- package/dist/src/build/mdx-island-transform.js +2 -0
- package/dist/src/build/mdx-plugin.js +1 -0
- package/dist/src/build/page-island-transform.js +3 -0
- package/dist/src/build/prop-extractors/index.js +1 -0
- package/dist/src/build/prop-extractors/lit.js +1 -0
- package/dist/src/build/prop-extractors/qwik.js +1 -0
- package/dist/src/build/prop-extractors/solid.js +1 -0
- package/dist/src/build/prop-extractors/svelte.js +1 -0
- package/dist/src/build/prop-extractors/vue.js +1 -0
- package/dist/src/build/sidecar-file-manager.js +1 -0
- package/dist/src/build/sidecar-renderer.js +6 -0
- package/dist/src/client/adapters/index.js +1 -0
- package/dist/src/client/components.js +1 -0
- package/dist/src/client/css-hmr-handler.js +1 -0
- package/dist/src/client/framework-adapter.js +13 -0
- package/dist/src/client/hmr-coordinator.js +1 -0
- package/dist/src/client/hmr-error-overlay.js +214 -0
- package/dist/src/client/main.js +39 -0
- package/{src → dist/src}/client/types/framework-runtime.d.ts +68 -68
- package/{src → dist/src}/client/types/vite-hmr.d.ts +46 -46
- package/dist/src/client/types/vite-virtual-modules.d.ts +70 -0
- package/dist/src/components/Image.js +1 -0
- package/dist/src/components/IslandErrorBoundary.js +1 -0
- package/dist/src/components/LayoutDataErrorBoundary.js +1 -0
- package/dist/src/components/LayoutErrorBoundary.js +1 -0
- package/dist/src/components/PersistentIsland.js +1 -0
- package/dist/src/components/StreamingErrorBoundary.js +1 -0
- package/dist/src/components/StreamingLayout.js +29 -0
- package/dist/src/core/components/component-analyzer.js +1 -0
- package/dist/src/core/components/component-detection.js +5 -0
- package/dist/src/core/components/enhanced-framework-detector.js +1 -0
- package/dist/src/core/components/framework-registry.js +1 -0
- package/dist/src/core/content/mdx-processor.js +1 -0
- package/dist/src/core/integrations/index.js +1 -0
- package/dist/src/core/integrations/loader.js +1 -0
- package/dist/src/core/integrations/registry.js +1 -0
- package/dist/src/core/islands/island-persistence.js +1 -0
- package/dist/src/core/islands/island-state-serializer.js +1 -0
- package/dist/src/core/islands/persistent-island-context.js +1 -0
- package/dist/src/core/islands/use-persistent-state.js +1 -0
- package/dist/src/core/layout/enhanced-layout-resolver.js +1 -0
- package/dist/src/core/layout/layout-cache-manager.js +1 -0
- package/dist/src/core/layout/layout-composer.js +1 -0
- package/dist/src/core/layout/layout-data-loader.js +1 -0
- package/dist/src/core/layout/layout-discovery.js +1 -0
- package/dist/src/core/layout/layout-matcher.js +1 -0
- package/dist/src/core/layout/layout-types.js +1 -0
- package/dist/src/core/modules/framework-module-resolver.js +1 -0
- package/dist/src/islands/component-analysis.js +1 -0
- package/dist/src/islands/css-utils.js +17 -0
- package/dist/src/islands/discovery/index.js +1 -0
- package/dist/src/islands/discovery/registry.js +1 -0
- package/dist/src/islands/discovery/resolver.js +2 -0
- package/dist/src/islands/discovery/scanner.js +1 -0
- package/dist/src/islands/discovery/types.js +1 -0
- package/dist/src/islands/discovery/validator.js +18 -0
- package/dist/src/islands/discovery/watcher.js +1 -0
- package/dist/src/islands/framework-detection.js +1 -0
- package/dist/src/islands/integration-loader.js +1 -0
- package/dist/src/islands/island.js +1 -0
- package/dist/src/islands/render-cache.js +1 -0
- package/dist/src/islands/types.js +1 -0
- package/dist/src/islands/universal-css-collector.js +5 -0
- package/dist/src/islands/universal-head-collector.js +2 -0
- package/{src → dist/src}/layout-system.d.ts +592 -592
- package/dist/src/layout-system.js +1 -0
- package/dist/src/middleware/discovery.js +1 -0
- package/dist/src/middleware/executor.js +1 -0
- package/dist/src/middleware/index.js +1 -0
- package/dist/src/middleware/types.js +1 -0
- package/dist/src/nitro/build-config.js +1 -0
- package/dist/src/nitro/config.js +1 -0
- package/dist/src/nitro/error-handler.js +198 -0
- package/dist/src/nitro/index.js +1 -0
- package/dist/src/nitro/island-manifest.js +2 -0
- package/dist/src/nitro/middleware-adapter.js +1 -0
- package/dist/src/nitro/renderer.js +183 -0
- package/dist/src/nitro/route-discovery.js +1 -0
- package/dist/src/nitro/types.js +1 -0
- package/dist/src/render/collect-css.js +3 -0
- package/{src/render/error-pages.ts → dist/src/render/error-pages.js} +7 -38
- package/dist/src/render/isolated-ssr-renderer.js +1 -0
- package/dist/src/render/ssr.js +90 -0
- package/dist/src/schemas/api.js +1 -0
- package/dist/src/schemas/core.js +1 -0
- package/dist/src/schemas/index.js +1 -0
- package/dist/src/schemas/layout.js +1 -0
- package/dist/src/schemas/routing/index.js +1 -0
- package/dist/src/schemas/routing.js +1 -0
- package/dist/src/types/as-island.js +1 -0
- package/{src → dist/src}/types/image.d.ts +106 -106
- package/{src → dist/src}/types/index.d.ts +22 -22
- package/{src → dist/src}/types/island-jsx.d.ts +33 -33
- package/{src → dist/src}/types/island-prop.d.ts +20 -20
- package/dist/src/types/layout.js +1 -0
- package/{src → dist/src}/types/mdx.d.ts +6 -6
- package/dist/src/types/routing.js +1 -0
- package/dist/src/types/types.js +1 -0
- package/{src → dist/src}/types/urlpattern.d.ts +49 -49
- package/{src → dist/src}/types/vite-env.d.ts +11 -11
- package/dist/src/utils/dev-logger.js +12 -0
- package/dist/src/utils/fs.js +1 -0
- package/dist/src/vite-plugin/auto-discover.js +1 -0
- package/dist/src/vite-plugin/config.js +1 -0
- package/dist/src/vite-plugin/errors.js +1 -0
- package/dist/src/vite-plugin/image-optimization.js +45 -0
- package/dist/src/vite-plugin/integration-activator.js +1 -0
- package/dist/src/vite-plugin/island-sidecar-plugin.js +1 -0
- package/dist/src/vite-plugin/module-discovery.js +1 -0
- package/dist/src/vite-plugin/nitro-integration.js +42 -0
- package/dist/src/vite-plugin/plugin.js +1 -0
- package/dist/src/vite-plugin/types.js +1 -0
- package/dist/src/vite-plugin/validation.js +2 -0
- package/package.json +57 -26
- package/mod.ts +0 -302
- package/src/build/integration-bundler-plugin.ts +0 -116
- package/src/build/integration-config.ts +0 -168
- package/src/build/integration-detection-plugin.ts +0 -117
- package/src/build/integration-resolver-plugin.ts +0 -90
- package/src/build/island-manifest.ts +0 -269
- package/src/build/island-types-generator.ts +0 -476
- package/src/build/mdx-island-transform.ts +0 -464
- package/src/build/mdx-plugin.ts +0 -98
- package/src/build/page-island-transform.ts +0 -598
- package/src/build/prop-extractors/index.ts +0 -21
- package/src/build/prop-extractors/lit.ts +0 -140
- package/src/build/prop-extractors/qwik.ts +0 -16
- package/src/build/prop-extractors/solid.ts +0 -125
- package/src/build/prop-extractors/svelte.ts +0 -194
- package/src/build/prop-extractors/vue.ts +0 -111
- package/src/build/sidecar-file-manager.ts +0 -104
- package/src/build/sidecar-renderer.ts +0 -30
- package/src/client/adapters/index.js +0 -12
- package/src/client/adapters/index.ts +0 -13
- package/src/client/adapters/lit-adapter.js +0 -467
- package/src/client/adapters/lit-adapter.ts +0 -654
- package/src/client/adapters/preact-adapter.js +0 -223
- package/src/client/adapters/preact-adapter.ts +0 -331
- package/src/client/adapters/qwik-adapter.js +0 -259
- package/src/client/adapters/qwik-adapter.ts +0 -345
- package/src/client/adapters/react-adapter.js +0 -220
- package/src/client/adapters/react-adapter.ts +0 -353
- package/src/client/adapters/solid-adapter.js +0 -295
- package/src/client/adapters/solid-adapter.ts +0 -451
- package/src/client/adapters/svelte-adapter.js +0 -368
- package/src/client/adapters/svelte-adapter.ts +0 -524
- package/src/client/adapters/vue-adapter.js +0 -278
- package/src/client/adapters/vue-adapter.ts +0 -467
- package/src/client/components.js +0 -23
- package/src/client/components.ts +0 -35
- package/src/client/css-hmr-handler.js +0 -263
- package/src/client/css-hmr-handler.ts +0 -344
- package/src/client/framework-adapter.js +0 -283
- package/src/client/framework-adapter.ts +0 -462
- package/src/client/hmr-coordinator.js +0 -274
- package/src/client/hmr-coordinator.ts +0 -396
- package/src/client/hmr-error-overlay.js +0 -533
- package/src/client/main.js +0 -816
- package/src/client/types/vite-virtual-modules.d.ts +0 -60
- package/src/components/Image.tsx +0 -123
- package/src/components/IslandErrorBoundary.tsx +0 -145
- package/src/components/LayoutDataErrorBoundary.tsx +0 -141
- package/src/components/LayoutErrorBoundary.tsx +0 -127
- package/src/components/PersistentIsland.tsx +0 -52
- package/src/components/StreamingErrorBoundary.tsx +0 -233
- package/src/components/StreamingLayout.tsx +0 -538
- package/src/core/components/component-analyzer.ts +0 -192
- package/src/core/components/component-detection.ts +0 -508
- package/src/core/components/enhanced-framework-detector.ts +0 -500
- package/src/core/components/framework-registry.ts +0 -563
- package/src/core/content/mdx-processor.ts +0 -46
- package/src/core/integrations/index.ts +0 -19
- package/src/core/integrations/loader.ts +0 -125
- package/src/core/integrations/registry.ts +0 -175
- package/src/core/islands/island-persistence.ts +0 -325
- package/src/core/islands/island-state-serializer.ts +0 -258
- package/src/core/islands/persistent-island-context.tsx +0 -80
- package/src/core/islands/use-persistent-state.ts +0 -68
- package/src/core/layout/enhanced-layout-resolver.ts +0 -322
- package/src/core/layout/layout-cache-manager.ts +0 -485
- package/src/core/layout/layout-composer.ts +0 -357
- package/src/core/layout/layout-data-loader.ts +0 -516
- package/src/core/layout/layout-discovery.ts +0 -243
- package/src/core/layout/layout-matcher.ts +0 -299
- package/src/core/layout/layout-types.ts +0 -110
- package/src/core/modules/framework-module-resolver.ts +0 -273
- package/src/islands/component-analysis.ts +0 -213
- package/src/islands/css-utils.ts +0 -565
- package/src/islands/discovery/index.ts +0 -80
- package/src/islands/discovery/registry.ts +0 -340
- package/src/islands/discovery/resolver.ts +0 -477
- package/src/islands/discovery/scanner.ts +0 -386
- package/src/islands/discovery/types.ts +0 -117
- package/src/islands/discovery/validator.ts +0 -544
- package/src/islands/discovery/watcher.ts +0 -368
- package/src/islands/framework-detection.ts +0 -428
- package/src/islands/integration-loader.ts +0 -490
- package/src/islands/island.tsx +0 -565
- package/src/islands/render-cache.ts +0 -550
- package/src/islands/types.ts +0 -80
- package/src/islands/universal-css-collector.ts +0 -157
- package/src/islands/universal-head-collector.ts +0 -137
- package/src/layout-system.ts +0 -218
- package/src/middleware/discovery.ts +0 -268
- package/src/middleware/executor.ts +0 -315
- package/src/middleware/index.ts +0 -76
- package/src/middleware/types.ts +0 -99
- package/src/nitro/build-config.ts +0 -576
- package/src/nitro/config.ts +0 -483
- package/src/nitro/error-handler.ts +0 -636
- package/src/nitro/index.ts +0 -173
- package/src/nitro/island-manifest.ts +0 -584
- package/src/nitro/middleware-adapter.ts +0 -260
- package/src/nitro/renderer.ts +0 -1471
- package/src/nitro/route-discovery.ts +0 -439
- package/src/nitro/types.ts +0 -321
- package/src/render/collect-css.ts +0 -198
- package/src/render/isolated-ssr-renderer.ts +0 -654
- package/src/render/ssr.ts +0 -1030
- package/src/schemas/api.ts +0 -30
- package/src/schemas/core.ts +0 -64
- package/src/schemas/index.ts +0 -212
- package/src/schemas/layout.ts +0 -279
- package/src/schemas/routing/index.ts +0 -38
- package/src/schemas/routing.ts +0 -376
- package/src/types/as-island.ts +0 -20
- package/src/types/layout.ts +0 -285
- package/src/types/routing.ts +0 -555
- package/src/types/types.ts +0 -5
- package/src/utils/dev-logger.ts +0 -299
- package/src/utils/fs.ts +0 -151
- package/src/vite-plugin/auto-discover.ts +0 -551
- package/src/vite-plugin/config.ts +0 -266
- package/src/vite-plugin/errors.ts +0 -127
- package/src/vite-plugin/image-optimization.ts +0 -156
- package/src/vite-plugin/integration-activator.ts +0 -126
- package/src/vite-plugin/island-sidecar-plugin.ts +0 -176
- package/src/vite-plugin/module-discovery.ts +0 -189
- package/src/vite-plugin/nitro-integration.ts +0 -1354
- package/src/vite-plugin/plugin.ts +0 -401
- package/src/vite-plugin/types.ts +0 -327
- package/src/vite-plugin/validation.ts +0 -228
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Qwik HMR Adapter
|
|
3
|
-
*
|
|
4
|
-
* Provides Hot Module Replacement support for Qwik components.
|
|
5
|
-
* Qwik is fundamentally different from other frameworks — it uses resumability
|
|
6
|
-
* instead of hydration. Components are serialized on the server and resumed
|
|
7
|
-
* on the client without replaying application logic.
|
|
8
|
-
*
|
|
9
|
-
* Key Qwik concepts:
|
|
10
|
-
* - Resumability: No hydration step — the app resumes from serialized state
|
|
11
|
-
* - Lazy loading via $: Code is split at $ boundaries and loaded on demand
|
|
12
|
-
* - Containers: Qwik apps run inside container elements with q:container attribute
|
|
13
|
-
* - Qwikloader: A tiny (~1KB) script that sets up global event listeners
|
|
14
|
-
*
|
|
15
|
-
* Requirements: 2.1
|
|
16
|
-
*/
|
|
17
|
-
/// <reference lib="dom" />
|
|
18
|
-
import { BaseFrameworkAdapter } from "../framework-adapter.js";
|
|
19
|
-
/**
|
|
20
|
-
* Qwik HMR Adapter
|
|
21
|
-
*
|
|
22
|
-
* Unlike other frameworks, Qwik doesn't hydrate — it resumes. This adapter
|
|
23
|
-
* handles HMR by re-rendering the container rather than performing traditional
|
|
24
|
-
* hydration-based hot updates.
|
|
25
|
-
*
|
|
26
|
-
* During development, Qwik's optimizer (via vite-plugin-qwik) handles most
|
|
27
|
-
* of the HMR heavy lifting. This adapter provides the island-level integration
|
|
28
|
-
* for Avalon's HMR coordinator.
|
|
29
|
-
*/
|
|
30
|
-
export class QwikHMRAdapter extends BaseFrameworkAdapter {
|
|
31
|
-
name = "qwik";
|
|
32
|
-
/**
|
|
33
|
-
* Track container elements for cleanup
|
|
34
|
-
*/
|
|
35
|
-
containers = new WeakMap();
|
|
36
|
-
/**
|
|
37
|
-
* Check if a component is a Qwik component
|
|
38
|
-
*
|
|
39
|
-
* Qwik components are created with component$() and have distinctive markers:
|
|
40
|
-
* - __brand or __qrl property from the Qwik optimizer
|
|
41
|
-
* - $ suffix convention in source
|
|
42
|
-
* - QRL (Qwik Resource Locator) references
|
|
43
|
-
*/
|
|
44
|
-
canHandle(component) {
|
|
45
|
-
if (!component) return false;
|
|
46
|
-
// Check if it's a function (Qwik components are functions)
|
|
47
|
-
if (typeof component === "function") {
|
|
48
|
-
const comp = component;
|
|
49
|
-
// Check for Qwik-specific markers set by the optimizer
|
|
50
|
-
if (comp.__brand === "QwikComponent" || comp.__qrl) {
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
// Check for QRL wrapper pattern
|
|
54
|
-
if (comp.getSymbol || comp.getHash) {
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
try {
|
|
58
|
-
const funcStr = component.toString();
|
|
59
|
-
// Look for Qwik-specific compiled patterns
|
|
60
|
-
if (funcStr.includes("component$") || funcStr.includes("qrl") || funcStr.includes("useSignal") || funcStr.includes("useStore") || funcStr.includes("useTask$") || funcStr.includes("useVisibleTask$") || funcStr.includes("_qrl") || funcStr.includes("qwik")) {
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
} catch {}
|
|
64
|
-
}
|
|
65
|
-
// Check if it's a Qwik component object (wrapped or exported)
|
|
66
|
-
if (typeof component === "object" && component !== null) {
|
|
67
|
-
const obj = component;
|
|
68
|
-
// Check for default export pattern
|
|
69
|
-
if (obj.default && typeof obj.default === "function") {
|
|
70
|
-
return this.canHandle(obj.default);
|
|
71
|
-
}
|
|
72
|
-
// Check for Qwik QRL markers
|
|
73
|
-
if (obj.__qrl || obj.__brand === "QwikComponent") {
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Preserve Qwik component state before HMR update
|
|
81
|
-
*
|
|
82
|
-
* Qwik serializes state into the DOM via q:container attributes and
|
|
83
|
-
* inline <script type="qwik/json"> blocks. We capture this serialized
|
|
84
|
-
* state so it can be used during re-rendering.
|
|
85
|
-
*/
|
|
86
|
-
preserveState(island) {
|
|
87
|
-
try {
|
|
88
|
-
const baseSnapshot = super.preserveState(island);
|
|
89
|
-
if (!baseSnapshot) return null;
|
|
90
|
-
// Get props from the island
|
|
91
|
-
const propsAttr = island.getAttribute("data-props");
|
|
92
|
-
const capturedProps = propsAttr ? JSON.parse(propsAttr) : {};
|
|
93
|
-
const src = island.getAttribute("data-src") || "";
|
|
94
|
-
const componentName = this.extractComponentName(src);
|
|
95
|
-
// Capture Qwik container state — serialized in the DOM
|
|
96
|
-
const containerEl = island.closest("[q\\:container]") || island;
|
|
97
|
-
const containerState = containerEl.querySelector("script[type=\"qwik/json\"]")?.textContent || null;
|
|
98
|
-
// Capture q: attributes that hold container metadata
|
|
99
|
-
const qContainerAttrs = {};
|
|
100
|
-
const attrs = containerEl.attributes;
|
|
101
|
-
if (attrs) {
|
|
102
|
-
for (let i = 0; i < attrs.length; i++) {
|
|
103
|
-
const attr = attrs[i];
|
|
104
|
-
if (attr.name.startsWith("q:")) {
|
|
105
|
-
qContainerAttrs[attr.name] = attr.value;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return {
|
|
110
|
-
...baseSnapshot,
|
|
111
|
-
framework: "qwik",
|
|
112
|
-
data: {
|
|
113
|
-
componentName,
|
|
114
|
-
capturedProps,
|
|
115
|
-
containerState,
|
|
116
|
-
qContainerAttrs
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
} catch (error) {
|
|
120
|
-
console.warn("Failed to preserve Qwik state:", error);
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Update Qwik component with HMR
|
|
126
|
-
*
|
|
127
|
-
* Qwik's resumability model means we don't hydrate in the traditional sense.
|
|
128
|
-
* Instead, during HMR:
|
|
129
|
-
* 1. The Qwik optimizer (vite-plugin-qwik) invalidates the affected QRLs
|
|
130
|
-
* 2. We re-render the component into the island container
|
|
131
|
-
* 3. Qwikloader picks up the new event bindings automatically
|
|
132
|
-
* 4. Serialized state is restored from the container
|
|
133
|
-
*/
|
|
134
|
-
async update(island, newComponent, props) {
|
|
135
|
-
if (!this.canHandle(newComponent)) {
|
|
136
|
-
throw new Error("Component is not a valid Qwik component");
|
|
137
|
-
}
|
|
138
|
-
// Extract the actual component function
|
|
139
|
-
let Component;
|
|
140
|
-
if (typeof newComponent === "object" && newComponent !== null) {
|
|
141
|
-
const obj = newComponent;
|
|
142
|
-
if (obj.default && typeof obj.default === "function") {
|
|
143
|
-
Component = obj.default;
|
|
144
|
-
} else {
|
|
145
|
-
throw new Error("Qwik component object must have a default export");
|
|
146
|
-
}
|
|
147
|
-
} else if (typeof newComponent === "function") {
|
|
148
|
-
Component = newComponent;
|
|
149
|
-
} else {
|
|
150
|
-
throw new Error("Invalid Qwik component type");
|
|
151
|
-
}
|
|
152
|
-
try {
|
|
153
|
-
// Clean up existing container tracking
|
|
154
|
-
const existing = this.containers.get(island);
|
|
155
|
-
if (existing?.cleanup) {
|
|
156
|
-
try {
|
|
157
|
-
existing.cleanup();
|
|
158
|
-
} catch (error) {
|
|
159
|
-
console.warn("Failed to clean up existing Qwik container:", error);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
// Dynamically import Qwik's client render API
|
|
163
|
-
// In development, vite-plugin-qwik makes this available
|
|
164
|
-
// Use a variable to prevent Vite's static import analysis from resolving this at build time
|
|
165
|
-
const qwikId = "@builder.io/qwik";
|
|
166
|
-
const qwikModule = await import(
|
|
167
|
-
/* @vite-ignore */
|
|
168
|
-
qwikId
|
|
169
|
-
);
|
|
170
|
-
if (qwikModule.render) {
|
|
171
|
-
// Use Qwik's client-side render to mount the component
|
|
172
|
-
// Qwik's render handles setting up the container and resumability
|
|
173
|
-
const jsxNode = typeof qwikModule.jsx === "function" ? qwikModule.jsx(Component, props) : Component(props);
|
|
174
|
-
await qwikModule.render(island, jsxNode);
|
|
175
|
-
} else {
|
|
176
|
-
// Fallback: replace innerHTML and let Qwikloader resume
|
|
177
|
-
// This works because Qwik's event listeners are declarative in the DOM
|
|
178
|
-
console.warn("Qwik render API not available, using innerHTML fallback");
|
|
179
|
-
const result = Component(props);
|
|
180
|
-
if (typeof result === "string") {
|
|
181
|
-
island.innerHTML = result;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
// Track the container for future cleanup
|
|
185
|
-
this.containers.set(island, {});
|
|
186
|
-
// Mark as hydrated (resumed, in Qwik terms)
|
|
187
|
-
island.setAttribute("data-hydrated", "true");
|
|
188
|
-
island.setAttribute("data-hydration-status", "success");
|
|
189
|
-
} catch (error) {
|
|
190
|
-
console.error("Qwik HMR update failed:", error);
|
|
191
|
-
island.setAttribute("data-hydration-status", "error");
|
|
192
|
-
throw error;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Restore Qwik component state after HMR update
|
|
197
|
-
*
|
|
198
|
-
* Qwik's serialized state lives in the DOM, so restoring the container
|
|
199
|
-
* state script block and q: attributes is sufficient for the framework
|
|
200
|
-
* to resume correctly.
|
|
201
|
-
*/
|
|
202
|
-
restoreState(island, state) {
|
|
203
|
-
try {
|
|
204
|
-
// Restore DOM state (scroll, focus, form values)
|
|
205
|
-
super.restoreState(island, state);
|
|
206
|
-
} catch (error) {
|
|
207
|
-
console.warn("Failed to restore Qwik state:", error);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Handle errors during Qwik HMR update
|
|
212
|
-
*/
|
|
213
|
-
handleError(island, error) {
|
|
214
|
-
console.error("Qwik HMR error:", error);
|
|
215
|
-
super.handleError(island, error);
|
|
216
|
-
const errorIndicator = island.querySelector(".hmr-error-indicator");
|
|
217
|
-
if (errorIndicator) {
|
|
218
|
-
const errorMessage = error.message;
|
|
219
|
-
let hint = "";
|
|
220
|
-
if (errorMessage.includes("component$") || errorMessage.includes("component\\$")) {
|
|
221
|
-
hint = " (Hint: Ensure component is wrapped with component$())";
|
|
222
|
-
} else if (errorMessage.includes("useSignal") || errorMessage.includes("useStore")) {
|
|
223
|
-
hint = " (Hint: Qwik hooks must be called inside component$() body)";
|
|
224
|
-
} else if (errorMessage.includes("QRL") || errorMessage.includes("qrl")) {
|
|
225
|
-
hint = " (Hint: Check that lazy-loaded boundaries use $ correctly)";
|
|
226
|
-
} else if (errorMessage.includes("serialize") || errorMessage.includes("container")) {
|
|
227
|
-
hint = " (Hint: Ensure all state is serializable — Qwik serializes state to the DOM)";
|
|
228
|
-
} else if (errorMessage.includes("resumable") || errorMessage.includes("resume")) {
|
|
229
|
-
hint = " (Hint: Server and client container state must match for resumability)";
|
|
230
|
-
}
|
|
231
|
-
errorIndicator.textContent = `Qwik HMR Error: ${errorMessage}${hint}`;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Extract component name from source path
|
|
236
|
-
*/
|
|
237
|
-
extractComponentName(src) {
|
|
238
|
-
const parts = src.split("/");
|
|
239
|
-
const filename = parts[parts.length - 1];
|
|
240
|
-
return filename.replace(/\.qwik\.(tsx?|jsx?)$/, "").replace(/\.(tsx?|jsx?)$/, "");
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Clean up Qwik component when island is removed
|
|
244
|
-
*/
|
|
245
|
-
unmount(island) {
|
|
246
|
-
const container = this.containers.get(island);
|
|
247
|
-
if (container) {
|
|
248
|
-
try {
|
|
249
|
-
if (container.cleanup) {
|
|
250
|
-
container.cleanup();
|
|
251
|
-
}
|
|
252
|
-
this.containers.delete(island);
|
|
253
|
-
} catch (error) {
|
|
254
|
-
console.warn("Failed to unmount Qwik component:", error);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
export const qwikAdapter = new QwikHMRAdapter();
|
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Qwik HMR Adapter
|
|
3
|
-
*
|
|
4
|
-
* Provides Hot Module Replacement support for Qwik components.
|
|
5
|
-
* Qwik is fundamentally different from other frameworks — it uses resumability
|
|
6
|
-
* instead of hydration. Components are serialized on the server and resumed
|
|
7
|
-
* on the client without replaying application logic.
|
|
8
|
-
*
|
|
9
|
-
* Key Qwik concepts:
|
|
10
|
-
* - Resumability: No hydration step — the app resumes from serialized state
|
|
11
|
-
* - Lazy loading via $: Code is split at $ boundaries and loaded on demand
|
|
12
|
-
* - Containers: Qwik apps run inside container elements with q:container attribute
|
|
13
|
-
* - Qwikloader: A tiny (~1KB) script that sets up global event listeners
|
|
14
|
-
*
|
|
15
|
-
* Requirements: 2.1
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
/// <reference lib="dom" />
|
|
19
|
-
|
|
20
|
-
import { BaseFrameworkAdapter, type StateSnapshot } from '../framework-adapter.ts';
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Qwik component type
|
|
24
|
-
* Qwik components are created with component$() which returns a QRL-wrapped component
|
|
25
|
-
*/
|
|
26
|
-
type QwikComponent<P = Record<string, unknown>> = {
|
|
27
|
-
(props: P): unknown;
|
|
28
|
-
__brand?: 'QwikComponent';
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Qwik module interface for SSR/client rendering
|
|
33
|
-
*/
|
|
34
|
-
interface QwikModule {
|
|
35
|
-
renderToString?: (opts: Record<string, unknown>) => Promise<string>;
|
|
36
|
-
render?: (parent: Element, jsxNode: unknown) => Promise<void>;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Qwik state snapshot extending base state
|
|
41
|
-
*/
|
|
42
|
-
interface QwikStateSnapshot extends StateSnapshot {
|
|
43
|
-
framework: 'qwik';
|
|
44
|
-
data: {
|
|
45
|
-
componentName: string;
|
|
46
|
-
capturedProps: Record<string, unknown>;
|
|
47
|
-
containerState: string | null;
|
|
48
|
-
qContainerAttrs: Record<string, string>;
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Qwik HMR Adapter
|
|
54
|
-
*
|
|
55
|
-
* Unlike other frameworks, Qwik doesn't hydrate — it resumes. This adapter
|
|
56
|
-
* handles HMR by re-rendering the container rather than performing traditional
|
|
57
|
-
* hydration-based hot updates.
|
|
58
|
-
*
|
|
59
|
-
* During development, Qwik's optimizer (via vite-plugin-qwik) handles most
|
|
60
|
-
* of the HMR heavy lifting. This adapter provides the island-level integration
|
|
61
|
-
* for Avalon's HMR coordinator.
|
|
62
|
-
*/
|
|
63
|
-
export class QwikHMRAdapter extends BaseFrameworkAdapter {
|
|
64
|
-
readonly name = 'qwik';
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Track container elements for cleanup
|
|
68
|
-
*/
|
|
69
|
-
private containers: WeakMap<HTMLElement, { cleanup?: () => void }> = new WeakMap();
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Check if a component is a Qwik component
|
|
73
|
-
*
|
|
74
|
-
* Qwik components are created with component$() and have distinctive markers:
|
|
75
|
-
* - __brand or __qrl property from the Qwik optimizer
|
|
76
|
-
* - $ suffix convention in source
|
|
77
|
-
* - QRL (Qwik Resource Locator) references
|
|
78
|
-
*/
|
|
79
|
-
canHandle(component: unknown): boolean {
|
|
80
|
-
if (!component) return false;
|
|
81
|
-
|
|
82
|
-
// Check if it's a function (Qwik components are functions)
|
|
83
|
-
if (typeof component === 'function') {
|
|
84
|
-
const comp = component as unknown as Record<string, unknown>;
|
|
85
|
-
|
|
86
|
-
// Check for Qwik-specific markers set by the optimizer
|
|
87
|
-
if (comp.__brand === 'QwikComponent' || comp.__qrl) {
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Check for QRL wrapper pattern
|
|
92
|
-
if (comp.getSymbol || comp.getHash) {
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
const funcStr = component.toString();
|
|
98
|
-
|
|
99
|
-
// Look for Qwik-specific compiled patterns
|
|
100
|
-
if (
|
|
101
|
-
funcStr.includes('component$') ||
|
|
102
|
-
funcStr.includes('qrl') ||
|
|
103
|
-
funcStr.includes('useSignal') ||
|
|
104
|
-
funcStr.includes('useStore') ||
|
|
105
|
-
funcStr.includes('useTask$') ||
|
|
106
|
-
funcStr.includes('useVisibleTask$') ||
|
|
107
|
-
funcStr.includes('_qrl') ||
|
|
108
|
-
funcStr.includes('qwik')
|
|
109
|
-
) {
|
|
110
|
-
return true;
|
|
111
|
-
}
|
|
112
|
-
} catch {
|
|
113
|
-
// Ignore errors from toString()
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Check if it's a Qwik component object (wrapped or exported)
|
|
118
|
-
if (typeof component === 'object' && component !== null) {
|
|
119
|
-
const obj = component as Record<string, unknown>;
|
|
120
|
-
|
|
121
|
-
// Check for default export pattern
|
|
122
|
-
if (obj.default && typeof obj.default === 'function') {
|
|
123
|
-
return this.canHandle(obj.default);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Check for Qwik QRL markers
|
|
127
|
-
if (obj.__qrl || obj.__brand === 'QwikComponent') {
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Preserve Qwik component state before HMR update
|
|
137
|
-
*
|
|
138
|
-
* Qwik serializes state into the DOM via q:container attributes and
|
|
139
|
-
* inline <script type="qwik/json"> blocks. We capture this serialized
|
|
140
|
-
* state so it can be used during re-rendering.
|
|
141
|
-
*/
|
|
142
|
-
override preserveState(island: HTMLElement): QwikStateSnapshot | null {
|
|
143
|
-
try {
|
|
144
|
-
const baseSnapshot = super.preserveState(island);
|
|
145
|
-
if (!baseSnapshot) return null;
|
|
146
|
-
|
|
147
|
-
// Get props from the island
|
|
148
|
-
const propsAttr = island.getAttribute('data-props');
|
|
149
|
-
const capturedProps = propsAttr ? JSON.parse(propsAttr) : {};
|
|
150
|
-
|
|
151
|
-
const src = island.getAttribute('data-src') || '';
|
|
152
|
-
const componentName = this.extractComponentName(src);
|
|
153
|
-
|
|
154
|
-
// Capture Qwik container state — serialized in the DOM
|
|
155
|
-
const containerEl = island.closest('[q\\:container]') || island;
|
|
156
|
-
const containerState = containerEl.querySelector('script[type="qwik/json"]')?.textContent || null;
|
|
157
|
-
|
|
158
|
-
// Capture q: attributes that hold container metadata
|
|
159
|
-
const qContainerAttrs: Record<string, string> = {};
|
|
160
|
-
const attrs = containerEl.attributes;
|
|
161
|
-
if (attrs) {
|
|
162
|
-
for (let i = 0; i < attrs.length; i++) {
|
|
163
|
-
const attr = attrs[i];
|
|
164
|
-
if (attr.name.startsWith('q:')) {
|
|
165
|
-
qContainerAttrs[attr.name] = attr.value;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return {
|
|
171
|
-
...baseSnapshot,
|
|
172
|
-
framework: 'qwik',
|
|
173
|
-
data: {
|
|
174
|
-
componentName,
|
|
175
|
-
capturedProps,
|
|
176
|
-
containerState,
|
|
177
|
-
qContainerAttrs,
|
|
178
|
-
},
|
|
179
|
-
};
|
|
180
|
-
} catch (error) {
|
|
181
|
-
console.warn('Failed to preserve Qwik state:', error);
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Update Qwik component with HMR
|
|
188
|
-
*
|
|
189
|
-
* Qwik's resumability model means we don't hydrate in the traditional sense.
|
|
190
|
-
* Instead, during HMR:
|
|
191
|
-
* 1. The Qwik optimizer (vite-plugin-qwik) invalidates the affected QRLs
|
|
192
|
-
* 2. We re-render the component into the island container
|
|
193
|
-
* 3. Qwikloader picks up the new event bindings automatically
|
|
194
|
-
* 4. Serialized state is restored from the container
|
|
195
|
-
*/
|
|
196
|
-
async update(
|
|
197
|
-
island: HTMLElement,
|
|
198
|
-
newComponent: unknown,
|
|
199
|
-
props: Record<string, unknown>
|
|
200
|
-
): Promise<void> {
|
|
201
|
-
if (!this.canHandle(newComponent)) {
|
|
202
|
-
throw new Error('Component is not a valid Qwik component');
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Extract the actual component function
|
|
206
|
-
let Component: QwikComponent;
|
|
207
|
-
if (typeof newComponent === 'object' && newComponent !== null) {
|
|
208
|
-
const obj = newComponent as Record<string, unknown>;
|
|
209
|
-
if (obj.default && typeof obj.default === 'function') {
|
|
210
|
-
Component = obj.default as QwikComponent;
|
|
211
|
-
} else {
|
|
212
|
-
throw new Error('Qwik component object must have a default export');
|
|
213
|
-
}
|
|
214
|
-
} else if (typeof newComponent === 'function') {
|
|
215
|
-
Component = newComponent as QwikComponent;
|
|
216
|
-
} else {
|
|
217
|
-
throw new Error('Invalid Qwik component type');
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
// Clean up existing container tracking
|
|
222
|
-
const existing = this.containers.get(island);
|
|
223
|
-
if (existing?.cleanup) {
|
|
224
|
-
try {
|
|
225
|
-
existing.cleanup();
|
|
226
|
-
} catch (error) {
|
|
227
|
-
console.warn('Failed to clean up existing Qwik container:', error);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Dynamically import Qwik's client render API
|
|
232
|
-
// In development, vite-plugin-qwik makes this available
|
|
233
|
-
// Use a variable to prevent Vite's static import analysis from resolving this at build time
|
|
234
|
-
const qwikId = '@builder.io/qwik';
|
|
235
|
-
const qwikModule = await import(/* @vite-ignore */ qwikId) as QwikModule & Record<string, unknown>;
|
|
236
|
-
|
|
237
|
-
if (qwikModule.render) {
|
|
238
|
-
// Use Qwik's client-side render to mount the component
|
|
239
|
-
// Qwik's render handles setting up the container and resumability
|
|
240
|
-
const jsxNode = typeof qwikModule.jsx === 'function'
|
|
241
|
-
? qwikModule.jsx(Component, props)
|
|
242
|
-
: Component(props);
|
|
243
|
-
|
|
244
|
-
await qwikModule.render(island, jsxNode);
|
|
245
|
-
} else {
|
|
246
|
-
// Fallback: replace innerHTML and let Qwikloader resume
|
|
247
|
-
// This works because Qwik's event listeners are declarative in the DOM
|
|
248
|
-
console.warn('Qwik render API not available, using innerHTML fallback');
|
|
249
|
-
const result = Component(props);
|
|
250
|
-
if (typeof result === 'string') {
|
|
251
|
-
island.innerHTML = result;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Track the container for future cleanup
|
|
256
|
-
this.containers.set(island, {});
|
|
257
|
-
|
|
258
|
-
// Mark as hydrated (resumed, in Qwik terms)
|
|
259
|
-
island.setAttribute('data-hydrated', 'true');
|
|
260
|
-
island.setAttribute('data-hydration-status', 'success');
|
|
261
|
-
|
|
262
|
-
} catch (error) {
|
|
263
|
-
console.error('Qwik HMR update failed:', error);
|
|
264
|
-
island.setAttribute('data-hydration-status', 'error');
|
|
265
|
-
throw error;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Restore Qwik component state after HMR update
|
|
271
|
-
*
|
|
272
|
-
* Qwik's serialized state lives in the DOM, so restoring the container
|
|
273
|
-
* state script block and q: attributes is sufficient for the framework
|
|
274
|
-
* to resume correctly.
|
|
275
|
-
*/
|
|
276
|
-
override restoreState(island: HTMLElement, state: StateSnapshot): void {
|
|
277
|
-
try {
|
|
278
|
-
// Restore DOM state (scroll, focus, form values)
|
|
279
|
-
super.restoreState(island, state);
|
|
280
|
-
|
|
281
|
-
// Qwik's resumability handles reactive state automatically
|
|
282
|
-
// through its serialized container state in the DOM
|
|
283
|
-
|
|
284
|
-
} catch (error) {
|
|
285
|
-
console.warn('Failed to restore Qwik state:', error);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Handle errors during Qwik HMR update
|
|
291
|
-
*/
|
|
292
|
-
override handleError(island: HTMLElement, error: Error): void {
|
|
293
|
-
console.error('Qwik HMR error:', error);
|
|
294
|
-
|
|
295
|
-
super.handleError(island, error);
|
|
296
|
-
|
|
297
|
-
const errorIndicator = island.querySelector('.hmr-error-indicator');
|
|
298
|
-
if (errorIndicator) {
|
|
299
|
-
const errorMessage = error.message;
|
|
300
|
-
|
|
301
|
-
let hint = '';
|
|
302
|
-
if (errorMessage.includes('component$') || errorMessage.includes('component\\$')) {
|
|
303
|
-
hint = ' (Hint: Ensure component is wrapped with component$())';
|
|
304
|
-
} else if (errorMessage.includes('useSignal') || errorMessage.includes('useStore')) {
|
|
305
|
-
hint = ' (Hint: Qwik hooks must be called inside component$() body)';
|
|
306
|
-
} else if (errorMessage.includes('QRL') || errorMessage.includes('qrl')) {
|
|
307
|
-
hint = ' (Hint: Check that lazy-loaded boundaries use $ correctly)';
|
|
308
|
-
} else if (errorMessage.includes('serialize') || errorMessage.includes('container')) {
|
|
309
|
-
hint = ' (Hint: Ensure all state is serializable — Qwik serializes state to the DOM)';
|
|
310
|
-
} else if (errorMessage.includes('resumable') || errorMessage.includes('resume')) {
|
|
311
|
-
hint = ' (Hint: Server and client container state must match for resumability)';
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
errorIndicator.textContent = `Qwik HMR Error: ${errorMessage}${hint}`;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Extract component name from source path
|
|
320
|
-
*/
|
|
321
|
-
private extractComponentName(src: string): string {
|
|
322
|
-
const parts = src.split('/');
|
|
323
|
-
const filename = parts[parts.length - 1];
|
|
324
|
-
return filename.replace(/\.qwik\.(tsx?|jsx?)$/, '').replace(/\.(tsx?|jsx?)$/, '');
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Clean up Qwik component when island is removed
|
|
329
|
-
*/
|
|
330
|
-
unmount(island: HTMLElement): void {
|
|
331
|
-
const container = this.containers.get(island);
|
|
332
|
-
if (container) {
|
|
333
|
-
try {
|
|
334
|
-
if (container.cleanup) {
|
|
335
|
-
container.cleanup();
|
|
336
|
-
}
|
|
337
|
-
this.containers.delete(island);
|
|
338
|
-
} catch (error) {
|
|
339
|
-
console.warn('Failed to unmount Qwik component:', error);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
export const qwikAdapter = new QwikHMRAdapter();
|