UNPKG

19.6 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.normalizeOptions = normalizeOptions;
7exports.shouldUseModulesPlugins = shouldUseModulesPlugins;
8exports.shouldUseImportPlugin = shouldUseImportPlugin;
9exports.shouldUseURLPlugin = shouldUseURLPlugin;
10exports.shouldUseIcssPlugin = shouldUseIcssPlugin;
11exports.normalizeUrl = normalizeUrl;
12exports.requestify = requestify;
13exports.getFilter = getFilter;
14exports.getModulesOptions = getModulesOptions;
15exports.getModulesPlugins = getModulesPlugins;
16exports.normalizeSourceMap = normalizeSourceMap;
17exports.getPreRequester = getPreRequester;
18exports.getImportCode = getImportCode;
19exports.getModuleCode = getModuleCode;
20exports.getExportCode = getExportCode;
21exports.resolveRequests = resolveRequests;
22exports.isUrlRequestable = isUrlRequestable;
23exports.sort = sort;
24exports.webpackIgnoreCommentRegexp = void 0;
25
26var _url = require("url");
27
28var _path = _interopRequireDefault(require("path"));
29
30var _loaderUtils = require("loader-utils");
31
32var _cssesc = _interopRequireDefault(require("cssesc"));
33
34var _postcssModulesValues = _interopRequireDefault(require("postcss-modules-values"));
35
36var _postcssModulesLocalByDefault = _interopRequireDefault(require("postcss-modules-local-by-default"));
37
38var _postcssModulesExtractImports = _interopRequireDefault(require("postcss-modules-extract-imports"));
39
40var _postcssModulesScope = _interopRequireDefault(require("postcss-modules-scope"));
41
42var _camelcase = _interopRequireDefault(require("camelcase"));
43
44function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
45
46/*
47 MIT License http://www.opensource.org/licenses/mit-license.php
48 Author Tobias Koppers @sokra
49*/
50const whitespace = "[\\x20\\t\\r\\n\\f]";
51const unescapeRegExp = new RegExp(`\\\\([\\da-f]{1,6}${whitespace}?|(${whitespace})|.)`, "ig");
52const matchNativeWin32Path = /^[A-Z]:[/\\]|^\\\\/i;
53const webpackIgnoreCommentRegexp = /webpackIgnore:(\s+)?(true|false)/;
54exports.webpackIgnoreCommentRegexp = webpackIgnoreCommentRegexp;
55
56function unescape(str) {
57 return str.replace(unescapeRegExp, (_, escaped, escapedWhitespace) => {
58 const high = `0x${escaped}` - 0x10000;
59 /* eslint-disable line-comment-position */
60 // NaN means non-codepoint
61 // Workaround erroneous numeric interpretation of +"0x"
62 // eslint-disable-next-line no-self-compare
63
64 return high !== high || escapedWhitespace ? escaped : high < 0 ? // BMP codepoint
65 String.fromCharCode(high + 0x10000) : // Supplemental Plane codepoint (surrogate pair)
66 // eslint-disable-next-line no-bitwise
67 String.fromCharCode(high >> 10 | 0xd800, high & 0x3ff | 0xdc00);
68 /* eslint-enable line-comment-position */
69 });
70}
71
72function normalizePath(file) {
73 return _path.default.sep === "\\" ? file.replace(/\\/g, "/") : file;
74} // eslint-disable-next-line no-control-regex
75
76
77const filenameReservedRegex = /[<>:"/\\|?*]/g; // eslint-disable-next-line no-control-regex
78
79const reControlChars = /[\u0000-\u001f\u0080-\u009f]/g;
80
81function escapeLocalIdent(localident) {
82 return (0, _cssesc.default)(localident // For `[hash]` placeholder
83 .replace(/^((-?[0-9])|--)/, "_$1").replace(filenameReservedRegex, "-").replace(reControlChars, "-").replace(/\./g, "-"), {
84 isIdentifier: true
85 });
86}
87
88function defaultGetLocalIdent(loaderContext, localIdentName, localName, options) {
89 const {
90 context,
91 hashPrefix
92 } = options;
93 const {
94 resourcePath
95 } = loaderContext;
96 const request = normalizePath(_path.default.relative(context, resourcePath)); // eslint-disable-next-line no-param-reassign
97
98 options.content = `${hashPrefix + request}\x00${localName}`;
99 return (0, _loaderUtils.interpolateName)(loaderContext, localIdentName, options);
100}
101
102function normalizeUrl(url, isStringValue) {
103 let normalizedUrl = url;
104
105 if (isStringValue && /\\(\n|\r\n|\r|\f)/.test(normalizedUrl)) {
106 normalizedUrl = normalizedUrl.replace(/\\(\n|\r\n|\r|\f)/g, "");
107 }
108
109 if (matchNativeWin32Path.test(url)) {
110 return decodeURI(normalizedUrl);
111 }
112
113 return decodeURI(unescape(normalizedUrl));
114}
115
116function requestify(url, rootContext) {
117 if (/^file:/i.test(url)) {
118 return (0, _url.fileURLToPath)(url);
119 }
120
121 return url.charAt(0) === "/" ? (0, _loaderUtils.urlToRequest)(url, rootContext) : (0, _loaderUtils.urlToRequest)(url);
122}
123
124function getFilter(filter, resourcePath) {
125 return (...args) => {
126 if (typeof filter === "function") {
127 return filter(...args, resourcePath);
128 }
129
130 return true;
131 };
132}
133
134function getValidLocalName(localName, exportLocalsConvention) {
135 if (exportLocalsConvention === "dashesOnly") {
136 return dashesCamelCase(localName);
137 }
138
139 return (0, _camelcase.default)(localName);
140}
141
142const moduleRegExp = /\.module(s)?\.\w+$/i;
143const icssRegExp = /\.icss\.\w+$/i;
144
145function getModulesOptions(rawOptions, loaderContext) {
146 const {
147 resourcePath
148 } = loaderContext;
149 let isIcss;
150
151 if (typeof rawOptions.modules === "undefined") {
152 const isModules = moduleRegExp.test(resourcePath);
153
154 if (!isModules) {
155 isIcss = icssRegExp.test(resourcePath);
156 }
157
158 if (!isModules && !isIcss) {
159 return false;
160 }
161 } else if (typeof rawOptions.modules === "boolean" && rawOptions.modules === false) {
162 return false;
163 }
164
165 let modulesOptions = {
166 compileType: isIcss ? "icss" : "module",
167 auto: true,
168 mode: "local",
169 exportGlobals: false,
170 localIdentName: "[hash:base64]",
171 localIdentContext: loaderContext.rootContext,
172 localIdentHashPrefix: "",
173 // eslint-disable-next-line no-undefined
174 localIdentRegExp: undefined,
175 // eslint-disable-next-line no-undefined
176 getLocalIdent: undefined,
177 namedExport: false,
178 exportLocalsConvention: "asIs",
179 exportOnlyLocals: false
180 };
181
182 if (typeof rawOptions.modules === "boolean" || typeof rawOptions.modules === "string") {
183 modulesOptions.mode = typeof rawOptions.modules === "string" ? rawOptions.modules : "local";
184 } else {
185 if (rawOptions.modules) {
186 if (typeof rawOptions.modules.auto === "boolean") {
187 const isModules = rawOptions.modules.auto && moduleRegExp.test(resourcePath);
188
189 if (!isModules) {
190 return false;
191 }
192 } else if (rawOptions.modules.auto instanceof RegExp) {
193 const isModules = rawOptions.modules.auto.test(resourcePath);
194
195 if (!isModules) {
196 return false;
197 }
198 } else if (typeof rawOptions.modules.auto === "function") {
199 const isModule = rawOptions.modules.auto(resourcePath);
200
201 if (!isModule) {
202 return false;
203 }
204 }
205
206 if (rawOptions.modules.namedExport === true && typeof rawOptions.modules.exportLocalsConvention === "undefined") {
207 modulesOptions.exportLocalsConvention = "camelCaseOnly";
208 }
209 }
210
211 modulesOptions = { ...modulesOptions,
212 ...(rawOptions.modules || {})
213 };
214 }
215
216 if (typeof modulesOptions.mode === "function") {
217 modulesOptions.mode = modulesOptions.mode(loaderContext.resourcePath);
218 }
219
220 if (modulesOptions.namedExport === true) {
221 if (rawOptions.esModule === false) {
222 throw new Error('The "modules.namedExport" option requires the "esModules" option to be enabled');
223 }
224
225 if (modulesOptions.exportLocalsConvention !== "camelCaseOnly" && modulesOptions.exportLocalsConvention !== "dashesOnly") {
226 throw new Error('The "modules.namedExport" option requires the "modules.exportLocalsConvention" option to be "camelCaseOnly" or "dashesOnly"');
227 }
228 }
229
230 if (/\[emoji(?::(\d+))?\]/i.test(modulesOptions.localIdentName)) {
231 loaderContext.emitWarning("Emoji is deprecated and will be removed in next major release.");
232 }
233
234 return modulesOptions;
235}
236
237function normalizeOptions(rawOptions, loaderContext) {
238 const modulesOptions = getModulesOptions(rawOptions, loaderContext);
239 return {
240 url: typeof rawOptions.url === "undefined" ? true : rawOptions.url,
241 import: typeof rawOptions.import === "undefined" ? true : rawOptions.import,
242 modules: modulesOptions,
243 sourceMap: typeof rawOptions.sourceMap === "boolean" ? rawOptions.sourceMap : loaderContext.sourceMap,
244 importLoaders: typeof rawOptions.importLoaders === "string" ? parseInt(rawOptions.importLoaders, 10) : rawOptions.importLoaders,
245 esModule: typeof rawOptions.esModule === "undefined" ? true : rawOptions.esModule
246 };
247}
248
249function shouldUseImportPlugin(options) {
250 if (options.modules.exportOnlyLocals) {
251 return false;
252 }
253
254 if (typeof options.import === "boolean") {
255 return options.import;
256 }
257
258 return true;
259}
260
261function shouldUseURLPlugin(options) {
262 if (options.modules.exportOnlyLocals) {
263 return false;
264 }
265
266 if (typeof options.url === "boolean") {
267 return options.url;
268 }
269
270 return true;
271}
272
273function shouldUseModulesPlugins(options) {
274 return options.modules.compileType === "module";
275}
276
277function shouldUseIcssPlugin(options) {
278 return options.icss === true || Boolean(options.modules);
279}
280
281function getModulesPlugins(options, loaderContext) {
282 const {
283 mode,
284 getLocalIdent,
285 localIdentName,
286 localIdentContext,
287 localIdentHashPrefix,
288 localIdentRegExp
289 } = options.modules;
290 let plugins = [];
291
292 try {
293 plugins = [_postcssModulesValues.default, (0, _postcssModulesLocalByDefault.default)({
294 mode
295 }), (0, _postcssModulesExtractImports.default)(), (0, _postcssModulesScope.default)({
296 generateScopedName(exportName) {
297 let localIdent;
298
299 if (typeof getLocalIdent !== "undefined") {
300 localIdent = getLocalIdent(loaderContext, localIdentName, unescape(exportName), {
301 context: localIdentContext,
302 hashPrefix: localIdentHashPrefix,
303 regExp: localIdentRegExp
304 });
305 } // A null/undefined value signals that we should invoke the default
306 // getLocalIdent method.
307
308
309 if (typeof localIdent === "undefined" || localIdent === null) {
310 localIdent = defaultGetLocalIdent(loaderContext, localIdentName, unescape(exportName), {
311 context: localIdentContext,
312 hashPrefix: localIdentHashPrefix,
313 regExp: localIdentRegExp
314 });
315 return escapeLocalIdent(localIdent).replace(/\\\[local\\]/gi, exportName);
316 }
317
318 return escapeLocalIdent(localIdent);
319 },
320
321 exportGlobals: options.modules.exportGlobals
322 })];
323 } catch (error) {
324 loaderContext.emitError(error);
325 }
326
327 return plugins;
328}
329
330const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i;
331const ABSOLUTE_SCHEME = /^[a-z0-9+\-.]+:/i;
332
333function getURLType(source) {
334 if (source[0] === "/") {
335 if (source[1] === "/") {
336 return "scheme-relative";
337 }
338
339 return "path-absolute";
340 }
341
342 if (IS_NATIVE_WIN32_PATH.test(source)) {
343 return "path-absolute";
344 }
345
346 return ABSOLUTE_SCHEME.test(source) ? "absolute" : "path-relative";
347}
348
349function normalizeSourceMap(map, resourcePath) {
350 let newMap = map; // Some loader emit source map as string
351 // Strip any JSON XSSI avoidance prefix from the string (as documented in the source maps specification), and then parse the string as JSON.
352
353 if (typeof newMap === "string") {
354 newMap = JSON.parse(newMap);
355 }
356
357 delete newMap.file;
358 const {
359 sourceRoot
360 } = newMap;
361 delete newMap.sourceRoot;
362
363 if (newMap.sources) {
364 // Source maps should use forward slash because it is URLs (https://github.com/mozilla/source-map/issues/91)
365 // We should normalize path because previous loaders like `sass-loader` using backslash when generate source map
366 newMap.sources = newMap.sources.map(source => {
367 // Non-standard syntax from `postcss`
368 if (source.indexOf("<") === 0) {
369 return source;
370 }
371
372 const sourceType = getURLType(source); // Do no touch `scheme-relative` and `absolute` URLs
373
374 if (sourceType === "path-relative" || sourceType === "path-absolute") {
375 const absoluteSource = sourceType === "path-relative" && sourceRoot ? _path.default.resolve(sourceRoot, normalizePath(source)) : normalizePath(source);
376 return _path.default.relative(_path.default.dirname(resourcePath), absoluteSource);
377 }
378
379 return source;
380 });
381 }
382
383 return newMap;
384}
385
386function getPreRequester({
387 loaders,
388 loaderIndex
389}) {
390 const cache = Object.create(null);
391 return number => {
392 if (cache[number]) {
393 return cache[number];
394 }
395
396 if (number === false) {
397 cache[number] = "";
398 } else {
399 const loadersRequest = loaders.slice(loaderIndex, loaderIndex + 1 + (typeof number !== "number" ? 0 : number)).map(x => x.request).join("!");
400 cache[number] = `-!${loadersRequest}!`;
401 }
402
403 return cache[number];
404 };
405}
406
407function getImportCode(imports, options) {
408 let code = "";
409
410 for (const item of imports) {
411 const {
412 importName,
413 url,
414 icss
415 } = item;
416
417 if (options.esModule) {
418 if (icss && options.modules.namedExport) {
419 code += `import ${options.modules.exportOnlyLocals ? "" : `${importName}, `}* as ${importName}_NAMED___ from ${url};\n`;
420 } else {
421 code += `import ${importName} from ${url};\n`;
422 }
423 } else {
424 code += `var ${importName} = require(${url});\n`;
425 }
426 }
427
428 return code ? `// Imports\n${code}` : "";
429}
430
431function normalizeSourceMapForRuntime(map, loaderContext) {
432 const resultMap = map ? map.toJSON() : null;
433
434 if (resultMap) {
435 delete resultMap.file;
436 resultMap.sourceRoot = "";
437 resultMap.sources = resultMap.sources.map(source => {
438 // Non-standard syntax from `postcss`
439 if (source.indexOf("<") === 0) {
440 return source;
441 }
442
443 const sourceType = getURLType(source);
444
445 if (sourceType !== "path-relative") {
446 return source;
447 }
448
449 const resourceDirname = _path.default.dirname(loaderContext.resourcePath);
450
451 const absoluteSource = _path.default.resolve(resourceDirname, source);
452
453 const contextifyPath = normalizePath(_path.default.relative(loaderContext.rootContext, absoluteSource));
454 return `webpack://./${contextifyPath}`;
455 });
456 }
457
458 return JSON.stringify(resultMap);
459}
460
461function getModuleCode(result, api, replacements, options, loaderContext) {
462 if (options.modules.exportOnlyLocals === true) {
463 return "";
464 }
465
466 const sourceMapValue = options.sourceMap ? `,${normalizeSourceMapForRuntime(result.map, loaderContext)}` : "";
467 let code = JSON.stringify(result.css);
468 let beforeCode = `var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(${options.sourceMap ? "___CSS_LOADER_API_SOURCEMAP_IMPORT___" : "function(i){return i[1]}"});\n`;
469
470 for (const item of api) {
471 const {
472 url,
473 media,
474 dedupe
475 } = item;
476 beforeCode += url ? `___CSS_LOADER_EXPORT___.push([module.id, ${JSON.stringify(`@import url(${url});`)}${media ? `, ${JSON.stringify(media)}` : ""}]);\n` : `___CSS_LOADER_EXPORT___.i(${item.importName}${media ? `, ${JSON.stringify(media)}` : dedupe ? ', ""' : ""}${dedupe ? ", true" : ""});\n`;
477 }
478
479 for (const item of replacements) {
480 const {
481 replacementName,
482 importName,
483 localName
484 } = item;
485
486 if (localName) {
487 code = code.replace(new RegExp(replacementName, "g"), () => options.modules.namedExport ? `" + ${importName}_NAMED___[${JSON.stringify(getValidLocalName(localName, options.modules.exportLocalsConvention))}] + "` : `" + ${importName}.locals[${JSON.stringify(localName)}] + "`);
488 } else {
489 const {
490 hash,
491 needQuotes
492 } = item;
493 const getUrlOptions = [].concat(hash ? [`hash: ${JSON.stringify(hash)}`] : []).concat(needQuotes ? "needQuotes: true" : []);
494 const preparedOptions = getUrlOptions.length > 0 ? `, { ${getUrlOptions.join(", ")} }` : "";
495 beforeCode += `var ${replacementName} = ___CSS_LOADER_GET_URL_IMPORT___(${importName}${preparedOptions});\n`;
496 code = code.replace(new RegExp(replacementName, "g"), () => `" + ${replacementName} + "`);
497 }
498 }
499
500 return `${beforeCode}// Module\n___CSS_LOADER_EXPORT___.push([module.id, ${code}, ""${sourceMapValue}]);\n`;
501}
502
503function dashesCamelCase(str) {
504 return str.replace(/-+(\w)/g, (match, firstLetter) => firstLetter.toUpperCase());
505}
506
507function getExportCode(exports, replacements, options) {
508 let code = "// Exports\n";
509 let localsCode = "";
510
511 const addExportToLocalsCode = (name, value) => {
512 if (options.modules.namedExport) {
513 localsCode += `export const ${name} = ${JSON.stringify(value)};\n`;
514 } else {
515 if (localsCode) {
516 localsCode += `,\n`;
517 }
518
519 localsCode += `\t${JSON.stringify(name)}: ${JSON.stringify(value)}`;
520 }
521 };
522
523 for (const {
524 name,
525 value
526 } of exports) {
527 switch (options.modules.exportLocalsConvention) {
528 case "camelCase":
529 {
530 addExportToLocalsCode(name, value);
531 const modifiedName = (0, _camelcase.default)(name);
532
533 if (modifiedName !== name) {
534 addExportToLocalsCode(modifiedName, value);
535 }
536
537 break;
538 }
539
540 case "camelCaseOnly":
541 {
542 addExportToLocalsCode((0, _camelcase.default)(name), value);
543 break;
544 }
545
546 case "dashes":
547 {
548 addExportToLocalsCode(name, value);
549 const modifiedName = dashesCamelCase(name);
550
551 if (modifiedName !== name) {
552 addExportToLocalsCode(modifiedName, value);
553 }
554
555 break;
556 }
557
558 case "dashesOnly":
559 {
560 addExportToLocalsCode(dashesCamelCase(name), value);
561 break;
562 }
563
564 case "asIs":
565 default:
566 addExportToLocalsCode(name, value);
567 break;
568 }
569 }
570
571 for (const item of replacements) {
572 const {
573 replacementName,
574 localName
575 } = item;
576
577 if (localName) {
578 const {
579 importName
580 } = item;
581 localsCode = localsCode.replace(new RegExp(replacementName, "g"), () => {
582 if (options.modules.namedExport) {
583 return `" + ${importName}_NAMED___[${JSON.stringify(getValidLocalName(localName, options.modules.exportLocalsConvention))}] + "`;
584 } else if (options.modules.exportOnlyLocals) {
585 return `" + ${importName}[${JSON.stringify(localName)}] + "`;
586 }
587
588 return `" + ${importName}.locals[${JSON.stringify(localName)}] + "`;
589 });
590 } else {
591 localsCode = localsCode.replace(new RegExp(replacementName, "g"), () => `" + ${replacementName} + "`);
592 }
593 }
594
595 if (options.modules.exportOnlyLocals) {
596 code += options.modules.namedExport ? localsCode : `${options.esModule ? "export default" : "module.exports ="} {\n${localsCode}\n};\n`;
597 return code;
598 }
599
600 if (localsCode) {
601 code += options.modules.namedExport ? localsCode : `___CSS_LOADER_EXPORT___.locals = {\n${localsCode}\n};\n`;
602 }
603
604 code += `${options.esModule ? "export default" : "module.exports ="} ___CSS_LOADER_EXPORT___;\n`;
605 return code;
606}
607
608async function resolveRequests(resolve, context, possibleRequests) {
609 return resolve(context, possibleRequests[0]).then(result => result).catch(error => {
610 const [, ...tailPossibleRequests] = possibleRequests;
611
612 if (tailPossibleRequests.length === 0) {
613 throw error;
614 }
615
616 return resolveRequests(resolve, context, tailPossibleRequests);
617 });
618}
619
620function isUrlRequestable(url) {
621 // Protocol-relative URLs
622 if (/^\/\//.test(url)) {
623 return false;
624 } // `file:` protocol
625
626
627 if (/^file:/i.test(url)) {
628 return true;
629 } // Absolute URLs
630
631
632 if (/^[a-z][a-z0-9+.-]*:/i.test(url) && !matchNativeWin32Path.test(url)) {
633 return false;
634 } // `#` URLs
635
636
637 if (/^#/.test(url)) {
638 return false;
639 }
640
641 return true;
642}
643
644function sort(a, b) {
645 return a.index - b.index;
646}
\No newline at end of file