@sigil-dev/grimoire 0.7.5 → 0.7.6
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/.grimoire/_routes.dom.js +8 -0
- package/.grimoire/_routes.hydrate.js +8 -0
- package/.grimoire/tsconfig.generated.json +11 -0
- package/.grimoire/types/ambient.d.ts +59 -0
- package/.grimoire/types/api/hello/$types.d.ts +50 -0
- package/.grimoire/types/api/items/$types.d.ts +50 -0
- package/.grimoire/types/echo/$types.d.ts +50 -0
- package/.grimoire/types/env-private.d.ts +5 -0
- package/.grimoire/types/env-public.d.ts +5 -0
- package/.grimoire/types/mixed/$types.d.ts +50 -0
- package/.grimoire/types/params/[docId]/$types.d.ts +52 -0
- package/.grimoire/types/reject/$types.d.ts +50 -0
- package/index.ts +34 -34
- package/package.json +8 -4
- package/preload.js +2 -0
- package/public/__grimoire__/hydrate.js +585 -0
- package/public/__grimoire__/index.js +490 -0
- package/src/client/head.ts +29 -0
- package/src/client/router.ts +224 -76
- package/src/env/index.ts +25 -0
- package/src/env/plugin.ts +13 -0
- package/src/env/private.ts +5 -0
- package/src/env/public.ts +7 -0
- package/src/env/typegen.ts +51 -0
- package/src/integrations/vite.ts +72 -72
- package/src/rendering/head.ts +22 -2
- package/src/rendering/hydrate.ts +81 -27
- package/src/rendering/index.ts +199 -186
- package/src/rendering/ssrPlugin.ts +53 -47
- package/src/routing/manifest-gen.ts +39 -26
- package/src/routing/router.ts +106 -98
- package/src/routing/scanner.ts +135 -129
- package/src/routing/transform-routes.ts +101 -101
- package/src/server/build.ts +147 -90
- package/src/server/coordinator.ts +306 -297
- package/src/server/hooks.ts +24 -3
- package/src/server/index.ts +144 -70
- package/src/server/worker.ts +59 -59
- package/src/typegen/index.ts +353 -340
- package/src/types.ts +269 -260
- package/test/context.test.ts +52 -52
- package/test/hydration.test.ts +119 -119
- package/test/middleware.test.ts +223 -221
- package/test/rendering.test.ts +425 -425
- package/test/routing.test.ts +83 -45
- package/test/scanning.test.ts +181 -169
- package/test/server.test.ts +229 -229
- package/test/streaming.test.ts +106 -106
- package/test/transform-routes.test.ts +84 -84
- package/test/typegen.test.ts +19 -1
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
// .grimoire/_routes.hydrate.js
|
|
2
|
+
var routes = {};
|
|
3
|
+
var layouts = [];
|
|
4
|
+
// ../runtime/reactivity/map.ts
|
|
5
|
+
class ReactiveMap extends Map {
|
|
6
|
+
#keyVersions = new Map;
|
|
7
|
+
#structVersion;
|
|
8
|
+
#structVal = 0;
|
|
9
|
+
#getKeyVersion(key) {
|
|
10
|
+
let sig = this.#keyVersions.get(key);
|
|
11
|
+
if (!sig) {
|
|
12
|
+
const inner = createSignal(0);
|
|
13
|
+
this.#keyVersions.set(key, inner);
|
|
14
|
+
sig = inner;
|
|
15
|
+
}
|
|
16
|
+
return sig;
|
|
17
|
+
}
|
|
18
|
+
#bumpKey(key) {
|
|
19
|
+
const sig = this.#keyVersions.get(key);
|
|
20
|
+
if (sig) {
|
|
21
|
+
const v = sig.peek() + 1;
|
|
22
|
+
sig.set(v);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
#bumpStruct() {
|
|
26
|
+
this.#structVal++;
|
|
27
|
+
this.#structVersion.set(this.#structVal);
|
|
28
|
+
}
|
|
29
|
+
constructor(entries) {
|
|
30
|
+
super(entries);
|
|
31
|
+
this.#structVersion = createSignal(0);
|
|
32
|
+
if (entries) {
|
|
33
|
+
for (const [k] of entries)
|
|
34
|
+
this.#getKeyVersion(k);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
get(key) {
|
|
38
|
+
this.#getKeyVersion(key)();
|
|
39
|
+
return super.get(key);
|
|
40
|
+
}
|
|
41
|
+
set(key, value) {
|
|
42
|
+
const existed = super.has(key);
|
|
43
|
+
super.set(key, value);
|
|
44
|
+
this.#bumpKey(key);
|
|
45
|
+
if (!existed)
|
|
46
|
+
this.#bumpStruct();
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
delete(key) {
|
|
50
|
+
const result = super.delete(key);
|
|
51
|
+
if (result) {
|
|
52
|
+
this.#bumpKey(key);
|
|
53
|
+
this.#bumpStruct();
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
clear() {
|
|
58
|
+
if (this.size > 0) {
|
|
59
|
+
for (const key of this.keys())
|
|
60
|
+
this.#bumpKey(key);
|
|
61
|
+
super.clear();
|
|
62
|
+
this.#bumpStruct();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
forEach(callbackfn, thisArg) {
|
|
66
|
+
this.#structVersion();
|
|
67
|
+
super.forEach(callbackfn, thisArg);
|
|
68
|
+
}
|
|
69
|
+
get size() {
|
|
70
|
+
this.#structVersion();
|
|
71
|
+
return super.size;
|
|
72
|
+
}
|
|
73
|
+
entries() {
|
|
74
|
+
this.#structVersion();
|
|
75
|
+
return super.entries();
|
|
76
|
+
}
|
|
77
|
+
keys() {
|
|
78
|
+
this.#structVersion();
|
|
79
|
+
return super.keys();
|
|
80
|
+
}
|
|
81
|
+
values() {
|
|
82
|
+
this.#structVersion();
|
|
83
|
+
return super.values();
|
|
84
|
+
}
|
|
85
|
+
[Symbol.iterator]() {
|
|
86
|
+
this.#structVersion();
|
|
87
|
+
return super[Symbol.iterator]();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// ../runtime/reactivity/set.ts
|
|
91
|
+
class ReactiveSet extends Set {
|
|
92
|
+
#version;
|
|
93
|
+
#versionVal = 0;
|
|
94
|
+
constructor(values) {
|
|
95
|
+
super(values);
|
|
96
|
+
this.#version = createSignal(0);
|
|
97
|
+
}
|
|
98
|
+
#bump() {
|
|
99
|
+
this.#versionVal++;
|
|
100
|
+
this.#version.set(this.#versionVal);
|
|
101
|
+
}
|
|
102
|
+
has(value) {
|
|
103
|
+
this.#version();
|
|
104
|
+
return super.has(value);
|
|
105
|
+
}
|
|
106
|
+
add(value) {
|
|
107
|
+
const sizeBefore = super.size;
|
|
108
|
+
super.add(value);
|
|
109
|
+
if (super.size !== sizeBefore)
|
|
110
|
+
this.#bump();
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
delete(value) {
|
|
114
|
+
const result = super.delete(value);
|
|
115
|
+
if (result)
|
|
116
|
+
this.#bump();
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
clear() {
|
|
120
|
+
if (this.size > 0) {
|
|
121
|
+
super.clear();
|
|
122
|
+
this.#bump();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
forEach(callbackfn, thisArg) {
|
|
126
|
+
this.#version();
|
|
127
|
+
super.forEach(callbackfn, thisArg);
|
|
128
|
+
}
|
|
129
|
+
get size() {
|
|
130
|
+
this.#version();
|
|
131
|
+
return super.size;
|
|
132
|
+
}
|
|
133
|
+
entries() {
|
|
134
|
+
this.#version();
|
|
135
|
+
return super.entries();
|
|
136
|
+
}
|
|
137
|
+
keys() {
|
|
138
|
+
this.#version();
|
|
139
|
+
return super.keys();
|
|
140
|
+
}
|
|
141
|
+
values() {
|
|
142
|
+
this.#version();
|
|
143
|
+
return super.values();
|
|
144
|
+
}
|
|
145
|
+
[Symbol.iterator]() {
|
|
146
|
+
this.#version();
|
|
147
|
+
return super[Symbol.iterator]();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ../runtime/index.ts
|
|
152
|
+
var SIGIL_SAFE = Symbol.for("sigil.safe");
|
|
153
|
+
var currentEffect = null;
|
|
154
|
+
var currentDeps = null;
|
|
155
|
+
var batchDepth = 0;
|
|
156
|
+
var scheduledFlush = false;
|
|
157
|
+
var pendingEffects = new Set;
|
|
158
|
+
var pendingPreEffects = new Set;
|
|
159
|
+
function flush() {
|
|
160
|
+
scheduledFlush = false;
|
|
161
|
+
pendingPreEffects.forEach((e) => e());
|
|
162
|
+
pendingPreEffects.clear();
|
|
163
|
+
pendingEffects.forEach((e) => e());
|
|
164
|
+
pendingEffects.clear();
|
|
165
|
+
}
|
|
166
|
+
function scheduleEffect(fn) {
|
|
167
|
+
const target = fn.__sigil_pre ? pendingPreEffects : pendingEffects;
|
|
168
|
+
target.add(fn);
|
|
169
|
+
if (batchDepth === 0 && !scheduledFlush) {
|
|
170
|
+
scheduledFlush = true;
|
|
171
|
+
queueMicrotask(flush);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
var targetDepsMap = new WeakMap;
|
|
175
|
+
function getDeps(target, key) {
|
|
176
|
+
let deps = targetDepsMap.get(target);
|
|
177
|
+
if (!deps) {
|
|
178
|
+
deps = new Map;
|
|
179
|
+
targetDepsMap.set(target, deps);
|
|
180
|
+
}
|
|
181
|
+
let set = deps.get(key);
|
|
182
|
+
if (!set) {
|
|
183
|
+
set = new Set;
|
|
184
|
+
deps.set(key, set);
|
|
185
|
+
}
|
|
186
|
+
return set;
|
|
187
|
+
}
|
|
188
|
+
function track(target, key) {
|
|
189
|
+
if (currentEffect && currentDeps) {
|
|
190
|
+
const deps = getDeps(target, key);
|
|
191
|
+
deps.add(currentEffect);
|
|
192
|
+
currentDeps.add(deps);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function trigger(target, key) {
|
|
196
|
+
const deps = targetDepsMap.get(target);
|
|
197
|
+
if (!deps)
|
|
198
|
+
return;
|
|
199
|
+
const set = deps.get(key);
|
|
200
|
+
if (!set)
|
|
201
|
+
return;
|
|
202
|
+
for (const fn of [...set]) {
|
|
203
|
+
scheduleEffect(fn);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
var proxyCache = new WeakMap;
|
|
207
|
+
var proxyToTarget = new WeakMap;
|
|
208
|
+
var reactiveTargets = new WeakSet;
|
|
209
|
+
function createReactiveProxy(target) {
|
|
210
|
+
const existing = proxyCache.get(target);
|
|
211
|
+
if (existing)
|
|
212
|
+
return existing;
|
|
213
|
+
const proxy = new Proxy(target, {
|
|
214
|
+
get(target2, key, receiver) {
|
|
215
|
+
if (key === "set") {
|
|
216
|
+
return (newValue) => {
|
|
217
|
+
const oldKeys = new Set(Object.keys(target2));
|
|
218
|
+
const newKeys = new Set(Object.keys(newValue));
|
|
219
|
+
for (const k of oldKeys) {
|
|
220
|
+
if (!newKeys.has(k)) {
|
|
221
|
+
delete target2[k];
|
|
222
|
+
trigger(target2, k);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
for (const k of newKeys) {
|
|
226
|
+
target2[k] = newValue[k];
|
|
227
|
+
trigger(target2, k);
|
|
228
|
+
}
|
|
229
|
+
if (Array.isArray(target2)) {
|
|
230
|
+
trigger(target2, "length");
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
if (key === "peek") {
|
|
235
|
+
return () => target2;
|
|
236
|
+
}
|
|
237
|
+
if (typeof key === "symbol") {
|
|
238
|
+
return Reflect.get(target2, key, receiver);
|
|
239
|
+
}
|
|
240
|
+
track(target2, key);
|
|
241
|
+
const value = target2[key];
|
|
242
|
+
if (typeof value === "object" && value !== null) {
|
|
243
|
+
return createReactiveProxy(value);
|
|
244
|
+
}
|
|
245
|
+
return value;
|
|
246
|
+
},
|
|
247
|
+
set(target2, key, newValue, receiver) {
|
|
248
|
+
const oldValue = target2[key];
|
|
249
|
+
const result = Reflect.set(target2, key, newValue, receiver);
|
|
250
|
+
if (oldValue !== newValue) {
|
|
251
|
+
trigger(target2, key);
|
|
252
|
+
if (Array.isArray(target2) && typeof key === "string" && !Number.isNaN(Number(key))) {
|
|
253
|
+
trigger(target2, "length");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return result;
|
|
257
|
+
},
|
|
258
|
+
deleteProperty(target2, key) {
|
|
259
|
+
const result = Reflect.deleteProperty(target2, key);
|
|
260
|
+
trigger(target2, key);
|
|
261
|
+
return result;
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
proxyCache.set(target, proxy);
|
|
265
|
+
proxyToTarget.set(proxy, target);
|
|
266
|
+
reactiveTargets.add(target);
|
|
267
|
+
return proxy;
|
|
268
|
+
}
|
|
269
|
+
function createPrimitiveSignal(initial) {
|
|
270
|
+
const subs = new Set;
|
|
271
|
+
let value = initial;
|
|
272
|
+
const getter = () => {
|
|
273
|
+
if (currentEffect && currentDeps) {
|
|
274
|
+
subs.add(currentEffect);
|
|
275
|
+
currentDeps.add(subs);
|
|
276
|
+
}
|
|
277
|
+
return value;
|
|
278
|
+
};
|
|
279
|
+
getter.set = (next) => {
|
|
280
|
+
value = next;
|
|
281
|
+
for (const fn of [...subs]) {
|
|
282
|
+
scheduleEffect(fn);
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
getter.peek = () => value;
|
|
286
|
+
return getter;
|
|
287
|
+
}
|
|
288
|
+
function createSignal(initial) {
|
|
289
|
+
if (typeof initial === "object" && initial !== null) {
|
|
290
|
+
const proxy = createReactiveProxy(initial);
|
|
291
|
+
const getter = () => proxy;
|
|
292
|
+
getter.set = (next) => proxy.set(next);
|
|
293
|
+
getter.peek = () => initial;
|
|
294
|
+
return getter;
|
|
295
|
+
}
|
|
296
|
+
return createPrimitiveSignal(initial);
|
|
297
|
+
}
|
|
298
|
+
function claim(nodes, tag, parent) {
|
|
299
|
+
for (let i = 0;i < nodes.length; i++) {
|
|
300
|
+
const node = nodes[i];
|
|
301
|
+
if (node instanceof Element && node.tagName.toLowerCase() === tag.toLowerCase()) {
|
|
302
|
+
nodes.splice(i, 1);
|
|
303
|
+
return node;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const el = document.createElement(tag);
|
|
307
|
+
if (parent)
|
|
308
|
+
parent.appendChild(el);
|
|
309
|
+
return el;
|
|
310
|
+
}
|
|
311
|
+
function insert(parent, value) {
|
|
312
|
+
if (value === false || value === null || value === undefined)
|
|
313
|
+
return;
|
|
314
|
+
if (typeof value === "function") {
|
|
315
|
+
insert(parent, value());
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (Array.isArray(value)) {
|
|
319
|
+
for (const item of value)
|
|
320
|
+
insert(parent, item);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (value instanceof Node) {
|
|
324
|
+
parent.appendChild(value);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
parent.append(String(value));
|
|
328
|
+
}
|
|
329
|
+
var clientContextStore = new Map;
|
|
330
|
+
var _hydrationStack = [];
|
|
331
|
+
function pushHydrationNodes(nodes) {
|
|
332
|
+
_hydrationStack.push(nodes);
|
|
333
|
+
}
|
|
334
|
+
function popHydrationNodes() {
|
|
335
|
+
_hydrationStack.pop();
|
|
336
|
+
}
|
|
337
|
+
function getHydrationNodes() {
|
|
338
|
+
return _hydrationStack[_hydrationStack.length - 1] ?? [];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// src/client/scope.ts
|
|
342
|
+
function withEffectScope(fn) {
|
|
343
|
+
const prev = globalThis.__sigilEffectScope;
|
|
344
|
+
const disposers = [];
|
|
345
|
+
globalThis.__sigilEffectScope = disposers;
|
|
346
|
+
try {
|
|
347
|
+
fn();
|
|
348
|
+
} finally {
|
|
349
|
+
globalThis.__sigilEffectScope = prev;
|
|
350
|
+
}
|
|
351
|
+
return () => {
|
|
352
|
+
for (const d of disposers)
|
|
353
|
+
d();
|
|
354
|
+
disposers.length = 0;
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/client/router.ts
|
|
359
|
+
var routeMap = {};
|
|
360
|
+
var layoutList = [];
|
|
361
|
+
var disposeCurrentPage = null;
|
|
362
|
+
var currentPath = location.pathname;
|
|
363
|
+
var beforeNavigateCb = null;
|
|
364
|
+
var onNavigateCb = null;
|
|
365
|
+
var afterNavigateCb = null;
|
|
366
|
+
var scrollPositions = new Map;
|
|
367
|
+
function saveScrollPosition() {
|
|
368
|
+
scrollPositions.set(currentPath, window.scrollY);
|
|
369
|
+
}
|
|
370
|
+
function restoreScrollPosition(path) {
|
|
371
|
+
const saved = scrollPositions.get(path);
|
|
372
|
+
if (saved !== undefined) {
|
|
373
|
+
requestAnimationFrame(() => window.scrollTo(0, saved));
|
|
374
|
+
} else {
|
|
375
|
+
requestAnimationFrame(() => window.scrollTo(0, 0));
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
var preloadTimer = null;
|
|
379
|
+
function setupPreload() {
|
|
380
|
+
document.addEventListener("mouseover", (e) => {
|
|
381
|
+
const a = e.target.closest("a");
|
|
382
|
+
if (!a)
|
|
383
|
+
return;
|
|
384
|
+
const href = a.getAttribute("href");
|
|
385
|
+
if (!href)
|
|
386
|
+
return;
|
|
387
|
+
if (/^(https?:\/\/|\/\/|#|mailto:|tel:)/.test(href))
|
|
388
|
+
return;
|
|
389
|
+
const preloadData = a.hasAttribute("data-sigil-preload-data");
|
|
390
|
+
const preloadCode = a.hasAttribute("data-sigil-preload-code");
|
|
391
|
+
if (!preloadData && !preloadCode)
|
|
392
|
+
return;
|
|
393
|
+
if (preloadTimer)
|
|
394
|
+
clearTimeout(preloadTimer);
|
|
395
|
+
preloadTimer = setTimeout(() => {
|
|
396
|
+
if (preloadCode) {
|
|
397
|
+
const link = document.createElement("link");
|
|
398
|
+
link.rel = "prefetch";
|
|
399
|
+
link.href = href;
|
|
400
|
+
document.head.appendChild(link);
|
|
401
|
+
}
|
|
402
|
+
if (preloadData) {
|
|
403
|
+
fetch(href, {
|
|
404
|
+
headers: { "x-grimoire-navigate": "1" },
|
|
405
|
+
priority: "low"
|
|
406
|
+
}).catch(() => {});
|
|
407
|
+
}
|
|
408
|
+
}, 80);
|
|
409
|
+
});
|
|
410
|
+
document.addEventListener("mouseout", (e) => {
|
|
411
|
+
const a = e.target.closest("a");
|
|
412
|
+
if (!a)
|
|
413
|
+
return;
|
|
414
|
+
if (preloadTimer)
|
|
415
|
+
clearTimeout(preloadTimer);
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
async function navigate(path) {
|
|
419
|
+
const url = new URL(path, location.origin);
|
|
420
|
+
if (beforeNavigateCb) {
|
|
421
|
+
const shouldProceed = beforeNavigateCb(url);
|
|
422
|
+
if (shouldProceed === false)
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
onNavigateCb?.(url);
|
|
426
|
+
saveScrollPosition();
|
|
427
|
+
const res = await fetch(path, { headers: { "x-grimoire-navigate": "1" } });
|
|
428
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
429
|
+
if (!contentType.includes("application/json")) {
|
|
430
|
+
window.location.href = path;
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const json = await res.json();
|
|
434
|
+
const { data, layoutData, params, pattern, head } = json;
|
|
435
|
+
const Page = routeMap[pattern];
|
|
436
|
+
if (!Page) {
|
|
437
|
+
window.location.href = path;
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
disposeCurrentPage?.();
|
|
441
|
+
disposeCurrentPage = null;
|
|
442
|
+
pushHydrationNodes([]);
|
|
443
|
+
let rootNode;
|
|
444
|
+
disposeCurrentPage = withEffectScope(() => {
|
|
445
|
+
try {
|
|
446
|
+
const matchedLayouts = layoutList.filter((l) => l.path === "/" || pattern === l.path || pattern.startsWith(l.path + "/")).sort((a, b) => a.path.length - b.path.length);
|
|
447
|
+
let renderFn = () => {
|
|
448
|
+
const pageNode = Page({ data, params });
|
|
449
|
+
const pageDiv = document.createElement("div");
|
|
450
|
+
pageDiv.id = "grimoire-page";
|
|
451
|
+
pageDiv.appendChild(pageNode);
|
|
452
|
+
return pageDiv;
|
|
453
|
+
};
|
|
454
|
+
for (let i = matchedLayouts.length - 1;i >= 0; i--) {
|
|
455
|
+
const LayoutComponent = matchedLayouts[i].component;
|
|
456
|
+
const innerRender = renderFn;
|
|
457
|
+
const currentLayoutData = layoutData?.[i];
|
|
458
|
+
renderFn = () => {
|
|
459
|
+
const childNode = innerRender();
|
|
460
|
+
return LayoutComponent({
|
|
461
|
+
data: currentLayoutData,
|
|
462
|
+
params,
|
|
463
|
+
children: childNode
|
|
464
|
+
});
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
rootNode = renderFn();
|
|
468
|
+
} catch (e) {
|
|
469
|
+
console.error(e);
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
popHydrationNodes();
|
|
473
|
+
updateHead(head);
|
|
474
|
+
document.title = head?.title ?? document.title;
|
|
475
|
+
document.getElementById("grimoire-root")?.replaceChildren(rootNode);
|
|
476
|
+
currentPath = path;
|
|
477
|
+
restoreScrollPosition(path);
|
|
478
|
+
afterNavigateCb?.(url);
|
|
479
|
+
}
|
|
480
|
+
function initRouter(routes2, layouts2, initialDispose) {
|
|
481
|
+
routeMap = routes2;
|
|
482
|
+
layoutList = layouts2;
|
|
483
|
+
disposeCurrentPage = initialDispose ?? null;
|
|
484
|
+
currentPath = location.pathname;
|
|
485
|
+
saveScrollPosition();
|
|
486
|
+
document.addEventListener("click", handleClick);
|
|
487
|
+
window.addEventListener("popstate", () => {
|
|
488
|
+
if (location.pathname !== currentPath) {
|
|
489
|
+
saveScrollPosition();
|
|
490
|
+
currentPath = location.pathname;
|
|
491
|
+
navigate(location.pathname);
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
setupPreload();
|
|
495
|
+
}
|
|
496
|
+
function updateHead(headHtml) {
|
|
497
|
+
document.querySelectorAll("[data-grimoire-head]").forEach((el) => el.remove());
|
|
498
|
+
const parsed = headHtml ? new DOMParser().parseFromString(`<head>${headHtml}</head>`, "text/html") : null;
|
|
499
|
+
const incomingTitle = parsed?.querySelector("title");
|
|
500
|
+
document.querySelectorAll("title").forEach((el) => el.remove());
|
|
501
|
+
document.title = incomingTitle?.textContent ?? document.title;
|
|
502
|
+
if (parsed) {
|
|
503
|
+
for (const el of Array.from(parsed.head.children)) {
|
|
504
|
+
if (el.tagName === "TITLE")
|
|
505
|
+
continue;
|
|
506
|
+
el.dataset.grimoireHead = "1";
|
|
507
|
+
document.head.appendChild(el);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
function handleClick(e) {
|
|
512
|
+
const a = e.target.closest("a");
|
|
513
|
+
if (!a)
|
|
514
|
+
return;
|
|
515
|
+
const href = a.getAttribute("href");
|
|
516
|
+
if (!href)
|
|
517
|
+
return;
|
|
518
|
+
if (/^(https?:\/\/|\/\/|#|mailto:|tel:)/.test(href))
|
|
519
|
+
return;
|
|
520
|
+
e.preventDefault();
|
|
521
|
+
navigate(href);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// src/rendering/hydrate.ts
|
|
525
|
+
var stateEl = document.getElementById("__grimoire_state__");
|
|
526
|
+
var initialDispose;
|
|
527
|
+
if (stateEl) {
|
|
528
|
+
const state = JSON.parse(stateEl.textContent);
|
|
529
|
+
const Page = routes[state.pattern];
|
|
530
|
+
if (Page) {
|
|
531
|
+
const slot = document.getElementById("grimoire-root");
|
|
532
|
+
if (slot) {
|
|
533
|
+
const matchedLayouts = layouts.filter((l) => l.path === "/" || state.pattern === l.path || state.pattern.startsWith(l.path + "/")).sort((a, b) => a.path.length - b.path.length);
|
|
534
|
+
const hasLayouts = matchedLayouts.length > 0;
|
|
535
|
+
if (hasLayouts) {
|
|
536
|
+
slot.replaceChildren();
|
|
537
|
+
}
|
|
538
|
+
const ssrClones = hasLayouts ? [] : Array.from(slot.childNodes).map((n) => n.cloneNode(true));
|
|
539
|
+
pushHydrationNodes(hasLayouts ? [] : Array.from(slot.childNodes));
|
|
540
|
+
try {
|
|
541
|
+
initialDispose = withEffectScope(() => {
|
|
542
|
+
try {
|
|
543
|
+
let renderFn = () => {
|
|
544
|
+
const pageDiv = claim(getHydrationNodes(), "div");
|
|
545
|
+
pageDiv.id = "grimoire-page";
|
|
546
|
+
if (pageDiv.childNodes.length > 0) {
|
|
547
|
+
pushHydrationNodes(Array.from(pageDiv.childNodes));
|
|
548
|
+
const pageNode = Page({ data: state.data, params: state.params });
|
|
549
|
+
popHydrationNodes();
|
|
550
|
+
insert(pageDiv, pageNode);
|
|
551
|
+
} else {
|
|
552
|
+
const pageNode = Page({ data: state.data, params: state.params });
|
|
553
|
+
insert(pageDiv, pageNode);
|
|
554
|
+
}
|
|
555
|
+
return pageDiv;
|
|
556
|
+
};
|
|
557
|
+
for (let i = matchedLayouts.length - 1;i >= 0; i--) {
|
|
558
|
+
const LayoutComponent = matchedLayouts[i].component;
|
|
559
|
+
const innerRender = renderFn;
|
|
560
|
+
const layoutData = state.layoutData?.[i];
|
|
561
|
+
renderFn = () => {
|
|
562
|
+
const childNode = innerRender();
|
|
563
|
+
return LayoutComponent({
|
|
564
|
+
data: layoutData,
|
|
565
|
+
params: state.params,
|
|
566
|
+
children: childNode
|
|
567
|
+
});
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
const rootNode = renderFn();
|
|
571
|
+
insert(slot, rootNode);
|
|
572
|
+
} finally {
|
|
573
|
+
popHydrationNodes();
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
} catch (e) {
|
|
577
|
+
console.warn("[grimoire] hydration error:", e);
|
|
578
|
+
}
|
|
579
|
+
if (!slot.hasChildNodes() && ssrClones.length > 0) {
|
|
580
|
+
slot.replaceChildren(...ssrClones);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
initRouter(routes, layouts, initialDispose);
|