Comparing version 1.8.1 to 2.0.0
@@ -163,2 +163,15 @@ 'use strict'; | ||
/** | ||
* Proxy to render HTML. | ||
* | ||
* @param {Object} element | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Helpers.prototype.open = function open(element) { | ||
if (element.type in this) { | ||
return this[element.type](element); | ||
} | ||
}; | ||
/** | ||
* Provide closing tag for element if required. | ||
@@ -165,0 +178,0 @@ * |
@@ -6,4 +6,4 @@ 'use strict'; | ||
// | ||
var debug = require('diagnostics')('minimize') | ||
, EventEmitter = require('events').EventEmitter | ||
var EventEmitter = require('events').EventEmitter | ||
, debug = require('diagnostics')('minimize') | ||
, Helpers = require('./helpers') | ||
@@ -20,3 +20,3 @@ , html = require('htmlparser2') | ||
* @Constructor | ||
* @param {Mixed} HTMLParser2 instance, i.e. to support SVG if required. | ||
* @param {Function} parser HTMLParser2 instance, i.e. to support SVG if required. | ||
* @param {Object} options parsing options, optional | ||
@@ -32,5 +32,5 @@ * @api public | ||
this.emits = emits; | ||
this.plugins = Object.create(null); | ||
this.helpers = new Helpers(options); | ||
this.plugins = Object.create(null); | ||
this.emits = emits; | ||
@@ -64,9 +64,23 @@ // | ||
Minimize.prototype.parse = function parse(content, callback) { | ||
if (typeof callback !== 'function') throw new Error('No callback provided'); | ||
var id = uuid.v4(); | ||
var minimize = this | ||
, id = uuid.v4() | ||
, sync = false | ||
, output; | ||
if (typeof callback !== 'function') { | ||
sync = true; | ||
callback = function parsed(error, result) { | ||
if (error) { | ||
minimize.emit('error', error); | ||
} | ||
output = result; | ||
} | ||
} | ||
// | ||
// Listen to DOM parsing, so the htmlparser callback can trigger it. | ||
// | ||
this.once('read', this.minifier.bind(this, id)); | ||
this.once('read', this.minifier.bind(this, id, sync)); | ||
this.once('parsed:' + id, callback); | ||
@@ -78,2 +92,7 @@ | ||
this.htmlparser.parseComplete(content); | ||
// | ||
// Return content if generated synchronously otherwise undefined. | ||
// | ||
return output; | ||
}; | ||
@@ -86,5 +105,6 @@ | ||
* @param {Object} dom presented as traversable object | ||
* @returns {Void} | ||
* @api private | ||
*/ | ||
Minimize.prototype.minifier = function minifier(id, error, dom) { | ||
Minimize.prototype.minifier = function minifier(id, sync, error, dom) { | ||
if (error) throw new Error('Minifier failed to parse DOM', error); | ||
@@ -95,3 +115,3 @@ | ||
// | ||
this.traverse(dom, '', this.emits('parsed:' + id)); | ||
return this.traverse(dom, '', sync, this.emits('parsed:' + id)); | ||
}; | ||
@@ -108,3 +128,3 @@ | ||
*/ | ||
Minimize.prototype.traverse = function traverse(data, html, done) { | ||
Minimize.prototype.traverse = function traverse(data, html, sync, done) { | ||
var minimize = this | ||
@@ -114,47 +134,117 @@ , plugins = Object.keys(this.plugins); | ||
// | ||
// Reduce all provided elements to minimized HTML. | ||
// Ensure data can be reduced. | ||
// | ||
debug('Reducing %d parsed HTML elements', data.length); | ||
async.reduce(data, html, function reduce(html, element, step) { | ||
var children; | ||
data = data || []; | ||
/** | ||
* Close element in HTML. | ||
* | ||
* @param {Error} error | ||
* @param {String} html | ||
* @api private | ||
*/ | ||
function close(error, html) { | ||
if (error) { | ||
debug('Error received by #close %s', error.message); | ||
return step(error); | ||
} | ||
/** | ||
* (A)synchronously return or callback with HTML. | ||
* | ||
* @param {Error} error Runtime error | ||
* @param {String} html minimized HTML | ||
* @param {Function} cb optional callback | ||
* @returns {String} HTML | ||
* @api private | ||
*/ | ||
function returns(error, html, cb) { | ||
var fn = typeof cb === 'function'; | ||
html += minimize.helpers.close(element); | ||
step(null, html); | ||
if (error) { | ||
debug('Error received: %s', error.message); | ||
if (fn) cb(error); | ||
return minimize.emit('error', error); | ||
} | ||
/** | ||
* Enter element in HTML. | ||
* | ||
* @param {Error} error | ||
* @api private | ||
*/ | ||
function enter(error) { | ||
if (error) { | ||
debug('Error received by #enter %s', error.message); | ||
return done(error); | ||
if (fn) return cb(null, html); | ||
return html; | ||
} | ||
/** | ||
* For all children run same iterator. | ||
* | ||
* @param {Object} element Current element | ||
* @param {String} content Current minimized HTML, memo. | ||
* @param {Function} next Completion callback. | ||
* @returns {Function} Runner. | ||
* @api private | ||
*/ | ||
function traverser(element, content, next) { | ||
return function () { | ||
return minimize.traverse(element.children, content, sync, step(element, 'close', next)) | ||
}; | ||
} | ||
/** | ||
* Minimize single level of HTML. | ||
* | ||
* @param {Object} element Properties | ||
* @param {String} type Element type | ||
* @param {Function} cb Completion callback | ||
* @api private | ||
*/ | ||
function step(element, type, cb) { | ||
return function generate(error, html) { | ||
html += minimize.helpers[type](element); | ||
return returns(error, html, cb); | ||
} | ||
} | ||
/** | ||
* Traverse children of current element. | ||
* | ||
* @param {Object} element Properties | ||
* @param {Function} cb Completion callback | ||
* @api private | ||
*/ | ||
function run(element, cb) { | ||
return function level(error, content) { | ||
debug('Traversing children of element %s', element.name); | ||
if (sync) { | ||
return traverser(element, content, cb)(); | ||
} | ||
children = element.children; | ||
html += minimize.helpers[element.type](element); | ||
if (!children) return close(null, html); | ||
setImmediate(traverser(element, content, cb)); | ||
} | ||
} | ||
debug('Traversing %d children of element %s', children.length, element.name); | ||
setImmediate(function delay() { | ||
traverse.call(minimize, children, html, close); | ||
}); | ||
/** | ||
* Create opening string of element. | ||
* | ||
* @param {Object} element Properties | ||
* @param {String} html Current minimized HTML, memo. | ||
* @param {Function} cb Completion callback | ||
* @returns {String} Result | ||
* @api private | ||
*/ | ||
function open(element, html, cb) { | ||
return step(element, 'open', cb)(null, html); | ||
} | ||
/** | ||
* Create plugins for element. | ||
* | ||
* @param {Object} element Properties. | ||
* @return {Function} Plugin function to run. | ||
* @api private | ||
*/ | ||
function createPlug(element) { | ||
return function plug(plugin, fn) { | ||
fn = fn || minimize.emits('plugin'); | ||
debug('Running plugin for element %s', element.name); | ||
minimize.plugins[plugin].element.call(minimize, element, fn); | ||
} | ||
} | ||
/** | ||
* Reduce each HTML element and its children. | ||
* | ||
* @param {String} html Current compiled HTML, memo. | ||
* @param {Object} element Current element. | ||
* @param {Function} step Completion callback | ||
* @returns {String} minimized HTML. | ||
* @api private | ||
*/ | ||
function reduce(html, element, next) { | ||
// | ||
@@ -164,8 +254,23 @@ // Run the registered plugins before the element is processed. | ||
// | ||
if (!plugins.length) return enter(); | ||
async.eachSeries(plugins, function plug(plugin, next) { | ||
debug('Running plugin for element %s', element.name); | ||
minimize.plugins[plugin].element.call(minimize, element, next); | ||
}, enter); | ||
}, done); | ||
if (sync) { | ||
plugins.forEach(createPlug(element)); | ||
return open(element, html, run(element, next)); | ||
} | ||
async.eachSeries(plugins, createPlug(element), function finished(error) { | ||
if (error) return next(error); | ||
return open(element, html, run(element, next)); | ||
}); | ||
} | ||
// | ||
// Reduce all provided elements to minimized HTML. | ||
// | ||
if (sync) { | ||
debug('Synchronously reducing %d parsed HTML elements', data.length); | ||
return done(null, data.reduce(reduce, html)); | ||
} | ||
debug('Asynchronously reducing %d parsed HTML elements', data.length); | ||
return async.reduce(data, html, reduce, done); | ||
}; | ||
@@ -172,0 +277,0 @@ |
{ | ||
"name": "minimize", | ||
"version": "1.8.1", | ||
"version": "2.0.0", | ||
"description": "Minimize HTML", | ||
@@ -17,15 +17,15 @@ "main": "./lib/minimize", | ||
"argh": "~0.1.4", | ||
"async": "~1.5.2", | ||
"async": "~2.0.0-rc.6", | ||
"cli-color": "~1.1.0", | ||
"diagnostics": "~1.0.1", | ||
"emits": "~3.0.0", | ||
"htmlparser2": "~3.9.0", | ||
"htmlparser2": "~3.9.1", | ||
"node-uuid": "~1.4.7" | ||
}, | ||
"devDependencies": { | ||
"chai": "~3.4.1", | ||
"istanbul": "~0.4.2", | ||
"mocha": "~2.3.4", | ||
"pre-commit": "~1.1.2", | ||
"sinon": "~1.17.2", | ||
"chai": "~3.5.0", | ||
"istanbul": "~0.4.3", | ||
"mocha": "~2.5.3", | ||
"pre-commit": "~1.1.3", | ||
"sinon": "~1.17.4", | ||
"sinon-chai": "~2.8.0" | ||
@@ -32,0 +32,0 @@ }, |
@@ -34,20 +34,18 @@ # HTML minifier | ||
```javascript | ||
```js | ||
var Minimize = require('minimize') | ||
, minimize = new Minimize({ | ||
empty: true, // KEEP empty attributes | ||
cdata: true, // KEEP CDATA from scripts | ||
comments: true, // KEEP comments | ||
ssi: true, // KEEP Server Side Includes | ||
conditionals: true, // KEEP conditional internet explorer comments | ||
spare: true, // KEEP redundant attributes | ||
quotes: true, // KEEP arbitrary quotes | ||
loose: true, // KEEP one whitespace | ||
dom: { // options of !(htmlparser2)[https://github.com/fb55/htmlparser2] | ||
xmlMode: false, // Disables the special behavior for script/style tags (false by default) | ||
lowerCaseAttributeNames: true, // call .toLowerCase for each attribute name (true if xmlMode is `false`) | ||
lowerCaseTags: true // call .toLowerCase for each tag name (true if xmlMode is `false`) | ||
} | ||
}); | ||
, content = new Minimize().parse(content); | ||
console.log(content); | ||
``` | ||
#### Asynchronous usage | ||
Simply pass a callback as second argument. This is relevant is plugins perform | ||
asynchronous operations. | ||
```js | ||
var Minimize = require('minimize') | ||
, minimize = new Minimize(); | ||
minimize.parse(content, function (error, data) { | ||
@@ -58,6 +56,26 @@ console.log(data); | ||
#### Options | ||
List of available options. Note that all options are set to `false` by default and | ||
need to be explicitly enabled by providing `true`. For example `empty: true`. | ||
- [empty](#empty) | ||
- [cdata](#cdata) | ||
- [comments](#comments) | ||
- [ssi](#server-side-includes-ssi) | ||
- [conditionals](#conditionals) | ||
- [spare](#spare) | ||
- [quotes](#quotes) | ||
- [loose](#loose) | ||
- [dom](#dom) | ||
- xmlMode | ||
- lowerCaseAttributeNames | ||
- lowerCaseTags | ||
#### Custom parser | ||
Supplying a custom instance to do the HTML parsing is possible. I.e. this can | ||
be useful if the HTML contains SVG or if you need to specific options to the parser. | ||
```javascript | ||
```js | ||
var Minimize = require('minimize') | ||
@@ -79,3 +97,3 @@ , html = require('htmlparser2') | ||
**Empty** | ||
###### Empty | ||
@@ -86,3 +104,3 @@ Empty attributes can usually be removed, by default all are removed, excluded | ||
```javascript | ||
```js | ||
var Minimize = require('minimize') | ||
@@ -99,3 +117,3 @@ , minimize = new Minimize({ empty: true }); | ||
**CDATA** | ||
###### CDATA | ||
@@ -106,3 +124,3 @@ CDATA is only required for HTML to parse as valid XML. For normal webpages this | ||
```javascript | ||
```js | ||
var Minimize = require('minimize') | ||
@@ -119,3 +137,3 @@ , minimize = new Minimize({ cdata: true }); | ||
**Comments** | ||
###### Comments | ||
@@ -128,3 +146,3 @@ Comments inside HTML are usually beneficial while developing. Hiding your | ||
```javascript | ||
```js | ||
var Minimize = require('minimize') | ||
@@ -141,3 +159,3 @@ , minimize = new Minimize({ comments: true }); | ||
**Server Side Includes (SSI)** | ||
###### Server Side Includes (SSI) | ||
@@ -148,3 +166,3 @@ Server side includes are special set of commands that are support by several | ||
```javascript | ||
```js | ||
var Minimize = require('minimize') | ||
@@ -161,3 +179,3 @@ , minimize = new Minimize({ ssi: true }); | ||
**Conditionals** | ||
###### Conditionals | ||
@@ -169,3 +187,3 @@ Conditional comments only work in IE, and are thus excellently suited to give | ||
```javascript | ||
```js | ||
var Minimize = require('minimize') | ||
@@ -182,3 +200,3 @@ , minimize = new Minimize({ conditionals: true }); | ||
**Spare** | ||
###### Spare | ||
@@ -188,3 +206,3 @@ Spare attributes are of type boolean of which the value can be omitted in HTML5. | ||
```javascript | ||
```js | ||
var Minimize = require('minimize') | ||
@@ -201,3 +219,3 @@ , minimize = new Minimize({ spare: true }); | ||
**Quotes** | ||
###### Quotes | ||
@@ -208,3 +226,3 @@ Quotes are always added around attributes that have spaces or an equal sign in | ||
```javascript | ||
```js | ||
var Minimize = require('minimize') | ||
@@ -221,3 +239,3 @@ , minimize = new Minimize({ quotes: true }); | ||
**Loose** | ||
###### Loose | ||
@@ -229,3 +247,3 @@ Minimize will only keep whitespaces in structural elements and remove all other | ||
```javascript | ||
```js | ||
var Minimize = require('minimize') | ||
@@ -242,7 +260,7 @@ , minimize = new Minimize({ loose: true }); | ||
**dom** | ||
###### dom | ||
Minimize use !(htmlparser2)[https://github.com/fb55/htmlparser2] to parse the dom. The `dom` option permit to customize htmlparser2. | ||
```javascript | ||
```js | ||
var Minimize = require('minimize') | ||
@@ -265,7 +283,7 @@ , minimize = new Minimize({ dom: { lowerCaseAttributeNames: false }}); | ||
```javascript | ||
```js | ||
var Minimize = require('minimize') | ||
, minimize = new Minimize({ plugins: [{ | ||
id: 'remove', | ||
element: function element(node, next) { | ||
element: function element(node, next) { // callback is optional | ||
if (node.type === 'text') delete node.data; | ||
@@ -272,0 +290,0 @@ next(); |
@@ -52,3 +52,3 @@ 'use strict'; | ||
function err () { | ||
minimize.minifier('some error', []); | ||
minimize.minifier('id', false, 'some error', []); | ||
} | ||
@@ -66,4 +66,16 @@ | ||
it('can emit the parsed content to `minimize.read`'); | ||
it('emits the parsed content to `minimize.read`', function (done) { | ||
minimize.once('read', function (error, dom) { | ||
expect(error).to.equal(null); | ||
expect(dom).to.be.an('array'); | ||
done(); | ||
}); | ||
minimize.parse(html.interpunction, function (error, result) { | ||
expect(error).to.equal(null); | ||
expect(result).to.be.an('string'); | ||
}); | ||
}); | ||
it('should start traversing the DOM as soon as HTML parser is ready', function (done) { | ||
@@ -447,3 +459,3 @@ var emit = sinon.spy(minimize, 'emit'); | ||
it('should traverse the DOM object and return string', function (done) { | ||
minimize.traverse([html.element], '', function(error, result) { | ||
minimize.traverse([html.element], '', false, function(error, result) { | ||
expect(result).to.be.a('string'); | ||
@@ -459,11 +471,26 @@ expect(result).to.be.equal( | ||
describe('#parse', function () { | ||
it('should throw an error if no callback is provided', function () { | ||
function err () { | ||
minimize.parse(html.content, null); | ||
it('should parse the content synchronously without callback', function () { | ||
var result = minimize.parse(html.content); | ||
expect(result).to.be.a('string'); | ||
expect(result).to.be.equal( | ||
'<div class="slide nodejs"><h3>100% Node.js</h3><p>We are Node.js experts and the first hosting platform to build our full stack in node. We understand your node application better than anyone.</p></div>' | ||
); | ||
}); | ||
it('has the ability to use synchronous plugins to alter elements', function () { | ||
var pluggable = new Minimize({ plugins: [{ | ||
id: 'test', | ||
name: 'em', | ||
element: function element(node) { | ||
if (node.name === 'em') { | ||
delete node.children; | ||
} | ||
} | ||
}]}); | ||
expect(err).throws('No callback provided'); | ||
}); | ||
expect(pluggable.parse(html.br)).to.equal("<p class=slide><span><em></em></span><br><br><span>Your private npm registry makes managing them simple by giving you the power to work with a blacklist and a whitelist of public npm packages.</span></p>"); | ||
}); | ||
describe('#parse', function () { | ||
it('applies callback after DOM is parsed', function () { | ||
@@ -470,0 +497,0 @@ function fn () { } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
88507
1827
321
+ Addedasync@2.0.1(transitive)
+ Addedlodash@4.17.21(transitive)
- Removedasync@1.5.2(transitive)
Updatedasync@~2.0.0-rc.6
Updatedhtmlparser2@~3.9.1