ContextReplacementPlugin.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const ContextElementDependency = require("./dependencies/ContextElementDependency");
  7. const { join } = require("./util/fs");
  8. /** @typedef {import("./Compiler")} Compiler */
  9. class ContextReplacementPlugin {
  10. /**
  11. * @param {RegExp} resourceRegExp A regular expression that determines which files will be selected
  12. * @param {TODO=} newContentResource A new resource to replace the match
  13. * @param {TODO=} newContentRecursive If true, all subdirectories are searched for matches
  14. * @param {TODO=} newContentRegExp A regular expression that determines which files will be selected
  15. */
  16. constructor(
  17. resourceRegExp,
  18. newContentResource,
  19. newContentRecursive,
  20. newContentRegExp
  21. ) {
  22. this.resourceRegExp = resourceRegExp;
  23. if (typeof newContentResource === "function") {
  24. this.newContentCallback = newContentResource;
  25. } else if (
  26. typeof newContentResource === "string" &&
  27. typeof newContentRecursive === "object"
  28. ) {
  29. this.newContentResource = newContentResource;
  30. this.newContentCreateContextMap = (fs, callback) => {
  31. callback(null, newContentRecursive);
  32. };
  33. } else if (
  34. typeof newContentResource === "string" &&
  35. typeof newContentRecursive === "function"
  36. ) {
  37. this.newContentResource = newContentResource;
  38. this.newContentCreateContextMap = newContentRecursive;
  39. } else {
  40. if (typeof newContentResource !== "string") {
  41. newContentRegExp = newContentRecursive;
  42. newContentRecursive = newContentResource;
  43. newContentResource = undefined;
  44. }
  45. if (typeof newContentRecursive !== "boolean") {
  46. newContentRegExp = newContentRecursive;
  47. newContentRecursive = undefined;
  48. }
  49. this.newContentResource = newContentResource;
  50. this.newContentRecursive = newContentRecursive;
  51. this.newContentRegExp = newContentRegExp;
  52. }
  53. }
  54. /**
  55. * Apply the plugin
  56. * @param {Compiler} compiler the compiler instance
  57. * @returns {void}
  58. */
  59. apply(compiler) {
  60. const resourceRegExp = this.resourceRegExp;
  61. const newContentCallback = this.newContentCallback;
  62. const newContentResource = this.newContentResource;
  63. const newContentRecursive = this.newContentRecursive;
  64. const newContentRegExp = this.newContentRegExp;
  65. const newContentCreateContextMap = this.newContentCreateContextMap;
  66. compiler.hooks.contextModuleFactory.tap("ContextReplacementPlugin", cmf => {
  67. cmf.hooks.beforeResolve.tap("ContextReplacementPlugin", result => {
  68. if (!result) return;
  69. if (resourceRegExp.test(result.request)) {
  70. if (newContentResource !== undefined) {
  71. result.request = newContentResource;
  72. }
  73. if (newContentRecursive !== undefined) {
  74. result.recursive = newContentRecursive;
  75. }
  76. if (newContentRegExp !== undefined) {
  77. result.regExp = newContentRegExp;
  78. }
  79. if (typeof newContentCallback === "function") {
  80. newContentCallback(result);
  81. } else {
  82. for (const d of result.dependencies) {
  83. if (d.critical) d.critical = false;
  84. }
  85. }
  86. }
  87. return result;
  88. });
  89. cmf.hooks.afterResolve.tap("ContextReplacementPlugin", result => {
  90. if (!result) return;
  91. if (resourceRegExp.test(result.resource)) {
  92. if (newContentResource !== undefined) {
  93. if (
  94. newContentResource.startsWith("/") ||
  95. (newContentResource.length > 1 && newContentResource[1] === ":")
  96. ) {
  97. result.resource = newContentResource;
  98. } else {
  99. result.resource = join(
  100. compiler.inputFileSystem,
  101. result.resource,
  102. newContentResource
  103. );
  104. }
  105. }
  106. if (newContentRecursive !== undefined) {
  107. result.recursive = newContentRecursive;
  108. }
  109. if (newContentRegExp !== undefined) {
  110. result.regExp = newContentRegExp;
  111. }
  112. if (typeof newContentCreateContextMap === "function") {
  113. result.resolveDependencies =
  114. createResolveDependenciesFromContextMap(
  115. newContentCreateContextMap
  116. );
  117. }
  118. if (typeof newContentCallback === "function") {
  119. const origResource = result.resource;
  120. newContentCallback(result);
  121. if (
  122. result.resource !== origResource &&
  123. !result.resource.startsWith("/") &&
  124. (result.resource.length <= 1 || result.resource[1] !== ":")
  125. ) {
  126. // When the function changed it to an relative path
  127. result.resource = join(
  128. compiler.inputFileSystem,
  129. origResource,
  130. result.resource
  131. );
  132. }
  133. } else {
  134. for (const d of result.dependencies) {
  135. if (d.critical) d.critical = false;
  136. }
  137. }
  138. }
  139. return result;
  140. });
  141. });
  142. }
  143. }
  144. const createResolveDependenciesFromContextMap = createContextMap => {
  145. const resolveDependenciesFromContextMap = (fs, options, callback) => {
  146. createContextMap(fs, (err, map) => {
  147. if (err) return callback(err);
  148. const dependencies = Object.keys(map).map(key => {
  149. return new ContextElementDependency(
  150. map[key] + options.resourceQuery + options.resourceFragment,
  151. key,
  152. options.category,
  153. options.referencedExports
  154. );
  155. });
  156. callback(null, dependencies);
  157. });
  158. };
  159. return resolveDependenciesFromContextMap;
  160. };
  161. module.exports = ContextReplacementPlugin;