Template.js 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const Tapable = require("tapable");
  7. const ConcatSource = require("webpack-sources").ConcatSource;
  8. const START_LOWERCASE_ALPHABET_CODE = "a".charCodeAt(0);
  9. const START_UPPERCASE_ALPHABET_CODE = "A".charCodeAt(0);
  10. const DELTA_A_TO_Z = "z".charCodeAt(0) - START_LOWERCASE_ALPHABET_CODE + 1;
  11. const FUNCTION_CONTENT_REGEX = /^function\s?\(\)\s?\{\n?|\n?\}$/g;
  12. const INDENT_MULTILINE_REGEX = /^\t/mg;
  13. const IDENTIFIER_NAME_REPLACE_REGEX = /^[^a-zA-Z$_]/;
  14. const IDENTIFIER_ALPHA_NUMERIC_NAME_REPLACE_REGEX = /[^a-zA-Z0-9$_]/g;
  15. const PATH_NAME_NORMALIZE_REPLACE_REGEX = /[^a-zA-Z0-9_!§$()=\-^°]+/g;
  16. const MATCH_PADDED_HYPHENS_REPLACE_REGEX = /^-|-$/g;
  17. module.exports = class Template extends Tapable {
  18. constructor(outputOptions) {
  19. super();
  20. this.outputOptions = outputOptions || {};
  21. }
  22. static getFunctionContent(fn) {
  23. return fn.toString().replace(FUNCTION_CONTENT_REGEX, "").replace(INDENT_MULTILINE_REGEX, "");
  24. }
  25. static toIdentifier(str) {
  26. if(typeof str !== "string") return "";
  27. return str.replace(IDENTIFIER_NAME_REPLACE_REGEX, "_").replace(IDENTIFIER_ALPHA_NUMERIC_NAME_REPLACE_REGEX, "_");
  28. }
  29. static toPath(str) {
  30. if(typeof str !== "string") return "";
  31. return str.replace(PATH_NAME_NORMALIZE_REPLACE_REGEX, "-").replace(MATCH_PADDED_HYPHENS_REPLACE_REGEX, "");
  32. }
  33. // map number to a single character a-z, A-Z or <_ + number> if number is too big
  34. static numberToIdentifer(n) {
  35. // lower case
  36. if(n < DELTA_A_TO_Z) return String.fromCharCode(START_LOWERCASE_ALPHABET_CODE + n);
  37. // upper case
  38. n -= DELTA_A_TO_Z;
  39. if(n < DELTA_A_TO_Z) return String.fromCharCode(START_UPPERCASE_ALPHABET_CODE + n);
  40. // fall back to _ + number
  41. n -= DELTA_A_TO_Z;
  42. return "_" + n;
  43. }
  44. indent(str) {
  45. if(Array.isArray(str)) {
  46. return str.map(this.indent.bind(this)).join("\n");
  47. } else {
  48. str = str.trimRight();
  49. if(!str) return "";
  50. var ind = (str[0] === "\n" ? "" : "\t");
  51. return ind + str.replace(/\n([^\n])/g, "\n\t$1");
  52. }
  53. }
  54. prefix(str, prefix) {
  55. if(Array.isArray(str)) {
  56. str = str.join("\n");
  57. }
  58. str = str.trim();
  59. if(!str) return "";
  60. const ind = (str[0] === "\n" ? "" : prefix);
  61. return ind + str.replace(/\n([^\n])/g, "\n" + prefix + "$1");
  62. }
  63. asString(str) {
  64. if(Array.isArray(str)) {
  65. return str.join("\n");
  66. }
  67. return str;
  68. }
  69. getModulesArrayBounds(modules) {
  70. if(!modules.every(moduleIdIsNumber))
  71. return false;
  72. var maxId = -Infinity;
  73. var minId = Infinity;
  74. modules.forEach(function(module) {
  75. if(maxId < module.id) maxId = module.id;
  76. if(minId > module.id) minId = module.id;
  77. });
  78. if(minId < 16 + ("" + minId).length) {
  79. // add minId x ',' instead of 'Array(minId).concat(...)'
  80. minId = 0;
  81. }
  82. var objectOverhead = modules.map(function(module) {
  83. var idLength = (module.id + "").length;
  84. return idLength + 2;
  85. }).reduce(function(a, b) {
  86. return a + b;
  87. }, -1);
  88. var arrayOverhead = minId === 0 ? maxId : 16 + ("" + minId).length + maxId;
  89. return arrayOverhead < objectOverhead ? [minId, maxId] : false;
  90. }
  91. renderChunkModules(chunk, moduleTemplate, dependencyTemplates, prefix) {
  92. if(!prefix) prefix = "";
  93. var source = new ConcatSource();
  94. if(chunk.getNumberOfModules() === 0) {
  95. source.add("[]");
  96. return source;
  97. }
  98. var removedModules = chunk.removedModules;
  99. var allModules = chunk.mapModules(function(module) {
  100. return {
  101. id: module.id,
  102. source: moduleTemplate.render(module, dependencyTemplates, chunk)
  103. };
  104. });
  105. if(removedModules && removedModules.length > 0) {
  106. removedModules.forEach(function(id) {
  107. allModules.push({
  108. id: id,
  109. source: "false"
  110. });
  111. });
  112. }
  113. var bounds = this.getModulesArrayBounds(allModules);
  114. if(bounds) {
  115. // Render a spare array
  116. var minId = bounds[0];
  117. var maxId = bounds[1];
  118. if(minId !== 0) source.add("Array(" + minId + ").concat(");
  119. source.add("[\n");
  120. var modules = {};
  121. allModules.forEach(function(module) {
  122. modules[module.id] = module;
  123. });
  124. for(var idx = minId; idx <= maxId; idx++) {
  125. var module = modules[idx];
  126. if(idx !== minId) source.add(",\n");
  127. source.add("/* " + idx + " */");
  128. if(module) {
  129. source.add("\n");
  130. source.add(module.source);
  131. }
  132. }
  133. source.add("\n" + prefix + "]");
  134. if(minId !== 0) source.add(")");
  135. } else {
  136. // Render an object
  137. source.add("{\n");
  138. allModules
  139. .sort(stringifyIdSortPredicate)
  140. .forEach(function(module, idx) {
  141. if(idx !== 0) source.add(",\n");
  142. source.add(`\n/***/ ${JSON.stringify(module.id)}:\n`);
  143. source.add(module.source);
  144. });
  145. source.add("\n\n" + prefix + "}");
  146. }
  147. return source;
  148. }
  149. };
  150. function stringifyIdSortPredicate(a, b) {
  151. var aId = a.id + "";
  152. var bId = b.id + "";
  153. if(aId < bId) return -1;
  154. if(aId > bId) return 1;
  155. return 0;
  156. }
  157. function moduleIdIsNumber(module) {
  158. return typeof module.id === "number";
  159. }