Skip to content
This repository was archived by the owner on Jan 21, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
{
"extends": "airbnb-base"
"extends": "airbnb-base",
"env": {
"node": true
},
"rules": {
"no-param-reassign": "off"
}
}
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
nodejs 9.1.0
nodejs 9.2.0
2 changes: 0 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
'use strict';

module.exports = require('./lib/plugin');
78 changes: 35 additions & 43 deletions lib/plugin.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
const Hashids = require('hashids');
const posthtml = require('posthtml');
const postcssSafeParser = require('postcss-safe-parser');
const postcssSelectorParser = require('postcss-selector-parser');
const cssNameGenerator = require('css-class-generator');

const { version } = require('../package');

const ALPHABET = 'abcdefghijklmnopqrstuvwxyz';

class HTMLUglify {
constructor(config = {}) {
const salt = config.salt || 'use the force harry';
this.hashids = new Hashids(salt, 0, ALPHABET);
this.whitelist = config.whitelist || [];
constructor({ whitelist = [] } = {}) {
this.version = version;
this.whitelist = whitelist;
this.generators = {
id: cssNameGenerator(),
class: cssNameGenerator(),
};
}

checkForStandardPointer(type, value, lookups) {
static insertLookup(type, value, pointer, lookups) {
if (!lookups[type]) {
lookups[type] = {};
}
lookups[type][value] = pointer;
}

static checkForStandardPointer(type, value, lookups) {
return lookups[type] && lookups[type][value];
}

checkForAttributePointer(type, value, lookups) {
static checkForAttributePointer(type, value, lookups) {
const typeLookups = lookups[type] || {};
const keys = Object.keys(typeLookups);
let pointer;
Expand All @@ -35,49 +42,33 @@ class HTMLUglify {
return pointer;
}

generatePointer(lookups) {
const idCount = Object.keys(lookups['id'] || {}).length;
const classCount = Object.keys(lookups['class'] || {}).length;
const counter = idCount + classCount;
generatePointer(type) {
const { value } = this.generators[type].next();
if (this.isWhitelisted(type, value)) {
return this.generatePointer(type);
}

return this.hashids.encode(counter);
return value;
}

pointer(type, value, lookups) {
return this.checkForStandardPointer(type, value, lookups) ||
this.checkForAttributePointer(type, value, lookups) ||
this.generatePointer(lookups);
}

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

createLookup(type, value, lookups) {
let pointer;
if (value && !this.isWhitelisted(type, value)) {
pointer = this.pointer(type, value, lookups);
this.insertLookup(type, value, pointer, lookups);
this.constructor.insertLookup(type, value, pointer, lookups);
}
return pointer;
}

isWhitelisted(type, value) {
switch (type) {
case 'class':
value = '.' + value;
break;
case 'id':
value = '#' + value;
break;
default:
break;
}

return this.whitelist.indexOf(value) >= 0;
const prefix = type === 'id' ? '#' : '.';
return this.whitelist.includes(`${prefix}${value}`);
}

pointerizeClass(node, lookups) {
Expand Down Expand Up @@ -127,7 +118,7 @@ class HTMLUglify {
});

rule.selector = String(selectors);
}).process(rule.selector);
}).processSync(rule.selector);
}
});
}
Expand Down Expand Up @@ -156,28 +147,29 @@ class HTMLUglify {
if (node.tag === 'style' && node.content) {
const ast = postcssSafeParser([].concat(node.content).join(''));
this.processRules(ast.nodes, lookups);
node.content = ast.toString();
node.content = [ast.toString()];
}
return node;
});
}

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

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

module.exports.HTMLUglify = HTMLUglify;

module.exports.process = (html, options) => {
module.exports.process = (html, options) => { // eslint-disable-line arrow-body-style
return posthtml().use(module.exports(options)).process(html, { sync: true });
};
Loading