solid-js 1.2.6 → 1.3.0-beta.3

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.
@@ -232,111 +232,205 @@ function stringifyString(str) {
232
232
  return result;
233
233
  }
234
234
 
235
+ const REPLACE_SCRIPT = `function $df(e,y,t){t=document.getElementById(e),document.getElementById("pl"+e).replaceWith(...t.childNodes),_$HY.set(e,y)}`;
236
+ const FRAGMENT_REPLACE = /<!\[([\d.]+)\]>/;
235
237
  function renderToString(code, options = {}) {
236
- solidJs.sharedConfig.context = Object.assign({
238
+ let scripts = "";
239
+ solidJs.sharedConfig.context = {
237
240
  id: "",
238
241
  count: 0,
239
- assets: []
240
- }, options);
241
- let html = resolveSSRNode(escape(code()));
242
- return injectAssets(solidJs.sharedConfig.context.assets, html);
242
+ suspense: {},
243
+ assets: [],
244
+ nonce: options.nonce,
245
+ writeResource(id, p, error) {
246
+ if (error) scripts += `_$HY.set("${id}", ${serializeError(p)});`;
247
+ }
248
+ };
249
+ let html = injectAssets(solidJs.sharedConfig.context.assets, resolveSSRNode(escape(code())));
250
+ if (scripts.length) html = injectScripts(html, scripts, options.nonce);
251
+ return html;
243
252
  }
