Skip to content

Commit f4be263

Browse files
committed
optimizations!
1 parent e0d66eb commit f4be263

3 files changed

Lines changed: 43 additions & 37 deletions

File tree

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@ It lets your build apps using Preact/React/etc directly in the browser.
1111
JSX can be converted to `htm` with only a few tiny modifications.
1212
Templates are parsed by the browser's HTML parser and cached, achieving minimal overhead.
1313

14-
`htm` is just _650 bytes_ standalone, or **only 500 bytes** when used with Preact! _(through the magic of gzip 🌈)_
14+
## `htm` by the numbers:
15+
16+
🐣 **700 bytes** when used directly in the browser
17+
18+
⚛️ **500 bytes** when used with Preact _(the magic of gzip 🌈)_
19+
20+
🏅 **0 bytes** when compiled using [babel-plugin-htm]
21+
1522

1623
## Syntax: Like JSX but more lit
1724

@@ -32,6 +39,7 @@ Here's some ergonomic features you get for free that aren't present in JSX:
3239
- Optional end-tags: `<section><h1>this is the whole template!`
3340
- Component end-tags: `<${Footer}>footer content<//>`
3441
- Support for HTML comments: `<div><!-- don't delete this! --></div>`
42+
- Syntax highlighting and language support via the [lit-html VSCode extension].
3543

3644
## Project Status
3745

