mangleString.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. var esprima = require('esprima');
  2. var escodegen = require('escodegen');
  3. var estraverse = require('estraverse');
  4. var SYNTAX = estraverse.Syntax;
  5. var STR_MIN_LENGTH = 5;
  6. var STR_MIN_DIST = 1000;
  7. var STR_MIN_COUNT = 2;
  8. function createDeclaration(declarations) {
  9. return {
  10. type: SYNTAX.VariableDeclaration,
  11. declarations: declarations,
  12. kind: 'var'
  13. };
  14. }
  15. function createDeclarator(id, init) {
  16. return {
  17. type: SYNTAX.VariableDeclarator,
  18. id: {
  19. type: SYNTAX.Identifier,
  20. name: id
  21. },
  22. init: {
  23. type: SYNTAX.Literal,
  24. value: init
  25. }
  26. };
  27. }
  28. function base54Digits() {
  29. return 'etnrisouaflchpdvmgybwESxTNCkLAOM_DPHBjFIqRUzWXV$JKQGYZ0516372984';
  30. }
  31. var base54 = (function(){
  32. var DIGITS = base54Digits();
  33. return function(num) {
  34. var ret = '';
  35. var base = 54;
  36. do {
  37. ret += DIGITS.charAt(num % base);
  38. num = Math.floor(num / base);
  39. base = 64;
  40. } while (num > 0);
  41. return ret;
  42. };
  43. })();
  44. function mangleString(source) {
  45. var ast = esprima.parse(source, {
  46. loc: true
  47. });
  48. var stringVariables = {};
  49. var stringRelaceCount = 0;
  50. estraverse.traverse(ast, {
  51. enter: function (node, parent) {
  52. if (node.type === SYNTAX.Literal
  53. && typeof node.value === 'string'
  54. ) {
  55. // Ignore if string is the key of property
  56. if (parent.type === SYNTAX.Property) {
  57. return;
  58. }
  59. var value = node.value;
  60. if (value.length > STR_MIN_LENGTH) {
  61. if (!stringVariables[value]) {
  62. stringVariables[value] = {
  63. count: 0,
  64. lastLoc: node.loc.start.line,
  65. name: '__echartsString__' + base54(stringRelaceCount++)
  66. };
  67. }
  68. var diff = node.loc.start.line - stringVariables[value].lastLoc;
  69. // GZIP ?
  70. if (diff >= STR_MIN_DIST) {
  71. stringVariables[value].lastLoc = node.loc.start.line;
  72. stringVariables[value].count++;
  73. }
  74. }
  75. }
  76. if (node.type === SYNTAX.MemberExpression && !node.computed) {
  77. if (node.property.type === SYNTAX.Identifier) {
  78. var value = node.property.name;
  79. if (value.length > STR_MIN_LENGTH) {
  80. if (!stringVariables[value]) {
  81. stringVariables[value] = {
  82. count: 0,
  83. lastLoc: node.loc.start.line,
  84. name: '__echartsString__' + base54(stringRelaceCount++)
  85. };
  86. }
  87. var diff = node.loc.start.line - stringVariables[value].lastLoc;
  88. if (diff >= STR_MIN_DIST) {
  89. stringVariables[value].lastLoc = node.loc.start.line;
  90. stringVariables[value].count++;
  91. }
  92. }
  93. }
  94. }
  95. }
  96. });
  97. estraverse.replace(ast, {
  98. enter: function (node, parent) {
  99. if ((node.type === SYNTAX.Literal
  100. && typeof node.value === 'string')
  101. ) {
  102. // Ignore if string is the key of property
  103. if (parent.type === SYNTAX.Property) {
  104. return;
  105. }
  106. var str = node.value;
  107. if (stringVariables[str] && stringVariables[str].count > STR_MIN_COUNT) {
  108. return {
  109. type: SYNTAX.Identifier,
  110. name: stringVariables[str].name
  111. };
  112. }
  113. }
  114. if (node.type === SYNTAX.MemberExpression && !node.computed) {
  115. if (node.property.type === SYNTAX.Identifier) {
  116. var str = node.property.name;
  117. if (stringVariables[str] && stringVariables[str].count > STR_MIN_COUNT) {
  118. return {
  119. type: SYNTAX.MemberExpression,
  120. object: node.object,
  121. property: {
  122. type: SYNTAX.Identifier,
  123. name: stringVariables[str].name
  124. },
  125. computed: true
  126. };
  127. }
  128. }
  129. }
  130. }
  131. });
  132. // Add variables in the top
  133. for (var str in stringVariables) {
  134. // Used more than once
  135. if (stringVariables[str].count > STR_MIN_COUNT) {
  136. ast.body.unshift(createDeclaration([
  137. createDeclarator(stringVariables[str].name, str)
  138. ]));
  139. }
  140. }
  141. return escodegen.generate(
  142. ast,
  143. {
  144. format: {escapeless: true},
  145. comment: true
  146. }
  147. );
  148. }
  149. exports = module.exports = mangleString;