html-minifier 2.1.3 → 2.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@ See [corresponding blog post](http://perfectionkills.com/experimenting-with-html
12
12
 
13
13
  [Test suite is available online](http://kangax.github.io/html-minifier/tests/).
14
14
 
15
- Also see corresponding [Ruby wrapper](https://github.com/stereobooster/html_minifier), and for Node.js, [Grunt plugin](https://github.com/gruntjs/grunt-contrib-htmlmin), [Gulp module](https://github.com/jonschlinkert/gulp-htmlmin), and [Koa middleware wrapper](https://github.com/koajs/html-minifier).
15
+ Also see corresponding [Ruby wrapper](https://github.com/stereobooster/html_minifier), and for Node.js, [Grunt plugin](https://github.com/gruntjs/grunt-contrib-htmlmin), [Gulp module](https://github.com/jonschlinkert/gulp-htmlmin), [Koa middleware wrapper](https://github.com/koajs/html-minifier) and [Express middleware wrapper](https://github.com/melonmanchan/express-minify-html).
16
16
 
17
17
  For lint-like capabilities take a look at [HTMLLint](https://github.com/kangax/html-lint).
18
18
 
@@ -22,19 +22,19 @@ How does HTMLMinifier compare to other solutions — [HTML Minifier from Will Pe
22
22
 
23
23
  | Site | Original size *(KB)* | HTMLMinifier | minimize | Will Peavy | htmlcompressor.com |
24
24
  | --------------------------------------------------------------------------- |:--------------------:| ------------:| --------:| ----------:| ------------------:|
25
- | [Google](https://www.google.com/) | 57 | **54** | 57 | 59 | 57 |
26
- | [CNN](http://www.cnn.com/) | 109 | **99** | 105 | 107 | 103 |
27
- | [HTMLMinifier](https://github.com/kangax/html-minifier) | 117 | **75** | 99 | 104 | 100 |
28
- | [BBC](http://www.bbc.co.uk/) | 154 | **123** | 148 | 152 | 145 |
29
- | [New York Times](http://www.nytimes.com/) | 170 | **106** | 121 | 125 | 121 |
30
- | [Stack Overflow](http://stackoverflow.com/) | 240 | **185** | 195 | 204 | 194 |
25
+ | [Google](https://www.google.com/) | 48 | **46** | 48 | 50 | 48 |
26
+ | [CNN](http://www.cnn.com/) | 115 | **105** | 111 | 113 | 109 |
27
+ | [HTMLMinifier](https://github.com/kangax/html-minifier) | 117 | **79** | 99 | 103 | 99 |
28
+ | [BBC](http://www.bbc.co.uk/) | 179 | **147** | 172 | 177 | 168 |
29
+ | [New York Times](http://www.nytimes.com/) | 186 | **127** | 136 | 140 | 134 |
30
+ | [Stack Overflow](http://stackoverflow.com/) | 240 | **186** | 195 | 204 | 193 |
31
31
  | [Bootstrap CSS](http://getbootstrap.com/css/) | 277 | **264** | 274 | 232 | 274 |
32
- | [Amazon](http://www.amazon.co.uk/) | 444 | **388** | 424 | 437 | n/a |
33
- | [NBC](http://www.nbc.com/) | 465 | **446** | 464 | 466 | n/a |
34
- | [Wikipedia](https://en.wikipedia.org/wiki/President_of_the_United_States) | 476 | **422** | 459 | 474 | n/a |
32
+ | [Amazon](http://www.amazon.co.uk/) | 431 | **377** | 412 | 424 | n/a |
33
+ | [Wikipedia](https://en.wikipedia.org/wiki/President_of_the_United_States) | 481 | **437** | 464 | 479 | n/a |
34
+ | [NBC](http://www.nbc.com/) | 508 | **486** | 506 | 508 | n/a |
35
35
  | [Eloquent Javascript](http://eloquentjavascript.net/1st_edition/print.html) | 870 | **815** | 840 | 864 | n/a |
36
- | [ES6 table](http://kangax.github.io/compat-table/es6/) | 3801 | **3194** | 3579 | 3756 | n/a |
37
- | [ES6 draft](https://tc39.github.io/ecma262/) | 4777 | **4225** | 4355 | 4477 | n/a |
36
+ | [ES6 table](http://kangax.github.io/compat-table/es6/) | 3884 | **3272** | 3664 | 3839 | n/a |
37
+ | [ES6 draft](https://tc39.github.io/ecma262/) | 4797 | **4247** | 4377 | 4500 | n/a |
38
38
 
39
39
  ## Options Quick Reference
40
40
 
@@ -56,7 +56,7 @@ How does HTMLMinifier compare to other solutions — [HTML Minifier from Will Pe
56
56
  | `includeAutoGeneratedTags` | Insert tags generated by HTML parser | `true` |
57
57
  | `keepClosingSlash` | Keep the trailing slash on singleton elements | `false` |
58
58
  | `maxLineLength` | Specify a maximum line length. Compressed output will be split by newlines at valid HTML split-points |
59
- | `minifyCSS` | Minify CSS in style elements and style attributes (uses [clean-css](https://github.com/jakubpawlowicz/clean-css)) | `false` (could be `true`, `Object`, `Function(text, inline)`) |
59
+ | `minifyCSS` | Minify CSS in style elements and style attributes (uses [clean-css](https://github.com/jakubpawlowicz/clean-css)) | `false` (could be `true`, `Object`, `Function(text)`) |
60
60
  | `minifyJS` | Minify JavaScript in script elements and event attributes (uses [UglifyJS](https://github.com/mishoo/UglifyJS2)) | `false` (could be `true`, `Object`, `Function(text, inline)`) |
61
61
  | `minifyURLs` | Minify URLs in various attributes (uses [relateurl](https://github.com/stevenvachon/relateurl)) | `false` (could be `String`, `Object`, `Function(text)`) |
62
62
  | `preserveLineBreaks` | Always collapse to 1 line break (never remove it entirely) when whitespace between tags include a line break. Must be used in conjunction with `collapseWhitespace=true` | `false` |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "html-minifier",
3
3
  "description": "Highly configurable, well-tested, JavaScript-based HTML minifier.",
4
- "version": "2.1.3",
4
+ "version": "2.1.7",
5
5
  "keywords": [
6
6
  "cli",
7
7
  "compress",
@@ -54,7 +54,7 @@
54
54
  "test": "grunt test"
55
55
  },
56
56
  "dependencies": {
57
- "change-case": "2.3.x",
57
+ "change-case": "3.0.x",
58
58
  "clean-css": "3.4.x",
59
59
  "commander": "2.9.x",
60
60
  "he": "1.1.x",
@@ -66,16 +66,16 @@
66
66
  "grunt": "1.0.x",
67
67
  "grunt-browserify": "5.0.x",
68
68
  "grunt-contrib-uglify": "1.0.x",
69
- "grunt-eslint": "18.1.x",
69
+ "grunt-eslint": "19.0.x",
70
70
  "phantomjs-prebuilt": "2.1.x",
71
- "qunitjs": "1.x"
71
+ "qunitjs": "2.x"
72
72
  },
73
73
  "benchmarkDependencies": {
74
74
  "brotli": "1.2.x",
75
75
  "chalk": "1.1.x",
76
76
  "cli-table": "0.3.x",
77
77
  "lzma": "2.3.x",
78
- "minimize": "1.8.x",
78
+ "minimize": "2.0.x",
79
79
  "progress": "1.1.x"
80
80
  },
81
81
  "files": [
@@ -63,7 +63,7 @@ var inlineTags = createMapFromString('a,abbr,acronym,b,bdi,bdo,big,button,cite,c
63
63
  // non-empty tags that will maintain whitespace within them
64
64
  var inlineTextTags = createMapFromString('a,abbr,acronym,b,big,del,em,font,i,ins,kbd,mark,nobr,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var');
65
65
  // self-closing tags that will maintain whitespace around them
66
- var selfClosingInlineTags = createMapFromString('comment,img,input');
66
+ var selfClosingInlineTags = createMapFromString('comment,img,input,wbr');
67
67
 
68
68
  function collapseWhitespaceSmart(str, prevTag, nextTag, options) {
69
69
  var trimLeft = prevTag && !selfClosingInlineTags(prevTag);
@@ -228,17 +228,21 @@ function isNumberTypeAttribute(attrName, tag) {
228
228
  );
229
229
  }
230
230
 
231
- function isCanonicalURL(tag, attrs) {
231
+ function isLinkType(tag, attrs, value) {
232
232
  if (tag !== 'link') {
233
233
  return false;
234
234
  }
235
235
  for (var i = 0, len = attrs.length; i < len; i++) {
236
- if (attrs[i].name === 'rel' && attrs[i].value === 'canonical') {
236
+ if (attrs[i].name === 'rel' && attrs[i].value === value) {
237
237
  return true;
238
238
  }
239
239
  }
240
240
  }
241
241
 
242
+ function isMediaQuery(tag, attrs, attrName) {
243
+ return attrName === 'media' && (isLinkType(tag, attrs, 'stylesheet') || isStyleSheet(tag, attrs));
244
+ }
245
+
242
246
  var srcsetTags = createMapFromString('img,source');
243
247
 
244
248
  function isSrcset(attrName, tag) {
@@ -262,17 +266,20 @@ function cleanAttributeValue(tag, attrName, attrValue, options, attrs) {
262
266
  }
263
267
  else if (isUriTypeAttribute(attrName, tag)) {
264
268
  attrValue = trimWhitespace(attrValue);
265
- return isCanonicalURL(tag, attrs) ? attrValue : options.minifyURLs(attrValue);
269
+ return isLinkType(tag, attrs, 'canonical') ? attrValue : options.minifyURLs(attrValue);
266
270
  }
267
271
  else if (isNumberTypeAttribute(attrName, tag)) {
268
272
  return trimWhitespace(attrValue);
269
273
  }
270
274
  else if (attrName === 'style') {
271
275
  attrValue = trimWhitespace(attrValue);
272
- if (attrValue && /;$/.test(attrValue) && !/&#?[0-9a-zA-Z]+;$/.test(attrValue)) {
273
- attrValue = attrValue.replace(/\s*;$/, '');
276
+ if (attrValue) {
277
+ if (/;$/.test(attrValue) && !/&#?[0-9a-zA-Z]+;$/.test(attrValue)) {
278
+ attrValue = attrValue.replace(/\s*;$/, '');
279
+ }
280
+ attrValue = unwrapInlineCSS(options.minifyCSS(wrapInlineCSS(attrValue)));
274
281
  }
275
- return options.minifyCSS(attrValue, true);
282
+ return attrValue;
276
283
  }
277
284
  else if (isSrcset(attrName, tag)) {
278
285
  // https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-srcset
@@ -305,6 +312,10 @@ function cleanAttributeValue(tag, attrName, attrValue, options, attrs) {
305
312
  else if (tag === 'script' && attrName === 'type') {
306
313
  attrValue = trimWhitespace(attrValue.replace(/\s*;\s*/g, ';'));
307
314
  }
315
+ else if (isMediaQuery(tag, attrs, attrName)) {
316
+ attrValue = trimWhitespace(attrValue);
317
+ return unwrapMediaQuery(options.minifyCSS(wrapMediaQuery(attrValue)));
318
+ }
308
319
  return attrValue;
309
320
  }
310
321
 
@@ -321,16 +332,22 @@ function isMetaViewport(tag, attrs) {
321
332
 
322
333
  // Wrap CSS declarations for CleanCSS > 3.x
323
334
  // See https://github.com/jakubpawlowicz/clean-css/issues/418
324
- function wrapCSS(text) {
335
+ function wrapInlineCSS(text) {
325
336
  return '*{' + text + '}';
326
337
  }
327
338
 
328
- function unwrapCSS(text) {
329
- var matches = text.match(/^\*\{([\s\S]*)\}$/m);
330
- if (matches && matches[1]) {
331
- return matches[1];
332
- }
333
- return text;
339
+ function unwrapInlineCSS(text) {
340
+ var matches = text.match(/^\*\{([\s\S]*)\}$/);
341
+ return matches ? matches[1] : text;
342
+ }
343
+
344
+ function wrapMediaQuery(text) {
345
+ return '@media ' + text + '{a{top:0}}';
346
+ }
347
+
348
+ function unwrapMediaQuery(text) {
349
+ var matches = text.match(/^@media ([\s\S]*?)\s*{[\s\S]*}$/);
350
+ return matches ? matches[1] : text;
334
351
  }
335
352
 
336
353
  function cleanConditionalComment(comment, options) {
@@ -685,18 +702,14 @@ function processOptions(options) {
685
702
  if (typeof minifyCSS.advanced === 'undefined') {
686
703
  minifyCSS.advanced = false;
687
704
  }
688
- options.minifyCSS = function(text, inline) {
705
+ options.minifyCSS = function(text) {
689
706
  text = text.replace(/(url\s*\(\s*)("|'|)(.*?)\2(\s*\))/ig, function(match, prefix, quote, url, suffix) {
690
707
  return prefix + quote + options.minifyURLs(url) + quote + suffix;
691
708
  });
692
709
  var start = text.match(/^\s*<!--/);
693
710
  var style = start ? text.slice(start[0].length).replace(/-->\s*$/, '') : text;
694
711
  try {
695
- var cleanCSS = new CleanCSS(minifyCSS);
696
- if (inline) {
697
- return unwrapCSS(cleanCSS.minify(wrapCSS(style)).styles);
698
- }
699
- return cleanCSS.minify(style).styles;
712
+ return new CleanCSS(minifyCSS).minify(style).styles;
700
713
  }
701
714
  catch (err) {
702
715
  options.log(err);
@@ -709,7 +722,7 @@ function processOptions(options) {
709
722
  function uniqueId(value) {
710
723
  var id;
711
724
  do {
712
- id = Math.random().toString(36).slice(2);
725
+ id = Math.random().toString(36).replace(/^0\.[0-9]*/, '');
713
726
  } while (~value.indexOf(id));
714
727
  return id;
715
728
  }
@@ -822,7 +835,8 @@ function minify(value, options, partialMarkup) {
822
835
  ignoredMarkupChunks = [],
823
836
  ignoredCustomMarkupChunks = [],
824
837
  uidIgnore,
825
- uidAttr;
838
+ uidAttr,
839
+ uidPattern;
826
840
 
827
841
  // temporarily replace ignored chunks with comments,
828
842
  // so that we don't have to worry what's there.
@@ -846,6 +860,24 @@ function minify(value, options, partialMarkup) {
846
860
  value = value.replace(reCustomIgnore, function(match) {
847
861
  if (!uidAttr) {
848
862
  uidAttr = uniqueId(value);
863
+ uidPattern = new RegExp('(\\s*)' + uidAttr + '([0-9]+)(\\s*)', 'g');
864
+ var minifyCSS = options.minifyCSS;
865
+ if (minifyCSS) {
866
+ options.minifyCSS = function(text) {
867
+ return minifyCSS(text).replace(uidPattern, function(match, prefix, index, suffix) {
868
+ return (prefix && '\t') + uidAttr + index + (suffix && '\t');
869
+ });
870
+ };
871
+ }
872
+ var minifyJS = options.minifyJS;
873
+ if (minifyJS) {
874
+ var pattern = new RegExp('(\\\\t|)' + uidAttr + '([0-9]+)(\\\\t|)', 'g');
875
+ options.minifyJS = function(text, inline) {
876
+ return minifyJS(text, inline).replace(pattern, function(match, prefix, index, suffix) {
877
+ return (prefix && '\t') + uidAttr + index + (suffix && '\t');
878
+ });
879
+ };
880
+ }
849
881
  }
850
882
  var token = uidAttr + ignoredCustomMarkupChunks.length;
851
883
  ignoredCustomMarkupChunks.push(match);
@@ -1097,9 +1129,9 @@ function minify(value, options, partialMarkup) {
1097
1129
  }
1098
1130
  }
1099
1131
  if (prevTag) {
1100
- if (prevTag === '/nobr') {
1132
+ if (prevTag === '/nobr' || prevTag === 'wbr') {
1101
1133
  if (/^\s/.test(text)) {
1102
- trimTrailingWhitespace(buffer.length - 1, 'br');
1134
+ trimTrailingWhitespace(buffer.length - 2, 'br');
1103
1135
  }
1104
1136
  }
1105
1137
  else if (inlineTextTags(prevTag.charAt(0) === '/' ? prevTag.slice(1) : prevTag)) {
@@ -1203,8 +1235,8 @@ function minify(value, options, partialMarkup) {
1203
1235
 
1204
1236
  var str = joinResultSegments(buffer, options);
1205
1237
 
1206
- if (uidAttr) {
1207
- str = str.replace(new RegExp('(\\s*)' + uidAttr + '([0-9]+)(\\s*)', 'g'), function(match, prefix, index, suffix) {
1238
+ if (uidPattern) {
1239
+ str = str.replace(uidPattern, function(match, prefix, index, suffix) {
1208
1240
  var chunk = ignoredCustomMarkupChunks[+index];
1209
1241
  if (options.collapseWhitespace) {
1210
1242
  if (prefix !== '\t') {