JS algoritmus na minifikáciu JavaScriptu
Pred niekoľkými týždňami som používal v Total.js algoritmus na minifikáciu JavaScriptu od pána Crockforda a bol som s ním dlhodobo spokojný. No nedávno som napísal správičku ohľadom Total.js na server Root, kde som bol upozornený, že JS minifikátor používa v licencii frázu "Good, not Evil". Keďže s touto frázou malo problém viacej vývojárov / firiem, tak som začal ihneď konať a povedal som si, že Total.js musí mať jasne definovanú licenciu.
Priznám sa, že ani vo sne ma nenapadlo, že jedného dňa budem písať vlastný JavaScriptový minifikátor. Našťastie sa to podarilo a ešte lepšie ako som očakával. Dovolím si povedať, že nižšie uvedený algoritmus minifikuje lepšie JavaScriptový kód ako algoritmus od pána Crockforda.
Riešenie problémov
Keď som sa zamyslel nad algoritmom, tak som si povedal, že vlastne na tom nič extra ťažké nemôže byť, stačí vyriešiť problémy s deklaráciou regulárnych výrazov a deklaráciu stringových hodnôt v znakoch "
a '
.
Funkčnosť
- odstraňuje komentáre
- odstraňuje nadbytočné medzery, tabulátory
- funguje aj s ES6
- MIT licencia
// The MIT License
// Copyright 2012-2016 (c) Peter Širka <petersirka@gmail.com>
function minify(data) {
var index = 0;
var output = [];
var isCS = false;
var isCI = false;
var alpha = /[0-9a-z]/i;
var chars = /[a-z]/i;
var white = /\W/;
var skip = { '$': true, '_': true };
var regexp = false;
var scope;
var prev;
var next;
var last;
while (true) {
// For web browsers
var c = data.substring(index, index + 1);
var prev = data.substring(index - 1, index);
var next = data.substring(index + 1, index + 2);
// For Node.js
// var c = data[index];
// var prev = data[index - 1];
// var next = data[index + 1];
index++;
if (c === undefined)
break;
if (!scope) {
if (!regexp) {
if (c === '/' && next === '*') {
isCS = true;
continue;
} else if (c === '*' && next === '/') {
isCS = false;
index++;
continue;
}
if (isCS)
continue;
if (c === '/' && next === '/') {
isCI = true;
continue;
} else if (isCI && (c === '\n' || c === '\r')) {
isCI = false;
alpha.test(last) && output.push(' ');
last = '';
continue;
}
if (isCI)
continue;
}
if (c === '\t' || c === '\n' || c === '\r') {
if (!last || !alpha.test(last))
continue;
output.push(' ');
last = '';
continue;
}
if (!regexp && (c === ' ' && (white.test(prev) || white.test(next)))) {
if (!skip[prev] && !skip[next])
continue;
}
if (regexp) {
if ((last !== '\\' && c === '/') || (last === '\\' && c === '/' && output[output.length - 2] === '\\'))
regexp = false;
} else
regexp = (last === '=' || last === '(' || last === ':') && (c === '/');
}
if (scope && c === '\\') {
output.push(c);
output.push(next);
index++;
last = next;
continue;
}
if (!regexp && (c === '"' || c === '\'' || c === '`')) {
if (scope && scope !== c) {
output.push(c);
continue;
}
if (c === scope)
scope = 0;
else
scope = c;
}
if (c === '}' && last === ';')
output.pop();
output.push(c);
last = c;
}
return output.join('').trim();
}