utils.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. "use strict";
  2. const NativeModule = require("module");
  3. const path = require("path");
  4. /** @typedef {import("webpack").Compilation} Compilation */
  5. /** @typedef {import("webpack").Module} Module */
  6. /** @typedef {import("webpack").LoaderContext<any>} LoaderContext */
  7. /**
  8. * @returns {boolean}
  9. */
  10. function trueFn() {
  11. return true;
  12. }
  13. /**
  14. * @param {Compilation} compilation
  15. * @param {string | number} id
  16. * @returns {null | Module}
  17. */
  18. function findModuleById(compilation, id) {
  19. const {
  20. modules,
  21. chunkGraph
  22. } = compilation;
  23. for (const module of modules) {
  24. const moduleId = typeof chunkGraph !== "undefined" ? chunkGraph.getModuleId(module) : module.id;
  25. if (moduleId === id) {
  26. return module;
  27. }
  28. }
  29. return null;
  30. }
  31. /**
  32. * @param {LoaderContext} loaderContext
  33. * @param {string | Buffer} code
  34. * @param {string} filename
  35. * @returns {object}
  36. */
  37. function evalModuleCode(loaderContext, code, filename) {
  38. // @ts-ignore
  39. const module = new NativeModule(filename, loaderContext);
  40. // @ts-ignore
  41. module.paths = NativeModule._nodeModulePaths(loaderContext.context); // eslint-disable-line no-underscore-dangle
  42. module.filename = filename;
  43. // @ts-ignore
  44. module._compile(code, filename); // eslint-disable-line no-underscore-dangle
  45. return module.exports;
  46. }
  47. /**
  48. * @param {string} a
  49. * @param {string} b
  50. * @returns {0 | 1 | -1}
  51. */
  52. function compareIds(a, b) {
  53. if (typeof a !== typeof b) {
  54. return typeof a < typeof b ? -1 : 1;
  55. }
  56. if (a < b) {
  57. return -1;
  58. }
  59. if (a > b) {
  60. return 1;
  61. }
  62. return 0;
  63. }
  64. /**
  65. * @param {Module} a
  66. * @param {Module} b
  67. * @returns {0 | 1 | -1}
  68. */
  69. function compareModulesByIdentifier(a, b) {
  70. return compareIds(a.identifier(), b.identifier());
  71. }
  72. const MODULE_TYPE = "css/mini-extract";
  73. const AUTO_PUBLIC_PATH = "__mini_css_extract_plugin_public_path_auto__";
  74. const ABSOLUTE_PUBLIC_PATH = "webpack:///mini-css-extract-plugin/";
  75. const BASE_URI = "webpack://";
  76. const SINGLE_DOT_PATH_SEGMENT = "__mini_css_extract_plugin_single_dot_path_segment__";
  77. /**
  78. * @param {string} str
  79. * @returns {boolean}
  80. */
  81. function isAbsolutePath(str) {
  82. return path.posix.isAbsolute(str) || path.win32.isAbsolute(str);
  83. }
  84. const RELATIVE_PATH_REGEXP = /^\.\.?[/\\]/;
  85. /**
  86. * @param {string} str
  87. * @returns {boolean}
  88. */
  89. function isRelativePath(str) {
  90. return RELATIVE_PATH_REGEXP.test(str);
  91. }
  92. // TODO simplify for the next major release
  93. /**
  94. * @param {LoaderContext} loaderContext
  95. * @param {string} request
  96. * @returns {string}
  97. */
  98. function stringifyRequest(loaderContext, request) {
  99. if (typeof loaderContext.utils !== "undefined" && typeof loaderContext.utils.contextify === "function") {
  100. return JSON.stringify(loaderContext.utils.contextify(loaderContext.context || loaderContext.rootContext, request));
  101. }
  102. const splitted = request.split("!");
  103. const {
  104. context
  105. } = loaderContext;
  106. return JSON.stringify(splitted.map(part => {
  107. // First, separate singlePath from query, because the query might contain paths again
  108. const splittedPart = part.match(/^(.*?)(\?.*)/);
  109. const query = splittedPart ? splittedPart[2] : "";
  110. let singlePath = splittedPart ? splittedPart[1] : part;
  111. if (isAbsolutePath(singlePath) && context) {
  112. singlePath = path.relative(context, singlePath);
  113. if (isAbsolutePath(singlePath)) {
  114. // If singlePath still matches an absolute path, singlePath was on a different drive than context.
  115. // In this case, we leave the path platform-specific without replacing any separators.
  116. // @see https://github.com/webpack/loader-utils/pull/14
  117. return singlePath + query;
  118. }
  119. if (isRelativePath(singlePath) === false) {
  120. // Ensure that the relative path starts at least with ./ otherwise it would be a request into the modules directory (like node_modules).
  121. singlePath = `./${singlePath}`;
  122. }
  123. }
  124. return singlePath.replace(/\\/g, "/") + query;
  125. }).join("!"));
  126. }
  127. /**
  128. * @param {string} filename
  129. * @param {string} outputPath
  130. * @param {boolean} enforceRelative
  131. * @returns {string}
  132. */
  133. function getUndoPath(filename, outputPath, enforceRelative) {
  134. let depth = -1;
  135. let append = "";
  136. // eslint-disable-next-line no-param-reassign
  137. outputPath = outputPath.replace(/[\\/]$/, "");
  138. for (const part of filename.split(/[/\\]+/)) {
  139. if (part === "..") {
  140. if (depth > -1) {
  141. // eslint-disable-next-line no-plusplus
  142. depth--;
  143. } else {
  144. const i = outputPath.lastIndexOf("/");
  145. const j = outputPath.lastIndexOf("\\");
  146. const pos = i < 0 ? j : j < 0 ? i : Math.max(i, j);
  147. if (pos < 0) {
  148. return `${outputPath}/`;
  149. }
  150. append = `${outputPath.slice(pos + 1)}/${append}`;
  151. // eslint-disable-next-line no-param-reassign
  152. outputPath = outputPath.slice(0, pos);
  153. }
  154. } else if (part !== ".") {
  155. // eslint-disable-next-line no-plusplus
  156. depth++;
  157. }
  158. }
  159. return depth > 0 ? `${"../".repeat(depth)}${append}` : enforceRelative ? `./${append}` : append;
  160. }
  161. /**
  162. *
  163. * @param {string | function} value
  164. * @returns {string}
  165. */
  166. function stringifyLocal(value) {
  167. return typeof value === "function" ? value.toString() : JSON.stringify(value);
  168. }
  169. module.exports = {
  170. trueFn,
  171. findModuleById,
  172. evalModuleCode,
  173. compareModulesByIdentifier,
  174. MODULE_TYPE,
  175. AUTO_PUBLIC_PATH,
  176. ABSOLUTE_PUBLIC_PATH,
  177. BASE_URI,
  178. SINGLE_DOT_PATH_SEGMENT,
  179. stringifyRequest,
  180. stringifyLocal,
  181. getUndoPath
  182. };