bimba-cli 0.5.3 → 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 +35 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bimba-cli",
3
- "version": "0.5.3",
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,24 +67,49 @@ 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
- // Remove duplicate root elements. Re-importing a module with a fresh
73
- // ?t= query causes top-level code (e.g. imba.mount()) to run again,
74
- // which can append a second copy of the root tag to body.
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
+
102
+ if (typeof imba !== 'undefined') imba.commit();
103
+
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.
75
107
  const seen = new Set();
76
108
  [...document.body.children].forEach(el => {
77
109
  const tag = el.tagName.toLowerCase();
78
110
  if (seen.has(tag)) el.remove();
79
111
  else seen.add(tag);
80
112
  });
81
-
82
- // Let Imba re-render in place from the patched prototypes. We do NOT
83
- // touch instance DOM (no innerHTML reset, no symbol cleanup) — that
84
- // would destroy rendered children like open popups / dropdowns and
85
- // collapse any transient UI state. Prototype patching already makes
86
- // the next render use the new methods; imba.commit() triggers it.
87
- if (typeof imba !== 'undefined') imba.commit();
88
113
  });
89
114
  }
90
115