fibrae 0.3.1 → 0.3.2
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/dist/cli/vite-plugin.js +39 -0
- package/dist/cli/vite-plugin.js.map +1 -1
- package/dist/core.js +37 -10
- package/dist/core.js.map +1 -1
- package/dist/dom.d.ts +9 -0
- package/dist/dom.js +30 -4
- package/dist/dom.js.map +1 -1
- package/dist/fiber-commit.d.ts +2 -0
- package/dist/fiber-commit.js +171 -36
- package/dist/fiber-commit.js.map +1 -1
- package/dist/fiber-render.js +13 -27
- package/dist/fiber-render.js.map +1 -1
- package/dist/fiber-update.d.ts +1 -1
- package/dist/fiber-update.js +26 -11
- package/dist/fiber-update.js.map +1 -1
- package/dist/h.d.ts +65 -10
- package/dist/h.js +98 -22
- package/dist/h.js.map +1 -1
- package/dist/head.d.ts +64 -0
- package/dist/head.js +257 -0
- package/dist/head.js.map +1 -0
- package/dist/hydration-dev.d.ts +24 -0
- package/dist/hydration-dev.js +138 -0
- package/dist/hydration-dev.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime/index.d.ts +174 -9
- package/dist/jsx-runtime/index.js +2 -0
- package/dist/jsx-runtime/index.js.map +1 -1
- package/dist/mdx/index.d.ts +4 -4
- package/dist/mdx/index.js +1 -1
- package/dist/mdx/index.js.map +1 -1
- package/dist/mdx/parse.d.ts +1 -1
- package/dist/mdx/parse.js.map +1 -1
- package/dist/mdx/render.d.ts +6 -5
- package/dist/mdx/render.js +62 -64
- package/dist/mdx/render.js.map +1 -1
- package/dist/router/Link.js +7 -1
- package/dist/router/Link.js.map +1 -1
- package/dist/router/Navigator.d.ts +24 -0
- package/dist/router/Navigator.js +24 -0
- package/dist/router/Navigator.js.map +1 -1
- package/dist/router/RouterOutlet.js +28 -5
- package/dist/router/RouterOutlet.js.map +1 -1
- package/dist/router/index.d.ts +5 -1
- package/dist/router/index.js +3 -1
- package/dist/router/index.js.map +1 -1
- package/dist/server.js +7 -3
- package/dist/server.js.map +1 -1
- package/dist/shared.d.ts +34 -4
- package/dist/shared.js +12 -2
- package/dist/shared.js.map +1 -1
- package/dist/tracking.d.ts +3 -2
- package/dist/tracking.js +4 -1
- package/dist/tracking.js.map +1 -1
- package/dist/transition.d.ts +51 -0
- package/dist/transition.js +46 -0
- package/dist/transition.js.map +1 -0
- package/package.json +2 -2
package/dist/head.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Head component — lets any component set document head elements.
|
|
3
|
+
*
|
|
4
|
+
* On the client, directly mutates document.head (title, meta, link tags).
|
|
5
|
+
* On SSR, contributes to HeadCollector for server-side rendering.
|
|
6
|
+
* Cleans up added/modified elements on unmount via ComponentScope finalizer.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const PostPage = (props: { title: string }) =>
|
|
11
|
+
* Effect.gen(function* () {
|
|
12
|
+
* return (
|
|
13
|
+
* <div>
|
|
14
|
+
* <Head title={`${props.title} | My Site`} meta={[
|
|
15
|
+
* { name: "description", content: "A blog post" }
|
|
16
|
+
* ]} />
|
|
17
|
+
* <h1>{props.title}</h1>
|
|
18
|
+
* </div>
|
|
19
|
+
* );
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import * as Effect from "effect/Effect";
|
|
24
|
+
import * as Context from "effect/Context";
|
|
25
|
+
import { ComponentScope, type VElement } from "./shared.js";
|
|
26
|
+
import type { HeadData, MetaDescriptor } from "./router/RouterBuilder.js";
|
|
27
|
+
/**
|
|
28
|
+
* Service for accumulating head elements during SSR.
|
|
29
|
+
* Components call add() during render; the SSR layer calls collect() after.
|
|
30
|
+
*/
|
|
31
|
+
export interface HeadCollectorService {
|
|
32
|
+
/** Register head data from a component. */
|
|
33
|
+
readonly add: (data: HeadData) => Effect.Effect<void>;
|
|
34
|
+
/** Collect all registered head data, merged and deduplicated. */
|
|
35
|
+
readonly collect: () => Effect.Effect<HeadData>;
|
|
36
|
+
}
|
|
37
|
+
declare const HeadCollector_base: Context.TagClass<HeadCollector, "fibrae/HeadCollector", HeadCollectorService>;
|
|
38
|
+
/**
|
|
39
|
+
* Context tag for the HeadCollector service.
|
|
40
|
+
* Only present during SSR — client-side Head components skip this.
|
|
41
|
+
*/
|
|
42
|
+
export declare class HeadCollector extends HeadCollector_base {
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Create a HeadCollector backed by a mutable Ref.
|
|
46
|
+
* Returns a Layer providing the HeadCollector service.
|
|
47
|
+
*/
|
|
48
|
+
export declare const HeadCollectorLive: Effect.Effect<Context.Context<HeadCollector>, never, never>;
|
|
49
|
+
export interface HeadProps {
|
|
50
|
+
readonly title?: string;
|
|
51
|
+
readonly meta?: ReadonlyArray<MetaDescriptor>;
|
|
52
|
+
readonly links?: ReadonlyArray<Record<string, string>>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* General-purpose Head component.
|
|
56
|
+
*
|
|
57
|
+
* Accepts title, meta, and link props. On the client, directly mutates
|
|
58
|
+
* document.head and registers cleanup via ComponentScope. On SSR,
|
|
59
|
+
* contributes to HeadCollector if available.
|
|
60
|
+
*
|
|
61
|
+
* Returns an empty fragment (renders nothing visible).
|
|
62
|
+
*/
|
|
63
|
+
export declare const Head: (props: HeadProps) => Effect.Effect<VElement, never, ComponentScope>;
|
|
64
|
+
export {};
|
package/dist/head.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Head component — lets any component set document head elements.
|
|
3
|
+
*
|
|
4
|
+
* On the client, directly mutates document.head (title, meta, link tags).
|
|
5
|
+
* On SSR, contributes to HeadCollector for server-side rendering.
|
|
6
|
+
* Cleans up added/modified elements on unmount via ComponentScope finalizer.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const PostPage = (props: { title: string }) =>
|
|
11
|
+
* Effect.gen(function* () {
|
|
12
|
+
* return (
|
|
13
|
+
* <div>
|
|
14
|
+
* <Head title={`${props.title} | My Site`} meta={[
|
|
15
|
+
* { name: "description", content: "A blog post" }
|
|
16
|
+
* ]} />
|
|
17
|
+
* <h1>{props.title}</h1>
|
|
18
|
+
* </div>
|
|
19
|
+
* );
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import { jsx } from "./jsx-runtime/index.js";
|
|
24
|
+
import * as Effect from "effect/Effect";
|
|
25
|
+
import * as Context from "effect/Context";
|
|
26
|
+
import * as Scope from "effect/Scope";
|
|
27
|
+
import * as Option from "effect/Option";
|
|
28
|
+
import * as Ref from "effect/Ref";
|
|
29
|
+
import { ComponentScope } from "./shared.js";
|
|
30
|
+
/**
|
|
31
|
+
* Context tag for the HeadCollector service.
|
|
32
|
+
* Only present during SSR — client-side Head components skip this.
|
|
33
|
+
*/
|
|
34
|
+
export class HeadCollector extends Context.Tag("fibrae/HeadCollector")() {
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Dedup key for a meta descriptor — same logic as cli/html.ts.
|
|
38
|
+
* Tags with matching keys are deduplicated (later wins).
|
|
39
|
+
*/
|
|
40
|
+
const metaKey = (meta) => {
|
|
41
|
+
if ("name" in meta)
|
|
42
|
+
return `name:${meta.name}`;
|
|
43
|
+
if ("property" in meta)
|
|
44
|
+
return `property:${meta.property}`;
|
|
45
|
+
if ("httpEquiv" in meta)
|
|
46
|
+
return `httpEquiv:${meta.httpEquiv}`;
|
|
47
|
+
if ("charset" in meta)
|
|
48
|
+
return "charset";
|
|
49
|
+
return undefined;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Merge two HeadData objects. Later data wins for title and deduped meta.
|
|
53
|
+
* Links and scripts are concatenated.
|
|
54
|
+
*/
|
|
55
|
+
const mergeHeadData = (a, b) => {
|
|
56
|
+
// Deduplicate meta: b overrides a when keys match
|
|
57
|
+
const aMeta = a.meta ?? [];
|
|
58
|
+
const bMeta = b.meta ?? [];
|
|
59
|
+
const bKeys = new Set(bMeta.map(metaKey).filter(Boolean));
|
|
60
|
+
const mergedMeta = [
|
|
61
|
+
...aMeta.filter((m) => {
|
|
62
|
+
const key = metaKey(m);
|
|
63
|
+
return key === undefined || !bKeys.has(key);
|
|
64
|
+
}),
|
|
65
|
+
...bMeta,
|
|
66
|
+
];
|
|
67
|
+
return {
|
|
68
|
+
title: b.title ?? a.title,
|
|
69
|
+
meta: mergedMeta.length > 0 ? mergedMeta : undefined,
|
|
70
|
+
links: [...(a.links ?? []), ...(b.links ?? [])],
|
|
71
|
+
scripts: [...(a.scripts ?? []), ...(b.scripts ?? [])],
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Create a HeadCollector backed by a mutable Ref.
|
|
76
|
+
* Returns a Layer providing the HeadCollector service.
|
|
77
|
+
*/
|
|
78
|
+
export const HeadCollectorLive = Effect.gen(function* () {
|
|
79
|
+
const ref = yield* Ref.make({});
|
|
80
|
+
return HeadCollector.of({
|
|
81
|
+
add: (data) => Ref.update(ref, (current) => mergeHeadData(current, data)),
|
|
82
|
+
collect: () => Ref.get(ref),
|
|
83
|
+
});
|
|
84
|
+
}).pipe(Effect.map((service) => Context.make(HeadCollector, service)));
|
|
85
|
+
// =============================================================================
|
|
86
|
+
// Client-side DOM helpers
|
|
87
|
+
// =============================================================================
|
|
88
|
+
const DEV = typeof import.meta !== "undefined" && !!import.meta.hot;
|
|
89
|
+
/**
|
|
90
|
+
* Find an existing meta element by its dedup key.
|
|
91
|
+
*/
|
|
92
|
+
const findMetaElement = (meta) => {
|
|
93
|
+
if ("name" in meta && !("tagName" in meta))
|
|
94
|
+
return document.head.querySelector(`meta[name="${meta.name}"]`);
|
|
95
|
+
if ("property" in meta && !("tagName" in meta))
|
|
96
|
+
return document.head.querySelector(`meta[property="${meta.property}"]`);
|
|
97
|
+
if ("httpEquiv" in meta)
|
|
98
|
+
return document.head.querySelector(`meta[http-equiv="${meta.httpEquiv}"]`);
|
|
99
|
+
if ("charset" in meta)
|
|
100
|
+
return document.head.querySelector("meta[charset]");
|
|
101
|
+
return null;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Set attributes on an element from a MetaDescriptor.
|
|
105
|
+
*/
|
|
106
|
+
const applyMetaAttrs = (el, meta) => {
|
|
107
|
+
if ("charset" in meta) {
|
|
108
|
+
el.setAttribute("charset", meta.charset);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if ("name" in meta && "content" in meta && !("tagName" in meta)) {
|
|
112
|
+
el.setAttribute("name", meta.name);
|
|
113
|
+
el.setAttribute("content", meta.content);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if ("property" in meta && "content" in meta && !("tagName" in meta)) {
|
|
117
|
+
el.setAttribute("property", meta.property);
|
|
118
|
+
el.setAttribute("content", meta.content);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if ("httpEquiv" in meta && "content" in meta) {
|
|
122
|
+
el.setAttribute("http-equiv", meta.httpEquiv);
|
|
123
|
+
el.setAttribute("content", meta.content);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if ("script:ld+json" in meta) {
|
|
127
|
+
el.setAttribute("type", "application/ld+json");
|
|
128
|
+
el.textContent = JSON.stringify(meta["script:ld+json"]);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if ("tagName" in meta) {
|
|
132
|
+
const { tagName: _, ...attrs } = meta;
|
|
133
|
+
Object.entries(attrs).forEach(([k, v]) => el.setAttribute(k, v));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Apply a single MetaDescriptor to the document head.
|
|
139
|
+
* Returns a cleanup function that restores the previous state.
|
|
140
|
+
*/
|
|
141
|
+
const applyMeta = (meta) => {
|
|
142
|
+
// ld+json scripts are separate elements
|
|
143
|
+
if ("script:ld+json" in meta) {
|
|
144
|
+
const el = document.createElement("script");
|
|
145
|
+
applyMetaAttrs(el, meta);
|
|
146
|
+
document.head.appendChild(el);
|
|
147
|
+
return () => el.remove();
|
|
148
|
+
}
|
|
149
|
+
// tagName meta — create element of specified tag
|
|
150
|
+
if ("tagName" in meta) {
|
|
151
|
+
const el = document.createElement(meta.tagName);
|
|
152
|
+
applyMetaAttrs(el, meta);
|
|
153
|
+
document.head.appendChild(el);
|
|
154
|
+
return () => el.remove();
|
|
155
|
+
}
|
|
156
|
+
// title meta — handled by title prop, skip here
|
|
157
|
+
if ("title" in meta)
|
|
158
|
+
return () => { };
|
|
159
|
+
// Standard meta tags — find existing or create new
|
|
160
|
+
const existing = findMetaElement(meta);
|
|
161
|
+
if (existing) {
|
|
162
|
+
// Save previous attributes for restore
|
|
163
|
+
const prevAttrs = new Map();
|
|
164
|
+
Array.from(existing.attributes).forEach((attr) => prevAttrs.set(attr.name, attr.value));
|
|
165
|
+
applyMetaAttrs(existing, meta);
|
|
166
|
+
return () => {
|
|
167
|
+
// Restore previous attributes
|
|
168
|
+
prevAttrs.forEach((v, k) => existing.setAttribute(k, v));
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
// Create new element
|
|
172
|
+
const el = document.createElement("meta");
|
|
173
|
+
applyMetaAttrs(el, meta);
|
|
174
|
+
document.head.appendChild(el);
|
|
175
|
+
return () => el.remove();
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* Apply a link descriptor to the document head.
|
|
179
|
+
* Returns a cleanup function.
|
|
180
|
+
*/
|
|
181
|
+
const applyLink = (attrs) => {
|
|
182
|
+
// Try to find existing link by rel+href
|
|
183
|
+
const selector = attrs.rel && attrs.href ? `link[rel="${attrs.rel}"][href="${attrs.href}"]` : null;
|
|
184
|
+
const existing = selector ? document.head.querySelector(selector) : null;
|
|
185
|
+
if (existing) {
|
|
186
|
+
const prevAttrs = new Map();
|
|
187
|
+
Array.from(existing.attributes).forEach((attr) => prevAttrs.set(attr.name, attr.value));
|
|
188
|
+
Object.entries(attrs).forEach(([k, v]) => existing.setAttribute(k, v));
|
|
189
|
+
return () => {
|
|
190
|
+
prevAttrs.forEach((v, k) => existing.setAttribute(k, v));
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
const el = document.createElement("link");
|
|
194
|
+
Object.entries(attrs).forEach(([k, v]) => el.setAttribute(k, v));
|
|
195
|
+
document.head.appendChild(el);
|
|
196
|
+
return () => el.remove();
|
|
197
|
+
};
|
|
198
|
+
// =============================================================================
|
|
199
|
+
// Head Component
|
|
200
|
+
// =============================================================================
|
|
201
|
+
/**
|
|
202
|
+
* General-purpose Head component.
|
|
203
|
+
*
|
|
204
|
+
* Accepts title, meta, and link props. On the client, directly mutates
|
|
205
|
+
* document.head and registers cleanup via ComponentScope. On SSR,
|
|
206
|
+
* contributes to HeadCollector if available.
|
|
207
|
+
*
|
|
208
|
+
* Returns an empty fragment (renders nothing visible).
|
|
209
|
+
*/
|
|
210
|
+
export const Head = (props) => Effect.gen(function* () {
|
|
211
|
+
const headData = {
|
|
212
|
+
title: props.title,
|
|
213
|
+
meta: props.meta ? [...props.meta] : undefined,
|
|
214
|
+
links: props.links ? [...props.links] : undefined,
|
|
215
|
+
};
|
|
216
|
+
// SSR path: contribute to HeadCollector if available
|
|
217
|
+
const collectorOption = yield* Effect.serviceOption(HeadCollector);
|
|
218
|
+
if (Option.isSome(collectorOption)) {
|
|
219
|
+
yield* collectorOption.value.add(headData);
|
|
220
|
+
return jsx("FRAGMENT", { children: [] });
|
|
221
|
+
}
|
|
222
|
+
// Client path: mutate document.head directly
|
|
223
|
+
if (typeof document !== "undefined") {
|
|
224
|
+
const { scope } = yield* ComponentScope;
|
|
225
|
+
const cleanups = [];
|
|
226
|
+
// Apply title
|
|
227
|
+
if (props.title !== undefined) {
|
|
228
|
+
const prevTitle = document.title;
|
|
229
|
+
document.title = props.title;
|
|
230
|
+
cleanups.push(() => {
|
|
231
|
+
document.title = prevTitle;
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
// Apply meta tags
|
|
235
|
+
if (props.meta) {
|
|
236
|
+
props.meta.forEach((meta) => {
|
|
237
|
+
cleanups.push(applyMeta(meta));
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
// Apply link tags
|
|
241
|
+
if (props.links) {
|
|
242
|
+
props.links.forEach((link) => {
|
|
243
|
+
cleanups.push(applyLink(link));
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
// Register cleanup on unmount
|
|
247
|
+
yield* Scope.addFinalizer(scope, Effect.sync(() => {
|
|
248
|
+
// Run cleanups in reverse order
|
|
249
|
+
cleanups.toReversed().forEach((fn) => fn());
|
|
250
|
+
}));
|
|
251
|
+
if (DEV) {
|
|
252
|
+
yield* Effect.logDebug(`Head: applied ${cleanups.length} head mutations`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return jsx("FRAGMENT", { children: [] });
|
|
256
|
+
});
|
|
257
|
+
//# sourceMappingURL=head.js.map
|
package/dist/head.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"head.js","sourceRoot":"","sources":["../src/head.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hydration mismatch detection — dev mode only.
|
|
3
|
+
*
|
|
4
|
+
* All functions in this module are gated behind the DEV flag and will
|
|
5
|
+
* tree-shake to nothing in production builds.
|
|
6
|
+
*/
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import type { VElement } from "./shared.js";
|
|
9
|
+
/**
|
|
10
|
+
* Check if a host element's tag name matches the VElement type.
|
|
11
|
+
* Warns if server rendered a different tag than client expects.
|
|
12
|
+
*/
|
|
13
|
+
export declare const checkTagMismatch: (vElement: VElement, domNode: Node) => Effect.Effect<void, never, never>;
|
|
14
|
+
/**
|
|
15
|
+
* Check if a text node's content matches the VElement's text value.
|
|
16
|
+
* Only compares trimmed content — whitespace differences are expected
|
|
17
|
+
* between SSR serialization and client render.
|
|
18
|
+
*/
|
|
19
|
+
export declare const checkTextMismatch: (vElement: VElement, domNode: Node) => Effect.Effect<void, never, never>;
|
|
20
|
+
/**
|
|
21
|
+
* Check key attributes on a host element for mismatches.
|
|
22
|
+
* Compares: class/className, id, style, data-*, aria-* attributes.
|
|
23
|
+
*/
|
|
24
|
+
export declare const checkAttributeMismatches: (vElement: VElement, domNode: Node) => Effect.Effect<void, never, never>;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hydration mismatch detection — dev mode only.
|
|
3
|
+
*
|
|
4
|
+
* All functions in this module are gated behind the DEV flag and will
|
|
5
|
+
* tree-shake to nothing in production builds.
|
|
6
|
+
*/
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import { isProperty } from "./dom.js";
|
|
9
|
+
// Same DEV pattern as core.ts — import.meta.hot present = dev mode
|
|
10
|
+
const DEV = typeof import.meta !== "undefined" && !!import.meta.hot;
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// Attribute normalization helpers
|
|
13
|
+
// =============================================================================
|
|
14
|
+
/** Map JSX prop names to their DOM attribute equivalents */
|
|
15
|
+
const propToAttr = {
|
|
16
|
+
className: "class",
|
|
17
|
+
htmlFor: "for",
|
|
18
|
+
tabIndex: "tabindex",
|
|
19
|
+
readOnly: "readonly",
|
|
20
|
+
autoFocus: "autofocus",
|
|
21
|
+
autoPlay: "autoplay",
|
|
22
|
+
noValidate: "novalidate",
|
|
23
|
+
formNoValidate: "formnovalidate",
|
|
24
|
+
allowFullscreen: "allowfullscreen",
|
|
25
|
+
playsInline: "playsinline",
|
|
26
|
+
};
|
|
27
|
+
/** Normalize a JSX prop name to the corresponding DOM attribute name */
|
|
28
|
+
const normalizeAttrName = (name) => propToAttr[name] ??
|
|
29
|
+
(name.startsWith("data-") || name.startsWith("aria-") ? name : name.toLowerCase());
|
|
30
|
+
/** Normalize a style prop (object or string) to a comparable string */
|
|
31
|
+
const normalizeStyle = (value) => {
|
|
32
|
+
if (typeof value === "string")
|
|
33
|
+
return value.replace(/\s+/g, " ").trim();
|
|
34
|
+
if (typeof value === "object" && value !== null) {
|
|
35
|
+
return Object.entries(value)
|
|
36
|
+
.map(([k, v]) => {
|
|
37
|
+
const prop = k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
38
|
+
return `${prop}: ${v}`;
|
|
39
|
+
})
|
|
40
|
+
.join("; ");
|
|
41
|
+
}
|
|
42
|
+
return "";
|
|
43
|
+
};
|
|
44
|
+
// =============================================================================
|
|
45
|
+
// Mismatch warning helpers
|
|
46
|
+
// =============================================================================
|
|
47
|
+
const warnMismatch = (message) => Effect.logWarning(`[fibrae] Hydration mismatch: ${message}`);
|
|
48
|
+
// =============================================================================
|
|
49
|
+
// Public check functions — all no-op in production
|
|
50
|
+
// =============================================================================
|
|
51
|
+
/**
|
|
52
|
+
* Check if a host element's tag name matches the VElement type.
|
|
53
|
+
* Warns if server rendered a different tag than client expects.
|
|
54
|
+
*/
|
|
55
|
+
export const checkTagMismatch = (vElement, domNode) => {
|
|
56
|
+
if (!DEV)
|
|
57
|
+
return Effect.void;
|
|
58
|
+
const type = vElement.type;
|
|
59
|
+
if (typeof type !== "string")
|
|
60
|
+
return Effect.void;
|
|
61
|
+
if (type === "TEXT_ELEMENT" || type === "FRAGMENT" || type === "SUSPENSE" || type === "BOUNDARY")
|
|
62
|
+
return Effect.void;
|
|
63
|
+
if (domNode.nodeType === Node.ELEMENT_NODE) {
|
|
64
|
+
const expected = type.toUpperCase();
|
|
65
|
+
const actual = domNode.tagName;
|
|
66
|
+
if (expected !== actual) {
|
|
67
|
+
return warnMismatch(`expected <${type}> but found <${actual.toLowerCase()}>`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (domNode.nodeType === Node.TEXT_NODE) {
|
|
71
|
+
const text = domNode.textContent?.substring(0, 50) ?? "";
|
|
72
|
+
return warnMismatch(`expected <${type}> but found text node "${text}"`);
|
|
73
|
+
}
|
|
74
|
+
return Effect.void;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Check if a text node's content matches the VElement's text value.
|
|
78
|
+
* Only compares trimmed content — whitespace differences are expected
|
|
79
|
+
* between SSR serialization and client render.
|
|
80
|
+
*/
|
|
81
|
+
export const checkTextMismatch = (vElement, domNode) => {
|
|
82
|
+
if (!DEV)
|
|
83
|
+
return Effect.void;
|
|
84
|
+
if (vElement.type !== "TEXT_ELEMENT")
|
|
85
|
+
return Effect.void;
|
|
86
|
+
if (domNode.nodeType !== Node.TEXT_NODE)
|
|
87
|
+
return Effect.void;
|
|
88
|
+
const expected = String(vElement.props.nodeValue ?? "");
|
|
89
|
+
const actual = domNode.textContent ?? "";
|
|
90
|
+
// Trim both sides — SSR and client may differ in whitespace
|
|
91
|
+
if (expected.trim() !== actual.trim()) {
|
|
92
|
+
return warnMismatch(`text content differs: server "${actual.substring(0, 50)}" vs client "${expected.substring(0, 50)}"`);
|
|
93
|
+
}
|
|
94
|
+
return Effect.void;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Check key attributes on a host element for mismatches.
|
|
98
|
+
* Compares: class/className, id, style, data-*, aria-* attributes.
|
|
99
|
+
*/
|
|
100
|
+
export const checkAttributeMismatches = (vElement, domNode) => {
|
|
101
|
+
if (!DEV)
|
|
102
|
+
return Effect.void;
|
|
103
|
+
if (typeof vElement.type !== "string")
|
|
104
|
+
return Effect.void;
|
|
105
|
+
if (vElement.type === "TEXT_ELEMENT" || vElement.type === "FRAGMENT")
|
|
106
|
+
return Effect.void;
|
|
107
|
+
if (domNode.nodeType !== Node.ELEMENT_NODE)
|
|
108
|
+
return Effect.void;
|
|
109
|
+
const el = domNode;
|
|
110
|
+
const props = vElement.props;
|
|
111
|
+
// Collect warnings for all mismatched attributes, then emit them together
|
|
112
|
+
return Effect.forEach(Object.keys(props).filter(isProperty), (key) => {
|
|
113
|
+
const value = props[key];
|
|
114
|
+
if (value == null || value === false)
|
|
115
|
+
return Effect.void;
|
|
116
|
+
// Style needs special comparison
|
|
117
|
+
if (key === "style") {
|
|
118
|
+
const expectedStyle = normalizeStyle(value);
|
|
119
|
+
const actualStyle = el.getAttribute("style") ?? "";
|
|
120
|
+
// Only warn if substantially different (ignore trailing semicolons, whitespace)
|
|
121
|
+
if (expectedStyle.replace(/;\s*$/, "").trim() !== actualStyle.replace(/;\s*$/, "").trim()) {
|
|
122
|
+
return warnMismatch(`attribute "style" on <${vElement.type}>: server "${actualStyle}" vs client "${expectedStyle}"`);
|
|
123
|
+
}
|
|
124
|
+
return Effect.void;
|
|
125
|
+
}
|
|
126
|
+
const attrName = normalizeAttrName(key);
|
|
127
|
+
const actualValue = el.getAttribute(attrName);
|
|
128
|
+
const expectedValue = value === true ? "" : String(value);
|
|
129
|
+
if (actualValue === null) {
|
|
130
|
+
return warnMismatch(`attribute "${attrName}" missing on <${vElement.type}>: expected "${expectedValue}"`);
|
|
131
|
+
}
|
|
132
|
+
if (actualValue !== expectedValue) {
|
|
133
|
+
return warnMismatch(`attribute "${attrName}" on <${vElement.type}>: server "${actualValue}" vs client "${expectedValue}"`);
|
|
134
|
+
}
|
|
135
|
+
return Effect.void;
|
|
136
|
+
}, { discard: true });
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=hydration-dev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydration-dev.js","sourceRoot":"","sources":["../src/hydration-dev.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,10 @@ export { h, createTextElement } from "./h.js";
|
|
|
5
5
|
export type { VElement, ElementType, Primitive } from "./shared.js";
|
|
6
6
|
export type { VElement as VNode } from "./shared.js";
|
|
7
7
|
export { RenderError, StreamError, EventHandlerError, type ComponentError } from "./shared.js";
|
|
8
|
-
export { ComponentScope } from "./shared.js";
|
|
8
|
+
export { ComponentScope, createRef } from "./shared.js";
|
|
9
|
+
export type { Ref } from "./shared.js";
|
|
9
10
|
export { HydrationState, HydrationStateLive, HydrationStateEmpty } from "./hydration-state.js";
|
|
10
11
|
export { Atom, AtomHttpApi, AtomRef, AtomRpc, Hydration, Registry as AtomRegistry, Result, } from "@effect-atom/atom";
|
|
11
12
|
export { mountAtom, subscribeAtom } from "./atom-utils.js";
|
|
13
|
+
export type { TransitionService } from "./transition.js";
|
|
14
|
+
export { Transition, TransitionLive } from "./transition.js";
|
package/dist/index.js
CHANGED
|
@@ -12,12 +12,13 @@ export { h, createTextElement } from "./h.js";
|
|
|
12
12
|
// Error types
|
|
13
13
|
export { RenderError, StreamError, EventHandlerError } from "./shared.js";
|
|
14
14
|
// Component lifecycle
|
|
15
|
-
export { ComponentScope } from "./shared.js";
|
|
15
|
+
export { ComponentScope, createRef } from "./shared.js";
|
|
16
16
|
// Hydration state service
|
|
17
17
|
export { HydrationState, HydrationStateLive, HydrationStateEmpty } from "./hydration-state.js";
|
|
18
18
|
// Re-export upstream Effect Atom APIs for consumers
|
|
19
19
|
export { Atom, AtomHttpApi, AtomRef, AtomRpc, Hydration, Registry as AtomRegistry, Result, } from "@effect-atom/atom";
|
|
20
20
|
// Component-scoped atom utilities
|
|
21
21
|
export { mountAtom, subscribeAtom } from "./atom-utils.js";
|
|
22
|
+
export { Transition, TransitionLive } from "./transition.js";
|
|
22
23
|
// Router available via "fibrae/router" import
|
|
23
24
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,aAAa;AACb,gFAAgF;AAEhF,uBAAuB;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC,UAAU;AACV,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEtE,sBAAsB;AACtB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE1D,iCAAiC;AACjC,OAAO,EAAE,CAAC,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAM9C,cAAc;AACd,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAuB,MAAM,aAAa,CAAC;AAE/F,sBAAsB;AACtB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,aAAa;AACb,gFAAgF;AAEhF,uBAAuB;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC,UAAU;AACV,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEtE,sBAAsB;AACtB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE1D,iCAAiC;AACjC,OAAO,EAAE,CAAC,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAM9C,cAAc;AACd,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAuB,MAAM,aAAa,CAAC;AAE/F,sBAAsB;AACtB,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxD,0BAA0B;AAC1B,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE/F,oDAAoD;AACpD,OAAO,EACL,IAAI,EACJ,WAAW,EACX,OAAO,EACP,OAAO,EACP,SAAS,EACT,QAAQ,IAAI,YAAY,EACxB,MAAM,GACP,MAAM,mBAAmB,CAAC;AAE3B,kCAAkC;AAClC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAI3D,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE7D,8CAA8C"}
|