micra.js 1.0.0 → 2.0.0
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/README.md +7 -4
- package/dist/dom/events.d.ts +9 -4
- package/dist/dom/query.d.ts +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/micra.cjs.js +219 -53
- package/dist/micra.cjs.js.map +3 -3
- package/dist/micra.esm.js +219 -53
- package/dist/micra.esm.js.map +3 -3
- package/dist/micra.js +219 -53
- package/dist/micra.js.map +3 -3
- package/dist/micra.min.js +2 -2
- package/dist/types.d.ts +33 -4
- package/dist/utils/expr.d.ts +12 -1
- package/package.json +2 -2
- package/src/core/bus.ts +4 -1
- package/src/core/mount.ts +54 -3
- package/src/dom/directives.ts +107 -29
- package/src/dom/each.ts +14 -2
- package/src/dom/events.ts +50 -20
- package/src/dom/query.ts +15 -1
- package/src/index.ts +1 -1
- package/src/types.ts +36 -4
- package/src/utils/expr.ts +119 -7
- package/src/utils/fetch.ts +2 -2
package/dist/micra.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Micra.js
|
|
1
|
+
/* Micra.js v2.0.0 — https://github.com/micra-js/micra — MIT */
|
|
2
2
|
"use strict";
|
|
3
3
|
var Micra = (() => {
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -66,9 +66,9 @@ var Micra = (() => {
|
|
|
66
66
|
}
|
|
67
67
|
if (Object.keys(params).length)
|
|
68
68
|
finalUrl += (url.includes("?") ? "&" : "?") + new URLSearchParams(params);
|
|
69
|
-
} else {
|
|
69
|
+
} else if (options.body !== void 0) {
|
|
70
70
|
headers["Content-Type"] = "application/json";
|
|
71
|
-
body = JSON.stringify(options.body
|
|
71
|
+
body = JSON.stringify(options.body);
|
|
72
72
|
}
|
|
73
73
|
const res = await fetch(finalUrl, {
|
|
74
74
|
method,
|
|
@@ -115,10 +115,69 @@ var Micra = (() => {
|
|
|
115
115
|
|
|
116
116
|
// src/utils/expr.ts
|
|
117
117
|
var exprCache = /* @__PURE__ */ new Map();
|
|
118
|
+
var warnedRuntime = /* @__PURE__ */ new Set();
|
|
118
119
|
var SIMPLE_PATH = /^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/;
|
|
120
|
+
var ALLOWED_GLOBALS = /* @__PURE__ */ new Set([
|
|
121
|
+
"Math",
|
|
122
|
+
"JSON",
|
|
123
|
+
"Date",
|
|
124
|
+
"String",
|
|
125
|
+
"Number",
|
|
126
|
+
"Boolean",
|
|
127
|
+
"Array",
|
|
128
|
+
"Object",
|
|
129
|
+
"parseInt",
|
|
130
|
+
"parseFloat",
|
|
131
|
+
"isNaN",
|
|
132
|
+
"isFinite",
|
|
133
|
+
"NaN",
|
|
134
|
+
"Infinity",
|
|
135
|
+
"undefined"
|
|
136
|
+
]);
|
|
137
|
+
var PARAM_S = "$s";
|
|
138
|
+
var PARAM_SAFE = "$safe";
|
|
139
|
+
var SAFE_OUTER = new Proxy(/* @__PURE__ */ Object.create(null), {
|
|
140
|
+
has(_target, key) {
|
|
141
|
+
if (typeof key !== "string") return false;
|
|
142
|
+
if (key === PARAM_S || key === PARAM_SAFE) return false;
|
|
143
|
+
return !ALLOWED_GLOBALS.has(key);
|
|
144
|
+
},
|
|
145
|
+
get() {
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
var safeWrapCache = /* @__PURE__ */ new WeakMap();
|
|
150
|
+
var OBJ_PROTO_KEYS = new Set(Object.getOwnPropertyNames(Object.prototype));
|
|
151
|
+
function safeStateWrap(state) {
|
|
152
|
+
const cached = safeWrapCache.get(state);
|
|
153
|
+
if (cached) return cached;
|
|
154
|
+
const wrapped = new Proxy(state, {
|
|
155
|
+
has(target, key) {
|
|
156
|
+
return safeStateHas(target, key);
|
|
157
|
+
},
|
|
158
|
+
get(target, key) {
|
|
159
|
+
return Reflect.get(target, key);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
safeWrapCache.set(state, wrapped);
|
|
163
|
+
return wrapped;
|
|
164
|
+
}
|
|
165
|
+
function safeStateHas(state, key) {
|
|
166
|
+
if (typeof key !== "string") return false;
|
|
167
|
+
if (!Reflect.has(state, key)) return false;
|
|
168
|
+
if (!OBJ_PROTO_KEYS.has(key)) return true;
|
|
169
|
+
let obj = state;
|
|
170
|
+
while (obj && obj !== Object.prototype) {
|
|
171
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) return true;
|
|
172
|
+
obj = Object.getPrototypeOf(obj);
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
119
176
|
function evalExpr(expr, state) {
|
|
120
177
|
if (SIMPLE_PATH.test(expr)) {
|
|
121
|
-
|
|
178
|
+
const parts = expr.split(".");
|
|
179
|
+
if (!safeStateHas(state, parts[0])) return void 0;
|
|
180
|
+
return parts.reduce(
|
|
122
181
|
(obj, key) => obj != null ? obj[key] : void 0,
|
|
123
182
|
state
|
|
124
183
|
);
|
|
@@ -127,7 +186,7 @@ var Micra = (() => {
|
|
|
127
186
|
try {
|
|
128
187
|
exprCache.set(
|
|
129
188
|
expr,
|
|
130
|
-
new Function("$s", `with($s){return (${expr})}`)
|
|
189
|
+
new Function("$s", "$safe", `with($safe){with($s){return (${expr})}}`)
|
|
131
190
|
);
|
|
132
191
|
} catch {
|
|
133
192
|
warn(`invalid expression "${expr}"`);
|
|
@@ -135,8 +194,12 @@ var Micra = (() => {
|
|
|
135
194
|
}
|
|
136
195
|
}
|
|
137
196
|
try {
|
|
138
|
-
return exprCache.get(expr)(state);
|
|
139
|
-
} catch {
|
|
197
|
+
return exprCache.get(expr)(safeStateWrap(state), SAFE_OUTER);
|
|
198
|
+
} catch (e) {
|
|
199
|
+
if (!warnedRuntime.has(expr)) {
|
|
200
|
+
warnedRuntime.add(expr);
|
|
201
|
+
warn(`runtime error in "${expr}": ${e.message}`);
|
|
202
|
+
}
|
|
140
203
|
return void 0;
|
|
141
204
|
}
|
|
142
205
|
}
|
|
@@ -152,8 +215,10 @@ var Micra = (() => {
|
|
|
152
215
|
return () => off(event, handler);
|
|
153
216
|
}
|
|
154
217
|
function off(event, handler) {
|
|
155
|
-
|
|
156
|
-
|
|
218
|
+
const set = _bus.get(event);
|
|
219
|
+
if (!set) return;
|
|
220
|
+
set.delete(handler);
|
|
221
|
+
if (set.size === 0) _bus.delete(event);
|
|
157
222
|
}
|
|
158
223
|
function emit(event, payload) {
|
|
159
224
|
var _a;
|
|
@@ -194,7 +259,13 @@ var Micra = (() => {
|
|
|
194
259
|
return Array.from(root.querySelectorAll(sel));
|
|
195
260
|
}
|
|
196
261
|
function queryOwn(root, attr) {
|
|
197
|
-
return queryAll(root, `[${attr}]`)
|
|
262
|
+
return filterOwn(root, queryAll(root, `[${attr}]`));
|
|
263
|
+
}
|
|
264
|
+
function queryOwnAll(root, sel) {
|
|
265
|
+
return filterOwn(root, queryAll(root, sel));
|
|
266
|
+
}
|
|
267
|
+
function filterOwn(root, els) {
|
|
268
|
+
return els.filter((el) => {
|
|
198
269
|
let node = el.parentElement;
|
|
199
270
|
while (node && node !== root) {
|
|
200
271
|
if (node.hasAttribute("data-component")) return false;
|
|
@@ -214,15 +285,25 @@ var Micra = (() => {
|
|
|
214
285
|
var _a;
|
|
215
286
|
el.innerHTML = String((_a = evalExpr(expr, state)) != null ? _a : "");
|
|
216
287
|
}
|
|
217
|
-
function applyIf(
|
|
288
|
+
function applyIf(binding, state) {
|
|
289
|
+
const el = binding.el;
|
|
290
|
+
const truthy = !!evalExpr(binding.expr, state);
|
|
291
|
+
if (truthy) {
|
|
292
|
+
const ph = binding.placeholder;
|
|
293
|
+
if (ph && ph.parentNode) ph.parentNode.replaceChild(el, ph);
|
|
294
|
+
} else {
|
|
295
|
+
const parent = el.parentNode;
|
|
296
|
+
if (parent) {
|
|
297
|
+
if (!binding.placeholder) binding.placeholder = document.createComment("if");
|
|
298
|
+
parent.replaceChild(binding.placeholder, el);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function applyShow(el, expr, state) {
|
|
218
303
|
el.style.display = evalExpr(expr, state) ? "" : "none";
|
|
219
304
|
}
|
|
220
|
-
function applyBind(el,
|
|
221
|
-
for (const
|
|
222
|
-
const colonIdx = pair.indexOf(":");
|
|
223
|
-
if (colonIdx === -1) continue;
|
|
224
|
-
const attr = pair.slice(0, colonIdx).trim();
|
|
225
|
-
const valExpr = pair.slice(colonIdx + 1).trim();
|
|
305
|
+
function applyBind(el, pairs, state) {
|
|
306
|
+
for (const [attr, valExpr] of pairs) {
|
|
226
307
|
const val = evalExpr(valExpr, state);
|
|
227
308
|
if (attr === "class") {
|
|
228
309
|
el.className = String(val != null ? val : "");
|
|
@@ -242,21 +323,28 @@ var Micra = (() => {
|
|
|
242
323
|
}
|
|
243
324
|
}
|
|
244
325
|
}
|
|
245
|
-
function applyClass(el,
|
|
246
|
-
for (const
|
|
247
|
-
const colonIdx = pair.indexOf(":");
|
|
248
|
-
if (colonIdx === -1) continue;
|
|
249
|
-
const cls = pair.slice(0, colonIdx).trim();
|
|
250
|
-
const valExpr = pair.slice(colonIdx + 1).trim();
|
|
251
|
-
if (!cls) continue;
|
|
326
|
+
function applyClass(el, pairs, state) {
|
|
327
|
+
for (const [cls, valExpr] of pairs) {
|
|
252
328
|
el.classList.toggle(cls, Boolean(evalExpr(valExpr, state)));
|
|
253
329
|
}
|
|
254
330
|
}
|
|
331
|
+
function parsePairs(expr) {
|
|
332
|
+
const out = [];
|
|
333
|
+
for (const part of expr.split(",")) {
|
|
334
|
+
const colonIdx = part.indexOf(":");
|
|
335
|
+
if (colonIdx === -1) continue;
|
|
336
|
+
const left = part.slice(0, colonIdx).trim();
|
|
337
|
+
const right = part.slice(colonIdx + 1).trim();
|
|
338
|
+
if (!left) continue;
|
|
339
|
+
out.push([left, right]);
|
|
340
|
+
}
|
|
341
|
+
return out;
|
|
342
|
+
}
|
|
255
343
|
function applyModel(el, key, rawState) {
|
|
256
344
|
const html = el;
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
345
|
+
const stateVal = rawState[key];
|
|
346
|
+
const desired = stateVal == null ? "" : String(stateVal);
|
|
347
|
+
if (html.value !== desired) html.value = desired;
|
|
260
348
|
}
|
|
261
349
|
function buildCache(root) {
|
|
262
350
|
const pick = (attr) => {
|
|
@@ -265,14 +353,15 @@ var Micra = (() => {
|
|
|
265
353
|
if ((_a = root.hasAttribute) == null ? void 0 : _a.call(root, attr)) els.unshift(root);
|
|
266
354
|
return els.filter((el) => !el.closest("template")).map((el) => ({ el, expr: el.getAttribute(attr) }));
|
|
267
355
|
};
|
|
356
|
+
const pickPairs = (attr) => pick(attr).map((b) => ({ ...b, pairs: parsePairs(b.expr) }));
|
|
268
357
|
return {
|
|
269
358
|
text: pick("data-text"),
|
|
270
359
|
html: pick("data-html"),
|
|
271
360
|
if: pick("data-if"),
|
|
272
361
|
show: pick("data-show"),
|
|
273
|
-
bind:
|
|
362
|
+
bind: pickPairs("data-bind"),
|
|
274
363
|
model: pick("data-model"),
|
|
275
|
-
class:
|
|
364
|
+
class: pickPairs("data-class")
|
|
276
365
|
};
|
|
277
366
|
}
|
|
278
367
|
function applyDirectives(root, state, rawState, _instance) {
|
|
@@ -285,35 +374,56 @@ var Micra = (() => {
|
|
|
285
374
|
applyFromList(el.__micraCache, state, rawState);
|
|
286
375
|
}
|
|
287
376
|
function applyFromList(cache, state, rawState) {
|
|
377
|
+
cache.if.forEach((b) => applyIf(b, state));
|
|
288
378
|
cache.text.forEach((b) => applyText(b.el, b.expr, state));
|
|
289
379
|
cache.html.forEach((b) => applyHtml(b.el, b.expr, state));
|
|
290
|
-
cache.
|
|
291
|
-
cache.
|
|
292
|
-
cache.bind.forEach((b) => applyBind(b.el, b.expr, state));
|
|
380
|
+
cache.show.forEach((b) => applyShow(b.el, b.expr, state));
|
|
381
|
+
cache.bind.forEach((b) => applyBind(b.el, b.pairs, state));
|
|
293
382
|
cache.model.forEach((b) => applyModel(b.el, b.expr.trim(), rawState));
|
|
294
|
-
cache.class.forEach((b) => applyClass(b.el, b.
|
|
383
|
+
cache.class.forEach((b) => applyClass(b.el, b.pairs, state));
|
|
295
384
|
}
|
|
296
385
|
function buildFragmentList(frag) {
|
|
297
386
|
const pick = (attr) => queryAll(frag, `[${attr}]`).filter((el) => !el.closest("template")).map((el) => ({ el, expr: el.getAttribute(attr) }));
|
|
387
|
+
const pickPairs = (attr) => pick(attr).map((b) => ({ ...b, pairs: parsePairs(b.expr) }));
|
|
298
388
|
return {
|
|
299
389
|
text: pick("data-text"),
|
|
300
390
|
html: pick("data-html"),
|
|
301
391
|
if: pick("data-if"),
|
|
302
392
|
show: pick("data-show"),
|
|
303
|
-
bind:
|
|
393
|
+
bind: pickPairs("data-bind"),
|
|
304
394
|
model: pick("data-model"),
|
|
305
|
-
class:
|
|
395
|
+
class: pickPairs("data-class")
|
|
306
396
|
};
|
|
307
397
|
}
|
|
308
398
|
function validateDirectives(root) {
|
|
399
|
+
var _a, _b;
|
|
309
400
|
queryOwn(root, "data-each").forEach((el) => {
|
|
310
|
-
|
|
401
|
+
const tmpl = el;
|
|
402
|
+
if (!el.hasAttribute("data-key") && !tmpl.__micraNoKeyWarned) {
|
|
403
|
+
tmpl.__micraNoKeyWarned = true;
|
|
311
404
|
warn(`data-each="${el.getAttribute("data-each")}" has no data-key \u2014 keyed diff disabled. Add data-key="id" for better performance.`);
|
|
312
405
|
}
|
|
313
406
|
});
|
|
407
|
+
const bindEls = queryOwn(root, "data-bind");
|
|
408
|
+
if (((_a = root.hasAttribute) == null ? void 0 : _a.call(root, "data-bind")) && !bindEls.includes(root)) bindEls.unshift(root);
|
|
409
|
+
for (const el of bindEls) {
|
|
410
|
+
const spec = (_b = el.getAttribute("data-bind")) != null ? _b : "";
|
|
411
|
+
const hasClassBind = spec.split(",").some((p) => {
|
|
412
|
+
var _a2;
|
|
413
|
+
return ((_a2 = p.trim().split(":")[0]) == null ? void 0 : _a2.trim()) === "class";
|
|
414
|
+
});
|
|
415
|
+
if (hasClassBind && el.hasAttribute("data-class")) {
|
|
416
|
+
warn(`element has both data-bind="class:..." and data-class \u2014 they fight on every render. Use one.`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
314
419
|
}
|
|
315
420
|
|
|
316
421
|
// src/dom/events.ts
|
|
422
|
+
function track(instance, el, type, fn) {
|
|
423
|
+
var _a;
|
|
424
|
+
el.addEventListener(type, fn);
|
|
425
|
+
((_a = instance.__micraListeners) != null ? _a : instance.__micraListeners = []).push({ el, type, fn });
|
|
426
|
+
}
|
|
317
427
|
function bindDataOn(root, instance) {
|
|
318
428
|
var _a, _b;
|
|
319
429
|
const isFragment = root.nodeType === 11;
|
|
@@ -329,7 +439,7 @@ var Micra = (() => {
|
|
|
329
439
|
const [evSpec, method] = part.trim().split(":");
|
|
330
440
|
if (!evSpec || !method) continue;
|
|
331
441
|
const [evName, ...mods] = evSpec.split(".");
|
|
332
|
-
el
|
|
442
|
+
track(instance, el, evName, (e) => {
|
|
333
443
|
if (mods.includes("prevent")) e.preventDefault();
|
|
334
444
|
if (mods.includes("stop")) e.stopPropagation();
|
|
335
445
|
if (mods.includes("self") && e.target !== el) return;
|
|
@@ -341,16 +451,18 @@ var Micra = (() => {
|
|
|
341
451
|
}
|
|
342
452
|
}
|
|
343
453
|
function bindAtEvents(root, instance) {
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const all = queryAll(root, "*");
|
|
454
|
+
const isFragment = root.nodeType === 11;
|
|
455
|
+
const all = isFragment ? queryAll(root, "*") : queryOwnAll(root, "*");
|
|
456
|
+
if (!isFragment && !all.includes(root)) all.unshift(root);
|
|
348
457
|
for (const el of all) {
|
|
458
|
+
const mEl = el;
|
|
459
|
+
if (mEl.__micraAtBound) continue;
|
|
460
|
+
let bound = false;
|
|
349
461
|
for (const attr of Array.from(el.attributes)) {
|
|
350
462
|
if (!attr.name.startsWith("@")) continue;
|
|
351
463
|
const [evSpec, ...rest] = attr.name.slice(1).split(".");
|
|
352
464
|
const method = attr.value.trim();
|
|
353
|
-
el
|
|
465
|
+
track(instance, el, evSpec, (e) => {
|
|
354
466
|
if (rest.includes("prevent")) e.preventDefault();
|
|
355
467
|
if (rest.includes("stop")) e.stopPropagation();
|
|
356
468
|
if (rest.includes("self") && e.target !== el) return;
|
|
@@ -358,7 +470,9 @@ var Micra = (() => {
|
|
|
358
470
|
if (typeof fn === "function") fn.call(instance, e);
|
|
359
471
|
else warn(`method "${method}" not found`);
|
|
360
472
|
});
|
|
473
|
+
bound = true;
|
|
361
474
|
}
|
|
475
|
+
if (bound) mEl.__micraAtBound = true;
|
|
362
476
|
}
|
|
363
477
|
}
|
|
364
478
|
function bindModels(root, instance) {
|
|
@@ -371,14 +485,22 @@ var Micra = (() => {
|
|
|
371
485
|
mEl.__micraModel = true;
|
|
372
486
|
const key = (_a = el.dataset["model"]) != null ? _a : "";
|
|
373
487
|
const tag = el.tagName;
|
|
488
|
+
const inputEl = el;
|
|
489
|
+
const inputType = inputEl.type;
|
|
374
490
|
const update = () => {
|
|
375
|
-
|
|
491
|
+
let val;
|
|
492
|
+
if (tag === "INPUT" && inputType === "checkbox") {
|
|
493
|
+
val = inputEl.checked;
|
|
494
|
+
} else if (tag === "INPUT" && (inputType === "number" || inputType === "range")) {
|
|
495
|
+
val = inputEl.value === "" ? null : inputEl.valueAsNumber;
|
|
496
|
+
} else {
|
|
497
|
+
val = inputEl.value;
|
|
498
|
+
}
|
|
499
|
+
;
|
|
376
500
|
instance.state[key] = val;
|
|
377
501
|
};
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
update
|
|
381
|
-
);
|
|
502
|
+
const evType = tag === "SELECT" || inputType === "radio" ? "change" : "input";
|
|
503
|
+
track(instance, el, evType, update);
|
|
382
504
|
}
|
|
383
505
|
}
|
|
384
506
|
|
|
@@ -401,6 +523,7 @@ var Micra = (() => {
|
|
|
401
523
|
const marker = tmpl.__micraMarker;
|
|
402
524
|
const keyMap = tmpl.__micraNodes;
|
|
403
525
|
const parent = marker.parentNode;
|
|
526
|
+
if (!parent) return;
|
|
404
527
|
if (!Array.isArray(items)) {
|
|
405
528
|
tmpl.__micraList.forEach((n) => n.remove());
|
|
406
529
|
tmpl.__micraList = [];
|
|
@@ -417,9 +540,18 @@ var Micra = (() => {
|
|
|
417
540
|
function renderKeyed(tmpl, items, keyAttr, marker, keyMap, parent, state, rawState, instance) {
|
|
418
541
|
const nextKeys = /* @__PURE__ */ new Set();
|
|
419
542
|
const nextNodes = [];
|
|
543
|
+
let warnedNullKey = false;
|
|
544
|
+
let warnedDupKey = false;
|
|
420
545
|
for (const [index, item] of items.entries()) {
|
|
421
546
|
const key = item[keyAttr];
|
|
422
|
-
if (key == null
|
|
547
|
+
if (key == null && !warnedNullKey) {
|
|
548
|
+
warn(`data-key="${keyAttr}" is null/undefined on item at index ${index}`);
|
|
549
|
+
warnedNullKey = true;
|
|
550
|
+
}
|
|
551
|
+
if (nextKeys.has(key) && !warnedDupKey) {
|
|
552
|
+
warn(`data-key="${keyAttr}" has duplicate value ${JSON.stringify(key)} \u2014 rows will collide`);
|
|
553
|
+
warnedDupKey = true;
|
|
554
|
+
}
|
|
423
555
|
nextKeys.add(key);
|
|
424
556
|
let node = keyMap.get(key);
|
|
425
557
|
if (!node) {
|
|
@@ -522,15 +654,35 @@ var Micra = (() => {
|
|
|
522
654
|
let isRendering = false;
|
|
523
655
|
const schedule = createScheduler(() => instance.render());
|
|
524
656
|
instance.state = createReactiveState(rawState, schedule);
|
|
657
|
+
const boundMethods = /* @__PURE__ */ new Map();
|
|
525
658
|
const exprState = new Proxy(rawState, {
|
|
526
659
|
get(target, key) {
|
|
527
|
-
if (key
|
|
528
|
-
if (key
|
|
660
|
+
if (Object.prototype.hasOwnProperty.call(target, key)) return target[key];
|
|
661
|
+
if (Object.prototype.hasOwnProperty.call(instance, key) && typeof instance[key] === "function") {
|
|
662
|
+
const cached = boundMethods.get(key);
|
|
663
|
+
if (cached) return cached;
|
|
664
|
+
const bound = instance[key].bind(instance);
|
|
665
|
+
boundMethods.set(key, bound);
|
|
666
|
+
return bound;
|
|
667
|
+
}
|
|
529
668
|
return void 0;
|
|
669
|
+
},
|
|
670
|
+
has(target, key) {
|
|
671
|
+
if (typeof key !== "string") return false;
|
|
672
|
+
if (Object.prototype.hasOwnProperty.call(target, key)) return true;
|
|
673
|
+
return Object.prototype.hasOwnProperty.call(instance, key) && typeof instance[key] === "function";
|
|
530
674
|
}
|
|
531
675
|
});
|
|
676
|
+
let warnedReentry = false;
|
|
532
677
|
instance.render = function() {
|
|
533
|
-
if (
|
|
678
|
+
if (instance.__micraDestroyed) return;
|
|
679
|
+
if (isRendering) {
|
|
680
|
+
if (!warnedReentry) {
|
|
681
|
+
warn("render() re-entry detected \u2014 mutation inside a directive expression is ignored. Move state writes to a method.");
|
|
682
|
+
warnedReentry = true;
|
|
683
|
+
}
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
534
686
|
isRendering = true;
|
|
535
687
|
try {
|
|
536
688
|
applyDirectives(root, exprState, rawState, instance);
|
|
@@ -544,8 +696,22 @@ var Micra = (() => {
|
|
|
544
696
|
}
|
|
545
697
|
};
|
|
546
698
|
instance.destroy = function() {
|
|
547
|
-
var _a2;
|
|
548
|
-
(
|
|
699
|
+
var _a2, _b;
|
|
700
|
+
if (instance.__micraDestroyed) return;
|
|
701
|
+
instance.__micraDestroyed = true;
|
|
702
|
+
(_a2 = instance.__micraListeners) == null ? void 0 : _a2.forEach(({ el, type, fn }) => el.removeEventListener(type, fn));
|
|
703
|
+
instance.__micraListeners = [];
|
|
704
|
+
const clearFlags = (el) => {
|
|
705
|
+
const m = el;
|
|
706
|
+
delete m.__micraEvents;
|
|
707
|
+
delete m.__micraAtBound;
|
|
708
|
+
delete m.__micraModel;
|
|
709
|
+
delete m.__micraCache;
|
|
710
|
+
};
|
|
711
|
+
clearFlags(root);
|
|
712
|
+
root.querySelectorAll("*").forEach(clearFlags);
|
|
713
|
+
(_b = instance.__micraSubs) == null ? void 0 : _b.forEach((unsub) => unsub());
|
|
714
|
+
instance.__micraSubs = [];
|
|
549
715
|
if (typeof definition.onDestroy === "function")
|
|
550
716
|
definition.onDestroy.call(instance);
|
|
551
717
|
_instances.delete(root);
|