micra.js 2.3.2 → 2.4.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/dist/micra.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- /* Micra.js v2.3.2 — https://github.com/micra-js/micra — MIT */
1
+ /* Micra.js v2.4.0 — https://github.com/micra-js/micra — MIT */
2
2
 
3
3
  // src/utils/fetch.ts
4
4
  function getCSRF() {
@@ -81,42 +81,196 @@ function debug() {
81
81
  }
82
82
 
83
83
  // src/utils/expr.ts
84
- var exprCache = /* @__PURE__ */ new Map();
85
- var warnedRuntime = /* @__PURE__ */ new Set();
86
- var SIMPLE_PATH = /^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/;
87
84
  var ALLOWED_GLOBALS = new Set(
88
85
  "Math,JSON,Date,String,Number,Boolean,Array,Object,parseInt,parseFloat,isNaN,isFinite,NaN,Infinity,undefined".split(",")
89
86
  );
90
- var PARAM_S = "$s";
91
- var PARAM_SAFE = "$safe";
92
- var SAFE_OUTER = new Proxy(/* @__PURE__ */ Object.create(null), {
93
- has(_target, key) {
94
- if (typeof key !== "string") return false;
95
- if (key === PARAM_S || key === PARAM_SAFE) return false;
96
- return !ALLOWED_GLOBALS.has(key);
97
- },
98
- get() {
99
- return void 0;
100
- }
101
- });
102
- var safeWrapCache = /* @__PURE__ */ new WeakMap();
87
+ var BLOCKED_PROPS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
103
88
  var OBJ_PROTO_KEYS = new Set(Object.getOwnPropertyNames(Object.prototype));
104
- function safeStateWrap(state) {
105
- const cached = safeWrapCache.get(state);
106
- if (cached) return cached;
107
- const wrapped = new Proxy(state, {
108
- has(target, key) {
109
- return safeStateHas(target, key);
110
- },
111
- get(target, key) {
112
- return Reflect.get(target, key);
89
+ var PUNCT = [
90
+ "===",
91
+ "!==",
92
+ "==",
93
+ "!=",
94
+ "<=",
95
+ ">=",
96
+ "&&",
97
+ "||",
98
+ "(",
99
+ ")",
100
+ ".",
101
+ ",",
102
+ "?",
103
+ ":",
104
+ "!",
105
+ "<",
106
+ ">",
107
+ "+",
108
+ "-",
109
+ "*",
110
+ "/",
111
+ "%"
112
+ ];
113
+ function tokenize(src) {
114
+ var _a;
115
+ const toks = [];
116
+ let i = 0;
117
+ const n = src.length;
118
+ while (i < n) {
119
+ const c = src[i];
120
+ if (c === " " || c === " " || c === "\n" || c === "\r" || c === "\f") {
121
+ i++;
122
+ continue;
113
123
  }
114
- });
115
- safeWrapCache.set(state, wrapped);
116
- return wrapped;
124
+ if (c === '"' || c === "'") {
125
+ let s = "";
126
+ i++;
127
+ while (i < n && src[i] !== c) {
128
+ if (src[i] === "\\") {
129
+ s += (_a = src[i + 1]) != null ? _a : "";
130
+ i += 2;
131
+ } else {
132
+ s += src[i];
133
+ i++;
134
+ }
135
+ }
136
+ if (src[i] !== c) throw 0;
137
+ i++;
138
+ toks.push({ t: "str", v: s });
139
+ continue;
140
+ }
141
+ if (c >= "0" && c <= "9") {
142
+ let s = "";
143
+ while (i < n && (src[i] >= "0" && src[i] <= "9" || src[i] === ".")) {
144
+ s += src[i];
145
+ i++;
146
+ }
147
+ toks.push({ t: "num", v: s });
148
+ continue;
149
+ }
150
+ if (/[A-Za-z_$]/.test(c)) {
151
+ let s = "";
152
+ while (i < n && /[A-Za-z0-9_$]/.test(src[i])) {
153
+ s += src[i];
154
+ i++;
155
+ }
156
+ toks.push({ t: "id", v: s });
157
+ continue;
158
+ }
159
+ const m = PUNCT.find((p) => src.startsWith(p, i));
160
+ if (!m) throw 0;
161
+ toks.push({ t: "p", v: m });
162
+ i += m.length;
163
+ }
164
+ return toks;
165
+ }
166
+ var BIN_PREC = {
167
+ "||": 1,
168
+ "&&": 2,
169
+ "==": 3,
170
+ "!=": 3,
171
+ "===": 3,
172
+ "!==": 3,
173
+ "<": 4,
174
+ "<=": 4,
175
+ ">": 4,
176
+ ">=": 4,
177
+ "+": 5,
178
+ "-": 5,
179
+ "*": 6,
180
+ "/": 6,
181
+ "%": 6
182
+ };
183
+ function parse(toks) {
184
+ let pos = 0;
185
+ const peek = () => toks[pos];
186
+ const next = () => toks[pos++];
187
+ const eat = (v) => {
188
+ var _a;
189
+ if (((_a = peek()) == null ? void 0 : _a.v) !== v) throw 0;
190
+ pos++;
191
+ };
192
+ function parseExpr() {
193
+ var _a;
194
+ const c = parseBin(1);
195
+ if (((_a = peek()) == null ? void 0 : _a.v) === "?") {
196
+ next();
197
+ const a = parseExpr();
198
+ eat(":");
199
+ const b = parseExpr();
200
+ return { k: "tern", c, a, b };
201
+ }
202
+ return c;
203
+ }
204
+ function parseBin(minPrec) {
205
+ let left = parseUnary();
206
+ for (; ; ) {
207
+ const t = peek();
208
+ const prec = t && t.t === "p" ? BIN_PREC[t.v] : void 0;
209
+ if (prec === void 0 || prec < minPrec) break;
210
+ next();
211
+ const right = parseBin(prec + 1);
212
+ left = { k: "bin", op: t.v, l: left, r: right };
213
+ }
214
+ return left;
215
+ }
216
+ function parseUnary() {
217
+ const t = peek();
218
+ if (t && t.t === "p" && (t.v === "!" || t.v === "-")) {
219
+ next();
220
+ return { k: "un", op: t.v, x: parseUnary() };
221
+ }
222
+ return parsePostfix();
223
+ }
224
+ function parsePostfix() {
225
+ var _a, _b;
226
+ let node = parsePrimary();
227
+ for (; ; ) {
228
+ const t = peek();
229
+ if ((t == null ? void 0 : t.v) === ".") {
230
+ next();
231
+ const id = next();
232
+ if (!id || id.t !== "id") throw 0;
233
+ node = { k: "mem", o: node, p: id.v };
234
+ } else if ((t == null ? void 0 : t.v) === "(") {
235
+ next();
236
+ const args = [];
237
+ if (((_a = peek()) == null ? void 0 : _a.v) !== ")") {
238
+ args.push(parseExpr());
239
+ while (((_b = peek()) == null ? void 0 : _b.v) === ",") {
240
+ next();
241
+ args.push(parseExpr());
242
+ }
243
+ }
244
+ eat(")");
245
+ node = { k: "call", c: node, a: args };
246
+ } else break;
247
+ }
248
+ return node;
249
+ }
250
+ function parsePrimary() {
251
+ const t = next();
252
+ if (!t) throw 0;
253
+ if (t.t === "num") return { k: "lit", v: Number(t.v) };
254
+ if (t.t === "str") return { k: "lit", v: t.v };
255
+ if (t.v === "(") {
256
+ const e = parseExpr();
257
+ eat(")");
258
+ return e;
259
+ }
260
+ if (t.t === "id") {
261
+ if (t.v === "true") return { k: "lit", v: true };
262
+ if (t.v === "false") return { k: "lit", v: false };
263
+ if (t.v === "null") return { k: "lit", v: null };
264
+ if (t.v === "undefined") return { k: "lit", v: void 0 };
265
+ return { k: "id", n: t.v };
266
+ }
267
+ throw 0;
268
+ }
269
+ const ast = parseExpr();
270
+ if (pos !== toks.length) throw 0;
271
+ return ast;
117
272
  }
118
273
  function safeStateHas(state, key) {
119
- if (typeof key !== "string") return false;
120
274
  if (!Reflect.has(state, key)) return false;
121
275
  if (!OBJ_PROTO_KEYS.has(key)) return true;
122
276
  let obj = state;
@@ -126,6 +280,87 @@ function safeStateHas(state, key) {
126
280
  }
127
281
  return false;
128
282
  }
283
+ function resolveIdent(name, scope) {
284
+ if (safeStateHas(scope, name)) return scope[name];
285
+ if (ALLOWED_GLOBALS.has(name)) return globalThis[name];
286
+ return void 0;
287
+ }
288
+ function evalNode(node, scope) {
289
+ switch (node.k) {
290
+ case "lit":
291
+ return node.v;
292
+ case "id":
293
+ return resolveIdent(node.n, scope);
294
+ case "mem": {
295
+ const o = evalNode(node.o, scope);
296
+ if (o == null || BLOCKED_PROPS.has(node.p)) return void 0;
297
+ return o[node.p];
298
+ }
299
+ case "un": {
300
+ const x = evalNode(node.x, scope);
301
+ return node.op === "!" ? !x : -x;
302
+ }
303
+ case "tern":
304
+ return evalNode(node.c, scope) ? evalNode(node.a, scope) : evalNode(node.b, scope);
305
+ case "bin": {
306
+ const op = node.op;
307
+ if (op === "&&") {
308
+ const l2 = evalNode(node.l, scope);
309
+ return l2 ? evalNode(node.r, scope) : l2;
310
+ }
311
+ if (op === "||") {
312
+ const l2 = evalNode(node.l, scope);
313
+ return l2 ? l2 : evalNode(node.r, scope);
314
+ }
315
+ const l = evalNode(node.l, scope);
316
+ const r = evalNode(node.r, scope);
317
+ switch (op) {
318
+ case "+":
319
+ return l + r;
320
+ case "-":
321
+ return l - r;
322
+ case "*":
323
+ return l * r;
324
+ case "/":
325
+ return l / r;
326
+ case "%":
327
+ return l % r;
328
+ case "<":
329
+ return l < r;
330
+ case "<=":
331
+ return l <= r;
332
+ case ">":
333
+ return l > r;
334
+ case ">=":
335
+ return l >= r;
336
+ case "==":
337
+ return l == r;
338
+ case "!=":
339
+ return l != r;
340
+ case "===":
341
+ return l === r;
342
+ case "!==":
343
+ return l !== r;
344
+ }
345
+ return void 0;
346
+ }
347
+ case "call": {
348
+ let fn;
349
+ let self;
350
+ if (node.c.k === "mem") {
351
+ self = evalNode(node.c.o, scope);
352
+ fn = self == null || BLOCKED_PROPS.has(node.c.p) ? void 0 : self[node.c.p];
353
+ } else {
354
+ fn = evalNode(node.c, scope);
355
+ }
356
+ if (typeof fn !== "function") throw new TypeError("not a function");
357
+ return fn.apply(self, node.a.map((x) => evalNode(x, scope)));
358
+ }
359
+ }
360
+ }
361
+ var exprCache = /* @__PURE__ */ new Map();
362
+ var warnedRuntime = /* @__PURE__ */ new Set();
363
+ var SIMPLE_PATH = /^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/;
129
364
  function evalExpr(expr, state) {
130
365
  let cached = exprCache.get(expr);
131
366
  if (!cached) {
@@ -133,26 +368,24 @@ function evalExpr(expr, state) {
133
368
  cached = { kind: "path", parts: expr.split(".") };
134
369
  } else {
135
370
  try {
136
- cached = {
137
- kind: "fn",
138
- fn: new Function("$s", "$safe", `with($safe){with($s){return (${expr})}}`)
139
- };
371
+ cached = { kind: "ast", ast: parse(tokenize(expr)) };
140
372
  } catch {
141
373
  warn(`invalid expression "${expr}"`);
142
- cached = { kind: "fn", fn: () => void 0 };
374
+ cached = { kind: "err" };
143
375
  }
144
376
  }
145
377
  exprCache.set(expr, cached);
146
378
  }
147
379
  if (cached.kind === "path") {
148
- if (!safeStateHas(state, cached.parts[0])) return void 0;
149
- return cached.parts.reduce(
150
- (obj, key) => obj != null ? obj[key] : void 0,
151
- state
152
- );
380
+ const parts = cached.parts;
381
+ if (!safeStateHas(state, parts[0])) return void 0;
382
+ let obj = state;
383
+ for (const key of parts) obj = obj != null ? obj[key] : void 0;
384
+ return obj;
153
385
  }
386
+ if (cached.kind === "err") return void 0;
154
387
  try {
155
- return cached.fn(safeStateWrap(state), SAFE_OUTER);
388
+ return evalNode(cached.ast, state);
156
389
  } catch (e) {
157
390
  if (!warnedRuntime.has(expr)) {
158
391
  warnedRuntime.add(expr);
@@ -312,6 +545,23 @@ function track(instance, el, type, fn) {
312
545
  el.addEventListener(type, fn);
313
546
  ((_a = instance.__micraListeners) != null ? _a : instance.__micraListeners = []).push({ el, type, fn });
314
547
  }
548
+ function runHandler(instance, el, value, e) {
549
+ var _a;
550
+ if (value.includes("(")) {
551
+ let base;
552
+ for (let n = el; n && !base; n = n.parentElement) {
553
+ base = n._itemState;
554
+ }
555
+ const scope = Object.create((_a = base != null ? base : instance.__micraExpr) != null ? _a : null);
556
+ scope["$event"] = e;
557
+ scope["event"] = e;
558
+ evalExpr(value, scope);
559
+ return;
560
+ }
561
+ const fn = instance[value];
562
+ if (typeof fn === "function") fn.call(instance, e);
563
+ else warn(`method "${value}" not found`);
564
+ }
315
565
  function bindDataOn(els, instance) {
316
566
  var _a;
317
567
  for (const el of els) {
@@ -323,13 +573,12 @@ function bindDataOn(els, instance) {
323
573
  const [evSpec, method] = part.trim().split(":");
324
574
  if (!evSpec || !method) continue;
325
575
  const [evName, ...mods] = evSpec.split(".");
576
+ const handler = method.trim();
326
577
  track(instance, el, evName, (e) => {
327
578
  if (mods.includes("prevent")) e.preventDefault();
328
579
  if (mods.includes("stop")) e.stopPropagation();
329
580
  if (mods.includes("self") && e.target !== el) return;
330
- const fn = instance[method.trim()];
331
- if (typeof fn === "function") fn.call(instance, e);
332
- else warn(`method "${method.trim()}" not found`);
581
+ runHandler(instance, el, handler, e);
333
582
  });
334
583
  }
335
584
  }
@@ -342,14 +591,12 @@ function bindAtEvents(els, instance) {
342
591
  for (const attr of Array.from(el.attributes)) {
343
592
  if (!attr.name.startsWith("@")) continue;
344
593
  const [evSpec, ...rest] = attr.name.slice(1).split(".");
345
- const method = attr.value.trim();
594
+ const handler = attr.value.trim();
346
595
  track(instance, el, evSpec, (e) => {
347
596
  if (rest.includes("prevent")) e.preventDefault();
348
597
  if (rest.includes("stop")) e.stopPropagation();
349
598
  if (rest.includes("self") && e.target !== el) return;
350
- const fn = instance[method];
351
- if (typeof fn === "function") fn.call(instance, e);
352
- else warn(`method "${method}" not found`);
599
+ runHandler(instance, el, handler, e);
353
600
  });
354
601
  bound = true;
355
602
  }
@@ -759,6 +1006,7 @@ function mount(selector, definition) {
759
1006
  return Object.prototype.hasOwnProperty.call(instance, key) && typeof instance[key] === "function";
760
1007
  }
761
1008
  });
1009
+ instance.__micraExpr = exprState;
762
1010
  let warnedReentry = false;
763
1011
  instance.render = function() {
764
1012
  var _a2;