UNPKG

30.8 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.c0ControlCodesExclude = c0ControlCodesExclude;
7exports.defaultMinimizerOptions = void 0;
8exports.getExportCode = getExportCode;
9exports.getFilter = getFilter;
10exports.getImportCode = getImportCode;
11exports.getModuleCode = getModuleCode;
12exports.isUrlRequestable = isUrlRequestable;
13exports.normalizeOptions = normalizeOptions;
14exports.parseSrc = parseSrc;
15exports.parseSrcset = parseSrcset;
16exports.pluginRunner = pluginRunner;
17exports.requestify = requestify;
18exports.srcType = srcType;
19exports.srcsetType = srcsetType;
20exports.traverse = traverse;
21exports.webpackIgnoreCommentRegexp = void 0;
22
23var _path = _interopRequireDefault(require("path"));
24
25var _HtmlSourceError = _interopRequireDefault(require("./HtmlSourceError"));
26
27function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
28
29function isASCIIWhitespace(character) {
30 return (// Horizontal tab
31 character === "\u0009" || // New line
32 character === "\u000A" || // Form feed
33 character === "\u000C" || // Carriage return
34 character === "\u000D" || // Space
35 character === "\u0020"
36 );
37} // (Don't use \s, to avoid matching non-breaking space)
38// eslint-disable-next-line no-control-regex
39
40
41const regexLeadingSpaces = /^[ \t\n\r\u000c]+/; // eslint-disable-next-line no-control-regex
42
43const regexLeadingCommasOrSpaces = /^[, \t\n\r\u000c]+/; // eslint-disable-next-line no-control-regex
44
45const regexLeadingNotSpaces = /^[^ \t\n\r\u000c]+/;
46const regexTrailingCommas = /[,]+$/;
47const regexNonNegativeInteger = /^\d+$/; // ( Positive or negative or unsigned integers or decimals, without or without exponents.
48// Must include at least one digit.
49// According to spec tests any decimal point must be followed by a digit.
50// No leading plus sign is allowed.)
51// https://html.spec.whatwg.org/multipage/infrastructure.html#valid-floating-point-number
52
53const regexFloatingPoint = /^-?(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/;
54
55function parseSrcset(input) {
56 // 1. Let input be the value passed to this algorithm.
57 const inputLength = input.length;
58 let url;
59 let descriptors;
60 let currentDescriptor;
61 let state;
62 let c; // 2. Let position be a pointer into input, initially pointing at the start
63 // of the string.
64
65 let position = 0;
66 let startOffset; // eslint-disable-next-line consistent-return
67
68 function collectCharacters(regEx) {
69 let chars;
70 const match = regEx.exec(input.substring(position));
71
72 if (match) {
73 [chars] = match;
74 position += chars.length;
75 return chars;
76 }
77 } // 3. Let candidates be an initially empty source set.
78
79
80 const candidates = []; // 4. Splitting loop: Collect a sequence of characters that are space
81 // characters or U+002C COMMA characters. If any U+002C COMMA characters
82 // were collected, that is a parse error.
83 // eslint-disable-next-line no-constant-condition
84
85 while (true) {
86 collectCharacters(regexLeadingCommasOrSpaces); // 5. If position is past the end of input, return candidates and abort these steps.
87
88 if (position >= inputLength) {
89 if (candidates.length === 0) {
90 throw new Error("Must contain one or more image candidate strings");
91 } // (we're done, this is the sole return path)
92
93
94 return candidates;
95 } // 6. Collect a sequence of characters that are not space characters,
96 // and let that be url.
97
98
99 startOffset = position;
100 url = collectCharacters(regexLeadingNotSpaces); // 7. Let descriptors be a new empty list.
101
102 descriptors = []; // 8. If url ends with a U+002C COMMA character (,), follow these substeps:
103 // (1). Remove all trailing U+002C COMMA characters from url. If this removed
104 // more than one character, that is a parse error.
105
106 if (url.slice(-1) === ",") {
107 url = url.replace(regexTrailingCommas, ""); // (Jump ahead to step 9 to skip tokenization and just push the candidate).
108
109 parseDescriptors();
110 } // Otherwise, follow these substeps:
111 else {
112 tokenize();
113 } // 16. Return to the step labeled splitting loop.
114
115 }
116 /**
117 * Tokenizes descriptor properties prior to parsing
118 * Returns undefined.
119 */
120
121
122 function tokenize() {
123 // 8.1. Descriptor tokenizer: Skip whitespace
124 collectCharacters(regexLeadingSpaces); // 8.2. Let current descriptor be the empty string.
125
126 currentDescriptor = ""; // 8.3. Let state be in descriptor.
127
128 state = "in descriptor"; // eslint-disable-next-line no-constant-condition
129
130 while (true) {
131 // 8.4. Let c be the character at position.
132 c = input.charAt(position); // Do the following depending on the value of state.
133 // For the purpose of this step, "EOF" is a special character representing
134 // that position is past the end of input.
135 // In descriptor
136
137 if (state === "in descriptor") {
138 // Do the following, depending on the value of c:
139 // Space character
140 // If current descriptor is not empty, append current descriptor to
141 // descriptors and let current descriptor be the empty string.
142 // Set state to after descriptor.
143 if (isASCIIWhitespace(c)) {
144 if (currentDescriptor) {
145 descriptors.push(currentDescriptor);
146 currentDescriptor = "";
147 state = "after descriptor";
148 }
149 } // U+002C COMMA (,)
150 // Advance position to the next character in input. If current descriptor
151 // is not empty, append current descriptor to descriptors. Jump to the step
152 // labeled descriptor parser.
153 else if (c === ",") {
154 position += 1;
155
156 if (currentDescriptor) {
157 descriptors.push(currentDescriptor);
158 }
159
160 parseDescriptors();
161 return;
162 } // U+0028 LEFT PARENTHESIS (()
163 // Append c to current descriptor. Set state to in parens.
164 else if (c === "\u0028") {
165 currentDescriptor += c;
166 state = "in parens";
167 } // EOF
168 // If current descriptor is not empty, append current descriptor to
169 // descriptors. Jump to the step labeled descriptor parser.
170 else if (c === "") {
171 if (currentDescriptor) {
172 descriptors.push(currentDescriptor);
173 }
174
175 parseDescriptors();
176 return; // Anything else
177 // Append c to current descriptor.
178 } else {
179 currentDescriptor += c;
180 }
181 } // In parens
182 else if (state === "in parens") {
183 // U+0029 RIGHT PARENTHESIS ())
184 // Append c to current descriptor. Set state to in descriptor.
185 if (c === ")") {
186 currentDescriptor += c;
187 state = "in descriptor";
188 } // EOF
189 // Append current descriptor to descriptors. Jump to the step labeled
190 // descriptor parser.
191 else if (c === "") {
192 descriptors.push(currentDescriptor);
193 parseDescriptors();
194 return;
195 } // Anything else
196 // Append c to current descriptor.
197 else {
198 currentDescriptor += c;
199 }
200 } // After descriptor
201 else if (state === "after descriptor") {
202 // Do the following, depending on the value of c:
203 if (isASCIIWhitespace(c)) {// Space character: Stay in this state.
204 } // EOF: Jump to the step labeled descriptor parser.
205 else if (c === "") {
206 parseDescriptors();
207 return;
208 } // Anything else
209 // Set state to in descriptor. Set position to the previous character in input.
210 else {
211 state = "in descriptor";
212 position -= 1;
213 }
214 } // Advance position to the next character in input.
215
216
217 position += 1;
218 }
219 }
220 /**
221 * Adds descriptor properties to a candidate, pushes to the candidates array
222 * @return undefined
223 */
224 // Declared outside of the while loop so that it's only created once.
225
226
227 function parseDescriptors() {
228 // 9. Descriptor parser: Let error be no.
229 let pError = false; // 10. Let width be absent.
230 // 11. Let density be absent.
231 // 12. Let future-compat-h be absent. (We're implementing it now as h)
232
233 let w;
234 let d;
235 let h;
236 let i;
237 const candidate = {};
238 let desc;
239 let lastChar;
240 let value;
241 let intVal;
242 let floatVal; // 13. For each descriptor in descriptors, run the appropriate set of steps
243 // from the following list:
244
245 for (i = 0; i < descriptors.length; i++) {
246 desc = descriptors[i];
247 lastChar = desc[desc.length - 1];
248 value = desc.substring(0, desc.length - 1);
249 intVal = parseInt(value, 10);
250 floatVal = parseFloat(value); // If the descriptor consists of a valid non-negative integer followed by
251 // a U+0077 LATIN SMALL LETTER W character
252
253 if (regexNonNegativeInteger.test(value) && lastChar === "w") {
254 // If width and density are not both absent, then let error be yes.
255 if (w || d) {
256 pError = true;
257 } // Apply the rules for parsing non-negative integers to the descriptor.
258 // If the result is zero, let error be yes.
259 // Otherwise, let width be the result.
260
261
262 if (intVal === 0) {
263 pError = true;
264 } else {
265 w = intVal;
266 }
267 } // If the descriptor consists of a valid floating-point number followed by
268 // a U+0078 LATIN SMALL LETTER X character
269 else if (regexFloatingPoint.test(value) && lastChar === "x") {
270 // If width, density and future-compat-h are not all absent, then let error
271 // be yes.
272 if (w || d || h) {
273 pError = true;
274 } // Apply the rules for parsing floating-point number values to the descriptor.
275 // If the result is less than zero, let error be yes. Otherwise, let density
276 // be the result.
277
278
279 if (floatVal < 0) {
280 pError = true;
281 } else {
282 d = floatVal;
283 }
284 } // If the descriptor consists of a valid non-negative integer followed by
285 // a U+0068 LATIN SMALL LETTER H character
286 else if (regexNonNegativeInteger.test(value) && lastChar === "h") {
287 // If height and density are not both absent, then let error be yes.
288 if (h || d) {
289 pError = true;
290 } // Apply the rules for parsing non-negative integers to the descriptor.
291 // If the result is zero, let error be yes. Otherwise, let future-compat-h
292 // be the result.
293
294
295 if (intVal === 0) {
296 pError = true;
297 } else {
298 h = intVal;
299 } // Anything else, Let error be yes.
300
301 } else {
302 pError = true;
303 }
304 } // 15. If error is still no, then append a new image source to candidates whose
305 // URL is url, associated with a width width if not absent and a pixel
306 // density density if not absent. Otherwise, there is a parse error.
307
308
309 if (!pError) {
310 candidate.source = {
311 value: url,
312 startOffset
313 };
314
315 if (w) {
316 candidate.width = {
317 value: w
318 };
319 }
320
321 if (d) {
322 candidate.density = {
323 value: d
324 };
325 }
326
327 if (h) {
328 candidate.height = {
329 value: h
330 };
331 }
332
333 candidates.push(candidate);
334 } else {
335 throw new Error(`Invalid srcset descriptor found in '${input}' at '${desc}'`);
336 }
337 }
338}
339
340function parseSrc(input) {
341 if (!input) {
342 throw new Error("Must be non-empty");
343 }
344
345 let startOffset = 0;
346 let value = input;
347
348 while (isASCIIWhitespace(value.substring(0, 1))) {
349 startOffset += 1;
350 value = value.substring(1, value.length);
351 }
352
353 while (isASCIIWhitespace(value.substring(value.length - 1, value.length))) {
354 value = value.substring(0, value.length - 1);
355 }
356
357 if (!value) {
358 throw new Error("Must be non-empty");
359 }
360
361 return {
362 value,
363 startOffset
364 };
365}
366
367const WINDOWS_ABS_PATH_REGEXP = /^[a-zA-Z]:[\\/]|^\\\\/;
368
369function isUrlRequestable(url) {
370 // Protocol-relative URLs
371 if (/^\/\//.test(url)) {
372 return false;
373 } // `file:` protocol
374
375
376 if (/^file:/i.test(url)) {
377 return true;
378 } // Absolute URLs
379
380
381 if (/^[a-z][a-z0-9+.-]*:/i.test(url) && !WINDOWS_ABS_PATH_REGEXP.test(url)) {
382 return false;
383 } // It's some kind of url for a template
384
385
386 if (/^[{}[\]#*;,'§$%&(=?`´^°<>]/.test(url)) {
387 return false;
388 }
389
390 return true;
391}
392
393const WINDOWS_PATH_SEPARATOR_REGEXP = /\\/g;
394const RELATIVE_PATH_REGEXP = /^\.\.?[/\\]/;
395
396const absoluteToRequest = (context, maybeAbsolutePath) => {
397 if (maybeAbsolutePath[0] === "/") {
398 if (maybeAbsolutePath.length > 1 && maybeAbsolutePath[maybeAbsolutePath.length - 1] === "/") {
399 // this 'path' is actually a regexp generated by dynamic requires.
400 // Don't treat it as an absolute path.
401 return maybeAbsolutePath;
402 }
403
404 const querySplitPos = maybeAbsolutePath.indexOf("?");
405 let resource = querySplitPos === -1 ? maybeAbsolutePath : maybeAbsolutePath.slice(0, querySplitPos);
406 resource = _path.default.posix.relative(context, resource);
407
408 if (!resource.startsWith("../")) {
409 resource = `./${resource}`;
410 }
411
412 return querySplitPos === -1 ? resource : resource + maybeAbsolutePath.slice(querySplitPos);
413 }
414
415 if (WINDOWS_ABS_PATH_REGEXP.test(maybeAbsolutePath)) {
416 const querySplitPos = maybeAbsolutePath.indexOf("?");
417 let resource = querySplitPos === -1 ? maybeAbsolutePath : maybeAbsolutePath.slice(0, querySplitPos);
418 resource = _path.default.win32.relative(context, resource);
419
420 if (!WINDOWS_ABS_PATH_REGEXP.test(resource)) {
421 resource = resource.replace(WINDOWS_PATH_SEPARATOR_REGEXP, "/");
422
423 if (!resource.startsWith("../")) {
424 resource = `./${resource}`;
425 }
426 }
427
428 return querySplitPos === -1 ? resource : resource + maybeAbsolutePath.slice(querySplitPos);
429 }
430
431 if (!RELATIVE_PATH_REGEXP.test(maybeAbsolutePath)) {
432 return `./${maybeAbsolutePath.replace(WINDOWS_PATH_SEPARATOR_REGEXP, "/")}`;
433 } // not an absolute path
434
435
436 return maybeAbsolutePath;
437};
438
439const contextify = (context, request) => request.split("!").map(r => absoluteToRequest(context, r)).join("!");
440
441const MODULE_REQUEST_REGEXP = /^[^?]*~/;
442
443function requestify(context, request) {
444 const isWindowsAbsolutePath = WINDOWS_ABS_PATH_REGEXP.test(request);
445 const newRequest = isWindowsAbsolutePath ? decodeURI(request).replace(/[\t\n\r]/g, "") : decodeURI(request).replace(/[\t\n\r]/g, "").replace(/\\/g, "/");
446
447 if (isWindowsAbsolutePath || newRequest[0] === "/") {
448 return newRequest;
449 }
450
451 if (/^file:/i.test(newRequest)) {
452 return newRequest;
453 } // A `~` makes the url an module
454
455
456 if (MODULE_REQUEST_REGEXP.test(newRequest)) {
457 return newRequest.replace(MODULE_REQUEST_REGEXP, "");
458 } // every other url is threaded like a relative url
459
460
461 return contextify(context, newRequest);
462}
463
464function isProductionMode(loaderContext) {
465 return loaderContext.mode === "production" || !loaderContext.mode;
466}
467
468const defaultMinimizerOptions = {
469 caseSensitive: true,
470 // `collapseBooleanAttributes` is not always safe, since this can break CSS attribute selectors and not safe for XHTML
471 collapseWhitespace: true,
472 conservativeCollapse: true,
473 keepClosingSlash: true,
474 // We need ability to use cssnano, or setup own function without extra dependencies
475 minifyCSS: true,
476 minifyJS: true,
477 // `minifyURLs` is unsafe, because we can't guarantee what the base URL is
478 // `removeAttributeQuotes` is not safe in some rare cases, also HTML spec recommends against doing this
479 removeComments: true,
480 // `removeEmptyAttributes` is not safe, can affect certain style or script behavior, look at https://github.com/webpack-contrib/html-loader/issues/323
481 // `removeRedundantAttributes` is not safe, can affect certain style or script behavior, look at https://github.com/webpack-contrib/html-loader/issues/323
482 removeScriptTypeAttributes: true,
483 removeStyleLinkTypeAttributes: true // `useShortDoctype` is not safe for XHTML
484
485};
486exports.defaultMinimizerOptions = defaultMinimizerOptions;
487
488function getMinimizeOption(rawOptions, loaderContext) {
489 if (typeof rawOptions.minimize === "undefined") {
490 return isProductionMode(loaderContext) ? defaultMinimizerOptions : false;
491 }
492
493 if (typeof rawOptions.minimize === "boolean") {
494 return rawOptions.minimize === true ? defaultMinimizerOptions : false;
495 }
496
497 return rawOptions.minimize;
498}
499
500function getAttributeValue(attributes, name) {
501 const [result] = attributes.filter(i => i.name.toLowerCase() === name);
502 return typeof result === "undefined" ? result : result.value;
503}
504
505function scriptSrcFilter(tag, attribute, attributes) {
506 let type = getAttributeValue(attributes, "type");
507
508 if (!type) {
509 return true;
510 }
511
512 type = type.trim();
513
514 if (!type) {
515 return false;
516 }
517
518 if (type !== "module" && type !== "text/javascript" && type !== "application/javascript") {
519 return false;
520 }
521
522 return true;
523}
524
525function linkHrefFilter(tag, attribute, attributes) {
526 let rel = getAttributeValue(attributes, "rel");
527
528 if (!rel) {
529 return false;
530 }
531
532 rel = rel.trim();
533
534 if (!rel) {
535 return false;
536 }
537
538 rel = rel.toLowerCase();
539 const usedRels = rel.split(" ").filter(value => value);
540 const allowedRels = ["stylesheet", "icon", "mask-icon", "apple-touch-icon", "apple-touch-icon-precomposed", "apple-touch-startup-image", "manifest", "prefetch", "preload"];
541 return allowedRels.filter(value => usedRels.includes(value)).length > 0;
542}
543
544const META = new Map([["name", new Set([// msapplication-TileImage
545"msapplication-tileimage", "msapplication-square70x70logo", "msapplication-square150x150logo", "msapplication-wide310x150logo", "msapplication-square310x310logo", "msapplication-config", "msapplication-task", "twitter:image"])], ["property", new Set(["og:image", "og:image:url", "og:image:secure_url", "og:audio", "og:audio:secure_url", "og:video", "og:video:secure_url", "vk:image"])], ["itemprop", new Set(["image", "logo", "screenshot", "thumbnailurl", "contenturl", "downloadurl", "duringmedia", "embedurl", "installurl", "layoutimage"])]]);
546
547function linkItempropFilter(tag, attribute, attributes) {
548 let name = getAttributeValue(attributes, "itemprop");
549
550 if (name) {
551 name = name.trim();
552
553 if (!name) {
554 return false;
555 }
556
557 name = name.toLowerCase();
558 return META.get("itemprop").has(name);
559 }
560
561 return false;
562}
563
564function linkUnionFilter(tag, attribute, attributes) {
565 return linkHrefFilter(tag, attribute, attributes) || linkItempropFilter(tag, attribute, attributes);
566}
567
568function metaContentFilter(tag, attribute, attributes) {
569 for (const item of META) {
570 const [key, allowedNames] = item;
571 let name = getAttributeValue(attributes, key);
572
573 if (name) {
574 name = name.trim();
575
576 if (!name) {
577 // eslint-disable-next-line no-continue
578 continue;
579 }
580
581 name = name.toLowerCase();
582 return allowedNames.has(name);
583 }
584 }
585
586 return false;
587}
588
589function srcType(options) {
590 let source;
591
592 try {
593 source = parseSrc(options.value);
594 } catch (error) {
595 throw new _HtmlSourceError.default(`Bad value for attribute "${options.attribute}" on element "${options.tag}": ${error.message}`, options.attributeStartOffset, options.attributeEndOffset, options.html);
596 }
597
598 source = c0ControlCodesExclude(source);
599
600 if (!isUrlRequestable(source.value)) {
601 return [];
602 }
603
604 const startOffset = options.valueStartOffset + source.startOffset;
605 const endOffset = startOffset + source.value.length;
606 return [{
607 value: source.value,
608 startOffset,
609 endOffset
610 }];
611}
612
613function srcsetType(options) {
614 let sourceSet;
615
616 try {
617 sourceSet = parseSrcset(options.value);
618 } catch (error) {
619 throw new _HtmlSourceError.default(`Bad value for attribute "${options.attribute}" on element "${options.tag}": ${error.message}`, options.attributeStartOffset, options.attributeEndOffset, options.html);
620 }
621
622 const result = [];
623 sourceSet.forEach(sourceItem => {
624 let {
625 source
626 } = sourceItem;
627 source = c0ControlCodesExclude(source);
628
629 if (!isUrlRequestable(source.value)) {
630 return false;
631 }
632
633 const startOffset = options.valueStartOffset + source.startOffset;
634 const endOffset = startOffset + source.value.length;
635 result.push({
636 value: source.value,
637 startOffset,
638 endOffset
639 });
640 return false;
641 });
642 return result;
643}
644
645function metaContentType(options) {
646 const isMsapplicationTask = options.attributes.find(i => i.name.toLowerCase() === "name" && i.value.toLowerCase() === "msapplication-task");
647
648 if (isMsapplicationTask) {
649 let startOffset = options.valueStartOffset;
650 let endOffset = options.valueStartOffset;
651 let value;
652 const parts = options.value.split(";");
653
654 for (const [index, part] of parts.entries()) {
655 const isLastIteration = index === parts.length - 1;
656
657 if (/^icon-uri/i.test(part.trim())) {
658 const [name, src] = part.split("=");
659 startOffset += name.length + 1;
660 let source;
661
662 try {
663 source = parseSrc(src);
664 } catch (error) {
665 throw new _HtmlSourceError.default(`Bad value for attribute "icon-uri" on element "${options.tag}": ${error.message}`, options.attributeStartOffset, options.attributeEndOffset, options.html);
666 }
667
668 source = c0ControlCodesExclude(source);
669 ({
670 value
671 } = source);
672 startOffset += source.startOffset;
673 endOffset = startOffset + value.length;
674 break;
675 } // +1 because of ";"
676
677
678 startOffset += part.length + (isLastIteration ? 0 : 1);
679 }
680
681 if (!value) {
682 return [];
683 }
684
685 return [{
686 startOffset,
687 endOffset,
688 value
689 }];
690 }
691
692 return srcType(options);
693} // function webpackImportType(options) {
694// let source;
695//
696// try {
697// source = parseSrc(options.value);
698// } catch (error) {
699// throw new HtmlSourceError(
700// `Bad value for attribute "${options.attribute}" on element "${options.tag}": ${error.message}`,
701// options.attributeStartOffset,
702// options.attributeEndOffset,
703// options.html
704// );
705// }
706//
707// source = c0ControlCodesExclude(source);
708//
709// if (!isUrlRequestable(source.value)) {
710// return [];
711// }
712//
713// const { startOffset } = options.startTag;
714// let { endOffset } = options.startTag;
715//
716// if (options.endTag) {
717// ({ endOffset } = options.endTag);
718// }
719//
720// return [
721// {
722// format: 'import',
723// runtime: false,
724// value: source.value,
725// startOffset,
726// endOffset,
727// },
728// ];
729// }
730
731
732const defaultSources = new Map([["audio", new Map([["src", {
733 type: srcType
734}]])], ["embed", new Map([["src", {
735 type: srcType
736}]])], ["img", new Map([["src", {
737 type: srcType
738}], ["srcset", {
739 type: srcsetType
740}]])], ["input", new Map([["src", {
741 type: srcType
742}]])], ["link", new Map([["href", {
743 type: srcType,
744 filter: linkUnionFilter
745}], ["imagesrcset", {
746 type: srcsetType,
747 filter: linkHrefFilter
748}]])], ["meta", new Map([["content", {
749 type: metaContentType,
750 filter: metaContentFilter
751}]])], ["object", new Map([["data", {
752 type: srcType
753}]])], ["script", new Map([["src", {
754 type: srcType,
755 filter: scriptSrcFilter
756}], // Using href with <script> is described here: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/script
757["href", {
758 type: srcType,
759 filter: scriptSrcFilter
760}], ["xlink:href", {
761 type: srcType,
762 filter: scriptSrcFilter
763}]])], ["source", new Map([["src", {
764 type: srcType
765}], ["srcset", {
766 type: srcsetType
767}]])], ["track", new Map([["src", {
768 type: srcType
769}]])], ["video", new Map([["poster", {
770 type: srcType
771}], ["src", {
772 type: srcType
773}]])], // SVG
774["image", new Map([["xlink:href", {
775 type: srcType
776}], ["href", {
777 type: srcType
778}]])], ["use", new Map([["xlink:href", {
779 type: srcType
780}], ["href", {
781 type: srcType
782}]])] // [
783// 'webpack-import',
784// new Map([
785// [
786// 'src',
787// {
788// type: webpackImportType,
789// },
790// ],
791// ]),
792// ],
793]);
794
795function normalizeSourcesList(sources) {
796 if (typeof sources === "undefined") {
797 return defaultSources;
798 }
799
800 const result = new Map();
801
802 for (const source of sources) {
803 if (source === "...") {
804 for (const [tag, attributes] of defaultSources.entries()) {
805 let newAttributes;
806 const existingAttributes = result.get(tag);
807
808 if (existingAttributes) {
809 newAttributes = new Map([...existingAttributes, ...attributes]);
810 } else {
811 newAttributes = new Map(attributes);
812 }
813
814 result.set(tag, newAttributes);
815 } // eslint-disable-next-line no-continue
816
817
818 continue;
819 }
820
821 let {
822 tag = "*",
823 attribute = "*"
824 } = source;
825 tag = tag.toLowerCase();
826 attribute = attribute.toLowerCase();
827
828 if (!result.has(tag)) {
829 result.set(tag, new Map());
830 }
831
832 let typeFn; // eslint-disable-next-line default-case
833
834 switch (source.type) {
835 case "src":
836 typeFn = srcType;
837 break;
838
839 case "srcset":
840 typeFn = srcsetType;
841 break;
842 }
843
844 result.get(tag).set(attribute, {
845 type: typeFn,
846 filter: source.filter
847 });
848 }
849
850 return result;
851}
852
853function getSourcesOption(rawOptions) {
854 if (typeof rawOptions.sources === "undefined") {
855 return {
856 list: normalizeSourcesList()
857 };
858 }
859
860 if (typeof rawOptions.sources === "boolean") {
861 return rawOptions.sources === true ? {
862 list: normalizeSourcesList()
863 } : false;
864 }
865
866 const sources = normalizeSourcesList(rawOptions.sources.list);
867 return {
868 list: sources,
869 urlFilter: rawOptions.sources.urlFilter,
870 scriptingEnabled: typeof rawOptions.sources.scriptingEnabled === "undefined" ? true : rawOptions.sources.scriptingEnabled
871 };
872}
873
874function normalizeOptions(rawOptions, loaderContext) {
875 return {
876 preprocessor: rawOptions.preprocessor,
877 sources: getSourcesOption(rawOptions),
878 minimize: getMinimizeOption(rawOptions, loaderContext),
879 esModule: typeof rawOptions.esModule === "undefined" ? true : rawOptions.esModule
880 };
881}
882
883function pluginRunner(plugins) {
884 return {
885 async process(content) {
886 const result = {};
887
888 for (const plugin of plugins) {
889 // eslint-disable-next-line no-param-reassign, no-await-in-loop
890 content = await plugin(content, result);
891 }
892
893 result.html = content;
894 return result;
895 }
896
897 };
898}
899
900function getFilter(filter) {
901 return (attribute, value, resourcePath) => {
902 if (typeof filter === "function") {
903 return filter(attribute, value, resourcePath);
904 }
905
906 return true;
907 };
908}
909
910const GET_SOURCE_FROM_IMPORT_NAME = "___HTML_LOADER_GET_SOURCE_FROM_IMPORT___";
911
912function getImportCode(html, loaderContext, imports, options) {
913 if (imports.length === 0) {
914 return "";
915 } // TODO simpify in the next major release
916
917
918 const getURLRuntime = require.resolve("./runtime/getUrl.js");
919
920 const context = loaderContext.context || loaderContext.rootContext;
921 const fileURLToHelper = typeof loaderContext.utils !== "undefined" && typeof loaderContext.utils.contextify === "function" ? loaderContext.utils.contextify(context, getURLRuntime) : contextify(context, getURLRuntime);
922 let code = options.esModule ? `import ${GET_SOURCE_FROM_IMPORT_NAME} from "${fileURLToHelper}";\n` : `var ${GET_SOURCE_FROM_IMPORT_NAME} = require("${fileURLToHelper}");\n`;
923
924 for (const item of imports) {
925 const {
926 format,
927 importName,
928 request
929 } = item;
930
931 switch (format) {
932 case "import":
933 code += options.esModule ? `import ${importName} from ${JSON.stringify(request)};\n` : `var ${importName} = require(${JSON.stringify(request)});\n`;
934 break;
935
936 case "url":
937 default:
938 code += options.esModule ? `var ${importName} = new URL(${JSON.stringify(request)}, import.meta.url);\n` : `var ${importName} = require(${JSON.stringify(request)});\n`;
939 }
940 }
941
942 return `// Imports\n${code}`;
943}
944
945function getModuleCode(html, replacements) {
946 let code = JSON.stringify(html) // Invalid in JavaScript but valid HTML
947 .replace(/[\u2028\u2029]/g, str => str === "\u2029" ? "\\u2029" : "\\u2028");
948 let replacersCode = "";
949
950 for (const item of replacements) {
951 const {
952 runtime,
953 importName,
954 replacementName,
955 isValueQuoted,
956 hash
957 } = item;
958
959 if (typeof runtime === "undefined" || runtime === true) {
960 const getUrlOptions = [].concat(hash ? [`hash: ${JSON.stringify(hash)}`] : []).concat(isValueQuoted ? [] : "maybeNeedQuotes: true");
961 const preparedOptions = getUrlOptions.length > 0 ? `, { ${getUrlOptions.join(", ")} }` : "";
962 replacersCode += `var ${replacementName} = ${GET_SOURCE_FROM_IMPORT_NAME}(${importName}${preparedOptions});\n`;
963 code = code.replace(new RegExp(replacementName, "g"), () => `" + ${replacementName} + "`);
964 } else {
965 code = code.replace(new RegExp(replacementName, "g"), () => `" + ${importName} + "`);
966 }
967 } // Replaces "<script>" or "</script>" to "<" + "script>" or "<" + "/script>".
968
969
970 code = code.replace(/<(\/?script)/g, (_, s) => `<" + "${s}`);
971 return `// Module\n${replacersCode}var code = ${code};\n`;
972}
973
974function getExportCode(html, options) {
975 if (options.esModule) {
976 return `// Exports\nexport default code;`;
977 }
978
979 return `// Exports\nmodule.exports = code;`;
980}
981
982function isASCIIC0group(character) {
983 // C0 and &nbsp;
984 // eslint-disable-next-line no-control-regex
985 return /^[\u0001-\u0019\u00a0]/.test(character);
986}
987
988function c0ControlCodesExclude(source) {
989 let {
990 value,
991 startOffset
992 } = source;
993
994 if (!value) {
995 throw new Error("Must be non-empty");
996 }
997
998 while (isASCIIC0group(value.substring(0, 1))) {
999 startOffset += 1;
1000 value = value.substring(1, value.length);
1001 }
1002
1003 while (isASCIIC0group(value.substring(value.length - 1, value.length))) {
1004 value = value.substring(0, value.length - 1);
1005 }
1006
1007 if (!value) {
1008 throw new Error("Must be non-empty");
1009 }
1010
1011 return {
1012 value,
1013 startOffset
1014 };
1015}
1016
1017function traverse(root, callback) {
1018 const visit = (node, parent) => {
1019 let res;
1020
1021 if (callback) {
1022 res = callback(node, parent);
1023 }
1024
1025 let {
1026 childNodes
1027 } = node; // in case a <template> tag is in the middle of the HTML: https://github.com/JPeer264/node-rcs-core/issues/58
1028
1029 if (node.content && Array.isArray(node.content.childNodes)) {
1030 ({
1031 childNodes
1032 } = node.content);
1033 }
1034
1035 if (res !== false && Array.isArray(childNodes) && childNodes.length >= 0) {
1036 childNodes.forEach(child => {
1037 visit(child, node);
1038 });
1039 }
1040 };
1041
1042 visit(root, null);
1043}
1044
1045const webpackIgnoreCommentRegexp = /webpackIgnore:(\s+)?(true|false)/;
1046exports.webpackIgnoreCommentRegexp = webpackIgnoreCommentRegexp;
\No newline at end of file