vite-plugin-vanjs 0.1.8 → 0.1.10

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
@@ -7,13 +7,13 @@
7
7
  [![typescript version](https://img.shields.io/badge/typescript-5.6.2-brightgreen)](https://www.typescriptlang.org/)
8
8
  [![vanjs-core version](https://img.shields.io/badge/vanjs--core-1.5.5-brightgreen)](https://github.com/vanjs-org/van)
9
9
  [![mini-van-plate version](https://img.shields.io/badge/mini--van--plate-0.6.3-brightgreen)](https://github.com/vanjs-org/mini-van-plate)
10
- [![vitest version](https://img.shields.io/badge/vitest-3.1.3-brightgreen)](https://www.vitest.dev/)
10
+ [![vitest version](https://img.shields.io/badge/vitest-3.2.2-brightgreen)](https://www.vitest.dev/)
11
11
  [![vite version](https://img.shields.io/badge/vite-6.3.5-brightgreen)](https://vite.dev)
12
12
 
13
13
  A mini meta-framework for [VanJS](https://vanjs.org/) developed around the awesome [vite](https://vite.dev) and tested with [vitest](https://vitest.dev). The plugin comes with a set of modules to streamline your workflow:
14
14
  * ***@vanjs/router*** - one of the most important part of an application which allows you to split code and lazy load page like components with ease, handles both Client Side Rendering (SSR) and Server Side Rendering (CSR) and makes it really easy to work with;
15
15
  * ***@vanjs/meta*** - allows you to create metadata for your pages as well as load additional assets with ease;
16
- * ***@vanjs/jsx*** - enables JSX transformation;
16
+ * ***@vanjs/jsx*** - enables JSX transformation with automatic namespace resolution;
17
17
  * ***@vanjs/setup*** - enables loading VanJS modules isomorphically;
18
18
  * ***@vanjs/server*** - provides various tools for Server Side Rendering (SSR);
19
19
  * ***@vanjs/client*** - provides various tools for Client Side Rendering (CSR).
@@ -36,6 +36,7 @@ deno add npm:vite-plugin-vanjs@latest
36
36
  bun add vite-plugin-vanjs@latest
37
37
  ```
38
38
 
39
+
39
40
  ### Wiki
40
41
 
41
42
  For a complete guide on how to use the plugin, be sure to check the wiki:
@@ -6,7 +6,7 @@ declare module "@vanjs/client" {
6
6
  } from "mini-van-plate/van-plate";
7
7
 
8
8
  /**
9
- * Sets the attribute value of the given name of the given element.
9
+ * Sets the attribute value of the given name to a given target element.
10
10
  *
11
11
  * @param element the target element
12
12
  * @param key the attribute name
@@ -18,6 +18,22 @@ declare module "@vanjs/client" {
18
18
  value: boolean | string | number | null | undefined,
19
19
  ) => void;
20
20
 
21
+ /**
22
+ * Sets the attribute value of the given name and a given namespace
23
+ * to a given target element.
24
+ *
25
+ * @param namespace the attribute/element namespace
26
+ * @param element the target element
27
+ * @param key the attribute name
28
+ * @param value the attribute value
29
+ */
30
+ export const setAttributeNS: (
31
+ namespace: string | null,
32
+ element: Element,
33
+ name: string,
34
+ value: boolean | string | number | null | undefined,
35
+ ) => void;
36
+
21
37
  /**
22
38
  * Normalize the style value and convert it to a string
23
39
  *
package/client/index.mjs CHANGED
@@ -15,6 +15,64 @@ export const setAttribute = (element, key, value) => {
15
15
  }
16
16
  };
17
17
 
18
+ /**
19
+ * Sets or removes an attribute with the specified or inferred namespace on an element.
20
+ *
21
+ * @param {string|null} ns - The namespace URI (e.g., 'http://www.w3.org/2000/svg') or null to infer from element.
22
+ * @param {Element} element - The DOM element to modify.
23
+ * @param {string} key - The attribute name (e.g., 'stroke-width', 'xlink:href').
24
+ * @param {string|boolean|null|undefined} value - The attribute value; falsy values remove the attribute.
25
+ */
26
+ export const setAttributeNS = (ns, element, key, value) => {
27
+ // Infer namespace from element if ns is null
28
+ const elementNS = ns || element.namespaceURI ||
29
+ /* istanbul ignore next - this is a required fallback */ null;
30
+
31
+ // Map attributes to specific namespaces
32
+ const attrNamespaces = {
33
+ "xlink:": "http://www.w3.org/1999/xlink", // XLink attributes (e.g., xlink:href)
34
+ "xml:": "http://www.w3.org/XML/1998/namespace", // XML attributes (e.g., xml:lang)
35
+ "xsi:": "http://www.w3.org/2001/XMLSchema-instance", // XML Schema Instance (e.g., xsi:schemaLocation)
36
+ };
37
+
38
+ // Determine attribute namespace
39
+ let attrNS = elementNS;
40
+ for (const [prefix, uri] of Object.entries(attrNamespaces)) {
41
+ if (key.startsWith(prefix)) {
42
+ attrNS = uri;
43
+ break;
44
+ }
45
+ }
46
+
47
+ if (value == null || value === false || value === "" || value === undefined) {
48
+ // Remove attribute
49
+ try {
50
+ // istanbul ignore else - case may not be covered by happy-dom?
51
+ if (attrNS && attrNS !== "null") {
52
+ // Strip prefix (e.g., xlink:href -> href)
53
+ element.removeAttributeNS(attrNS, key.replace(/^[^:]+:/, ""));
54
+ } else {
55
+ // istanbul ignore next - case may not be covered by happy-dom?
56
+ element.removeAttribute(key);
57
+ // istanbul ignore next - case may not be covered by happy-dom?
58
+ element.removeAttribute(key.replace(/^[^:]+:/, ""));
59
+ }
60
+ } catch (_e) {
61
+ // Silent fail: attribute may not exist
62
+ }
63
+ } else {
64
+ // Set attribute
65
+ const attr = value === true ? key.replace(/^[^:]+:/, "") : String(value);
66
+ try {
67
+ element.setAttributeNS(attrNS, key, attr);
68
+ } catch (_e) {
69
+ // Fallback to non-namespaced set
70
+ // istanbul ignore next - case may not be covered by happy-dom?
71
+ element.setAttribute(key, attr);
72
+ }
73
+ }
74
+ };
75
+
18
76
  /**
19
77
  * @param {import("csstype").Properties | string} style
20
78
  * @returns {string}
package/client/types.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  import * as CSS from "csstype";
3
3
 
4
4
  /**
5
- * Sets the attribute value of the given name of the given element.
5
+ * Sets the attribute value of a given name of a given element.
6
6
  *
7
7
  * @param element the target element
8
8
  * @param key the attribute name
@@ -14,6 +14,22 @@ export const setAttribute: (
14
14
  value: boolean | string | number | null | undefined,
15
15
  ) => void;
16
16
 
17
+ /**
18
+ * Sets a namespaced attribute value of a given namespace, name and a given element.
19
+ * Fallback to regular setAttribute automatically.
20
+ *
21
+ * @param namespace the namespace string
22
+ * @param element the target element
23
+ * @param key the attribute name
24
+ * @param value the attribute value
25
+ */
26
+ export const setAttributeNS: (
27
+ namespace: string,
28
+ element: Element,
29
+ name: string,
30
+ value: boolean | string | number | null | undefined,
31
+ ) => void;
32
+
17
33
  /**
18
34
  * Normalize the style value and convert it to a string
19
35
  *
package/jsx/jsx.d.ts CHANGED
@@ -34,7 +34,7 @@ declare global {
34
34
  declare namespace JSX {
35
35
  // type FunctionMaybe<T = unknown> = { (): T } | T;
36
36
  // type Element = VanNode;
37
- type FunctionMaybe<T = unknown> = (() => T) | StateView<T> | T;
37
+ type FunctionMaybe<T = unknown> = (() => T) | StateView<T> | T | undefined;
38
38
  type Element =
39
39
  | State<Primitive | null | undefined>
40
40
  | Node
@@ -402,61 +402,61 @@ declare namespace JSX {
402
402
  * Identifies the currently active element when DOM focus is on a composite widget, textbox,
403
403
  * group, or application.
404
404
  */
405
- "aria-activedescendant"?: string;
405
+ "aria-activedescendant"?: FunctionMaybe<string>;
406
406
  /**
407
407
  * Indicates whether assistive technologies will present all, or only parts of, the changed
408
408
  * region based on the change notifications defined by the aria-relevant attribute.
409
409
  */
410
- "aria-atomic"?: boolean | "false" | "true";
410
+ "aria-atomic"?: FunctionMaybe<boolean | "false" | "true">;
411
411
  /**
412
412
  * Indicates whether inputting text could trigger display of one or more predictions of the
413
413
  * user's intended value for an input and specifies how predictions would be presented if they
414
414
  * are made.
415
415
  */
416
- "aria-autocomplete"?: "none" | "inline" | "list" | "both";
416
+ "aria-autocomplete"?: FunctionMaybe<"none" | "inline" | "list" | "both">;
417
417
  /**
418
418
  * Indicates an element is being modified and that assistive technologies MAY want to wait until
419
419
  * the modifications are complete before exposing them to the user.
420
420
  */
421
- "aria-busy"?: boolean | "false" | "true";
421
+ "aria-busy"?: FunctionMaybe<boolean | "false" | "true">;
422
422
  /**
423
423
  * Indicates the current "checked" state of checkboxes, radio buttons, and other widgets.
424
424
  *
425
425
  * @see aria-pressed @see aria-selected.
426
426
  */
427
- "aria-checked"?: boolean | "false" | "mixed" | "true";
427
+ "aria-checked"?: FunctionMaybe<boolean | "false" | "mixed" | "true">;
428
428
  /**
429
429
  * Defines the total number of columns in a table, grid, or treegrid.
430
430
  *
431
431
  * @see aria-colindex.
432
432
  */
433
- "aria-colcount"?: number | string;
433
+ "aria-colcount"?: FunctionMaybe<number | string>;
434
434
  /**
435
435
  * Defines an element's column index or position with respect to the total number of columns
436
436
  * within a table, grid, or treegrid.
437
437
  *
438
438
  * @see aria-colcount @see aria-colspan.
439
439
  */
440
- "aria-colindex"?: number | string;
440
+ "aria-colindex"?: FunctionMaybe<number | string>;
441
441
  /**
442
442
  * Defines the number of columns spanned by a cell or gridcell within a table, grid, or
443
443
  * treegrid.
444
444
  *
445
445
  * @see aria-colindex @see aria-rowspan.
446
446
  */
447
- "aria-colspan"?: number | string;
447
+ "aria-colspan"?: FunctionMaybe<number | string>;
448
448
  /**
449
449
  * Identifies the element (or elements) whose contents or presence are controlled by the current
450
450
  * element.
451
451
  *
452
452
  * @see aria-owns.
453
453
  */
454
- "aria-controls"?: string;
454
+ "aria-controls"?: FunctionMaybe<string>;
455
455
  /**
456
456
  * Indicates the element that represents the current item within a container or set of related
457
457
  * elements.
458
458
  */
459
- "aria-current"?:
459
+ "aria-current"?: FunctionMaybe<
460
460
  | boolean
461
461
  | "false"
462
462
  | "true"
@@ -464,61 +464,65 @@ declare namespace JSX {
464
464
  | "step"
465
465
  | "location"
466
466
  | "date"
467
- | "time";
467
+ | "time"
468
+ | undefined
469
+ >;
468
470
  /**
469
471
  * Identifies the element (or elements) that describes the object.
470
472
  *
471
473
  * @see aria-labelledby
472
474
  */
473
- "aria-describedby"?: string;
475
+ "aria-describedby"?: FunctionMaybe<string>;
474
476
  /**
475
477
  * Identifies the element that provides a detailed, extended description for the object.
476
478
  *
477
479
  * @see aria-describedby.
478
480
  */
479
- "aria-details"?: string;
481
+ "aria-details"?: FunctionMaybe<string>;
480
482
  /**
481
483
  * Indicates that the element is perceivable but disabled, so it is not editable or otherwise
482
484
  * operable.
483
485
  *
484
486
  * @see aria-hidden @see aria-readonly.
485
487
  */
486
- "aria-disabled"?: boolean | "false" | "true";
488
+ "aria-disabled"?: FunctionMaybe<boolean | "false" | "true">;
487
489
  /**
488
490
  * Indicates what functions can be performed when a dragged object is released on the drop
489
491
  * target.
490
492
  *
491
493
  * @deprecated In ARIA 1.1
492
494
  */
493
- "aria-dropeffect"?: "none" | "copy" | "execute" | "link" | "move" | "popup";
495
+ "aria-dropeffect"?: FunctionMaybe<
496
+ "none" | "copy" | "execute" | "link" | "move" | "popup"
497
+ >;
494
498
  /**
495
499
  * Identifies the element that provides an error message for the object.
496
500
  *
497
501
  * @see aria-invalid @see aria-describedby.
498
502
  */
499
- "aria-errormessage"?: string;
503
+ "aria-errormessage"?: FunctionMaybe<string>;
500
504
  /**
501
505
  * Indicates whether the element, or another grouping element it controls, is currently expanded
502
506
  * or collapsed.
503
507
  */
504
- "aria-expanded"?: boolean | "false" | "true";
508
+ "aria-expanded"?: FunctionMaybe<boolean | "false" | "true">;
505
509
  /**
506
510
  * Identifies the next element (or elements) in an alternate reading order of content which, at
507
511
  * the user's discretion, allows assistive technology to override the general default of reading
508
512
  * in document source order.
509
513
  */
510
- "aria-flowto"?: string;
514
+ "aria-flowto"?: FunctionMaybe<string>;
511
515
  /**
512
516
  * Indicates an element's "grabbed" state in a drag-and-drop operation.
513
517
  *
514
518
  * @deprecated In ARIA 1.1
515
519
  */
516
- "aria-grabbed"?: boolean | "false" | "true";
520
+ "aria-grabbed"?: FunctionMaybe<boolean | "false" | "true">;
517
521
  /**
518
522
  * Indicates the availability and type of interactive popup element, such as menu or dialog,
519
523
  * that can be triggered by an element.
520
524
  */
521
- "aria-haspopup"?:
525
+ "aria-haspopup"?: FunctionMaybe<
522
526
  | boolean
523
527
  | "false"
524
528
  | "true"
@@ -526,54 +530,57 @@ declare namespace JSX {
526
530
  | "listbox"
527
531
  | "tree"
528
532
  | "grid"
529
- | "dialog";
533
+ | "dialog"
534
+ >;
530
535
  /**
531
536
  * Indicates whether the element is exposed to an accessibility API.
532
537
  *
533
538
  * @see aria-disabled.
534
539
  */
535
- "aria-hidden"?: boolean | "false" | "true";
540
+ "aria-hidden"?: FunctionMaybe<boolean | "false" | "true">;
536
541
  /**
537
542
  * Indicates the entered value does not conform to the format expected by the application.
538
543
  *
539
544
  * @see aria-errormessage.
540
545
  */
541
- "aria-invalid"?: boolean | "false" | "true" | "grammar" | "spelling";
546
+ "aria-invalid"?: FunctionMaybe<
547
+ boolean | "false" | "true" | "grammar" | "spelling"
548
+ >;
542
549
  /**
543
550
  * Indicates keyboard shortcuts that an author has implemented to activate or give focus to an
544
551
  * element.
545
552
  */
546
- "aria-keyshortcuts"?: string;
553
+ "aria-keyshortcuts"?: FunctionMaybe<string>;
547
554
  /**
548
555
  * Defines a string value that labels the current element.
549
556
  *
550
557
  * @see aria-labelledby.
551
558
  */
552
- "aria-label"?: string;
559
+ "aria-label"?: FunctionMaybe<string>;
553
560
  /**
554
561
  * Identifies the element (or elements) that labels the current element.
555
562
  *
556
563
  * @see aria-describedby.
557
564
  */
558
- "aria-labelledby"?: string;
565
+ "aria-labelledby"?: FunctionMaybe<string>;
559
566
  /** Defines the hierarchical level of an element within a structure. */
560
- "aria-level"?: number | string;
567
+ "aria-level"?: FunctionMaybe<number | string>;
561
568
  /**
562
569
  * Indicates that an element will be updated, and describes the types of updates the user
563
570
  * agents, assistive technologies, and user can expect from the live region.
564
571
  */
565
- "aria-live"?: "off" | "assertive" | "polite";
572
+ "aria-live"?: FunctionMaybe<"off" | "assertive" | "polite">;
566
573
  /** Indicates whether an element is modal when displayed. */
567
- "aria-modal"?: boolean | "false" | "true";
574
+ "aria-modal"?: FunctionMaybe<boolean | "false" | "true">;
568
575
  /** Indicates whether a text box accepts multiple lines of input or only a single line. */
569
- "aria-multiline"?: boolean | "false" | "true";
576
+ "aria-multiline"?: FunctionMaybe<boolean | "false" | "true">;
570
577
  /**
571
578
  * Indicates that the user may select more than one item from the current selectable
572
579
  * descendants.
573
580
  */
574
- "aria-multiselectable"?: boolean | "false" | "true";
581
+ "aria-multiselectable"?: FunctionMaybe<boolean | "false" | "true">;
575
582
  /** Indicates whether the element's orientation is horizontal, vertical, or unknown/ambiguous. */
576
- "aria-orientation"?: "horizontal" | "vertical";
583
+ "aria-orientation"?: FunctionMaybe<"horizontal" | "vertical">;
577
584
  /**
578
585
  * Identifies an element (or elements) in order to define a visual, functional, or contextual
579
586
  * parent/child relationship between DOM elements where the DOM hierarchy cannot be used to
@@ -581,39 +588,39 @@ declare namespace JSX {
581
588
  *
582
589
  * @see aria-controls.
583
590
  */
584
- "aria-owns"?: string;
591
+ "aria-owns"?: FunctionMaybe<string>;
585
592
  /**
586
593
  * Defines a short hint (a word or short phrase) intended to aid the user with data entry when
587
594
  * the control has no value. A hint could be a sample value or a brief description of the
588
595
  * expected format.
589
596
  */
590
- "aria-placeholder"?: string;
597
+ "aria-placeholder"?: FunctionMaybe<string>;
591
598
  /**
592
599
  * Defines an element's number or position in the current set of listitems or treeitems. Not
593
600
  * required if all elements in the set are present in the DOM.
594
601
  *
595
602
  * @see aria-setsize.
596
603
  */
597
- "aria-posinset"?: number | string;
604
+ "aria-posinset"?: FunctionMaybe<number | string>;
598
605
  /**
599
606
  * Indicates the current "pressed" state of toggle buttons.
600
607
  *
601
608
  * @see aria-checked @see aria-selected.
602
609
  */
603
- "aria-pressed"?: boolean | "false" | "mixed" | "true";
610
+ "aria-pressed"?: FunctionMaybe<boolean | "false" | "mixed" | "true">;
604
611
  /**
605
612
  * Indicates that the element is not editable, but is otherwise operable.
606
613
  *
607
614
  * @see aria-disabled.
608
615
  */
609
- "aria-readonly"?: boolean | "false" | "true";
616
+ "aria-readonly"?: FunctionMaybe<boolean | "false" | "true">;
610
617
  /**
611
618
  * Indicates what notifications the user agent will trigger when the accessibility tree within a
612
619
  * live region is modified.
613
620
  *
614
621
  * @see aria-atomic.
615
622
  */
616
- "aria-relevant"?:
623
+ "aria-relevant"?: FunctionMaybe<
617
624
  | "additions"
618
625
  | "additions removals"
619
626
  | "additions text"
@@ -623,57 +630,58 @@ declare namespace JSX {
623
630
  | "removals text"
624
631
  | "text"
625
632
  | "text additions"
626
- | "text removals";
633
+ | "text removals"
634
+ >;
627
635
  /** Indicates that user input is required on the element before a form may be submitted. */
628
- "aria-required"?: boolean | "false" | "true";
636
+ "aria-required"?: FunctionMaybe<boolean | "false" | "true">;
629
637
  /** Defines a human-readable, author-localized description for the role of an element. */
630
- "aria-roledescription"?: string;
638
+ "aria-roledescription"?: FunctionMaybe<string>;
631
639
  /**
632
640
  * Defines the total number of rows in a table, grid, or treegrid.
633
641
  *
634
642
  * @see aria-rowindex.
635
643
  */
636
- "aria-rowcount"?: number | string;
644
+ "aria-rowcount"?: FunctionMaybe<number | string>;
637
645
  /**
638
646
  * Defines an element's row index or position with respect to the total number of rows within a
639
647
  * table, grid, or treegrid.
640
648
  *
641
649
  * @see aria-rowcount @see aria-rowspan.
642
650
  */
643
- "aria-rowindex"?: number | string;
651
+ "aria-rowindex"?: FunctionMaybe<number | string>;
644
652
  /**
645
653
  * Defines the number of rows spanned by a cell or gridcell within a table, grid, or treegrid.
646
654
  *
647
655
  * @see aria-rowindex @see aria-colspan.
648
656
  */
649
- "aria-rowspan"?: number | string;
657
+ "aria-rowspan"?: FunctionMaybe<number | string>;
650
658
  /**
651
659
  * Indicates the current "selected" state of various widgets.
652
660
  *
653
661
  * @see aria-checked @see aria-pressed.
654
662
  */
655
- "aria-selected"?: boolean | "false" | "true";
663
+ "aria-selected"?: FunctionMaybe<boolean | "false" | "true">;
656
664
  /**
657
665
  * Defines the number of items in the current set of listitems or treeitems. Not required if all
658
666
  * elements in the set are present in the DOM.
659
667
  *
660
668
  * @see aria-posinset.
661
669
  */
662
- "aria-setsize"?: number | string;
670
+ "aria-setsize"?: FunctionMaybe<number | string>;
663
671
  /** Indicates if items in a table or grid are sorted in ascending or descending order. */
664
- "aria-sort"?: "none" | "ascending" | "descending" | "other";
672
+ "aria-sort"?: FunctionMaybe<"none" | "ascending" | "descending" | "other">;
665
673
  /** Defines the maximum allowed value for a range widget. */
666
- "aria-valuemax"?: number | string;
674
+ "aria-valuemax"?: FunctionMaybe<number | string>;
667
675
  /** Defines the minimum allowed value for a range widget. */
668
- "aria-valuemin"?: number | string;
676
+ "aria-valuemin"?: FunctionMaybe<number | string>;
669
677
  /**
670
678
  * Defines the current value for a range widget.
671
679
  *
672
680
  * @see aria-valuetext.
673
681
  */
674
- "aria-valuenow"?: number | string;
682
+ "aria-valuenow"?: FunctionMaybe<number | string>;
675
683
  /** Defines the human readable text alternative of aria-valuenow for a range widget. */
676
- "aria-valuetext"?: string;
684
+ "aria-valuetext"?: FunctionMaybe<string>;
677
685
  role?: FunctionMaybe<
678
686
  | "alert"
679
687
  | "alertdialog"
@@ -2333,6 +2341,215 @@ declare namespace JSX {
2333
2341
  use: UseSVGAttributes<SVGUseElement>;
2334
2342
  view: ViewSVGAttributes<SVGViewElement>;
2335
2343
  }
2344
+ /** @type {MathMLElementTagNameMap} */
2345
+ interface MathMLElementTags {
2346
+ math: MathMLMathAttributes<MathMLElement>;
2347
+ mi: MathMLAnnotationAttributes<MathMLMiElement>;
2348
+ mn: MathMLAnnotationAttributes<MathMLMnElement>;
2349
+ mo: MathMLOperatorAttributes<MathMLMoElement>;
2350
+ ms: MathMLAnnotationAttributes<MathMLMsElement>;
2351
+ mtext: MathMLAnnotationAttributes<MathMLMtextElement>;
2352
+ mspace: MathMLMspaceAttributes<MathMLMspaceElement>;
2353
+ mrow: MathMLRowAttributes<MathMLMrowElement>;
2354
+ mfrac: MathMLFracAttributes<MathMLMfracElement>;
2355
+ msqrt: MathMLRowAttributes<MathMLMsqrtElement>;
2356
+ mroot: MathMLRowAttributes<MathMLMrootElement>;
2357
+ mstyle: MathMLStyleAttributes<MathMLMstyleElement>;
2358
+ merror: MathMLRowAttributes<MathMLMerrorElement>;
2359
+ mpadded: MathMLPaddedAttributes<MathMLMpaddedElement>;
2360
+ mphantom: MathMLRowAttributes<MathMLMphantomElement>;
2361
+ mfenced: MathMLFencedAttributes<MathMLMfencedElement>;
2362
+ mtable: MathMLTableAttributes<MathMLMtableElement>;
2363
+ mtr: MathMLTableRowAttributes<MathMLMtrElement>;
2364
+ mtd: MathMLTableCellAttributes<MathMLMtdElement>;
2365
+ msub: MathMLScriptAttributes<MathMLMsubElement>;
2366
+ msup: MathMLScriptAttributes<MathMLMsupElement>;
2367
+ msubsup: MathMLScriptAttributes<MathMLMsubsupElement>;
2368
+ mmultiscripts: MathMLMultiscriptsAttributes<MathMLMmultiscriptsElement>;
2369
+ mover: MathMLScriptAttributes<MathMLMoverElement>;
2370
+ munder: MathMLScriptAttributes<MathMLMunderElement>;
2371
+ munderover: MathMLScriptAttributes<MathMLMunderoverElement>;
2372
+ semantics: MathMLSemanticsAttributes<MathMLSemanticsElement>;
2373
+ annotation: MathMLAnnotationElementAttributes<MathMLAnnotationElement>;
2374
+ "annotation-xml": MathMLAnnotationElementAttributes<
2375
+ MathMLAnnotationXMLElement
2376
+ >;
2377
+ }
2378
+
2379
+ /** MathML-specific attribute interfaces */
2380
+ interface MathMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
2381
+ class?: FunctionMaybe<string>;
2382
+ id?: FunctionMaybe<string>;
2383
+ style?: FunctionMaybe<CSSProperties | string>;
2384
+ dir?: FunctionMaybe<"ltr" | "rtl">;
2385
+ mathvariant?: FunctionMaybe<
2386
+ | "normal"
2387
+ | "bold"
2388
+ | "italic"
2389
+ | "bold-italic"
2390
+ | "double-struck"
2391
+ | "bold-fraktur"
2392
+ | "script"
2393
+ | "bold-script"
2394
+ | "fraktur"
2395
+ | "sans-serif"
2396
+ | "bold-sans-serif"
2397
+ | "sans-serif-italic"
2398
+ | "sans-serif-bold-italic"
2399
+ | "monospace"
2400
+ | "initial"
2401
+ | "tailed"
2402
+ | "looped"
2403
+ | "stretched"
2404
+ >;
2405
+ }
2406
+
2407
+ interface MathMLMathAttributes<T> extends MathMLAttributes<T> {
2408
+ display?: FunctionMaybe<"block" | "inline">;
2409
+ alttext?: FunctionMaybe<string>;
2410
+ altimg?: FunctionMaybe<string>;
2411
+ "altimg-width"?: FunctionMaybe<string>;
2412
+ "altimg-height"?: FunctionMaybe<string>;
2413
+ "altimg-valign"?: FunctionMaybe<string>;
2414
+ mathbackground?: FunctionMaybe<string>;
2415
+ mathcolor?: FunctionMaybe<string>;
2416
+ mathsize?: FunctionMaybe<string>;
2417
+ }
2418
+
2419
+ interface MathMLAnnotationAttributes<T> extends MathMLAttributes<T> {
2420
+ mathbackground?: FunctionMaybe<string>;
2421
+ mathcolor?: FunctionMaybe<string>;
2422
+ }
2423
+
2424
+ interface MathMLOperatorAttributes<T> extends MathMLAnnotationAttributes<T> {
2425
+ form?: FunctionMaybe<"prefix" | "infix" | "postfix">;
2426
+ fence?: FunctionMaybe<"true" | "false">;
2427
+ separator?: FunctionMaybe<"true" | "false">;
2428
+ lspace?: FunctionMaybe<string>;
2429
+ rspace?: FunctionMaybe<string>;
2430
+ stretchy?: FunctionMaybe<"true" | "false">;
2431
+ symmetric?: FunctionMaybe<"true" | "false">;
2432
+ maxsize?: FunctionMaybe<string>;
2433
+ minsize?: FunctionMaybe<string>;
2434
+ largeop?: FunctionMaybe<"true" | "false">;
2435
+ movablelimits?: FunctionMaybe<"true" | "false">;
2436
+ accent?: FunctionMaybe<"true" | "false">;
2437
+ }
2438
+
2439
+ interface MathMLMspaceAttributes<T> extends MathMLAnnotationAttributes<T> {
2440
+ width?: FunctionMaybe<string>;
2441
+ height?: FunctionMaybe<string>;
2442
+ depth?: FunctionMaybe<string>;
2443
+ }
2444
+
2445
+ interface MathMLRowAttributes<T> extends MathMLAnnotationAttributes<T> {}
2446
+
2447
+ interface MathMLFracAttributes<T> extends MathMLAnnotationAttributes<T> {
2448
+ linethickness?: FunctionMaybe<string>;
2449
+ numalign?: FunctionMaybe<"left" | "center" | "right">;
2450
+ denomalign?: FunctionMaybe<"left" | "center" | "right">;
2451
+ bevelled?: FunctionMaybe<"true" | "false">;
2452
+ }
2453
+
2454
+ interface MathMLStyleAttributes<T> extends MathMLAnnotationAttributes<T> {
2455
+ mathbackground?: FunctionMaybe<string>;
2456
+ mathcolor?: FunctionMaybe<string>;
2457
+ mathsize?: FunctionMaybe<string>;
2458
+ mathdepth?: FunctionMaybe<string>;
2459
+ }
2460
+
2461
+ interface MathMLPaddedAttributes<T> extends MathMLAnnotationAttributes<T> {
2462
+ width?: FunctionMaybe<string>;
2463
+ height?: FunctionMaybe<string>;
2464
+ depth?: FunctionMaybe<string>;
2465
+ lspace?: FunctionMaybe<string>;
2466
+ voffset?: FunctionMaybe<string>;
2467
+ }
2468
+
2469
+ interface MathMLFencedAttributes<T> extends MathMLAnnotationAttributes<T> {
2470
+ open?: FunctionMaybe<string>;
2471
+ close?: FunctionMaybe<string>;
2472
+ separators?: FunctionMaybe<string>;
2473
+ }
2474
+
2475
+ interface MathMLTableAttributes<T> extends MathMLAnnotationAttributes<T> {
2476
+ align?: FunctionMaybe<
2477
+ | "axis"
2478
+ | "baseline"
2479
+ | "center"
2480
+ | "top"
2481
+ | "bottom"
2482
+ | string
2483
+ >;
2484
+ rowalign?: FunctionMaybe<
2485
+ "top" | "bottom" | "center" | "baseline" | "axis"
2486
+ >;
2487
+ columnalign?: FunctionMaybe<
2488
+ "left" | "center" | "right"
2489
+ >;
2490
+ columnlines?: FunctionMaybe<
2491
+ "none" | "solid" | "dashed"
2492
+ >;
2493
+ rowlines?: FunctionMaybe<
2494
+ "none" | "solid" | "dashed"
2495
+ >;
2496
+ frame?: FunctionMaybe<
2497
+ "none" | "solid" | "dashed"
2498
+ >;
2499
+ framespacing?: FunctionMaybe<string>;
2500
+ equalrows?: FunctionMaybe<"true" | "false">;
2501
+ equalcolumns?: FunctionMaybe<"true" | "false">;
2502
+ displaystyle?: FunctionMaybe<"true" | "false">;
2503
+ side?: FunctionMaybe<"left" | "right" | "leftoverlap" | "rightoverlap">;
2504
+ minlabelspacing?: FunctionMaybe<string>;
2505
+ width?: FunctionMaybe<string>;
2506
+ }
2507
+
2508
+ interface MathMLTableRowAttributes<T> extends MathMLAnnotationAttributes<T> {
2509
+ rowalign?: FunctionMaybe<
2510
+ "top" | "bottom" | "center" | "baseline" | "axis"
2511
+ >;
2512
+ columnalign?: FunctionMaybe<
2513
+ "left" | "center" | "right"
2514
+ >;
2515
+ }
2516
+
2517
+ interface MathMLTableCellAttributes<T> extends MathMLAnnotationAttributes<T> {
2518
+ rowspan?: FunctionMaybe<number | string>;
2519
+ columnspan?: FunctionMaybe<number | string>;
2520
+ rowalign?: FunctionMaybe<
2521
+ "top" | "bottom" | "center" | "baseline" | "axis"
2522
+ >;
2523
+ columnalign?: FunctionMaybe<
2524
+ "left" | "center" | "right"
2525
+ >;
2526
+ }
2527
+
2528
+ interface MathMLScriptAttributes<T> extends MathMLAnnotationAttributes<T> {
2529
+ subscriptshift?: FunctionMaybe<string>;
2530
+ superscriptshift?: FunctionMaybe<string>;
2531
+ }
2532
+
2533
+ interface MathMLMultiscriptsAttributes<T>
2534
+ extends MathMLAnnotationAttributes<T> {
2535
+ subscriptshift?: FunctionMaybe<string>;
2536
+ superscriptshift?: FunctionMaybe<string>;
2537
+ }
2538
+
2539
+ interface MathMLSemanticsAttributes<T> extends MathMLAnnotationAttributes<T> {
2540
+ encoding?: FunctionMaybe<string>;
2541
+ src?: FunctionMaybe<string>;
2542
+ }
2543
+
2544
+ interface MathMLAnnotationElementAttributes<T>
2545
+ extends MathMLAnnotationAttributes<T> {
2546
+ encoding?: FunctionMaybe<string>;
2547
+ src?: FunctionMaybe<string>;
2548
+ }
2336
2549
  interface IntrinsicElements
2337
- extends HTMLElementTags, HTMLElementDeprecatedTags, SVGElementTags {}
2550
+ extends
2551
+ HTMLElementTags,
2552
+ HTMLElementDeprecatedTags,
2553
+ SVGElementTags,
2554
+ MathMLElementTags {}
2338
2555
  }
package/jsx/jsx.mjs CHANGED
@@ -1,22 +1,32 @@
1
1
  import van from "vanjs-core";
2
2
  import isServer from "../setup/isServer.mjs";
3
- import { setAttribute, styleToString } from "../client/index.mjs";
3
+ import { setAttributeNS, styleToString } from "../client/index.mjs";
4
+ import { namespaceElements } from "./namespaceElements.mjs";
4
5
 
6
+ let currentNamespace;
7
+
8
+ /**
9
+ * Compiles JSX to VanJS elements with automatic namespace resolution.
10
+ *
11
+ * @param {string|Function} jsxTag - The tag name (e.g., 'svg') or component function.
12
+ * @param {Object} props - Props including children, ref, style, and attributes.
13
+ * @returns {Element|null} The compiled VanJS element or null for invalid tags.
14
+ */
5
15
  export const jsx = (jsxTag, { children, ref, style, ...rest }) => {
6
- // filter props with undefined values
16
+ // Filter props with undefined values
7
17
  const props = Object.fromEntries(
8
18
  Object.entries(rest).filter(([_, val]) => val !== undefined),
9
19
  );
10
20
 
11
21
  if (typeof jsxTag === "string") {
12
- const newElement = van.tags[jsxTag](props, children);
22
+ const ns = currentNamespace || namespaceElements[jsxTag];
23
+ const newElement = (ns ? van.tags(ns) : van.tags)[jsxTag](props, children);
13
24
 
25
+ // Handle style reactively
14
26
  van.derive(() => {
15
- /* istanbul ignore else */
16
27
  if (style) {
17
28
  const styleProp = typeof style === "function" ? style() : style;
18
29
  const styleValue = styleToString(styleProp);
19
-
20
30
  if (isServer) {
21
31
  newElement.propsStr += ` style="${styleValue}"`;
22
32
  } else {
@@ -25,13 +35,18 @@ export const jsx = (jsxTag, { children, ref, style, ...rest }) => {
25
35
  }
26
36
  });
27
37
 
28
- // on server it's good enough to return here
29
- if (isServer) return newElement;
38
+ // On server, apply props as string
39
+ if (isServer) {
40
+ return newElement;
41
+ }
30
42
 
31
- // on the client, we sure do need to set the attributes
43
+ // On client, apply attributes reactively
32
44
  for (const [k, value] of Object.entries(props)) {
45
+ // Use element's namespace for attributes
46
+ const attrNamespace = k === "xmlns" ? null : newElement.namespaceURI;
47
+
33
48
  if (typeof value === "function" && !k.startsWith("on")) {
34
- van.derive(() => setAttribute(newElement, k, value()));
49
+ van.derive(() => setAttributeNS(attrNamespace, newElement, k, value()));
35
50
  continue;
36
51
  }
37
52
 
@@ -41,11 +56,13 @@ export const jsx = (jsxTag, { children, ref, style, ...rest }) => {
41
56
  }
42
57
 
43
58
  if (typeof value === "object" && "val" in value) {
44
- van.derive(() => setAttribute(newElement, k, value.val));
59
+ van.derive(() =>
60
+ setAttributeNS(attrNamespace, newElement, k, value.val)
61
+ );
45
62
  continue;
46
63
  }
47
64
 
48
- setAttribute(newElement, k, value);
65
+ setAttributeNS(attrNamespace, newElement, k, value);
49
66
  }
50
67
 
51
68
  if (ref) ref.val = { current: newElement };
@@ -0,0 +1,138 @@
1
+ // in the browser we have the Document API, is it possible to
2
+ export const namespaceElementsMap = {
3
+ "http://www.w3.org/1999/xhtml": [
4
+ // tags common with SVG
5
+ "a",
6
+ "style",
7
+ "title",
8
+ // "abbr", "address", "area", "article", "aside", "audio", "b", "base", "bdi", "bdo",
9
+ // "blockquote", "body", "br", "button", "canvas", "caption", "cite", "code", "col", "colgroup",
10
+ // "data", "datalist", "dd", "del", "details", "dfn", "dialog", "div", "dl", "dt", "em", "embed",
11
+ // "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6",
12
+ // "head", "header", "hgroup", "hr", "html", "i", "iframe", "img", "input", "ins", "kbd",
13
+ // "label", "legend", "li", "link", "main", "map", "mark", "menu", "meta", "meter", "nav",
14
+ // "noscript", "object", "ol", "optgroup", "option", "output", "p", "param", "picture", "pre",
15
+ // "progress", "q", "rp", "rt", "ruby", "s", "samp", "script", "section", "select", "slot",
16
+ // "small", "source", "span", "strong",
17
+ // "sub", "summary", "sup", "table", "tbody",
18
+ // "td", "template", "textarea", "tfoot", "th", "thead", "time",
19
+ // "tr", "track", "u",
20
+ // "ul", "var", "video", "wbr"
21
+ ],
22
+ "http://www.w3.org/2000/svg": [
23
+ "svg",
24
+ "a",
25
+ "animate",
26
+ "animateMotion",
27
+ "animateTransform",
28
+ "circle",
29
+ "clipPath",
30
+ "defs",
31
+ "desc",
32
+ "ellipse",
33
+ "feBlend",
34
+ "feColorMatrix",
35
+ "feComponentTransfer",
36
+ "feComposite",
37
+ "feConvolveMatrix",
38
+ "feDiffuseLighting",
39
+ "feDisplacementMap",
40
+ "feDistantLight",
41
+ "feDropShadow",
42
+ "feFlood",
43
+ "feFuncA",
44
+ "feFuncB",
45
+ "feFuncG",
46
+ "feFuncR",
47
+ "feGaussianBlur",
48
+ "feImage",
49
+ "feMerge",
50
+ "feMergeNode",
51
+ "feMorphology",
52
+ "feOffset",
53
+ "fePointLight",
54
+ "feSpecularLighting",
55
+ "feSpotLight",
56
+ "feTile",
57
+ "feTurbulence",
58
+ "filter",
59
+ "foreignObject",
60
+ "g",
61
+ "image",
62
+ "line",
63
+ "linearGradient",
64
+ "marker",
65
+ "mask",
66
+ "metadata",
67
+ "mpath",
68
+ "path",
69
+ "pattern",
70
+ "polygon",
71
+ "polyline",
72
+ "radialGradient",
73
+ "rect",
74
+ "set",
75
+ "stop",
76
+ "style",
77
+ "switch",
78
+ "symbol",
79
+ "text",
80
+ "textPath",
81
+ "title",
82
+ "tspan",
83
+ "use",
84
+ "view",
85
+ ],
86
+ "http://www.w3.org/1998/Math/MathML": [
87
+ "math",
88
+ "maction",
89
+ "maligngroup",
90
+ "malignmark",
91
+ "menclose",
92
+ "merror",
93
+ "mfenced",
94
+ "mfrac",
95
+ "mglyph",
96
+ "mi",
97
+ "mlabeledtr",
98
+ "mmultiscripts",
99
+ "mn",
100
+ "mo",
101
+ "mover",
102
+ "mpadded",
103
+ "mphantom",
104
+ "mprescripts",
105
+ "mroot",
106
+ "mrow",
107
+ "ms",
108
+ "mspace",
109
+ "msqrt",
110
+ "mstyle",
111
+ "msub",
112
+ "msubsup",
113
+ "msup",
114
+ "mtable",
115
+ "mtd",
116
+ "mtext",
117
+ "mtr",
118
+ "munder",
119
+ "munderover",
120
+ "semantics",
121
+ "annotation",
122
+ "annotation-xml",
123
+ ],
124
+ };
125
+
126
+ /**
127
+ * Create reverse lookup for namespace elements
128
+ * @type {Record<string, string>}
129
+ */
130
+ export const namespaceElements = Object.entries(namespaceElementsMap).reduce(
131
+ (acc, [namespace, elements]) => {
132
+ elements.forEach((element) => {
133
+ if (!(element in acc)) acc[element] = namespace;
134
+ });
135
+ return acc;
136
+ },
137
+ {},
138
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-vanjs",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "author": "thednp",
5
5
  "license": "MIT",
6
6
  "description": "A mini meta-framework for VanJS powered by Vite",
@@ -84,17 +84,17 @@
84
84
  "csstype": "^3.1.3",
85
85
  "mini-van-plate": "^0.6.3",
86
86
  "vanjs-core": "^1.5.5",
87
- "vanjs-ext": "^0.6.2"
87
+ "vanjs-ext": "^0.6.3"
88
88
  },
89
89
  "devDependencies": {
90
- "@types/node": "^22.15.3",
91
- "@vitest/browser": "^3.1.3",
92
- "@vitest/coverage-istanbul": "^3.1.3",
93
- "@vitest/ui": "^3.1.3",
90
+ "@types/node": "^22.15.30",
91
+ "@vitest/browser": "^3.2.2",
92
+ "@vitest/coverage-istanbul": "^3.2.2",
93
+ "@vitest/ui": "^3.2.2",
94
94
  "happy-dom": "^16.8.1",
95
95
  "typescript": "5.6.2",
96
96
  "vite": "^6.3.5",
97
- "vitest": "^3.1.3"
97
+ "vitest": "^3.2.2"
98
98
  },
99
99
  "packageManager": "pnpm@8.6.12",
100
100
  "engines": {
package/plugin/index.mjs CHANGED
@@ -71,8 +71,7 @@ export default function VitePluginVanJS(options = {}) {
71
71
  include: [
72
72
  "vanjs-core",
73
73
  "vanjs-ext",
74
- "mini-van-plate/van-plate",
75
- "mini-van-plate/shared",
74
+ "mini-van-plate",
76
75
  ],
77
76
  },
78
77
  ssr: {