utils.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.WEBPACK_IGNORE_COMMENT_REGEXP = void 0;
  6. exports.camelCase = camelCase;
  7. exports.combineRequests = combineRequests;
  8. exports.defaultGetLocalIdent = defaultGetLocalIdent;
  9. exports.getExportCode = getExportCode;
  10. exports.getFilter = getFilter;
  11. exports.getImportCode = getImportCode;
  12. exports.getModuleCode = getModuleCode;
  13. exports.getModulesOptions = getModulesOptions;
  14. exports.getModulesPlugins = getModulesPlugins;
  15. exports.getPreRequester = getPreRequester;
  16. exports.isDataUrl = isDataUrl;
  17. exports.isURLRequestable = isURLRequestable;
  18. exports.normalizeOptions = normalizeOptions;
  19. exports.normalizeSourceMap = normalizeSourceMap;
  20. exports.normalizeUrl = normalizeUrl;
  21. exports.requestify = requestify;
  22. exports.resolveRequests = resolveRequests;
  23. exports.shouldUseIcssPlugin = shouldUseIcssPlugin;
  24. exports.shouldUseImportPlugin = shouldUseImportPlugin;
  25. exports.shouldUseModulesPlugins = shouldUseModulesPlugins;
  26. exports.shouldUseURLPlugin = shouldUseURLPlugin;
  27. exports.sort = sort;
  28. exports.stringifyRequest = stringifyRequest;
  29. var _url = require("url");
  30. var _path = _interopRequireDefault(require("path"));
  31. var _postcssModulesValues = _interopRequireDefault(require("postcss-modules-values"));
  32. var _postcssModulesLocalByDefault = _interopRequireDefault(require("postcss-modules-local-by-default"));
  33. var _postcssModulesExtractImports = _interopRequireDefault(require("postcss-modules-extract-imports"));
  34. var _postcssModulesScope = _interopRequireDefault(require("postcss-modules-scope"));
  35. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  36. /*
  37. MIT License http://www.opensource.org/licenses/mit-license.php
  38. Author Tobias Koppers @sokra
  39. */
  40. const WEBPACK_IGNORE_COMMENT_REGEXP = /webpackIgnore:(\s+)?(true|false)/;
  41. exports.WEBPACK_IGNORE_COMMENT_REGEXP = WEBPACK_IGNORE_COMMENT_REGEXP;
  42. const matchRelativePath = /^\.\.?[/\\]/;
  43. function isAbsolutePath(str) {
  44. return _path.default.posix.isAbsolute(str) || _path.default.win32.isAbsolute(str);
  45. }
  46. function isRelativePath(str) {
  47. return matchRelativePath.test(str);
  48. }
  49. // TODO simplify for the next major release
  50. function stringifyRequest(loaderContext, request) {
  51. if (typeof loaderContext.utils !== "undefined" && typeof loaderContext.utils.contextify === "function") {
  52. return JSON.stringify(loaderContext.utils.contextify(loaderContext.context || loaderContext.rootContext, request));
  53. }
  54. const splitted = request.split("!");
  55. const {
  56. context
  57. } = loaderContext;
  58. return JSON.stringify(splitted.map(part => {
  59. // First, separate singlePath from query, because the query might contain paths again
  60. const splittedPart = part.match(/^(.*?)(\?.*)/);
  61. const query = splittedPart ? splittedPart[2] : "";
  62. let singlePath = splittedPart ? splittedPart[1] : part;
  63. if (isAbsolutePath(singlePath) && context) {
  64. singlePath = _path.default.relative(context, singlePath);
  65. if (isAbsolutePath(singlePath)) {
  66. // If singlePath still matches an absolute path, singlePath was on a different drive than context.
  67. // In this case, we leave the path platform-specific without replacing any separators.
  68. // @see https://github.com/webpack/loader-utils/pull/14
  69. return singlePath + query;
  70. }
  71. if (isRelativePath(singlePath) === false) {
  72. // Ensure that the relative path starts at least with ./ otherwise it would be a request into the modules directory (like node_modules).
  73. singlePath = `./${singlePath}`;
  74. }
  75. }
  76. return singlePath.replace(/\\/g, "/") + query;
  77. }).join("!"));
  78. }
  79. // We can't use path.win32.isAbsolute because it also matches paths starting with a forward slash
  80. const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i;
  81. const IS_MODULE_REQUEST = /^[^?]*~/;
  82. function urlToRequest(url, root) {
  83. let request;
  84. if (IS_NATIVE_WIN32_PATH.test(url)) {
  85. // absolute windows path, keep it
  86. request = url;
  87. } else if (typeof root !== "undefined" && /^\//.test(url)) {
  88. request = root + url;
  89. } else if (/^\.\.?\//.test(url)) {
  90. // A relative url stays
  91. request = url;
  92. } else {
  93. // every other url is threaded like a relative url
  94. request = `./${url}`;
  95. }
  96. // A `~` makes the url an module
  97. if (IS_MODULE_REQUEST.test(request)) {
  98. request = request.replace(IS_MODULE_REQUEST, "");
  99. }
  100. return request;
  101. }
  102. // eslint-disable-next-line no-useless-escape
  103. const regexSingleEscape = /[ -,.\/:-@[\]\^`{-~]/;
  104. const regexExcessiveSpaces = /(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g;
  105. const preserveCamelCase = string => {
  106. let result = string;
  107. let isLastCharLower = false;
  108. let isLastCharUpper = false;
  109. let isLastLastCharUpper = false;
  110. for (let i = 0; i < result.length; i++) {
  111. const character = result[i];
  112. if (isLastCharLower && /[\p{Lu}]/u.test(character)) {
  113. result = `${result.slice(0, i)}-${result.slice(i)}`;
  114. isLastCharLower = false;
  115. isLastLastCharUpper = isLastCharUpper;
  116. isLastCharUpper = true;
  117. i += 1;
  118. } else if (isLastCharUpper && isLastLastCharUpper && /[\p{Ll}]/u.test(character)) {
  119. result = `${result.slice(0, i - 1)}-${result.slice(i - 1)}`;
  120. isLastLastCharUpper = isLastCharUpper;
  121. isLastCharUpper = false;
  122. isLastCharLower = true;
  123. } else {
  124. isLastCharLower = character.toLowerCase() === character && character.toUpperCase() !== character;
  125. isLastLastCharUpper = isLastCharUpper;
  126. isLastCharUpper = character.toUpperCase() === character && character.toLowerCase() !== character;
  127. }
  128. }
  129. return result;
  130. };
  131. function camelCase(input) {
  132. let result = input.trim();
  133. if (result.length === 0) {
  134. return "";
  135. }
  136. if (result.length === 1) {
  137. return result.toLowerCase();
  138. }
  139. const hasUpperCase = result !== result.toLowerCase();
  140. if (hasUpperCase) {
  141. result = preserveCamelCase(result);
  142. }
  143. return result.replace(/^[_.\- ]+/, "").toLowerCase().replace(/[_.\- ]+([\p{Alpha}\p{N}_]|$)/gu, (_, p1) => p1.toUpperCase()).replace(/\d+([\p{Alpha}\p{N}_]|$)/gu, m => m.toUpperCase());
  144. }
  145. function escape(string) {
  146. let output = "";
  147. let counter = 0;
  148. while (counter < string.length) {
  149. // eslint-disable-next-line no-plusplus
  150. const character = string.charAt(counter++);
  151. let value;
  152. // eslint-disable-next-line no-control-regex
  153. if (/[\t\n\f\r\x0B]/.test(character)) {
  154. const codePoint = character.charCodeAt();
  155. value = `\\${codePoint.toString(16).toUpperCase()} `;
  156. } else if (character === "\\" || regexSingleEscape.test(character)) {
  157. value = `\\${character}`;
  158. } else {
  159. value = character;
  160. }
  161. output += value;
  162. }
  163. const firstChar = string.charAt(0);
  164. if (/^-[-\d]/.test(output)) {
  165. output = `\\-${output.slice(1)}`;
  166. } else if (/\d/.test(firstChar)) {
  167. output = `\\3${firstChar} ${output.slice(1)}`;
  168. }
  169. // Remove spaces after `\HEX` escapes that are not followed by a hex digit,
  170. // since they’re redundant. Note that this is only possible if the escape
  171. // sequence isn’t preceded by an odd number of backslashes.
  172. output = output.replace(regexExcessiveSpaces, ($0, $1, $2) => {
  173. if ($1 && $1.length % 2) {
  174. // It’s not safe to remove the space, so don’t.
  175. return $0;
  176. }
  177. // Strip the space.
  178. return ($1 || "") + $2;
  179. });
  180. return output;
  181. }
  182. function gobbleHex(str) {
  183. const lower = str.toLowerCase();
  184. let hex = "";
  185. let spaceTerminated = false;
  186. // eslint-disable-next-line no-undefined
  187. for (let i = 0; i < 6 && lower[i] !== undefined; i++) {
  188. const code = lower.charCodeAt(i);
  189. // check to see if we are dealing with a valid hex char [a-f|0-9]
  190. const valid = code >= 97 && code <= 102 || code >= 48 && code <= 57;
  191. // https://drafts.csswg.org/css-syntax/#consume-escaped-code-point
  192. spaceTerminated = code === 32;
  193. if (!valid) {
  194. break;
  195. }
  196. hex += lower[i];
  197. }
  198. if (hex.length === 0) {
  199. // eslint-disable-next-line no-undefined
  200. return undefined;
  201. }
  202. const codePoint = parseInt(hex, 16);
  203. const isSurrogate = codePoint >= 0xd800 && codePoint <= 0xdfff;
  204. // Add special case for
  205. // "If this number is zero, or is for a surrogate, or is greater than the maximum allowed code point"
  206. // https://drafts.csswg.org/css-syntax/#maximum-allowed-code-point
  207. if (isSurrogate || codePoint === 0x0000 || codePoint > 0x10ffff) {
  208. return ["\uFFFD", hex.length + (spaceTerminated ? 1 : 0)];
  209. }
  210. return [String.fromCodePoint(codePoint), hex.length + (spaceTerminated ? 1 : 0)];
  211. }
  212. const CONTAINS_ESCAPE = /\\/;
  213. function unescape(str) {
  214. const needToProcess = CONTAINS_ESCAPE.test(str);
  215. if (!needToProcess) {
  216. return str;
  217. }
  218. let ret = "";
  219. for (let i = 0; i < str.length; i++) {
  220. if (str[i] === "\\") {
  221. const gobbled = gobbleHex(str.slice(i + 1, i + 7));
  222. // eslint-disable-next-line no-undefined
  223. if (gobbled !== undefined) {
  224. ret += gobbled[0];
  225. i += gobbled[1];
  226. // eslint-disable-next-line no-continue
  227. continue;
  228. }
  229. // Retain a pair of \\ if double escaped `\\\\`
  230. // https://github.com/postcss/postcss-selector-parser/commit/268c9a7656fb53f543dc620aa5b73a30ec3ff20e
  231. if (str[i + 1] === "\\") {
  232. ret += "\\";
  233. i += 1;
  234. // eslint-disable-next-line no-continue
  235. continue;
  236. }
  237. // if \\ is at the end of the string retain it
  238. // https://github.com/postcss/postcss-selector-parser/commit/01a6b346e3612ce1ab20219acc26abdc259ccefb
  239. if (str.length === i + 1) {
  240. ret += str[i];
  241. }
  242. // eslint-disable-next-line no-continue
  243. continue;
  244. }
  245. ret += str[i];
  246. }
  247. return ret;
  248. }
  249. function normalizePath(file) {
  250. return _path.default.sep === "\\" ? file.replace(/\\/g, "/") : file;
  251. }
  252. // eslint-disable-next-line no-control-regex
  253. const filenameReservedRegex = /[<>:"/\\|?*]/g;
  254. // eslint-disable-next-line no-control-regex
  255. const reControlChars = /[\u0000-\u001f\u0080-\u009f]/g;
  256. function escapeLocalIdent(localident) {
  257. // TODO simplify in the next major release
  258. return escape(localident
  259. // For `[hash]` placeholder
  260. .replace(/^((-?[0-9])|--)/, "_$1").replace(filenameReservedRegex, "-").replace(reControlChars, "-").replace(/\./g, "-"));
  261. }
  262. function defaultGetLocalIdent(loaderContext, localIdentName, localName, options) {
  263. const {
  264. context,
  265. hashSalt,
  266. hashStrategy
  267. } = options;
  268. const {
  269. resourcePath
  270. } = loaderContext;
  271. let relativeResourcePath = normalizePath(_path.default.relative(context, resourcePath));
  272. // eslint-disable-next-line no-underscore-dangle
  273. if (loaderContext._module && loaderContext._module.matchResource) {
  274. relativeResourcePath = `${normalizePath(
  275. // eslint-disable-next-line no-underscore-dangle
  276. _path.default.relative(context, loaderContext._module.matchResource))}`;
  277. }
  278. // eslint-disable-next-line no-param-reassign
  279. options.content = hashStrategy === "minimal-subset" && /\[local\]/.test(localIdentName) ? relativeResourcePath : `${relativeResourcePath}\x00${localName}`;
  280. let {
  281. hashFunction,
  282. hashDigest,
  283. hashDigestLength
  284. } = options;
  285. const matches = localIdentName.match(/\[(?:([^:\]]+):)?(?:(hash|contenthash|fullhash))(?::([a-z]+\d*))?(?::(\d+))?\]/i);
  286. if (matches) {
  287. const hashName = matches[2] || hashFunction;
  288. hashFunction = matches[1] || hashFunction;
  289. hashDigest = matches[3] || hashDigest;
  290. hashDigestLength = matches[4] || hashDigestLength;
  291. // `hash` and `contenthash` are same in `loader-utils` context
  292. // let's keep `hash` for backward compatibility
  293. // eslint-disable-next-line no-param-reassign
  294. localIdentName = localIdentName.replace(/\[(?:([^:\]]+):)?(?:hash|contenthash|fullhash)(?::([a-z]+\d*))?(?::(\d+))?\]/gi, () => hashName === "fullhash" ? "[fullhash]" : "[contenthash]");
  295. }
  296. let localIdentHash = "";
  297. for (let tier = 0; localIdentHash.length < hashDigestLength; tier++) {
  298. // TODO remove this in the next major release
  299. const hash = loaderContext.utils && typeof loaderContext.utils.createHash === "function" ? loaderContext.utils.createHash(hashFunction) :
  300. // eslint-disable-next-line no-underscore-dangle
  301. loaderContext._compiler.webpack.util.createHash(hashFunction);
  302. if (hashSalt) {
  303. hash.update(hashSalt);
  304. }
  305. const tierSalt = Buffer.allocUnsafe(4);
  306. tierSalt.writeUInt32LE(tier);
  307. hash.update(tierSalt);
  308. // TODO: bug in webpack with unicode characters with strings
  309. hash.update(Buffer.from(options.content, "utf8"));
  310. localIdentHash = (localIdentHash + hash.digest(hashDigest)
  311. // Remove all leading digits
  312. ).replace(/^\d+/, "")
  313. // Replace all slashes with underscores (same as in base64url)
  314. .replace(/\//g, "_")
  315. // Remove everything that is not an alphanumeric or underscore
  316. .replace(/[^A-Za-z0-9_]+/g, "").slice(0, hashDigestLength);
  317. }
  318. // TODO need improve on webpack side, we should allow to pass hash/contentHash without chunk property, also `data` for `getPath` should be looks good without chunk property
  319. const ext = _path.default.extname(resourcePath);
  320. const base = _path.default.basename(resourcePath);
  321. const name = base.slice(0, base.length - ext.length);
  322. const data = {
  323. filename: _path.default.relative(context, resourcePath),
  324. contentHash: localIdentHash,
  325. chunk: {
  326. name,
  327. hash: localIdentHash,
  328. contentHash: localIdentHash
  329. }
  330. };
  331. // eslint-disable-next-line no-underscore-dangle
  332. let result = loaderContext._compilation.getPath(localIdentName, data);
  333. if (/\[folder\]/gi.test(result)) {
  334. const dirname = _path.default.dirname(resourcePath);
  335. let directory = normalizePath(_path.default.relative(context, `${dirname + _path.default.sep}_`));
  336. directory = directory.substring(0, directory.length - 1);
  337. let folder = "";
  338. if (directory.length > 1) {
  339. folder = _path.default.basename(directory);
  340. }
  341. result = result.replace(/\[folder\]/gi, () => folder);
  342. }
  343. if (options.regExp) {
  344. const match = resourcePath.match(options.regExp);
  345. if (match) {
  346. match.forEach((matched, i) => {
  347. result = result.replace(new RegExp(`\\[${i}\\]`, "ig"), matched);
  348. });
  349. }
  350. }
  351. return result;
  352. }
  353. function fixedEncodeURIComponent(str) {
  354. return str.replace(/[!'()*]/g, c => `%${c.charCodeAt(0).toString(16)}`);
  355. }
  356. function isDataUrl(url) {
  357. if (/^data:/i.test(url)) {
  358. return true;
  359. }
  360. return false;
  361. }
  362. const NATIVE_WIN32_PATH = /^[A-Z]:[/\\]|^\\\\/i;
  363. function normalizeUrl(url, isStringValue) {
  364. let normalizedUrl = url.replace(/^( |\t\n|\r\n|\r|\f)*/g, "").replace(/( |\t\n|\r\n|\r|\f)*$/g, "");
  365. if (isStringValue && /\\(\n|\r\n|\r|\f)/.test(normalizedUrl)) {
  366. normalizedUrl = normalizedUrl.replace(/\\(\n|\r\n|\r|\f)/g, "");
  367. }
  368. if (NATIVE_WIN32_PATH.test(url)) {
  369. try {
  370. normalizedUrl = decodeURI(normalizedUrl);
  371. } catch (error) {
  372. // Ignore
  373. }
  374. return normalizedUrl;
  375. }
  376. normalizedUrl = unescape(normalizedUrl);
  377. if (isDataUrl(url)) {
  378. // Todo fixedEncodeURIComponent is workaround. Webpack resolver shouldn't handle "!" in dataURL
  379. return fixedEncodeURIComponent(normalizedUrl);
  380. }
  381. try {
  382. normalizedUrl = decodeURI(normalizedUrl);
  383. } catch (error) {
  384. // Ignore
  385. }
  386. return normalizedUrl;
  387. }
  388. function requestify(url, rootContext, needToResolveURL = true) {
  389. if (needToResolveURL) {
  390. if (/^file:/i.test(url)) {
  391. return (0, _url.fileURLToPath)(url);
  392. }
  393. return url.charAt(0) === "/" ? urlToRequest(url, rootContext) : urlToRequest(url);
  394. }
  395. if (url.charAt(0) === "/" || /^file:/i.test(url)) {
  396. return url;
  397. }
  398. // A `~` makes the url an module
  399. if (IS_MODULE_REQUEST.test(url)) {
  400. return url.replace(IS_MODULE_REQUEST, "");
  401. }
  402. return url;
  403. }
  404. function getFilter(filter, resourcePath) {
  405. return (...args) => {
  406. if (typeof filter === "function") {
  407. return filter(...args, resourcePath);
  408. }
  409. return true;
  410. };
  411. }
  412. function getValidLocalName(localName, exportLocalsConvention) {
  413. const result = exportLocalsConvention(localName);
  414. return Array.isArray(result) ? result[0] : result;
  415. }
  416. const IS_MODULES = /\.module(s)?\.\w+$/i;
  417. const IS_ICSS = /\.icss\.\w+$/i;
  418. function getModulesOptions(rawOptions, exportType, loaderContext) {
  419. if (typeof rawOptions.modules === "boolean" && rawOptions.modules === false) {
  420. return false;
  421. }
  422. const resourcePath =
  423. // eslint-disable-next-line no-underscore-dangle
  424. loaderContext._module && loaderContext._module.matchResource || loaderContext.resourcePath;
  425. let auto;
  426. let rawModulesOptions;
  427. if (typeof rawOptions.modules === "undefined") {
  428. rawModulesOptions = {};
  429. auto = true;
  430. } else if (typeof rawOptions.modules === "boolean") {
  431. rawModulesOptions = {};
  432. } else if (typeof rawOptions.modules === "string") {
  433. rawModulesOptions = {
  434. mode: rawOptions.modules
  435. };
  436. } else {
  437. rawModulesOptions = rawOptions.modules;
  438. ({
  439. auto
  440. } = rawModulesOptions);
  441. }
  442. // eslint-disable-next-line no-underscore-dangle
  443. const {
  444. outputOptions
  445. } = loaderContext._compilation;
  446. const needNamedExport = exportType === "css-style-sheet" || exportType === "string";
  447. const modulesOptions = {
  448. auto,
  449. mode: "local",
  450. exportGlobals: false,
  451. localIdentName: "[hash:base64]",
  452. localIdentContext: loaderContext.rootContext,
  453. localIdentHashSalt: outputOptions.hashSalt,
  454. localIdentHashFunction: outputOptions.hashFunction,
  455. localIdentHashDigest: outputOptions.hashDigest,
  456. localIdentHashDigestLength: outputOptions.hashDigestLength,
  457. // eslint-disable-next-line no-undefined
  458. localIdentRegExp: undefined,
  459. // eslint-disable-next-line no-undefined
  460. getLocalIdent: undefined,
  461. namedExport: needNamedExport || false,
  462. exportLocalsConvention: (rawModulesOptions.namedExport === true || needNamedExport) && typeof rawModulesOptions.exportLocalsConvention === "undefined" ? "camelCaseOnly" : "asIs",
  463. exportOnlyLocals: false,
  464. ...rawModulesOptions
  465. };
  466. let exportLocalsConventionType;
  467. if (typeof modulesOptions.exportLocalsConvention === "string") {
  468. exportLocalsConventionType = modulesOptions.exportLocalsConvention;
  469. modulesOptions.exportLocalsConvention = name => {
  470. switch (exportLocalsConventionType) {
  471. case "camelCase":
  472. {
  473. return [name, camelCase(name)];
  474. }
  475. case "camelCaseOnly":
  476. {
  477. return camelCase(name);
  478. }
  479. case "dashes":
  480. {
  481. return [name, dashesCamelCase(name)];
  482. }
  483. case "dashesOnly":
  484. {
  485. return dashesCamelCase(name);
  486. }
  487. case "asIs":
  488. default:
  489. return name;
  490. }
  491. };
  492. }
  493. if (typeof modulesOptions.auto === "boolean") {
  494. const isModules = modulesOptions.auto && IS_MODULES.test(resourcePath);
  495. let isIcss;
  496. if (!isModules) {
  497. isIcss = IS_ICSS.test(resourcePath);
  498. if (isIcss) {
  499. modulesOptions.mode = "icss";
  500. }
  501. }
  502. if (!isModules && !isIcss) {
  503. return false;
  504. }
  505. } else if (modulesOptions.auto instanceof RegExp) {
  506. const isModules = modulesOptions.auto.test(resourcePath);
  507. if (!isModules) {
  508. return false;
  509. }
  510. } else if (typeof modulesOptions.auto === "function") {
  511. const isModule = modulesOptions.auto(resourcePath);
  512. if (!isModule) {
  513. return false;
  514. }
  515. }
  516. if (typeof modulesOptions.mode === "function") {
  517. modulesOptions.mode = modulesOptions.mode(loaderContext.resourcePath);
  518. }
  519. if (needNamedExport) {
  520. if (rawOptions.esModule === false) {
  521. throw new Error("The 'exportType' option with the 'css-style-sheet' or 'string' value requires the 'esModules' option to be enabled");
  522. }
  523. if (modulesOptions.namedExport === false) {
  524. throw new Error("The 'exportType' option with the 'css-style-sheet' or 'string' value requires the 'modules.namedExport' option to be enabled");
  525. }
  526. }
  527. if (modulesOptions.namedExport === true) {
  528. if (rawOptions.esModule === false) {
  529. throw new Error("The 'modules.namedExport' option requires the 'esModules' option to be enabled");
  530. }
  531. if (typeof exportLocalsConventionType === "string" && exportLocalsConventionType !== "camelCaseOnly" && exportLocalsConventionType !== "dashesOnly") {
  532. throw new Error('The "modules.namedExport" option requires the "modules.exportLocalsConvention" option to be "camelCaseOnly" or "dashesOnly"');
  533. }
  534. }
  535. return modulesOptions;
  536. }
  537. function normalizeOptions(rawOptions, loaderContext) {
  538. const exportType = typeof rawOptions.exportType === "undefined" ? "array" : rawOptions.exportType;
  539. const modulesOptions = getModulesOptions(rawOptions, exportType, loaderContext);
  540. return {
  541. url: typeof rawOptions.url === "undefined" ? true : rawOptions.url,
  542. import: typeof rawOptions.import === "undefined" ? true : rawOptions.import,
  543. modules: modulesOptions,
  544. sourceMap: typeof rawOptions.sourceMap === "boolean" ? rawOptions.sourceMap : loaderContext.sourceMap,
  545. importLoaders: typeof rawOptions.importLoaders === "string" ? parseInt(rawOptions.importLoaders, 10) : rawOptions.importLoaders,
  546. esModule: typeof rawOptions.esModule === "undefined" ? true : rawOptions.esModule,
  547. exportType
  548. };
  549. }
  550. function shouldUseImportPlugin(options) {
  551. if (options.modules.exportOnlyLocals) {
  552. return false;
  553. }
  554. if (typeof options.import === "boolean") {
  555. return options.import;
  556. }
  557. return true;
  558. }
  559. function shouldUseURLPlugin(options) {
  560. if (options.modules.exportOnlyLocals) {
  561. return false;
  562. }
  563. if (typeof options.url === "boolean") {
  564. return options.url;
  565. }
  566. return true;
  567. }
  568. function shouldUseModulesPlugins(options) {
  569. if (typeof options.modules === "boolean" && options.modules === false) {
  570. return false;
  571. }
  572. return options.modules.mode !== "icss";
  573. }
  574. function shouldUseIcssPlugin(options) {
  575. return Boolean(options.modules);
  576. }
  577. function getModulesPlugins(options, loaderContext) {
  578. const {
  579. mode,
  580. getLocalIdent,
  581. localIdentName,
  582. localIdentContext,
  583. localIdentHashSalt,
  584. localIdentHashFunction,
  585. localIdentHashDigest,
  586. localIdentHashDigestLength,
  587. localIdentRegExp,
  588. hashStrategy
  589. } = options.modules;
  590. let plugins = [];
  591. try {
  592. plugins = [_postcssModulesValues.default, (0, _postcssModulesLocalByDefault.default)({
  593. mode
  594. }), (0, _postcssModulesExtractImports.default)(), (0, _postcssModulesScope.default)({
  595. generateScopedName(exportName) {
  596. let localIdent;
  597. if (typeof getLocalIdent !== "undefined") {
  598. localIdent = getLocalIdent(loaderContext, localIdentName, unescape(exportName), {
  599. context: localIdentContext,
  600. hashSalt: localIdentHashSalt,
  601. hashFunction: localIdentHashFunction,
  602. hashDigest: localIdentHashDigest,
  603. hashDigestLength: localIdentHashDigestLength,
  604. hashStrategy,
  605. regExp: localIdentRegExp
  606. });
  607. }
  608. // A null/undefined value signals that we should invoke the default
  609. // getLocalIdent method.
  610. if (typeof localIdent === "undefined" || localIdent === null) {
  611. localIdent = defaultGetLocalIdent(loaderContext, localIdentName, unescape(exportName), {
  612. context: localIdentContext,
  613. hashSalt: localIdentHashSalt,
  614. hashFunction: localIdentHashFunction,
  615. hashDigest: localIdentHashDigest,
  616. hashDigestLength: localIdentHashDigestLength,
  617. hashStrategy,
  618. regExp: localIdentRegExp
  619. });
  620. return escapeLocalIdent(localIdent).replace(/\\\[local\\]/gi, exportName);
  621. }
  622. return escapeLocalIdent(localIdent);
  623. },
  624. exportGlobals: options.modules.exportGlobals
  625. })];
  626. } catch (error) {
  627. loaderContext.emitError(error);
  628. }
  629. return plugins;
  630. }
  631. const ABSOLUTE_SCHEME = /^[a-z0-9+\-.]+:/i;
  632. function getURLType(source) {
  633. if (source[0] === "/") {
  634. if (source[1] === "/") {
  635. return "scheme-relative";
  636. }
  637. return "path-absolute";
  638. }
  639. if (IS_NATIVE_WIN32_PATH.test(source)) {
  640. return "path-absolute";
  641. }
  642. return ABSOLUTE_SCHEME.test(source) ? "absolute" : "path-relative";
  643. }
  644. function normalizeSourceMap(map, resourcePath) {
  645. let newMap = map;
  646. // Some loader emit source map as string
  647. // Strip any JSON XSSI avoidance prefix from the string (as documented in the source maps specification), and then parse the string as JSON.
  648. if (typeof newMap === "string") {
  649. newMap = JSON.parse(newMap);
  650. }
  651. delete newMap.file;
  652. const {
  653. sourceRoot
  654. } = newMap;
  655. delete newMap.sourceRoot;
  656. if (newMap.sources) {
  657. // Source maps should use forward slash because it is URLs (https://github.com/mozilla/source-map/issues/91)
  658. // We should normalize path because previous loaders like `sass-loader` using backslash when generate source map
  659. newMap.sources = newMap.sources.map(source => {
  660. // Non-standard syntax from `postcss`
  661. if (source.indexOf("<") === 0) {
  662. return source;
  663. }
  664. const sourceType = getURLType(source);
  665. // Do no touch `scheme-relative` and `absolute` URLs
  666. if (sourceType === "path-relative" || sourceType === "path-absolute") {
  667. const absoluteSource = sourceType === "path-relative" && sourceRoot ? _path.default.resolve(sourceRoot, normalizePath(source)) : normalizePath(source);
  668. return _path.default.relative(_path.default.dirname(resourcePath), absoluteSource);
  669. }
  670. return source;
  671. });
  672. }
  673. return newMap;
  674. }
  675. function getPreRequester({
  676. loaders,
  677. loaderIndex
  678. }) {
  679. const cache = Object.create(null);
  680. return number => {
  681. if (cache[number]) {
  682. return cache[number];
  683. }
  684. if (number === false) {
  685. cache[number] = "";
  686. } else {
  687. const loadersRequest = loaders.slice(loaderIndex, loaderIndex + 1 + (typeof number !== "number" ? 0 : number)).map(x => x.request).join("!");
  688. cache[number] = `-!${loadersRequest}!`;
  689. }
  690. return cache[number];
  691. };
  692. }
  693. function getImportCode(imports, options) {
  694. let code = "";
  695. for (const item of imports) {
  696. const {
  697. importName,
  698. url,
  699. icss,
  700. type
  701. } = item;
  702. if (options.esModule) {
  703. if (icss && options.modules.namedExport) {
  704. code += `import ${options.modules.exportOnlyLocals ? "" : `${importName}, `}* as ${importName}_NAMED___ from ${url};\n`;
  705. } else {
  706. code += type === "url" ? `var ${importName} = new URL(${url}, import.meta.url);\n` : `import ${importName} from ${url};\n`;
  707. }
  708. } else {
  709. code += `var ${importName} = require(${url});\n`;
  710. }
  711. }
  712. return code ? `// Imports\n${code}` : "";
  713. }
  714. function normalizeSourceMapForRuntime(map, loaderContext) {
  715. const resultMap = map ? map.toJSON() : null;
  716. if (resultMap) {
  717. delete resultMap.file;
  718. /* eslint-disable no-underscore-dangle */
  719. if (loaderContext._compilation && loaderContext._compilation.options && loaderContext._compilation.options.devtool && loaderContext._compilation.options.devtool.includes("nosources")) {
  720. /* eslint-enable no-underscore-dangle */
  721. delete resultMap.sourcesContent;
  722. }
  723. resultMap.sourceRoot = "";
  724. resultMap.sources = resultMap.sources.map(source => {
  725. // Non-standard syntax from `postcss`
  726. if (source.indexOf("<") === 0) {
  727. return source;
  728. }
  729. const sourceType = getURLType(source);
  730. if (sourceType !== "path-relative") {
  731. return source;
  732. }
  733. const resourceDirname = _path.default.dirname(loaderContext.resourcePath);
  734. const absoluteSource = _path.default.resolve(resourceDirname, source);
  735. const contextifyPath = normalizePath(_path.default.relative(loaderContext.rootContext, absoluteSource));
  736. return `webpack://./${contextifyPath}`;
  737. });
  738. }
  739. return JSON.stringify(resultMap);
  740. }
  741. function printParams(media, dedupe, supports, layer) {
  742. let result = "";
  743. if (typeof layer !== "undefined") {
  744. result = `, ${JSON.stringify(layer)}`;
  745. }
  746. if (typeof supports !== "undefined") {
  747. result = `, ${JSON.stringify(supports)}${result}`;
  748. } else if (result.length > 0) {
  749. result = `, undefined${result}`;
  750. }
  751. if (dedupe) {
  752. result = `, true${result}`;
  753. } else if (result.length > 0) {
  754. result = `, false${result}`;
  755. }
  756. if (media) {
  757. result = `${JSON.stringify(media)}${result}`;
  758. } else if (result.length > 0) {
  759. result = `""${result}`;
  760. }
  761. return result;
  762. }
  763. function getModuleCode(result, api, replacements, options, loaderContext) {
  764. if (options.modules.exportOnlyLocals === true) {
  765. return "";
  766. }
  767. let sourceMapValue = "";
  768. if (options.sourceMap) {
  769. const sourceMap = result.map;
  770. sourceMapValue = `,${normalizeSourceMapForRuntime(sourceMap, loaderContext)}`;
  771. }
  772. let code = JSON.stringify(result.css);
  773. let beforeCode = `var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(${options.sourceMap ? "___CSS_LOADER_API_SOURCEMAP_IMPORT___" : "___CSS_LOADER_API_NO_SOURCEMAP_IMPORT___"});\n`;
  774. for (const item of api) {
  775. const {
  776. url,
  777. layer,
  778. supports,
  779. media,
  780. dedupe
  781. } = item;
  782. if (url) {
  783. // eslint-disable-next-line no-undefined
  784. const printedParam = printParams(media, undefined, supports, layer);
  785. beforeCode += `___CSS_LOADER_EXPORT___.push([module.id, ${JSON.stringify(`@import url(${url});`)}${printedParam.length > 0 ? `, ${printedParam}` : ""}]);\n`;
  786. } else {
  787. const printedParam = printParams(media, dedupe, supports, layer);
  788. beforeCode += `___CSS_LOADER_EXPORT___.i(${item.importName}${printedParam.length > 0 ? `, ${printedParam}` : ""});\n`;
  789. }
  790. }
  791. for (const item of replacements) {
  792. const {
  793. replacementName,
  794. importName,
  795. localName
  796. } = item;
  797. if (localName) {
  798. code = code.replace(new RegExp(replacementName, "g"), () => options.modules.namedExport ? `" + ${importName}_NAMED___[${JSON.stringify(getValidLocalName(localName, options.modules.exportLocalsConvention))}] + "` : `" + ${importName}.locals[${JSON.stringify(localName)}] + "`);
  799. } else {
  800. const {
  801. hash,
  802. needQuotes
  803. } = item;
  804. const getUrlOptions = [].concat(hash ? [`hash: ${JSON.stringify(hash)}`] : []).concat(needQuotes ? "needQuotes: true" : []);
  805. const preparedOptions = getUrlOptions.length > 0 ? `, { ${getUrlOptions.join(", ")} }` : "";
  806. beforeCode += `var ${replacementName} = ___CSS_LOADER_GET_URL_IMPORT___(${importName}${preparedOptions});\n`;
  807. code = code.replace(new RegExp(replacementName, "g"), () => `" + ${replacementName} + "`);
  808. }
  809. }
  810. // Indexes description:
  811. // 0 - module id
  812. // 1 - CSS code
  813. // 2 - media
  814. // 3 - source map
  815. // 4 - supports
  816. // 5 - layer
  817. return `${beforeCode}// Module\n___CSS_LOADER_EXPORT___.push([module.id, ${code}, ""${sourceMapValue}]);\n`;
  818. }
  819. function dashesCamelCase(str) {
  820. return str.replace(/-+(\w)/g, (match, firstLetter) => firstLetter.toUpperCase());
  821. }
  822. function getExportCode(exports, replacements, icssPluginUsed, options) {
  823. let code = "// Exports\n";
  824. if (icssPluginUsed) {
  825. let localsCode = "";
  826. const addExportToLocalsCode = (names, value) => {
  827. const normalizedNames = Array.isArray(names) ? new Set(names) : new Set([names]);
  828. for (const name of normalizedNames) {
  829. if (options.modules.namedExport) {
  830. localsCode += `export var ${name} = ${JSON.stringify(value)};\n`;
  831. } else {
  832. if (localsCode) {
  833. localsCode += `,\n`;
  834. }
  835. localsCode += `\t${JSON.stringify(name)}: ${JSON.stringify(value)}`;
  836. }
  837. }
  838. };
  839. for (const {
  840. name,
  841. value
  842. } of exports) {
  843. addExportToLocalsCode(options.modules.exportLocalsConvention(name), value);
  844. }
  845. for (const item of replacements) {
  846. const {
  847. replacementName,
  848. localName
  849. } = item;
  850. if (localName) {
  851. const {
  852. importName
  853. } = item;
  854. localsCode = localsCode.replace(new RegExp(replacementName, "g"), () => {
  855. if (options.modules.namedExport) {
  856. return `" + ${importName}_NAMED___[${JSON.stringify(getValidLocalName(localName, options.modules.exportLocalsConvention))}] + "`;
  857. } else if (options.modules.exportOnlyLocals) {
  858. return `" + ${importName}[${JSON.stringify(localName)}] + "`;
  859. }
  860. return `" + ${importName}.locals[${JSON.stringify(localName)}] + "`;
  861. });
  862. } else {
  863. localsCode = localsCode.replace(new RegExp(replacementName, "g"), () => `" + ${replacementName} + "`);
  864. }
  865. }
  866. if (options.modules.exportOnlyLocals) {
  867. code += options.modules.namedExport ? localsCode : `${options.esModule ? "export default" : "module.exports ="} {\n${localsCode}\n};\n`;
  868. return code;
  869. }
  870. code += options.modules.namedExport ? localsCode : `___CSS_LOADER_EXPORT___.locals = {${localsCode ? `\n${localsCode}\n` : ""}};\n`;
  871. }
  872. const isCSSStyleSheetExport = options.exportType === "css-style-sheet";
  873. if (isCSSStyleSheetExport) {
  874. code += "var ___CSS_LOADER_STYLE_SHEET___ = new CSSStyleSheet();\n";
  875. code += "___CSS_LOADER_STYLE_SHEET___.replaceSync(___CSS_LOADER_EXPORT___.toString());\n";
  876. }
  877. let finalExport;
  878. switch (options.exportType) {
  879. case "string":
  880. finalExport = "___CSS_LOADER_EXPORT___.toString()";
  881. break;
  882. case "css-style-sheet":
  883. finalExport = "___CSS_LOADER_STYLE_SHEET___";
  884. break;
  885. default:
  886. case "array":
  887. finalExport = "___CSS_LOADER_EXPORT___";
  888. break;
  889. }
  890. code += `${options.esModule ? "export default" : "module.exports ="} ${finalExport};\n`;
  891. return code;
  892. }
  893. async function resolveRequests(resolve, context, possibleRequests) {
  894. return resolve(context, possibleRequests[0]).then(result => result).catch(error => {
  895. const [, ...tailPossibleRequests] = possibleRequests;
  896. if (tailPossibleRequests.length === 0) {
  897. throw error;
  898. }
  899. return resolveRequests(resolve, context, tailPossibleRequests);
  900. });
  901. }
  902. function isURLRequestable(url, options = {}) {
  903. // Protocol-relative URLs
  904. if (/^\/\//.test(url)) {
  905. return {
  906. requestable: false,
  907. needResolve: false
  908. };
  909. }
  910. // `#` URLs
  911. if (/^#/.test(url)) {
  912. return {
  913. requestable: false,
  914. needResolve: false
  915. };
  916. }
  917. // Data URI
  918. if (isDataUrl(url) && options.isSupportDataURL) {
  919. try {
  920. decodeURIComponent(url);
  921. } catch (ignoreError) {
  922. return {
  923. requestable: false,
  924. needResolve: false
  925. };
  926. }
  927. return {
  928. requestable: true,
  929. needResolve: false
  930. };
  931. }
  932. // `file:` protocol
  933. if (/^file:/i.test(url)) {
  934. return {
  935. requestable: true,
  936. needResolve: true
  937. };
  938. }
  939. // Absolute URLs
  940. if (/^[a-z][a-z0-9+.-]*:/i.test(url) && !NATIVE_WIN32_PATH.test(url)) {
  941. if (options.isSupportAbsoluteURL && /^https?:/i.test(url)) {
  942. return {
  943. requestable: true,
  944. needResolve: false
  945. };
  946. }
  947. return {
  948. requestable: false,
  949. needResolve: false
  950. };
  951. }
  952. return {
  953. requestable: true,
  954. needResolve: true
  955. };
  956. }
  957. function sort(a, b) {
  958. return a.index - b.index;
  959. }
  960. function combineRequests(preRequest, url) {
  961. const idx = url.indexOf("!=!");
  962. return idx !== -1 ? url.slice(0, idx + 3) + preRequest + url.slice(idx + 3) : preRequest + url;
  963. }