micra.js 2.3.1 → 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/CHANGELOG.md +96 -0
- package/README.md +22 -2
- package/dist/index.d.ts +1 -1
- package/dist/micra.cjs.js +306 -67
- package/dist/micra.cjs.js.map +3 -3
- package/dist/micra.esm.js +306 -67
- package/dist/micra.esm.js.map +3 -3
- package/dist/micra.js +306 -67
- package/dist/micra.js.map +3 -3
- package/dist/micra.min.js +3 -2
- package/dist/types.d.ts +1 -0
- package/dist/utils/expr.d.ts +29 -22
- package/llms-full.txt +19 -19
- package/llms.txt +3 -3
- package/package.json +3 -3
- package/src/core/mount.ts +4 -0
- package/src/dom/each.ts +19 -2
- package/src/dom/events.ts +37 -10
- package/src/index.ts +1 -1
- package/src/types.ts +1 -0
- package/src/utils/expr.ts +278 -122
package/dist/micra.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Micra.js v2.
|
|
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,56 +81,196 @@ function debug() {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// src/utils/expr.ts
|
|
84
|
-
var
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
var
|
|
88
|
-
"Math",
|
|
89
|
-
"JSON",
|
|
90
|
-
"Date",
|
|
91
|
-
"String",
|
|
92
|
-
"Number",
|
|
93
|
-
"Boolean",
|
|
94
|
-
"Array",
|
|
95
|
-
"Object",
|
|
96
|
-
"parseInt",
|
|
97
|
-
"parseFloat",
|
|
98
|
-
"isNaN",
|
|
99
|
-
"isFinite",
|
|
100
|
-
"NaN",
|
|
101
|
-
"Infinity",
|
|
102
|
-
"undefined"
|
|
103
|
-
]);
|
|
104
|
-
var PARAM_S = "$s";
|
|
105
|
-
var PARAM_SAFE = "$safe";
|
|
106
|
-
var SAFE_OUTER = new Proxy(/* @__PURE__ */ Object.create(null), {
|
|
107
|
-
has(_target, key) {
|
|
108
|
-
if (typeof key !== "string") return false;
|
|
109
|
-
if (key === PARAM_S || key === PARAM_SAFE) return false;
|
|
110
|
-
return !ALLOWED_GLOBALS.has(key);
|
|
111
|
-
},
|
|
112
|
-
get() {
|
|
113
|
-
return void 0;
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
var safeWrapCache = /* @__PURE__ */ new WeakMap();
|
|
84
|
+
var ALLOWED_GLOBALS = new Set(
|
|
85
|
+
"Math,JSON,Date,String,Number,Boolean,Array,Object,parseInt,parseFloat,isNaN,isFinite,NaN,Infinity,undefined".split(",")
|
|
86
|
+
);
|
|
87
|
+
var BLOCKED_PROPS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
117
88
|
var OBJ_PROTO_KEYS = new Set(Object.getOwnPropertyNames(Object.prototype));
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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;
|
|
127
123
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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;
|
|
131
272
|
}
|
|
132
273
|
function safeStateHas(state, key) {
|
|
133
|
-
if (typeof key !== "string") return false;
|
|
134
274
|
if (!Reflect.has(state, key)) return false;
|
|
135
275
|
if (!OBJ_PROTO_KEYS.has(key)) return true;
|
|
136
276
|
let obj = state;
|
|
@@ -140,6 +280,87 @@ function safeStateHas(state, key) {
|
|
|
140
280
|
}
|
|
141
281
|
return false;
|
|
142
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_$]*)*$/;
|
|
143
364
|
function evalExpr(expr, state) {
|
|
144
365
|
let cached = exprCache.get(expr);
|
|
145
366
|
if (!cached) {
|
|
@@ -147,26 +368,24 @@ function evalExpr(expr, state) {
|
|
|
147
368
|
cached = { kind: "path", parts: expr.split(".") };
|
|
148
369
|
} else {
|
|
149
370
|
try {
|
|
150
|
-
cached = {
|
|
151
|
-
kind: "fn",
|
|
152
|
-
fn: new Function("$s", "$safe", `with($safe){with($s){return (${expr})}}`)
|
|
153
|
-
};
|
|
371
|
+
cached = { kind: "ast", ast: parse(tokenize(expr)) };
|
|
154
372
|
} catch {
|
|
155
373
|
warn(`invalid expression "${expr}"`);
|
|
156
|
-
cached = { kind: "
|
|
374
|
+
cached = { kind: "err" };
|
|
157
375
|
}
|
|
158
376
|
}
|
|
159
377
|
exprCache.set(expr, cached);
|
|
160
378
|
}
|
|
161
379
|
if (cached.kind === "path") {
|
|
162
|
-
|
|
163
|
-
return
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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;
|
|
167
385
|
}
|
|
386
|
+
if (cached.kind === "err") return void 0;
|
|
168
387
|
try {
|
|
169
|
-
return cached.
|
|
388
|
+
return evalNode(cached.ast, state);
|
|
170
389
|
} catch (e) {
|
|
171
390
|
if (!warnedRuntime.has(expr)) {
|
|
172
391
|
warnedRuntime.add(expr);
|
|
@@ -326,6 +545,23 @@ function track(instance, el, type, fn) {
|
|
|
326
545
|
el.addEventListener(type, fn);
|
|
327
546
|
((_a = instance.__micraListeners) != null ? _a : instance.__micraListeners = []).push({ el, type, fn });
|
|
328
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
|
+
}
|
|
329
565
|
function bindDataOn(els, instance) {
|
|
330
566
|
var _a;
|
|
331
567
|
for (const el of els) {
|
|
@@ -337,13 +573,12 @@ function bindDataOn(els, instance) {
|
|
|
337
573
|
const [evSpec, method] = part.trim().split(":");
|
|
338
574
|
if (!evSpec || !method) continue;
|
|
339
575
|
const [evName, ...mods] = evSpec.split(".");
|
|
576
|
+
const handler = method.trim();
|
|
340
577
|
track(instance, el, evName, (e) => {
|
|
341
578
|
if (mods.includes("prevent")) e.preventDefault();
|
|
342
579
|
if (mods.includes("stop")) e.stopPropagation();
|
|
343
580
|
if (mods.includes("self") && e.target !== el) return;
|
|
344
|
-
|
|
345
|
-
if (typeof fn === "function") fn.call(instance, e);
|
|
346
|
-
else warn(`method "${method.trim()}" not found`);
|
|
581
|
+
runHandler(instance, el, handler, e);
|
|
347
582
|
});
|
|
348
583
|
}
|
|
349
584
|
}
|
|
@@ -356,14 +591,12 @@ function bindAtEvents(els, instance) {
|
|
|
356
591
|
for (const attr of Array.from(el.attributes)) {
|
|
357
592
|
if (!attr.name.startsWith("@")) continue;
|
|
358
593
|
const [evSpec, ...rest] = attr.name.slice(1).split(".");
|
|
359
|
-
const
|
|
594
|
+
const handler = attr.value.trim();
|
|
360
595
|
track(instance, el, evSpec, (e) => {
|
|
361
596
|
if (rest.includes("prevent")) e.preventDefault();
|
|
362
597
|
if (rest.includes("stop")) e.stopPropagation();
|
|
363
598
|
if (rest.includes("self") && e.target !== el) return;
|
|
364
|
-
|
|
365
|
-
if (typeof fn === "function") fn.call(instance, e);
|
|
366
|
-
else warn(`method "${method}" not found`);
|
|
599
|
+
runHandler(instance, el, handler, e);
|
|
367
600
|
});
|
|
368
601
|
bound = true;
|
|
369
602
|
}
|
|
@@ -539,8 +772,13 @@ function renderList(templates, state, rawState, instance, triggerKey) {
|
|
|
539
772
|
function createRowNode(tmpl, state, instance) {
|
|
540
773
|
const frag = tmpl.content.cloneNode(true);
|
|
541
774
|
let node;
|
|
542
|
-
|
|
543
|
-
|
|
775
|
+
const first = frag.firstElementChild;
|
|
776
|
+
const single = !!first && !first.nextElementSibling && !Array.prototype.some.call(
|
|
777
|
+
frag.childNodes,
|
|
778
|
+
(c) => c.nodeType === 3 && /[^\x00- ]/.test(c.textContent)
|
|
779
|
+
);
|
|
780
|
+
if (single) {
|
|
781
|
+
node = first;
|
|
544
782
|
} else {
|
|
545
783
|
node = document.createElement("micra-each-item");
|
|
546
784
|
node.style.display = "contents";
|
|
@@ -768,6 +1006,7 @@ function mount(selector, definition) {
|
|
|
768
1006
|
return Object.prototype.hasOwnProperty.call(instance, key) && typeof instance[key] === "function";
|
|
769
1007
|
}
|
|
770
1008
|
});
|
|
1009
|
+
instance.__micraExpr = exprState;
|
|
771
1010
|
let warnedReentry = false;
|
|
772
1011
|
instance.render = function() {
|
|
773
1012
|
var _a2;
|