@solidjs/web 2.0.0-beta.15 → 2.0.0-beta.17

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/server.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createRenderEffect, createMemo, sharedConfig, createRoot, ssrHandleError, getOwner, runWithOwner, createComponent, omit, getNextChildId, createOwner } from 'solid-js';
2
- export { Errored, For, Hydration, Loading, Match, NoHydration, Repeat, Reveal, Show, Switch, createComponent, getOwner, untrack } from 'solid-js';
1
+ import { createRenderEffect, createMemo, sharedConfig, createRoot, ssrHandleError, getOwner, runWithOwner, createComponent, omit, getNextChildId, NotReadyError } from 'solid-js';
2
+ export { Errored, For, Hydration, Loading, Match, NoHydration, Repeat, Reveal, Show, Switch, createComponent, getOwner, merge as mergeProps, ssrScope as scope, untrack } from 'solid-js';
3
3
  import { Serializer, Feature, getCrossReferenceHeader } from 'seroval';
4
4
  import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
5
5
 
@@ -59,9 +59,9 @@ const syncOptions = {
59
59
  sync: true
60
60
  };
61
61
  const effect = (fn, effectFn, options) => createRenderEffect(fn, effectFn, options ? {
62
- transparent: true,
63
62
  sync: true,
64
- ...options
63
+ ...options,
64
+ transparent: !options.scope
65
65
  } : transparentOptions);
66
66
  const memo = fn => createMemo(() => fn(), syncOptions);
67
67
 
@@ -93,9 +93,15 @@ function getLocalHeaderScript(id) {
93
93
  return getCrossReferenceHeader(id) + ";";
94
94
  }
95
95
 
