@lightspeed-js/core 0.4.3 → 0.4.5

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/compile.js CHANGED
@@ -71,7 +71,7 @@ var START = "lsp ";
71
71
  var END = "/lsp";
72
72
  var MARKER_END = `<!--${END}-->`;
73
73
  function serializeMarker(payload) {
74
- const json = JSON.stringify(payload).replace(/-(?=-)/g, "\\u002d");
74
+ const json = JSON.stringify(payload).replace(/-(?=-)/g, "\\u002d").replace(/</g, "\\u003c");
75
75
  return `<!--${START}${json}-->`;
76
76
  }
77
77
 
@@ -999,7 +999,7 @@ class BindingEvaluator {
999
999
  }
1000
1000
  if (binding.kind === "prop") {
1001
1001
  const target = binding.target;
1002
- if (Object.is(target.props[binding.name], value))
1002
+ if (sameValue(target.props[binding.name], value))
1003
1003
  return;
1004
1004
  target.props[binding.name] = value;
1005
1005
  this.onPropChanged(target);
@@ -1112,6 +1112,26 @@ function setHidden(el, hidden) {
1112
1112
  else
1113
1113
  el.removeAttribute(HIDDEN_ATTR);
1114
1114
  }
1115
+ function sameValue(a, b, depth = 0) {
1116
+ if (Object.is(a, b))
1117
+ return true;
1118
+ if (depth > 20 || !isPlain(a) || !isPlain(b))
1119
+ return false;
1120
+ if (Array.isArray(a) !== Array.isArray(b))
1121
+ return false;
1122
+ const keys = Object.keys(a);
1123
+ if (keys.length !== Object.keys(b).length)
1124
+ return false;
1125
+ return keys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && sameValue(a[key], b[key], depth + 1));
1126
+ }
1127
+ function isPlain(value) {
1128
+ if (value === null || typeof value !== "object")
1129
+ return false;
1130
+ if (Array.isArray(value))
1131
+ return true;
1132
+ const proto = Object.getPrototypeOf(value);
1133
+ return proto === Object.prototype || proto === null;
1134
+ }
1115
1135
  // src/runtime/dom/ForBlock.ts
