mono-jsx 0.9.6 → 0.9.8

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 CHANGED
@@ -874,6 +874,7 @@ mono-jsx binds a scoped signals object to `this` of your component functions. Th
874
874
 
875
875
  The `this` object has the following built-in properties:
876
876
 
877
+ - `init(initValue)`: Initializes the signals.
877
878
  - `app`: The app global signals.
878
879
  - `context`: The context object defined on the root `<html>` element.
879
880
  - `request`: The request object from the `fetch` handler.
@@ -885,14 +886,15 @@ The `this` object has the following built-in properties:
885
886
 
886
887
  ```ts
887
888
  type FC<Signals = {}, Refs = {}> = {
888
- readonly app: AppSignals & { refs: AppRefs; url: WithParams<URL> }
889
- readonly context: Context;
890
- readonly request: WithParams<Request>;
891
- readonly session: Session;
892
- readonly refs: Refs;
893
- readonly computed: <T = unknown>(fn: () => T) => T;
894
- readonly $: FC["computed"]; // A shortcut for `FC.computed`.
895
- readonly effect: (fn: () => void | (() => void)) => void;
889
+ init(initValue: Signals): void;
890
+ app: AppSignals & { refs: AppRefs; url: WithParams<URL> }
891
+ context: Context;
892
+ request: WithParams<Request>;
893
+ session: Session;
894
+ refs: Refs;
895
+ computed<T = unknown>(fn: () => T): T;
896
+ $: FC["computed"]; // A shortcut for `FC.computed`.
897
+ effect(fn: () => void | (() => void)): void;
896
898
  } & Signals;
897
899
 
898
900
  // define `AppSignals` type
@@ -131,6 +131,9 @@ var InsertMark = class {
131
131
  this.#root = root;
132
132
  this.#anchor = anchor;
133
133
  }
134
+ setText(text) {
135
+ this.#anchor.textContent = text;
136
+ }
134
137
  insert(...nodes) {
135
138
  const parent = this.#anchor.parentElement ?? this.#root;
136
139
  for (const node of nodes) {
@@ -315,244 +318,253 @@ var render = (scope, child, root, abortSignal) => {
315
318
  case "undefined":
316
319
  return;
317
320
  case "object":
318
- if (child !== null) {
319
- if (child instanceof ReactiveList) {
320
- let { reactive, callback } = child;
321
- let insertMark = new InsertMark(root, abortSignal);
322
- let list = /* @__PURE__ */ new Map();
323
- let cleanup = () => {
324
- list.forEach((items) => items.forEach(([ac]) => ac.abort()));
325
- list.clear();
326
- };
327
- reactive.reactive((v) => {
328
- if (!Array.isArray(v)) {
329
- v = [v];
321
+ if (child === null) {
322
+ return;
323
+ }
324
+ if (child instanceof ReactiveList) {
325
+ let { reactive, callback } = child;
326
+ let insertMark = new InsertMark(root, abortSignal);
327
+ let list = /* @__PURE__ */ new Map();
328
+ let cleanup = () => {
329
+ list.forEach((items) => items.forEach(([ac]) => ac.abort()));
330
+ list.clear();
331
+ };
332
+ reactive.reactive((v) => {
333
+ if (!Array.isArray(v)) {
334
+ v = [v];
335
+ }
336
+ let nodes = [];
337
+ let newList = /* @__PURE__ */ new Map();
338
+ v.forEach((item, index) => {
339
+ let render2 = list.get(item)?.shift();
340
+ if (callback.length >= 2 && render2 && render2[2] !== index) {
341
+ render2[0].abort();
342
+ render2 = void 0;
330
343
  }
331
- let nodes = [];
332
- let newList = /* @__PURE__ */ new Map();
333
- v.forEach((item, index) => {
334
- let render2 = list.get(item)?.shift();
335
- if (callback.length >= 2 && render2 && render2[2] !== index) {
336
- render2[0].abort();
337
- render2 = void 0;
338
- }
339
- if (!render2) {
340
- const ac = new AbortController();
341
- render2 = [ac, [...renderToFragment(scope, callback(item, index), ac.signal).childNodes], index];
342
- }
343
- nodes.push(...render2[1]);
344
- if (newList.has(item)) {
345
- newList.get(item).push(render2);
346
- } else {
347
- newList.set(item, [render2]);
348
- }
349
- });
350
- cleanup();
351
- insertMark.insert(...nodes);
352
- list = newList;
353
- }, abortSignal);
354
- onAbort(abortSignal, cleanup);
355
- return;
356
- }
357
- if (child instanceof Reactive) {
358
- const textNode2 = createTextNode();
359
- child.reactive((value) => {
360
- textNode2.textContent = String(value);
361
- }, abortSignal);
362
- root.appendChild(textNode2);
363
- onAbort(abortSignal, textNode2.remove.bind(textNode2));
364
- return;
365
- }
366
- if (isVNode(child)) {
367
- const [tag, props] = child;
368
- switch (tag) {
369
- // fragment element
370
- case $fragment: {
371
- const { children } = props;
372
- if (children !== void 0) {
373
- renderChildren(scope, children, root, abortSignal);
374
- }
375
- break;
344
+ if (!render2) {
345
+ const ac = new AbortController();
346
+ render2 = [ac, [...renderToFragment(scope, callback(item, index), ac.signal).childNodes], index];
376
347
  }
377
- // XSS!
378
- case $html: {
379
- const { innerHTML } = props;
380
- const mark = new InsertMark(root, abortSignal);
381
- if (innerHTML instanceof Reactive) {
382
- let cleanup;
383
- innerHTML.reactive((html2) => {
384
- cleanup?.();
385
- cleanup = mark.insertHTML(html2);
386
- }, abortSignal);
387
- onAbort(abortSignal, () => cleanup?.());
388
- } else {
389
- onAbort(abortSignal, mark.insertHTML(innerHTML));
390
- }
391
- break;
348
+ nodes.push(...render2[1]);
349
+ if (newList.has(item)) {
350
+ newList.get(item).push(render2);
351
+ } else {
352
+ newList.set(item, [render2]);
392
353
  }
393
- // `<slot>` element
394
- case "slot": {
395
- const slots = scope[$slots];
396
- if (slots) {
397
- renderChildren(scope, slots, root, abortSignal);
398
- }
399
- break;
354
+ });
355
+ cleanup();
356
+ insertMark.insert(...nodes);
357
+ list = newList;
358
+ }, abortSignal);
359
+ onAbort(abortSignal, cleanup);
360
+ return;
361
+ }
362
+ if (child instanceof Reactive) {
363
+ let insertMark = new InsertMark(root, abortSignal);
364
+ let ac;
365
+ child.reactive((value) => {
366
+ if (isVNode(value)) {
367
+ ac?.abort();
368
+ ac = new AbortController();
369
+ insertMark.insert(...renderToFragment(scope, value, ac.signal).childNodes);
370
+ } else {
371
+ insertMark.setText(
372
+ value === void 0 || value === null || typeof value === "boolean" ? "" : String(value)
373
+ );
374
+ }
375
+ onAbort(abortSignal, () => ac?.abort());
376
+ }, abortSignal);
377
+ return;
378
+ }
379
+ if (isVNode(child)) {
380
+ const [tag, props] = child;
381
+ switch (tag) {
382
+ // fragment element
383
+ case $fragment: {
384
+ const { children } = props;
385
+ if (children !== void 0) {
386
+ renderChildren(scope, children, root, abortSignal);
400
387
  }
401
- // `<show>` and `<hidden>` elements
402
- case "show":
403
- case "hidden": {
404
- let { when = true, children } = props;
405
- if (children !== void 0) {
406
- if (when instanceof Reactive) {
407
- let mark = new InsertMark(root, abortSignal);
408
- let ac;
409
- when.reactive((value) => {
410
- ac?.abort();
411
- if (tag === "show" ? value : !value) {
412
- ac = new AbortController();
413
- mark.insert(...renderToFragment(scope, children, ac.signal).childNodes);
414
- }
415
- }, abortSignal);
416
- onAbort(abortSignal, () => ac?.abort());
417
- } else {
418
- console.warn("[mono-jsx] <" + tag + "> The `when` prop is not a signal/computed.");
419
- if (when) {
420
- renderChildren(scope, children, root, abortSignal);
388
+ break;
389
+ }
390
+ // XSS!
391
+ case $html: {
392
+ const { innerHTML } = props;
393
+ const mark = new InsertMark(root, abortSignal);
394
+ if (innerHTML instanceof Reactive) {
395
+ let cleanup;
396
+ innerHTML.reactive((html2) => {
397
+ cleanup?.();
398
+ cleanup = mark.insertHTML(html2);
399
+ }, abortSignal);
400
+ onAbort(abortSignal, () => cleanup?.());
401
+ } else {
402
+ onAbort(abortSignal, mark.insertHTML(innerHTML));
403
+ }
404
+ break;
405
+ }
406
+ // `<slot>` element
407
+ case "slot": {
408
+ const slots = scope[$slots];
409
+ if (slots) {
410
+ renderChildren(scope, slots, root, abortSignal);
411
+ }
412
+ break;
413
+ }
414
+ // `<show>` and `<hidden>` elements
415
+ case "show":
416
+ case "hidden": {
417
+ let { when = true, children } = props;
418
+ if (children !== void 0) {
419
+ if (when instanceof Reactive) {
420
+ let mark = new InsertMark(root, abortSignal);
421
+ let ac;
422
+ when.reactive((value) => {
423
+ ac?.abort();
424
+ if (tag === "show" ? value : !value) {
425
+ ac = new AbortController();
426
+ mark.insert(...renderToFragment(scope, children, ac.signal).childNodes);
421
427
  }
428
+ }, abortSignal);
429
+ onAbort(abortSignal, () => ac?.abort());
430
+ } else {
431
+ console.warn("[mono-jsx] <" + tag + "> The `when` prop is not a signal/computed.");
432
+ if (when) {
433
+ renderChildren(scope, children, root, abortSignal);
422
434
  }
423
435
  }
424
- break;
425
436
  }
426
- // `<switch>` element
427
- case "switch": {
428
- const { value: valueProp, children } = props;
429
- if (children !== void 0) {
430
- if (valueProp instanceof Reactive) {
431
- let mark = new InsertMark(root, abortSignal);
432
- let ac;
433
- valueProp.reactive((value) => {
434
- const slots = children.filter((v) => isVNode(v) && v[1].slot === String(value));
435
- ac?.abort();
436
- if (slots.length > 0) {
437
- ac = new AbortController();
438
- mark.insert(...renderToFragment(scope, slots, ac.signal).childNodes);
439
- }
440
- }, abortSignal);
441
- onAbort(abortSignal, () => ac?.abort());
442
- } else {
443
- renderChildren(
444
- scope,
445
- children.filter((v) => isVNode(v) && v[1].slot === String(valueProp)),
446
- root,
447
- abortSignal
448
- );
449
- }
437
+ break;
438
+ }
439
+ // `<switch>` element
440
+ case "switch": {
441
+ const { value: valueProp, children } = props;
442
+ if (children !== void 0) {
443
+ if (valueProp instanceof Reactive) {
444
+ let mark = new InsertMark(root, abortSignal);
445
+ let ac;
446
+ valueProp.reactive((value) => {
447
+ const slots = children.filter((v) => isVNode(v) && v[1].slot === String(value));
448
+ ac?.abort();
449
+ if (slots.length > 0) {
450
+ ac = new AbortController();
451
+ mark.insert(...renderToFragment(scope, slots, ac.signal).childNodes);
452
+ }
453
+ }, abortSignal);
454
+ onAbort(abortSignal, () => ac?.abort());
455
+ } else {
456
+ renderChildren(
457
+ scope,
458
+ children.filter((v) => isVNode(v) && v[1].slot === String(valueProp)),
459
+ root,
460
+ abortSignal
461
+ );
450
462
  }
463
+ }
464
+ break;
465
+ }
466
+ default: {
467
+ if (typeof tag === "function") {
468
+ renderFC(tag, props, root, abortSignal);
451
469
  break;
452
470
  }
453
- default: {
454
- if (typeof tag === "function") {
455
- renderFC(tag, props, root, abortSignal);
471
+ if (isString(tag)) {
472
+ if (customElements.has(tag)) {
473
+ renderFC(customElements.get(tag), props, root, abortSignal);
456
474
  break;
457
475
  }
458
- if (isString(tag)) {
459
- if (customElements.has(tag)) {
460
- renderFC(customElements.get(tag), props, root, abortSignal);
461
- break;
462
- }
463
- const { portal, children, ...attrs } = props;
464
- const el = createElement(tag);
465
- for (const [attrName, attrValue] of Object.entries(attrs)) {
466
- switch (attrName) {
467
- case "class": {
468
- const updateClassName = (className) => {
469
- el.className = [className, ...el.classList.values().filter((name) => name.startsWith("css-"))].join(" ");
470
- };
471
- if (isString(attrValue)) {
472
- updateClassName(attrValue);
473
- } else {
474
- let mark = /* @__PURE__ */ new Set();
475
- let update = () => updateClassName(cx(attrValue, mark));
476
- update();
477
- for (const reactive of mark) {
478
- reactive.watch(update, abortSignal);
479
- }
480
- mark = void 0;
476
+ const { portal, children, ...attrs } = props;
477
+ const el = createElement(tag);
478
+ for (const [attrName, attrValue] of Object.entries(attrs)) {
479
+ switch (attrName) {
480
+ case "class": {
481
+ const updateClassName = (className) => {
482
+ el.className = [className, ...el.classList.values().filter((name) => name.startsWith("css-"))].join(" ");
483
+ };
484
+ if (isString(attrValue)) {
485
+ updateClassName(attrValue);
486
+ } else {
487
+ let mark = /* @__PURE__ */ new Set();
488
+ let update = () => updateClassName(cx(attrValue, mark));
489
+ update();
490
+ for (const reactive of mark) {
491
+ reactive.watch(update, abortSignal);
481
492
  }
482
- break;
493
+ mark = void 0;
483
494
  }
484
- case "style": {
485
- if (isString(attrValue)) {
486
- el.style.cssText = attrValue;
487
- } else {
488
- let mark = /* @__PURE__ */ new Set();
489
- let update = () => applyStyle(el, attrValue, mark);
490
- update();
491
- for (const reactive of mark) {
492
- reactive.watch(update, abortSignal);
493
- }
494
- mark = void 0;
495
+ break;
496
+ }
497
+ case "style": {
498
+ if (isString(attrValue)) {
499
+ el.style.cssText = attrValue;
500
+ } else {
501
+ let mark = /* @__PURE__ */ new Set();
502
+ let update = () => applyStyle(el, attrValue, mark);
503
+ update();
504
+ for (const reactive of mark) {
505
+ reactive.watch(update, abortSignal);
495
506
  }
496
- break;
507
+ mark = void 0;
497
508
  }
498
- case "ref":
499
- if (isFunction(attrValue)) {
500
- const ret = attrValue(el);
501
- if (isFunction(ret)) {
502
- onAbort(abortSignal, ret);
503
- }
504
- } else if (attrValue instanceof Ref) {
505
- attrValue.refs.set(attrValue.name, el);
506
- }
507
- break;
508
- case "slot":
509
- break;
510
- case "$checked":
511
- case "$value":
512
- if (attrValue instanceof Signal) {
513
- const name = attrName.slice(1);
514
- const isValue = name.charAt(0) === "v";
515
- attrValue.reactive((value) => {
516
- el[name] = isValue ? String(value) : !!value;
517
- }, abortSignal);
518
- el.addEventListener("input", () => attrValue.set(el[name]));
519
- } else {
520
- setAttribute(el, attrName.slice(1), attrValue);
521
- }
522
- break;
523
- case "viewTransition":
524
- break;
525
- case "action":
526
- if (isFunction(attrValue) && tag === "form") {
527
- el.addEventListener("submit", (evt) => {
528
- evt.preventDefault();
529
- attrValue(new FormData(evt.target), evt);
530
- });
531
- } else {
532
- setAttribute(el, attrName, attrValue);
533
- }
534
- break;
535
- default:
536
- if (attrName.startsWith("on") && isFunction(attrValue)) {
537
- el.addEventListener(attrName.slice(2).toLowerCase(), attrValue);
538
- } else if (attrValue instanceof Reactive) {
539
- attrValue.reactive((value) => setAttribute(el, attrName, value), abortSignal);
540
- } else {
541
- setAttribute(el, attrName, attrValue);
542
- }
543
- break;
509
+ break;
544
510
  }
545
- }
546
- onAbort(abortSignal, el.remove.bind(el));
547
- (portal instanceof HTMLElement ? portal : root).appendChild(el);
548
- if (children !== void 0) {
549
- renderChildren(scope, children, el, abortSignal);
511
+ case "ref":
512
+ if (isFunction(attrValue)) {
513
+ const ret = attrValue(el);
514
+ if (isFunction(ret)) {
515
+ onAbort(abortSignal, ret);
516
+ }
517
+ } else if (attrValue instanceof Ref) {
518
+ attrValue.refs.set(attrValue.name, el);
519
+ }
520
+ break;
521
+ case "slot":
522
+ break;
523
+ case "$checked":
524
+ case "$value":
525
+ if (attrValue instanceof Signal) {
526
+ const name = attrName.slice(1);
527
+ const isValue = name.charAt(0) === "v";
528
+ attrValue.reactive((value) => {
529
+ el[name] = isValue ? String(value) : !!value;
530
+ }, abortSignal);
531
+ el.addEventListener("input", () => attrValue.set(el[name]));
532
+ } else {
533
+ setAttribute(el, attrName.slice(1), attrValue);
534
+ }
535
+ break;
536
+ case "viewTransition":
537
+ break;
538
+ case "action":
539
+ if (isFunction(attrValue) && tag === "form") {
540
+ el.addEventListener("submit", (evt) => {
541
+ evt.preventDefault();
542
+ attrValue(new FormData(evt.target), evt);
543
+ });
544
+ } else {
545
+ setAttribute(el, attrName, attrValue);
546
+ }
547
+ break;
548
+ default:
549
+ if (attrName.startsWith("on") && isFunction(attrValue)) {
550
+ el.addEventListener(attrName.slice(2).toLowerCase(), attrValue);
551
+ } else if (attrValue instanceof Reactive) {
552
+ attrValue.reactive((value) => setAttribute(el, attrName, value), abortSignal);
553
+ } else {
554
+ setAttribute(el, attrName, attrValue);
555
+ }
556
+ break;
550
557
  }
551
558
  }
559
+ onAbort(abortSignal, el.remove.bind(el));
560
+ (portal instanceof HTMLElement ? portal : root).appendChild(el);
561
+ if (children !== void 0) {
562
+ renderChildren(scope, children, el, abortSignal);
563
+ }
552
564
  }
553
565
  }
554
- return;
555
566
  }
567
+ return;
556
568
  }
557
569
  }
558
570
  const textNode = createTextNode(String(child));
@@ -638,10 +650,10 @@ var applyStyle = (el, style, mark) => {
638
650
  if (isPlainObject(style)) {
639
651
  let { classList } = el;
640
652
  let inline;
653
+ let css = [];
641
654
  classList.remove(...classList.values().filter((key) => key.startsWith("css-")));
642
655
  for (let [k, v] of Object.entries(style)) {
643
656
  v = $(v, mark);
644
- let css = [];
645
657
  switch (k.charCodeAt(0)) {
646
658
  case /* ':' */
647
659
  58:
@@ -671,12 +683,14 @@ var applyStyle = (el, style, mark) => {
671
683
  inline ??= {};
672
684
  inline[k] = v;
673
685
  }
674
- if (css.length) {
675
- classList.add(computeStyleClassName(css));
676
- }
677
686
  }
678
- if (inline) {
679
- classList.add(computeStyleClassName([null, renderStyle(inline)]));
687
+ if (css.length > 0) {
688
+ if (inline) {
689
+ css.unshift(null, renderStyle(inline));
690
+ }
691
+ classList.add(createStyleElement(css));
692
+ } else if (inline) {
693
+ el.style.cssText = renderStyle(inline).slice(1, -1);
680
694
  }
681
695
  } else if (isString(style)) {
682
696
  el.style.cssText = style;
@@ -698,7 +712,7 @@ var renderStyle = (style, mark) => {
698
712
  }
699
713
  return "{" + css + "}";
700
714
  };
701
- var computeStyleClassName = (css) => {
715
+ var createStyleElement = (css) => {
702
716
  const hash = hashCode(css.join("")).toString(36);
703
717
  const className = "css-" + hash;
704
718
  if (!document2.getElementById(className)) {
package/jsx-runtime.mjs CHANGED
@@ -176,7 +176,7 @@ var $vnode = /* @__PURE__ */ Symbol.for("jsx.vnode");
176
176
  var $setup = /* @__PURE__ */ Symbol.for("mono.setup");
177
177
 
178
178
  // version.ts
179
- var VERSION = "0.9.6";
179
+ var VERSION = "0.9.8";
180
180
 
181
181
  // render.ts
182
182
  var FunctionIdGenerator = class extends Map {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mono-jsx",
3
- "version": "0.9.6",
3
+ "version": "0.9.8",
4
4
  "description": "`<html>` as a `Response`.",
5
5
  "type": "module",
6
6
  "module": "./index.mjs",
package/types/jsx.d.ts CHANGED
@@ -117,7 +117,7 @@ declare global {
117
117
  [K in keyof IntrinsicElements]: P extends IntrinsicElements[K] ? K : never;
118
118
  }[keyof IntrinsicElements]
119
119
  | ComponentType<P>;
120
- type Raw = (template: string | TemplateStringsArray, ...values: unknown[]) => Element;
120
+ type Raw = (template: MaybeGetter<string> | TemplateStringsArray, ...values: unknown[]) => Element;
121
121
  interface CustomAttributes {}
122
122
  interface HtmlCustomAttributes {}
123
123
  interface BuiltinElements {}