solid-js 2.0.0-experimental.14 → 2.0.0-experimental.15

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
@@ -25,12 +25,24 @@ function getObserver() {
25
25
  }
26
26
  function createSignal(first, second, third) {
27
27
  if (typeof first === "function") {
28
+ const ssrSource = third?.ssrSource;
29
+ if (ssrSource === "initial" || ssrSource === "client") {
30
+ signals.createOwner();
31
+ let value = second;
32
+ return [() => value, v => {
33
+ return value = typeof v === "function" ? v(value) : v;
34
+ }];
35
+ }
36
+ const memoOpts = third?.deferStream || ssrSource ? {
37
+ deferStream: third?.deferStream,
38
+ ssrSource
39
+ } : undefined;
28
40
  const memo = createMemo(p => {
29
41
  let value = first(p ? p[0]() : second);
30
42
  return [() => value, v => {
31
43
  return value = typeof v === "function" ? v(value) : v;
32
44
  }];
33
- });
45
+ }, undefined, memoOpts);
34
46
  return [() => memo()[0](), v => memo()[1](v)];
35
47
  }
36
48
  return [() => first, v => {
@@ -45,14 +57,19 @@ function createMemo(compute, value, options) {
45
57
  value: value,
46
58
  compute: compute,
47
59
  error: undefined,
48
- computed: false
60
+ computed: false,
61
+ disposed: false
49
62
  };
63
+ signals.runWithOwner(owner, () => signals.onCleanup(() => {
64
+ comp.disposed = true;
65
+ }));
50
66
  function update() {
67
+ if (comp.disposed) return;
51
68
  try {
52
69
  comp.error = undefined;
53
70
  const result = signals.runWithOwner(owner, () => runWithObserver(comp, () => comp.compute(comp.value)));
54
71
  comp.computed = true;
55
- processResult(comp, result, owner, ctx);
72
+ processResult(comp, result, owner, ctx, options?.deferStream, options?.ssrSource);
56
73
  } catch (err) {
57
74
  if (err instanceof signals.NotReadyError) {
58
75
  err.source?.then(() => update());
@@ -61,7 +78,10 @@ function createMemo(compute, value, options) {
61
78
  comp.computed = true;
62
79
  }
63
80
  }
64
- if (!options?.lazy) {
81
+ const ssrSource = options?.ssrSource;
82
+ if (ssrSource === "initial" || ssrSource === "client") {
83
+ comp.computed = true;
84
+ } else if (!options?.lazy) {
65
85
  update();
66
86
  }
67
87
  return () => {
@@ -74,16 +94,79 @@ function createMemo(compute, value, options) {
74
94
  return comp.value;
75
95
  };
76
96
  }
77
- function processResult(comp, result, owner, ctx) {
97
+ function createDeepProxy(target, patches, basePath = []) {
98
+ const childProxies = new Map();
99
+ const handler = {
100
+ get(obj, key, receiver) {
101
+ if (Array.isArray(obj)) {
102
+ if (key === "shift") {
103
+ return function () {
104
+ if (obj.length === 0) return undefined;
105
+ const removed = obj[0];
106
+ Array.prototype.shift.call(obj);
107
+ childProxies.clear();
108
+ patches.push([[...basePath, 0]]);
109
+ return removed;
110
+ };
111
+ }
112
+ if (key === "unshift") {
113
+ return function (...items) {
114
+ const result = Array.prototype.unshift.apply(obj, items);
115
+ childProxies.clear();
116
+ for (let i = 0; i < items.length; i++) {
117
+ patches.push([[...basePath, i], items[i], 1]);
118
+ }
119
+ return result;
120
+ };
121
+ }
122
+ if (key === "splice") {
123
+ return function (start, deleteCount, ...items) {
124
+ const len = obj.length;
125
+ const s = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);
126
+ const d = deleteCount === undefined ? len - s : Math.min(Math.max(deleteCount, 0), len - s);
127
+ const removed = Array.prototype.splice.apply(obj, [s, d, ...items]);
128
+ childProxies.clear();
129
+ for (let i = 0; i < d; i++) patches.push([[...basePath, s]]);
130
+ for (let i = 0; i < items.length; i++) patches.push([[...basePath, s + i], items[i], 1]);
131
+ return removed;
132
+ };
133
+ }
134
+ }
135
+ const value = Reflect.get(obj, key, receiver);
136
+ if (value !== null && typeof value === "object" && typeof key !== "symbol") {
137
+ if (!childProxies.has(key)) {
138
+ childProxies.set(key, createDeepProxy(value, patches, [...basePath, key]));
139
+ }
140
+ return childProxies.get(key);
141
+ }
142
+ return value;
143
+ },
144
+ set(obj, key, value) {
145
+ childProxies.delete(key);
146
+ patches.push([[...basePath, key], value]);
147
+ return Reflect.set(obj, key, value);
148
+ },
149
+ deleteProperty(obj, key) {
150
+ childProxies.delete(key);
151
+ patches.push([[...basePath, key]]);
152
+ return Reflect.deleteProperty(obj, key);
153
+ }
154
+ };
155
+ return new Proxy(target, handler);
156
+ }
157
+ function processResult(comp, result, owner, ctx, deferStream, ssrSource) {
158
+ if (comp.disposed) return;
78
159
  const id = owner.id;
79
160
  const uninitialized = comp.value === undefined;
80
161
  if (result instanceof Promise) {
81
162
  result.then(v => {
82
163
  result.s = 1;
83
- result.v = comp.value = v;
164
+ result.v = v;
165
+ if (comp.disposed) return;
166
+ comp.value = v;
84
167
  comp.error = undefined;
85
- });
86
- if (ctx?.serialize && id) ctx.serialize(id, result);
168
+ }, () => {});
169
+ if (ctx?.async && ctx.serialize && id) ctx.serialize(id, result, deferStream);
87
170
  if (uninitialized) {
88
171
  comp.error = new signals.NotReadyError(result);
89
172
  }
@@ -92,14 +175,46 @@ function processResult(comp, result, owner, ctx) {
92
175
  const iterator = result?.[Symbol.asyncIterator];
93
176
  if (typeof iterator === "function") {
94
177
  const iter = iterator.call(result);
95
- const promise = iter.next().then(v => {
96
- promise.s = 1;
97
- promise.v = comp.value = v.value;
98
- comp.error = undefined;
99
- });
100
- if (ctx?.serialize && id) ctx.serialize(id, promise);
101
- if (uninitialized) {
102
- comp.error = new signals.NotReadyError(promise);
178
+ if (ssrSource === "hybrid") {
179
+ const promise = iter.next().then(v => {
180
+ promise.s = 1;
181
+ promise.v = v.value;
182
+ if (comp.disposed) return;
183
+ comp.value = v.value;
184
+ comp.error = undefined;
185
+ }, () => {});
186
+ if (ctx?.async && ctx.serialize && id) ctx.serialize(id, promise, deferStream);
187
+ if (uninitialized) {
188
+ comp.error = new signals.NotReadyError(promise);
189
+ }
190
+ } else {
191
+ const firstNext = iter.next();
192
+ const firstReady = firstNext.then(r => {
193
+ if (comp.disposed) return;
194
+ if (!r.done) {
195
+ comp.value = r.value;
196
+ comp.error = undefined;
197
+ }
198
+ }, () => {});
199
+ let servedFirst = false;
200
+ const tapped = {
201
+ [Symbol.asyncIterator]: () => ({
202
+ next() {
203
+ if (!servedFirst) {
204
+ servedFirst = true;
205
+ return firstNext.then(r => {
206
+ if (!r.done && !comp.disposed) comp.value = r.value;
207
+ return r;
208
+ });
209
+ }
210
+ return iter.next().then(r => r);
211
+ }
212
+ })
213
+ };
214
+ if (ctx?.async && ctx.serialize && id) ctx.serialize(id, tapped, deferStream);
215
+ if (uninitialized) {
216
+ comp.error = new signals.NotReadyError(firstReady);
217
+ }
103
218
  }
104
219
  return;
105
220
  }
@@ -117,7 +232,8 @@ function createRenderEffect(compute, effectFn, value, options) {
117
232
  value: value,
118
233
  compute: compute,
119
234
  error: undefined,
120
- computed: true
235
+ computed: true,
236
+ disposed: false
121
237
  }, () => compute(value)));
122
238
  effectFn(result, value);
123
239
  } catch (err) {
@@ -133,7 +249,7 @@ function createReaction(effectFn, options) {
133
249
  };
134
250
  }
135
251
  function createOptimistic(first, second, third) {
136
- return createSignal(first, second);
252
+ return createSignal(first, second, third);
137
253
  }
138
254
  function setProperty(state, property, value) {
139
255
  if (state[property] === value) return;
@@ -141,16 +257,145 @@ function setProperty(state, property, value) {
141
257
  delete state[property];
142
258
  } else state[property] = value;
143
259
  }
144
- function createStore(state) {
145
- function setStore(fn) {
146
- fn(state);
260
+ function createStore(first, second) {
261
+ if (typeof first === "function") {
262
+ const store = createProjection(first, second ?? {});
263
+ return [store, fn => fn(store)];
147
264
  }
148
- return [state, setStore];
265
+ const state = first;
266
+ return [state, fn => fn(state)];
149
267
  }
150
268
  const createOptimisticStore = createStore;
151
- function createProjection(fn, initialValue = {}) {
269
+ function createPendingProxy(state, source) {
270
+ let pending = true;
271
+ let readTarget = state;
272
+ const proxy = new Proxy(state, {
273
+ get(obj, key, receiver) {
274
+ if (pending && typeof key !== "symbol") {
275
+ throw new signals.NotReadyError(source);
276
+ }
277
+ return Reflect.get(readTarget, key);
278
+ }
279
+ });
280
+ return [proxy, frozen => {
281
+ if (frozen) readTarget = frozen;
282
+ pending = false;
283
+ }];
284
+ }
285
+ function createProjection(fn, initialValue = {}, options) {
286
+ const ctx = sharedConfig.context;
287
+ const owner = signals.createOwner();
152
288
  const [state] = createStore(initialValue);
153
- fn(state);
289
+ if (options?.ssrSource === "initial" || options?.ssrSource === "client") {
290
+ return state;
291
+ }
292
+ let disposed = false;
293
+ signals.runWithOwner(owner, () => signals.onCleanup(() => {
294
+ disposed = true;
295
+ }));
296
+ const ssrSource = options?.ssrSource;
297
+ const useProxy = ssrSource !== "hybrid";
298
+ const patches = [];
299
+ const draft = useProxy ? createDeepProxy(state, patches) : state;
300
+ const result = signals.runWithOwner(owner, () => fn(draft));
301
+ const iteratorFn = result?.[Symbol.asyncIterator];
302
+ if (typeof iteratorFn === "function") {
303
+ const iter = iteratorFn.call(result);
304
+ if (ssrSource === "hybrid") {
305
+ const promise = iter.next().then(r => {
306
+ promise.s = 1;
307
+ if (disposed) {
308
+ promise.v = state;
309
+ return;
310
+ }
311
+ if (r.value !== undefined && r.value !== state) {
312
+ Object.assign(state, r.value);
313
+ }
314
+ promise.v = state;
315
+ markReady();
316
+ }, () => {});
317
+ if (ctx?.async && !ctx.noHydrate && owner.id) ctx.serialize(owner.id, promise, options?.deferStream);
318
+ const [pending, markReady] = createPendingProxy(state, promise);
319
+ return pending;
320
+ } else {
321
+ const firstNext = iter.next();
322
+ const firstReady = firstNext.then(r => {
323
+ if (disposed) return;
324
+ patches.length = 0;
325
+ if (!r.done) {
326
+ if (r.value !== undefined && r.value !== draft) {
327
+ Object.assign(state, r.value);
328
+ }
329
+ }
330
+ markReady(JSON.parse(JSON.stringify(state)));
331
+ }, () => {
332
+ markReady();
333
+ });
334
+ let servedFirst = false;
335
+ const tapped = {
336
+ [Symbol.asyncIterator]: () => ({
337
+ next() {
338
+ if (!servedFirst) {
339
+ servedFirst = true;
340
+ return firstNext.then(r => {
341
+ if (!r.done && !disposed) return {
342
+ done: false,
343
+ value: state
344
+ };
345
+ return {
346
+ done: r.done,
347
+ value: undefined
348
+ };
349
+ });
350
+ }
351
+ return iter.next().then(r => {
352
+ if (disposed) return {
353
+ done: true,
354
+ value: undefined
355
+ };
356
+ const flushed = patches.splice(0);
357
+ if (!r.done) {
358
+ if (r.value !== undefined && r.value !== draft) {
359
+ Object.assign(state, r.value);
360
+ }
361
+ return {
362
+ done: false,
363
+ value: flushed
364
+ };
365
+ }
366
+ return {
367
+ done: true,
368
+ value: undefined
369
+ };
370
+ });
371
+ }
372
+ })
373
+ };
374
+ if (ctx?.async && !ctx.noHydrate && owner.id) ctx.serialize(owner.id, tapped, options?.deferStream);
375
+ const [pending, markReady] = createPendingProxy(state, firstReady);
376
+ return pending;
377
+ }
378
+ }
379
+ if (result instanceof Promise) {
380
+ const promise = result.then(v => {
381
+ promise.s = 1;
382
+ if (disposed) {
383
+ promise.v = state;
384
+ return;
385
+ }
386
+ if (v !== undefined && v !== state) {
387
+ Object.assign(state, v);
388
+ }
389
+ promise.v = state;
390
+ markReady();
391
+ }, () => {});
392
+ if (ctx?.async && !ctx.noHydrate && owner.id) ctx.serialize(owner.id, promise, options?.deferStream);
393
+ const [pending, markReady] = createPendingProxy(state, promise);
394
+ return pending;
395
+ }
396
+ if (result !== undefined && result !== state && result !== draft) {
397
+ Object.assign(state, result);
398
+ }
154
399
  return state;
155
400
  }
156
401
  function reconcile(value) {
@@ -202,15 +447,18 @@ const ErrorContext = {
202
447
  defaultValue: null
203
448
  };
204
449
  function createErrorBoundary(fn, fallback) {
450
+ const ctx = sharedConfig.context;
205
451
  const owner = signals.createOwner();
206
452
  return signals.runWithOwner(owner, () => {
207
453
  let result;
208
454
  signals.setContext(ErrorContext, err => {
455
+ if (ctx && !ctx.noHydrate && owner.id) ctx.serialize(owner.id, err);
209
456
  result = fallback(err, () => {});
210
457
  });
211
458
  try {
212
459
  result = fn();
213
460
  } catch (err) {
461
+ if (ctx && !ctx.noHydrate && owner.id) ctx.serialize(owner.id, err);
214
462
  result = fallback(err, () => {});
215
463
  }
216
464
  return () => result;
@@ -299,9 +547,9 @@ function enableHydration() {}
299
547
  function createComponent(Comp, props) {
300
548
  return Comp(props || {});
301
549
  }
302
- function lazy(fn) {
550
+ function lazy(fn, moduleUrl) {
303
551
  let p;
304
- let load = id => {
552
+ let load = () => {
305
553
  if (!p) {
306
554
  p = fn();
307
555
  p.then(mod => {
@@ -311,15 +559,30 @@ function lazy(fn) {
311
559
  return p;
312
560
  };
313
561
  const wrap = props => {
314
- sharedConfig.getNextContextId();
562
+ if (!moduleUrl) {
563
+ throw new Error("lazy() used in SSR without a moduleUrl. " + "All lazy() components require a moduleUrl for correct hydration. " + "This is typically injected by the bundler plugin.");
564
+ }
565
+ if (!sharedConfig.context?.resolveAssets) {
566
+ throw new Error(`lazy() called with moduleUrl "${moduleUrl}" but no asset manifest is set. ` + "Pass a manifest option to renderToStream/renderToString.");
567
+ }
315
568
  load();
316
- if (p.v) return p.v(props);
317
- if (sharedConfig.context?.async) {
318
- sharedConfig.context.block(p.then(() => {
569
+ const ctx = sharedConfig.context;
570
+ if (!ctx?.registerAsset || !ctx.resolveAssets) return;
571
+ const assets = ctx.resolveAssets(moduleUrl);
572
+ if (assets) {
573
+ for (let i = 0; i < assets.css.length; i++) ctx.registerAsset("style", assets.css[i]);
574
+ for (let i = 0; i < assets.js.length; i++) ctx.registerAsset("module", assets.js[i]);
575
+ ctx.registerModule?.(moduleUrl, assets.js[0]);
576
+ }
577
+ if (ctx?.async) {
578
+ ctx.block(p.then(() => {
319
579
  p.s = "success";
320
580
  }));
321
581
  }
322
- throw new signals.NotReadyError(p);
582
+ return createMemo(() => {
583
+ if (!p.v) throw new signals.NotReadyError(p);
584
+ return p.v(props);
585
+ });
323
586
  };
324
587
  wrap.preload = load;
325
588
  return wrap;
@@ -402,18 +665,35 @@ function Loading(props) {
402
665
  const id = o.id;
403
666
  o.id = id + "00";
404
667
  let runPromise;
668
+ let serializeBuffer = [];
669
+ const origSerialize = ctx.serialize;
405
670
  function runInitially() {
406
671
  o.dispose(false);
407
- return signals.runWithOwner(o, () => {
672
+ serializeBuffer = [];
673
+ ctx.serialize = (id, p, deferStream) => {
674
+ serializeBuffer.push([id, p, deferStream]);
675
+ };
676
+ const prevBoundary = ctx._currentBoundaryId;
677
+ ctx._currentBoundaryId = id;
678
+ const result = signals.runWithOwner(o, () => {
408
679
  try {
409
- return ctx.resolve(signals.flatten(props.children));
680
+ return ctx.resolve(props.children);
410
681
  } catch (err) {
411
682
  runPromise = ssrHandleError(err);
412
683
  }
413
684
  });
685
+ ctx._currentBoundaryId = prevBoundary;
686
+ ctx.serialize = origSerialize;
687
+ return result;
414
688
  }
415
689
  let ret = runInitially();
416
- if (!(runPromise || ret?.p?.length)) return ret;
690
+ if (!(runPromise || ret?.p?.length)) {
691
+ for (const args of serializeBuffer) origSerialize(args[0], args[1], args[2]);
692
+ serializeBuffer = [];
693
+ const modules = ctx.getBoundaryModules?.(id);
694
+ if (modules) ctx.serialize(id + "_assets", modules);
695
+ return ret;
696
+ }
417
697
  const fallbackOwner = signals.createOwner({
418
698
  id
419
699
  });
@@ -421,19 +701,30 @@ function Loading(props) {
421
701
  if (ctx.async) {
422
702
  const done = ctx.registerFragment(id);
423
703
  (async () => {
424
- while (runPromise) {
425
- await runPromise;
426
- runPromise = undefined;
427
- ret = runInitially();
428
- }
429
- while (ret.p.length) {
430
- await Promise.all(ret.p);
431
- ret = ctx.ssr(ret.t, ...ret.h);
704
+ try {
705
+ while (runPromise) {
706
+ o.dispose(false);
707
+ await runPromise;
708
+ runPromise = undefined;
709
+ ret = runInitially();
710
+ }
711
+ for (const args of serializeBuffer) origSerialize(args[0], args[1], args[2]);
712
+ serializeBuffer = [];
713
+ while (ret.p.length) {
714
+ await Promise.all(ret.p);
715
+ ret = signals.runWithOwner(o, () => ctx.ssr(ret.t, ...ret.h));
716
+ }
717
+ done(ret.t[0]);
718
+ } catch (err) {
719
+ done(undefined, err);
432
720
  }
433
- done(ret.t[0]);
434
721
  })();
435
722
  return signals.runWithOwner(fallbackOwner, () => ctx.ssr([`<template id="pl-${id}"></template>`, `<!--pl-${id}-->`], ctx.escape(props.fallback)));
436
723
  }
724
+ for (const args of serializeBuffer) origSerialize(args[0], args[1], args[2]);
725
+ serializeBuffer = [];
726
+ const modules = ctx.getBoundaryModules?.(id);
727
+ if (modules) ctx.serialize(id + "_assets", modules);
437
728
  ctx.serialize(id, "$$f");
438
729
  return signals.runWithOwner(fallbackOwner, () => props.fallback);
439
730
  }
@@ -505,6 +796,7 @@ exports.action = action;
505
796
  exports.children = children;
506
797
  exports.createComponent = createComponent;
507
798
  exports.createContext = createContext;
799
+ exports.createDeepProxy = createDeepProxy;
508
800
  exports.createEffect = createEffect;
509
801
  exports.createMemo = createMemo;
510
802
  exports.createOptimistic = createOptimistic;