solid-js 1.2.4 → 1.3.0-beta.2

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,160 @@ function stringifyString(str) {
232
232
  return result;
233
233
  }
234
234
 
235
+ const REPLACE_SCRIPT = `function $df(e,t){t=document.getElementById(e),document.getElementById("pl"+e).replaceWith(...t.childNodes),_$HY.set(e)}`;
235
236
  function renderToString(code, options = {}) {
236
- solidJs.sharedConfig.context = Object.assign({
237
+ solidJs.sharedConfig.context = {
237
238
  id: "",
238
239
  count: 0,
239
- assets: []
240
- }, options);
240
+ suspense: {},
241
+ assets: [],
242
+ nonce: options.nonce
243
+ };
241
244
  let html = resolveSSRNode(escape(code()));
242
245
  return injectAssets(solidJs.sharedConfig.context.assets, html);
243
246
  }
244
247
  function renderToStringAsync(code, options = {}) {
245
- options = {
246
- timeoutMs: 30000,
247
- ...options
248
- };
249
- const context = solidJs.sharedConfig.context = Object.assign({
248
+ const {
249
+ nonce,
250
+ timeoutMs = 30000
251
+ } = options;
252
+ const context = solidJs.sharedConfig.context = {
250
253
  id: "",
251
254
  count: 0,
252
255
  resources: {},
253
256
  suspense: {},
254
257
  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);
258
+ async: true,
259
+ nonce
260
+ };
261
+ let scripts = "";
262
+ solidJs.sharedConfig.context.writeResource = (id, p) => p.then(d => scripts += `_$HY.set("${id}", ${devalue(d)});`);
263
+ const timeout = new Promise((_, reject) => setTimeout(() => reject("renderToString timed out"), timeoutMs));
264
+ return Promise.race([asyncWrap(() => escape(code())), timeout]).then(res => {
265
+ let html = injectAssets(context.assets, resolveSSRNode(res));
266
+ if (scripts.length) html = injectScripts(html, scripts, nonce);
267
+ return html;
261
268
  });
262
269
  }
263
- function pipeToNodeWritable(code, writable, options = {}) {
270
+ function renderToPipeableStream(code, options) {
264
271
  const {
265
272
  nonce,
266
- onReady = ({
267
- startWriting
268
- }) => startWriting(),
269
- onComplete
273
+ onCompleteShell,
274
+ onCompleteAll
270
275
  } = options;
271
276
  const tmp = [];
272
- let count = 0,
273
- completed = 0,
274
- buffer = {
275
- write(payload) {
276
- tmp.push(payload);
277
+ const tasks = [];
278
+ const registry = new Set();
279
+ const checkEnd = () => {
280
+ if (!registry.size && !completed) {
281
+ onCompleteAll && onCompleteAll(result);
282
+ writable && writable.end();
283
+ completed = true;
277
284
  }
278
285
  };
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();
286
+ const writeInitialScript = () => {
287
+ if (tasks.length) {
288
+ buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>${tasks.join(";")}</script>`);
289
+ tasks.length = 0;
291
290
  }
291
+ scheduled = false;
292
292
  };
293
- const checkEnd = () => {
294
- if (completed === count) {
295
- onComplete && onComplete(result);
296
- writable.end();
293
+ let writable;
294
+ let completed = false;
295
+ let scriptFlushed = false;
296
+ let scheduled = true;
297
+ let buffer = {
298
+ write(payload) {
299
+ tmp.push(payload);
297
300
  }
298
301
  };
299
- solidJs.sharedConfig.context = Object.assign({
302
+ solidJs.sharedConfig.context = {
300
303
  id: "",
301
304
  count: 0,
305
+ async: true,
302
306
  streaming: true,
307
+ resources: {},
303
308
  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
- });
309
+ assets: [],
310
+ nonce,
311
+ writeResource(id, p) {
312
+ if (!scheduled) {
313
+ Promise.resolve().then(writeInitialScript);
314
+ scheduled = true;
315
+ }
316
+ tasks.push(`_$HY.init("${id}")`);
317
+ p.then(d => {
318
+ buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>_$HY.set("${id}", ${devalue(d)})</script>`);
319
+ });
320
+ },
321
+ registerFragment(key) {
322
+ registry.add(key);
323
+ Promise.resolve().then(() => {
324
+ if (registry.has(key)) {
325
+ if (!scheduled) {
326
+ Promise.resolve().then(writeInitialScript);
327
+ scheduled = true;
328
+ }
329
+ tasks.push(`_$HY.init("${key}")`);
330
+ }
331
+ });
332
+ return value => {
333
+ registry.delete(key);
334
+ if (!value) return;
335
+ buffer.write(`<div hidden id="${key}">${value}</div><script${nonce ? ` nonce="${nonce}"` : ""}>${!scriptFlushed ? REPLACE_SCRIPT : ""}$df("${key}")</script>`);
336
+ scriptFlushed = true;
337
+ checkEnd();
338
+ };
339
+ }
313
340
  };
314
341
  let html = resolveSSRNode(escape(code()));
315
342
  html = injectAssets(solidJs.sharedConfig.context.assets, html);
316
- buffer.write(html);
317
- onReady(result);
343
+ Promise.resolve().then(() => {
344
+ if (tasks.length) html = injectScripts(html, tasks.join(";"), nonce);
345
+ buffer.write(html);
346
+ tasks.length = 0;
347
+ scheduled = false;
348
+ onCompleteShell && onCompleteShell();
349
+ });
350
+ return {
351
+ pipe(w) {
352
+ buffer = writable = w;
353
+ tmp.forEach(chunk => buffer.write(chunk));
354
+ if (completed) writable.end();else setTimeout(checkEnd);
355
+ }
356
+ };
318
357
  }
319
358
  function pipeToWritable(code, writable, options = {}) {
320
359
  const {
321
360
  nonce,
322
- onReady = ({
361
+ onCompleteShell = ({
323
362
  startWriting
324
363
  }) => startWriting(),
325
- onComplete
364
+ onCompleteAll
326
365
  } = options;
327
366
  const tmp = [];
367
+ const tasks = [];
328
368
  const writer = writable.getWriter();
329
369
  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 = {
370
+ const registry = new Set();
371
+ const checkEnd = () => {
372
+ if (!registry.size && !completed) {
373
+ onCompleteAll && onCompleteAll(result);
374
+ writable && writable.close();
375
+ completed = true;
376
+ }
377
+ };
378
+ const writeInitialScript = () => {
379
+ if (tasks.length) {
380
+ buffer.write(encoder.encode(`<script${nonce ? ` nonce="${nonce}"` : ""}>${tasks.join(";")}</script>`));
381
+ tasks.length = 0;
382
+ }
383
+ scheduled = false;
384
+ };
385
+ let completed = false;
386
+ let scriptFlushed = false;
387
+ let scheduled = true;
388
+ let buffer = {
340
389
  write(payload) {
341
390
  tmp.push(payload);
342
391
  }
@@ -351,28 +400,61 @@ function pipeToWritable(code, writable, options = {}) {
351
400
  writer.write(encoder.encode(c));
352
401
  },
353
402
  abort() {
354
- completed = count;
403
+ registry.clear();
355
404
  checkEnd();
356
405
  }
357
406
  };
358
- const checkEnd = () => {
359
- if (completed === count) {
360
- onComplete && onComplete(result);
361
- writable.close();
407
+ solidJs.sharedConfig.context = {
408
+ id: "",
409
+ count: 0,
410
+ async: true,
411
+ streaming: true,
412
+ resources: {},
413
+ suspense: {},
414
+ assets: [],
415
+ nonce,
416
+ writeResource(id, p) {
417
+ if (!scheduled) {
418
+ Promise.resolve().then(writeInitialScript);
419
+ scheduled = true;
420
+ }
421
+ tasks.push(`_$HY.init("${id}")`);
422
+ p.then(d => {
423
+ buffer.write(encoder.encode(`<script${nonce ? ` nonce="${nonce}"` : ""}>_$HY.set("${id}", ${devalue(d)})</script>`));
424
+ });
425
+ },
426
+ registerFragment(key) {
427
+ registry.add(key);
428
+ Promise.resolve().then(() => {
429
+ if (registry.has(key)) {
430
+ if (!scheduled) {
431
+ Promise.resolve().then(writeInitialScript);
432
+ scheduled = true;
433
+ }
434
+ tasks.push(`_$HY.init("${key}")`);
435
+ }
436
+ });
437
+ return value => {
438
+ registry.delete(key);
439
+ if (!value) return;
440
+ buffer.write(encoder.encode(`<div hidden id="${key}">${value}</div><script${nonce ? ` nonce="${nonce}"` : ""}>${!scriptFlushed ? REPLACE_SCRIPT : ""}$df("${key}")</script>`));
441
+ scriptFlushed = true;
442
+ checkEnd();
443
+ };
362
444
  }
363
445
  };
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
446
  let html = resolveSSRNode(escape(code()));
373
447
  html = injectAssets(solidJs.sharedConfig.context.assets, html);
374
- buffer.write(encoder.encode(html));
375
- onReady(result);
448
+ Promise.resolve().then(() => {
449
+ if (tasks.length) html = injectScripts(html, tasks.join(";"), nonce);
450
+ buffer.write(encoder.encode(html));
451
+ tasks.length = 0;
452
+ scheduled = false;
453
+ onCompleteShell && onCompleteShell(result);
454
+ });
455
+ }
456
+ function pipeToNodeWritable(code, writable, options = {}) {
457
+ renderToPipeableStream(code, options).pipe(writable);
376
458
  }
377
459
  function Assets(props) {
378
460
  solidJs.sharedConfig.context.assets.push(() => NoHydration({
@@ -382,15 +464,21 @@ function Assets(props) {
382
464
  }));
383
465
  return ssr(`%%$${solidJs.sharedConfig.context.assets.length - 1}%%`);
384
466
  }
385
- function HydrationScript() {
386
- solidJs.sharedConfig.context.assets.push(generateHydrationScript);
467
+ function HydrationScript(props) {
468
+ const {
469
+ nonce
470
+ } = solidJs.sharedConfig.context;
471
+ solidJs.sharedConfig.context.assets.push(() => generateHydrationScript({
472
+ nonce,
473
+ ...props
474
+ }));
387
475
  return ssr(`%%$${solidJs.sharedConfig.context.assets.length - 1}%%`);
388
476
  }
389
477
  function NoHydration(props) {
390
478
  const c = solidJs.sharedConfig.context;
391
- delete solidJs.sharedConfig.context;
479
+ c.noHydrate = true;
392
480
  const children = props.children;
393
- solidJs.sharedConfig.context = c;
481
+ c.noHydrate = false;
394
482
  return children;
395
483
  }
396
484
  function ssr(t, ...nodes) {
@@ -518,26 +606,13 @@ function resolveSSRNode(node) {
518
606
  }
519
607
  function getHydrationKey() {
520
608
  const hydrate = solidJs.sharedConfig.context;
521
- return hydrate && `${hydrate.id}${hydrate.count++}`;
609
+ return hydrate && !hydrate.noHydrate && `${hydrate.id}${hydrate.count++}`;
522
610
  }
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>`;
611
+ function generateHydrationScript({
612
+ eventNames = ["click", "input"],
613
+ nonce
614
+ }) {
615
+ 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.load=(e,t)=>{if(t=o[e])return t[0]}})(window._$HY||(_$HY={events:[],completed:new WeakSet}));</script><!xs>`;
541
616
  }
542
617
  function injectAssets(assets, html) {
543
618
  for (let i = 0; i < assets.length; i++) {
@@ -545,6 +620,41 @@ function injectAssets(assets, html) {
545
620
  }
546
621
  return html;
547
622
  }
623
+ function injectScripts(html, scripts, nonce) {
624
+ const tag = `<script${nonce ? ` nonce="${nonce}"` : ""}>${scripts}</script>`;
625
+ const index = html.indexOf("<!xs>");
626
+ if (index > -1) {
627
+ return html.slice(0, index) + tag + html.slice(index);
628
+ }
629
+ return html + tag;
630
+ }
631
+ const FRAGMENT_REPLACE = /<!\[([\d.]+)\]>/;
632
+ function asyncWrap(fn) {
633
+ return new Promise(resolve => {
634
+ const registry = new Set();
635
+ const cache = Object.create(null);
636
+ solidJs.sharedConfig.context.registerFragment = register;
637
+ const rendered = fn();
638
+ if (!registry.size) resolve(rendered);
639
+ function register(key) {
640
+ registry.add(key);
641
+ return value => {
642
+ if (value) cache[key] = value;
643
+ registry.delete(key);
644
+ if (!registry.size) Promise.resolve().then(() => {
645
+ let source = resolveSSRNode(rendered);
646
+ let final = "";
647
+ let match;
648
+ while (match = source.match(FRAGMENT_REPLACE)) {
649
+ final += source.substring(0, match.index);
650
+ source = cache[match[1]] + source.substring(match.index + match[0].length);
651
+ }
652
+ resolve(final + source);
653
+ });
654
+ };
655
+ }
656
+ });
657
+ }
548
658
 
549
659
  const isServer = true;
550
660
  function spread() {}
@@ -634,6 +744,7 @@ exports.getHydrationKey = getHydrationKey;
634
744
  exports.isServer = isServer;
635
745
  exports.pipeToNodeWritable = pipeToNodeWritable;
636
746
  exports.pipeToWritable = pipeToWritable;
747
+ exports.renderToPipeableStream = renderToPipeableStream;
637
748
  exports.renderToString = renderToString;
638
749
  exports.renderToStringAsync = renderToStringAsync;
639
750
  exports.resolveSSRNode = resolveSSRNode;