bimba-cli 0.5.4 → 0.5.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 (2) hide show
  1. package/package.json +1 -1
  2. package/serve.js +33 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bimba-cli",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/HeapVoid/bimba.git"
package/serve.js CHANGED
@@ -67,26 +67,43 @@ const hmrClient = `
67
67
  _hotTags = [];
68
68
 
69
69
  import('/' + file + '?t=' + Date.now()).then(() => {
70
+ const tags = _hotTags;
70
71
  _hotTags = [];
71
72
 
72
- // Let Imba re-render in place from the patched prototypes. We do NOT
73
- // touch instance DOM (no innerHTML reset, no symbol cleanup) — that
74
- // would destroy rendered children like open popups / dropdowns and
75
- // collapse any transient UI state. Prototype patching already makes
76
- // the next render use the new methods; imba.commit() triggers it.
73
+ // For each tag that was just (re)defined, find all live instances
74
+ // and reset their imba render-state.
75
+ //
76
+ // Why this is needed: imba stores cached references to rendered
77
+ // child slots on the element instance under Symbol keys. Each
78
+ // compilation of a .imba file creates a NEW set of Symbols, so
79
+ // after HMR the new render() walks new keys, finds nothing, and
80
+ // APPENDS a fresh subtree alongside the stale one — visible as a
81
+ // duplicated popup/section inside the same tag.
82
+ //
83
+ // Fix: snapshot own enumerable properties (instance state like
84
+ // "opened", "data", etc.), wipe Symbol-keyed slots and innerHTML,
85
+ // restore instance state, then re-render. The DOM is rebuilt for
86
+ // the patched tag only, but instance state (and therefore "the
87
+ // popup is open with these props") is preserved. Other tags are
88
+ // untouched, so unrelated popups/dropdowns keep their state.
89
+ for (const tag of tags) {
90
+ document.querySelectorAll(tag).forEach(el => {
91
+ const state = {};
92
+ for (const k of Object.keys(el)) state[k] = el[k];
93
+ for (const sym of Object.getOwnPropertySymbols(el)) {
94
+ try { delete el[sym]; } catch(_) {}
95
+ }
96
+ el.innerHTML = '';
97
+ Object.assign(el, state);
98
+ try { el.render && el.render(); } catch(_) {}
99
+ });
100
+ }
101
+
77
102
  if (typeof imba !== 'undefined') imba.commit();
78
103
 
79
- // Dedupe direct body children by tag — runs AFTER commit so it
80
- // catches both:
81
- // 1) Re-importing top-level code that calls imba.mount() again
82
- // (appends a second root tag to body).
83
- // 2) Detached popups: a popup uses imba.mount(self) to reparent
84
- // itself to body, so on commit the parent re-renders, doesn't
85
- // see the popup among its children, and creates a brand-new
86
- // instance which gets appended to body. The original (with
87
- // its state) is first in body.children; the freshly-created
88
- // duplicate is last — removing later occurrences preserves
89
- // state.
104
+ // Dedupe direct body children by tag — handles re-importing
105
+ // top-level code that calls imba.mount() again, which would
106
+ // append a second copy of the root tag.
90
107
  const seen = new Set();
91
108
  [...document.body.children].forEach(el => {
92
109
  const tag = el.tagName.toLowerCase();