This repository has been archived by the owner on Jan 21, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
5,108 additions
and
716 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,3 @@ | ||
{ | ||
"extends": "eslint:recommended", | ||
"env": { | ||
"node": true | ||
} | ||
"extends": "airbnb-base" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
nodejs 9.1.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }); | ||
}; |
Oops, something went wrong.