thunderous 2.3.8 → 2.3.9

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/index.cjs CHANGED
@@ -72,9 +72,20 @@ var createSignal = (initVal, options) => {
72
72
  return value;
73
73
  };
74
74
  getter.getter = true;
75
+ let stackLength = 0;
75
76
  const setter = (newValue, setterOptions) => {
77
+ stackLength++;
78
+ queueMicrotask(() => stackLength--);
79
+ if (stackLength > 1e3) {
80
+ console.error(new Error("Signal setter stack overflow detected. Possible infinite loop. Bailing out."));
81
+ stackLength = 0;
82
+ return;
83
+ }
76
84
  const isObject = typeof newValue === "object" && newValue !== null;
77
85
  if (!isObject && value === newValue) return;
86
+ if (isObject && typeof value === "object" && value !== null) {
87
+ if (JSON.stringify(value) === JSON.stringify(newValue)) return;
88
+ }
78
89
  const oldValue = value;
79
90
  value = newValue;
80
91
  for (const fn of subscribers) {
@@ -99,8 +110,8 @@ var createSignal = (initVal, options) => {
99
110
  };
100
111
  return [getter, setter];
101
112
  };
102
- var derived = (fn) => {
103
- const [getter, setter] = createSignal();
113
+ var derived = (fn, options) => {
114
+ const [getter, setter] = createSignal(void 0, options);
104
115
  createEffect(() => {
105
116
  try {
106
117
  setter(fn());
@@ -282,12 +293,6 @@ var renderState = {
282
293
  fragmentMap: /* @__PURE__ */ new Map(),
283
294
  registry: typeof customElements !== "undefined" ? customElements : {}
284
295
  };
285
- var logValueError = (value) => {
286
- console.error(
287
- "An invalid value was passed to a template function. Non-primitive values are not supported.\n\nValue:\n",
288
- value
289
- );
290
- };
291
296
  var logPropertyWarning = (propName, element) => {
292
297
  console.warn(
293
298
  `Property "${propName}" does not exist on element:`,
@@ -361,10 +366,6 @@ var processValue = (value) => {
361
366
  renderState.callbackMap.set(uniqueKey, value);
362
367
  return isServer ? String(value()) : `{{callback:${uniqueKey}}}`;
363
368
  }
364
- if (typeof value === "object" && value !== null) {
365
- logValueError(value);
366
- return "";
367
- }
368
369
  return String(value);
369
370
  };
370
371
  var evaluateBindings = (element, fragment) => {
@@ -438,6 +439,7 @@ var evaluateBindings = (element, fragment) => {
438
439
  const attrName = attr.name;
439
440
  if (SIGNAL_BINDING_REGEX.test(attr.value)) {
440
441
  const textList = attr.value.split(SIGNAL_BINDING_REGEX);
442
+ let prevText = attr.value;
441
443
  createEffect(() => {
442
444
  let newText = "";
443
445
  let hasNull = false;
@@ -454,17 +456,18 @@ var evaluateBindings = (element, fragment) => {
454
456
  }
455
457
  }
456
458
  if (hasNull && newText === "null" || attrName.startsWith("prop:")) {
457
- child.removeAttribute(attrName);
459
+ if (child.hasAttribute(attrName)) child.removeAttribute(attrName);
458
460
  } else {
459
- child.setAttribute(attrName, newText);
461
+ if (newText !== prevText) child.setAttribute(attrName, newText);
460
462
  }
461
463
  if (attrName.startsWith("prop:")) {
462
- child.removeAttribute(attrName);
464
+ if (child.hasAttribute(attrName)) child.removeAttribute(attrName);
463
465
  const propName = attrName.replace("prop:", "");
464
466
  const newValue = hasNull && newText === "null" ? null : newText;
465
467
  if (!(propName in child)) logPropertyWarning(propName, child);
466
468
  child[propName] = signal !== void 0 ? signal() : newValue;
467
469
  }
470
+ prevText = newText;
468
471
  });
469
472
  } else if (LEGACY_CALLBACK_BINDING_REGEX.test(attr.value)) {
470
473
  const getRootNode = child.getRootNode.bind(child);
@@ -540,7 +543,7 @@ var css = (strings, ...values) => {
540
543
  value = isServer ? value() : `{{signal:${uniqueKey}}}`;
541
544
  }
542
545
  if (typeof value === "object" && value !== null) {
543
- logValueError(value);
546
+ console.error("Objects are not valid in CSS values. Received:", value);
544
547
  value = "";
545
548
  }
546
549
  cssText += string + String(value);
@@ -667,14 +670,44 @@ var customElement = (render, options) => {
667
670
  });
668
671
  #getPropSignal = ((prop, { allowUndefined = false } = {}) => {
669
672
  if (!(prop in this.#propSignals)) this.#propSignals[prop] = createSignal();
670
- const [_getter, _setter] = this.#propSignals[prop];
671
- let setFromProp = false;
672
- const setter = (newValue) => {
673
- if (!setFromProp) this[prop] = newValue;
674
- _setter(newValue);
673
+ const [_getter, __setter] = this.#propSignals[prop];
674
+ let stackLength = 0;
675
+ const _setter = (newValue, options2) => {
676
+ stackLength++;
677
+ queueMicrotask(() => stackLength--);
678
+ if (stackLength > 999) {
679
+ console.error(
680
+ new Error(
681
+ `Property signal setter stack overflow detected. Possible infinite loop. Bailing out.
682
+
683
+ Property: ${prop}
684
+
685
+ New value: ${JSON.stringify(newValue, null, 2)}
686
+
687
+ Element: <${this.tagName.toLowerCase()}>
688
+ `
689
+ )
690
+ );
691
+ stackLength = 0;
692
+ return;
693
+ }
694
+ __setter(newValue, options2);
675
695
  };
676
- const getter = () => {
677
- const value = _getter();
696
+ const descriptor = Object.getOwnPropertyDescriptor(this, prop);
697
+ if (descriptor === void 0) {
698
+ Object.defineProperty(this, prop, {
699
+ get: _getter,
700
+ set: _setter,
701
+ configurable: false,
702
+ enumerable: true
703
+ });
704
+ }
705
+ const setter = (newValue, options2) => {
706
+ this[prop] = newValue;
707
+ _setter(newValue, options2);
708
+ };
709
+ const getter = (options2) => {
710
+ const value = _getter(options2);
678
711
  if (value === void 0 && !allowUndefined) {
679
712
  const error = new Error(
680
713
  `Error accessing property: "${prop}"
@@ -687,17 +720,6 @@ You must set an initial value before calling a property signal's getter.
687
720
  return value;
688
721
  };
689
722
  getter.getter = true;
690
- const descriptor = Object.getOwnPropertyDescriptor(this, prop);
691
- if (descriptor === void 0) {
692
- Object.defineProperty(this, prop, {
693
- get: getter,
694
- set: (newValue) => {
695
- setFromProp = true;
696
- _setter(newValue);
697
- setFromProp = false;
698
- }
699
- });
700
- }
701
723
  const publicSignal = [getter, setter];
702
724
  publicSignal.init = (value) => {
703
725
  _setter(value);
@@ -705,7 +727,7 @@ You must set an initial value before calling a property signal's getter.
705
727
  };
706
728
  return publicSignal;
707
729
  }).bind(this);
708
- #render() {
730
+ #render = (() => {
709
731
  const root = this.#shadowRoot ?? this;
710
732
  renderState.currentShadowRoot = this.#shadowRoot;
711
733
  renderState.registry = shadowRootOptions.customElements ?? customElements;
@@ -795,7 +817,7 @@ You must set an initial value before calling a property signal's getter.
795
817
  root.replaceChildren(fragment);
796
818
  renderState.currentShadowRoot = null;
797
819
  renderState.registry = customElements;
798
- }
820
+ }).bind(this);
799
821
  static get formAssociated() {
800
822
  return formAssociated;
801
823
  }
package/dist/index.d.cts CHANGED
@@ -163,7 +163,7 @@ declare const createSignal: <T = undefined>(initVal?: T, options?: SignalOptions
163
163
  * const doubleCount = derived(() => getCount() * 2);
164
164
  * ```
165
165
  */
166
- declare const derived: <T>(fn: () => T) => SignalGetter<T>;
166
+ declare const derived: <T>(fn: () => T, options?: SignalOptions) => SignalGetter<T>;
167
167
  /**
168
168
  * Create an effect that runs when signals change.
169
169
  * @example
package/dist/index.d.ts CHANGED
@@ -163,7 +163,7 @@ declare const createSignal: <T = undefined>(initVal?: T, options?: SignalOptions
163
163
  * const doubleCount = derived(() => getCount() * 2);
164
164
  * ```
165
165
  */
166
- declare const derived: <T>(fn: () => T) => SignalGetter<T>;
166
+ declare const derived: <T>(fn: () => T, options?: SignalOptions) => SignalGetter<T>;
167
167
  /**
168
168
  * Create an effect that runs when signals change.
169
169
  * @example
package/dist/index.js CHANGED
@@ -37,9 +37,20 @@ var createSignal = (initVal, options) => {
37
37
  return value;
38
38
  };
39
39
  getter.getter = true;
40
+ let stackLength = 0;
40
41
  const setter = (newValue, setterOptions) => {
42
+ stackLength++;
43
+ queueMicrotask(() => stackLength--);
44
+ if (stackLength > 1e3) {
45
+ console.error(new Error("Signal setter stack overflow detected. Possible infinite loop. Bailing out."));
46
+ stackLength = 0;
47
+ return;
48
+ }
41
49
  const isObject = typeof newValue === "object" && newValue !== null;
42
50
  if (!isObject && value === newValue) return;
51
+ if (isObject && typeof value === "object" && value !== null) {
52
+ if (JSON.stringify(value) === JSON.stringify(newValue)) return;
53
+ }
43
54
  const oldValue = value;
44
55
  value = newValue;
45
56
  for (const fn of subscribers) {
@@ -64,8 +75,8 @@ var createSignal = (initVal, options) => {
64
75
  };
65
76
  return [getter, setter];
66
77
  };
67
- var derived = (fn) => {
68
- const [getter, setter] = createSignal();
78
+ var derived = (fn, options) => {
79
+ const [getter, setter] = createSignal(void 0, options);
69
80
  createEffect(() => {
70
81
  try {
71
82
  setter(fn());
@@ -247,12 +258,6 @@ var renderState = {
247
258
  fragmentMap: /* @__PURE__ */ new Map(),
248
259
  registry: typeof customElements !== "undefined" ? customElements : {}
249
260
  };
250
- var logValueError = (value) => {
251
- console.error(
252
- "An invalid value was passed to a template function. Non-primitive values are not supported.\n\nValue:\n",
253
- value
254
- );
255
- };
256
261
  var logPropertyWarning = (propName, element) => {
257
262
  console.warn(
258
263
  `Property "${propName}" does not exist on element:`,
@@ -326,10 +331,6 @@ var processValue = (value) => {
326
331
  renderState.callbackMap.set(uniqueKey, value);
327
332
  return isServer ? String(value()) : `{{callback:${uniqueKey}}}`;
328
333
  }
329
- if (typeof value === "object" && value !== null) {
330
- logValueError(value);
331
- return "";
332
- }
333
334
  return String(value);
334
335
  };
335
336
  var evaluateBindings = (element, fragment) => {
@@ -403,6 +404,7 @@ var evaluateBindings = (element, fragment) => {
403
404
  const attrName = attr.name;
404
405
  if (SIGNAL_BINDING_REGEX.test(attr.value)) {
405
406
  const textList = attr.value.split(SIGNAL_BINDING_REGEX);
407
+ let prevText = attr.value;
406
408
  createEffect(() => {
407
409
  let newText = "";
408
410
  let hasNull = false;
@@ -419,17 +421,18 @@ var evaluateBindings = (element, fragment) => {
419
421
  }
420
422
  }
421
423
  if (hasNull && newText === "null" || attrName.startsWith("prop:")) {
422
- child.removeAttribute(attrName);
424
+ if (child.hasAttribute(attrName)) child.removeAttribute(attrName);
423
425
  } else {
424
- child.setAttribute(attrName, newText);
426
+ if (newText !== prevText) child.setAttribute(attrName, newText);
425
427
  }
426
428
  if (attrName.startsWith("prop:")) {
427
- child.removeAttribute(attrName);
429
+ if (child.hasAttribute(attrName)) child.removeAttribute(attrName);
428
430
  const propName = attrName.replace("prop:", "");
429
431
  const newValue = hasNull && newText === "null" ? null : newText;
430
432
  if (!(propName in child)) logPropertyWarning(propName, child);
431
433
  child[propName] = signal !== void 0 ? signal() : newValue;
432
434
  }
435
+ prevText = newText;
433
436
  });
434
437
  } else if (LEGACY_CALLBACK_BINDING_REGEX.test(attr.value)) {
435
438
  const getRootNode = child.getRootNode.bind(child);
@@ -505,7 +508,7 @@ var css = (strings, ...values) => {
505
508
  value = isServer ? value() : `{{signal:${uniqueKey}}}`;
506
509
  }
507
510
  if (typeof value === "object" && value !== null) {
508
- logValueError(value);
511
+ console.error("Objects are not valid in CSS values. Received:", value);
509
512
  value = "";
510
513
  }
511
514
  cssText += string + String(value);
@@ -632,14 +635,44 @@ var customElement = (render, options) => {
632
635
  });
633
636
  #getPropSignal = ((prop, { allowUndefined = false } = {}) => {
634
637
  if (!(prop in this.#propSignals)) this.#propSignals[prop] = createSignal();
635
- const [_getter, _setter] = this.#propSignals[prop];
636
- let setFromProp = false;
637
- const setter = (newValue) => {
638
- if (!setFromProp) this[prop] = newValue;
639
- _setter(newValue);
638
+ const [_getter, __setter] = this.#propSignals[prop];
639
+ let stackLength = 0;
640
+ const _setter = (newValue, options2) => {
641
+ stackLength++;
642
+ queueMicrotask(() => stackLength--);
643
+ if (stackLength > 999) {
644
+ console.error(
645
+ new Error(
646
+ `Property signal setter stack overflow detected. Possible infinite loop. Bailing out.
647
+
648
+ Property: ${prop}
649
+
650
+ New value: ${JSON.stringify(newValue, null, 2)}
651
+
652
+ Element: <${this.tagName.toLowerCase()}>
653
+ `
654
+ )
655
+ );
656
+ stackLength = 0;
657
+ return;
658
+ }
659
+ __setter(newValue, options2);
640
660
  };
641
- const getter = () => {
642
- const value = _getter();
661
+ const descriptor = Object.getOwnPropertyDescriptor(this, prop);
662
+ if (descriptor === void 0) {
663
+ Object.defineProperty(this, prop, {
664
+ get: _getter,
665
+ set: _setter,
666
+ configurable: false,
667
+ enumerable: true
668
+ });
669
+ }
670
+ const setter = (newValue, options2) => {
671
+ this[prop] = newValue;
672
+ _setter(newValue, options2);
673
+ };
674
+ const getter = (options2) => {
675
+ const value = _getter(options2);
643
676
  if (value === void 0 && !allowUndefined) {
644
677
  const error = new Error(
645
678
  `Error accessing property: "${prop}"
@@ -652,17 +685,6 @@ You must set an initial value before calling a property signal's getter.
652
685
  return value;
653
686
  };
654
687
  getter.getter = true;
655
- const descriptor = Object.getOwnPropertyDescriptor(this, prop);
656
- if (descriptor === void 0) {
657
- Object.defineProperty(this, prop, {
658
- get: getter,
659
- set: (newValue) => {
660
- setFromProp = true;
661
- _setter(newValue);
662
- setFromProp = false;
663
- }
664
- });
665
- }
666
688
  const publicSignal = [getter, setter];
667
689
  publicSignal.init = (value) => {
668
690
  _setter(value);
@@ -670,7 +692,7 @@ You must set an initial value before calling a property signal's getter.
670
692
  };
671
693
  return publicSignal;
672
694
  }).bind(this);
673
- #render() {
695
+ #render = (() => {
674
696
  const root = this.#shadowRoot ?? this;
675
697
  renderState.currentShadowRoot = this.#shadowRoot;
676
698
  renderState.registry = shadowRootOptions.customElements ?? customElements;
@@ -760,7 +782,7 @@ You must set an initial value before calling a property signal's getter.
760
782
  root.replaceChildren(fragment);
761
783
  renderState.currentShadowRoot = null;
762
784
  renderState.registry = customElements;
763
- }
785
+ }).bind(this);
764
786
  static get formAssociated() {
765
787
  return formAssociated;
766
788
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thunderous",
3
- "version": "2.3.8",
3
+ "version": "2.3.9",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",