244
253
  function renderToStringAsync(code, options = {}) {
245
- options = {
246
- timeoutMs: 30000,
247
- ...options
248
- };
249
- const context = solidJs.sharedConfig.context = Object.assign({
254
+ const {
255
+ nonce,
256
+ timeoutMs = 30000
257
+ } = options;
258
+ const context = solidJs.sharedConfig.context = {
250
259
  id: "",
251
260
  count: 0,
252
261
  resources: {},
253
262
  suspense: {},
254
263
  assets: [],
255
- async: true
256
- }, options);
257
- const timeout = new Promise((_, reject) => setTimeout(() => reject("renderToString timed out"), options.timeoutMs));
258
- return Promise.race([solidJs.awaitSuspense(() => escape(code())), timeout]).then(res => {
259
- let html = resolveSSRNode(res);
260
- return injectAssets(context.assets, html);
264
+ async: true,
265
+ nonce
266
+ };
267
+ let scripts = "";
268
+ solidJs.sharedConfig.context.writeResource = (id, p, error) => {
269
+ if (error) return scripts += `_$HY.set("${id}", ${serializeError(p)});`;
270
+ p.then(d => scripts += `_$HY.set("${id}", ${devalue(d)});`).catch(() => scripts += `_$HY.set("${id}", null);`);
271
+ };
272
+ const timeout = new Promise((_, reject) => setTimeout(() => reject("renderToString timed out"), timeoutMs));
273
+ function asyncWrap(fn) {
274
+ return new Promise(resolve => {
275
+ const registry = new Set();
276
+ const cache = Object.create(null);
277
+ solidJs.sharedConfig.context.registerFragment = register;
278
+ const rendered = fn();
279
+ if (!registry.size) resolve(rendered);
280
+ function register(key) {
281
+ registry.add(key);
282
+ return (value = "", error) => {
283
+ cache[key] = value;
284
+ registry.delete(key);
285
+ if (error) return scripts += `_$HY.set("${key}", Promise.resolve(${serializeError(error)}));`;
286
+ if (!registry.size) Promise.resolve().then(() => {
287
+ let source = resolveSSRNode(rendered);
288
+ let final = "";
289
+ let match;
290
+ while (match = source.match(FRAGMENT_REPLACE)) {
291
+ final += source.substring(0, match.index);
292
+ source = cache[match[1]] + source.substring(match.index + match[0].length);
293
+ }
294
+ resolve(final + source);
295
+ });
296
+ return true;
297
+ };
298
+ }
299
+ });
300
+ }
301
+ return Promise.race([asyncWrap(() => escape(code())), timeout]).then(res => {
302
+ let html = injectAssets(context.assets, resolveSSRNode(res));
303
+ if (scripts.length) html = injectScripts(html, scripts, nonce);
304
+ return html;
261
305
  });
262
306
  }
263
- function pipeToNodeWritable(code, writable, options = {}) {
307
+ function renderToPipeableStream(code, options = {}) {
264
308
  const {
265
309
  nonce,
266
- onReady = ({
267
- startWriting
268
- }) => startWriting(),
269
- onComplete
310
+ onCompleteShell,
311
+ onCompleteAll,
312
+ dataOnly
270
313
  } = options;
271
314
  const tmp = [];
272
- let count = 0,
273
- completed = 0,
274
- buffer = {
275
- write(payload) {
276
- tmp.push(payload);
315
+ const tasks = [];
316
+ const registry = new Map();
317
+ const checkEnd = () => {
318
+ if (!registry.size && !completed) {
319
+ onCompleteAll && onCompleteAll(result);
320
+ writable && writable.end();
321
+ completed = true;
277
322
  }
278
323
  };
279
- const result = {
280
- startWriting() {
281
- buffer = writable;
282
- tmp.forEach(chunk => buffer.write(chunk));
283
- setTimeout(checkEnd);
284
- },
285
- write(c) {
286
- writable.write(c);
287
- },
288
- abort() {
289
- completed = count;
290
- checkEnd();
324
+ const writeInitialScript = () => {
325
+ if (tasks.length && !completed) {
326
+ buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>${tasks.join(";")}</script>`);
327
+ tasks.length = 0;
291
328
  }
329
+ scheduled = false;
292
330
  };
293
- const checkEnd = () => {
294
- if (completed === count) {
295
- onComplete && onComplete(result);
296
- writable.end();
331
+ let writable;
332
+ let completed = false;
333
+ let scriptFlushed = false;
334
+ let scheduled = true;
335
+ let buffer = {
336
+ write(payload) {
337
+ tmp.push(payload);
297
338
  }
298
339
  };
299
- solidJs.sharedConfig.context = Object.assign({
340
+ solidJs.sharedConfig.context = {
300
341
  id: "",
301
342
  count: 0,
343
+ async: true,
302
344
  streaming: true,
345
+ dataOnly,
346
+ resources: {},
303
347
  suspense: {},
304
- assets: []
305
- }, options);
306
- solidJs.sharedConfig.context.writeResource = (id, p) => {
307
- count++;
308
- Promise.resolve().then(() => buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>_$HYDRATION.startResource("${id}")</script>`));
309
- p.then(d => {
310
- buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>_$HYDRATION.resolveResource("${id}", ${devalue(d)})</script>`);
311
- ++completed && checkEnd();
312
- });
348
+ assets: [],
349
+ nonce,
350
+ writeResource(id, p, error) {
351
+ if (!scheduled) {
352
+ Promise.resolve().then(writeInitialScript);
353
+ scheduled = true;
354
+ }
355
+ if (error) return tasks.push(`_$HY.set("${id}", ${serializeError(p)})`);
356
+ tasks.push(`_$HY.init("${id}")`);
357
+ p.then(d => {
358
+ !completed && buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>_$HY.set("${id}", ${devalue(d)})</script>`);
359
+ }).catch(() => {
360
+ !completed && buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>_$HY.set("${id}", null)</script>`);
361
+ });
362
+ },
363
+ registerFragment(key) {
364
+ registry.set(key, []);
365
+ if (!dataOnly) {
366
+ if (!scheduled) {
367
+ Promise.resolve().then(writeInitialScript);
368
+ scheduled = true;
369
+ }
370
+ tasks.push(`_$HY.init("${key}")`);
371
+ }
372
+ return (value, error) => {
373
+ const keys = registry.get(key);
374
+ registry.delete(key);
375
+ if (waitForFragments(registry, key)) return;
376
+ if ((value !== undefined || error) && !completed) {
377
+ buffer.write(`<div hidden id="${key}">${value !== undefined ? value : " "}</div><script${nonce ? ` nonce="${nonce}"` : ""}>${!scriptFlushed ? REPLACE_SCRIPT : ""}${keys.length ? keys.map(k => `_$HY.unset("${k}");`) : ""}$df("${key}"${error ? "," + serializeError(error) : ""})</script>`);
378
+ scriptFlushed = true;
379
+ }
380
+ checkEnd();
381
+ return true;
382
+ };
383
+ }
313
384
  };
314
385
  let html = resolveSSRNode(escape(code()));
315
386
  html = injectAssets(solidJs.sharedConfig.context.assets, html);
316
- buffer.write(html);
317
- onReady(result);
387
+ Promise.resolve().then(() => {
388
+ if (tasks.length) html = injectScripts(html, tasks.join(";"), nonce);
389
+ buffer.write(html);
390
+ tasks.length = 0;
391
+ scheduled = false;
392
+ onCompleteShell && onCompleteShell();
393
+ });
394
+ return {
395
+ pipe(w) {
396
+ buffer = writable = w;
397
+ tmp.forEach(chunk => buffer.write(chunk));
398
+ if (completed) writable.end();else setTimeout(checkEnd);
399
+ }
400
+ };
318
401
  }
319
402
  function pipeToWritable(code, writable, options = {}) {
320
403
  const {
321
404
  nonce,
322
- onReady = ({
405
+ onCompleteShell = ({
323
406
  startWriting
324
407
  }) => startWriting(),
325
- onComplete
408
+ onCompleteAll,
409
+ dataOnly
326
410
  } = options;
327
411
  const tmp = [];
412
+ const tasks = [];
328
413
  const writer = writable.getWriter();
329
414
  const encoder = new TextEncoder();
330
- solidJs.sharedConfig.context = Object.assign({
331
- id: "",
332
- count: 0,
333
- streaming: true,
334
- suspense: {},
335
- assets: []
336
- }, options);
337
- let count = 0,
338
- completed = 0,
339
- buffer = {
415
+ const registry = new Map();
416
+ const checkEnd = () => {
417
+ if (!registry.size && !completed) {
418
+ onCompleteAll && onCompleteAll(result);
419
+ writable && writable.close();
420
+ completed = true;
421
+ }
422
+ };
423
+ const writeInitialScript = () => {
424
+ if (tasks.length) {
425
+ buffer.write(encoder.encode(`<script${nonce ? ` nonce="${nonce}"` : ""}>${tasks.join(";")}</script>`));
426
+ tasks.length = 0;
427
+ }
428
+ scheduled = false;
429
+ };
430
+ let completed = false;
431
+ let scriptFlushed = false;
432
+ let scheduled = true;
433
+ let buffer = {
340
434
  write(payload) {
341
435
  tmp.push(payload);
342
436
  }
@@ -351,28 +445,67 @@ function pipeToWritable(code, writable, options = {}) {
351
445
  writer.write(encoder.encode(c));
352
446
  },
353
447
  abort() {
354
- completed = count;
448
+ registry.clear();
355
449
  checkEnd();
356
450
  }
357
451
  };
358
- const checkEnd = () => {
359
- if (completed === count) {
360
- onComplete && onComplete(result);
361
- writable.close();
452
+ solidJs.sharedConfig.context = {
453
+ id: "",
454
+ count: 0,
455
+ async: true,
456
+ streaming: true,
457
+ dataOnly,
458
+ resources: {},
459
+ suspense: {},
460
+ assets: [],
461
+ nonce,
462
+ writeResource(id, p, error) {
463
+ if (!scheduled) {
464
+ Promise.resolve().then(writeInitialScript);
465
+ scheduled = true;
466
+ }
467
+ if (error) return tasks.push(`_$HY.set("${id}", ${serializeError(p)})`);
468
+ tasks.push(`_$HY.init("${id}")`);
469
+ p.then(d => {
470
+ !completed && buffer.write(encoder.encode(`<script${nonce ? ` nonce="${nonce}"` : ""}>_$HY.set("${id}", ${devalue(d)})</script>`));
471
+ }).catch(() => {
472
+ !completed && buffer.write(encoder.encode(`<script${nonce ? ` nonce="${nonce}"` : ""}>_$HY.set("${id}", null)</script>`));
473
+ });
474
+ },
475
+ registerFragment(key) {
476
+ registry.set(key, []);
477
+ if (!dataOnly) {
478
+ if (!scheduled) {
479
+ Promise.resolve().then(writeInitialScript);
480
+ scheduled = true;
481
+ }
482
+ tasks.push(`_$HY.init("${key}")`);
483
+ }
484
+ return (value, error) => {
485
+ const keys = registry.get(key);
486
+ registry.delete(key);
487
+ if (waitForFragments(registry, key)) return;
488
+ if ((value !== undefined || error) && !completed) {
489
+ buffer.write(encoder.encode(`<div hidden id="${key}">${value !== undefined ? value : " "}</div><script${nonce ? ` nonce="${nonce}"` : ""}>${!scriptFlushed ? REPLACE_SCRIPT : ""}${keys.length ? keys.map(k => `_$HY.unset("${k}");`) : ""}$df("${key}"${error ? "," + serializeError(error) : ""})</script>`));
490
+ scriptFlushed = true;
491
+ }
492
+ checkEnd();
493
+ return true;
494
+ };
362
495
  }
363
496
  };
364
- solidJs.sharedConfig.context.writeResource = (id, p) => {
365
- count++;
366
- Promise.resolve().then(() => buffer.write(encoder.encode(`<script${nonce ? ` nonce="${nonce}"` : ""}>_$HYDRATION.startResource("${id}")</script>`)));
367
- p.then(d => {
368
- buffer.write(encoder.encode(`<script${nonce ? ` nonce="${nonce}"` : ""}>_$HYDRATION.resolveResource("${id}", ${devalue(d)})</script>`));
369
- ++completed && checkEnd();
370
- });
371
- };
372
497
  let html = resolveSSRNode(escape(code()));
373
498
  html = injectAssets(solidJs.sharedConfig.context.assets, html);
374
- buffer.write(encoder.encode(html));
375
- onReady(result);
499
+ Promise.resolve().then(() => {
500
+ if (tasks.length) html = injectScripts(html, tasks.join(";"), nonce);
501
+ buffer.write(encoder.encode(html));
502
+ tasks.length = 0;
503
+ scheduled = false;
504
+ onCompleteShell && onCompleteShell(result);
505
+ });
506
+ }
507
+ function pipeToNodeWritable(code, writable, options = {}) {
508
+ renderToPipeableStream(code, options).pipe(writable);
376
509
  }
377
510
  function Assets(props) {
378
511
  solidJs.sharedConfig.context.assets.push(() => NoHydration({
@@ -382,15 +515,21 @@ function Assets(props) {
382
515
  }));
383
516
  return ssr(`%%$${solidJs.sharedConfig.context.assets.length - 1}%%`);
384
517
  }
385
- function HydrationScript() {
386
- solidJs.sharedConfig.context.assets.push(generateHydrationScript);
518
+ function HydrationScript(props) {
519
+ const {
520
+ nonce
521
+ } = solidJs.sharedConfig.context;
522
+ solidJs.sharedConfig.context.assets.push(() => generateHydrationScript({
523
+ nonce,
524
+ ...props
525
+ }));
387
526
  return ssr(`%%$${solidJs.sharedConfig.context.assets.length - 1}%%`);
388
527
  }
389
528
  function NoHydration(props) {
390
529
  const c = solidJs.sharedConfig.context;
391
- delete solidJs.sharedConfig.context;
530
+ c.noHydrate = true;
392
531
  const children = props.children;
393
- solidJs.sharedConfig.context = c;
532
+ c.noHydrate = false;
394
533
  return children;
395
534
  }
396
535
  function ssr(t, ...nodes) {
@@ -518,26 +657,13 @@ function resolveSSRNode(node) {
518
657
  }
519
658
  function getHydrationKey() {
520
659
  const hydrate = solidJs.sharedConfig.context;
521
- return hydrate && `${hydrate.id}${hydrate.count++}`;
660
+ return hydrate && !hydrate.noHydrate && `${hydrate.id}${hydrate.count++}`;
522
661
  }
523
- function generateHydrationScript() {
524
- const {
525
- nonce,
526
- streaming,
527
- resources,
528
- eventNames = ["click", "input"]
529
- } = solidJs.sharedConfig.context;
530
- let s = `<script${nonce ? ` nonce="${nonce}"` : ""}>(()=>{_$HYDRATION={events:[],completed:new WeakSet};const t=e=>e&&e.hasAttribute&&(e.hasAttribute("data-hk")?e:t(e.host&&e.host instanceof Node?e.host:e.parentNode)),e=e=>{let o=e.composedPath&&e.composedPath()[0]||e.target,s=t(o);s&&!_$HYDRATION.completed.has(s)&&_$HYDRATION.events.push([s,e])};["${eventNames.join('","')}"].forEach(t=>document.addEventListener(t,e))})();`;
531
- if (streaming) {
532
- s += `(()=>{const e=_$HYDRATION,o={};e.startResource=e=>{let r;o[e]=[new Promise(e=>r=e),r]},e.resolveResource=(e,r)=>{const n=o[e];if(!n)return o[e]=[r];n[1](r)},e.loadResource=e=>{const r=o[e];if(r)return r[0]}})();`;
533
- }
534
- if (resources) {
535
- s += `_$HYDRATION.resources = ${devalue(Object.keys(resources).reduce((r, k) => {
536
- r[k] = resources[k].data;
537
- return r;
538
- }, {}))};`;
539
- }
540
- return s + `</script>`;
662
+ function generateHydrationScript({
663
+ eventNames = ["click", "input"],
664
+ nonce
665
+ }) {
666
+ return `<script${nonce ? ` nonce="${nonce}"` : ""}>((e,t,o={})=>{t=e=>e&&e.hasAttribute&&(e.hasAttribute("data-hk")?e:t(e.host&&e.host instanceof Node?e.host:e.parentNode)),["${eventNames.join('","')}"].forEach((o=>document.addEventListener(o,(o=>{let n=o.composedPath&&o.composedPath()[0]||o.target,s=t(n);s&&!e.completed.has(s)&&e.events.push([s,o])})))),e.init=(e,t)=>{o[e]=[new Promise((e=>t=e)),t]},e.set=(e,t,n)=>{if(!(n=o[e]))return o[e]=[t];n[1](t)},e.unset=(e)=>{delete o[e]},e.load=(e,t)=>{if(t=o[e])return t[0]}})(window._$HY||(_$HY={events:[],completed:new WeakSet}))</script><!xs>`;
541
667
  }
542
668
  function injectAssets(assets, html) {
543
669
  for (let i = 0; i < assets.length; i++) {
@@ -545,6 +671,26 @@ function injectAssets(assets, html) {
545
671
  }
546
672
  return html;
547
673
  }
674
+ function injectScripts(html, scripts, nonce) {
675
+ const tag = `<script${nonce ? ` nonce="${nonce}"` : ""}>${scripts}</script>`;
676
+ const index = html.indexOf("<!xs>");
677
+ if (index > -1) {
678
+ return html.slice(0, index) + tag + html.slice(index);
679
+ }
680
+ return html + tag;
681
+ }
682
+ function serializeError(error) {
683
+ return error.message ? `new Error(${devalue(error.message)})` : devalue(error);
684
+ }
685
+ function waitForFragments(registry, key) {
686
+ for (const k of [...registry.keys()].reverse()) {
687
+ if (key.startsWith(k)) {
688
+ registry.get(k).push(key);
689
+ return true;
690
+ }
691
+ }
692
+ return false;
693
+ }
548
694
 
549
695
  const isServer = true;
550
696
  function spread() {}
@@ -634,6 +780,7 @@ exports.getHydrationKey = getHydrationKey;
634
780
  exports.isServer = isServer;
635
781
  exports.pipeToNodeWritable = pipeToNodeWritable;
636
782
  exports.pipeToWritable = pipeToWritable;
783
+ exports.renderToPipeableStream = renderToPipeableStream;
637
784
  exports.renderToString = renderToString;
638
785
  exports.renderToStringAsync = renderToStringAsync;
639
786
  exports.resolveSSRNode = resolveSSRNode;