WebReflection / uhtml

A micro HTML/SVG render

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Potential tweaks to allow smaller terser output

coreyfarrell opened this issue · comments

I've been using uhtml in my own projects, I noticed a couple small tweaks that could be made to allow better size reduction by terser. This isn't so much a bug report but just seeking feedback before I propose any PR's / investigate further.

Use arrow functions everywhere.

This is tiny, in my own build switching the two functions reduces final bundle output by 23 bytes. This is because terser can take all arrow functions and output var f1=()=>{},f2=()=>{}; where regular functions it has to output function f1(){}function f2(){}. Since arrow functions are already used elsewhere I think this should be a non-issue for compatibility.

Make it easier to detect const IE = importNode.length != 1; in esm/node.js.

For builds targeting modern browsers a babel plugin changing this to const IE = false; reduces the bundle by 96 bytes. This is because it allows terser to perform constant evaluation and eliminate IE == true branches. This isn't strictly required as I could have babel detect the IE variable declaration in uhtml/esm/node.js and replace the initialization value.

One question I have is ESM even supported on any platform where document.importNode.length != 1?

Consider having @ungap/create-content export createSVG and createHTML as separate functions.

This would make it possible for uhtml to use these two functions directly from the html and svg functions, allow tree shaking to eliminate the svg code if it is unused in a project. The idea is that uhtml/esm/index.js would have:

const html = tag(createHTML);
const svg = tag(createSVG);

I haven't fully evaluated the changes that would be needed to have uhtml use these two functions directly instead of having the string type argument and I also haven't looked to see how much this would actually reduce the size. Also not sure if this would be considered a breaking change for the Hole object to no longer have type: 'html'.

is ESM even supported on any platform where document.importNode.length != 1

legacy Edge has the same issue but it supports ES2015

This isn't so much a bug report but just seeking feedback before I propose any PR's / investigate further.

how many modules do I need to change, beside uhtml, and for which gain? 200 bytes? I wouldn't bother.

P.S. the new.js file is already ES2015 only https://github.com/WebReflection/uhtml/blob/master/new.js and it's 5.78 KB vs 6.91 KB

P.S.2 the createContent change sounds reasonable, but it would break @ungap so ... it's a major ... but again, for few bytes, I'm not sure the breaking change is worth it.

I've looked this over further and I think renaming the IE variable in esm/node.js is the only thing I hope to see merged into uhtml. I have the following in my babel config using babel-replace-variable-init:

plugins: [
	babelReplaceVariableInit({
		IE: ({types: t}) => t.booleanLiteral(false)
	})
]

Unfortunately if anything else declares an IE variable my babel plugin would reset that as well, so renaming the variable would make my build safer without needing to restrict the plugin to the specific file. My suggestion would be const importNodesLegacyArgs = importNodes.length != 1. If you don't object to this I could post a PR.

P.S. the new.js file is already ES2015 only https://github.com/WebReflection/uhtml/blob/master/new.js and it's 5.78 KB vs 6.91 KB

I'm not able to use this in my build process, I need to import from ES modules - import {html, render} from 'uthml'.

P.S.2 the createContent change sounds reasonable, but it would break @ungap so ... it's a major ... but again, for few bytes, I'm not sure the breaking change is worth it.

This could be non-breaking if the existing createContent remained as the default export and createHTML / createSVG became named exports but the changes inside uhtml could be complicated. Instead I'm just using rollup-plugin-includepaths to provide a stripped down copy of createContent which supports HTML only. This eliminates a few hundred bytes from my build without needing difficult or potentially breaking changes in uhtml.

I'm not able to use this in my build process, I need to import from ES modules - import {html, render} from 'uthml'.

my point is that I already drop unnecessary bloat https://github.com/WebReflection/uhtml/blob/master/rollup/new.config.js
maybe you could do the same?

This could be non-breaking if the existing createContent remained as the default export and createHTML / createSVG became named exports but the changes inside uhtml could be complicated.

it's not complicated, it's just that default and named exports usually play very badly with bundlers out there.

so ... IE has been renamed as isImportNodeLengthWrong which plays well with the ternary I'm using to define behaiors ... regarding createContent though, I don't think splitting the module would really allow tree shaking in uhtml, because I still need to check if the type is html instead of svg, and that's a runtime check I'm not sure bundlers can tree-shake.

Anyway, fixing/improving one thing at the time, so I hope this patch update works for you.

P.S. the same trick you do with IE variable, now renamed, could be applied to createSVG, as ()=>{} no-op. Tree shake, if you are sure you'll never use/need svg in there, would drop the surrounding code related to createSVG and all you have is a method that does nothing.

Renaming the variable is great, I'm just going to use a stripped @ungap/create-content in my build which will accomplish the other gains. Thanks!