提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

227 lines
7.7 KiB

  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. /** @typedef {import("./ContextModule").ContextModuleOptions} ContextModuleOptions */
  10. /** @typedef {import("./ContextModuleFactory").BeforeContextResolveData} BeforeContextResolveData */
  11. /** @typedef {import("./ContextModuleFactory").AfterContextResolveData} AfterContextResolveData */
  12. /** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
  13. /** @typedef {Record<string, string>} NewContentCreateContextMap */
  14. const PLUGIN_NAME = "ContextReplacementPlugin";
  15. class ContextReplacementPlugin {
  16. /**
  17. * @param {RegExp} resourceRegExp A regular expression that determines which files will be selected
  18. * @param {(string | ((context: BeforeContextResolveData | AfterContextResolveData) => void) | RegExp | boolean)=} newContentResource A new resource to replace the match
  19. * @param {(boolean | NewContentCreateContextMap | RegExp)=} newContentRecursive If true, all subdirectories are searched for matches
  20. * @param {RegExp=} newContentRegExp A regular expression that determines which files will be selected
  21. */
  22. constructor(
  23. resourceRegExp,
  24. newContentResource,
  25. newContentRecursive,
  26. newContentRegExp
  27. ) {
  28. this.resourceRegExp = resourceRegExp;
  29. // new webpack.ContextReplacementPlugin(/selector/, (context) => { /* Logic */ });
  30. if (typeof newContentResource === "function") {
  31. this.newContentCallback = newContentResource;
  32. }
  33. // new ContextReplacementPlugin(/selector/, './folder', { './request': './request' });
  34. else if (
  35. typeof newContentResource === "string" &&
  36. typeof newContentRecursive === "object"
  37. ) {
  38. this.newContentResource = newContentResource;
  39. /**
  40. * @param {InputFileSystem} fs input file system
  41. * @param {(err: null | Error, newContentRecursive: NewContentCreateContextMap) => void} callback callback
  42. */
  43. this.newContentCreateContextMap = (fs, callback) => {
  44. callback(
  45. null,
  46. /** @type {NewContentCreateContextMap} */ (newContentRecursive)
  47. );
  48. };
  49. }
  50. // new ContextReplacementPlugin(/selector/, './folder', (context) => { /* Logic */ });
  51. else if (
  52. typeof newContentResource === "string" &&
  53. typeof newContentRecursive === "function"
  54. ) {
  55. this.newContentResource = newContentResource;
  56. this.newContentCreateContextMap = newContentRecursive;
  57. } else {
  58. // new webpack.ContextReplacementPlugin(/selector/, false, /reg-exp/);
  59. if (typeof newContentResource !== "string") {
  60. newContentRegExp = /** @type {RegExp} */ (newContentRecursive);
  61. newContentRecursive = /** @type {boolean} */ (newContentResource);
  62. newContentResource = undefined;
  63. }
  64. // new webpack.ContextReplacementPlugin(/selector/, /de|fr|hu/);
  65. if (typeof newContentRecursive !== "boolean") {
  66. newContentRegExp = /** @type {RegExp} */ (newContentRecursive);
  67. newContentRecursive = undefined;
  68. }
  69. // new webpack.ContextReplacementPlugin(/selector/, './folder', false, /selector/);
  70. this.newContentResource =
  71. /** @type {string | undefined} */
  72. (newContentResource);
  73. this.newContentRecursive =
  74. /** @type {boolean | undefined} */
  75. (newContentRecursive);
  76. this.newContentRegExp =
  77. /** @type {RegExp | undefined} */
  78. (newContentRegExp);
  79. }
  80. }
  81. /**
  82. * Apply the plugin
  83. * @param {Compiler} compiler the compiler instance
  84. * @returns {void}
  85. */
  86. apply(compiler) {
  87. const resourceRegExp = this.resourceRegExp;
  88. const newContentCallback = this.newContentCallback;
  89. const newContentResource = this.newContentResource;
  90. const newContentRecursive = this.newContentRecursive;
  91. const newContentRegExp = this.newContentRegExp;
  92. const newContentCreateContextMap = this.newContentCreateContextMap;
  93. compiler.hooks.contextModuleFactory.tap(PLUGIN_NAME, (cmf) => {
  94. cmf.hooks.beforeResolve.tap(PLUGIN_NAME, (result) => {
  95. if (!result) return;
  96. if (resourceRegExp.test(result.request)) {
  97. if (newContentResource !== undefined) {
  98. result.request = newContentResource;
  99. }
  100. if (newContentRecursive !== undefined) {
  101. result.recursive = newContentRecursive;
  102. }
  103. if (newContentRegExp !== undefined) {
  104. result.regExp = newContentRegExp;
  105. }
  106. if (typeof newContentCallback === "function") {
  107. newContentCallback(result);
  108. } else {
  109. for (const d of result.dependencies) {
  110. if (d.critical) d.critical = false;
  111. }
  112. }
  113. }
  114. return result;
  115. });
  116. cmf.hooks.afterResolve.tap(PLUGIN_NAME, (result) => {
  117. if (!result) return;
  118. const isMatchResourceRegExp = () => {
  119. if (Array.isArray(result.resource)) {
  120. return result.resource.some((item) => resourceRegExp.test(item));
  121. }
  122. return resourceRegExp.test(result.resource);
  123. };
  124. if (isMatchResourceRegExp()) {
  125. if (newContentResource !== undefined) {
  126. if (
  127. newContentResource.startsWith("/") ||
  128. (newContentResource.length > 1 && newContentResource[1] === ":")
  129. ) {
  130. result.resource = newContentResource;
  131. } else {
  132. const rootPath =
  133. typeof result.resource === "string"
  134. ? result.resource
  135. : /** @type {string} */
  136. (result.resource.find((item) => resourceRegExp.test(item)));
  137. result.resource = join(
  138. /** @type {InputFileSystem} */
  139. (compiler.inputFileSystem),
  140. rootPath,
  141. newContentResource
  142. );
  143. }
  144. }
  145. if (newContentRecursive !== undefined) {
  146. result.recursive = newContentRecursive;
  147. }
  148. if (newContentRegExp !== undefined) {
  149. result.regExp = newContentRegExp;
  150. }
  151. if (typeof newContentCreateContextMap === "function") {
  152. result.resolveDependencies =
  153. createResolveDependenciesFromContextMap(
  154. newContentCreateContextMap
  155. );
  156. }
  157. if (typeof newContentCallback === "function") {
  158. const origResource = result.resource;
  159. newContentCallback(result);
  160. if (result.resource !== origResource) {
  161. const newResource = Array.isArray(result.resource)
  162. ? result.resource
  163. : [result.resource];
  164. for (let i = 0; i < newResource.length; i++) {
  165. if (
  166. !newResource[i].startsWith("/") &&
  167. (newResource[i].length <= 1 || newResource[i][1] !== ":")
  168. ) {
  169. // When the function changed it to an relative path
  170. newResource[i] = join(
  171. /** @type {InputFileSystem} */
  172. (compiler.inputFileSystem),
  173. origResource[i],
  174. newResource[i]
  175. );
  176. }
  177. }
  178. result.resource = newResource;
  179. }
  180. } else {
  181. for (const d of result.dependencies) {
  182. if (d.critical) d.critical = false;
  183. }
  184. }
  185. }
  186. return result;
  187. });
  188. });
  189. }
  190. }
  191. /**
  192. * @param {(fs: InputFileSystem, callback: (err: null | Error, map: NewContentCreateContextMap) => void) => void} createContextMap create context map function
  193. * @returns {(fs: InputFileSystem, options: ContextModuleOptions, callback: (err: null | Error, dependencies?: ContextElementDependency[]) => void) => void} resolve resolve dependencies from context map function
  194. */
  195. const createResolveDependenciesFromContextMap =
  196. (createContextMap) => (fs, options, callback) => {
  197. createContextMap(fs, (err, map) => {
  198. if (err) return callback(err);
  199. const dependencies = Object.keys(map).map(
  200. (key) =>
  201. new ContextElementDependency(
  202. map[key] + options.resourceQuery + options.resourceFragment,
  203. key,
  204. options.typePrefix,
  205. /** @type {string} */
  206. (options.category),
  207. options.referencedExports
  208. )
  209. );
  210. callback(null, dependencies);
  211. });
  212. };
  213. module.exports = ContextReplacementPlugin;