thunderous 2.4.4 → 2.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.
Files changed (3) hide show
  1. package/dist/index.cjs +107 -64
  2. package/dist/index.js +107 -64
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -165,12 +165,6 @@ var createEffect = (fn, value) => {
165
165
 
166
166
  // src/utilities.ts
167
167
  var NOOP = () => void 0;
168
- var queryComment = (node, comment) => {
169
- const walker = document.createTreeWalker(node, NodeFilter.SHOW_COMMENT, {
170
- acceptNode: (n) => n.nodeValue === comment ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
171
- });
172
- return walker.nextNode();
173
- };
174
168
  var queryChildren = (children, selector) => {
175
169
  for (const child of children) {
176
170
  if (child instanceof Element && child.matches(selector)) {
@@ -326,7 +320,7 @@ var clientOnlyCallback = (fn) => {
326
320
  // src/render.ts
327
321
  var CALLBACK_BINDING_REGEX = /(\{\{callback:.+\}\})/;
328
322
  var LEGACY_CALLBACK_BINDING_REGEX = /(this.getRootNode\(\).host.__customCallbackFns.get\('.+'\)\(event\))/;
329
- var SIGNAL_BINDING_REGEX = /(\{\{signal:.+\}\})/;
323
+ var SIGNAL_BINDING_REGEX = /(\{\{signal:.+?\}\})/;
330
324
  var FRAGMENT_ATTRIBUTE = "___thunderous-fragment";
331
325
  var renderState = {
332
326
  currentShadowRoot: null,
@@ -351,19 +345,34 @@ var logPropertyWarning = (propName, element) => {
351
345
  "\n\nThunderous will attempt to set the property anyway, but this may result in unexpected behavior. Please make sure the property exists on the element prior to setting it."
352
346
  );
353
347
  };
354
- var asNodeList = (value, parent) => {
348
+ var asNodeList = (value, parent, autoKey) => {
349
+ if (value === null || value === void 0) return [];
355
350
  if (typeof value === "string") return [new Text(value)];
356
- if (value instanceof DocumentFragment) return Array.from(value.children);
351
+ if (typeof value === "number" || typeof value === "boolean") return [new Text(String(value))];
352
+ if (value instanceof DocumentFragment) {
353
+ const children = Array.from(value.children);
354
+ if (autoKey !== void 0 && children.length > 0) {
355
+ const child = children[0];
356
+ if (child instanceof Element && child.getAttribute("key") === null) {
357
+ child.setAttribute("key", String(autoKey));
358
+ }
359
+ }
360
+ return children;
361
+ }
357
362
  if (Array.isArray(value)) {
358
363
  const nodeList = [];
359
364
  let count = 0;
360
365
  const keys = /* @__PURE__ */ new Set();
361
366
  for (const item of value) {
362
367
  const cachedItem = item instanceof DocumentFragment ? renderState.childrenMap.get(item) : void 0;
363
- const children = cachedItem ?? asNodeList(item, parent);
368
+ const children = cachedItem ?? asNodeList(item, parent, item instanceof DocumentFragment ? count : void 0);
364
369
  if (cachedItem === void 0 && item instanceof DocumentFragment) {
365
370
  renderState.childrenMap.set(item, children);
366
371
  }
372
+ if (!(item instanceof DocumentFragment)) {
373
+ nodeList.push(...children);
374
+ continue;
375
+ }
367
376
  if (children.length > 1) {
368
377
  console.error(
369
378
  "When rendering arrays, fragments must contain only one top-level element at a time. Error occured in:",
@@ -416,73 +425,77 @@ var processValue = (value) => {
416
425
  renderState.callbackMap.set(uniqueKey, value);
417
426
  return isServer ? String(value()) : `{{callback:${uniqueKey}}}`;
418
427
  }
419
- return String(value);
428
+ return value === null || value === void 0 ? "" : String(value);
420
429
  };
421
430
  var evaluateBindings = (element, fragment) => {
422
431
  for (const child of Array.from(element.childNodes)) {
423
432
  if (child instanceof Text && SIGNAL_BINDING_REGEX.test(child.data)) {
424
433
  const textList = child.data.split(SIGNAL_BINDING_REGEX);
425
- const nextSibling = child.nextSibling;
426
- const prevSibling = child.previousSibling;
427
- textList.forEach((text, i) => {
434
+ const allInitialChildren = [];
435
+ const signalEntries = [];
436
+ let signalIndex = 0;
437
+ const totalSignals = textList.filter((t) => SIGNAL_BINDING_REGEX.test(t)).length;
438
+ textList.forEach((text) => {
428
439
  const uniqueKey = SIGNAL_BINDING_REGEX.test(text) ? text.replace(/\{\{signal:(.+)\}\}/, "$1") : void 0;
429
440
  const signal = uniqueKey !== void 0 ? renderState.signalMap.get(uniqueKey) : void 0;
430
441
  const newValue = signal !== void 0 ? signal() : text;
431
- const initialChildren = asNodeList(newValue, element);
432
- if (i === 0) {
433
- child.replaceWith(...initialChildren);
434
- } else {
435
- const endAnchor2 = queryComment(element, `${uniqueKey}:end`) ?? nextSibling;
436
- if (endAnchor2 !== null) {
437
- endAnchor2.before(...initialChildren);
438
- } else {
439
- element.append(...initialChildren);
440
- }
442
+ const autoKey = signal !== void 0 && totalSignals > 1 ? signalIndex++ : void 0;
443
+ const initialChildren = asNodeList(newValue, element, autoKey);
444
+ allInitialChildren.push(...initialChildren);
445
+ if (uniqueKey !== void 0 && signal !== void 0) {
446
+ signalEntries.push({ uniqueKey, signal, initialChildren, autoKey });
441
447
  }
442
- if (uniqueKey === void 0) return;
448
+ });
449
+ child.replaceWith(...allInitialChildren);
450
+ signalEntries.forEach(({ uniqueKey, signal, initialChildren, autoKey }) => {
451
+ const firstChild = initialChildren[0];
452
+ const lastChild = initialChildren[initialChildren.length - 1];
453
+ if (uniqueKey === void 0 || firstChild === void 0) return;
443
454
  const startAnchor = document.createComment(`${uniqueKey}:start`);
444
- if (prevSibling !== null) {
445
- prevSibling.after(startAnchor);
446
- } else {
447
- element.prepend(startAnchor);
448
- }
455
+ firstChild.before(startAnchor);
449
456
  const endAnchor = document.createComment(`${uniqueKey}:end`);
450
- if (nextSibling !== null) {
451
- nextSibling.before(endAnchor);
457
+ if (lastChild !== void 0) {
458
+ lastChild.after(endAnchor);
452
459
  } else {
453
- element.append(endAnchor);
460
+ startAnchor.after(endAnchor);
454
461
  }
455
462
  const bindText = (node, signal2) => {
456
463
  createEffect(({ destroy }) => {
457
464
  const result = signal2();
458
465
  if (Array.isArray(result)) {
459
466
  destroy();
460
- bindArray(signal2);
467
+ bindArray(signal2, autoKey);
461
468
  return;
462
469
  }
463
470
  if (result instanceof DocumentFragment) {
464
471
  destroy();
465
- bindFragment(signal2);
472
+ bindFragment(signal2, initialChildren, autoKey);
466
473
  return;
467
474
  }
468
- node.data = result === null ? "" : String(result);
475
+ node.data = result === null || result === void 0 ? "" : String(result);
469
476
  });
470
477
  };
471
- const bindArray = (signal2) => {
478
+ const bindArray = (signal2, autoKey2) => {
472
479
  createEffect(
473
480
  ({ lastValue: oldChildren, destroy }) => {
474
481
  const result = signal2();
475
- const newChildren = asNodeList(result, element);
476
- const firstChild = newChildren[0];
477
- if (!Array.isArray(result) && newChildren.length === 1 && firstChild instanceof DocumentFragment) {
478
- destroy();
479
- bindFragment(signal2);
480
- return;
481
- }
482
- if (newChildren.length === 1 && firstChild instanceof Text) {
483
- destroy();
484
- bindText(firstChild, signal2);
485
- return;
482
+ const newChildren = asNodeList(result, element, autoKey2);
483
+ const firstChild2 = newChildren[0];
484
+ if (!Array.isArray(result)) {
485
+ if (newChildren.length === 1 && firstChild2 instanceof DocumentFragment) {
486
+ destroy();
487
+ bindFragment(signal2, initialChildren, autoKey2);
488
+ return;
489
+ }
490
+ if (newChildren.length === 1 && firstChild2 instanceof Text) {
491
+ while (startAnchor.nextSibling !== endAnchor) {
492
+ startAnchor.nextSibling?.remove();
493
+ }
494
+ startAnchor.after(firstChild2);
495
+ destroy();
496
+ bindText(firstChild2, signal2);
497
+ return;
498
+ }
486
499
  }
487
500
  while (startAnchor.nextSibling !== endAnchor) {
488
501
  startAnchor.nextSibling?.remove();
@@ -514,36 +527,50 @@ var evaluateBindings = (element, fragment) => {
514
527
  null
515
528
  );
516
529
  };
517
- const bindFragment = (signal2) => {
530
+ const bindFragment = (signal2, initialChildren2, autoKey2) => {
518
531
  const initialFragment = signal2();
519
- renderState.childrenMap.set(initialFragment, Array.from(initialFragment.childNodes));
532
+ const firstInitialChild = initialChildren2[0];
533
+ if (firstInitialChild instanceof Element) {
534
+ renderState.childrenMap.set(initialFragment, initialChildren2);
535
+ }
520
536
  createEffect(({ destroy }) => {
521
537
  const result = signal2();
522
- const cachedChildren = renderState.childrenMap.get(initialFragment);
523
- const children = cachedChildren ?? asNodeList(result, element);
538
+ const cachedChildren = result instanceof DocumentFragment ? renderState.childrenMap.get(result) : void 0;
539
+ const children = cachedChildren ?? asNodeList(result, element, autoKey2);
540
+ if (result instanceof DocumentFragment && !renderState.childrenMap.has(result)) {
541
+ renderState.childrenMap.set(result, children);
542
+ }
524
543
  if (Array.isArray(result)) {
525
544
  destroy();
526
- bindArray(signal2);
545
+ bindArray(signal2, autoKey2);
527
546
  return;
528
547
  }
529
- if (result instanceof Text) {
530
- const children2 = asNodeList(result, element);
531
- const text2 = children2[0];
548
+ if (!(result instanceof DocumentFragment) && result !== null && result !== void 0) {
549
+ while (startAnchor.nextSibling !== endAnchor) {
550
+ startAnchor.nextSibling?.remove();
551
+ }
552
+ const children2 = asNodeList(result, element, autoKey2);
553
+ const text = children2[0];
554
+ startAnchor.after(text);
532
555
  destroy();
533
- bindText(text2, signal2);
556
+ bindText(text, signal2);
534
557
  return;
535
558
  }
536
559
  while (startAnchor.nextSibling !== endAnchor) {
537
560
  startAnchor.nextSibling?.remove();
538
561
  }
562
+ if (result === null || result === void 0) {
563
+ return;
564
+ }
539
565
  startAnchor.after(...children);
540
566
  });
541
567
  };
542
568
  if (signal !== void 0) {
543
- if (Array.isArray(newValue)) {
544
- bindArray(signal);
545
- } else if (initialChildren instanceof DocumentFragment) {
546
- bindFragment(signal);
569
+ const currentValue = signal();
570
+ if (Array.isArray(currentValue)) {
571
+ bindArray(signal, autoKey);
572
+ } else if (currentValue instanceof DocumentFragment) {
573
+ bindFragment(signal, initialChildren, autoKey);
547
574
  } else {
548
575
  const initialChild = initialChildren[0];
549
576
  bindText(initialChild, signal);
@@ -816,7 +843,9 @@ var customElement = (render, options) => {
816
843
  for (const mutation of mutations) {
817
844
  const attrName = mutation.attributeName;
818
845
  if (mutation.type !== "attributes" || attrName === null) continue;
819
- if (!(attrName in this.#attrSignals)) this.#attrSignals[attrName] = createSignal(null);
846
+ if (!(attrName in this.#attrSignals)) {
847
+ this.#attrSignals[attrName] = createSignal(this.getAttribute(attrName));
848
+ }
820
849
  const [getter, setter] = this.#attrSignals[attrName];
821
850
  const oldValue = getter();
822
851
  const newValue = this.getAttribute(attrName);
@@ -916,7 +945,9 @@ You must set an initial value before calling a property signal's getter.
916
945
  {},
917
946
  {
918
947
  get: (_, prop) => {
919
- if (!(prop in this.#attrSignals)) this.#attrSignals[prop] = createSignal(null);
948
+ if (!(prop in this.#attrSignals)) {
949
+ this.#attrSignals[prop] = createSignal(this.getAttribute(prop));
950
+ }
920
951
  const [getter] = this.#attrSignals[prop];
921
952
  const setter = (newValue) => this.setAttribute(prop, newValue);
922
953
  return [getter, setter];
@@ -1003,7 +1034,9 @@ You must set an initial value before calling a property signal's getter.
1003
1034
  }
1004
1035
  connectedCallback() {
1005
1036
  for (const [attrName, attr] of this.#attributesAsPropertiesMap) {
1006
- if (!(attrName in this.#attrSignals)) this.#attrSignals[attrName] = createSignal(null);
1037
+ if (!(attrName in this.#attrSignals)) {
1038
+ this.#attrSignals[attrName] = createSignal(this.getAttribute(attrName));
1039
+ }
1007
1040
  const propName = attr.prop;
1008
1041
  const [getter] = this.#getPropSignal(propName, { allowUndefined: true });
1009
1042
  let busy = false;
@@ -1020,6 +1053,16 @@ You must set an initial value before calling a property signal's getter.
1020
1053
  busy = false;
1021
1054
  });
1022
1055
  }
1056
+ for (const attrName of Object.keys(this.#attrSignals)) {
1057
+ const signal = this.#attrSignals[attrName];
1058
+ if (signal) {
1059
+ const [getter, setter] = signal;
1060
+ const currentValue = this.getAttribute(attrName);
1061
+ if (getter() !== currentValue) {
1062
+ setter(currentValue);
1063
+ }
1064
+ }
1065
+ }
1023
1066
  if (this.#observer !== null) {
1024
1067
  this.#observer.observe(this, { attributes: true });
1025
1068
  }
package/dist/index.js CHANGED
@@ -128,12 +128,6 @@ var createEffect = (fn, value) => {
128
128
 
129
129
  // src/utilities.ts
130
130
  var NOOP = () => void 0;
131
- var queryComment = (node, comment) => {
132
- const walker = document.createTreeWalker(node, NodeFilter.SHOW_COMMENT, {
133
- acceptNode: (n) => n.nodeValue === comment ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
134
- });
135
- return walker.nextNode();
136
- };
137
131
  var queryChildren = (children, selector) => {
138
132
  for (const child of children) {
139
133
  if (child instanceof Element && child.matches(selector)) {
@@ -289,7 +283,7 @@ var clientOnlyCallback = (fn) => {
289
283
  // src/render.ts
290
284
  var CALLBACK_BINDING_REGEX = /(\{\{callback:.+\}\})/;
291
285
  var LEGACY_CALLBACK_BINDING_REGEX = /(this.getRootNode\(\).host.__customCallbackFns.get\('.+'\)\(event\))/;
292
- var SIGNAL_BINDING_REGEX = /(\{\{signal:.+\}\})/;
286
+ var SIGNAL_BINDING_REGEX = /(\{\{signal:.+?\}\})/;
293
287
  var FRAGMENT_ATTRIBUTE = "___thunderous-fragment";
294
288
  var renderState = {
295
289
  currentShadowRoot: null,
@@ -314,19 +308,34 @@ var logPropertyWarning = (propName, element) => {
314
308
  "\n\nThunderous will attempt to set the property anyway, but this may result in unexpected behavior. Please make sure the property exists on the element prior to setting it."
315
309
  );
316
310
  };
317
- var asNodeList = (value, parent) => {
311
+ var asNodeList = (value, parent, autoKey) => {
312
+ if (value === null || value === void 0) return [];
318
313
  if (typeof value === "string") return [new Text(value)];
319
- if (value instanceof DocumentFragment) return Array.from(value.children);
314
+ if (typeof value === "number" || typeof value === "boolean") return [new Text(String(value))];
315
+ if (value instanceof DocumentFragment) {
316
+ const children = Array.from(value.children);
317
+ if (autoKey !== void 0 && children.length > 0) {
318
+ const child = children[0];
319
+ if (child instanceof Element && child.getAttribute("key") === null) {
320
+ child.setAttribute("key", String(autoKey));
321
+ }
322
+ }
323
+ return children;
324
+ }
320
325
  if (Array.isArray(value)) {
321
326
  const nodeList = [];
322
327
  let count = 0;
323
328
  const keys = /* @__PURE__ */ new Set();
324
329
  for (const item of value) {
325
330
  const cachedItem = item instanceof DocumentFragment ? renderState.childrenMap.get(item) : void 0;
326
- const children = cachedItem ?? asNodeList(item, parent);
331
+ const children = cachedItem ?? asNodeList(item, parent, item instanceof DocumentFragment ? count : void 0);
327
332
  if (cachedItem === void 0 && item instanceof DocumentFragment) {
328
333
  renderState.childrenMap.set(item, children);
329
334
  }
335
+ if (!(item instanceof DocumentFragment)) {
336
+ nodeList.push(...children);
337
+ continue;
338
+ }
330
339
  if (children.length > 1) {
331
340
  console.error(
332
341
  "When rendering arrays, fragments must contain only one top-level element at a time. Error occured in:",
@@ -379,73 +388,77 @@ var processValue = (value) => {
379
388
  renderState.callbackMap.set(uniqueKey, value);
380
389
  return isServer ? String(value()) : `{{callback:${uniqueKey}}}`;
381
390
  }
382
- return String(value);
391
+ return value === null || value === void 0 ? "" : String(value);
383
392
  };
384
393
  var evaluateBindings = (element, fragment) => {
385
394
  for (const child of Array.from(element.childNodes)) {
386
395
  if (child instanceof Text && SIGNAL_BINDING_REGEX.test(child.data)) {
387
396
  const textList = child.data.split(SIGNAL_BINDING_REGEX);
388
- const nextSibling = child.nextSibling;
389
- const prevSibling = child.previousSibling;
390
- textList.forEach((text, i) => {
397
+ const allInitialChildren = [];
398
+ const signalEntries = [];
399
+ let signalIndex = 0;
400
+ const totalSignals = textList.filter((t) => SIGNAL_BINDING_REGEX.test(t)).length;
401
+ textList.forEach((text) => {
391
402
  const uniqueKey = SIGNAL_BINDING_REGEX.test(text) ? text.replace(/\{\{signal:(.+)\}\}/, "$1") : void 0;
392
403
  const signal = uniqueKey !== void 0 ? renderState.signalMap.get(uniqueKey) : void 0;
393
404
  const newValue = signal !== void 0 ? signal() : text;
394
- const initialChildren = asNodeList(newValue, element);
395
- if (i === 0) {
396
- child.replaceWith(...initialChildren);
397
- } else {
398
- const endAnchor2 = queryComment(element, `${uniqueKey}:end`) ?? nextSibling;
399
- if (endAnchor2 !== null) {
400
- endAnchor2.before(...initialChildren);
401
- } else {
402
- element.append(...initialChildren);
403
- }
405
+ const autoKey = signal !== void 0 && totalSignals > 1 ? signalIndex++ : void 0;
406
+ const initialChildren = asNodeList(newValue, element, autoKey);
407
+ allInitialChildren.push(...initialChildren);
408
+ if (uniqueKey !== void 0 && signal !== void 0) {
409
+ signalEntries.push({ uniqueKey, signal, initialChildren, autoKey });
404
410
  }
405
- if (uniqueKey === void 0) return;
411
+ });
412
+ child.replaceWith(...allInitialChildren);
413
+ signalEntries.forEach(({ uniqueKey, signal, initialChildren, autoKey }) => {
414
+ const firstChild = initialChildren[0];
415
+ const lastChild = initialChildren[initialChildren.length - 1];
416
+ if (uniqueKey === void 0 || firstChild === void 0) return;
406
417
  const startAnchor = document.createComment(`${uniqueKey}:start`);
407
- if (prevSibling !== null) {
408
- prevSibling.after(startAnchor);
409
- } else {
410
- element.prepend(startAnchor);
411
- }
418
+ firstChild.before(startAnchor);
412
419
  const endAnchor = document.createComment(`${uniqueKey}:end`);
413
- if (nextSibling !== null) {
414
- nextSibling.before(endAnchor);
420
+ if (lastChild !== void 0) {
421
+ lastChild.after(endAnchor);
415
422
  } else {
416
- element.append(endAnchor);
423
+ startAnchor.after(endAnchor);
417
424
  }
418
425
  const bindText = (node, signal2) => {
419
426
  createEffect(({ destroy }) => {
420
427
  const result = signal2();
421
428
  if (Array.isArray(result)) {
422
429
  destroy();
423
- bindArray(signal2);
430
+ bindArray(signal2, autoKey);
424
431
  return;
425
432
  }
426
433
  if (result instanceof DocumentFragment) {
427
434
  destroy();
428
- bindFragment(signal2);
435
+ bindFragment(signal2, initialChildren, autoKey);
429
436
  return;
430
437
  }
431
- node.data = result === null ? "" : String(result);
438
+ node.data = result === null || result === void 0 ? "" : String(result);
432
439
  });
433
440
  };
434
- const bindArray = (signal2) => {
441
+ const bindArray = (signal2, autoKey2) => {
435
442
  createEffect(
436
443
  ({ lastValue: oldChildren, destroy }) => {
437
444
  const result = signal2();
438
- const newChildren = asNodeList(result, element);
439
- const firstChild = newChildren[0];
440
- if (!Array.isArray(result) && newChildren.length === 1 && firstChild instanceof DocumentFragment) {
441
- destroy();
442
- bindFragment(signal2);
443
- return;
444
- }
445
- if (newChildren.length === 1 && firstChild instanceof Text) {
446
- destroy();
447
- bindText(firstChild, signal2);
448
- return;
445
+ const newChildren = asNodeList(result, element, autoKey2);
446
+ const firstChild2 = newChildren[0];
447
+ if (!Array.isArray(result)) {
448
+ if (newChildren.length === 1 && firstChild2 instanceof DocumentFragment) {
449
+ destroy();
450
+ bindFragment(signal2, initialChildren, autoKey2);
451
+ return;
452
+ }
453
+ if (newChildren.length === 1 && firstChild2 instanceof Text) {
454
+ while (startAnchor.nextSibling !== endAnchor) {
455
+ startAnchor.nextSibling?.remove();
456
+ }
457
+ startAnchor.after(firstChild2);
458
+ destroy();
459
+ bindText(firstChild2, signal2);
460
+ return;
461
+ }
449
462
  }
450
463
  while (startAnchor.nextSibling !== endAnchor) {
451
464
  startAnchor.nextSibling?.remove();
@@ -477,36 +490,50 @@ var evaluateBindings = (element, fragment) => {
477
490
  null
478
491
  );
479
492
  };
480
- const bindFragment = (signal2) => {
493
+ const bindFragment = (signal2, initialChildren2, autoKey2) => {
481
494
  const initialFragment = signal2();
482
- renderState.childrenMap.set(initialFragment, Array.from(initialFragment.childNodes));
495
+ const firstInitialChild = initialChildren2[0];
496
+ if (firstInitialChild instanceof Element) {
497
+ renderState.childrenMap.set(initialFragment, initialChildren2);
498
+ }
483
499
  createEffect(({ destroy }) => {
484
500
  const result = signal2();
485
- const cachedChildren = renderState.childrenMap.get(initialFragment);
486
- const children = cachedChildren ?? asNodeList(result, element);
501
+ const cachedChildren = result instanceof DocumentFragment ? renderState.childrenMap.get(result) : void 0;
502
+ const children = cachedChildren ?? asNodeList(result, element, autoKey2);
503
+ if (result instanceof DocumentFragment && !renderState.childrenMap.has(result)) {
504
+ renderState.childrenMap.set(result, children);
505
+ }
487
506
  if (Array.isArray(result)) {
488
507
  destroy();
489
- bindArray(signal2);
508
+ bindArray(signal2, autoKey2);
490
509
  return;
491
510
  }
492
- if (result instanceof Text) {
493
- const children2 = asNodeList(result, element);
494
- const text2 = children2[0];
511
+ if (!(result instanceof DocumentFragment) && result !== null && result !== void 0) {
512
+ while (startAnchor.nextSibling !== endAnchor) {
513
+ startAnchor.nextSibling?.remove();
514
+ }
515
+ const children2 = asNodeList(result, element, autoKey2);
516
+ const text = children2[0];
517
+ startAnchor.after(text);
495
518
  destroy();
496
- bindText(text2, signal2);
519
+ bindText(text, signal2);
497
520
  return;
498
521
  }
499
522
  while (startAnchor.nextSibling !== endAnchor) {
500
523
  startAnchor.nextSibling?.remove();
501
524
  }
525
+ if (result === null || result === void 0) {
526
+ return;
527
+ }
502
528
  startAnchor.after(...children);
503
529
  });
504
530
  };
505
531
  if (signal !== void 0) {
506
- if (Array.isArray(newValue)) {
507
- bindArray(signal);
508
- } else if (initialChildren instanceof DocumentFragment) {
509
- bindFragment(signal);
532
+ const currentValue = signal();
533
+ if (Array.isArray(currentValue)) {
534
+ bindArray(signal, autoKey);
535
+ } else if (currentValue instanceof DocumentFragment) {
536
+ bindFragment(signal, initialChildren, autoKey);
510
537
  } else {
511
538
  const initialChild = initialChildren[0];
512
539
  bindText(initialChild, signal);
@@ -779,7 +806,9 @@ var customElement = (render, options) => {
779
806
  for (const mutation of mutations) {
780
807
  const attrName = mutation.attributeName;
781
808
  if (mutation.type !== "attributes" || attrName === null) continue;
782
- if (!(attrName in this.#attrSignals)) this.#attrSignals[attrName] = createSignal(null);
809
+ if (!(attrName in this.#attrSignals)) {
810
+ this.#attrSignals[attrName] = createSignal(this.getAttribute(attrName));
811
+ }
783
812
  const [getter, setter] = this.#attrSignals[attrName];
784
813
  const oldValue = getter();
785
814
  const newValue = this.getAttribute(attrName);
@@ -879,7 +908,9 @@ You must set an initial value before calling a property signal's getter.
879
908
  {},
880
909
  {
881
910
  get: (_, prop) => {
882
- if (!(prop in this.#attrSignals)) this.#attrSignals[prop] = createSignal(null);
911
+ if (!(prop in this.#attrSignals)) {
912
+ this.#attrSignals[prop] = createSignal(this.getAttribute(prop));
913
+ }
883
914
  const [getter] = this.#attrSignals[prop];
884
915
  const setter = (newValue) => this.setAttribute(prop, newValue);
885
916
  return [getter, setter];
@@ -966,7 +997,9 @@ You must set an initial value before calling a property signal's getter.
966
997
  }
967
998
  connectedCallback() {
968
999
  for (const [attrName, attr] of this.#attributesAsPropertiesMap) {
969
- if (!(attrName in this.#attrSignals)) this.#attrSignals[attrName] = createSignal(null);
1000
+ if (!(attrName in this.#attrSignals)) {
1001
+ this.#attrSignals[attrName] = createSignal(this.getAttribute(attrName));
1002
+ }
970
1003
  const propName = attr.prop;
971
1004
  const [getter] = this.#getPropSignal(propName, { allowUndefined: true });
972
1005
  let busy = false;
@@ -983,6 +1016,16 @@ You must set an initial value before calling a property signal's getter.
983
1016
  busy = false;
984
1017
  });
985
1018
  }
1019
+ for (const attrName of Object.keys(this.#attrSignals)) {
1020
+ const signal = this.#attrSignals[attrName];
1021
+ if (signal) {
1022
+ const [getter, setter] = signal;
1023
+ const currentValue = this.getAttribute(attrName);
1024
+ if (getter() !== currentValue) {
1025
+ setter(currentValue);
1026
+ }
1027
+ }
1028
+ }
986
1029
  if (this.#observer !== null) {
987
1030
  this.#observer.observe(this, { attributes: true });
988
1031
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thunderous",
3
- "version": "2.4.4",
3
+ "version": "2.4.5",
4
4
  "description": "A lightweight, functional web components library that brings the power of signals to your UI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",