@@ -192,5 +200,7 @@ console.log(html`
192200

193201
[Tagged Templates]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates
194202
[lit-html]: https://github.com/Polymer/lit-html
203+
[babel-plugin-htm]: https://www.npmjs.com/package/babel-plugin-htm
204+
[lit-html VSCode extension]: https://marketplace.visualstudio.com/items?itemName=bierner.lit-html
195205
[vhtml]: https://github.com/developit/vhtml
196206
[jsxobj]: https://github.com/developit/jsxobj

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"eslintConfig": {
2222
"extends": "developit",
2323
"rules": {
24-
"prefer-const": 2
24+
"prefer-const": 0
2525
}
2626
},
2727
"jest": {

src/index.mjs

Lines changed: 31 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ const CACHE = {};
1616
const TEMPLATE = document.createElement('template');
1717

1818
const reg = /(\$_h\[\d+\])/g;
19-
const nomExp = /([\w-]+)=/g;
2019

2120
export default function html(statics) {
2221
const tpl = CACHE[statics] || (CACHE[statics] = build(statics));
@@ -26,73 +25,70 @@ export default function html(statics) {
2625

2726
/** Create a template function given strings from a tagged template. */
2827
function build(statics) {
29-
const noms = {};
30-
let str = '', i = 0;
28+
let str = statics[0], i = 1;
3129
while (i < statics.length) {
32-
getNoms(statics[i], noms);
33-
str += statics[i++] + (i < statics.length ? '$_h[' + i + ']' : '');
30+
str += '$_h[' + i + ']' + statics[i++];
3431
}
3532
// Template string preprocessing:
3633
// - replace <${Foo}> with <c c@=${Foo}>
3734
// - replace <x /> with <x></x>
3835
// - replace <${Foo}>a<//>b with <c c@=${Foo}>a</c>b
39-
TEMPLATE.innerHTML = str.replace(/<(?:(\/)\/|(\/?)(\$_h\[\d+\]))/g, '<$1$2c c@=$3').replace(/<([\w:-]+)(\s[^<>]*?)?\/>/gi, '<$1$2></$1>').trim();
40-
return Function('h', '$_h', 'return ' + walk((TEMPLATE.content || TEMPLATE).firstChild, noms));
36+
TEMPLATE.innerHTML = str
37+
.replace(/<(?:(\/)\/|(\/?)(\$_h\[\d+\]))/g, '<$1$2c c@=$3')
38+
// .replace(/<([\w:-]+)((?:\s[^<>]*?)?)(\/?)>/gi, (str, name, attrs, a) => {
39+
// const casedAttrs = attrs.replace(/(?:'.*?'|".*?"|([A-Z]))/g, (s, c) => c ? ':::'+c : s);
40+
// return '<' + name + casedAttrs + '>' + (a ? '</'+name+'>' : '');
41+
// })
42+
.replace(/<([\w:-]+)(?:\s[^<>]*?)?(\/?)>/g, (str, name, a) => (
43+
str.replace(/(?:'.*?'|".*?"|([A-Z]))/g, (s, c) => c ? ':::'+c : s) + (a ? '</'+name+'>' : '')
44+
))
45+
.trim();
46+
return Function('h', '$_h', 'return ' + walk((TEMPLATE.content || TEMPLATE).firstChild));
4147
}
4248

4349
/** Traverse a DOM tree and serialize it to hyperscript function calls */
44-
function walk(n, noms) {
45-
if (n.nodeType !== 1) {
46-
if (n.nodeType === 3 && n.data) return field(n.data, ',');
50+
function walk(n) {
51+
if (n.nodeType != 1) {
52+
if (n.nodeType == 3 && n.data) return field(n.data, ',');
4753
return 'null';
4854
}
49-
let nodeName = `"${n.localName}"`, str = '', pre, it, prevSpread, anySpread;
55+
let str = '',
56+
nodeName = field(n.localName, str),
57+
sub = '',
58+
start = ',({';
5059
for (let i=0; i<n.attributes.length; i++) {
51-
let { name } = n.attributes[i];
52-
const { value } = n.attributes[i];
53-
name = noms[name] || name;
60+
const name = n.attributes[i].name;
61+
const value = n.attributes[i].value;
5462
if (name=='c@') {
5563
nodeName = value;
56-
continue;
5764
}
58-
if (name.substring(0,3)==='...') {
59-
it = name.substring(3);
60-
pre = str ? (prevSpread ? ',' : '},') : '';
61-
prevSpread = anySpread = true;
65+
else if (name.substring(0,3)=='...') {
66+
sub = '';
67+
start = ',Object.assign({';
68+
str += '},' + name.substring(3) + ',{';
6269
}
6370
else {
64-
it = `"${name.replace(/:(\w)/g, upper)}":${value ? field(value, '+') : true}`;
65-
pre = str ? (prevSpread ? ',{' : ',') : '{';
66-
prevSpread = false;
71+
str += `${sub}"${name.replace(/:::(\w)/g, (s, i) => i.toUpperCase())}":${value ? field(value, '+') : true}`;
72+
sub = ',';
6773
}
68-
str += pre + it;
6974
}
70-
str = 'h(' + nodeName + ',' + (str ? (anySpread ? 'Object.assign({},' + str + (prevSpread ? '' : '}') + ')' : str + '}') : '{}');
75+
str = 'h(' + nodeName + start + str + '})';
7176
let child = n.firstChild;
7277
while (child) {
73-
str += ',' + walk(child, noms);
78+
str += ',' + walk(child);
7479
child = child.nextSibling;
7580
}
7681
return str + ')';
7782
}
7883

79-
function getNoms(str, noms) {
80-
let nom;
81-
while ((nom = nomExp.exec(str)) !== null) noms[nom[1].toLowerCase()] = nom[1];
82-
}
83-
84-
function upper (s, i) {
85-
return i.toUpperCase();
86-
}
87-
8884
/** Serialize a field to a String or reference for use in generated code. */
8985
function field(value, sep) {
9086
const matches = value.match(reg);
9187
let strValue = JSON.stringify(value);
9288
if (matches != null) {
9389
if (matches[0] === value) return value;
9490
strValue = strValue.replace(reg, `"${sep}$1${sep}"`).replace(/"[+,]"/g, '');
95-
if (sep === ',') strValue = `[${strValue}]`;
91+
if (sep == ',') strValue = `[${strValue}]`;
9692
}
9793
return strValue;
9894
}

0 commit comments

Comments
 (0)