Comparing version 0.4.5 to 0.5.0
@@ -24,5 +24,9 @@ 'use strict'; | ||
, structural = /pre|textarea|code/ | ||
, interpunction = /[\.,!%;:\?\$]/ | ||
, start = new RegExp('^' + interpunction.source) | ||
, end = new RegExp(interpunction.source + '$'); | ||
, cdata = { | ||
start: /\/*<!\[CDATA\[/g | ||
, end: /\/*\]\]>/g | ||
} | ||
, interpunction = '.,!%;:?$' | ||
, start = new RegExp('^[' + interpunction + ']') | ||
, end = new RegExp('[-' + interpunction + ']$'); | ||
@@ -33,6 +37,7 @@ /** | ||
var config = { | ||
empty: false // remove empty attributes | ||
, cdata: false // strip CDATA from scripts | ||
, comments: false // remove comments | ||
, spare: false // remove redundant attributes | ||
empty: false // remove(false) or retain(true) empty attributes | ||
, cdata: false // remove(false) or retain(true) CDATA from scripts | ||
, comments: false // remove(false) or retain(true) comments | ||
, spare: false // remove(false) or retain(true) redundant attributes | ||
, quotes: false // remove(false) or retain(true) quotes if not required | ||
}; | ||
@@ -48,5 +53,4 @@ | ||
function Helpers (options) { | ||
if (options) config = util.mixin(config, options); | ||
this.config = util.mixin(util.clone(config), options || {}); | ||
// History of elements that require structure. | ||
this.ancestor = []; | ||
@@ -56,2 +60,15 @@ } | ||
/** | ||
* Wraps the attribute in quotes, or anything that needs them. | ||
* | ||
* @param {String} value | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Helpers.prototype.quote = function quote (value) { | ||
return (/[\s=]+/).test(value) || this.config.quotes | ||
? '"' + value + '"' | ||
: value; | ||
}; | ||
/** | ||
* Is an element inline or not. | ||
@@ -81,6 +98,25 @@ * | ||
? ' <' | ||
: '<') + element.data + '>'; | ||
: '<') + element.name + this.attributes(element) + '>'; | ||
}; | ||
/** | ||
* Loop set of attributes belonging to an element. Surrounds attributes with | ||
* quotes if required, omits if not. | ||
* | ||
* @param {Object} attr attributes of element | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Helpers.prototype.attributes = function attributes (element) { | ||
var attr = element.attribs | ||
, self = this; | ||
if (!attr || typeof attr !== 'object') return ''; | ||
return Object.keys(attr).reduce(function (result, key) { | ||
return result + ' ' + key + '=' + self.quote(attr[key]); | ||
}, ''); | ||
}; | ||
/** | ||
* Provide closing tag for element if required. | ||
@@ -106,3 +142,3 @@ * | ||
* @return {Boolean} | ||
* @api private | ||
* @api public | ||
*/ | ||
@@ -120,3 +156,3 @@ Helpers.prototype.isJS = function isJS (element) { | ||
* @return {Boolean} | ||
* @api private | ||
* @api public | ||
*/ | ||
@@ -140,13 +176,15 @@ Helpers.prototype.structure = function structure (element) { | ||
Helpers.prototype.text = function text (element, data) { | ||
var ancestors = this.ancestor.length; | ||
element = element.data.trim(); | ||
// If we have ancestors stored do not remove structure. | ||
if (!ancestors) element = element.replace(/\n/g, ' ').replace(/\s+/g, ' '); | ||
// If we have ancestors stored do not remove structure. | ||
if (!this.ancestor.length) { | ||
element = element.replace(/\n/g, ' ').replace(/\s+/g, ' '); | ||
// Remove CDATA from scripts. | ||
if (!this.config.cdata && ancestors && this.isJS(this.ancestor[ancestors - 1])) { | ||
element = element.replace(cdata.start, '').replace(cdata.end, ''); | ||
} | ||
// Check if the text requires flowing based on last output and interpunction. | ||
if (flow.test(data) && !start.test(element)) { | ||
element = ' ' + element; | ||
} | ||
if (flow.test(data) && !start.test(element)) element = ' ' + element; | ||
@@ -164,9 +202,19 @@ return element; | ||
Helpers.prototype.comment = function comment (element) { | ||
return !config.comments ? '' : '<!--' + element.data + '-->'; | ||
return !this.config.comments ? '' : '<!--' + element.data + '-->'; | ||
}; | ||
/** | ||
* Return parsed directive. | ||
* | ||
* @param {Object} element | ||
* @return {String} comment | ||
* @api public | ||
*/ | ||
Helpers.prototype.directive = function directive (element) { | ||
return '<' + element.data + '>'; | ||
}; | ||
/** | ||
* Define some proxies for easy external reference. | ||
*/ | ||
Helpers.prototype.directive = Helpers.prototype.tag; | ||
Helpers.prototype.script = Helpers.prototype.tag; | ||
@@ -184,3 +232,5 @@ | ||
Helpers.prototype.interpunction = interpunction; | ||
Helpers.prototype.config = config; | ||
Helpers.prototype.start = start; | ||
Helpers.prototype.end = end; | ||
Helpers.prototype.cdata = cdata; | ||
} | ||
@@ -187,0 +237,0 @@ |
'use strict'; | ||
var parser = require('htmlparser') | ||
var parser = require('htmlparser2') | ||
, EventEmitter = require('events').EventEmitter | ||
, events = new EventEmitter() | ||
, Helpers = require('./helpers') | ||
, htmlparser = new parser.Parser( | ||
new parser.DefaultHandler(minifier, { ignoreWhitespace: true }) | ||
) | ||
, helpers; | ||
, Helpers = require('./helpers'); | ||
/** | ||
* Minimizer constructor. | ||
* | ||
* @Constructor | ||
* @param {Object} options parsing options, optional | ||
* @api public | ||
*/ | ||
function Minimize (options) { | ||
// Pass options to helpers. | ||
this.helpers = new Helpers(options || {}); | ||
// Prepare the parser. | ||
this.htmlparser = new parser.Parser( | ||
new parser.DefaultHandler(this.minifier.bind(this), { ignoreWhitespace: true }) | ||
); | ||
} | ||
Minimize.prototype.__proto__ = EventEmitter.prototype; | ||
/** | ||
* Start parsing the provided content and call the callback. | ||
* | ||
* @param {String} content HTML | ||
* @param {Function} callback | ||
* @api public | ||
*/ | ||
Minimize.prototype.parse = function parse(content, callback) { | ||
if (typeof callback !== 'function') throw new Error('No callback provided'); | ||
// Listen to dom parsing as the | ||
this.once('parsed', callback); | ||
// Parse the HTML. | ||
this.htmlparser.parseComplete(content); | ||
}; | ||
/** | ||
* Parse traversable DOM to content. | ||
@@ -19,8 +50,8 @@ * | ||
*/ | ||
function minifier (error, dom) { | ||
Minimize.prototype.minifier = function minifier (error, dom) { | ||
if (error) throw new Error('Minifier failed to parse DOM', error); | ||
// DOM has been completely parsed, emit the results. | ||
events.emit('parsed', error, traverse(dom, '')); | ||
} | ||
this.emit('parsed', error, this.traverse(dom, '')); | ||
}; | ||
@@ -35,5 +66,5 @@ /** | ||
*/ | ||
function traverse (data, html) { | ||
return data.reduce(walk, html); | ||
} | ||
Minimize.prototype.traverse = function traverse (data, html) { | ||
return data.reduce(this.walk.bind(this), html); | ||
}; | ||
@@ -48,49 +79,14 @@ /** | ||
*/ | ||
function walk (html, element) { | ||
html += helpers[element.type](element, html); | ||
Minimize.prototype.walk = function walk (html, element) { | ||
html += this.helpers[element.type](element, html); | ||
return (element.children | ||
? traverse(element.children, html) | ||
: html) + helpers.close(element); | ||
} | ||
? this.traverse(element.children, html) | ||
: html) + this.helpers.close(element); | ||
}; | ||
/** | ||
* Expose the minimizer. | ||
* | ||
* @param {String} content HTML | ||
* @param {Function} callback | ||
* @param {Object} options parsing options | ||
* @api public | ||
*/ | ||
function minimize (content, callback, options) { | ||
if (typeof callback !== 'function') throw new Error('No callback provided'); | ||
// Pass options to helpers. | ||
helpers = new Helpers(options); | ||
// Listen to dom parsing as the | ||
events.once('parsed', callback); | ||
// Parse the HTML. | ||
htmlparser.parseComplete(content); | ||
} | ||
/** | ||
* Expose the minimize function by default. | ||
*/ | ||
module.exports = minimize; | ||
/** | ||
* Expose some additional modules while testing. | ||
*/ | ||
if (process.env.NODE_ENV === 'test') { | ||
module.exports = { | ||
minimize: minimize | ||
, helpers: helpers | ||
, walk: walk | ||
, traverse: traverse | ||
, minifier: minifier | ||
, events: events | ||
, htmlparser: htmlparser | ||
}; | ||
} | ||
module.exports = Minimize; |
{ | ||
"name": "minimize", | ||
"version": "0.4.5", | ||
"version": "0.5.0", | ||
"description": "Minimize HTML", | ||
@@ -10,3 +10,3 @@ "main": "./lib/minimize", | ||
"dependencies": { | ||
"htmlparser": "1.7.6", | ||
"htmlparser2": "2.6.x", | ||
"utile": "0.1.7" | ||
@@ -13,0 +13,0 @@ }, |
101
README.md
@@ -8,8 +8,14 @@ [![Build Status][status]](https://travis-ci.org/Moveo/minimize) | ||
Minimize is a HTML minifier based on the node-htmlparser. This depedency will | ||
ensure output is solid and correct. Currently, HTML minifier is only usuable | ||
server side. Client side minification will be added in a future release. | ||
ensure output is solid and correct. Minimize is focussed on HTML5 and will not | ||
support older HTML drafts. It is not worth the effort and the web should move | ||
forward. Currently, HTML minifier is only usuable server side. Client side | ||
minification will be added in a future release. | ||
## Features | ||
Upcoming in release 1.0 | ||
- command line usage support | ||
- increased configurability (element replacement, etc.) | ||
Upcoming in release 2.0 | ||
@@ -19,3 +25,2 @@ | ||
- client side minification support | ||
- increased configurability (quote switcher, element replacement) | ||
@@ -27,5 +32,5 @@ ## Usage | ||
``` | ||
var minimize = require('minimize') | ||
, options = { | ||
```javascript | ||
var Minimize = require('minimize') | ||
minimize = new Minimize({ | ||
empty: true // DO NOT remove empty attributes | ||
@@ -35,8 +40,8 @@ , cdata: true // DO NOT strip CDATA from scripts | ||
, spare: true // DO NOT remove redundant attributes | ||
}; | ||
, quotes: true // DO NOT remove arbitrary quotes | ||
}); | ||
minimize(content, function (data) { | ||
minimize.parse(content, function (error, data) { | ||
console.log(data); | ||
}, options); | ||
}); | ||
``` | ||
@@ -46,13 +51,71 @@ | ||
**empty** | ||
**Empty** | ||
**cdata** | ||
**CDATA** | ||
**comments** | ||
CDATA is only required for HTML to parse as valid XML. For normal webpages this | ||
is rarely the case, thus CDATA around javascript can be omitted. Below is an | ||
example on how to do just that. | ||
**spare** | ||
```javascript | ||
var Minimize = require('minimize') | ||
minimize = new Minimize({ cdata: true }); | ||
minimize.parse( | ||
'<script type="text/javascript">\n//<![CDATA[\n...code...\n//]]>\n</script>' | ||
, function (error, data) { | ||
// data output: <script type=text/javascript>\n...code...\n</script> | ||
} | ||
); | ||
``` | ||
**Comments** | ||
Comments inside HTML are usually beneficial while developing. Hiding your | ||
comments in production is sane, safe and will reduce data transfer. If you | ||
ensist on keeping them, for instance to show a nice easter egg, set the option | ||
to true. | ||
```javascript | ||
var Minimize = require('minimize') | ||
minimize = new Minimize({ comments: true }); | ||
minimize.parse( | ||
'<!-- some HTML comment -->\n <div class="slide nodejs">' | ||
, function (error, data) { | ||
// data output: <!-- some HTML comment --><div class="slide nodejs"> | ||
} | ||
); | ||
``` | ||
**Spare** | ||
**Qoutes** | ||
Quotes are always added around attributes that have spaces or an equal sign in | ||
their value. But if you require quotes around all attributes, simply pass | ||
quotes:true, like below. | ||
```javascript | ||
var Minimize = require('minimize') | ||
minimize = new Minimize({ quotes: true }); | ||
minimize.parse( | ||
'<p class="paragraph" id="title">\n Some content\n </p>' | ||
, function (error, data) { | ||
// data output: <p class="paragraph" id="title">Some content</p> | ||
} | ||
); | ||
``` | ||
## Tests | ||
Tests can be easily run by using either of the following commands. Travis.ci is | ||
used for continous integration. | ||
```bash | ||
make test | ||
make test-watch | ||
npm test | ||
``` | ||
@@ -67,8 +130,10 @@ ## Benchmarks | ||
Kangax minifier also provides some additional options like linting. Minimize | ||
will retain strictly to the business of minifying. | ||
will retain strictly to the business of minifying. Minimize is already used in | ||
production by [Nodejitsu][nodejitsu]. | ||
[HTMLparser](tauto) of Tautologistics is used to create an object representation | ||
[node-htmlparser](fb55) of fb55 is used to create an object representation | ||
of the DOM. | ||
[kangax]: https://github.com/kangax/html-minifier | ||
[tauto]: https://github.com/tautologistics/node-htmlparser | ||
[kangax]: https://github.com/kangax/html-minifier/ | ||
[fb55]: https://github.com/fb55/node-htmlparser/ | ||
[nodejitsu]: http://www.nodejitsu.com/ |
@@ -8,46 +8,71 @@ { | ||
, "spacing": "<strong>npm</strong>. You don't have to worry\n about installing npm since it comes bundled with Node.js.\n\n <pre class=\"copy\">$ <span>npm install jitsu -g</span><a href=\"#\"><s class=\"ss-layers\" role=\"presentation\"></s> copy</a></pre>\n" | ||
, "full": "<!doctype html>\n<html class=\"no-js\">\n<head>\n</head>\n<body class=\"container\">\n<section class=\"navigation\" id=\"navigation\">\n <nav class=\"row\">\n <h1>\n <a href=\"/\" class=\"logo\" title=\"Back to the homepage\">Nodejitsu</a>\n </h1>\n <a href=\"#navigation\" class=\"mobile btn ss-rows\"></a>\n\n\n\n <a href=\"/paas\" >\n Cloud\n </a>\n\n <a href=\"/enterprise/private-cloud\" >\n Enterprise\n </a>\n </section>\n <input type=\"text\" name=\"temp\">\n</body>\n </html>" | ||
, "cdata": "<script type=\"text/javascript\">\n//<![CDATA[\n...code...\n//]]>\n</script>" | ||
, "doctype": { | ||
"raw": "!doctype html" | ||
, "data": "!doctype html" | ||
"data": "!doctype html" | ||
, "type": "directive" | ||
, "name": "!doctype" | ||
} | ||
, "block": { | ||
"type": "tag" | ||
, "name": "section" | ||
, "attribs": null | ||
, "children": null | ||
, "prev": null | ||
, "next": null | ||
, "parent": null | ||
} | ||
, "inline": { | ||
"raw": "strong" | ||
, "data": "strong" | ||
, "type": "tag" | ||
"type": "tag" | ||
, "name": "strong" | ||
, "children": null | ||
, "prev": null | ||
, "next": null | ||
, "parent": null | ||
} | ||
, "singular": { | ||
"raw": "input type=text" | ||
, "data": "input type=text" | ||
"attribs": { | ||
"type": "text" | ||
, "name": "temp" | ||
} | ||
, "type": "tag" | ||
, "name": "input" | ||
, "children": [] | ||
, "prev": null | ||
, "next": null | ||
, "parent": null | ||
} | ||
, "attribs": { | ||
"type": "text" | ||
, "name": "temp-name" | ||
, "class": "some classes with spaces" | ||
, "href": "http://without.params.com" | ||
, "hrefparam": "http://with.params.com?test=test" | ||
} | ||
, "script": { | ||
"raw": "script type=\"text/javascript\"" | ||
, "data": "script type=\"text/javascript\"" | ||
, "type": "script" | ||
"type": "script" | ||
, "name": "script" | ||
, "children": null | ||
, "prev": null | ||
, "next": null | ||
, "parent": null | ||
} | ||
, "structure": { | ||
"raw": "textarea" | ||
, "data": "textarea" | ||
, "type": "tag" | ||
"type": "tag" | ||
, "name": "textarea" | ||
, "children": null | ||
, "prev": null | ||
, "next": null | ||
, "parent": null | ||
} | ||
, "text": { | ||
"raw": "some random text" | ||
, "data": "some random text" | ||
"data": "some random text" | ||
, "type": "text" | ||
} | ||
, "multiline": { | ||
"raw": "some additional lines.\n\n some random text, and alot of spaces" | ||
, "data": "some additional lines.\n\n some random text, and alot of spaces" | ||
"data": "some additional lines.\n\n some random text, and alot of spaces" | ||
, "type": "text" | ||
} | ||
, "element": { | ||
"raw": "html class=no-js" | ||
, "data": "html class=no-js" | ||
, "type": "tag" | ||
"type": "tag" | ||
, "name": "html" | ||
@@ -57,13 +82,17 @@ , "attribs": { | ||
} | ||
, "prev": null | ||
, "next": null | ||
, "parent": null | ||
, "children": [ | ||
{ | ||
"raw": "head" | ||
, "data": "head" | ||
, "type": "tag" | ||
"type": "tag" | ||
, "name": "head" | ||
, "attribs": {} | ||
, "children": [] | ||
, "prev": null | ||
, "next": null | ||
, "parent": null | ||
} | ||
, { | ||
"raw": "body class=container" | ||
, "data": "body class=container" | ||
, "type": "tag" | ||
"type": "tag" | ||
, "name": "body" | ||
@@ -73,2 +102,6 @@ , "attribs": { | ||
} | ||
, "children": [] | ||
, "prev": null | ||
, "next": null | ||
, "parent": null | ||
} | ||
@@ -75,0 +108,0 @@ ] |
@@ -0,1 +1,2 @@ | ||
/*global beforeEach, afterEach*/ | ||
'use strict'; | ||
@@ -61,7 +62,25 @@ | ||
it('which has a regular expression named interpunction', function () { | ||
it('which has a regular expression named cdata', function () { | ||
expect(helpers).to.have.property('cdata'); | ||
expect(helpers.cdata).to.be.a('object'); | ||
expect(helpers.cdata.start).to.be.a('regexp'); | ||
expect(helpers.cdata.end).to.be.a('regexp'); | ||
}); | ||
it('which has a string with interpunction listed', function () { | ||
expect(helpers).to.have.property('interpunction'); | ||
expect(helpers.interpunction).to.be.a('regexp'); | ||
expect(helpers.interpunction).to.be.a('string'); | ||
expect(helpers.interpunction).to.be.equal('.,!%;:?$'); | ||
}); | ||
it('which has a regexp named start which triggers on interpunction', function () { | ||
expect(helpers).to.have.property('start'); | ||
expect(helpers.start).to.be.a('regexp'); | ||
}); | ||
it('which has a regexp named end which has dashes appended', function () { | ||
expect(helpers).to.have.property('end'); | ||
expect(helpers.end).to.be.a('regexp'); | ||
}); | ||
it('which has an inline element reference', function () { | ||
@@ -83,7 +102,67 @@ expect(helpers).to.have.property('inline'); | ||
describe('function directive', function () { | ||
it('returns a string wrapped with < >', function () { | ||
expect(helpers.directive(html.doctype)).to.be.equal('<!doctype html>'); | ||
}); | ||
}); | ||
describe('function attributes', function () { | ||
var quote; | ||
beforeEach(function () { | ||
quote = sinon.spy(helpers, 'quote'); | ||
}); | ||
afterEach(function () { | ||
quote.restore(); | ||
}); | ||
it('should convert the attribute object to string', function () { | ||
expect(helpers.attributes(html.singular)).to.be.equal(' type=text name=temp'); | ||
expect(quote).to.be.calledTwice; | ||
}); | ||
it('should return early if element has no attributes', function () { | ||
expect(helpers.attributes(html.block)).to.be.equal(''); | ||
expect(quote.callCount).to.be.equal(0); | ||
}); | ||
}); | ||
describe('function quote', function () { | ||
var quote; | ||
beforeEach(function () { | ||
quote = sinon.spy(helpers, 'quote'); | ||
}); | ||
afterEach(function () { | ||
quote.restore(); | ||
}); | ||
it('should omit quotes if an attribute does not require any', function () { | ||
expect(helpers.quote(html.attribs.href)).to.be.equal('http://without.params.com'); | ||
expect(helpers.quote(html.attribs.name)).to.be.equal('temp-name'); | ||
expect(helpers.quote(html.attribs.type)).to.be.equal('text'); | ||
}); | ||
it('should add quotes to attributes with spaces or =', function () { | ||
expect(helpers.quote(html.attribs.class)).to.be.equal('"some classes with spaces"'); | ||
expect(helpers.quote(html.attribs.hrefparam)).to.be.equal('"http://with.params.com?test=test"'); | ||
}); | ||
it('should always retain quotes if configured', function () { | ||
var configurable = new Helpers({ quotes: true }); | ||
expect(configurable.quote(html.attribs.name)).to.be.equal('"temp-name"'); | ||
expect(configurable.quote(html.attribs.type)).to.be.equal('"text"'); | ||
expect(configurable.quote(html.attribs.class)).to.be.equal('"some classes with spaces"'); | ||
}); | ||
}); | ||
describe('function tag', function () { | ||
var structure; | ||
var structure, attr; | ||
beforeEach(function () { | ||
structure = sinon.spy(helpers, 'structure'); | ||
attr = sinon.spy(helpers, 'attributes'); | ||
}); | ||
@@ -93,6 +172,7 @@ | ||
structure.restore(); | ||
attr.restore(); | ||
}); | ||
it('returns a string wrapped with < >', function () { | ||
expect(helpers.tag(html.doctype)).to.be.equal('<!doctype html>'); | ||
expect(helpers.tag(html.block)).to.be.equal('<section>'); | ||
@@ -102,16 +182,15 @@ expect(structure).to.be.calledOnce; | ||
it('calls helpers#attributes once and appends content behind name', function () { | ||
expect(helpers.tag(html.singular)).to.be.equal(' <input type=text name=temp>'); | ||
expect(attr).to.be.calledAfter(structure); | ||
expect(attr).to.be.calledOnce; | ||
}); | ||
it('is callable by element.type through proxy', function () { | ||
var elements = [ | ||
html.doctype | ||
, html.singular | ||
, html.script | ||
]; | ||
expect(helpers.script(html.script, '')).to.be.equal( | ||
'<script>' | ||
); | ||
elements.forEach(function loopElements (element) { | ||
expect(helpers[element.type](element, html.element, '')).to.be.equal( | ||
'<' + element.data + '>' | ||
); | ||
}); | ||
expect(structure).to.be.calledThrice; | ||
expect(structure).to.be.calledOnce; | ||
}); | ||
@@ -122,3 +201,3 @@ | ||
expect(helpers.tag(html.inline, 'text')).to.be.equal( | ||
' <' + html.inline.data + '>' | ||
' <' + html.inline.name + '>' | ||
); | ||
@@ -131,3 +210,3 @@ | ||
expect(helpers.tag(html.inline, 'text.')).to.be.equal( | ||
' <' + html.inline.data + '>' | ||
' <' + html.inline.name + '>' | ||
); | ||
@@ -140,3 +219,3 @@ | ||
expect(helpers.tag(html.inline, 'text</b>')).to.be.equal( | ||
' <' + html.inline.data + '>' | ||
' <' + html.inline.name + '>' | ||
); | ||
@@ -360,2 +439,42 @@ | ||
describe('regular expression start', function () { | ||
it('is a valid regular expression', function () { | ||
function regexp () { return new RegExp(helpers.start); } | ||
expect(regexp).to.not.throw(Error); | ||
}); | ||
it('matches interpunction without dashes', function () { | ||
expect(helpers.start.test('-')).to.be.false; | ||
expect(helpers.start.test('.')).to.be.true; | ||
}); | ||
}); | ||
describe('regular expression end', function () { | ||
it('is a valid regular expression', function () { | ||
function regexp () { return new RegExp(helpers.end); } | ||
expect(regexp).to.not.throw(Error); | ||
}); | ||
it('matches interpunction with dashes', function () { | ||
expect(helpers.end.test('-')).to.be.true; | ||
expect(helpers.end.test('.')).to.be.true; | ||
}); | ||
}); | ||
describe('regular expression cdata', function () { | ||
it('is a valid regular expression', function () { | ||
function regexp () { | ||
new RegExp(helpers.cdata.start); | ||
new RegExp(helpers.cdata.end); | ||
} | ||
expect(regexp).to.not.throw(Error); | ||
}); | ||
it('matches closing and ending parts of CDATA', function () { | ||
expect(helpers.cdata.start.test('//<![CDATA[')).to.be.true; | ||
expect(helpers.cdata.end.test('//]]>')).to.be.true; | ||
}); | ||
}); | ||
describe('regular expression flow', function () { | ||
@@ -381,3 +500,3 @@ it('is a valid regular expression', function () { | ||
describe('has options', function () { | ||
it('which are all true by default', function () { | ||
it('which are all false by default', function () { | ||
for (var key in helpers.config) { | ||
@@ -389,6 +508,6 @@ expect(helpers.config[key]).to.be.false; | ||
it('which are overideable with options', function () { | ||
var test = new Helpers({ empty: false }); | ||
expect(test.config.empty).to.be.false; | ||
var test = new Helpers({ empty: true }); | ||
expect(test.config.empty).to.be.true; | ||
}); | ||
}); | ||
}); |
@@ -8,3 +8,4 @@ 'use strict'; | ||
, html = require('./fixtures/html.json') | ||
, minimize = require('../lib/minimize'); | ||
, Minimize = require('../lib/minimize') | ||
, minimize = new Minimize(); | ||
@@ -16,5 +17,4 @@ chai.use(sinonChai); | ||
describe('is module', function () { | ||
it('which has minify', function () { | ||
expect(minimize).to.have.property('minimize'); | ||
expect(minimize.minimize).to.be.a('function'); | ||
it('which has a constructor', function () { | ||
expect(Minimize).to.be.a('function'); | ||
}); | ||
@@ -32,2 +32,7 @@ | ||
it('which has parse', function () { | ||
expect(minimize).to.have.property('parse'); | ||
expect(minimize.parse).to.be.a('function'); | ||
}); | ||
it('which has walk', function () { | ||
@@ -45,12 +50,2 @@ expect(minimize).to.have.property('walk'); | ||
describe('function minifier', function () { | ||
// Options need restoring as static content is exposed by prototype. | ||
afterEach(function () { | ||
minimize.minimize([], function () { }, { | ||
empty: false | ||
, cdata: false | ||
, comments: false | ||
, spare: false | ||
}); | ||
}); | ||
it('throws an error if HTML parsing failed', function () { | ||
@@ -65,3 +60,3 @@ function err () { | ||
it('should start traversing the DOM as soon as HTML parser is ready', function () { | ||
var emit = sinon.spy(minimize.events, 'emit'); | ||
var emit = sinon.spy(minimize, 'emit'); | ||
@@ -81,4 +76,4 @@ minimize.minifier(null, []); | ||
it('should handle inline flow properly', function (done) { | ||
minimize.minimize(html.interpunction, function (error, result) { | ||
expect(result).to.equal('<h3>Become a partner</h3><p>Interested in being part of the solution? <a href="/company/contact">Contact Nodejitsu to discuss</a>.</p>'); | ||
minimize.parse(html.interpunction, function (error, result) { | ||
expect(result).to.equal('<h3>Become a partner</h3><p>Interested in being part of the solution? <a href=/company/contact>Contact Nodejitsu to discuss</a>.</p>'); | ||
done(); | ||
@@ -89,11 +84,12 @@ }); | ||
it('should be configurable to retain comments', function (done) { | ||
minimize.minimize(html.comment, function (error, result) { | ||
var commentable = new Minimize({ comments: true }); | ||
commentable.parse(html.comment, function (error, result) { | ||
expect(result).to.equal('<!-- some HTML comment --><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>'); | ||
done(); | ||
}, { comments: true }); | ||
}); | ||
}); | ||
it('should leave structural elements (like scripts and code) intact', function (done) { | ||
minimize.minimize(html.code, function (error, result) { | ||
expect(result).to.equal("<code class=\"copy\"><span>var http = require('http');\nhttp.createServer(function (req, res) {\n res.writeHead(200, {'Content-Type': 'text/plain'});\n res.end('hello, i know nodejitsu');\n})listen(8080);</span> <a href=\"#\"><s class=\"ss-layers\" role=\"presentation\"></s> copy</a></code>"); | ||
minimize.parse(html.code, function (error, result) { | ||
expect(result).to.equal("<code class=copy><span>var http = require('http');\nhttp.createServer(function (req, res) {\n res.writeHead(200, {'Content-Type': 'text/plain'});\n res.end('hello, i know nodejitsu');\n})listen(8080);</span> <a href=#><s class=ss-layers role=presentation></s> copy</a></code>"); | ||
done(); | ||
@@ -104,4 +100,4 @@ }); | ||
it('should replace newlines between text with spaces', function (done) { | ||
minimize.minimize(html.newlines, function (error, result) { | ||
expect(result).to.equal("<li>We're <a href=\"http://nodejitsu.com\">Nodejitsu</a>, and we can give you scalable, fault-tolerant cloud hosting for your Node.js apps - and we're the best you'll find.</li>"); | ||
minimize.parse(html.newlines, function (error, result) { | ||
expect(result).to.equal("<li>We're <a href=http://nodejitsu.com>Nodejitsu</a>, and we can give you scalable, fault-tolerant cloud hosting for your Node.js apps - and we're the best you'll find.</li>"); | ||
done(); | ||
@@ -112,4 +108,4 @@ }); | ||
it('should prepend spaces inside structural elements if required', function (done) { | ||
minimize.minimize(html.spacing, function (error, result) { | ||
expect(result).to.equal("<strong>npm</strong>. You don't have to worry about installing npm since it comes bundled with Node.js.<pre class=\"copy\">$ <span>npm install jitsu -g</span> <a href=\"#\"><s class=\"ss-layers\" role=\"presentation\"></s> copy</a></pre>"); | ||
minimize.parse(html.spacing, function (error, result) { | ||
expect(result).to.equal("<strong>npm</strong>. You don't have to worry about installing npm since it comes bundled with Node.js.<pre class=copy>$ <span>npm install jitsu -g</span> <a href=#><s class=ss-layers role=presentation></s> copy</a></pre>"); | ||
done(); | ||
@@ -119,3 +115,30 @@ }); | ||
it('should be configurable to retain CDATA'); | ||
it('should parse the full stack', function (done) { | ||
minimize.parse(html.full, function (error, result) { | ||
expect(result).to.equal("<!doctype html><html class=no-js><head></head><body class=container><section class=navigation id=navigation><nav class=row><h1><a href=/ class=logo title=\"Back to the homepage\">Nodejitsu</a></h1> <a href=#navigation class=\"mobile btn ss-rows\"></a> <a href=/paas>Cloud</a> <a href=/enterprise/private-cloud>Enterprise</a></nav></section> <input type=text name=temp></body></html>"); | ||
done(); | ||
}); | ||
}); | ||
it('should prepend space if inline element is preluded by text', function (done) { | ||
minimize.parse('some text -\n <strong class="lol">keyword</strong>\n - more text', function (error, result) { | ||
expect(result).to.equal("some text - <strong class=lol>keyword</strong> - more text"); | ||
done(); | ||
}); | ||
}); | ||
it('should remove CDATA from scripts', function (done) { | ||
minimize.parse(html.cdata, function (error, result) { | ||
expect(result).to.equal("<script type=text/javascript>\n...code...\n</script>"); | ||
done(); | ||
}); | ||
}); | ||
it('should be configurable to retain CDATA', function (done) { | ||
var cdata = new Minimize({ cdata: true }); | ||
cdata.parse(html.cdata, function (error, result) { | ||
expect(result).to.equal("<script type=text/javascript>//<![CDATA[\n...code...\n//]]></script>"); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
@@ -154,3 +177,3 @@ | ||
function err () { | ||
minimize.minimize(html.content, null); | ||
minimize.parse(html.content, null); | ||
} | ||
@@ -163,5 +186,5 @@ | ||
function fn () { } | ||
var once = sinon.spy(minimize.events, 'once'); | ||
var once = sinon.spy(minimize, 'once'); | ||
minimize.minimize(html.content, fn); | ||
minimize.parse(html.content, fn); | ||
expect(once).to.be.calledOnce; | ||
@@ -180,3 +203,3 @@ | ||
minimize.minimize(html.content, function () {}); | ||
minimize.parse(html.content, function () {}); | ||
parser.restore(); | ||
@@ -183,0 +206,0 @@ }); |
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
40911
12
944
134
2
+ Addedhtmlparser2@2.6.x
+ Addeddomelementtype@1.3.1(transitive)
+ Addeddomhandler@2.0.3(transitive)
+ Addeddomutils@1.0.1(transitive)
+ Addedhtmlparser2@2.6.0(transitive)
- Removedhtmlparser@1.7.6
- Removedhtmlparser@1.7.6(transitive)