DefinePlugin.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const ConstDependency = require("./dependencies/ConstDependency");
  7. const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
  8. const ParserHelpers = require("./ParserHelpers");
  9. const NullFactory = require("./NullFactory");
  10. class DefinePlugin {
  11. constructor(definitions) {
  12. this.definitions = definitions;
  13. }
  14. apply(compiler) {
  15. const definitions = this.definitions;
  16. compiler.plugin("compilation", (compilation, params) => {
  17. compilation.dependencyFactories.set(ConstDependency, new NullFactory());
  18. compilation.dependencyTemplates.set(ConstDependency, new ConstDependency.Template());
  19. params.normalModuleFactory.plugin("parser", (parser) => {
  20. (function walkDefinitions(definitions, prefix) {
  21. Object.keys(definitions).forEach((key) => {
  22. const code = definitions[key];
  23. if(code && typeof code === "object" && !(code instanceof RegExp)) {
  24. walkDefinitions(code, prefix + key + ".");
  25. applyObjectDefine(prefix + key, code);
  26. return;
  27. }
  28. applyDefineKey(prefix, key);
  29. applyDefine(prefix + key, code);
  30. });
  31. }(definitions, ""));
  32. function stringifyObj(obj) {
  33. return "__webpack_require__.i({" + Object.keys(obj).map((key) => {
  34. const code = obj[key];
  35. return JSON.stringify(key) + ":" + toCode(code);
  36. }).join(",") + "})";
  37. }
  38. function toCode(code) {
  39. if(code === null) return "null";
  40. else if(code === undefined) return "undefined";
  41. else if(code instanceof RegExp && code.toString) return code.toString();
  42. else if(typeof code === "function" && code.toString) return "(" + code.toString() + ")";
  43. else if(typeof code === "object") return stringifyObj(code);
  44. else return code + "";
  45. }
  46. function applyDefineKey(prefix, key) {
  47. const splittedKey = key.split(".");
  48. splittedKey.slice(1).forEach((_, i) => {
  49. const fullKey = prefix + splittedKey.slice(0, i + 1).join(".");
  50. parser.plugin("can-rename " + fullKey, ParserHelpers.approve);
  51. });
  52. }
  53. function applyDefine(key, code) {
  54. const isTypeof = /^typeof\s+/.test(key);
  55. if(isTypeof) key = key.replace(/^typeof\s+/, "");
  56. let recurse = false;
  57. let recurseTypeof = false;
  58. code = toCode(code);
  59. if(!isTypeof) {
  60. parser.plugin("can-rename " + key, ParserHelpers.approve);
  61. parser.plugin("evaluate Identifier " + key, (expr) => {
  62. /**
  63. * this is needed in case there is a recursion in the DefinePlugin
  64. * to prevent an endless recursion
  65. * e.g.: new DefinePlugin({
  66. * "a": "b",
  67. * "b": "a"
  68. * });
  69. */
  70. if(recurse) return;
  71. recurse = true;
  72. const res = parser.evaluate(code);
  73. recurse = false;
  74. res.setRange(expr.range);
  75. return res;
  76. });
  77. parser.plugin("expression " + key, ParserHelpers.toConstantDependency(code));
  78. }
  79. const typeofCode = isTypeof ? code : "typeof (" + code + ")";
  80. parser.plugin("evaluate typeof " + key, (expr) => {
  81. /**
  82. * this is needed in case there is a recursion in the DefinePlugin
  83. * to prevent an endless recursion
  84. * e.g.: new DefinePlugin({
  85. * "typeof a": "tyepof b",
  86. * "typeof b": "typeof a"
  87. * });
  88. */
  89. if(recurseTypeof) return;
  90. recurseTypeof = true;
  91. const res = parser.evaluate(typeofCode);
  92. recurseTypeof = false;
  93. res.setRange(expr.range);
  94. return res;
  95. });
  96. parser.plugin("typeof " + key, (expr) => {
  97. const res = parser.evaluate(typeofCode);
  98. if(!res.isString()) return;
  99. return ParserHelpers.toConstantDependency(JSON.stringify(res.string)).bind(parser)(expr);
  100. });
  101. }
  102. function applyObjectDefine(key, obj) {
  103. const code = stringifyObj(obj);
  104. parser.plugin("can-rename " + key, ParserHelpers.approve);
  105. parser.plugin("evaluate Identifier " + key, (expr) => new BasicEvaluatedExpression().setRange(expr.range));
  106. parser.plugin("evaluate typeof " + key, ParserHelpers.evaluateToString("object"));
  107. parser.plugin("expression " + key, ParserHelpers.toConstantDependency(code));
  108. parser.plugin("typeof " + key, ParserHelpers.toConstantDependency(JSON.stringify("object")));
  109. }
  110. });
  111. });
  112. }
  113. }
  114. module.exports = DefinePlugin;