1116
1136
  function toIterable(value) {
1117
1137
  if (value == null)
package/dist/index.js CHANGED
@@ -571,7 +571,7 @@ class BindingEvaluator {
571
571
  }
572
572
  if (binding.kind === "prop") {
573
573
  const target = binding.target;
574
- if (Object.is(target.props[binding.name], value))
574
+ if (sameValue(target.props[binding.name], value))
575
575
  return;
576
576
  target.props[binding.name] = value;
577
577
  this.onPropChanged(target);
@@ -684,6 +684,26 @@ function setHidden(el, hidden) {
684
684
  else
685
685
  el.removeAttribute(HIDDEN_ATTR);
686
686
  }
687
+ function sameValue(a, b, depth = 0) {
688
+ if (Object.is(a, b))
689
+ return true;
690
+ if (depth > 20 || !isPlain(a) || !isPlain(b))
691
+ return false;
692
+ if (Array.isArray(a) !== Array.isArray(b))
693
+ return false;
694
+ const keys = Object.keys(a);
695
+ if (keys.length !== Object.keys(b).length)
696
+ return false;
697
+ return keys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && sameValue(a[key], b[key], depth + 1));
698
+ }
699
+ function isPlain(value) {
700
+ if (value === null || typeof value !== "object")
701
+ return false;
702
+ if (Array.isArray(value))
703
+ return true;
704
+ const proto = Object.getPrototypeOf(value);
705
+ return proto === Object.prototype || proto === null;
706
+ }
687
707
  // src/runtime/events/EventBinder.ts
688
708
  class EventBinder {
689
709
  bind(el, eventType, handlerName, owner, cleanupTarget = owner) {
@@ -1275,6 +1295,11 @@ function isSlotStash(node) {
1275
1295
  function slotNodesFor(payload, between) {
1276
1296
  if (!payload.ssr)
1277
1297
  return between;
1298
+ if (payload.slot) {
1299
+ const template = document.createElement("template");
1300
+ template.innerHTML = payload.slot;
1301
+ return Array.from(template.content.childNodes);
1302
+ }
1278
1303
  const stash = between.find(isSlotStash);
1279
1304
  return stash ? Array.from(stash.content.childNodes) : [];
1280
1305
  }
package/dist/ssg.js CHANGED
@@ -590,7 +590,7 @@ class BindingEvaluator {
590
590
  }
591
591
  if (binding.kind === "prop") {
592
592
  const target = binding.target;
593
- if (Object.is(target.props[binding.name], value))
593
+ if (sameValue(target.props[binding.name], value))
594
594
  return;
595
595
  target.props[binding.name] = value;
596
596
  this.onPropChanged(target);
@@ -703,6 +703,26 @@ function setHidden(el, hidden) {
703
703
  else
704
704
  el.removeAttribute(HIDDEN_ATTR);
705
705
  }
706
+ function sameValue(a, b, depth = 0) {
707
+ if (Object.is(a, b))
708
+ return true;
709
+ if (depth > 20 || !isPlain(a) || !isPlain(b))
710
+ return false;
711
+ if (Array.isArray(a) !== Array.isArray(b))
712
+ return false;
713
+ const keys = Object.keys(a);
714
+ if (keys.length !== Object.keys(b).length)
715
+ return false;
716
+ return keys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && sameValue(a[key], b[key], depth + 1));
717
+ }
718
+ function isPlain(value) {
719
+ if (value === null || typeof value !== "object")
720
+ return false;
721
+ if (Array.isArray(value))
722
+ return true;
723
+ const proto = Object.getPrototypeOf(value);
724
+ return proto === Object.prototype || proto === null;
725
+ }
706
726
  // src/runtime/events/EventBinder.ts
707
727
  class EventBinder {
708
728
  bind(el, eventType, handlerName, owner, cleanupTarget = owner) {
@@ -1260,7 +1280,7 @@ var START = "lsp ";
1260
1280
  var END = "/lsp";
1261
1281
  var MARKER_END = `<!--${END}-->`;
1262
1282
  function serializeMarker(payload) {
1263
- const json = JSON.stringify(payload).replace(/-(?=-)/g, "\\u002d");
1283
+ const json = JSON.stringify(payload).replace(/-(?=-)/g, "\\u002d").replace(/</g, "\\u003c");
1264
1284
  return `<!--${START}${json}-->`;
1265
1285
  }
1266
1286
  function isMarkerStart(node) {
@@ -1298,6 +1318,11 @@ function isSlotStash(node) {
1298
1318
  function slotNodesFor(payload, between) {
1299
1319
  if (!payload.ssr)
1300
1320
  return between;
1321
+ if (payload.slot) {
1322
+ const template = document.createElement("template");
1323
+ template.innerHTML = payload.slot;
1324
+ return Array.from(template.content.childNodes);
1325
+ }
1301
1326
  const stash = between.find(isSlotStash);
1302
1327
  return stash ? Array.from(stash.content.childNodes) : [];
1303
1328
  }
@@ -3156,7 +3181,14 @@ function serializeNodes(nodes) {
3156
3181
 
3157
3182
  // src/ssg/render.ts
3158
3183
  var MAX_BUILD_ROUNDS = 6;
3159
- async function renderPage(options) {
3184
+ var queue = Promise.resolve();
3185
+ function renderPage(options) {
3186
+ const next = queue.then(() => renderPageNow(options));
3187
+ queue = next.catch(() => {
3188
+ });
3189
+ return next;
3190
+ }
3191
+ async function renderPageNow(options) {
3160
3192
  const jsdom = await loadJsdom();
3161
3193
  if (!jsdom) {
3162
3194
  return {
@@ -3355,7 +3387,7 @@ function readIslands(document2) {
3355
3387
  hydrate: payload?.hydrate,
3356
3388
  binds: Object.values(payload?.bind ?? {}),
3357
3389
  handlers: Object.keys(payload?.on ?? {}).length,
3358
- slotted: false,
3390
+ slotted: !!payload?.slot,
3359
3391
  children: []
3360
3392
  };
3361
3393
  (stack[stack.length - 1]?.children ?? roots).push(island);
@@ -3366,9 +3398,6 @@ function readIslands(document2) {
3366
3398
  for (let cursor = island.start.nextSibling;cursor && cursor !== island.end; cursor = cursor.nextSibling) {
3367
3399
  if (cursor.nodeType !== 1)
3368
3400
  continue;
3369
- if (cursor.hasAttribute("data-lsp-slot")) {
3370
- island.slotted = true;
3371
- }
3372
3401
  const id = cursor.getAttribute("data-lsp-instance");
3373
3402
  if (id && !island.instance)
3374
3403
  island.instance = id;
@@ -3430,13 +3459,9 @@ function markRendered(document2, slots) {
3430
3459
  continue;
3431
3460
  }
3432
3461
  payload.ssr = 1;
3462
+ if (slot.html.trim())
3463
+ payload.slot = slot.html;
3433
3464
  slot.start.data = serializeMarker(payload).slice(4, -3);
3434
- if (slot.html.trim()) {
3435
- const template = document2.createElement("template");
3436
- template.setAttribute("data-lsp-slot", "");
3437
- template.innerHTML = slot.html;
3438
- slot.start.parentNode?.insertBefore(template, slot.start.nextSibling);
3439
- }
3440
3465
  rendered++;
3441
3466
  }
3442
3467
  return { rendered, skipped };
@@ -60,11 +60,24 @@ export interface MarkerPayload {
60
60
  needs?: string[];
61
61
  /** Set by the build: the content between the markers is rendered output. */
62
62
  ssr?: number;
63
+ /**
64
+ * Set by the build: the island's slot content, as HTML, for the client to
65
+ * rebuild from - the content between the markers is output, not slot
66
+ * content.
67
+ *
68
+ * In the marker rather than in an element beside the output, because an
69
+ * element there is a sibling the page's CSS can see: a stash before each
70
+ * island broke `.item + .item` in the build's HTML, and the layout shifted
71
+ * when hydration took the stash away.
72
+ */
73
+ slot?: string;
63
74
  }
64
75
  export declare const MARKER_END = "<!--/lsp-->";
65
76
  /**
66
77
  * A comment may not contain `--`, so any run of dashes in the payload is
67
- * escaped. `-` parses back to `-`, so the round trip is lossless.
78
+ * escaped; nor `<!--`, which slot content full of nested markers would bring,
79
+ * so `<` is escaped too. Both parse back to themselves: the round trip is
80
+ * lossless.
68
81
  */
69
82
  export declare function serializeMarker(payload: MarkerPayload): string;
70
83
  export declare function isMarkerStart(node: Node): boolean;
@@ -75,13 +88,17 @@ export declare function parseMarker(node: Node): MarkerPayload | null;
75
88
  * Returns -1 if unterminated.
76
89
  */
77
90
  export declare function findMarkerEnd(nodes: Node[], start: number): number;
78
- /** The inert `<template data-lsp-slot>` a server-rendered island keeps its slot content in. */
91
+ /**
92
+ * The inert `<template data-lsp-slot>` a server-rendered island kept its slot
93
+ * content in before 0.4.4. The payload's `slot` replaced it; this stays so a
94
+ * page built by an older version still hydrates.
95
+ */
79
96
  export declare function isSlotStash(node: Node): boolean;
80
97
  /**
81
98
  * The slot content for a marker.
82
99
  *
83
100
  * For a server-rendered island the nodes between the comments are *output*,
84
- * not slot content - the original is stashed in an inert
85
- * `<template data-lsp-slot>` so the client can rebuild from it.
101
+ * not slot content - the original travels in the payload's `slot` (or, from
102
+ * builds before 0.4.4, in a `<template data-lsp-slot>` stash).
86
103
  */
87
104
  export declare function slotNodesFor(payload: MarkerPayload, between: Node[]): Node[];
package/dist/vite.js CHANGED
@@ -329,7 +329,7 @@ class BindingEvaluator {
329
329
  }
330
330
  if (binding.kind === "prop") {
331
331
  const target = binding.target;
332
- if (Object.is(target.props[binding.name], value))
332
+ if (sameValue(target.props[binding.name], value))
333
333
  return;
334
334
  target.props[binding.name] = value;
335
335
  this.onPropChanged(target);
@@ -442,6 +442,26 @@ function setHidden(el, hidden) {
442
442
  else
443
443
  el.removeAttribute(HIDDEN_ATTR);
444
444
  }
445
+ function sameValue(a, b, depth = 0) {
446
+ if (Object.is(a, b))
447
+ return true;
448
+ if (depth > 20 || !isPlain(a) || !isPlain(b))
449
+ return false;
450
+ if (Array.isArray(a) !== Array.isArray(b))
451
+ return false;
452
+ const keys = Object.keys(a);
453
+ if (keys.length !== Object.keys(b).length)
454
+ return false;
455
+ return keys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && sameValue(a[key], b[key], depth + 1));
456
+ }
457
+ function isPlain(value) {
458
+ if (value === null || typeof value !== "object")
459
+ return false;
460
+ if (Array.isArray(value))
461
+ return true;
462
+ const proto = Object.getPrototypeOf(value);
463
+ return proto === Object.prototype || proto === null;
464
+ }
445
465
  // src/runtime/dom/ForBlock.ts
446
466
  class Row {
447
467
  locals;
@@ -691,7 +711,7 @@ var START = "lsp ";
691
711
  var END = "/lsp";
692
712
  var MARKER_END = `<!--${END}-->`;
693
713
  function serializeMarker(payload) {
694
- const json = JSON.stringify(payload).replace(/-(?=-)/g, "\\u002d");
714
+ const json = JSON.stringify(payload).replace(/-(?=-)/g, "\\u002d").replace(/</g, "\\u003c");
695
715
  return `<!--${START}${json}-->`;
696
716
  }
697
717
  function isMarkerStart(node) {
@@ -729,6 +749,11 @@ function isSlotStash(node) {
729
749
  function slotNodesFor(payload, between) {
730
750
  if (!payload.ssr)
731
751
  return between;
752
+ if (payload.slot) {
753
+ const template = document.createElement("template");
754
+ template.innerHTML = payload.slot;
755
+ return Array.from(template.content.childNodes);
756
+ }
732
757
  const stash = between.find(isSlotStash);
733
758
  return stash ? Array.from(stash.content.childNodes) : [];
734
759
  }
@@ -4547,7 +4572,14 @@ function serializeNodes(nodes) {
4547
4572
 
4548
4573
  // src/ssg/render.ts
4549
4574
  var MAX_BUILD_ROUNDS = 6;
4550
- async function renderPage(options) {
4575
+ var queue = Promise.resolve();
4576
+ function renderPage(options) {
4577
+ const next = queue.then(() => renderPageNow(options));
4578
+ queue = next.catch(() => {
4579
+ });
4580
+ return next;
4581
+ }
4582
+ async function renderPageNow(options) {
4551
4583
  const jsdom = await loadJsdom();
4552
4584
  if (!jsdom) {
4553
4585
  return {
@@ -4746,7 +4778,7 @@ function readIslands(document2) {
4746
4778
  hydrate: payload?.hydrate,
4747
4779
  binds: Object.values(payload?.bind ?? {}),
4748
4780
  handlers: Object.keys(payload?.on ?? {}).length,
4749
- slotted: false,
4781
+ slotted: !!payload?.slot,
4750
4782
  children: []
4751
4783
  };
4752
4784
  (stack[stack.length - 1]?.children ?? roots).push(island);
@@ -4757,9 +4789,6 @@ function readIslands(document2) {
4757
4789
  for (let cursor = island.start.nextSibling;cursor && cursor !== island.end; cursor = cursor.nextSibling) {
4758
4790
  if (cursor.nodeType !== 1)
4759
4791
  continue;
4760
- if (cursor.hasAttribute("data-lsp-slot")) {
4761
- island.slotted = true;
4762
- }
4763
4792
  const id = cursor.getAttribute("data-lsp-instance");
4764
4793
  if (id && !island.instance)
4765
4794
  island.instance = id;
@@ -4821,13 +4850,9 @@ function markRendered(document2, slots) {
4821
4850
  continue;
4822
4851
  }
4823
4852
  payload.ssr = 1;
4853
+ if (slot.html.trim())
4854
+ payload.slot = slot.html;
4824
4855
  slot.start.data = serializeMarker(payload).slice(4, -3);
4825
- if (slot.html.trim()) {
4826
- const template = document2.createElement("template");
4827
- template.setAttribute("data-lsp-slot", "");
4828
- template.innerHTML = slot.html;
4829
- slot.start.parentNode?.insertBefore(template, slot.start.nextSibling);
4830
- }
4831
4856
  rendered++;
4832
4857
  }
4833
4858
  return { rendered, skipped };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightspeed-js/core",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "Lightspeed: a compile-first framework for islands of reactivity in static HTML.",
5
5
  "keywords": [
6
6
  "lightspeed",