html-minifier 3.5.18 → 4.0.0
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 +18 -15
- package/cli.js +3 -2
- package/package.json +23 -23
- package/sample-cli-config-file.conf +1 -0
- package/src/htmlminifier.js +95 -66
- package/src/htmlparser.js +7 -3
package/README.md
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
[](https://travis-ci.org/kangax/html-minifier)
|
|
5
5
|
[](https://david-dm.org/kangax/html-minifier)
|
|
6
6
|
|
|
7
|
-
[HTMLMinifier](
|
|
7
|
+
[HTMLMinifier](https://kangax.github.io/html-minifier/) is a highly **configurable**, **well-tested**, JavaScript-based HTML minifier.
|
|
8
8
|
|
|
9
9
|
See [corresponding blog post](http://perfectionkills.com/experimenting-with-html-minifier/) for all the gory details of [how it works](http://perfectionkills.com/experimenting-with-html-minifier/#how_it_works), [description of each option](http://perfectionkills.com/experimenting-with-html-minifier/#options), [testing results](http://perfectionkills.com/experimenting-with-html-minifier/#field_testing) and [conclusions](http://perfectionkills.com/experimenting-with-html-minifier/#cost_and_benefits).
|
|
10
10
|
|
|
11
|
-
[Test suite is available online](
|
|
11
|
+
[Test suite is available online](https://kangax.github.io/html-minifier/tests/).
|
|
12
12
|
|
|
13
13
|
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).
|
|
14
14
|
|
|
@@ -20,19 +20,19 @@ How does HTMLMinifier compare to other solutions — [HTML Minifier from Will Pe
|
|
|
20
20
|
|
|
21
21
|
| Site | Original size *(KB)* | HTMLMinifier | minimize | Will Peavy | htmlcompressor.com |
|
|
22
22
|
| ---------------------------------------------------------------------------- |:--------------------:| ------------:| --------:| ----------:| ------------------:|
|
|
23
|
-
| [Google](https://www.google.com/) |
|
|
24
|
-
| [
|
|
25
|
-
| [
|
|
26
|
-
| [
|
|
27
|
-
| [
|
|
28
|
-
| [
|
|
29
|
-
| [
|
|
30
|
-
| [
|
|
31
|
-
| [Wikipedia](https://en.wikipedia.org/wiki/President_of_the_United_States) |
|
|
32
|
-
| [
|
|
23
|
+
| [Google](https://www.google.com/) | 46 | **42** | 46 | 48 | 46 |
|
|
24
|
+
| [HTMLMinifier](https://github.com/kangax/html-minifier) | 125 | **98** | 111 | 117 | 111 |
|
|
25
|
+
| [Twitter](https://twitter.com/) | 207 | **165** | 200 | 224 | 200 |
|
|
26
|
+
| [Stack Overflow](https://stackoverflow.com/) | 253 | **195** | 207 | 215 | 204 |
|
|
27
|
+
| [Bootstrap CSS](https://getbootstrap.com/docs/3.3/css/) | 271 | **260** | 269 | 228 | 269 |
|
|
28
|
+
| [BBC](https://www.bbc.co.uk/) | 298 | **239** | 290 | 291 | 280 |
|
|
29
|
+
| [Amazon](https://www.amazon.co.uk/) | 422 | **316** | 412 | 425 | n/a |
|
|
30
|
+
| [NBC](https://www.nbc.com/) | 553 | **530** | 552 | 553 | 534 |
|
|
31
|
+
| [Wikipedia](https://en.wikipedia.org/wiki/President_of_the_United_States) | 565 | **461** | 548 | 569 | 548 |
|
|
32
|
+
| [New York Times](https://www.nytimes.com/) | 678 | **606** | 675 | 670 | n/a |
|
|
33
33
|
| [Eloquent Javascript](https://eloquentjavascript.net/1st_edition/print.html) | 870 | **815** | 840 | 864 | n/a |
|
|
34
|
-
| [ES6 table](https://kangax.github.io/compat-table/es6/) |
|
|
35
|
-
| [
|
|
34
|
+
| [ES6 table](https://kangax.github.io/compat-table/es6/) | 5911 | **5051** | 5595 | n/a | n/a |
|
|
35
|
+
| [ES draft](https://tc39.github.io/ecma262/) | 6126 | **5495** | 5664 | n/a | n/a |
|
|
36
36
|
|
|
37
37
|
## Options Quick Reference
|
|
38
38
|
|
|
@@ -45,6 +45,7 @@ Most of the options are disabled by default.
|
|
|
45
45
|
| `collapseInlineTagWhitespace` | Don't leave any spaces between `display:inline;` elements when collapsing. Must be used in conjunction with `collapseWhitespace=true` | `false` |
|
|
46
46
|
| `collapseWhitespace` | [Collapse white space that contributes to text nodes in a document tree](http://perfectionkills.com/experimenting-with-html-minifier/#collapse_whitespace) | `false` |
|
|
47
47
|
| `conservativeCollapse` | Always collapse to 1 space (never remove it entirely). Must be used in conjunction with `collapseWhitespace=true` | `false` |
|
|
48
|
+
| `continueOnParseError` | [Handle parse errors](https://html.spec.whatwg.org/multipage/parsing.html#parse-errors) instead of aborting. | `false` |
|
|
48
49
|
| `customAttrAssign` | Arrays of regex'es that allow to support custom attribute assign expressions (e.g. `'<div flex?="{{mode != cover}}"></div>'`) | `[ ]` |
|
|
49
50
|
| `customAttrCollapse` | Regex that specifies custom attribute to strip newlines from (e.g. `/ng-class/`) | |
|
|
50
51
|
| `customAttrSurround` | Arrays of regex'es that allow to support custom attribute surround expressions (e.g. `<input {{#if value}}checked="checked"{{/if}}>`) | `[ ]` |
|
|
@@ -136,7 +137,9 @@ npm link .
|
|
|
136
137
|
|
|
137
138
|
## Usage
|
|
138
139
|
|
|
139
|
-
For command line usage please see `html-minifier --help`
|
|
140
|
+
Note that almost all options are disabled by default. For command line usage please see `html-minifier --help` for a list of available options. Experiment and find what works best for you and your project.
|
|
141
|
+
|
|
142
|
+
* **Sample command line:** ``html-minifier --collapse-whitespace --remove-comments --remove-optional-tags --remove-redundant-attributes --remove-script-type-attributes --remove-tag-whitespace --use-short-doctype --minify-css true --minify-js true``
|
|
140
143
|
|
|
141
144
|
### Node.js
|
|
142
145
|
|
package/cli.js
CHANGED
|
@@ -103,6 +103,7 @@ var mainOptions = {
|
|
|
103
103
|
collapseInlineTagWhitespace: 'Collapse white space around inline tag',
|
|
104
104
|
collapseWhitespace: 'Collapse white space that contributes to text nodes in a document tree.',
|
|
105
105
|
conservativeCollapse: 'Always collapse to 1 space (never remove it entirely)',
|
|
106
|
+
continueOnParseError: 'Handle parse errors instead of aborting',
|
|
106
107
|
customAttrAssign: ['Arrays of regex\'es that allow to support custom attribute assign expressions (e.g. \'<div flex?="{{mode != cover}}"></div>\')', parseJSONRegExpArray],
|
|
107
108
|
customAttrCollapse: ['Regex that specifies custom attribute to strip newlines from (e.g. /ng-class/)', parseRegExp],
|
|
108
109
|
customAttrSurround: ['Arrays of regex\'es that allow to support custom attribute surround expressions (e.g. <input {{#if value}}checked="checked"{{/if}}>)', parseJSONRegExpArray],
|
|
@@ -197,7 +198,7 @@ program.arguments('[files...]').action(function(files) {
|
|
|
197
198
|
function createOptions() {
|
|
198
199
|
var options = {};
|
|
199
200
|
mainOptionKeys.forEach(function(key) {
|
|
200
|
-
var param = program[camelCase(key)];
|
|
201
|
+
var param = program[key === 'minifyURLs' ? 'minifyUrls' : camelCase(key)];
|
|
201
202
|
if (typeof param !== 'undefined') {
|
|
202
203
|
options[key] = param;
|
|
203
204
|
}
|
|
@@ -297,7 +298,7 @@ if (inputDir || outputDir) {
|
|
|
297
298
|
processDirectory(inputDir, outputDir, fileExt);
|
|
298
299
|
}
|
|
299
300
|
// Minifying one or more files specified on the CMD line
|
|
300
|
-
else if (
|
|
301
|
+
else if (content) {
|
|
301
302
|
writeMinify();
|
|
302
303
|
}
|
|
303
304
|
// Minifying input coming from STDIN
|
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": "
|
|
4
|
+
"version": "4.0.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
7
7
|
"compress",
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
"uglifier",
|
|
24
24
|
"uglify"
|
|
25
25
|
],
|
|
26
|
-
"homepage": "
|
|
26
|
+
"homepage": "https://kangax.github.io/html-minifier/",
|
|
27
27
|
"author": "Juriy \"kangax\" Zaytsev",
|
|
28
28
|
"maintainers": [
|
|
29
29
|
"Alex Lam <alexlamsl@gmail.com>",
|
|
30
|
-
"Juriy Zaytsev <kangax@gmail.com> (http://perfectionkills.com)"
|
|
30
|
+
"Juriy Zaytsev <kangax@gmail.com> (http://perfectionkills.com/)"
|
|
31
31
|
],
|
|
32
32
|
"contributors": [
|
|
33
33
|
"Gilmore Davidson (https://github.com/gilmoreorless)",
|
|
@@ -47,36 +47,36 @@
|
|
|
47
47
|
"url": "https://github.com/kangax/html-minifier/issues"
|
|
48
48
|
},
|
|
49
49
|
"engines": {
|
|
50
|
-
"node": ">=
|
|
50
|
+
"node": ">=6"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"dist": "grunt dist",
|
|
54
54
|
"test": "grunt test"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"camel-case": "3.0.
|
|
58
|
-
"clean-css": "4.1
|
|
59
|
-
"commander": "2.
|
|
60
|
-
"he": "1.
|
|
61
|
-
"param-case": "2.1.
|
|
62
|
-
"relateurl": "0.2.
|
|
63
|
-
"uglify-js": "3.
|
|
57
|
+
"camel-case": "^3.0.0",
|
|
58
|
+
"clean-css": "^4.2.1",
|
|
59
|
+
"commander": "^2.19.0",
|
|
60
|
+
"he": "^1.2.0",
|
|
61
|
+
"param-case": "^2.1.1",
|
|
62
|
+
"relateurl": "^0.2.7",
|
|
63
|
+
"uglify-js": "^3.5.1"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"grunt": "1.0.
|
|
67
|
-
"grunt-browserify": "5.3.
|
|
68
|
-
"grunt-contrib-uglify": "
|
|
69
|
-
"
|
|
70
|
-
"phantomjs-prebuilt": "2.1.
|
|
71
|
-
"qunit": "2.
|
|
66
|
+
"grunt": "^1.0.4",
|
|
67
|
+
"grunt-browserify": "^5.3.0",
|
|
68
|
+
"grunt-contrib-uglify": "^4.0.1",
|
|
69
|
+
"grunt-eslint": "^21.0.0",
|
|
70
|
+
"phantomjs-prebuilt": "^2.1.16",
|
|
71
|
+
"qunit": "^2.9.2"
|
|
72
72
|
},
|
|
73
73
|
"benchmarkDependencies": {
|
|
74
|
-
"brotli": "1.3.
|
|
75
|
-
"chalk": "2.4.
|
|
76
|
-
"cli-table": "0.3.
|
|
77
|
-
"lzma": "2.3.
|
|
78
|
-
"minimize": "2.2.
|
|
79
|
-
"progress": "2.0.
|
|
74
|
+
"brotli": "^1.3.2",
|
|
75
|
+
"chalk": "^2.4.2",
|
|
76
|
+
"cli-table": "^0.3.1",
|
|
77
|
+
"lzma": "^2.3.2",
|
|
78
|
+
"minimize": "^2.2.0",
|
|
79
|
+
"progress": "^2.0.3"
|
|
80
80
|
},
|
|
81
81
|
"files": [
|
|
82
82
|
"src/*.js",
|
package/src/htmlminifier.js
CHANGED
|
@@ -109,7 +109,7 @@ function isEventAttribute(attrName, options) {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
function canRemoveAttributeQuotes(value) {
|
|
112
|
-
//
|
|
112
|
+
// https://mathiasbynens.be/notes/unquoted-attribute-values
|
|
113
113
|
return /^[^ \t\n\f\r"'`=<>]+$/.test(value);
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -311,6 +311,9 @@ function cleanAttributeValue(tag, attrName, attrValue, options, attrs) {
|
|
|
311
311
|
return (+numString).toString();
|
|
312
312
|
});
|
|
313
313
|
}
|
|
314
|
+
else if (isContentSecurityPolicy(tag, attrs) && attrName.toLowerCase() === 'content') {
|
|
315
|
+
return collapseWhitespaceAll(attrValue);
|
|
316
|
+
}
|
|
314
317
|
else if (options.customAttrCollapse && options.customAttrCollapse.test(attrName)) {
|
|
315
318
|
attrValue = attrValue.replace(/\n+|\r+|\s{2,}/g, '');
|
|
316
319
|
}
|
|
@@ -335,23 +338,44 @@ function isMetaViewport(tag, attrs) {
|
|
|
335
338
|
}
|
|
336
339
|
}
|
|
337
340
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
341
|
+
function isContentSecurityPolicy(tag, attrs) {
|
|
342
|
+
if (tag !== 'meta') {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
for (var i = 0, len = attrs.length; i < len; i++) {
|
|
346
|
+
if (attrs[i].name.toLowerCase() === 'http-equiv' && attrs[i].value.toLowerCase() === 'content-security-policy') {
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
342
350
|
}
|
|
343
351
|
|
|
344
|
-
function
|
|
345
|
-
|
|
346
|
-
return matches ? matches[1] : text;
|
|
352
|
+
function ignoreCSS(id) {
|
|
353
|
+
return '/* clean-css ignore:start */' + id + '/* clean-css ignore:end */';
|
|
347
354
|
}
|
|
348
355
|
|
|
349
|
-
|
|
350
|
-
|
|
356
|
+
// Wrap CSS declarations for CleanCSS > 3.x
|
|
357
|
+
// See https://github.com/jakubpawlowicz/clean-css/issues/418
|
|
358
|
+
function wrapCSS(text, type) {
|
|
359
|
+
switch (type) {
|
|
360
|
+
case 'inline':
|
|
361
|
+
return '*{' + text + '}';
|
|
362
|
+
case 'media':
|
|
363
|
+
return '@media ' + text + '{a{top:0}}';
|
|
364
|
+
default:
|
|
365
|
+
return text;
|
|
366
|
+
}
|
|
351
367
|
}
|
|
352
368
|
|
|
353
|
-
function
|
|
354
|
-
var matches
|
|
369
|
+
function unwrapCSS(text, type) {
|
|
370
|
+
var matches;
|
|
371
|
+
switch (type) {
|
|
372
|
+
case 'inline':
|
|
373
|
+
matches = text.match(/^\*\{([\s\S]*)\}$/);
|
|
374
|
+
break;
|
|
375
|
+
case 'media':
|
|
376
|
+
matches = text.match(/^@media ([\s\S]*?)\s*{[\s\S]*}$/);
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
355
379
|
return matches ? matches[1] : text;
|
|
356
380
|
}
|
|
357
381
|
|
|
@@ -374,7 +398,7 @@ function processScript(text, options, currentAttrs) {
|
|
|
374
398
|
// Tag omission rules from https://html.spec.whatwg.org/multipage/syntax.html#optional-tags
|
|
375
399
|
// with the following deviations:
|
|
376
400
|
// - retain <body> if followed by <noscript>
|
|
377
|
-
// - </rb>, </rt>, </rtc>, </rp> & </tfoot> follow
|
|
401
|
+
// - </rb>, </rt>, </rtc>, </rp> & </tfoot> follow https://www.w3.org/TR/html5/syntax.html#optional-tags
|
|
378
402
|
// - retain all tags which are adjacent to non-standard HTML tags
|
|
379
403
|
var optionalStartTags = createMapFromString('html,head,body,colgroup,tbody');
|
|
380
404
|
var optionalEndTags = createMapFromString('html,head,body,li,dt,dd,p,rb,rt,rtc,rp,optgroup,option,colgroup,caption,thead,tbody,tfoot,tr,td,th');
|
|
@@ -654,26 +678,12 @@ function processOptions(values) {
|
|
|
654
678
|
text = text.replace(/(url\s*\(\s*)("|'|)(.*?)\2(\s*\))/ig, function(match, prefix, quote, url, suffix) {
|
|
655
679
|
return prefix + quote + options.minifyURLs(url) + quote + suffix;
|
|
656
680
|
});
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
}
|
|
661
|
-
else if (type === 'media') {
|
|
662
|
-
text = wrapMediaQuery(text);
|
|
663
|
-
}
|
|
664
|
-
text = new CleanCSS(value).minify(text).styles;
|
|
665
|
-
if (type === 'inline') {
|
|
666
|
-
text = unwrapInlineCSS(text);
|
|
667
|
-
}
|
|
668
|
-
else if (type === 'media') {
|
|
669
|
-
text = unwrapMediaQuery(text);
|
|
670
|
-
}
|
|
671
|
-
return text;
|
|
672
|
-
}
|
|
673
|
-
catch (err) {
|
|
674
|
-
options.log(err);
|
|
681
|
+
var cleanCssOutput = new CleanCSS(value).minify(wrapCSS(text, type));
|
|
682
|
+
if (cleanCssOutput.errors.length > 0) {
|
|
683
|
+
cleanCssOutput.errors.forEach(options.log);
|
|
675
684
|
return text;
|
|
676
685
|
}
|
|
686
|
+
return unwrapCSS(cleanCssOutput.styles, type);
|
|
677
687
|
};
|
|
678
688
|
}
|
|
679
689
|
else if (key === 'minifyJS' && typeof value !== 'function') {
|
|
@@ -859,15 +869,6 @@ function minify(value, options, partialMarkup) {
|
|
|
859
869
|
return token;
|
|
860
870
|
});
|
|
861
871
|
|
|
862
|
-
function escapeFragments(fn) {
|
|
863
|
-
return function(text, type) {
|
|
864
|
-
return fn(text.replace(uidPattern, function(match, prefix, index) {
|
|
865
|
-
var chunks = ignoredCustomMarkupChunks[+index];
|
|
866
|
-
return chunks[1] + uidAttr + index + chunks[2];
|
|
867
|
-
}), type);
|
|
868
|
-
};
|
|
869
|
-
}
|
|
870
|
-
|
|
871
872
|
var customFragments = options.ignoreCustomFragments.map(function(re) {
|
|
872
873
|
return re.source;
|
|
873
874
|
});
|
|
@@ -877,15 +878,43 @@ function minify(value, options, partialMarkup) {
|
|
|
877
878
|
value = value.replace(reCustomIgnore, function(match) {
|
|
878
879
|
if (!uidAttr) {
|
|
879
880
|
uidAttr = uniqueId(value);
|
|
880
|
-
uidPattern = new RegExp('(\\s*)' + uidAttr + '([0-9]+)(\\s*)', 'g');
|
|
881
|
+
uidPattern = new RegExp('(\\s*)' + uidAttr + '([0-9]+)' + uidAttr + '(\\s*)', 'g');
|
|
881
882
|
if (options.minifyCSS) {
|
|
882
|
-
options.minifyCSS =
|
|
883
|
+
options.minifyCSS = (function(fn) {
|
|
884
|
+
return function(text, type) {
|
|
885
|
+
text = text.replace(uidPattern, function(match, prefix, index) {
|
|
886
|
+
var chunks = ignoredCustomMarkupChunks[+index];
|
|
887
|
+
return chunks[1] + uidAttr + index + uidAttr + chunks[2];
|
|
888
|
+
});
|
|
889
|
+
var ids = [];
|
|
890
|
+
new CleanCSS().minify(wrapCSS(text, type)).warnings.forEach(function(warning) {
|
|
891
|
+
var match = uidPattern.exec(warning);
|
|
892
|
+
if (match) {
|
|
893
|
+
var id = uidAttr + match[2] + uidAttr;
|
|
894
|
+
text = text.replace(id, ignoreCSS(id));
|
|
895
|
+
ids.push(id);
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
text = fn(text, type);
|
|
899
|
+
ids.forEach(function(id) {
|
|
900
|
+
text = text.replace(ignoreCSS(id), id);
|
|
901
|
+
});
|
|
902
|
+
return text;
|
|
903
|
+
};
|
|
904
|
+
})(options.minifyCSS);
|
|
883
905
|
}
|
|
884
906
|
if (options.minifyJS) {
|
|
885
|
-
options.minifyJS =
|
|
907
|
+
options.minifyJS = (function(fn) {
|
|
908
|
+
return function(text, type) {
|
|
909
|
+
return fn(text.replace(uidPattern, function(match, prefix, index) {
|
|
910
|
+
var chunks = ignoredCustomMarkupChunks[+index];
|
|
911
|
+
return chunks[1] + uidAttr + index + uidAttr + chunks[2];
|
|
912
|
+
}), type);
|
|
913
|
+
};
|
|
914
|
+
})(options.minifyJS);
|
|
886
915
|
}
|
|
887
916
|
}
|
|
888
|
-
var token = uidAttr + ignoredCustomMarkupChunks.length;
|
|
917
|
+
var token = uidAttr + ignoredCustomMarkupChunks.length + uidAttr;
|
|
889
918
|
ignoredCustomMarkupChunks.push(/^(\s*)[\s\S]*?(\s*)$/.exec(match));
|
|
890
919
|
return '\t' + token + '\t';
|
|
891
920
|
});
|
|
@@ -950,6 +979,9 @@ function minify(value, options, partialMarkup) {
|
|
|
950
979
|
|
|
951
980
|
new HTMLParser(value, {
|
|
952
981
|
partialMarkup: partialMarkup,
|
|
982
|
+
continueOnParseError: options.continueOnParseError,
|
|
983
|
+
customAttrAssign: options.customAttrAssign,
|
|
984
|
+
customAttrSurround: options.customAttrSurround,
|
|
953
985
|
html5: options.html5,
|
|
954
986
|
|
|
955
987
|
start: function(tag, attrs, unary, unarySlash, autoGenerated) {
|
|
@@ -1180,9 +1212,12 @@ function minify(value, options, partialMarkup) {
|
|
|
1180
1212
|
}
|
|
1181
1213
|
charsPrevTag = /^\s*$/.test(text) ? prevTag : 'comment';
|
|
1182
1214
|
if (options.decodeEntities && text && !specialContentTags(currentTag)) {
|
|
1183
|
-
//
|
|
1215
|
+
// Escape any `&` symbols that start either:
|
|
1216
|
+
// 1) a legacy named character reference (i.e. one that doesn't end with `;`)
|
|
1217
|
+
// 2) or any other character reference (i.e. one that does end with `;`)
|
|
1218
|
+
// Note that `&` can be escaped as `&`, without the semi-colon.
|
|
1184
1219
|
// https://mathiasbynens.be/notes/ambiguous-ampersands
|
|
1185
|
-
text = text.replace(/&(
|
|
1220
|
+
text = text.replace(/&((?:Iacute|aacute|uacute|plusmn|Otilde|otilde|agrave|Agrave|Yacute|yacute|Oslash|oslash|atilde|Atilde|brvbar|ccedil|Ccedil|Ograve|curren|divide|eacute|Eacute|ograve|Oacute|egrave|Egrave|Ugrave|frac12|frac14|frac34|ugrave|oacute|iacute|Ntilde|ntilde|Uacute|middot|igrave|Igrave|iquest|Aacute|cedil|laquo|micro|iexcl|Icirc|icirc|acirc|Ucirc|Ecirc|ocirc|Ocirc|ecirc|ucirc|Aring|aring|AElig|aelig|acute|pound|raquo|Acirc|times|THORN|szlig|thorn|COPY|auml|ordf|ordm|Uuml|macr|uuml|Auml|ouml|Ouml|para|nbsp|euml|quot|QUOT|Euml|yuml|cent|sect|copy|sup1|sup2|sup3|iuml|Iuml|ETH|shy|reg|not|yen|amp|AMP|REG|uml|eth|deg|gt|GT|LT|lt)(?!;)|(?:#?[0-9a-zA-Z]+;))/g, '&$1').replace(/</g, '<');
|
|
1186
1221
|
}
|
|
1187
1222
|
if (uidPattern && options.collapseWhitespace && stackNoTrimWhitespace.length) {
|
|
1188
1223
|
text = text.replace(uidPattern, function(match, prefix, index) {
|
|
@@ -1220,10 +1255,10 @@ function minify(value, options, partialMarkup) {
|
|
|
1220
1255
|
buffer.push(text);
|
|
1221
1256
|
},
|
|
1222
1257
|
doctype: function(doctype) {
|
|
1223
|
-
buffer.push(options.useShortDoctype ? '<!
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1258
|
+
buffer.push(options.useShortDoctype ? '<!doctype' +
|
|
1259
|
+
(options.removeTagWhitespace ? '' : ' ') + 'html>' :
|
|
1260
|
+
collapseWhitespaceAll(doctype));
|
|
1261
|
+
}
|
|
1227
1262
|
});
|
|
1228
1263
|
|
|
1229
1264
|
if (options.removeOptionalTags) {
|
|
@@ -1241,10 +1276,8 @@ function minify(value, options, partialMarkup) {
|
|
|
1241
1276
|
squashTrailingWhitespace('br');
|
|
1242
1277
|
}
|
|
1243
1278
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
if (uidPattern) {
|
|
1247
|
-
str = str.replace(uidPattern, function(match, prefix, index, suffix) {
|
|
1279
|
+
return joinResultSegments(buffer, options, uidPattern ? function(str) {
|
|
1280
|
+
return str.replace(uidPattern, function(match, prefix, index, suffix) {
|
|
1248
1281
|
var chunk = ignoredCustomMarkupChunks[+index][0];
|
|
1249
1282
|
if (options.collapseWhitespace) {
|
|
1250
1283
|
if (prefix !== '\t') {
|
|
@@ -1260,17 +1293,14 @@ function minify(value, options, partialMarkup) {
|
|
|
1260
1293
|
}
|
|
1261
1294
|
return chunk;
|
|
1262
1295
|
});
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
str = str.replace(new RegExp('<!--' + uidIgnore + '([0-9]+)-->', 'g'), function(match, index) {
|
|
1296
|
+
} : identity, uidIgnore ? function(str) {
|
|
1297
|
+
return str.replace(new RegExp('<!--' + uidIgnore + '([0-9]+)-->', 'g'), function(match, index) {
|
|
1266
1298
|
return ignoredMarkupChunks[+index];
|
|
1267
1299
|
});
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
return str;
|
|
1300
|
+
} : identity);
|
|
1271
1301
|
}
|
|
1272
1302
|
|
|
1273
|
-
function joinResultSegments(results, options) {
|
|
1303
|
+
function joinResultSegments(results, options, restoreCustom, restoreIgnore) {
|
|
1274
1304
|
var str;
|
|
1275
1305
|
var maxLineLength = options.maxLineLength;
|
|
1276
1306
|
if (maxLineLength) {
|
|
@@ -1279,13 +1309,13 @@ function joinResultSegments(results, options) {
|
|
|
1279
1309
|
var len = line.length;
|
|
1280
1310
|
var end = results[0].indexOf('\n');
|
|
1281
1311
|
if (end < 0) {
|
|
1282
|
-
line += results.shift();
|
|
1312
|
+
line += restoreIgnore(restoreCustom(results.shift()));
|
|
1283
1313
|
}
|
|
1284
1314
|
else {
|
|
1285
|
-
line += results[0].slice(0, end);
|
|
1315
|
+
line += restoreIgnore(restoreCustom(results[0].slice(0, end)));
|
|
1286
1316
|
results[0] = results[0].slice(end + 1);
|
|
1287
1317
|
}
|
|
1288
|
-
if (len > 0 && line.length
|
|
1318
|
+
if (len > 0 && line.length > maxLineLength) {
|
|
1289
1319
|
lines.push(line.slice(0, len));
|
|
1290
1320
|
line = line.slice(len);
|
|
1291
1321
|
}
|
|
@@ -1297,11 +1327,10 @@ function joinResultSegments(results, options) {
|
|
|
1297
1327
|
if (line) {
|
|
1298
1328
|
lines.push(line);
|
|
1299
1329
|
}
|
|
1300
|
-
|
|
1301
1330
|
str = lines.join('\n');
|
|
1302
1331
|
}
|
|
1303
1332
|
else {
|
|
1304
|
-
str = results.join('');
|
|
1333
|
+
str = restoreIgnore(restoreCustom(results.join('')));
|
|
1305
1334
|
}
|
|
1306
1335
|
return options.collapseWhitespace ? collapseWhitespace(str, options, true, true) : str;
|
|
1307
1336
|
}
|
package/src/htmlparser.js
CHANGED
|
@@ -60,7 +60,7 @@ var singleAttrIdentifier = /([^\s"'<>/=]+)/,
|
|
|
60
60
|
startTagOpen = new RegExp('^<' + qnameCapture),
|
|
61
61
|
startTagClose = /^\s*(\/?)>/,
|
|
62
62
|
endTag = new RegExp('^<\\/' + qnameCapture + '[^>]*>'),
|
|
63
|
-
doctype = /^<!DOCTYPE\s[^>]+>/i;
|
|
63
|
+
doctype = /^<!DOCTYPE\s?[^>]+>/i;
|
|
64
64
|
|
|
65
65
|
var IS_REGEX_CAPTURING_BROKEN = false;
|
|
66
66
|
'x'.replace(/x(.)?/g, function(m, g) {
|
|
@@ -140,7 +140,7 @@ function HTMLParser(html, handler) {
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
//
|
|
143
|
+
// https://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
|
|
144
144
|
if (/^<!\[/.test(html)) {
|
|
145
145
|
var conditionalEnd = html.indexOf(']>');
|
|
146
146
|
|
|
@@ -182,6 +182,11 @@ function HTMLParser(html, handler) {
|
|
|
182
182
|
prevTag = startTagMatch.tagName.toLowerCase();
|
|
183
183
|
continue;
|
|
184
184
|
}
|
|
185
|
+
|
|
186
|
+
// Treat `<` as text
|
|
187
|
+
if (handler.continueOnParseError) {
|
|
188
|
+
textEnd = html.indexOf('<', 1);
|
|
189
|
+
}
|
|
185
190
|
}
|
|
186
191
|
|
|
187
192
|
var text;
|
|
@@ -213,7 +218,6 @@ function HTMLParser(html, handler) {
|
|
|
213
218
|
handler.chars(text, prevTag, nextTag);
|
|
214
219
|
}
|
|
215
220
|
prevTag = '';
|
|
216
|
-
|
|
217
221
|
}
|
|
218
222
|
else {
|
|
219
223
|
var stackedTag = lastTag.toLowerCase();
|