mono-jsx 0.8.0-beta.1 → 0.8.0-beta.2

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.
@@ -0,0 +1,619 @@
1
+ // jsx.ts
2
+ var customElements = /* @__PURE__ */ new Map();
3
+ var JSX = {
4
+ customElements: {
5
+ define(tagName, fc) {
6
+ customElements.set(tagName, fc);
7
+ }
8
+ }
9
+ };
10
+
11
+ // runtime/utils.ts
12
+ var regexpCssBareUnitProps = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;
13
+ var cssIds = /* @__PURE__ */ new Set();
14
+ var isString = (v) => typeof v === "string";
15
+ var isObject = (v) => typeof v === "object" && v !== null;
16
+ var isFunction = (v) => typeof v === "function";
17
+ var toHyphenCase = (k) => k.replace(/[a-z][A-Z]/g, (m) => m.charAt(0) + "-" + m.charAt(1).toLowerCase());
18
+ var hashCode = (s) => [...s].reduce((hash, c) => Math.imul(31, hash) + c.charCodeAt(0) | 0, 0);
19
+ var cx = (className) => {
20
+ if (typeof className === "string") {
21
+ return className;
22
+ }
23
+ if (typeof className === "object" && className !== null) {
24
+ if (Array.isArray(className)) {
25
+ return className.map(cx).filter(Boolean).join(" ");
26
+ }
27
+ return Object.entries(className).filter(([, v]) => !!v).map(([k]) => k).join(" ");
28
+ }
29
+ return "";
30
+ };
31
+ var styleToCSS = (style) => {
32
+ const inline = [];
33
+ const css = [];
34
+ const ret = new NullProtoObject();
35
+ for (const [k, v] of Object.entries(style)) {
36
+ switch (k.charCodeAt(0)) {
37
+ case /* ':' */
38
+ 58:
39
+ css.push(k.startsWith("::view-") ? "" : null, k + "{" + renderStyle(v) + "}");
40
+ break;
41
+ case /* '@' */
42
+ 64:
43
+ if (k.startsWith("@keyframes ") || k.startsWith("@view-")) {
44
+ if (isObject(v)) {
45
+ css.push(k + "{" + Object.entries(v).map(([k2, v2]) => k2 + "{" + renderStyle(v2) + "}").join("") + "}");
46
+ }
47
+ } else {
48
+ css.push(k + "{", null, "{" + renderStyle(v) + "}}");
49
+ }
50
+ break;
51
+ case /* '&' */
52
+ 38:
53
+ css.push(null, k.slice(1) + "{" + renderStyle(v) + "}");
54
+ break;
55
+ default:
56
+ inline.push([k, v]);
57
+ }
58
+ }
59
+ if (inline.length > 0) {
60
+ ret.inline = renderStyle(inline);
61
+ }
62
+ if (css.length > 0) {
63
+ ret.css = css;
64
+ }
65
+ return ret;
66
+ };
67
+ var applyStyle = (el, style) => {
68
+ const { inline, css } = styleToCSS(style);
69
+ if (css) {
70
+ const prefix = "data-css-";
71
+ const id = hashCode((inline ?? "") + css.join(""));
72
+ const attrName = prefix + id.toString(36);
73
+ const selector = "[" + attrName + "]";
74
+ if (!cssIds.has(id)) {
75
+ cssIds.add(id);
76
+ document.head.appendChild(document.createElement("style")).textContent = (inline ? selector + "{" + inline + "}" : "") + css.map((v) => v === null ? selector : v).join("");
77
+ }
78
+ el.getAttributeNames().forEach((name) => name.startsWith(prefix) && el.removeAttribute(name));
79
+ el.setAttribute(attrName, "");
80
+ } else if (inline) {
81
+ el.setAttribute("style", inline);
82
+ }
83
+ };
84
+ var renderStyle = (style) => {
85
+ if (typeof style === "object" && style !== null) {
86
+ let css = "";
87
+ for (const [k, v] of Array.isArray(style) ? style : Object.entries(style)) {
88
+ if (isString(v) || typeof v === "number") {
89
+ const cssKey = toHyphenCase(k);
90
+ const cssValue = typeof v === "number" ? regexpCssBareUnitProps.test(k) ? "" + v : v + "px" : "" + v;
91
+ css += (css ? ";" : "") + cssKey + ":" + (cssKey === "content" ? JSON.stringify(cssValue) : cssValue);
92
+ }
93
+ }
94
+ return css;
95
+ }
96
+ return "";
97
+ };
98
+ var NullProtoObject = /* @__PURE__ */ (() => {
99
+ function NPO() {
100
+ }
101
+ NPO.prototype = Object.freeze(/* @__PURE__ */ Object.create(null));
102
+ return NPO;
103
+ })();
104
+ var domEscapeHTML = (text) => {
105
+ const div = document.createElement("div");
106
+ div.textContent = text;
107
+ return div.innerHTML;
108
+ };
109
+
110
+ // symbols.ts
111
+ var $fragment = Symbol.for("jsx.fragment");
112
+ var $html = Symbol.for("jsx.html");
113
+ var $vnode = Symbol.for("jsx.vnode");
114
+ var $setup = Symbol.for("mono.setup");
115
+
116
+ // dom/render.ts
117
+ var Signal = class {
118
+ constructor(scope, key) {
119
+ this.scope = scope;
120
+ this.key = key;
121
+ }
122
+ set(value) {
123
+ this.scope[this.key] = value;
124
+ }
125
+ watch(callback) {
126
+ this.scope[$watch](this.key, callback);
127
+ }
128
+ reactive(effect) {
129
+ const update = () => effect(this.scope[this.key]);
130
+ update();
131
+ this.watch(update);
132
+ }
133
+ map(callback) {
134
+ return new ReactiveList(this, callback);
135
+ }
136
+ };
137
+ var Compute = class {
138
+ constructor(scope, compute) {
139
+ this.scope = scope;
140
+ this.compute = compute;
141
+ }
142
+ reactive(effect) {
143
+ const update = () => effect(this.compute.call(this.scope));
144
+ $depsMark = /* @__PURE__ */ new Set();
145
+ update();
146
+ $depsMark.forEach((dep) => dep.watch(update));
147
+ $depsMark = void 0;
148
+ }
149
+ map(callback) {
150
+ return new ReactiveList(this, callback);
151
+ }
152
+ };
153
+ var ReactiveList = class {
154
+ constructor(reactive, callback) {
155
+ this.reactive = reactive;
156
+ this.callback = callback;
157
+ }
158
+ };
159
+ var Ref = class {
160
+ constructor(refs, name) {
161
+ this.refs = refs;
162
+ this.name = name;
163
+ }
164
+ };
165
+ var InsertMark = class {
166
+ #root;
167
+ #index;
168
+ constructor(root) {
169
+ this.#root = root;
170
+ this.#index = root.childNodes.length;
171
+ }
172
+ insert(...nodes) {
173
+ if (nodes.length === 1) {
174
+ this.#root.insertBefore(nodes[0], this.#root.childNodes[this.#index]);
175
+ } else {
176
+ const tmp = createTextNode();
177
+ this.#root.insertBefore(tmp, this.#root.childNodes[this.#index]);
178
+ tmp.replaceWith(...nodes);
179
+ }
180
+ }
181
+ };
182
+ var $slots = Symbol();
183
+ var $watch = Symbol();
184
+ var $postbind = Symbol();
185
+ var isVNode = (v) => Array.isArray(v) && v.length === 3 && v[2] === $vnode;
186
+ var isReactive = (v) => v instanceof Signal || v instanceof Compute;
187
+ var createTextNode = (text = "") => document.createTextNode(text);
188
+ var onAbort = (signal, callback) => signal?.addEventListener("abort", callback);
189
+ var render = (scope, child, root, abortSignal) => {
190
+ switch (typeof child) {
191
+ case "boolean":
192
+ case "undefined":
193
+ return;
194
+ case "object":
195
+ if (child !== null) {
196
+ if (child instanceof ReactiveList) {
197
+ let { reactive, callback } = child;
198
+ let insertMark = new InsertMark(root);
199
+ let list = /* @__PURE__ */ new Map();
200
+ let cleanup = () => {
201
+ list.forEach((items) => items.forEach(([ac]) => ac.abort()));
202
+ list.clear();
203
+ };
204
+ reactive.reactive((arr) => {
205
+ if (!Array.isArray(arr)) {
206
+ throw new TypeError("map is not a function");
207
+ }
208
+ let nodes = [];
209
+ let newList = /* @__PURE__ */ new Map();
210
+ arr.forEach((item, index) => {
211
+ let render2 = list.get(item)?.shift();
212
+ if (callback.length >= 2 && render2 && render2[2] !== index) {
213
+ render2[0].abort();
214
+ render2 = void 0;
215
+ }
216
+ if (!render2) {
217
+ const ac = new AbortController();
218
+ render2 = [ac, [...renderToFragment(scope, callback(item, index), ac.signal).childNodes], index];
219
+ }
220
+ nodes.push(...render2[1]);
221
+ if (newList.has(item)) {
222
+ newList.get(item).push(render2);
223
+ } else {
224
+ newList.set(item, [render2]);
225
+ }
226
+ });
227
+ cleanup();
228
+ insertMark.insert(...nodes);
229
+ list = newList;
230
+ });
231
+ onAbort(abortSignal, cleanup);
232
+ return;
233
+ }
234
+ if (isReactive(child)) {
235
+ const textNode2 = createTextNode();
236
+ child.reactive((value) => {
237
+ textNode2.textContent = String(value);
238
+ });
239
+ root.appendChild(textNode2);
240
+ onAbort(abortSignal, () => textNode2.remove());
241
+ return;
242
+ }
243
+ if (isVNode(child)) {
244
+ const [tag, props] = child;
245
+ switch (tag) {
246
+ // fragment element
247
+ case "mount":
248
+ case $fragment: {
249
+ const { children, root: rootProp } = props;
250
+ const rootEl = rootProp instanceof HTMLElement ? rootProp : root;
251
+ if (children !== void 0) {
252
+ renderChildren(scope, children, rootEl, abortSignal);
253
+ }
254
+ break;
255
+ }
256
+ // XSS!
257
+ case $html: {
258
+ const { innerHTML } = props;
259
+ if (isReactive(innerHTML)) {
260
+ } else if (isString(innerHTML)) {
261
+ }
262
+ break;
263
+ }
264
+ // `<slot>` element
265
+ case "slot": {
266
+ const slots = scope[$slots];
267
+ if (slots) {
268
+ renderChildren(scope, slots, root, abortSignal);
269
+ }
270
+ break;
271
+ }
272
+ // `<if>` element
273
+ case "if": {
274
+ let { value: valueProp, children } = props;
275
+ if (children !== void 0) {
276
+ if (isReactive(valueProp)) {
277
+ let mark = new InsertMark(root);
278
+ let ac;
279
+ valueProp.reactive((value) => {
280
+ ac?.abort();
281
+ if (value) {
282
+ ac = new AbortController();
283
+ mark.insert(renderToFragment(scope, children, ac.signal));
284
+ }
285
+ });
286
+ onAbort(abortSignal, () => ac?.abort());
287
+ } else if (valueProp) {
288
+ renderChildren(scope, children, root, abortSignal);
289
+ }
290
+ }
291
+ break;
292
+ }
293
+ // `<switch>` element
294
+ case "switch": {
295
+ const { value: valueProp, children } = props;
296
+ if (children !== void 0) {
297
+ if (isReactive(valueProp)) {
298
+ let mark = new InsertMark(root);
299
+ let ac;
300
+ valueProp.reactive((value) => {
301
+ const slots = children.filter((v) => isVNode(v) && v[1].slot === String(value));
302
+ ac?.abort();
303
+ if (slots.length > 0) {
304
+ ac = new AbortController();
305
+ mark.insert(renderToFragment(scope, slots, ac.signal));
306
+ }
307
+ });
308
+ onAbort(abortSignal, () => ac?.abort());
309
+ } else {
310
+ renderChildren(
311
+ scope,
312
+ children.filter((v) => isVNode(v) && v[1].slot === String(valueProp)),
313
+ root,
314
+ abortSignal
315
+ );
316
+ }
317
+ }
318
+ break;
319
+ }
320
+ default: {
321
+ if (typeof tag === "function") {
322
+ renderFC(tag, props, root, abortSignal);
323
+ break;
324
+ }
325
+ if (isString(tag)) {
326
+ if (customElements.has(tag)) {
327
+ renderFC(customElements.get(tag), props, root, abortSignal);
328
+ break;
329
+ }
330
+ const { root: rootProp, children, ...attrs } = props;
331
+ const el = document.createElement(tag);
332
+ for (const [attrName, attrValue] of Object.entries(attrs)) {
333
+ switch (attrName) {
334
+ case "class": {
335
+ const updateClassName = (value) => {
336
+ el.className = cx(value);
337
+ };
338
+ if (isReactive(attrValue)) {
339
+ attrValue.reactive(updateClassName);
340
+ } else {
341
+ updateClassName(attrValue);
342
+ }
343
+ break;
344
+ }
345
+ case "style": {
346
+ const updateStyle = (value) => {
347
+ if (isObject(value)) {
348
+ applyStyle(el, value);
349
+ } else if (isString(value)) {
350
+ el.style.cssText = value;
351
+ }
352
+ };
353
+ if (isReactive(attrValue)) {
354
+ attrValue.reactive(updateStyle);
355
+ } else {
356
+ updateStyle(attrValue);
357
+ }
358
+ break;
359
+ }
360
+ case "ref":
361
+ if (isFunction(attrValue)) {
362
+ const ret = attrValue(el);
363
+ if (isFunction(ret)) {
364
+ onAbort(abortSignal, ret);
365
+ }
366
+ } else if (attrValue instanceof Ref) {
367
+ attrValue.refs[attrValue.name] = el;
368
+ }
369
+ break;
370
+ case "slot":
371
+ break;
372
+ case "$checked":
373
+ case "$value":
374
+ if (attrValue instanceof Signal) {
375
+ const name = attrName.slice(1);
376
+ const isValue = name.charAt(0) === "v";
377
+ attrValue.reactive((value) => {
378
+ el[name] = isValue ? String(value) : !!value;
379
+ });
380
+ el.addEventListener("input", () => attrValue.set(el[name]));
381
+ } else {
382
+ throw new TypeError("not a signal");
383
+ }
384
+ break;
385
+ case "viewTransition": {
386
+ break;
387
+ }
388
+ case "action":
389
+ if (isFunction(attrValue) && tag === "form") {
390
+ el.addEventListener("submit", (evt) => {
391
+ evt.preventDefault();
392
+ attrValue(new FormData(evt.target), evt);
393
+ });
394
+ } else if (isString(attrValue)) {
395
+ el.setAttribute(attrName, attrValue);
396
+ }
397
+ break;
398
+ default:
399
+ if (attrName.startsWith("on") && isFunction(attrValue)) {
400
+ el.addEventListener(attrName.slice(2).toLowerCase(), attrValue);
401
+ } else if (isReactive(attrValue)) {
402
+ attrValue.reactive((value) => el.setAttribute(attrName, String(value)));
403
+ } else {
404
+ el.setAttribute(attrName, String(attrValue));
405
+ }
406
+ break;
407
+ }
408
+ }
409
+ (rootProp instanceof HTMLElement ? rootProp : root).appendChild(el);
410
+ onAbort(abortSignal, () => el.remove());
411
+ if (children !== void 0) {
412
+ renderChildren(scope, children, el, abortSignal);
413
+ }
414
+ }
415
+ }
416
+ }
417
+ return;
418
+ }
419
+ }
420
+ }
421
+ const textNode = createTextNode(String(child));
422
+ root.appendChild(textNode);
423
+ onAbort(abortSignal, () => textNode.remove());
424
+ };
425
+ var renderChildren = (scope, children, root, aboutSignal) => {
426
+ if (Array.isArray(children) && !isVNode(children)) {
427
+ for (const child of children) {
428
+ render(scope, child, root, aboutSignal);
429
+ }
430
+ } else {
431
+ render(scope, children, root, aboutSignal);
432
+ }
433
+ };
434
+ var renderFC = (fc, props, root, abortSignal) => {
435
+ const scope = createScope(props.children, abortSignal);
436
+ const v = fc.call(scope, props);
437
+ if (v instanceof Promise) {
438
+ let placeholder;
439
+ if (isVNode(props.placeholder)) {
440
+ placeholder = [...renderToFragment(scope, props.placeholder, abortSignal).childNodes];
441
+ }
442
+ if (!placeholder?.length) {
443
+ placeholder = [createTextNode()];
444
+ }
445
+ root.append(...placeholder);
446
+ v.then((nodes) => {
447
+ scope[$postbind]();
448
+ placeholder[0].replaceWith(...renderToFragment(scope, nodes, abortSignal).childNodes);
449
+ }).catch((err) => {
450
+ if (isFunction(props.catch)) {
451
+ const v2 = props.catch(err);
452
+ if (isVNode(v2)) {
453
+ placeholder[0].replaceWith(...renderToFragment(scope, v2, abortSignal).childNodes);
454
+ }
455
+ } else {
456
+ console.error(err);
457
+ }
458
+ }).finally(() => {
459
+ placeholder.forEach((node) => node.remove());
460
+ });
461
+ } else {
462
+ scope[$postbind]();
463
+ if (isObject(v) && !isVNode(v) && Symbol.iterator in v) {
464
+ for (const node of v) {
465
+ render(scope, node, root, abortSignal);
466
+ }
467
+ } else {
468
+ render(scope, v, root, abortSignal);
469
+ }
470
+ }
471
+ };
472
+ var renderToFragment = (scope, node, aboutSignal) => {
473
+ const fragment = document.createDocumentFragment();
474
+ renderChildren(scope, node, fragment, aboutSignal);
475
+ return fragment;
476
+ };
477
+ var $depsMark;
478
+ var createScope = (slots, abortSignal) => {
479
+ let isBound = false;
480
+ let watchHandlers = /* @__PURE__ */ new Map();
481
+ let signals = /* @__PURE__ */ new Map();
482
+ let getSignal = (key) => {
483
+ let signal = signals.get(key);
484
+ if (!signal) {
485
+ signal = new Signal(scope, key);
486
+ signals.set(key, signal);
487
+ }
488
+ return signal;
489
+ };
490
+ let refs = new Proxy(new NullProtoObject(), {
491
+ get(target, key) {
492
+ if (isBound || $depsMark) {
493
+ return target[key];
494
+ }
495
+ return new Ref(target, key);
496
+ }
497
+ });
498
+ let scope = new Proxy(new NullProtoObject(), {
499
+ get(target, key, receiver) {
500
+ switch (key) {
501
+ case $slots:
502
+ return slots;
503
+ case $watch:
504
+ return (key2, effect) => {
505
+ let handlers = watchHandlers.get(key2);
506
+ if (!handlers) {
507
+ handlers = /* @__PURE__ */ new Set();
508
+ watchHandlers.set(key2, handlers);
509
+ }
510
+ handlers.add(effect);
511
+ };
512
+ case $postbind:
513
+ return () => {
514
+ isBound = true;
515
+ };
516
+ case "init":
517
+ return ((args) => Object.assign(target, args));
518
+ case "$":
519
+ case "compute":
520
+ return (fn) => new Compute(receiver, fn);
521
+ case "effect":
522
+ return (callback) => {
523
+ queueMicrotask(() => {
524
+ $depsMark = /* @__PURE__ */ new Set();
525
+ let cleanup = callback.call(receiver);
526
+ $depsMark.forEach(
527
+ (dep) => dep.watch(() => {
528
+ cleanup?.();
529
+ cleanup = callback.call(receiver);
530
+ })
531
+ );
532
+ onAbort(abortSignal, () => cleanup?.());
533
+ $depsMark = void 0;
534
+ });
535
+ };
536
+ case "refs":
537
+ return refs;
538
+ default:
539
+ if (isBound || $depsMark) {
540
+ if ($depsMark && isString(key)) {
541
+ $depsMark.add(getSignal(key));
542
+ }
543
+ return target[key];
544
+ }
545
+ if (isString(key)) {
546
+ return getSignal(key);
547
+ }
548
+ }
549
+ },
550
+ set(target, key, value) {
551
+ if (isString(key)) {
552
+ const prev = target[key];
553
+ if (prev !== value) {
554
+ target[key] = value;
555
+ watchHandlers.get(key)?.forEach((effect) => effect());
556
+ }
557
+ }
558
+ return true;
559
+ }
560
+ });
561
+ onAbort(abortSignal, () => {
562
+ watchHandlers.clear();
563
+ signals.clear();
564
+ });
565
+ return scope;
566
+ };
567
+
568
+ // dom/jsx-runtime.ts
569
+ var Fragment = $fragment;
570
+ var jsx = (tag, props = new NullProtoObject(), key) => {
571
+ const vnode = [tag, props, $vnode];
572
+ const { root, abortSignal } = props;
573
+ if (key !== void 0) {
574
+ props.key = key;
575
+ }
576
+ if (tag === "mount" && root instanceof HTMLElement) {
577
+ render(
578
+ new NullProtoObject(),
579
+ vnode,
580
+ root,
581
+ abortSignal instanceof AbortSignal ? abortSignal : void 0
582
+ );
583
+ }
584
+ return vnode;
585
+ };
586
+ var jsxEscape = (value) => {
587
+ switch (typeof value) {
588
+ case "bigint":
589
+ case "number":
590
+ return String(value);
591
+ case "string":
592
+ return domEscapeHTML(value);
593
+ default:
594
+ return "";
595
+ }
596
+ };
597
+ var html = (template, ...values) => [
598
+ $html,
599
+ {
600
+ innerHTML: isString(template) || isReactive(template) ? template : String.raw(template, ...values.map(jsxEscape))
601
+ },
602
+ $vnode
603
+ ];
604
+ Object.assign(globalThis, {
605
+ JSX,
606
+ html,
607
+ css: html,
608
+ js: html
609
+ });
610
+ export {
611
+ Fragment,
612
+ JSX,
613
+ html as css,
614
+ html,
615
+ html as js,
616
+ jsx,
617
+ jsx as jsxDEV,
618
+ jsx as jsxs
619
+ };
package/jsx-runtime.mjs CHANGED
@@ -166,7 +166,7 @@ var $vnode = Symbol.for("jsx.vnode");
166
166
  var $setup = Symbol.for("mono.setup");
167
167
 
168
168
  // version.ts
169
- var VERSION = "0.7.5";
169
+ var VERSION = "0.8.0-beta.1";
170
170
 
171
171
  // render.ts
172
172
  var cdn = "https://raw.esm.sh";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mono-jsx",
3
- "version": "0.8.0-beta.1",
3
+ "version": "0.8.0-beta.2",
4
4
  "description": "`<html>` as a `Response`.",
5
5
  "type": "module",
6
6
  "module": "./index.mjs",
@@ -39,8 +39,10 @@
39
39
  "prepublishOnly": "deno task build"
40
40
  },
41
41
  "files": [
42
- "*.mjs",
43
- "./types/"
42
+ "./*.mjs",
43
+ "./dom/*.mjs",
44
+ "./types/*.d.ts",
45
+ "./types/dom/*.d.ts"
44
46
  ],
45
47
  "license": "MIT",
46
48
  "repository": {