var esprima = require('esprima');
var escodegen = require('escodegen');
var estraverse = require('estraverse');
var SYNTAX = estraverse.Syntax;
var STR_MIN_LENGTH = 5;
var STR_MIN_DIST = 1000;
var STR_MIN_COUNT = 2;
function createDeclaration(declarations) {
return {
type: SYNTAX.VariableDeclaration,
declarations: declarations,
kind: 'var'
};
}
function createDeclarator(id, init) {
return {
type: SYNTAX.VariableDeclarator,
id: {
type: SYNTAX.Identifier,
name: id
},
init: {
type: SYNTAX.Literal,
value: init
}
};
}
function base54Digits() {
return 'etnrisouaflchpdvmgybwESxTNCkLAOM_DPHBjFIqRUzWXV$JKQGYZ0516372984';
}
var base54 = (function(){
var DIGITS = base54Digits();
return function(num) {
var ret = '';
var base = 54;
do {
ret += DIGITS.charAt(num % base);
num = Math.floor(num / base);
base = 64;
} while (num > 0);
return ret;
};
})();
function mangleString(source) {
var ast = esprima.parse(source, {
loc: true
});
var stringVariables = {};
var stringRelaceCount = 0;
estraverse.traverse(ast, {
enter: function (node, parent) {
if (node.type === SYNTAX.Literal
&& typeof node.value === 'string'
) {
// Ignore if string is the key of property
if (parent.type === SYNTAX.Property) {
return;
}
var value = node.value;
if (value.length > STR_MIN_LENGTH) {
if (!stringVariables[value]) {
stringVariables[value] = {
count: 0,
lastLoc: node.loc.start.line,
name: '__echartsString__' + base54(stringRelaceCount++)
};
}
var diff = node.loc.start.line - stringVariables[value].lastLoc;
// GZIP ?
if (diff >= STR_MIN_DIST) {
stringVariables[value].lastLoc = node.loc.start.line;
stringVariables[value].count++;
}
}
}
if (node.type === SYNTAX.MemberExpression && !node.computed) {
if (node.property.type === SYNTAX.Identifier) {
var value = node.property.name;
if (value.length > STR_MIN_LENGTH) {
if (!stringVariables[value]) {
stringVariables[value] = {
count: 0,
lastLoc: node.loc.start.line,
name: '__echartsString__' + base54(stringRelaceCount++)
};
}
var diff = node.loc.start.line - stringVariables[value].lastLoc;
if (diff >= STR_MIN_DIST) {
stringVariables[value].lastLoc = node.loc.start.line;
stringVariables[value].count++;
}
}
}
}
}
});
estraverse.replace(ast, {
enter: function (node, parent) {
if ((node.type === SYNTAX.Literal
&& typeof node.value === 'string')
) {
// Ignore if string is the key of property
if (parent.type === SYNTAX.Property) {
return;
}
var str = node.value;
if (stringVariables[str] && stringVariables[str].count > STR_MIN_COUNT) {
return {
type: SYNTAX.Identifier,
name: stringVariables[str].name
};
}
}
if (node.type === SYNTAX.MemberExpression && !node.computed) {
if (node.property.type === SYNTAX.Identifier) {
var str = node.property.name;
if (stringVariables[str] && stringVariables[str].count > STR_MIN_COUNT) {
return {
type: SYNTAX.MemberExpression,
object: node.object,
property: {
type: SYNTAX.Identifier,
name: stringVariables[str].name
},
computed: true
};
}
}
}
}
});
// Add variables in the top
for (var str in stringVariables) {
// Used more than once
if (stringVariables[str].count > STR_MIN_COUNT) {
ast.body.unshift(createDeclaration([
createDeclarator(stringVariables[str].name, str)
]));
}
}
return escodegen.generate(
ast,
{
format: {escapeless: true},
comment: true
}
);
}
exports = module.exports = mangleString;
|