96
+ function joinAssetPath(base, file) {
97
+ if (/^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(file)) return file;
98
+ if (typeof base !== "string" || !base) base = "/";
99
+ if (base[base.length - 1] !== "/") base += "/";
100
+ return base + (file[0] === "/" ? file.slice(1) : file);
101
+ }
96
102
  function resolveAssets(moduleUrl, manifest) {
97
103
  if (!manifest) return null;
98
- const base = manifest._base || "/";
104
+ const base = manifest._base;
99
105
  const entry = manifest[moduleUrl];
100
106
  if (!entry) return null;
101
107
  const css = [];
@@ -106,8 +112,8 @@ function resolveAssets(moduleUrl, manifest) {
106
112
  visited.add(key);
107
113
  const e = manifest[key];
108
114
  if (!e) return;
109
- js.push(base + e.file);
110
- if (e.css) for (let i = 0; i < e.css.length; i++) css.push(base + e.css[i]);
115
+ js.push(joinAssetPath(base, e.file));
116
+ if (e.css) for (let i = 0; i < e.css.length; i++) css.push(joinAssetPath(base, e.css[i]));
111
117
  if (e.imports) for (let i = 0; i < e.imports.length; i++) walk(e.imports[i]);
112
118
  };
113
119
  walk(moduleUrl);
@@ -134,25 +140,48 @@ function createAssetTracking() {
134
140
  const boundaryModules = new Map();
135
141
  const boundaryStyles = new Map();
136
142
  const emittedAssets = new Set();
143
+ const inlineStyles = new Map();
137
144
  let currentBoundaryId = null;
138
145
  return {
139
146
  boundaryModules,
140
147
  boundaryStyles,
141
148
  emittedAssets,
149
+ inlineStyles,
150
+ registerInlineStyle(desc) {
151
+ let entry = inlineStyles.get(desc.id);
152
+ if (!entry) {
153
+ entry = {
154
+ id: desc.id,
155
+ content: desc.content || "",
156
+ attrs: desc.attrs,
157
+ emitted: false
158
+ };
159
+ inlineStyles.set(desc.id, entry);
160
+ }
161
+ if (currentBoundaryId) {
162
+ let styles = boundaryStyles.get(currentBoundaryId);
163
+ if (!styles) {
164
+ styles = new Set();
165
+ boundaryStyles.set(currentBoundaryId, styles);
166
+ }
167
+ styles.add(entry);
168
+ }
169
+ return entry;
170
+ },
142
171
  get currentBoundaryId() {
143
172
  return currentBoundaryId;
144
173
  },
145
174
  set currentBoundaryId(v) {
146
175
  currentBoundaryId = v;
147
176
  },
148
- registerModule(moduleUrl, entryUrl) {
177
+ registerModule(key, entryUrl) {
149
178
  const id = currentBoundaryId || "";
150
179
  let map = boundaryModules.get(id);
151
180
  if (!map) {
152
181
  map = {};
153
182
  boundaryModules.set(id, map);
154
183
  }
155
- map[moduleUrl] = entryUrl;
184
+ map[key] = entryUrl;
156
185
  },
157
186
  getBoundaryModules(id) {
158
187
  return boundaryModules.get(id) || null;
@@ -213,16 +242,20 @@ function renderToString(code, options = {}) {
213
242
  }
214
243
  serializer.write(id, p);
215
244
  },
216
- registerAsset(type, url) {
245
+ registerAsset(type, value) {
246
+ if (type === "inline-style") {
247
+ tracking.registerInlineStyle(value);
248
+ return;
249
+ }
217
250
  if (tracking.currentBoundaryId && type === "style") {
218
251
  let styles = tracking.boundaryStyles.get(tracking.currentBoundaryId);
219
252
  if (!styles) {
220
253
  styles = new Set();
221
254
  tracking.boundaryStyles.set(tracking.currentBoundaryId, styles);
222
255
  }
223
- styles.add(url);
256
+ styles.add(value);
224
257
  }
225
- tracking.emittedAssets.add(url);
258
+ tracking.emittedAssets.add(value);
226
259
  }
227
260
  };
228
261
  applyAssetTracking(sharedConfig.context, tracking, manifest);
@@ -238,6 +271,7 @@ function renderToString(code, options = {}) {
238
271
  serializer.close();
239
272
  html = injectAssets(sharedConfig.context.assets, html);
240
273
  html = injectPreloadLinks(tracking.emittedAssets, html);
274
+ html = injectInlineStyles(tracking.inlineStyles, html, nonce);
241
275
  if (scripts.length) html = injectScripts(html, scripts, options.nonce);
242
276
  return html;
243
277
  }
@@ -348,19 +382,27 @@ function renderToStream(code, options = {}) {
348
382
  async: true,
349
383
  assets: [],
350
384
  nonce,
351
- registerAsset(type, url) {
385
+ registerAsset(type, value) {
386
+ if (type === "inline-style") {
387
+ const entry = tracking.registerInlineStyle(value);
388
+ if (firstFlushed && !tracking.currentBoundaryId && !entry.emitted) {
389
+ entry.emitted = true;
390
+ buffer.write(renderInlineStyle(entry, nonce));
391
+ }
392
+ return;
393
+ }
352
394
  if (tracking.currentBoundaryId && type === "style") {
353
395
  let styles = tracking.boundaryStyles.get(tracking.currentBoundaryId);
354
396
  if (!styles) {
355
397
  styles = new Set();
356
398
  tracking.boundaryStyles.set(tracking.currentBoundaryId, styles);
357
399
  }
358
- styles.add(url);
400
+ styles.add(value);
359
401
  }
360
- if (!tracking.emittedAssets.has(url)) {
361
- tracking.emittedAssets.add(url);
402
+ if (!tracking.emittedAssets.has(value)) {
403
+ tracking.emittedAssets.add(value);
362
404
  if (firstFlushed && type === "module") {
363
- buffer.write(`<link rel="modulepreload" href="${url}">`);
405
+ buffer.write(`<link rel="modulepreload" href="${value}">`);
364
406
  }
365
407
  }
366
408
  },
@@ -444,10 +486,13 @@ function renderToStream(code, options = {}) {
444
486
  serializeFragmentAssets(key, tracking.boundaryModules, context);
445
487
  const styles = collectStreamStyles(key, tracking, headStyles);
446
488
  const deferActivation = !!revealGroup;
447
- if (styles.length) {
448
- emitTask(`$dfs("${key}",${styles.length},${deferActivation ? 1 : 0})`);
489
+ for (let i = 0; i < styles.inline.length; i++) {
490
+ buffer.write(renderInlineStyle(styles.inline[i], nonce));
491
+ }
492
+ if (styles.links.length) {
493
+ emitTask(`$dfs("${key}",${styles.links.length},${deferActivation ? 1 : 0})`);
449
494
  writeTasks();
450
- for (const url of styles) {
495
+ for (const url of styles.links) {
451
496
  buffer.write(`<link rel="stylesheet" href="${url}" onload="$dfc('${key}')" onerror="$dfc('${key}')">`);
452
497
  }
453
498
  buffer.write(`<template id="${key}">${value !== undefined ? value : " "}</template>`);
@@ -496,38 +541,41 @@ function renderToStream(code, options = {}) {
496
541
  }, {
497
542
  id: renderId
498
543
  });
499
- function doShell() {
500
- if (shellCompleted) return;
501
- if (rootHoles) {
502
- const pending = [];
503
- for (const {
504
- id,
505
- fn
506
- } of rootHoles) {
507
- const marker = `<!--rh${id}-->`;
508
- const res = resolveSSRNode(fn);
509
- if (!res.h.length) {
510
- html = html.replace(marker, res.t[0]);
511
- } else {
512
- let out = res.t[0];
513
- for (let j = 0; j < res.h.length; j++) {
514
- const newId = nextHoleId++;
515
- pending.push({
516
- id: newId,
517
- fn: res.h[j]
518
- });
519
- out += `<!--rh${newId}-->` + res.t[j + 1];
520
- }
521
- html = html.replace(marker, out);
522
- for (const p of res.p) blockingPromises.add(p);
544
+ function resolveRootHoles() {
545
+ if (!rootHoles) return true;
546
+ const pending = [];
547
+ for (const {
548
+ id,
549
+ fn
550
+ } of rootHoles) {
551
+ const marker = `<!--rh${id}-->`;
552
+ const res = resolveSSRNode(fn);
553
+ if (!res.h.length) {
554
+ html = html.replace(marker, res.t[0]);
555
+ } else {
556
+ let out = res.t[0];
557
+ for (let j = 0; j < res.h.length; j++) {
558
+ const newId = nextHoleId++;
559
+ pending.push({
560
+ id: newId,
561
+ fn: res.h[j]
562
+ });
563
+ out += `<!--rh${newId}-->` + res.t[j + 1];
523
564
  }
565
+ html = html.replace(marker, out);
566
+ for (const p of res.p) blockingPromises.add(p);
524
567
  }
525
- if (pending.length) {
526
- rootHoles = pending;
527
- return;
528
- }
529
- rootHoles = null;
530
568
  }
569
+ if (pending.length) {
570
+ rootHoles = pending;
571
+ return false;
572
+ }
573
+ rootHoles = null;
574
+ return true;
575
+ }
576
+ function doShell() {
577
+ if (shellCompleted) return;
578
+ if (!resolveRootHoles()) return;
531
579
  sharedConfig.context = context;
532
580
  html = injectAssets(context.assets, html);
533
581
  headStyles = new Set();
@@ -535,6 +583,7 @@ function renderToStream(code, options = {}) {
535
583
  if (url.endsWith(".css")) headStyles.add(url);
536
584
  }
537
585
  html = injectPreloadLinks(tracking.emittedAssets, html);
586
+ html = injectInlineStyles(tracking.inlineStyles, html, nonce);
538
587
  serializeRootAssets();
539
588
  if (tasks.length) html = injectScripts(html, tasks, nonce);
540
589
  buffer.write(html);
@@ -559,7 +608,15 @@ function renderToStream(code, options = {}) {
559
608
  complete();
560
609
  };
561
610
  } else onCompleteAll = complete;
562
- queue(flushEnd);
611
+ function flush() {
612
+ allSettled(blockingPromises).then(() => {
613
+ setTimeout(() => {
614
+ if (!resolveRootHoles()) return flush();
615
+ queue(flushEnd);
616
+ });
617
+ });
618
+ }
619
+ flush();
563
620
  },
564
621
  pipe(w) {
565
622
  function flush() {
@@ -832,10 +889,11 @@ function ssrStyleProperty(name, value) {
832
889
  return value != null ? name + value : "";
833
890
  }
834
891
  function ssrElement(tag, props, children, needsId) {
892
+ const hk = needsId ? ssrHydrationKey() : "";
835
893
  if (props == null) props = {};else if (typeof props === "function") props = props();
836
894
  const skipChildren = VOID_ELEMENTS.test(tag);
837
895
  const keys = Object.keys(props);
838
- let result = `<${tag}${needsId ? ssrHydrationKey() : ""} `;
896
+ let result = `<${tag}${hk} `;
839
897
  for (let i = 0; i < keys.length; i++) {
840
898
  const prop = keys[i];
841
899
  if (ChildProperties.has(prop)) {
@@ -880,7 +938,10 @@ function escape(s, attr) {
880
938
  for (let i = 0; i < s.length; i++) s[i] = escape(s[i]);
881
939
  return s;
882
940
  }
883
- if (attr && t === "boolean") return s;
941
+ if (attr) {
942
+ if (s == null || t === "boolean" || t === "number") return s;
943
+ return escape(String(s), attr);
944
+ }
884
945
  return s;
885
946
  }
886
947
  const delimCode = attr ? 34 : 60;
@@ -940,29 +1001,6 @@ function tryJoinPlainSSRArray(nodes) {
940
1001
  }
941
1002
  return out;
942
1003
  }
943
- function mergeProps(...sources) {
944
- const target = {};
945
- for (let i = 0; i < sources.length; i++) {
946
- let source = sources[i];
947
- if (typeof source === "function") source = source();
948
- if (source) {
949
- const descriptors = Object.getOwnPropertyDescriptors(source);
950
- for (const key in descriptors) {
951
- if (key in target) continue;
952
- Object.defineProperty(target, key, {
953
- enumerable: true,
954
- get() {
955
- for (let i = sources.length - 1; i >= 0; i--) {
956
- const v = (sources[i] || {})[key];
957
- if (v !== undefined) return v;
958
- }
959
- }
960
- });
961
- }
962
- }
963
- }
964
- return target;
965
- }
966
1004
  function getHydrationKey() {
967
1005
  const hydrate = sharedConfig.context;
968
1006
  return hydrate && sharedConfig.getNextContextId();
@@ -1039,14 +1077,48 @@ function propagateBoundaryStyles(childKey, parentKey, tracking) {
1039
1077
  }
1040
1078
  function collectStreamStyles(key, tracking, headStyles) {
1041
1079
  const styles = tracking.getBoundaryStyles(key);
1042
- if (!styles) return [];
1043
- const result = [];
1044
- for (const url of styles) {
1045
- if (!headStyles || !headStyles.has(url)) {
1046
- result.push(url);
1080
+ const links = [];
1081
+ const inline = [];
1082
+ if (!styles) return {
1083
+ links,
1084
+ inline
1085
+ };
1086
+ for (const entry of styles) {
1087
+ if (typeof entry === "string") {
1088
+ if (!headStyles || !headStyles.has(entry)) links.push(entry);
1089
+ } else if (!entry.emitted) {
1090
+ entry.emitted = true;
1091
+ inline.push(entry);
1047
1092
  }
1048
1093
  }
1049
- return result;
1094
+ return {
1095
+ links,
1096
+ inline
1097
+ };
1098
+ }
1099
+ function escapeStyleContent(content) {
1100
+ return content.replace(/<\/(style)/gi, "<\\/$1");
1101
+ }
1102
+ function renderInlineStyle(entry, nonce) {
1103
+ let attrs = "";
1104
+ if (entry.attrs) {
1105
+ for (const name in entry.attrs) {
1106
+ attrs += ` ${name}="${escape(String(entry.attrs[name]), true)}"`;
1107
+ }
1108
+ }
1109
+ return `<style${nonce ? ` nonce="${nonce}"` : ""} data-asset="${escape(entry.id, true)}"${attrs}>${escapeStyleContent(entry.content)}</style>`;
1110
+ }
1111
+ function injectInlineStyles(inlineStyles, html, nonce) {
1112
+ if (!inlineStyles.size) return html;
1113
+ const index = html.indexOf("</head>");
1114
+ if (index === -1) return html;
1115
+ let out = "";
1116
+ for (const entry of inlineStyles.values()) {
1117
+ if (entry.emitted) continue;
1118
+ entry.emitted = true;
1119
+ out += renderInlineStyle(entry, nonce);
1120
+ }
1121
+ return out ? html.slice(0, index) + out + html.slice(index) : html;
1050
1122
  }
1051
1123
  function injectScripts(html, scripts, nonce) {
1052
1124
  const tag = `<script${nonce ? ` nonce="${nonce}"` : ""}>${scripts}</script>`;
@@ -1111,6 +1183,10 @@ function tryResolveString(node) {
1111
1183
  if (node.h && node.h.length > 0) return {
1112
1184
  merge: node
1113
1185
  };
1186
+ if (node.t === undefined) {
1187
+ console.warn(`Unrecognized value. Skipped inserting`, node);
1188
+ return "";
1189
+ }
1114
1190
  return Array.isArray(node.t) ? node.t[0] : node.t;
1115
1191
  }
1116
1192
  if (t === "function") {
@@ -1149,7 +1225,9 @@ function resolveSSRNode(node, result = {
1149
1225
  result.h.push(...node.h);
1150
1226
  result.p.push(...node.p);
1151
1227
  }
1152
- } else result.t[result.t.length - 1] += node.t;
1228
+ } else if (node.t !== undefined) {
1229
+ result.t[result.t.length - 1] += node.t;
1230
+ } else console.warn(`Unrecognized value. Skipped inserting`, node);
1153
1231
  } else if (t === "function") {
1154
1232
  try {
1155
1233
  resolveSSRNode(node(), result);
@@ -1186,15 +1264,38 @@ function dynamic(source) {
1186
1264
  const o = getOwner();
1187
1265
  if (o?.id != null) getNextChildId(o);
1188
1266
  return props => {
1189
- const memoOwner = createOwner();
1190
- return runWithOwner(memoOwner, () => {
1191
- const comp = source(),
1192
- t = typeof comp;
1193
- if (comp) {
1194
- if (t === "function") return comp(props);else if (t === "string") {
1195
- return ssrElement(comp, props, undefined, true);
1267
+ const comp = source();
1268
+ let p;
1269
+ let settled = false;
1270
+ let value;
1271
+ let error;
1272
+ if (comp && typeof comp.then === "function") {
1273
+ p = comp;
1274
+ p.then(v => {
1275
+ value = v;
1276
+ settled = true;
1277
+ }, err => {
1278
+ error = err;
1279
+ settled = true;
1280
+ });
1281
+ const ctx = sharedConfig.context;
1282
+ if (ctx?.async) ctx.block(p.then(() => {}, () => {}));
1283
+ }
1284
+ return createMemo(() => {
1285
+ let c = comp;
1286
+ if (p) {
1287
+ if (!settled) throw new NotReadyError(p);
1288
+ if (error) throw error;
1289
+ c = value;
1290
+ }
1291
+ if (c) {
1292
+ if (typeof c === "function") return c(props);
1293
+ if (typeof c === "string") {
1294
+ return ssrElement(c, props, undefined, true);
1196
1295
  }
1197
1296
  }
1297
+ }, {
1298
+ sync: true
1198
1299
  });
1199
1300
  };
1200
1301
  }
@@ -1206,4 +1307,4 @@ function Portal(props) {
1206
1307
  throw new Error("Portal is not supported on the server");
1207
1308
  }
1208
1309
 
1209
- export { Assets, ChildProperties, DOMElements, DOMWithState, DelegatedEvents, Dynamic, HydrationScript, MathMLElements, Namespaces, Portal, RawTextElements, RequestContext, SVGElements, VoidElements, notSup as addEvent, applyRef, notSup as assign, notSup as className, notSup as delegateEvents, dynamic, notSup as dynamicProperty, effect, escape, generateHydrationScript, getAssets, notSup as getDelegatedRoot, getHydrationKey, notSup as getNextElement, notSup as getNextMarker, notSup as getNextMatch, getRequestEvent, notSup as hydrate, notSup as insert, isDev, isServer, memo, mergeProps, notSup as ref, notSup as registerDelegatedContainer, notSup as registerDelegatedRoot, notSup as render, renderToStream, renderToString, renderToStringAsync, notSup as runHydrationEvents, notSup as setAttribute, notSup as setAttributeNS, notSup as setProperty, notSup as setStyleProperty, notSup as spread, ssr, ssrAttribute, ssrClassName, ssrElement, ssrGroup, ssrHydrationKey, ssrStyle, ssrStyleProperty, notSup as style, notSup as template, notSup as unregisterDelegatedContainer, notSup as unregisterDelegatedRoot, useAssets };
1310
+ export { Assets, ChildProperties, DOMElements, DOMWithState, DelegatedEvents, Dynamic, HydrationScript, MathMLElements, Namespaces, Portal, RawTextElements, RequestContext, SVGElements, VoidElements, notSup as acquireAsset, notSup as addEvent, applyRef, notSup as assign, notSup as className, notSup as delegateEvents, dynamic, notSup as dynamicProperty, effect, escape, generateHydrationScript, getAssets, notSup as getDelegatedRoot, getHydrationKey, notSup as getNextElement, notSup as getNextMarker, notSup as getNextMatch, getRequestEvent, notSup as hydrate, notSup as insert, isDev, isServer, memo, notSup as ref, notSup as registerDelegatedContainer, notSup as registerDelegatedRoot, notSup as render, renderToStream, renderToString, renderToStringAsync, notSup as runHydrationEvents, notSup as setAttribute, notSup as setAttributeNS, notSup as setProperty, notSup as setStyleProperty, notSup as spread, ssr, ssrAttribute, ssrClassName, ssrElement, ssrGroup, ssrHydrationKey, ssrStyle, ssrStyleProperty, notSup as style, notSup as template, notSup as unregisterDelegatedContainer, notSup as unregisterDelegatedRoot, useAssets };