How avoid UglifyJS adding extra semicolons after Handlebars placeholders ignored by via html-minifier

Viewed 53

I am using Html-Minifier-Terser to pre-minify some Handlebars (HBS) templates in an express JS app via Gulp. Html-Minifier uses Terser (was UglifyJS) for the minifyJS option.

To prevent Html-Minifier from breaking on the HBS blocks, I have used the recommended approach of ignoring custom fragments, which seems to work fine.

ignoreCustomFragments: [ /{{[\s\S]*?}}/ ]

However when turning on JS minifying too, I have found that HBS conditional statements within script tags which are not inline (in one line), end up with spurious commas or semicolons after each ignored fragment line (or at least the opening tag).

This leads to potential , , in the final JS (after HBS renders it) which is a JS error i the browser, breaking the page.

I think I can avoid the problem by removing all the line breaks from these conditionals in the original view-templates but a) I don't want to because they clarify the code, b) it's a lot of work, c) it's not fixing the root problem, so I'll probably trip up in the future or other areas. I also know that dynamically rendering JS like this probably isn't ideal or modern and it should all be encapsulated in a library somewhere thus avoiding the whole issue, but that's another issue.

With the simplified case like this in my source view-template:

<script>
    {{#if foo}}
        console.log('foo');
    {{/if}}
    {{#if bar}}
        console.log('bar');
    {{/if}}
    {{#if three}}
        console.log('three');
    {{/if}}

and a standard Gulp task like:

const htmlmin = require('gulp-html-minifier-terser');

...pipe(htmlmin({
    collapseWhitespace: true,   
    conservativeCollapse: true, 
    minifyJS: true,
    removeComments: true,       
    ignoreCustomFragments: [ /{{[\s\S]*?}}/ ]
})

leads to a precompiled minified HBS template like this:

{{#if foo}} ,console.log("foo"), {{/if}} ,{{#if bar}} ,console.log("bar"), {{/if}} ,{{#if three}} ,console.log("three"), {{/if}} ,

which (if all the conditions happen to be false) is rendered in the browser as <script>, , , leading to a Javascript error.

I've tried:

Turning off sequences minifyJS: {compress:{sequences:false}}, results in semicolons being after each HBS block, which kind of solves the final JS error, but doesn't fix the root cause. This shows that the problem is earlier in the chain.

{{#if foo}} ;console.log("foo"); {{/if}} ;{{#if bar}} ;console.log("bar"); {{/if}} ;{{#if three}} ;console.log("three"); {{/if}} ;

This is rendered as `; ; ; ' which is ignored by the browser, but still not correct.

I also tried turning off compression and mangling entirely, {compress: false, mangle:false} and output:beautify.

There is also an output:{semicolons:false} option which sounds like the obvious answer, but turning this off actually removes semicolons! resulting in this:

{{#if foo}} console.log("foo") {{/if}} {{#if bar}} console.log("bar") {{/if}} {{#if three}} console.log("three") {{/if}} 

which would render (if flags were true) to this also invalid JS:

console.log("foo")   console.log("bar")  console.log("three") 

I am guessing it's because the JS minifier is seeing the {{...}} blocks which Html-Minifier has already ignored, and presumes they are some kind of JS scope block, which it adds a ; to in anticipation of removing the newlines.

So I was left thinking i also need to tell Uglifier to ignore the same blocks. Or some inbetween options to say "don't change the semicolons at all".

0 Answers
Related