From 5a3a4fa25f257de63341086171fb86da6c31ed34 Mon Sep 17 00:00:00 2001 From: Duncan Beevers Date: Wed, 9 Jul 2014 17:51:11 -0500 Subject: [PATCH] Implement maxLineLength option --- README.md | 1 + src/htmlminifier.js | 59 +++++++++++++++++++++++++++++++++++++++------ tests/minifier.js | 49 +++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 88d6d46..ab533f9 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ How does HTMLMinifier compare to [another solution](http://www.willpeavy.com/min | `minifyCSS` | Minify CSS in style elements and style attributes (uses [clean-css](https://github.com/GoalSmashers/clean-css)) | `false` (could be `true`, `false`, `Object` (options)) | | `ignoreCustomComments` | Array of regex'es that allow to ignore certain comments, when matched | `[ ]` | | `processScripts` | Array of strings corresponding to types of script elements to process through minifier (e.g. "text/ng-template", "text/x-handlebars-template", etc.) | `[ ]` | +| `maxLineLength` | Specify a maximum line length. Compressed output will be split by newlines at valid html split-points. | Chunks of markup can be ignored by wrapping them with ``. diff --git a/src/htmlminifier.js b/src/htmlminifier.js index cb24628..a59176d 100644 --- a/src/htmlminifier.js +++ b/src/htmlminifier.js @@ -459,7 +459,7 @@ start: function( tag, attrs, unary, unarySlash ) { if (isIgnoring) { - buffer.push('<', tag, attrsToMarkup(attrs), unarySlash ? '/' : '', '>'); + buffer.push('<' + tag, attrsToMarkup(attrs), unarySlash ? '/' : '', '>'); return; } @@ -478,20 +478,30 @@ } } - buffer.push('<', tag); + var openTag = '<' + tag; + var closeTag = ((unarySlash && options.keepClosingSlash) ? '/' : '') + '>'; + if ( attrs.length === 0) { + openTag += closeTag; + } + + buffer.push(openTag); lint && lint.testElement(tag); + var token; for ( var i = 0, len = attrs.length; i < len; i++ ) { lint && lint.testAttribute(tag, attrs[i].name.toLowerCase(), attrs[i].escaped); - buffer.push(normalizeAttribute(attrs[i], attrs, tag, options)); + token = normalizeAttribute(attrs[i], attrs, tag, options); + if ( i === len - 1 ) { + token += closeTag; + } + buffer.push(token); } - buffer.push(((unarySlash && options.keepClosingSlash) ? '/' : '') + '>'); }, end: function( tag ) { if (isIgnoring) { - buffer.push(''); + buffer.push(''); return; } @@ -510,7 +520,12 @@ var isElementEmpty = currentChars === '' && tag === currentTag; if ((options.removeEmptyElements && isElementEmpty && canRemoveElement(tag))) { // remove last "element" from buffer, return - buffer.splice(buffer.lastIndexOf('<')); + for ( var i = buffer.length - 1; i >= 0; i-- ) { + if ( /^<[^\/!]/.test(buffer[i]) ) { + buffer.splice(i); + break; + } + } return; } else if (options.removeOptionalTags && isOptionalTag(tag)) { @@ -519,7 +534,7 @@ } else { // push end tag to buffer - buffer.push(''); + buffer.push(''); results.push.apply(results, buffer); } // flush buffer @@ -600,11 +615,39 @@ }); results.push.apply(results, buffer); - var str = results.join(''); + var str = joinResultSegments(results, options); log('minified in: ' + (new Date() - t) + 'ms'); return str; } + function joinResultSegments( results, options ) { + var str; + var maxLineLength = options.maxLineLength; + if ( maxLineLength ) { + var token; + var lines = []; + var line = ''; + for ( var i = 0, len = results.length; i < len; i++ ) { + token = results[i]; + if ( line.length + token.length < maxLineLength ) { + line += token; + } + else { + lines.push(line.replace(/^\n/, '')); + line = token; + } + } + lines.push(line); + + str = lines.join('\n'); + } + else { + str = results.join(''); + } + + return str; + } + // for CommonJS enviroments, export everything if ( typeof exports !== 'undefined' ) { exports.minify = minify; diff --git a/tests/minifier.js b/tests/minifier.js index efb61ef..314c6fe 100644 --- a/tests/minifier.js +++ b/tests/minifier.js @@ -1003,4 +1003,53 @@ equal(minify(input), ''); }); + test('max line length', function() { + var options = { maxLineLength: 25 }; + + input = '
'; + equal(minify(input, options), '
\n
'); + + input = ' hello world \n world hello '; + equal(minify(input, options), '\n hello world \n world hello \n'); + + equal(minify('

x

'), '

x

'); + equal(minify('

x

'), '

x

'); + equal(minify('

x

'), '

x

'); + equal(minify('

xxx

'), '

xxx

'); + equal(minify('

xxx

'), '

xxx

'); + + input = '
' + + 'i\'m 10 levels deep' + + '
'; + + equal(minify(input), input); + + equal(minify('