@solidjs/web 2.0.0-beta.29 → 2.0.0-beta.30
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/dev.cjs +329 -2
- package/dist/dev.js +329 -3
- package/dist/server.cjs +451 -20
- package/dist/server.js +451 -21
- package/dist/web.cjs +325 -2
- package/dist/web.js +325 -3
- package/frames/dist/client.cjs +177 -34
- package/frames/dist/client.dev.cjs +177 -34
- package/frames/dist/client.dev.js +178 -35
- package/frames/dist/client.js +178 -35
- package/frames/dist/server.cjs +405 -17
- package/frames/dist/server.js +405 -17
- package/package.json +3 -3
- package/types/client.d.ts +20 -0
- package/types/frames/frame-client.d.ts +25 -0
- package/types/frames/frame-transport.d.ts +26 -0
- package/types/index.d.ts +1 -22
- package/types/server.d.ts +50 -0
- package/types-cjs/client.d.cts +20 -0
- package/types-cjs/frames/frame-client.d.cts +25 -0
- package/types-cjs/frames/frame-transport.d.cts +26 -0
- package/types-cjs/index.d.cts +1 -22
- package/types-cjs/server.d.cts +50 -0
package/dist/server.cjs
CHANGED
|
@@ -101,6 +101,90 @@ function getLocalHeaderScript(id) {
|
|
|
101
101
|
}
|
|
102
102
|
seroval.Feature.RegExp;
|
|
103
103
|
|
|
104
|
+
const HEAD_ELIGIBLE_TAGS = new Set(["title", "meta", "link", "style", "script", "base"]);
|
|
105
|
+
const HEAD_ATTR_NAME = /^[a-zA-Z_][a-zA-Z0-9_:.-]*$/;
|
|
106
|
+
const RESOURCE_LINK_RELS = new Set(["preload", "modulepreload", "prefetch", "preconnect", "dns-prefetch", "icon", "stylesheet"]);
|
|
107
|
+
const RESOURCE_QUALIFIERS = ["as", "crossorigin", "type", "media", "imagesrcset", "imagesizes"];
|
|
108
|
+
const STYLESHEET_FETCH_META = new Set(["crossorigin", "integrity", "referrerpolicy", "fetchpriority"]);
|
|
109
|
+
function evalHeadValue(v) {
|
|
110
|
+
return typeof v === "function" ? v() : v;
|
|
111
|
+
}
|
|
112
|
+
function evalHeadProps(props, presets) {
|
|
113
|
+
const out = {};
|
|
114
|
+
for (const name in props) out[name] = presets && name in presets ? presets[name] : evalHeadValue(props[name]);
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
function classifyHeadTag(desc) {
|
|
118
|
+
const tag = desc.tag;
|
|
119
|
+
if (tag === "link") {
|
|
120
|
+
const rel = evalHeadValue(desc.props && desc.props.rel);
|
|
121
|
+
return {
|
|
122
|
+
resource: RESOURCE_LINK_RELS.has(rel),
|
|
123
|
+
rel
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (tag === "style") return {
|
|
127
|
+
resource: !!(desc.props && "href" in desc.props)
|
|
128
|
+
};
|
|
129
|
+
if (tag === "script") return {
|
|
130
|
+
resource: !!(desc.props && "src" in desc.props)
|
|
131
|
+
};
|
|
132
|
+
return {
|
|
133
|
+
resource: false
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function resourceIdentity(tag, props) {
|
|
137
|
+
let id = "res:" + tag + ":" + (props.rel || "") + ":" + (props.href || props.src || "");
|
|
138
|
+
for (let i = 0; i < RESOURCE_QUALIFIERS.length; i++) {
|
|
139
|
+
const q = RESOURCE_QUALIFIERS[i];
|
|
140
|
+
if (props[q] != null) id += ":" + q + "=" + props[q];
|
|
141
|
+
}
|
|
142
|
+
return id;
|
|
143
|
+
}
|
|
144
|
+
function replaceableIdentity(tag, props, key, unique) {
|
|
145
|
+
if (tag === "title") return "title";
|
|
146
|
+
if (tag === "base") return "base";
|
|
147
|
+
if (tag === "meta" && props.charset != null) return "charset";
|
|
148
|
+
if (key != null) return tag + ":key:" + key;
|
|
149
|
+
if (tag === "meta") {
|
|
150
|
+
if (props.name != null) return "meta:name:" + props.name;
|
|
151
|
+
if (props.property != null) return "meta:property:" + props.property;
|
|
152
|
+
if (props["http-equiv"] != null) return "meta:http-equiv:" + props["http-equiv"];
|
|
153
|
+
return unique;
|
|
154
|
+
}
|
|
155
|
+
if (tag === "link") return "link:" + (props.rel || "") + ":" + (props.href || "");
|
|
156
|
+
return unique;
|
|
157
|
+
}
|
|
158
|
+
function resolveHead(groups) {
|
|
159
|
+
const winners = new Map();
|
|
160
|
+
const sorted = groups.slice().sort((a, b) => a.seq - b.seq);
|
|
161
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
162
|
+
const group = sorted[i];
|
|
163
|
+
const byIdentity = new Map();
|
|
164
|
+
for (let j = 0; j < group.tags.length; j++) {
|
|
165
|
+
const t = group.tags[j];
|
|
166
|
+
let list = byIdentity.get(t.identity);
|
|
167
|
+
if (!list) byIdentity.set(t.identity, list = []);
|
|
168
|
+
list.push(t);
|
|
169
|
+
}
|
|
170
|
+
for (const [identity, tags] of byIdentity) {
|
|
171
|
+
if (identity === "title") {
|
|
172
|
+
if (tags.length > 1) console.warn("Multiple <title> tags in one head group; the last one wins.");
|
|
173
|
+
winners.set(identity, {
|
|
174
|
+
seq: group.seq,
|
|
175
|
+
tags: [tags[tags.length - 1]]
|
|
176
|
+
});
|
|
177
|
+
} else {
|
|
178
|
+
winners.set(identity, {
|
|
179
|
+
seq: group.seq,
|
|
180
|
+
tags
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return winners;
|
|
186
|
+
}
|
|
187
|
+
|
|
104
188
|
function joinAssetPath(base, file) {
|
|
105
189
|
if (/^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(file)) return file;
|
|
106
190
|
if (typeof base !== "string" || !base) base = "/";
|
|
@@ -242,14 +326,274 @@ function applyAssetTracking(context, tracking, manifest, noScripts) {
|
|
|
242
326
|
context.resolveAssetsSync = resolve;
|
|
243
327
|
}
|
|
244
328
|
}
|
|
329
|
+
function isCssUrl(url) {
|
|
330
|
+
const q = url.search(/[?#]/);
|
|
331
|
+
return (q === -1 ? url : url.slice(0, q)).endsWith(".css");
|
|
332
|
+
}
|
|
333
|
+
function createHeadRegistry() {
|
|
334
|
+
return {
|
|
335
|
+
pending: [],
|
|
336
|
+
committed: [],
|
|
337
|
+
seq: 0,
|
|
338
|
+
uniq: 0,
|
|
339
|
+
resources: new Set(),
|
|
340
|
+
eagerHtml: "",
|
|
341
|
+
flushed: null,
|
|
342
|
+
shellFlushed: false
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function registerHeadTags(registry, context, tracking, emitResource, nonce, tags) {
|
|
346
|
+
const boundary = context._currentBoundaryId || "";
|
|
347
|
+
let replaceable = null;
|
|
348
|
+
for (let i = 0; i < tags.length; i++) {
|
|
349
|
+
const desc = tags[i];
|
|
350
|
+
if (!desc || !HEAD_ELIGIBLE_TAGS.has(desc.tag)) {
|
|
351
|
+
console.warn(`useHead: ignoring non-head tag`, desc);
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
const cls = classifyHeadTag(desc);
|
|
355
|
+
if (cls.resource) {
|
|
356
|
+
emitHeadResource(registry, context, tracking, emitResource, nonce, desc, cls.rel);
|
|
357
|
+
} else {
|
|
358
|
+
(replaceable || (replaceable = [])).push(cls.rel !== undefined ? {
|
|
359
|
+
tag: desc.tag,
|
|
360
|
+
props: desc.props,
|
|
361
|
+
key: desc.key,
|
|
362
|
+
rel: cls.rel
|
|
363
|
+
} : desc);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (replaceable) registry.pending.push({
|
|
367
|
+
boundary,
|
|
368
|
+
tags: replaceable
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
function emitHeadResource(registry, context, tracking, emitResource, nonce, desc, rel) {
|
|
372
|
+
let props;
|
|
373
|
+
try {
|
|
374
|
+
props = evalHeadProps(desc.props || {}, rel !== undefined ? {
|
|
375
|
+
rel
|
|
376
|
+
} : undefined);
|
|
377
|
+
} catch (err) {
|
|
378
|
+
console.warn(`useHead: error evaluating resource tag props`, err);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const identity = resourceIdentity(desc.tag, props);
|
|
382
|
+
if (registry.resources.has(identity)) return;
|
|
383
|
+
registry.resources.add(identity);
|
|
384
|
+
if (desc.tag === "link" && (rel === "stylesheet" || rel === "modulepreload")) {
|
|
385
|
+
let plain = true;
|
|
386
|
+
let gateable = rel === "stylesheet";
|
|
387
|
+
for (const name in props) {
|
|
388
|
+
if (name === "rel" || name === "href") continue;
|
|
389
|
+
plain = false;
|
|
390
|
+
if (!STYLESHEET_FETCH_META.has(name)) gateable = false;
|
|
391
|
+
}
|
|
392
|
+
if (plain && props.href != null) {
|
|
393
|
+
const isCss = isCssUrl(props.href);
|
|
394
|
+
if (rel === "stylesheet" ? isCss : !isCss) {
|
|
395
|
+
context.registerAsset(rel === "stylesheet" ? "style" : "module", props.href);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (gateable && props.href != null) {
|
|
400
|
+
const attrHtml = renderHeadAttrHtml(props);
|
|
401
|
+
const entry = {
|
|
402
|
+
href: props.href,
|
|
403
|
+
attrHtml,
|
|
404
|
+
attrs: headAttrRecord(props)
|
|
405
|
+
};
|
|
406
|
+
if (tracking.currentBoundaryId) {
|
|
407
|
+
let styles = tracking.boundaryStyles.get(tracking.currentBoundaryId);
|
|
408
|
+
if (!styles) tracking.boundaryStyles.set(tracking.currentBoundaryId, styles = new Set());
|
|
409
|
+
styles.add(entry);
|
|
410
|
+
}
|
|
411
|
+
const markup = `<link${attrHtml}>`;
|
|
412
|
+
if (emitResource) emitResource(markup, entry);else {
|
|
413
|
+
registry.eagerHtml += markup;
|
|
414
|
+
entry.emitted = true;
|
|
415
|
+
}
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
const url = props.href || props.src;
|
|
420
|
+
if (url != null && tracking.emittedAssets.has(url)) return;
|
|
421
|
+
const markup = renderHeadTagMarkup(desc.tag, props, null, nonce);
|
|
422
|
+
if (emitResource) emitResource(markup);else registry.eagerHtml += markup;
|
|
423
|
+
}
|
|
424
|
+
function commitHeadBoundary(registry, boundary, isPendingFragment) {
|
|
425
|
+
const keep = [];
|
|
426
|
+
const groups = [];
|
|
427
|
+
for (let i = 0; i < registry.pending.length; i++) {
|
|
428
|
+
const reg = registry.pending[i];
|
|
429
|
+
const mine = boundary === "" ? !(isPendingFragment && reg.boundary !== "" && isPendingFragment(reg.boundary)) : reg.boundary === boundary;
|
|
430
|
+
if (!mine) {
|
|
431
|
+
keep.push(reg);
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
const tags = [];
|
|
435
|
+
for (let j = 0; j < reg.tags.length; j++) {
|
|
436
|
+
const desc = reg.tags[j];
|
|
437
|
+
let props, key;
|
|
438
|
+
try {
|
|
439
|
+
props = evalHeadProps(desc.props || {}, desc.rel !== undefined ? {
|
|
440
|
+
rel: desc.rel
|
|
441
|
+
} : undefined);
|
|
442
|
+
key = evalHeadValue(desc.key);
|
|
443
|
+
} catch (err) {
|
|
444
|
+
console.warn(`useHead: error evaluating tag props`, err);
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
const identity = replaceableIdentity(desc.tag, props, key, "u:" + registry.uniq++);
|
|
448
|
+
if ((identity === "base" || identity === "charset") && registry.shellFlushed) {
|
|
449
|
+
console.warn(`useHead: <${desc.tag}> (${identity}) registered after shell flush is ignored`);
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
tags.push({
|
|
453
|
+
tag: desc.tag,
|
|
454
|
+
props,
|
|
455
|
+
identity
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
if (tags.length) groups.push({
|
|
459
|
+
seq: registry.seq++,
|
|
460
|
+
tags
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
registry.pending = keep;
|
|
464
|
+
for (let i = 0; i < groups.length; i++) registry.committed.push(groups[i]);
|
|
465
|
+
return groups;
|
|
466
|
+
}
|
|
467
|
+
function adoptHeadBoundary(registry, childKey, parentKey) {
|
|
468
|
+
for (let i = 0; i < registry.pending.length; i++) {
|
|
469
|
+
if (registry.pending[i].boundary === childKey) registry.pending[i].boundary = parentKey;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
function dropHeadBoundary(registry, boundary) {
|
|
473
|
+
registry.pending = registry.pending.filter(reg => reg.boundary !== boundary);
|
|
474
|
+
}
|
|
475
|
+
function headGroupSignature(winner) {
|
|
476
|
+
let sig = "" + winner.seq;
|
|
477
|
+
for (let i = 0; i < winner.tags.length; i++) {
|
|
478
|
+
const t = winner.tags[i];
|
|
479
|
+
sig += "|" + t.tag + JSON.stringify(t.props);
|
|
480
|
+
}
|
|
481
|
+
return sig;
|
|
482
|
+
}
|
|
483
|
+
function renderShellHead(registry, nonce, isPendingFragment) {
|
|
484
|
+
commitHeadBoundary(registry, "", isPendingFragment);
|
|
485
|
+
registry.shellFlushed = true;
|
|
486
|
+
const winners = resolveHead(registry.committed);
|
|
487
|
+
registry.flushed = new Map();
|
|
488
|
+
let prelude = "";
|
|
489
|
+
let links = "";
|
|
490
|
+
let metas = "";
|
|
491
|
+
let others = "";
|
|
492
|
+
let scripts = "";
|
|
493
|
+
for (const [identity, winner] of winners) {
|
|
494
|
+
registry.flushed.set(identity, headGroupSignature(winner));
|
|
495
|
+
for (let i = 0; i < winner.tags.length; i++) {
|
|
496
|
+
const t = winner.tags[i];
|
|
497
|
+
const markup = renderHeadTagMarkup(t.tag, t.props, identity, nonce);
|
|
498
|
+
if (identity === "charset" || identity === "base") prelude += markup;else if (t.tag === "link" || t.tag === "style") links += markup;else if (t.tag === "meta") metas += markup;else if (t.tag === "script") scripts += markup;else others += markup;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return {
|
|
502
|
+
prelude,
|
|
503
|
+
html: registry.eagerHtml + links + metas + others + scripts
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
function flushHeadFragment(registry, boundary) {
|
|
507
|
+
const groups = commitHeadBoundary(registry, boundary);
|
|
508
|
+
if (!groups.length) return null;
|
|
509
|
+
const winners = resolveHead(registry.committed);
|
|
510
|
+
const affected = new Set();
|
|
511
|
+
for (let i = 0; i < groups.length; i++) for (let j = 0; j < groups[i].tags.length; j++) affected.add(groups[i].tags[j].identity);
|
|
512
|
+
const ops = [];
|
|
513
|
+
for (const identity of affected) {
|
|
514
|
+
const winner = winners.get(identity);
|
|
515
|
+
const sig = headGroupSignature(winner);
|
|
516
|
+
if (registry.flushed.get(identity) === sig) continue;
|
|
517
|
+
const existed = registry.flushed.has(identity);
|
|
518
|
+
registry.flushed.set(identity, sig);
|
|
519
|
+
if (identity === "title") {
|
|
520
|
+
const children = winner.tags[0].props.children;
|
|
521
|
+
ops.push(["t", children == null ? "" : String(children)]);
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
if (existed) ops.push(["r", identity]);
|
|
525
|
+
for (let i = 0; i < winner.tags.length; i++) {
|
|
526
|
+
const t = winner.tags[i];
|
|
527
|
+
const attrs = {};
|
|
528
|
+
for (const name in t.props) {
|
|
529
|
+
if (name === "children" || name === "ref" || name.slice(0, 2) === "on") continue;
|
|
530
|
+
if (!HEAD_ATTR_NAME.test(name)) {
|
|
531
|
+
console.warn(`useHead: ignoring invalid attribute name "${name}"`);
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
const v = t.props[name];
|
|
535
|
+
if (v == null || v === false) continue;
|
|
536
|
+
attrs[name] = v === true ? "" : String(v);
|
|
537
|
+
}
|
|
538
|
+
const children = t.props.children;
|
|
539
|
+
ops.push(["a", identity, t.tag, attrs, children == null ? null : String(children)]);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return ops.length ? ops : null;
|
|
543
|
+
}
|
|
544
|
+
function renderHeadAttrHtml(props) {
|
|
545
|
+
let attrs = "";
|
|
546
|
+
for (const name in props) {
|
|
547
|
+
if (name === "children" || name === "ref" || name.slice(0, 2) === "on") continue;
|
|
548
|
+
if (!HEAD_ATTR_NAME.test(name)) {
|
|
549
|
+
console.warn(`useHead: ignoring invalid attribute name "${name}"`);
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
const v = props[name];
|
|
553
|
+
if (v == null || v === false) continue;
|
|
554
|
+
attrs += v === true ? ` ${name}` : ` ${name}="${escape(String(v), true)}"`;
|
|
555
|
+
}
|
|
556
|
+
return attrs;
|
|
557
|
+
}
|
|
558
|
+
function headAttrRecord(props, skipRelHref) {
|
|
559
|
+
let attrs = null;
|
|
560
|
+
for (const name in props) {
|
|
561
|
+
if (name === "children" || name === "ref" || name.slice(0, 2) === "on") continue;
|
|
562
|
+
if ((name === "rel" || name === "href")) continue;
|
|
563
|
+
if (!HEAD_ATTR_NAME.test(name)) continue;
|
|
564
|
+
const v = props[name];
|
|
565
|
+
if (v == null || v === false) continue;
|
|
566
|
+
(attrs || (attrs = {}))[name] = v === true ? "" : String(v);
|
|
567
|
+
}
|
|
568
|
+
return attrs;
|
|
569
|
+
}
|
|
570
|
+
function renderHeadTagMarkup(tag, props, identity, nonce) {
|
|
571
|
+
let attrs = renderHeadAttrHtml(props);
|
|
572
|
+
if (identity != null) attrs += ` data-dh="${escape(identity, true)}"`;
|
|
573
|
+
if (nonce && (tag === "script" || tag === "style")) attrs += ` nonce="${nonce}"`;
|
|
574
|
+
if (tag === "meta" || tag === "link" || tag === "base") return `<${tag}${attrs}>`;
|
|
575
|
+
let body = props.children == null ? "" : String(props.children);
|
|
576
|
+
if (tag === "script") body = body.replace(/<\/(script)/gi, "<\\/$1");else if (tag === "style") body = escapeStyleContent(body);else body = escape(body);
|
|
577
|
+
return `<${tag}${attrs}>${body}</${tag}>`;
|
|
578
|
+
}
|
|
579
|
+
function useHead(tags) {
|
|
580
|
+
const ctx = solidJs.sharedConfig.context;
|
|
581
|
+
if (!ctx || !ctx.registerHeadTags) {
|
|
582
|
+
console.warn("useHead() called outside of a server render; registration ignored.");
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
ctx.registerHeadTags(Array.isArray(tags) ? tags : [tags]);
|
|
586
|
+
}
|
|
245
587
|
const VOID_ELEMENTS = /^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i;
|
|
246
|
-
const REPLACE_SCRIPT = `function $df(e,n,o,t){if(!(n=document.getElementById(e)))return 0;if(!(o=document.getElementById("pl-"+e)))return(_$HY.dq=_$HY.dq||{})[e]=1,0;for(;o&&(8!==o.nodeType||o.nodeValue!=="pl-"+e);)t=o.nextSibling,o.remove(),o=t;t=o.parentNode,
|
|
588
|
+
const REPLACE_SCRIPT = `function $df(e){return _$HY.f?_$HY.f(e):$dfr(e)}function $dfr(e,n,o,t){if(!(n=document.getElementById(e)))return 0;if(!(o=document.getElementById("pl-"+e)))return(_$HY.dq=_$HY.dq||{})[e]=1,0;for(;o&&(8!==o.nodeType||o.nodeValue!=="pl-"+e);)t=o.nextSibling,o.remove(),o=t;t=o.parentNode,o.replaceWith(n.content),n.remove(),_$HY.fe(e,t),_$HY.hp&&_$HY.hp[e]&&($dh(_$HY.hp[e]),delete _$HY.hp[e]),$dfd();return 1}function $dfl(e,o,n){if(!(o=document.getElementById("pl-"+e)))return(_$HY.dlq=_$HY.dlq||{})[e]=1,0;if(o._$fl)return 1;for(n=o.nextSibling;n;){if(8===n.nodeType&&n.nodeValue==="pl-"+e){o.parentNode&&o.parentNode.insertBefore(o.content.cloneNode(!0),n),o._$fl=1,$dfd();return 1}n=n.nextSibling}return 0}function $dflj(e,i){for(i=0;i<e.length;i++)$dfl(e[i])}function $dfd(e,i){if(e=_$HY.dq){_$HY.dq=0;for(i in e)$df(i)}if(e=_$HY.dlq){_$HY.dlq=0;for(i in e)$dfl(i)}}function $dfs(e,c,d){(_$HY.sc=_$HY.sc||{})[e]=c,d&&((_$HY.sd=_$HY.sd||{})[e]=1)}function $dfg(e,g,i,k){if(!(g=_$HY.sg&&_$HY.sg[e]))return;for(i=0;i<g.length;i++)if(_$HY.sc&&_$HY.sc[g[i]]>0)return;for(i=0;i<g.length;i++)k=g[i],delete _$HY.sg[k],$df(k)}function $dfc(e){if(--_$HY.sc[e]<=0){delete _$HY.sc[e],_$HY.sg&&_$HY.sg[e]?$dfg(e):!(_$HY.sd&&_$HY.sd[e])&&$df(e);_$HY.sd&&delete _$HY.sd[e]}}function $dfj(e,i,n){for(i=0;i<e.length;i++)if(_$HY.sc&&_$HY.sc[e[i]]>0){for(n=0;n<e.length;n++)(_$HY.sg=_$HY.sg||{})[e[n]]=e;return}for(i=0;i<e.length;i++)$df(e[i])}`;
|
|
589
|
+
const HEAD_SCRIPT = `function $dha(o,i,e,n){for(i=0;i<o.length;i++)e=o[i],"t"==e[0]?((n=document.querySelector("title"))||(n=document.createElement("title"),document.head.appendChild(n)),n.textContent=e[1],n.setAttribute("data-dh","title")):"r"==e[0]?$dhr(e[1]):(n=document.createElement(e[2]),Object.keys(e[3]).forEach(function(a){n.setAttribute(a,e[3][a])}),null!=e[4]&&(n.textContent=e[4]),n.setAttribute("data-dh",e[1]),document.head.appendChild(n))}function $dhr(v,l,i){for(l=document.head.querySelectorAll("[data-dh]"),i=0;i<l.length;i++)l[i].getAttribute("data-dh")==v&&l[i].remove()}function $dh(o){_$HY.h?_$HY.h(o):$dha(o)}`;
|
|
247
590
|
function renderToString(code, options = {}) {
|
|
248
591
|
const {
|
|
249
592
|
renderId = "",
|
|
250
593
|
nonce,
|
|
251
594
|
noScripts,
|
|
252
|
-
manifest
|
|
595
|
+
manifest,
|
|
596
|
+
onHead
|
|
253
597
|
} = options;
|
|
254
598
|
let scripts = "";
|
|
255
599
|
const serializer = createHydrationSerializer({
|
|
@@ -265,12 +609,16 @@ function renderToString(code, options = {}) {
|
|
|
265
609
|
onError: options.onError
|
|
266
610
|
});
|
|
267
611
|
const tracking = createAssetTracking();
|
|
612
|
+
const headRegistry = createHeadRegistry();
|
|
268
613
|
solidJs.sharedConfig.context = {
|
|
269
614
|
assets: [],
|
|
270
615
|
nonce,
|
|
271
616
|
escape: escape,
|
|
272
617
|
resolve: resolveSSRNode,
|
|
273
618
|
ssr: ssr,
|
|
619
|
+
registerHeadTags(tags) {
|
|
620
|
+
registerHeadTags(headRegistry, solidJs.sharedConfig.context, tracking, null, nonce, tags);
|
|
621
|
+
},
|
|
274
622
|
serialize(id, p) {
|
|
275
623
|
if (solidJs.sharedConfig.context.noHydrate) return;
|
|
276
624
|
if (p != null && typeof p === "object" && (typeof p.then === "function" || typeof p[Symbol.asyncIterator] === "function")) {
|
|
@@ -306,7 +654,8 @@ function renderToString(code, options = {}) {
|
|
|
306
654
|
solidJs.sharedConfig.context.noHydrate = true;
|
|
307
655
|
serializer.close();
|
|
308
656
|
const assetsHtml = resolveAssetsHtml(solidJs.sharedConfig.context.assets);
|
|
309
|
-
|
|
657
|
+
const head = renderShellHead(headRegistry, nonce, null);
|
|
658
|
+
return assembleDocument(html, assetsHtml, tracking.emittedAssets, tracking.inlineStyles, scripts.length ? scripts : "", nonce, head, onHead);
|
|
310
659
|
}
|
|
311
660
|
function renderToStream(code, options = {}) {
|
|
312
661
|
let {
|
|
@@ -315,7 +664,8 @@ function renderToStream(code, options = {}) {
|
|
|
315
664
|
onCompleteAll,
|
|
316
665
|
renderId = "",
|
|
317
666
|
noScripts,
|
|
318
|
-
manifest
|
|
667
|
+
manifest,
|
|
668
|
+
onHead
|
|
319
669
|
} = options;
|
|
320
670
|
let dispose;
|
|
321
671
|
const blockingPromises = new Set();
|
|
@@ -357,8 +707,8 @@ function renderToStream(code, options = {}) {
|
|
|
357
707
|
if (styles.links.length) {
|
|
358
708
|
emitTask(`$dfs("${key}",${styles.links.length},${deferActivation ? 1 : 0})`);
|
|
359
709
|
writeTasks();
|
|
360
|
-
for (const
|
|
361
|
-
buffer.write(`<link rel="stylesheet" href="${
|
|
710
|
+
for (const entry of styles.links) {
|
|
711
|
+
buffer.write(typeof entry === "string" ? `<link rel="stylesheet" href="${entry}" onload="$dfc('${key}')" onerror="$dfc('${key}')">` : `<link${entry.attrHtml} onload="$dfc('${key}')" onerror="$dfc('${key}')">`);
|
|
362
712
|
}
|
|
363
713
|
buffer.write(`<template id="${key}">${value}</template>`);
|
|
364
714
|
} else {
|
|
@@ -376,10 +726,12 @@ function renderToStream(code, options = {}) {
|
|
|
376
726
|
buffer.write(`<link rel="modulepreload" href="${value}">`);
|
|
377
727
|
} else if (type === "inline-style") {
|
|
378
728
|
buffer.write(renderInlineStyle(value, nonce));
|
|
729
|
+
} else if (type === "head-tag") {
|
|
730
|
+
buffer.write(value);
|
|
379
731
|
}
|
|
380
732
|
},
|
|
381
733
|
shell(shellHtml, meta) {
|
|
382
|
-
buffer.write(assembleDocument(shellHtml, meta.assets, meta.preloads, meta.inlineStyles, meta.tasks.length ? meta.tasks : "", nonce));
|
|
734
|
+
buffer.write(assembleDocument(shellHtml, meta.assets, meta.preloads, meta.inlineStyles, meta.tasks.length ? meta.tasks : "", nonce, meta.head, onHead));
|
|
383
735
|
},
|
|
384
736
|
...options.sink
|
|
385
737
|
};
|
|
@@ -450,10 +802,28 @@ function renderToStream(code, options = {}) {
|
|
|
450
802
|
}
|
|
451
803
|
};
|
|
452
804
|
const tracking = createAssetTracking();
|
|
805
|
+
const headRegistry = createHeadRegistry();
|
|
806
|
+
let headScriptFlushed = false;
|
|
807
|
+
const emitHeadOps = (key, ops) => {
|
|
808
|
+
const payload = JSON.stringify(ops).replace(/</g, "\\u003C");
|
|
809
|
+
emitTask(`${!headScriptFlushed ? HEAD_SCRIPT : ""}(_$HY.hp=_$HY.hp||{})[${JSON.stringify(key)}]=${payload}`);
|
|
810
|
+
headScriptFlushed = true;
|
|
811
|
+
};
|
|
453
812
|
solidJs.sharedConfig.context = context = {
|
|
454
813
|
async: true,
|
|
455
814
|
assets: [],
|
|
456
815
|
nonce,
|
|
816
|
+
registerHeadTags(tags) {
|
|
817
|
+
registerHeadTags(headRegistry, context, tracking,
|
|
818
|
+
(markup, gateEntry) => {
|
|
819
|
+
if (!firstFlushed) {
|
|
820
|
+
headRegistry.eagerHtml += markup;
|
|
821
|
+
if (gateEntry) gateEntry.emitted = true;
|
|
822
|
+
} else if (!gateEntry || !tracking.currentBoundaryId) {
|
|
823
|
+
sink.asset("head-tag", markup);
|
|
824
|
+
}
|
|
825
|
+
}, nonce, tags);
|
|
826
|
+
},
|
|
457
827
|
registerAsset(type, value) {
|
|
458
828
|
if (type === "inline-style") {
|
|
459
829
|
const entry = tracking.registerInlineStyle(value);
|
|
@@ -544,10 +914,12 @@ function renderToStream(code, options = {}) {
|
|
|
544
914
|
parent.children[key] = value !== undefined ? value : "";
|
|
545
915
|
serializeFragmentAssets(key, tracking.boundaryModules, context);
|
|
546
916
|
propagateBoundaryStyles(key, parentKey, tracking);
|
|
917
|
+
adoptHeadBoundary(headRegistry, key, parentKey);
|
|
547
918
|
item.resolve();
|
|
548
919
|
return;
|
|
549
920
|
}
|
|
550
921
|
if (!completed) {
|
|
922
|
+
if (error) dropHeadBoundary(headRegistry, key);
|
|
551
923
|
if (!firstFlushed) {
|
|
552
924
|
queue(() => html = replacePlaceholder(html, key, value !== undefined ? value : ""));
|
|
553
925
|
serializeFragmentAssets(key, tracking.boundaryModules, context);
|
|
@@ -555,6 +927,8 @@ function renderToStream(code, options = {}) {
|
|
|
555
927
|
} else {
|
|
556
928
|
serializeFragmentAssets(key, tracking.boundaryModules, context);
|
|
557
929
|
const styles = collectStreamStyles(key, tracking, headStyles);
|
|
930
|
+
const headOps = error ? null : flushHeadFragment(headRegistry, key);
|
|
931
|
+
if (headOps) emitHeadOps(key, headOps);
|
|
558
932
|
sink.fragment(key, value !== undefined ? value : " ", {
|
|
559
933
|
styles,
|
|
560
934
|
revealGroup,
|
|
@@ -642,14 +1016,16 @@ function renderToStream(code, options = {}) {
|
|
|
642
1016
|
const assetsHtml = resolveAssetsHtml(context.assets);
|
|
643
1017
|
headStyles = new Set();
|
|
644
1018
|
for (const url of tracking.emittedAssets) {
|
|
645
|
-
if (url
|
|
1019
|
+
if (isCssUrl(url)) headStyles.add(url);
|
|
646
1020
|
}
|
|
647
1021
|
serializeRootAssets();
|
|
1022
|
+
const head = renderShellHead(headRegistry, nonce, k => registry.has(k));
|
|
648
1023
|
sink.shell(html, {
|
|
649
1024
|
assets: assetsHtml,
|
|
650
1025
|
preloads: tracking.emittedAssets,
|
|
651
1026
|
inlineStyles: tracking.inlineStyles,
|
|
652
|
-
tasks
|
|
1027
|
+
tasks,
|
|
1028
|
+
head
|
|
653
1029
|
});
|
|
654
1030
|
tasks = "";
|
|
655
1031
|
onCompleteShell && onCompleteShell({
|
|
@@ -1149,23 +1525,42 @@ function resolveAssetsHtml(assets) {
|
|
|
1149
1525
|
for (let i = 0, len = assets.length; i < len; i++) out += assets[i]();
|
|
1150
1526
|
return out;
|
|
1151
1527
|
}
|
|
1152
|
-
function assembleDocument(html, assetsHtml, emittedAssets, inlineStyles, scripts, nonce) {
|
|
1528
|
+
function assembleDocument(html, assetsHtml, emittedAssets, inlineStyles, scripts, nonce, headTags, onHead) {
|
|
1153
1529
|
const scriptTag = scripts ? `<script${nonce ? ` nonce="${nonce}"` : ""}>${scripts}</script>` : "";
|
|
1154
|
-
|
|
1530
|
+
const headTagsHtml = headTags ? headTags.html : "";
|
|
1531
|
+
const headPrelude = headTags ? headTags.prelude : "";
|
|
1532
|
+
if (!onHead && !assetsHtml && !headTagsHtml && !headPrelude && !(emittedAssets && emittedAssets.size) && !(inlineStyles && inlineStyles.size)) {
|
|
1155
1533
|
if (!scriptTag) return html;
|
|
1156
1534
|
const xs = html.indexOf("<!--xs-->");
|
|
1157
1535
|
return xs === -1 ? html + scriptTag : html.slice(0, xs) + scriptTag + html.slice(xs);
|
|
1158
1536
|
}
|
|
1537
|
+
if (headPrelude) {
|
|
1538
|
+
const open = html.match(/<head(?:\s[^>]*)?>/);
|
|
1539
|
+
if (open) {
|
|
1540
|
+
const at = open.index + open[0].length;
|
|
1541
|
+
html = html.slice(0, at) + headPrelude + html.slice(at);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1159
1544
|
const headIdx = html.indexOf("</head>");
|
|
1160
1545
|
if (headIdx === -1) {
|
|
1546
|
+
if (onHead) {
|
|
1547
|
+
onHead(headPrelude + headTagsHtml + (assetsHtml || "") + renderHeadAssets(emittedAssets, inlineStyles, nonce));
|
|
1548
|
+
}
|
|
1161
1549
|
if (!scriptTag) return html;
|
|
1162
1550
|
const xs = html.indexOf("<!--xs-->");
|
|
1163
1551
|
return xs === -1 ? html + scriptTag : html.slice(0, xs) + scriptTag + html.slice(xs);
|
|
1164
1552
|
}
|
|
1165
|
-
|
|
1553
|
+
const head = headTagsHtml + (assetsHtml || "") + renderHeadAssets(emittedAssets, inlineStyles, nonce);
|
|
1554
|
+
if (!scriptTag) return html.slice(0, headIdx) + head + html.slice(headIdx);
|
|
1555
|
+
const xsIdx = html.indexOf("<!--xs-->");
|
|
1556
|
+
if (xsIdx === -1) return html.slice(0, headIdx) + head + html.slice(headIdx) + scriptTag;
|
|
1557
|
+
return xsIdx < headIdx ? html.slice(0, xsIdx) + scriptTag + html.slice(xsIdx, headIdx) + head + html.slice(headIdx) : html.slice(0, headIdx) + head + html.slice(headIdx, xsIdx) + scriptTag + html.slice(xsIdx);
|
|
1558
|
+
}
|
|
1559
|
+
function renderHeadAssets(emittedAssets, inlineStyles, nonce) {
|
|
1560
|
+
let head = "";
|
|
1166
1561
|
if (emittedAssets && emittedAssets.size) {
|
|
1167
1562
|
for (const url of emittedAssets) {
|
|
1168
|
-
head += url
|
|
1563
|
+
head += isCssUrl(url) ? `<link rel="stylesheet" href="${url}">` : `<link rel="modulepreload" href="${url}">`;
|
|
1169
1564
|
}
|
|
1170
1565
|
}
|
|
1171
1566
|
if (inlineStyles && inlineStyles.size) {
|
|
@@ -1175,10 +1570,7 @@ function assembleDocument(html, assetsHtml, emittedAssets, inlineStyles, scripts
|
|
|
1175
1570
|
head += renderInlineStyle(entry, nonce);
|
|
1176
1571
|
}
|
|
1177
1572
|
}
|
|
1178
|
-
|
|
1179
|
-
const xsIdx = html.indexOf("<!--xs-->");
|
|
1180
|
-
if (xsIdx === -1) return html.slice(0, headIdx) + head + html.slice(headIdx) + scriptTag;
|
|
1181
|
-
return xsIdx < headIdx ? html.slice(0, xsIdx) + scriptTag + html.slice(xsIdx, headIdx) + head + html.slice(headIdx) : html.slice(0, headIdx) + head + html.slice(headIdx, xsIdx) + scriptTag + html.slice(xsIdx);
|
|
1573
|
+
return head;
|
|
1182
1574
|
}
|
|
1183
1575
|
function serializeFragmentAssets(key, boundaryModules, context) {
|
|
1184
1576
|
const map = boundaryModules.get(key);
|
|
@@ -1208,7 +1600,12 @@ function collectStreamStyles(key, tracking, headStyles) {
|
|
|
1208
1600
|
for (const entry of styles) {
|
|
1209
1601
|
if (typeof entry === "string") {
|
|
1210
1602
|
if (!headStyles || !headStyles.has(entry)) links.push(entry);
|
|
1211
|
-
} else if (
|
|
1603
|
+
} else if (entry.emitted) {
|
|
1604
|
+
continue;
|
|
1605
|
+
} else if (entry.attrHtml !== undefined) {
|
|
1606
|
+
entry.emitted = true;
|
|
1607
|
+
links.push(entry);
|
|
1608
|
+
} else {
|
|
1212
1609
|
entry.emitted = true;
|
|
1213
1610
|
inline.push(entry);
|
|
1214
1611
|
}
|
|
@@ -1468,8 +1865,41 @@ function Portal(props) {
|
|
|
1468
1865
|
if (o?.id != null) solidJs.getNextChildId(o);
|
|
1469
1866
|
return undefined;
|
|
1470
1867
|
}
|
|
1471
|
-
function
|
|
1472
|
-
|
|
1868
|
+
function registerClientOnlyPreload(moduleUrl) {
|
|
1869
|
+
const ctx = solidJs.sharedConfig.context;
|
|
1870
|
+
if (!ctx?.registerAsset || !ctx.resolveAssets) return;
|
|
1871
|
+
const registerAsset = ctx.registerAsset;
|
|
1872
|
+
const resolve = ctx.resolveAssets;
|
|
1873
|
+
const apply = assets => {
|
|
1874
|
+
if (!assets) return;
|
|
1875
|
+
for (let i = 0; i < assets.css.length; i++) {
|
|
1876
|
+
const css = assets.css[i];
|
|
1877
|
+
if (typeof css === "string") registerAsset("style", css);else registerAsset("inline-style", css);
|
|
1878
|
+
}
|
|
1879
|
+
for (let i = 0; i < assets.js.length; i++) registerAsset("module", assets.js[i]);
|
|
1880
|
+
};
|
|
1881
|
+
const assets = resolve(moduleUrl);
|
|
1882
|
+
if (assets && typeof assets.then === "function") {
|
|
1883
|
+
const boundary = ctx._currentBoundaryId;
|
|
1884
|
+
assets.then(resolved => {
|
|
1885
|
+
const current = ctx._currentBoundaryId;
|
|
1886
|
+
ctx._currentBoundaryId = boundary;
|
|
1887
|
+
try {
|
|
1888
|
+
apply(resolved);
|
|
1889
|
+
} finally {
|
|
1890
|
+
ctx._currentBoundaryId = current;
|
|
1891
|
+
}
|
|
1892
|
+
},
|
|
1893
|
+
() => {});
|
|
1894
|
+
} else {
|
|
1895
|
+
apply(assets);
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
function clientOnly(_fn, _options = {}, moduleUrl) {
|
|
1899
|
+
return props => {
|
|
1900
|
+
if (moduleUrl) registerClientOnlyPreload(moduleUrl);
|
|
1901
|
+
return solidJs.createMemo(() => props.fallback);
|
|
1902
|
+
};
|
|
1473
1903
|
}
|
|
1474
1904
|
function httpStatus(code, text) {
|
|
1475
1905
|
const event = getRequestEvent();
|
|
@@ -1637,3 +2067,4 @@ exports.template = notSup;
|
|
|
1637
2067
|
exports.unregisterDelegatedContainer = notSup;
|
|
1638
2068
|
exports.unregisterDelegatedRoot = notSup;
|
|
1639
2069
|
exports.useAssets = useAssets;
|
|
2070
|
+
exports.useHead = useHead;
|