html-minifier
Advanced tools
Comparing version 2.0.0 to 2.1.0
11
cli.js
@@ -75,3 +75,6 @@ #!/usr/bin/env node | ||
catch (e) { | ||
fatal('Could not parse JSON value \'' + value + '\''); | ||
if (/^{/.test(value)) { | ||
fatal('Could not parse JSON value \'' + value + '\''); | ||
} | ||
return value; | ||
} | ||
@@ -93,6 +96,2 @@ } | ||
function parseSiteURL(value) { | ||
return value && { site: value }; | ||
} | ||
function parseString(value) { | ||
@@ -121,3 +120,3 @@ return value; | ||
minifyJS: ['Minify Javascript in script elements and on* attributes (uses uglify-js)', parseJSON], | ||
minifyURLs: ['Minify URLs in various attributes (uses relateurl)', parseSiteURL], | ||
minifyURLs: ['Minify URLs in various attributes (uses relateurl)', parseJSON], | ||
preserveLineBreaks: 'Always collapse to 1 line break (never remove it entirely) when whitespace between tags include a line break.', | ||
@@ -124,0 +123,0 @@ preventAttributesEscaping: 'Prevents the escaping of the values of attributes.', |
{ | ||
"name": "html-minifier", | ||
"description": "Highly configurable, well-tested, JavaScript-based HTML minifier.", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"keywords": [ | ||
@@ -73,3 +73,3 @@ "cli", | ||
}, | ||
"optionalDependencies": { | ||
"benchmarkDependencies": { | ||
"brotli": "1.2.x", | ||
@@ -76,0 +76,0 @@ "chalk": "1.1.x", |
@@ -7,3 +7,2 @@ # HTMLMinifier | ||
[![devDependency Status](https://img.shields.io/david/dev/kangax/html-minifier.svg)](https://david-dm.org/kangax/html-minifier#info=devDependencies) | ||
[![optionalDependency Status](https://img.shields.io/david/optional/kangax/html-minifier.svg)](https://david-dm.org/kangax/html-minifier#info=optionalDependencies) | ||
[![Gitter](https://img.shields.io/gitter/room/kangax/html-minifier.svg)](https://gitter.im/kangax/html-minifier) | ||
@@ -56,3 +55,3 @@ | ||
| `html5` | Parse input according to HTML5 specifications | `true` | | ||
| `ignoreCustomComments` | Array of regex'es that allow to ignore certain comments, when matched | `[ ]` | | ||
| `ignoreCustomComments` | Array of regex'es that allow to ignore certain comments, when matched | `[ /^!/ ]` | | ||
| `ignoreCustomFragments` | Array of regex'es that allow to ignore certain fragments, when matched (e.g. `<?php ... ?>`, `{{ ... }}`, etc.) | `[ /<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/ ]` | | ||
@@ -62,5 +61,5 @@ | `includeAutoGeneratedTags` | Insert tags generated by HTML parser | `true` | | ||
| `maxLineLength` | Specify a maximum line length. Compressed output will be split by newlines at valid HTML split-points | | ||
| `minifyCSS` | Minify CSS in style elements and style attributes (uses [clean-css](https://github.com/jakubpawlowicz/clean-css)) | `false` (could be `true`, `false`, `Object` (options)) | | ||
| `minifyJS` | Minify JavaScript in script elements and event attributes (uses [UglifyJS](https://github.com/mishoo/UglifyJS2)) | `false` (could be `true`, `false`, `Object` (options)) | | ||
| `minifyURLs` | Minify URLs in various attributes (uses [relateurl](https://github.com/stevenvachon/relateurl)) | `false` (could be `Object` (options)) | | ||
| `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)`) | | ||
| `minifyJS` | Minify JavaScript in script elements and event attributes (uses [UglifyJS](https://github.com/mishoo/UglifyJS2)) | `false` (could be `true`, `Object`, `Function(text, inline)`) | | ||
| `minifyURLs` | Minify URLs in various attributes (uses [relateurl](https://github.com/stevenvachon/relateurl)) | `false` (could be `String`, `Object`, `Function(text)`) | | ||
| `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` | | ||
@@ -67,0 +66,0 @@ | `preventAttributesEscaping` | Prevents the escaping of the values of attributes | `false` | |
@@ -11,13 +11,2 @@ 'use strict'; | ||
var log; | ||
if (typeof window !== 'undefined' && typeof console !== 'undefined' && typeof console.log === 'function') { | ||
log = function(message) { | ||
// "preserving" `this` | ||
console.log(message); | ||
}; | ||
} | ||
else { | ||
log = function() {}; | ||
} | ||
var trimWhitespace = String.prototype.trim ? function(str) { | ||
@@ -97,14 +86,7 @@ if (typeof str !== 'string') { | ||
function isIgnoredComment(text, options) { | ||
if (/^!/.test(text)) { | ||
return true; | ||
} | ||
if (options.ignoreCustomComments) { | ||
for (var i = 0, len = options.ignoreCustomComments.length; i < len; i++) { | ||
if (options.ignoreCustomComments[i].test(text)) { | ||
return true; | ||
} | ||
for (var i = 0, len = options.ignoreCustomComments.length; i < len; i++) { | ||
if (options.ignoreCustomComments[i].test(text)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
@@ -262,13 +244,12 @@ } | ||
var fnPrefix = '!function(){'; | ||
var fnSuffix = '}();'; | ||
var srcsetTags = createMapFromString('img,source'); | ||
function isSrcset(attrName, tag) { | ||
return attrName === 'srcset' && srcsetTags(tag); | ||
} | ||
function cleanAttributeValue(tag, attrName, attrValue, options, attrs) { | ||
if (attrValue && isEventAttribute(attrName, options)) { | ||
attrValue = trimWhitespace(attrValue).replace(/^javascript:\s*/i, '').replace(/\s*;$/, ''); | ||
if (options.minifyJS) { | ||
var minified = minifyJS(fnPrefix + attrValue + fnSuffix, options.minifyJS); | ||
return minified.slice(fnPrefix.length, -fnSuffix.length); | ||
} | ||
return attrValue; | ||
attrValue = trimWhitespace(attrValue).replace(/^javascript:\s*/i, ''); | ||
return options.minifyJS(attrValue, true); | ||
} | ||
@@ -287,6 +268,3 @@ else if (attrName === 'class') { | ||
attrValue = trimWhitespace(attrValue); | ||
if (options.minifyURLs && !isCanonicalURL(tag, attrs)) { | ||
return minifyURLs(attrValue, options.minifyURLs); | ||
} | ||
return attrValue; | ||
return isCanonicalURL(tag, attrs) ? attrValue : options.minifyURLs(attrValue); | ||
} | ||
@@ -301,7 +279,21 @@ else if (isNumberTypeAttribute(attrName, tag)) { | ||
} | ||
if (options.minifyCSS) { | ||
return minifyStyles(attrValue, options, true); | ||
} | ||
return attrValue; | ||
return options.minifyCSS(attrValue, true); | ||
} | ||
else if (isSrcset(attrName, tag)) { | ||
// https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-srcset | ||
attrValue = trimWhitespace(attrValue).split(/\s+,\s*|\s*,\s+/).map(function(candidate) { | ||
var url = candidate; | ||
var descriptor = ''; | ||
var match = candidate.match(/\s+([1-9][0-9]*w|[0-9]+(?:\.[0-9]+)?x)$/); | ||
if (match) { | ||
url = url.slice(0, -match[0].length); | ||
var num = +match[1].slice(0, -1); | ||
var suffix = match[1].slice(-1); | ||
if (num !== 1 || suffix !== 'x') { | ||
descriptor = ' ' + num + suffix; | ||
} | ||
} | ||
return options.minifyURLs(url) + descriptor; | ||
}).join(', '); | ||
} | ||
else if (isMetaViewport(tag, attrs) && attrName === 'content') { | ||
@@ -597,2 +589,9 @@ attrValue = attrValue.replace(/\s+/g, '').replace(/[0-9]+\.[0-9]+/g, function(numString) { | ||
function identity(value) { | ||
return value; | ||
} | ||
var fnPrefix = '!function(){'; | ||
var fnSuffix = '}();'; | ||
function processOptions(options) { | ||
@@ -605,2 +604,6 @@ ['html5', 'includeAutoGeneratedTags'].forEach(function(key) { | ||
if (typeof options.log !== 'function') { | ||
options.log = identity; | ||
} | ||
var defaultTesters = ['canCollapseWhitespace', 'canTrimWhitespace']; | ||
@@ -615,71 +618,100 @@ for (var i = 0, len = defaultTesters.length; i < len; i++) { | ||
if (options.minifyURLs && typeof options.minifyURLs !== 'object') { | ||
options.minifyURLs = { }; | ||
if (!('ignoreCustomComments' in options)) { | ||
options.ignoreCustomComments = [/^!/]; | ||
} | ||
if (options.minifyJS) { | ||
if (typeof options.minifyJS !== 'object') { | ||
options.minifyJS = { }; | ||
} | ||
options.minifyJS.fromString = true; | ||
(options.minifyJS.output || (options.minifyJS.output = { })).inline_script = true; | ||
if (!('ignoreCustomFragments' in options)) { | ||
options.ignoreCustomFragments = [ | ||
/<%[\s\S]*?%>/, | ||
/<\?[\s\S]*?\?>/ | ||
]; | ||
} | ||
if (options.minifyCSS) { | ||
if (typeof options.minifyCSS !== 'object') { | ||
options.minifyCSS = { }; | ||
if (!options.minifyURLs) { | ||
options.minifyURLs = identity; | ||
} | ||
if (typeof options.minifyURLs !== 'function') { | ||
var minifyURLs = options.minifyURLs; | ||
if (typeof minifyURLs === 'string') { | ||
minifyURLs = { site: minifyURLs }; | ||
} | ||
if (typeof options.minifyCSS.advanced === 'undefined') { | ||
options.minifyCSS.advanced = false; | ||
else if (typeof minifyURLs !== 'object') { | ||
minifyURLs = {}; | ||
} | ||
options.minifyURLs = function(text) { | ||
try { | ||
return RelateUrl.relate(text, minifyURLs); | ||
} | ||
catch (err) { | ||
options.log(err); | ||
return text; | ||
} | ||
}; | ||
} | ||
} | ||
function minifyURLs(text, options) { | ||
try { | ||
return RelateUrl.relate(text, options); | ||
if (!options.minifyJS) { | ||
options.minifyJS = identity; | ||
} | ||
catch (err) { | ||
log(err); | ||
return text; | ||
if (typeof options.minifyJS !== 'function') { | ||
var minifyJS = options.minifyJS; | ||
if (typeof minifyJS !== 'object') { | ||
minifyJS = {}; | ||
} | ||
minifyJS.fromString = true; | ||
(minifyJS.output || (minifyJS.output = {})).inline_script = true; | ||
options.minifyJS = function(text, inline) { | ||
var start = text.match(/^\s*<!--.*/); | ||
var code = start ? text.slice(start[0].length).replace(/\n\s*-->\s*$/, '') : text; | ||
try { | ||
if (inline) { | ||
code = fnPrefix + code + fnSuffix; | ||
} | ||
code = UglifyJS.minify(code, minifyJS).code; | ||
if (inline) { | ||
code = code.slice(fnPrefix.length, -fnSuffix.length); | ||
} | ||
if (/;$/.test(code)) { | ||
code = code.slice(0, -1); | ||
} | ||
return code; | ||
} | ||
catch (err) { | ||
options.log(err); | ||
return text; | ||
} | ||
}; | ||
} | ||
} | ||
function minifyJS(text, options) { | ||
var start = text.match(/^\s*<!--.*/); | ||
var code = start ? text.slice(start[0].length).replace(/\n\s*-->\s*$/, '') : text; | ||
try { | ||
return UglifyJS.minify(code, options).code; | ||
if (!options.minifyCSS) { | ||
options.minifyCSS = identity; | ||
} | ||
catch (err) { | ||
log(err); | ||
return text; | ||
} | ||
} | ||
function minifyCSS(text, options, inline) { | ||
var start = text.match(/^\s*<!--/); | ||
var style = start ? text.slice(start[0].length).replace(/-->\s*$/, '') : text; | ||
try { | ||
var cleanCSS = new CleanCSS(options); | ||
if (inline) { | ||
return unwrapCSS(cleanCSS.minify(wrapCSS(style)).styles); | ||
if (typeof options.minifyCSS !== 'function') { | ||
var minifyCSS = options.minifyCSS; | ||
if (typeof minifyCSS !== 'object') { | ||
minifyCSS = {}; | ||
} | ||
return cleanCSS.minify(style).styles; | ||
if (typeof minifyCSS.advanced === 'undefined') { | ||
minifyCSS.advanced = false; | ||
} | ||
options.minifyCSS = function(text, inline) { | ||
text = text.replace(/(url\s*\(\s*)("|'|)(.*?)\2(\s*\))/ig, function(match, prefix, quote, url, suffix) { | ||
return prefix + quote + options.minifyURLs(url) + quote + suffix; | ||
}); | ||
var start = text.match(/^\s*<!--/); | ||
var style = start ? text.slice(start[0].length).replace(/-->\s*$/, '') : text; | ||
try { | ||
var cleanCSS = new CleanCSS(minifyCSS); | ||
if (inline) { | ||
return unwrapCSS(cleanCSS.minify(wrapCSS(style)).styles); | ||
} | ||
return cleanCSS.minify(style).styles; | ||
} | ||
catch (err) { | ||
options.log(err); | ||
return text; | ||
} | ||
}; | ||
} | ||
catch (err) { | ||
log(err); | ||
return text; | ||
} | ||
} | ||
function minifyStyles(text, options, inline) { | ||
if (options.minifyURLs) { | ||
text = text.replace(/(url\s*\(\s*)("|'|)(.*?)\2(\s*\))/ig, function(match, prefix, quote, url, suffix) { | ||
return prefix + quote + minifyURLs(url, options.minifyURLs) + quote + suffix; | ||
}); | ||
} | ||
return minifyCSS(text, options.minifyCSS, inline); | ||
} | ||
function uniqueId(value) { | ||
@@ -746,5 +778,8 @@ var id; | ||
var log = options.log; | ||
options.log = null; | ||
options.sortAttributes = false; | ||
options.sortClassName = false; | ||
scan(minify(value, options)); | ||
options.log = log; | ||
if (attrChains) { | ||
@@ -812,6 +847,3 @@ var attrSorters = Object.create(null); | ||
var customFragments = (options.ignoreCustomFragments || [ | ||
/<%[\s\S]*?%>/, | ||
/<\?[\s\S]*?\?>/ | ||
]).map(function(re) { | ||
var customFragments = options.ignoreCustomFragments.map(function(re) { | ||
return re.source; | ||
@@ -1061,13 +1093,15 @@ }); | ||
if (prevTag === 'comment') { | ||
var removed = buffer[buffer.length - 1] === ''; | ||
if (removed) { | ||
prevTag = charsPrevTag; | ||
var prevComment = buffer[buffer.length - 1]; | ||
if (prevComment.indexOf(uidIgnore) === -1) { | ||
if (!prevComment) { | ||
prevTag = charsPrevTag; | ||
} | ||
if (buffer.length > 1 && (!prevComment || / $/.test(currentChars))) { | ||
var charsIndex = buffer.length - 2; | ||
buffer[charsIndex] = buffer[charsIndex].replace(/\s+$/, function(trailingSpaces) { | ||
text = trailingSpaces + text; | ||
return ''; | ||
}); | ||
} | ||
} | ||
if (buffer.length > 1 && (removed || / $/.test(currentChars))) { | ||
var charsIndex = buffer.length - 2; | ||
buffer[charsIndex] = buffer[charsIndex].replace(/\s+$/, function(trailingSpaces) { | ||
text = trailingSpaces + text; | ||
return ''; | ||
}); | ||
} | ||
} | ||
@@ -1096,10 +1130,7 @@ if (prevTag) { | ||
} | ||
if (options.minifyJS && isExecutableScript(currentTag, currentAttrs)) { | ||
text = minifyJS(text, options.minifyJS); | ||
if (/;$/.test(text)) { | ||
text = text.slice(0, -1); | ||
} | ||
if (isExecutableScript(currentTag, currentAttrs)) { | ||
text = options.minifyJS(text); | ||
} | ||
if (options.minifyCSS && isStyleSheet(currentTag, currentAttrs)) { | ||
text = minifyStyles(text, options); | ||
if (isStyleSheet(currentTag, currentAttrs)) { | ||
text = options.minifyCSS(text); | ||
} | ||
@@ -1204,3 +1235,3 @@ if (options.removeOptionalTags && text) { | ||
log('minified in: ' + (Date.now() - t) + 'ms'); | ||
options.log('minified in: ' + (Date.now() - t) + 'ms'); | ||
return str; | ||
@@ -1207,0 +1238,0 @@ } |
84797
8
1935
157