@solidjs/web 2.0.0-experimental.14 → 2.0.0-experimental.16

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.cjs CHANGED
@@ -21,6 +21,9 @@ const DOMElements = /*#__PURE__*/new Set(["html", "base", "head", "link", "meta"
21
21
  "webview",
22
22
  "isindex", "listing", "multicol", "nextid", "noindex", "search"]);
23
23
 
24
+ const effect = (fn, effectFn, initial) => solidJs.createRenderEffect(fn, effectFn, initial, {
25
+ transparent: true
26
+ });
24
27
  const memo = fn => solidJs.createMemo(() => fn());
25
28
 
26
29
  const ES2017FLAG = seroval.Feature.AggregateError
@@ -30,13 +33,16 @@ function createSerializer({
30
33
  onData,
31
34
  onDone,
32
35
  scopeId,
33
- onError
36
+ onError,
37
+ plugins: customPlugins
34
38
  }) {
39
+ const defaultPlugins = [web.AbortSignalPlugin,
40
+ web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
41
+ web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin];
42
+ const allPlugins = customPlugins ? [...customPlugins, ...defaultPlugins] : defaultPlugins;
35
43
  return new seroval.Serializer({
36
44
  scopeId,
37
- plugins: [web.AbortSignalPlugin,
38
- web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
39
- web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin],
45
+ plugins: allPlugins,
40
46
  globalIdentifier: GLOBAL_IDENTIFIER,
41
47
  disabledFeatures: ES2017FLAG,
42
48
  onData,
@@ -48,17 +54,102 @@ function getLocalHeaderScript(id) {
48
54
  return seroval.getCrossReferenceHeader(id) + ';';
49
55
  }
50
56
 
57
+ function resolveAssets(moduleUrl, manifest) {
58
+ if (!manifest) return null;
59
+ const entry = manifest[moduleUrl];
60
+ if (!entry) return null;
61
+ const css = [];
62
+ const js = [];
63
+ const visited = new Set();
64
+ const walk = key => {
65
+ if (visited.has(key)) return;
66
+ visited.add(key);
67
+ const e = manifest[key];
68
+ if (!e) return;
69
+ js.push(e.file);
70
+ if (e.css) for (let i = 0; i < e.css.length; i++) css.push(e.css[i]);
71
+ if (e.imports) for (let i = 0; i < e.imports.length; i++) walk(e.imports[i]);
72
+ };
73
+ walk(moduleUrl);
74
+ return {
75
+ js,
76
+ css
77
+ };
78
+ }
79
+ function registerEntryAssets(manifest) {
80
+ if (!manifest) return;
81
+ const ctx = solidJs.sharedConfig.context;
82
+ if (!ctx?.registerAsset) return;
83
+ for (const key in manifest) {
84
+ if (manifest[key].isEntry) {
85
+ const assets = resolveAssets(key, manifest);
86
+ if (assets) {
87
+ for (let i = 0; i < assets.css.length; i++) ctx.registerAsset("style", assets.css[i]);
88
+ }
89
+ return;
90
+ }
91
+ }
92
+ }
93
+ function createAssetTracking() {
94
+ const boundaryModules = new Map();
95
+ const boundaryStyles = new Map();
96
+ const emittedAssets = new Set();
97
+ let currentBoundaryId = null;
98
+ return {
99
+ boundaryModules,
100
+ boundaryStyles,
101
+ emittedAssets,
102
+ get currentBoundaryId() {
103
+ return currentBoundaryId;
104
+ },
105
+ set currentBoundaryId(v) {
106
+ currentBoundaryId = v;
107
+ },
108
+ registerModule(moduleUrl, entryUrl) {
109
+ if (!currentBoundaryId) return;
110
+ let map = boundaryModules.get(currentBoundaryId);
111
+ if (!map) {
112
+ map = {};
113
+ boundaryModules.set(currentBoundaryId, map);
114
+ }
115
+ map[moduleUrl] = entryUrl;
116
+ },
117
+ getBoundaryModules(id) {
118
+ return boundaryModules.get(id) || null;
119
+ },
120
+ getBoundaryStyles(id) {
121
+ return boundaryStyles.get(id) || null;
122
+ }
123
+ };
124
+ }
125
+ function applyAssetTracking(context, tracking, manifest) {
126
+ Object.defineProperty(context, "_currentBoundaryId", {
127
+ get() {
128
+ return tracking.currentBoundaryId;
129
+ },
130
+ set(v) {
131
+ tracking.currentBoundaryId = v;
132
+ },
133
+ configurable: true,
134
+ enumerable: true
135
+ });
136
+ context.registerModule = tracking.registerModule;
137
+ context.getBoundaryModules = tracking.getBoundaryModules;
138
+ if (manifest) context.resolveAssets = moduleUrl => resolveAssets(moduleUrl, manifest);
139
+ }
51
140
  const VOID_ELEMENTS = /^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i;
52
- const REPLACE_SCRIPT = `function $df(e,n,o,t){if(n=document.getElementById(e),o=document.getElementById("pl-"+e)){for(;o&&8!==o.nodeType&&o.nodeValue!=="pl-"+e;)t=o.nextSibling,o.remove(),o=t;_$HY.done?o.remove():o.replaceWith(n.content)}n.remove(),_$HY.fe(e)}`;
141
+ const REPLACE_SCRIPT = `function $df(e,n,o,t){if(n=document.getElementById(e),o=document.getElementById("pl-"+e)){for(;o&&8!==o.nodeType&&o.nodeValue!=="pl-"+e;)t=o.nextSibling,o.remove(),o=t;_$HY.done?o.remove():o.replaceWith(n.content)}n.remove(),_$HY.fe(e)}function $dfs(e,c){(_$HY.sc=_$HY.sc||{})[e]=c}function $dfc(e){if(--_$HY.sc[e]<=0)delete _$HY.sc[e],$df(e)}`;
53
142
  function renderToString(code, options = {}) {
54
143
  const {
55
144
  renderId = "",
56
145
  nonce,
57
- noScripts
146
+ noScripts,
147
+ manifest
58
148
  } = options;
59
149
  let scripts = "";
60
150
  const serializer = createSerializer({
61
151
  scopeId: renderId,
152
+ plugins: options.plugins,
62
153
  onData(script) {
63
154
  if (noScripts) return;
64
155
  if (!scripts) {
@@ -68,6 +159,7 @@ function renderToString(code, options = {}) {
68
159
  },
69
160
  onError: options.onError
70
161
  });
162
+ const tracking = createAssetTracking();
71
163
  solidJs.sharedConfig.context = {
72
164
  assets: [],
73
165
  nonce,
@@ -75,9 +167,26 @@ function renderToString(code, options = {}) {
75
167
  resolve: resolveSSRNode,
76
168
  ssr: ssr,
77
169
  serialize(id, p) {
78
- !solidJs.sharedConfig.context.noHydrate && serializer.write(id, p);
170
+ if (solidJs.sharedConfig.context.noHydrate) return;
171
+ if (p != null && typeof p === "object" && (typeof p.then === "function" || typeof p[Symbol.asyncIterator] === "function")) {
172
+ throw new Error("Cannot serialize async value in renderToString (id: " + id + "). " + "Use renderToStream for async data.");
173
+ }
174
+ serializer.write(id, p);
175
+ },
176
+ registerAsset(type, url) {
177
+ if (tracking.currentBoundaryId && type === "style") {
178
+ let styles = tracking.boundaryStyles.get(tracking.currentBoundaryId);
179
+ if (!styles) {
180
+ styles = new Set();
181
+ tracking.boundaryStyles.set(tracking.currentBoundaryId, styles);
182
+ }
183
+ styles.add(url);
184
+ }
185
+ tracking.emittedAssets.add(url);
79
186
  }
80
187
  };
188
+ applyAssetTracking(solidJs.sharedConfig.context, tracking, manifest);
189
+ registerEntryAssets(manifest);
81
190
  let html = solidJs.createRoot(d => {
82
191
  setTimeout(d);
83
192
  return resolveSSRSync(escape(code()));
@@ -87,6 +196,7 @@ function renderToString(code, options = {}) {
87
196
  solidJs.sharedConfig.context.noHydrate = true;
88
197
  serializer.close();
89
198
  html = injectAssets(solidJs.sharedConfig.context.assets, html);
199
+ html = injectPreloadLinks(tracking.emittedAssets, html);
90
200
  if (scripts.length) html = injectScripts(html, scripts, options.nonce);
91
201
  return html;
92
202
  }
@@ -96,10 +206,11 @@ function renderToStream(code, options = {}) {
96
206
  onCompleteShell,
97
207
  onCompleteAll,
98
208
  renderId = "",
99
- noScripts
209
+ noScripts,
210
+ manifest
100
211
  } = options;
101
212
  let dispose;
102
- const blockingPromises = [];
213
+ const blockingPromises = new Set();
103
214
  const pushTask = task => {
104
215
  if (noScripts) return;
105
216
  if (!tasks && !firstFlushed) {
@@ -124,6 +235,7 @@ function renderToStream(code, options = {}) {
124
235
  };
125
236
  const serializer = createSerializer({
126
237
  scopeId: options.renderId,
238
+ plugins: options.plugins,
127
239
  onData: pushTask,
128
240
  onDone,
129
241
  onError: options.onError
@@ -150,18 +262,38 @@ function renderToStream(code, options = {}) {
150
262
  let completed = false;
151
263
  let shellCompleted = false;
152
264
  let scriptFlushed = false;
265
+ let headStyles;
153
266
  let timer = null;
267
+ let rootHoles = null;
268
+ let nextHoleId = 0;
154
269
  let buffer = {
155
270
  write(payload) {
156
271
  tmp += payload;
157
272
  }
158
273
  };
274
+ const tracking = createAssetTracking();
159
275
  solidJs.sharedConfig.context = context = {
160
276
  async: true,
161
277
  assets: [],
162
278
  nonce,
279
+ registerAsset(type, url) {
280
+ if (tracking.currentBoundaryId && type === "style") {
281
+ let styles = tracking.boundaryStyles.get(tracking.currentBoundaryId);
282
+ if (!styles) {
283
+ styles = new Set();
284
+ tracking.boundaryStyles.set(tracking.currentBoundaryId, styles);
285
+ }
286
+ styles.add(url);
287
+ }
288
+ if (!tracking.emittedAssets.has(url)) {
289
+ tracking.emittedAssets.add(url);
290
+ if (firstFlushed && type === "module") {
291
+ buffer.write(`<link rel="modulepreload" href="${url}">`);
292
+ }
293
+ }
294
+ },
163
295
  block(p) {
164
- if (!firstFlushed) blockingPromises.push(p);
296
+ if (!firstFlushed) blockingPromises.add(p);
165
297
  },
166
298
  replace(id, payloadFn) {
167
299
  if (firstFlushed) return;
@@ -171,16 +303,12 @@ function renderToStream(code, options = {}) {
171
303
  const last = html.indexOf(`<!--!$/${id}-->`, first + placeholder.length);
172
304
  html = html.slice(0, first) + resolveSSRSync(escape(payloadFn())) + html.slice(last + placeholder.length + 1);
173
305
  },
174
- serialize(id, p, wait) {
175
- const serverOnly = solidJs.sharedConfig.context.noHydrate;
176
- if (!firstFlushed && wait && typeof p === "object" && "then" in p) {
177
- blockingPromises.push(p);
178
- !serverOnly && p.then(d => {
179
- serializer.write(id, d);
180
- }).catch(e => {
181
- serializer.write(id, e);
182
- });
183
- } else if (!serverOnly) serializer.write(id, p);
306
+ serialize(id, p, deferStream) {
307
+ if (solidJs.sharedConfig.context.noHydrate) return;
308
+ if (!firstFlushed && deferStream && typeof p === "object" && "then" in p) {
309
+ blockingPromises.add(p);
310
+ p.then(d => serializer.write(id, d)).catch(e => serializer.write(id, e));
311
+ } else serializer.write(id, p);
184
312
  },
185
313
  escape: escape,
186
314
  resolve: resolveSSRNode,
@@ -211,18 +339,33 @@ function renderToStream(code, options = {}) {
211
339
  const parent = registry.get(parentKey);
212
340
  parent.children ||= {};
213
341
  parent.children[key] = value !== undefined ? value : "";
342
+ serializeFragmentAssets(key, tracking.boundaryModules, context);
343
+ propagateBoundaryStyles(key, parentKey, tracking);
214
344
  item.resolve();
215
345
  return;
216
346
  }
217
347
  if (!completed) {
218
348
  if (!firstFlushed) {
219
349
  queue(() => html = replacePlaceholder(html, key, value !== undefined ? value : ""));
350
+ serializeFragmentAssets(key, tracking.boundaryModules, context);
220
351
  item.resolve(error);
221
352
  } else {
222
- buffer.write(`<template id="${key}">${value !== undefined ? value : " "}</template>`);
223
- pushTask(`$df("${key}")${!scriptFlushed ? ";" + REPLACE_SCRIPT : ""}`);
353
+ serializeFragmentAssets(key, tracking.boundaryModules, context);
354
+ const styles = collectStreamStyles(key, tracking, headStyles);
355
+ if (styles.length) {
356
+ pushTask(`$dfs("${key}",${styles.length})${!scriptFlushed ? ";" + REPLACE_SCRIPT : ""}`);
357
+ scriptFlushed = true;
358
+ writeTasks();
359
+ for (const url of styles) {
360
+ buffer.write(`<link rel="stylesheet" href="${url}" onload="$dfc('${key}')" onerror="$dfc('${key}')">`);
361
+ }
362
+ buffer.write(`<template id="${key}">${value !== undefined ? value : " "}</template>`);
363
+ } else {
364
+ buffer.write(`<template id="${key}">${value !== undefined ? value : " "}</template>`);
365
+ pushTask(`$df("${key}")${!scriptFlushed ? ";" + REPLACE_SCRIPT : ""}`);
366
+ scriptFlushed = true;
367
+ }
224
368
  item.resolve(error);
225
- scriptFlushed = true;
226
369
  }
227
370
  }
228
371
  }
@@ -230,16 +373,47 @@ function renderToStream(code, options = {}) {
230
373
  };
231
374
  }
232
375
  };
376
+ applyAssetTracking(context, tracking, manifest);
377
+ registerEntryAssets(manifest);
233
378
  let html = solidJs.createRoot(d => {
234
379
  dispose = d;
235
- return resolveSSRSync(escape(code()));
380
+ const res = resolveSSRNode(escape(code()));
381
+ if (!res.h.length) return res.t[0];
382
+ rootHoles = [];
383
+ let out = res.t[0];
384
+ for (let i = 0; i < res.h.length; i++) {
385
+ const id = nextHoleId++;
386
+ rootHoles.push({
387
+ id,
388
+ fn: res.h[i]
389
+ });
390
+ out += `<!--rh${id}-->` + res.t[i + 1];
391
+ }
392
+ for (const p of res.p) blockingPromises.add(p);
393
+ return out;
236
394
  }, {
237
395
  id: renderId
238
396
  });
239
397
  function doShell() {
240
398
  if (shellCompleted) return;
399
+ if (rootHoles) {
400
+ for (const {
401
+ id,
402
+ fn
403
+ } of rootHoles) {
404
+ const marker = `<!--rh${id}-->`;
405
+ const res = resolveSSRNode(fn);
406
+ html = html.replace(marker, !res.h.length ? res.t[0] : "");
407
+ }
408
+ rootHoles = null;
409
+ }
241
410
  solidJs.sharedConfig.context = context;
242
411
  html = injectAssets(context.assets, html);
412
+ headStyles = new Set();
413
+ for (const url of tracking.emittedAssets) {
414
+ if (url.endsWith(".css")) headStyles.add(url);
415
+ }
416
+ html = injectPreloadLinks(tracking.emittedAssets, html);
243
417
  if (tasks.length) html = injectScripts(html, tasks, nonce);
244
418
  buffer.write(html);
245
419
  tasks = "";
@@ -290,13 +464,13 @@ function renderToStream(code, options = {}) {
290
464
  writable = {
291
465
  end() {
292
466
  writer.releaseLock();
293
- w.close();
467
+ w.close().catch(() => {});
294
468
  resolve();
295
469
  }
296
470
  };
297
471
  buffer = {
298
472
  write(payload) {
299
- writer.write(encoder.encode(payload));
473
+ writer.write(encoder.encode(payload)).catch(() => {});
300
474
  }
301
475
  };
302
476
  buffer.write(tmp);
@@ -474,6 +648,9 @@ function getHydrationKey() {
474
648
  const hydrate = solidJs.sharedConfig.context;
475
649
  return hydrate && !hydrate.noHydrate && solidJs.sharedConfig.getNextContextId();
476
650
  }
651
+ function applyRef(r, element) {
652
+ Array.isArray(r) ? r.flat(Infinity).forEach(f => f && f(element)) : r(element);
653
+ }
477
654
  function useAssets(fn) {
478
655
  solidJs.sharedConfig.context.assets.push(() => resolveSSRSync(escape(fn())));
479
656
  }
@@ -514,9 +691,9 @@ function queue(fn) {
514
691
  return Promise.resolve().then(fn);
515
692
  }
516
693
  function allSettled(promises) {
517
- let length = promises.length;
694
+ let size = promises.size;
518
695
  return Promise.allSettled(promises).then(() => {
519
- if (promises.length !== length) return allSettled(promises);
696
+ if (promises.size !== size) return allSettled(promises);
520
697
  return;
521
698
  });
522
699
  }
@@ -528,6 +705,48 @@ function injectAssets(assets, html) {
528
705
  if (index === -1) return html;
529
706
  return html.slice(0, index) + out + html.slice(index);
530
707
  }
708
+ function injectPreloadLinks(emittedAssets, html, nonce) {
709
+ if (!emittedAssets.size) return html;
710
+ let links = "";
711
+ for (const url of emittedAssets) {
712
+ if (url.endsWith(".css")) {
713
+ links += `<link rel="stylesheet" href="${url}">`;
714
+ } else {
715
+ links += `<link rel="modulepreload" href="${url}">`;
716
+ }
717
+ }
718
+ const index = html.indexOf("</head>");
719
+ if (index === -1) return html;
720
+ return html.slice(0, index) + links + html.slice(index);
721
+ }
722
+ function serializeFragmentAssets(key, boundaryModules, context) {
723
+ const map = boundaryModules.get(key);
724
+ if (!map || !Object.keys(map).length) return;
725
+ context.serialize(key + "_assets", map);
726
+ }
727
+ function propagateBoundaryStyles(childKey, parentKey, tracking) {
728
+ const childStyles = tracking.getBoundaryStyles(childKey);
729
+ if (!childStyles) return;
730
+ let parentStyles = tracking.boundaryStyles.get(parentKey);
731
+ if (!parentStyles) {
732
+ parentStyles = new Set();
733
+ tracking.boundaryStyles.set(parentKey, parentStyles);
734
+ }
735
+ for (const url of childStyles) {
736
+ parentStyles.add(url);
737
+ }
738
+ }
739
+ function collectStreamStyles(key, tracking, headStyles) {
740
+ const styles = tracking.getBoundaryStyles(key);
741
+ if (!styles) return [];
742
+ const result = [];
743
+ for (const url of styles) {
744
+ if (!headStyles || !headStyles.has(url)) {
745
+ result.push(url);
746
+ }
747
+ }
748
+ return result;
749
+ }
531
750
  function injectScripts(html, scripts, nonce) {
532
751
  const tag = `<script${nonce ? ` nonce="${nonce}"` : ""}>${scripts}</script>`;
533
752
  const index = html.indexOf("<!--xs-->");
@@ -618,14 +837,14 @@ function resolveSSRNode(node, result = {
618
837
  function resolveSSRSync(node) {
619
838
  const res = resolveSSRNode(node);
620
839
  if (!res.h.length) return res.t[0];
621
- throw new Error("This value cannot be rendered synchronously. Are you missing a Suspsense boundary?");
840
+ throw new Error("This value cannot be rendered synchronously. Are you missing a boundary?");
622
841
  }
623
842
  const RequestContext = Symbol();
624
843
  function getRequestEvent() {
625
- return globalThis[RequestContext] ? globalThis[RequestContext].getStore() || solidJs.sharedConfig.context && solidJs.sharedConfig.context.event || console.log("RequestEvent is missing. This is most likely due to accessing `getRequestEvent` non-managed async scope in a partially polyfilled environment. Try moving it above all `await` calls.") : undefined;
844
+ return globalThis[RequestContext] ? globalThis[RequestContext].getStore() || solidJs.sharedConfig.context && solidJs.sharedConfig.context.event || console.warn("RequestEvent is missing. This is most likely due to accessing `getRequestEvent` non-managed async scope in a partially polyfilled environment. Try moving it above all `await` calls.") : undefined;
626
845
  }
627
846
  function renderToStringAsync(code, options = {}) {
628
- return renderToStream(code, options).then(html => html);
847
+ return new Promise(resolve => renderToStream(code, options).then(resolve));
629
848
  }
630
849
  function notSup() {
631
850
  throw new Error("Client-only API called on the server side. Run client-only code in onMount, or conditionally run client-only component with <Show>.");
@@ -634,13 +853,18 @@ function notSup() {
634
853
  const isServer = true;
635
854
  const isDev = false;
636
855
  function createDynamic(component, props) {
637
- const comp = component(),
638
- t = typeof comp;
639
- if (comp) {
640
- if (t === "function") return comp(props);else if (t === "string") {
641
- return ssrElement(comp, props, undefined, true);
856
+ const o = solidJs.getOwner();
857
+ if (o?.id != null) solidJs.getNextChildId(o);
858
+ const memoOwner = solidJs.createOwner();
859
+ return solidJs.runWithOwner(memoOwner, () => {
860
+ const comp = component(),
861
+ t = typeof comp;
862
+ if (comp) {
863
+ if (t === "function") return comp(props);else if (t === "string") {
864
+ return ssrElement(comp, props, undefined, true);
865
+ }
642
866
  }
643
- }
867
+ });
644
868
  }
645
869
  function Dynamic(props) {
646
870
  const others = solidJs.omit(props, "component");
@@ -682,10 +906,6 @@ Object.defineProperty(exports, "createComponent", {
682
906
  enumerable: true,
683
907
  get: function () { return solidJs.createComponent; }
684
908
  });
685
- Object.defineProperty(exports, "effect", {
686
- enumerable: true,
687
- get: function () { return solidJs.createRenderEffect; }
688
- });
689
909
  Object.defineProperty(exports, "getOwner", {
690
910
  enumerable: true,
691
911
  get: function () { return solidJs.getOwner; }
@@ -711,11 +931,13 @@ exports.RequestContext = RequestContext;
711
931
  exports.SVGElements = SVGElements;
712
932
  exports.SVGNamespace = SVGNamespace;
713
933
  exports.addEventListener = notSup;
934
+ exports.applyRef = applyRef;
714
935
  exports.assign = notSup;
715
936
  exports.className = notSup;
716
937
  exports.createDynamic = createDynamic;
717
938
  exports.delegateEvents = notSup;
718
939
  exports.dynamicProperty = notSup;
940
+ exports.effect = effect;
719
941
  exports.escape = escape;
720
942
  exports.generateHydrationScript = generateHydrationScript;
721
943
  exports.getAssets = getAssets;