Skip to content
This repository has been archived by the owner on Jan 21, 2024. It is now read-only.

Commit

Permalink
Merge 0074da2 into 925c30c
Browse files Browse the repository at this point in the history
  • Loading branch information
devinus committed Nov 11, 2017
2 parents 925c30c + 0074da2 commit 1dfe978
Show file tree
Hide file tree
Showing 11 changed files with 5,108 additions and 716 deletions.
5 changes: 1 addition & 4 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
{
"extends": "eslint:recommended",
"env": {
"node": true
}
"extends": "airbnb-base"
}
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodejs 9.1.0
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
language: node_js
node_js:
- 6
- 4
- lts/*
after_script:
- npm run coveralls
303 changes: 144 additions & 159 deletions lib/plugin.js
Original file line number Diff line number Diff line change
@@ -1,198 +1,183 @@
'use strict';
const Hashids = require('hashids');
const posthtml = require('posthtml');
const postcssSafeParser = require('postcss-safe-parser');
const postcssSelectorParser = require('postcss-selector-parser');

var Hashids = require('hashids');
var posthtml = require('posthtml');
var postcssSafeParser = require('postcss-safe-parser');
var postcssSelectorParser = require('postcss-selector-parser');
const { version } = require('../package');

var ALPHABET = 'abcdefghijklmnopqrstuvwxyz';
var VERSION = require('../package.json').version;
const ALPHABET = 'abcdefghijklmnopqrstuvwxyz';

function HTMLUglify(config) {
this.version = VERSION;

config = config || {};

var salt = config.salt || 'use the force harry';
this.hashids = new Hashids(salt, 0, ALPHABET);
this.whitelist = config.whitelist || [];
}

HTMLUglify.prototype.checkForStandardPointer = function(type, value, lookups) {
return lookups[type] && lookups[type][value];
};
class HTMLUglify {
constructor(config = {}) {
const salt = config.salt || 'use the force harry';
this.hashids = new Hashids(salt, 0, ALPHABET);
this.whitelist = config.whitelist || [];
this.version = version;
}

HTMLUglify.prototype.checkForAttributePointer = function(type, value, lookups) {
var typeLookups = lookups[type] || {};
var keys = Object.keys(typeLookups);
var pointer;
checkForStandardPointer(type, value, lookups) {
return lookups[type] && lookups[type][value];
}

keys.some(function(key) {
if (value.indexOf(key) !== -1) {
pointer = value.replace(key, typeLookups[key]);
return true;
}
return false;
});
checkForAttributePointer(type, value, lookups) {
const typeLookups = lookups[type] || {};
const keys = Object.keys(typeLookups);
let pointer;

return pointer;
};
keys.some((key) => {
if (value.indexOf(key) !== -1) {
pointer = value.replace(key, typeLookups[key]);
return true;
}
return false;
});

HTMLUglify.prototype.generatePointer = function(lookups) {
var idCount = Object.keys(lookups['id'] || {}).length;
var classCount = Object.keys(lookups['class'] || {}).length;
var counter = idCount + classCount;
return pointer;
}

return this.hashids.encode(counter);
};
generatePointer(lookups) {
const idCount = Object.keys(lookups['id'] || {}).length;
const classCount = Object.keys(lookups['class'] || {}).length;
const counter = idCount + classCount;

HTMLUglify.prototype.pointer = function(type, value, lookups) {
return this.checkForStandardPointer(type, value, lookups) ||
this.checkForAttributePointer(type, value, lookups) ||
this.generatePointer(lookups);
};
return this.hashids.encode(counter);
}

HTMLUglify.prototype.insertLookup = function(type, value, pointer, lookups) {
if (!lookups[type]) {
lookups[type] = {};
pointer(type, value, lookups) {
return this.checkForStandardPointer(type, value, lookups) ||
this.checkForAttributePointer(type, value, lookups) ||
this.generatePointer(lookups);
}
lookups[type][value] = pointer;
};

HTMLUglify.prototype.createLookup = function(type, value, lookups) {
var pointer;
if (value && !this.isWhitelisted(type, value)) {
pointer = this.pointer(type, value, lookups);
this.insertLookup(type, value, pointer, lookups);
insertLookup(type, value, pointer, lookups) {
if (!lookups[type]) {
lookups[type] = {};
}
lookups[type][value] = pointer;
}
return pointer;
};

HTMLUglify.prototype.isWhitelisted = function(type, value) {
switch (type) {
case 'class':
value = '.' + value;
break;
case 'id':
value = '#' + value;
break;
default:
break;
createLookup(type, value, lookups) {
let pointer;
if (value && !this.isWhitelisted(type, value)) {
pointer = this.pointer(type, value, lookups);
this.insertLookup(type, value, pointer, lookups);
}
return pointer;
}

return this.whitelist.indexOf(value) >= 0;
};
isWhitelisted(type, value) {
switch (type) {
case 'class':
value = '.' + value;
break;
case 'id':
value = '#' + value;
break;
default:
break;
}

HTMLUglify.prototype.pointerizeClass = function(node, lookups) {
var self = this;
var classes = node.attrs.class;
return this.whitelist.indexOf(value) >= 0;
}

if (classes) {
node.attrs.class = classes.split(/\s+/).map(function(value) {
var pointer = self.createLookup('class', value, lookups);
if (pointer) {
return pointer;
}
pointerizeClass(node, lookups) {
const classes = node.attrs.class;

return value;
}).join(' ');
}
};
if (classes) {
node.attrs.class = classes.split(/\s+/).map((value) => {
const pointer = this.createLookup('class', value, lookups);
if (pointer) {
return pointer;
}

HTMLUglify.prototype.pointerizeIdAndFor = function(type, node, lookups) {
var pointer = this.createLookup('id', node.attrs[type], lookups);
if (pointer) {
node.attrs[type] = pointer;
return value;
}).join(' ');
}
}
};

HTMLUglify.prototype.processRules = function(rules, lookups) {
var self = this;

rules.forEach(function(rule) {
// go deeper inside media rule to find css rules
if (rule.type === 'atrule' && (rule.name === 'media' || rule.name === 'supports')) {
self.processRules(rule.nodes, lookups);
} else if (rule.type === 'rule') {
postcssSelectorParser(function(selectors) {
selectors.walk(function(selector) {
var pointer;

if ((selector.type === 'class')
|| (selector.type === 'attribute' && selector.attribute === 'class')) {
pointer = self.createLookup('class', selector.value, lookups);
} else if ((selector.type === 'id')
|| (selector.type === 'attribute' && selector.attribute === 'id')
|| (selector.type === 'attribute' && selector.attribute === 'for')) {
pointer = self.createLookup('id', selector.value, lookups);
}

if (pointer) {
selector.value = pointer;
}
});

rule.selector = String(selectors);
}).process(rule.selector);
pointerizeIdAndFor(type, node, lookups) {
const pointer = this.createLookup('id', node.attrs[type], lookups);
if (pointer) {
node.attrs[type] = pointer;
}
});
};
}

HTMLUglify.prototype.rewriteElements = function(tree, lookups) {
var self = this;
processRules(rules, lookups) {
rules.forEach((rule) => {
// go deeper inside media rule to find css rules
if (rule.type === 'atrule' && (rule.name === 'media' || rule.name === 'supports')) {
this.processRules(rule.nodes, lookups);
} else if (rule.type === 'rule') {
postcssSelectorParser((selectors) => {
selectors.walk((selector) => {
let pointer;

if ((selector.type === 'class')
|| (selector.type === 'attribute' && selector.attribute === 'class')) {
pointer = this.createLookup('class', selector.value, lookups);
} else if ((selector.type === 'id')
|| (selector.type === 'attribute' && selector.attribute === 'id')
|| (selector.type === 'attribute' && selector.attribute === 'for')) {
pointer = this.createLookup('id', selector.value, lookups);
}

if (pointer) {
selector.value = pointer;
}
});

rule.selector = String(selectors);
}).process(rule.selector);
}
});
}

lookups = lookups || {};
rewriteElements(tree, lookups = {}) {
return tree.walk((node) => {
if (node.attrs) {
if (node.attrs.class) {
this.pointerizeClass(node, lookups);
}

return tree.walk(function(node) {
if (node.attrs) {
if (node.attrs.class) {
self.pointerizeClass(node, lookups);
}
if (node.attrs.id) {
this.pointerizeIdAndFor('id', node, lookups);
}

if (node.attrs.id) {
self.pointerizeIdAndFor('id', node, lookups);
if (node.attrs.for) {
this.pointerizeIdAndFor('for', node, lookups);
}
}
return node;
});
}

if (node.attrs.for) {
self.pointerizeIdAndFor('for', node, lookups);
rewriteStyles(tree, lookups = {}) {
return tree.walk((node) => {
if (node.tag === 'style' && node.content) {
const ast = postcssSafeParser([].concat(node.content).join(''));
this.processRules(ast.nodes, lookups);
node.content = ast.toString();
}
}
return node;
});
};

HTMLUglify.prototype.rewriteStyles = function(tree, lookups) {
var self = this;

lookups = lookups || {};

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;
});
};

HTMLUglify.prototype.process = function(tree) {
var lookups = {};

tree = this.rewriteStyles(tree, lookups);
tree = this.rewriteElements(tree, lookups);
return node;
});
}

return tree;
};
process(tree) {
const lookups = {};
tree = this.rewriteStyles(tree, lookups);
tree = this.rewriteElements(tree, lookups);
return tree;
}
}

module.exports = function(options) {
return function posthtmlUglify(tree) {
module.exports = (options) => {
return (tree) => {
return new HTMLUglify(options).process(tree);
}
};

module.exports.HTMLUglify = HTMLUglify;

module.exports.process = function(html, options) {
return posthtml().use(module.exports(options)).process(html, { sync: true });
module.exports.process = (html, options) => {
return posthtml().use(module.exports(options)).process(html, { sync: true });
};

0 comments on commit 1dfe978

Please sign in to comment.