0.1.1 • Published 2 years ago

xml-jsx-runtime v0.1.1

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

xml-jsx-runtime

npm GitHub Repo stars

A custom automatic JSX runtime that transforms JSX into xml-js's non-compact objects that can be converted into plain XML strings.


Installation:

Install the runtime from npm:

npm install xml-jsx-runtime -D

You also need to install xml-js independently in order to convert your XML non-compact objects into strings:

npm install xml-jsx-runtime -S

Usage:

Method 1: with a configuration file.

Pass the following options to @babel/plugin-transform-react-jsx (or whatever JSX transformer you're using):

{
  "throwIfNamespace": false,
  "runtime": "automatic",
  "importSource": "xml-jsx-runtime/runtime"
}

Note that you might not be using @babel/plugin-transform-react-jsx directly, as it is included in other presets such as @babel/preset-react or @vitejs/plugin-react. Or you might be using esbuild's own JSX loader instead. Either way, you should figure out how to configure your JSX loader by yourself.

Method 2: with a @jsxImportSource pragma comment (recommanded).

You may add the following comment to the top of your .jsx file:

/** @jsxImportSource xml-jsx-runtime/runtime */

And the runtime will be explicitly enabled for that file (only).

This method allows you to keep using React JSX in your project, and only use xml-jsx-runtime where you need it.

You'll still need to set throwIfNamespace to false from your configuration file if you want XML namespace support. Also you should add /** @jsxRuntime automatic */ as well if it's not already set by default.

Examples & Features

Quick start:

/** @jsxImportSource xml-jsx-runtime/runtime */
import { js2xml } from 'xml-js'


const src = <book category='WEB'>
  <title lang='en'>Learning XML</title>
  <author>Erik T. Ray</author>
  <year>2003</year>
  <price>39.95</price>
</book>

console.log(src)
/** Output:
 * XMLElement {
 *   elements: [
 *     XMLElement {
 *       type: 'element',
 *       name: 'book',
 *       attributes: [Object],
 *       elements: [Array]
 *     }
 *   ]
 * }
 */

const xml = js2xml(src) // to xml string

console.log(xml)
/** Output:
 * <book category="WEB"><title lang="en">Learning XML</title><author>Erik T. Ray</author><year>2003</year><price>39.95</price></book>
 */

Fragments are supported:

/** @jsxImportSource xml-jsx-runtime/runtime */
import { js2xml } from 'xml-js'

const xml = js2xml(
  <>
    <book id='1' />
    <book id='2' />
    <>
      <book id='3' />
      <>
        <book id='4' />
      </>
    </>
  </>
)

console.log(xml)
/** Output:
 * <book id="1"/><book id="2"/><book id="3"/><book id="4"/>
 */

Unlike React, the key attribute isn't concidered special:

/** @jsxImportSource xml-jsx-runtime/runtime */
import { js2xml } from 'xml-js'

const xml = js2xml(
  <>
    <elm key='1' />
    <elm key='2' {...undefined} />
    <elm {...undefined} key='3' />
  </>
)

console.log(xml)
/** Output:
 * <elm key="1"/><elm key="2"/><elm key="3"/>
 */

Note that internally the 3rd element will fallback to the lagecy createElement function instead of the jsx function. Thankfully, xml-jsx-runtime handles that case correctly. See more about this issue here.

The children attribute is reserved by JSX to pass child elements, however you may still specify it by adding the $: namespace prefix:

/** @jsxImportSource xml-jsx-runtime/runtime */
import { js2xml } from 'xml-js'

const xml = js2xml(
  <room adults={2} $:children={1} />
  /**
   * Or
   * <room adults={2} {...{'$:children': 1}} />
   * if you environment doesn't support XML namespaces
   */
)

console.log(xml)
/** Output:
 * <room adults="2" children="1"/>
 */

Keep in mind that the $ character is not valid as an xml namespace prefix. However, JSX supports it, so I have decided to use it as a way to bypass such limitations. It will always gets ignored in the result.

JSX always treat tags with capitalized names as value-based elements, but you still may have capitalized elements by defining a variable then assigning a string to it:

/** @jsxImportSource xml-jsx-runtime/runtime */
import { js2xml } from 'xml-js'

const Element = 'Element'

const xml = js2xml(
  <Element />
)

console.log(xml)
/** Output:
 * <Element />
 */

Or, you may completely avoid that pattren by adding the $: prefix 😁:

/** @jsxImportSource xml-jsx-runtime/runtime */
import { js2xml } from 'xml-js'

const xml = js2xml(
  <$:Element />
)

console.log(xml)
/** Output:
 * <Element />
 */

Function elements (value-based elements) are supported, just like in React:

/** @jsxImportSource xml-jsx-runtime/runtime */
import { js2xml } from 'xml-js'

const Element = ({ attribute } : { attribute: string }) => (
  <element attribute={attribute} />
)

const xml = js2xml(
  <Element attribute='value' />
)

console.log(xml)
/** Output:
 * <element attribute="value"/>
 */

For children, the values undefined, null, false, true will be ignored in the result. Multidimensional arrays will get flattened automatically. Passing objects that are not instances of XMLElement, or types other than string or number will cause xml-jsx-runtime to throw an error. For attributes, all values will get converted into strings, even objects:

/** @jsxImportSource xml-jsx-runtime/runtime */
import { js2xml } from 'xml-js'

const text = <text>Lorem {undefined} ipsum {null} dolor {false} sit {true} amet</text>

const xml = js2xml(
  <>
    {text}
    {[<a />, [<b />, false, 1337, [<c />]], <d attribute={{}} />]}
  </>
)

console.log(xml)
/** Output:
 * <text>Lorem  ipsum  dolor  sit  amet</text><a/><b/>1337<c/><d attribute="[object Object]"/>
 */

License

MIT © ${Mr.DJA}