From df6d1e6dac61d686a5fa428880c728fbed4f28bd Mon Sep 17 00:00:00 2001 From: Devin Torres Date: Mon, 25 Jan 2016 19:26:27 -0600 Subject: [PATCH 1/3] WIP posthtml --- lib/main.js | 104 +++-- test/benchmark.js | 9 +- test/main.js | 1025 ++++++++++++++++++++++----------------------- 3 files changed, 576 insertions(+), 562 deletions(-) diff --git a/lib/main.js b/lib/main.js index c8e005c..1caee08 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,7 +1,7 @@ 'use strict'; -var cheerio = require('cheerio'); var Hashids = require('hashids'); +var posthtml = require('posthtml'); var postcssSafeParser = require('postcss-safe-parser'); var postcssSelectorParser = require('postcss-selector-parser'); @@ -83,30 +83,27 @@ HTMLUglify.prototype.isWhitelisted = function(type, value) { return this.whitelist.indexOf(value) >= 0; }; -HTMLUglify.prototype.pointerizeClass = function($element, lookups) { +HTMLUglify.prototype.pointerizeClass = function(node, lookups) { var self = this; - var value = $element.attr('class'); + var value = node.attrs.class; if (value) { - var splitClasses = value.split(/\s+/); + var newClasses = value.split(/\s+/).map(function(value) { + return self.createLookup('class', value, lookups); + }).join(' '); - splitClasses.forEach(function(value) { - var pointer = self.createLookup('class', value, lookups); - if (pointer) { - $element.removeClass(value); - $element.addClass(pointer); - } - }); + node.attrs.class = newClasses; } }; -HTMLUglify.prototype.pointerizeIdAndFor = function(type, $element, lookups) { - var value = $element.attr(type); - - var pointer = this.createLookup('id', value, lookups); - if (pointer) { - $element.attr(type, pointer); - } +HTMLUglify.prototype.pointerizeIdAndFor = function(type, node, lookups) { + // var value = node.attrs[type]; + // var pointer = this.createLookup('id', value, lookups); + // if (pointer) { + // node.attrs[type] = pointer; + // } + var pointer = this.createLookup('id', node.attrs[type], lookups); + node.attrs[type] = pointer; }; HTMLUglify.prototype.processRules = function(rules, lookups) { @@ -141,49 +138,70 @@ HTMLUglify.prototype.processRules = function(rules, lookups) { }); }; -HTMLUglify.prototype.rewriteElements = function($, lookups) { +HTMLUglify.prototype.rewriteElements = function(tree, lookups) { var self = this; lookups = lookups || {}; - $('*[id]').each(function() { - self.pointerizeIdAndFor('id', $(this), lookups); - }); + // $('*[id]').each(function() { + // self.pointerizeIdAndFor('id', $(this), lookups); + // }); + // + // $('*[for]').each(function() { + // self.pointerizeIdAndFor('for', $(this), lookups); + // }); + // + // $('*[class]').each(function() { + // self.pointerizeClass($(this), lookups); + // }); + return tree.walk(function(node) { + if (node.attrs) { + if (node.attrs.id) { + self.pointerizeIdAndFor('id', node, lookups); + } - $('*[for]').each(function() { - self.pointerizeIdAndFor('for', $(this), lookups); - }); + if (node.attrs.for) { + self.pointerizeIdAndFor('for', node, lookups); + } - $('*[class]').each(function() { - self.pointerizeClass($(this), lookups); + if (node.attrs.class) { + self.pointerizeClass(node, lookups); + } + } + return node; }); - - return $; }; -HTMLUglify.prototype.rewriteStyles = function($, lookups) { +HTMLUglify.prototype.rewriteStyles = function(tree, lookups) { var self = this; lookups = lookups || {}; - $('style').each(function() { - var $style = $(this); - var ast = postcssSafeParser($style.text()); - self.processRules(ast.nodes, lookups); - $style.text(ast.toString()); + return tree.walk(function(node) { + if (node.tag === 'style' && node.content) { + var ast = postcssSafeParser([].concat(node.content).join('')); + self.processRules(ast.nodes, lookups); + node.content = ast.toString(); + } + return node; }); - - return $; }; -HTMLUglify.prototype.process = function(html) { +HTMLUglify.prototype.process = function(tree) { var lookups = {}; - var $ = cheerio.load(html); - $ = this.rewriteStyles($, lookups); - $ = this.rewriteElements($, lookups); + tree = this.rewriteStyles(tree, lookups); + tree = this.rewriteElements(tree, lookups); - return $.html(); + return tree; }; -module.exports = HTMLUglify; +module.exports = function(options) { + return function(tree) { + return new HTMLUglify(options).process(tree); + } +}; + +module.exports.process = function(html, options) { + return posthtml().use(module.exports(options)).process(html, { sync: true }); +}; diff --git a/test/benchmark.js b/test/benchmark.js index 7284ca1..b2881de 100644 --- a/test/benchmark.js +++ b/test/benchmark.js @@ -2,18 +2,19 @@ var fs = require('fs'); var Benchmark = require('benchmark'); -var HTMLUglify = require('../lib/main.js'); +var posthtml = require('posthtml'); +var uglify = require('../lib/main.js'); var suite = new Benchmark.Suite(); -var htmlUglify = new HTMLUglify(); +var htmlUglify = posthtml().use(uglify()); console.log('Running benchmark'); var html = fs.readFileSync('./test/test.html'); suite -.add('#process', function() { - htmlUglify.process(html); +.add('#process', function(done) { + htmlUglify.process(html, { sync: true }); }) .on('cycle', function(event) { console.log(String(event.target)); diff --git a/test/main.js b/test/main.js index 3becbef..e5d69cd 100644 --- a/test/main.js +++ b/test/main.js @@ -1,526 +1,521 @@ 'use strict'; var assert = require('chai').assert; -var cheerio = require('cheerio'); -var HTMLUglify = require('../lib/main.js'); - -var htmlUglify = new HTMLUglify(); +var htmlUglify = require('../lib/main.js'); describe('HTMLUglify', function() { - describe('#isWhitelisted', function() { - var whitelist; - var htmlUglify; - - beforeEach(function() { - whitelist = ['#theid', '.theclass', '#★', '.★']; - htmlUglify = new HTMLUglify({whitelist: whitelist}); - }); - it('returns true if id is in whitelist', function() { - var whitelisted = htmlUglify.isWhitelisted('id', 'theid'); - assert.isTrue(whitelisted); - }); - it('returns false if id is in the whitelist but only checking for classes', function() { - var whitelisted = htmlUglify.isWhitelisted('class', 'theid'); - assert.isFalse(whitelisted); - }); - it('returns true if class is in whitelist', function() { - var whitelisted = htmlUglify.isWhitelisted('class', 'theclass'); - assert.isTrue(whitelisted); - }); - it('returns true if id is in whitelist for a unicode character', function() { - var whitelisted = htmlUglify.isWhitelisted('id', '★'); - assert.isTrue(whitelisted); - }); - it('returns true if class is in whitelist for a unicode character', function() { - var whitelisted = htmlUglify.isWhitelisted('class', '★'); - assert.isTrue(whitelisted); - }); - }); - describe('#checkForStandardPointer', function() { - it('returns undefined when name not found', function() { - var lookups = { - 'class': { 'something': 'zzz' } - }; - var value = 'other'; - var pointer = htmlUglify.checkForStandardPointer(lookups, 'class', value); - - assert.isUndefined(pointer); - }); - it('returns pointer when found', function() { - var lookups = { - 'class': { 'something': 'zzz' } - }; - var value = 'something'; - var pointer = htmlUglify.checkForStandardPointer(lookups, 'class', value); - - assert.equal(pointer, 'zzz'); - }); - }); - describe('#checkForAttributePointer', function() { - it('returns undefined when not found', function() { - var lookups = { - 'class': { 'something': 'zzz' } - }; - var value = 'other'; - var pointer = htmlUglify.checkForAttributePointer(lookups, 'class', value); - - assert.isUndefined(pointer); - }); - it('returns the pointer when value contains same string as an existing lookup', function() { - var lookups = { - 'class': { 'something': 'zzz' } - }; - var value = 'somethingElse'; - var pointer = htmlUglify.checkForAttributePointer(lookups, 'class', value); - - assert.equal(pointer, 'zzzElse'); - }); - }); - describe('#generatePointer', function() { - it('returns xz for counter 0 lookups', function() { - var lookups = {}; - var pointer = htmlUglify.generatePointer(lookups); - assert.equal(pointer, 'xz'); - }); - it('returns wk for 1 lookups', function() { - var lookups = { 'id': { 'a': 'xz' } }; - var pointer = htmlUglify.generatePointer(lookups); - assert.equal(pointer, 'wk'); - }); - it('returns en for 2 lookups', function() { - var lookups = { 'id': { 'a': 'xz' }, 'class': { 'b': 'wk' } }; - var pointer = htmlUglify.generatePointer(lookups); - assert.equal(pointer, 'en'); - }); - }); - describe('#pointer', function() { - it('generates a new pointer', function() { - var lookups = {}; - var pointer = htmlUglify.pointer('class', 'newClass', {}); - assert.equal(pointer, 'xz', lookups); - }); - it('generates a new pointer given a different one exists', function() { - var lookups = { - 'class': { 'otherClass': 'wk' } - }; - var pointer = htmlUglify.pointer('class', 'newClass', lookups); - assert.equal(pointer, 'wk', lookups); - }); - it('generates a new pointer given a different one exists in a different attribute', function() { - var lookups = { - 'id': { 'someId': 'wk' } - }; - var pointer = htmlUglify.pointer('class', 'newClass', lookups); - assert.equal(pointer, 'wk', lookups); - }); - it('finds an existing class pointer', function() { - var lookups = { - 'class': { 'someClass': 'xz' } - }; - var pointer = htmlUglify.pointer('class', 'someClass', lookups); - assert.equal(pointer, 'xz', lookups); - }); - it('finds an existing id pointer', function() { - var lookups = { - 'id': { 'someId': 'en' } - }; - var pointer = htmlUglify.pointer('id', 'someId', lookups); - assert.equal(pointer, 'en'); - }); - it('finds a more complex existing pointer', function() { - var lookups = { - class: { - test: 'xz', - testOther: 'wk', - otratest: 'en' - } - }; - var pointer = htmlUglify.pointer('class', 'test', lookups); - - assert.equal(pointer, 'xz'); - }); - }); - - describe('#rewriteStyles', function() { - it('rewrites an id given lookups', function() { - var lookups = { 'id=abe': 'xz' }; - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ''); - }); - it('rewrites an id', function() { - var lookups = { }; - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ''); - }); - it('rewrites an id with the same name as the element', function() { - var lookups = {'id': {'label': 'ab' }}; - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ''); - }); - it('rewrites a for= given lookups', function() { - var lookups = { 'id': {'email': 'ab'} }; - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ""); - }); - it('does rewrites a for=', function() { - var lookups = {}; - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ""); - }); - it('rewrites a for= with quotes given lookups', function() { - var lookups = { 'id': {'email': 'ab'} }; - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ''); - }); - it('rewrites a for= with the same name as the element', function() { - var lookups = { 'id': { 'label': 'ab' }}; - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ''); - }); - it('rewrites an id= given lookups', function() { - var lookups = { 'id': {'email': 'ab'} }; - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ''); - }); - it('rewrites an id= with quotes given lookups', function() { - var lookups = { 'id': { 'email': 'ab' } }; - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ''); - }); - it('rewrites an id= with quotes and with the same name as the element', function() { - var lookups = { 'id': {'label': 'ab'} }; - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ''); - }); - it('rewrites a class given lookups', function() { - var lookups = { 'class': { 'email': 'ab' }}; - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ''); - }); - it('rewrites a class with the same name as the element', function() { - var lookups = { 'class': { 'label': 'ab' }}; - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ''); - }); - it('rewrites a class= given lookups', function() { - var lookups = { 'class': { 'email': 'ab' }}; - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ""); - }); - it('rewrites multi-selector rule', function() { - var lookups = { 'class': { 'email': 'ab' }}; - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ''); - }); - it('rewrites css media queries', function() { - var lookups = { 'id': { 'abe': 'wz' }}; - - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ''); - }); - it('rewrites nested css media queries', function() { - var lookups = { 'id': { 'abe': 'wz' }}; - - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($, lookups).html(); - assert.equal(results, ''); - }); - it('handles malformed syntax', function() { - var html = ''; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteStyles($).html(); - assert.equal(results, ''); - }); - }); - - describe('#pointerizeClass', function() { - var $element; - - beforeEach(function() { - var html = '

'; - var $ = cheerio.load(html); - - $element = $('p').first(); - }); - - it('works with empty lookups', function() { - var lookups = {}; - htmlUglify.pointerizeClass($element, lookups); - assert.deepEqual(lookups, { class: { one: 'xz', two: 'wk' } }); - }); - it('works with single lookup', function() { - var lookups = { class: { one: 'ab' } }; - htmlUglify.pointerizeClass($element, lookups); - assert.deepEqual(lookups, { class: { one: 'ab', two: 'wk' } }); - }); - it('works with whitelist', function() { - var lookups = {}; - htmlUglify.whitelist = [ '.two' ]; - htmlUglify.pointerizeClass($element, lookups); - assert.deepEqual(lookups, { class: { one: 'xz' } }); - }); - }); - describe('#pointerizeIdAndFor', function() { - var $element; - - beforeEach(function() { - var html = '

'; - var $ = cheerio.load(html); - - $element = $('p').first(); - }); - - it('works with empty lookups', function() { - var lookups = {}; - htmlUglify.pointerizeIdAndFor('id', $element, lookups); - assert.deepEqual(lookups, { id: { one: 'xz' } }); - }); - it('works with existing lookup', function() { - var lookups = { class: { one: 'ab' } }; - htmlUglify.pointerizeClass($element, lookups); - assert.deepEqual(lookups, { class: { one: 'ab' } }); - }); - it('works with whitelist', function() { - var lookups = {}; - htmlUglify.whitelist = [ '#one' ]; - htmlUglify.pointerizeClass($element, lookups); - assert.deepEqual(lookups, {}); - }); - }); - describe('#rewriteElements', function() { - it('rewrites an id', function() { - var html = '

Header

'; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteElements($).html(); - assert.equal(results, '

Header

'); - }); - it('rewrites a class', function() { - var html = '

Header

'; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteElements($).html(); - assert.equal(results, '

Header

'); - }); - it('rewrites a multiple classes', function() { - var html = '

Header

'; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteElements($).html(); - assert.equal(results, '

Header

'); - }); - it('rewrites a multiple classes with more than one space between them', function() { - var html = '

Header

'; - var $ = cheerio.load(html); - var results = htmlUglify.rewriteElements($).html(); - assert.equal(results, '

Header

'); - }); - it('rewrites a for', function() { - var html = '