diff --git a/cjs/index.js b/cjs/index.js
index 060522c..65c356a 100644
--- a/cjs/index.js
+++ b/cjs/index.js
@@ -1,6 +1,12 @@
'use strict';
/*! (c) Andrea Giammarchi - ISC */
+class Bound {
+ constructor(_) {
+ this._ = _;
+ }
+}
+
const empty = /^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i;
const er = '<☠>';
const re = /^on([A-Z])/;
@@ -12,14 +18,17 @@ const place = (_, $) => ('@' + $.toLowerCase());
* @param {any} value the attribute value.
* @returns {object} An object with `name` and `value` fields.
*/
-const attribute = (name, value) => {
- const type = typeof value;
- switch (type) {
+const defaultAttribute = (name, value) => {
+ switch (typeof value) {
case 'string':
+ case 'number':
return {name, value};
case 'boolean':
return {name: '?' + name, value};
case 'object':
+ case 'undefined':
+ if (value == null)
+ return {name, value};
if (value instanceof Bound)
return {name: '.' + name, value: value._};
}
@@ -29,69 +38,80 @@ const attribute = (name, value) => {
const bind = value => new Bound(value);
exports.bind = bind;
+/**
+ * @typedef {object} config - optionally configure the pragma function
+ * @property {function} [attribute=defaultAttribute] - a `callback(name, value)` to return a `{name, value}` literal.
+ * @property {Map} [cache=new Map] - a cache for already known/parsed templates.
+ * @property {boolean} [xml=false] - treat nodes as XML with self-closing tags.
+ */
+
/**
* Return an `h` / pragma function usable with JSX transformation.
- * @param {function} tag A template literal tag function to invoke.
- * @param {Map?} cache A cache to avoid passing along different arrays per same template / values.
- * @returns {function} The `h` / pragma function to use with JSX.
+ * @param {function} tag a template literal tag function to invoke.
+ * @param {config={}} config an optional configuration object.
+ * @returns {function} the `h` / `React.createElement` like pragma function to use with JSX.
*/
-const createPragma = (tag, cache) => {
- if (!cache)
- cache = new Map;
- return function h(entry, attributes, ...children) {
- const component = typeof entry === 'function';
- if (component && !('tagName' in entry)) {
- (attributes || (attributes = {})).children = children;
- return 'prototype' in entry ? new entry(attributes) : entry(attributes);
+const createPragma = (
+ tag,
+ {
+ attribute = defaultAttribute,
+ cache = new Map,
+ xml = false
+ } = {}
+) => function h(entry, attributes, ...children) {
+ const component = typeof entry === 'function';
+ // avoid dealing with µbe classes
+ if (component && !('tagName' in entry)) {
+ // pass {...props, children} to the component
+ (attributes || (attributes = {})).children = children;
+ return 'prototype' in entry ? new entry(attributes) : entry(attributes);
+ }
+ const template = ['<'];
+ const args = [template];
+ let i = 0;
+ if (component) {
+ args.push(entry);
+ i = template.push('') - 1;
+ }
+ else
+ template[i] += entry;
+ for (const key in attributes) {
+ const {name, value} = attribute(key, attributes[key]);
+ template[i] += ` ${name}="`;
+ args.push(value);
+ i = template.push('"') - 1;
+ }
+ const {length} = children;
+ template[i] += (length || !xml) ? '>' : ' />';
+ for (let child, j = 0; j < length; j++) {
+ child = children[j];
+ if (typeof child === 'string')
+ template[i] += child;
+ else {
+ args.push(child);
+ i = template.push('') - 1;
}
- const template = ['<'];
- const args = [null];
- let i = 0;
+ }
+ if (
+ length || (
+ !xml && (
+ (component && !empty.test(component.tagName)) ||
+ !empty.test(entry)
+ )
+ )
+ ) {
if (component) {
+ template[i] += '';
args.push(entry);
- i = template.push('') - 1;
+ template.push('>');
}
else
- template[i] += entry;
- for (const key in attributes) {
- const {name, value} = attribute(key, attributes[key]);
- args.push(value);
- template[i] += ` ${name}="`;
- i = template.push('"') - 1;
- }
- template[i] += '>';
- const {length} = children;
- for (let child, j = 0; j < length; j++) {
- child = children[j];
- if (typeof child === 'string')
- template[i] += child;
- else {
- args.push(child);
- i = template.push('') - 1;
- }
- }
- if (
- 0 < length ||
- (component && !empty.test(component.tagName)) ||
- !empty.test(entry)
- ) {
- if (component) {
- template[i] += '';
- args.push(entry);
- template.push('>');
- }
- else
- template[i] += `${entry}>`;
- }
- const whole = template.join(er);
- args[0] = cache.get(whole) || template;
- if (args[0] === template)
- cache.set(whole, template);
- return tag.apply(this, args);
- };
+ template[i] += `${entry}>`;
+ }
+ const whole = template.join(er);
+ args[0] = cache.get(whole) || template;
+ if (args[0] === template)
+ cache.set(whole, template);
+ return tag.apply(this, args);
};
exports.createPragma = createPragma;
-
-function Bound(_) {
- this._ = _;
-}
diff --git a/esm/index.js b/esm/index.js
index a77bde9..0be22eb 100644
--- a/esm/index.js
+++ b/esm/index.js
@@ -1,5 +1,11 @@
/*! (c) Andrea Giammarchi - ISC */
+class Bound {
+ constructor(_) {
+ this._ = _;
+ }
+}
+
const empty = /^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i;
const er = '<☠>';
const re = /^on([A-Z])/;
@@ -11,14 +17,17 @@ const place = (_, $) => ('@' + $.toLowerCase());
* @param {any} value the attribute value.
* @returns {object} An object with `name` and `value` fields.
*/
-const attribute = (name, value) => {
- const type = typeof value;
- switch (type) {
+const defaultAttribute = (name, value) => {
+ switch (typeof value) {
case 'string':
+ case 'number':
return {name, value};
case 'boolean':
return {name: '?' + name, value};
case 'object':
+ case 'undefined':
+ if (value == null)
+ return {name, value};
if (value instanceof Bound)
return {name: '.' + name, value: value._};
}
@@ -27,70 +36,79 @@ const attribute = (name, value) => {
export const bind = value => new Bound(value);
+/**
+ * @typedef {object} config - optionally configure the pragma function
+ * @property {function} [attribute=defaultAttribute] - a `callback(name, value)` to return a `{name, value}` literal.
+ * @property {Map} [cache=new Map] - a cache for already known/parsed templates.
+ * @property {boolean} [xml=false] - treat nodes as XML with self-closing tags.
+ */
+
/**
* Return an `h` / pragma function usable with JSX transformation.
- * @param {function} tag A template literal tag function to invoke.
- * @param {Map?} cache A cache to avoid passing along different arrays per same template / values.
- * @returns {function} The `h` / pragma function to use with JSX.
+ * @param {function} tag a template literal tag function to invoke.
+ * @param {config={}} config an optional configuration object.
+ * @returns {function} the `h` / `React.createElement` like pragma function to use with JSX.
*/
-export const createPragma = (tag, cache) => {
- if (!cache)
- cache = new Map;
- return function h(entry, attributes, ...children) {
- const component = typeof entry === 'function';
- // avoid dealing with ube classes
- if (component && !('tagName' in entry)) {
- // pass {...props, children} to the component
- (attributes || (attributes = {})).children = children;
- return 'prototype' in entry ? new entry(attributes) : entry(attributes);
+export const createPragma = (
+ tag,
+ {
+ attribute = defaultAttribute,
+ cache = new Map,
+ xml = false
+ } = {}
+) => function h(entry, attributes, ...children) {
+ const component = typeof entry === 'function';
+ // avoid dealing with µbe classes
+ if (component && !('tagName' in entry)) {
+ // pass {...props, children} to the component
+ (attributes || (attributes = {})).children = children;
+ return 'prototype' in entry ? new entry(attributes) : entry(attributes);
+ }
+ const template = ['<'];
+ const args = [template];
+ let i = 0;
+ if (component) {
+ args.push(entry);
+ i = template.push('') - 1;
+ }
+ else
+ template[i] += entry;
+ for (const key in attributes) {
+ const {name, value} = attribute(key, attributes[key]);
+ template[i] += ` ${name}="`;
+ args.push(value);
+ i = template.push('"') - 1;
+ }
+ const {length} = children;
+ template[i] += (length || !xml) ? '>' : ' />';
+ for (let child, j = 0; j < length; j++) {
+ child = children[j];
+ if (typeof child === 'string')
+ template[i] += child;
+ else {
+ args.push(child);
+ i = template.push('') - 1;
}
- const template = ['<'];
- const args = [null];
- let i = 0;
+ }
+ if (
+ length || (
+ !xml && (
+ (component && !empty.test(component.tagName)) ||
+ !empty.test(entry)
+ )
+ )
+ ) {
if (component) {
+ template[i] += '';
args.push(entry);
- i = template.push('') - 1;
+ template.push('>');
}
else
- template[i] += entry;
- for (const key in attributes) {
- const {name, value} = attribute(key, attributes[key]);
- args.push(value);
- template[i] += ` ${name}="`;
- i = template.push('"') - 1;
- }
- template[i] += '>';
- const {length} = children;
- for (let child, j = 0; j < length; j++) {
- child = children[j];
- if (typeof child === 'string')
- template[i] += child;
- else {
- args.push(child);
- i = template.push('') - 1;
- }
- }
- if (
- 0 < length ||
- (component && !empty.test(component.tagName)) ||
- !empty.test(entry)
- ) {
- if (component) {
- template[i] += '';
- args.push(entry);
- template.push('>');
- }
- else
- template[i] += `${entry}>`;
- }
- const whole = template.join(er);
- args[0] = cache.get(whole) || template;
- if (args[0] === template)
- cache.set(whole, template);
- return tag.apply(this, args);
- };
+ template[i] += `${entry}>`;
+ }
+ const whole = template.join(er);
+ args[0] = cache.get(whole) || template;
+ if (args[0] === template)
+ cache.set(whole, template);
+ return tag.apply(this, args);
};
-
-function Bound(_) {
- this._ = _;
-}
diff --git a/test/index.js b/test/index.js
index 04a9724..b9fb4a2 100644
--- a/test/index.js
+++ b/test/index.js
@@ -1,17 +1,29 @@
// your template literal library of choice
const {
render,
- html
+ html,
+ svg
} = require('uhtml-ssr'); // this module
const {
bind,
createPragma
-} = require('../cjs/index.js'); // create your `h` / pragma function
+} = require('../cjs/index.js');
+const assert = (value, expected) => {
+ /* c8 ignore start */
+ if (expected !== render(String, value)) {
+ console.error('got ', render(String, value));
+ console.error('expected', expected);
+ process.exit(1);
+ }
+ /* c8 ignore stop */
+
+}; // create your `h` / pragma function
-const h = createPragma(html); // any component (passed as template value)
+
+let h = createPragma(html); // any component (passed as template value)
const Bold = ({
children
@@ -39,6 +51,7 @@ const test = 123; // test it!
const myDocument = h("p", {
class: "what",
+ nope: null,
test: bind(test),
onClick: console.log
}, h(Bold, null, "Hello"), ", ", h("input", {
@@ -47,15 +60,13 @@ const myDocument = h("p", {
}), h(Span, {
id: "greetings"
}, "Hello"), " ", h(World, null));
-/* c8 ignore start */
-
-const expected = `Hello, Hello
`;
-
-if (expected !== render(String, myDocument)) {
- console.error('got ', render(String, myDocument));
- console.error('expected', expected);
- process.exit(1);
-}
-
-console.log('\x1b[1mOK\x1b[0m');
-/* c8 ignore stop */
\ No newline at end of file
+assert(myDocument, `Hello, Hello
`);
+h = createPragma(svg, {
+ xml: true
+});
+const svgDocument = h("rect", {
+ x: 10,
+ y: "20"
+});
+assert(svgDocument, '');
+console.log('Test: \x1b[1mOK\x1b[0m');
\ No newline at end of file
diff --git a/test/index.jsx b/test/index.jsx
index 8ca9696..47d7134 100644
--- a/test/index.jsx
+++ b/test/index.jsx
@@ -1,11 +1,21 @@
// your template literal library of choice
-const {render, html} = require('uhtml-ssr');
+const {render, html, svg} = require('uhtml-ssr');
// this module
const {bind, createPragma} = require('../cjs/index.js');
+const assert = (value, expected) => {
+ /* c8 ignore start */
+ if (expected !== render(String, value)) {
+ console.error('got ', render(String, value));
+ console.error('expected', expected);
+ process.exit(1);
+ }
+ /* c8 ignore stop */
+};
+
// create your `h` / pragma function
-const h = createPragma(html);
+let h = createPragma(html);
// any component (passed as template value)
const Bold = ({children}) => html`${children}`;
@@ -26,18 +36,28 @@ const test = 123;
// test it!
const myDocument = (
-
+
Hello,
Hello
);
-/* c8 ignore start */
-const expected = `Hello, Hello
`;
-if (expected !== render(String, myDocument)) {
- console.error('got ', render(String, myDocument));
- console.error('expected', expected);
- process.exit(1);
-}
-console.log('\x1b[1mOK\x1b[0m');
-/* c8 ignore stop */
+assert(
+ myDocument,
+ `Hello, Hello
`
+);
+
+h = createPragma(svg, {
+ xml: true
+});
+
+const svgDocument = (
+
+);
+
+assert(
+ svgDocument,
+ ''
+);
+
+console.log('Test: \x1b[1mOK\x1b[0m');