1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.parseSrcset = parseSrcset;
|
7 | exports.parseSrc = parseSrc;
|
8 | exports.normalizeUrl = normalizeUrl;
|
9 | exports.requestify = requestify;
|
10 | exports.isUrlRequestable = isUrlRequestable;
|
11 | exports.stringifyRequest = stringifyRequest;
|
12 | exports.typeSrc = typeSrc;
|
13 | exports.typeSrcset = typeSrcset;
|
14 | exports.normalizeOptions = normalizeOptions;
|
15 | exports.pluginRunner = pluginRunner;
|
16 | exports.getFilter = getFilter;
|
17 | exports.getImportCode = getImportCode;
|
18 | exports.getModuleCode = getModuleCode;
|
19 | exports.getExportCode = getExportCode;
|
20 | exports.c0ControlCodesExclude = c0ControlCodesExclude;
|
21 |
|
22 | var _path = _interopRequireDefault(require("path"));
|
23 |
|
24 | var _HtmlSourceError = _interopRequireDefault(require("./HtmlSourceError"));
|
25 |
|
26 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
27 |
|
28 | function isASCIIWhitespace(character) {
|
29 | return (
|
30 | character === '\u0009' ||
|
31 | character === '\u000A' ||
|
32 | character === '\u000C' ||
|
33 | character === '\u000D' ||
|
34 | character === '\u0020'
|
35 | );
|
36 | }
|
37 |
|
38 |
|
39 |
|
40 | const regexLeadingSpaces = /^[ \t\n\r\u000c]+/;
|
41 |
|
42 | const regexLeadingCommasOrSpaces = /^[, \t\n\r\u000c]+/;
|
43 |
|
44 | const regexLeadingNotSpaces = /^[^ \t\n\r\u000c]+/;
|
45 | const regexTrailingCommas = /[,]+$/;
|
46 | const regexNonNegativeInteger = /^\d+$/;
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 | const regexFloatingPoint = /^-?(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/;
|
53 |
|
54 | function parseSrcset(input) {
|
55 |
|
56 | const inputLength = input.length;
|
57 | let url;
|
58 | let descriptors;
|
59 | let currentDescriptor;
|
60 | let state;
|
61 | let c;
|
62 |
|
63 |
|
64 | let position = 0;
|
65 | let startUrlPosition;
|
66 |
|
67 | function collectCharacters(regEx) {
|
68 | let chars;
|
69 | const match = regEx.exec(input.substring(position));
|
70 |
|
71 | if (match) {
|
72 | [chars] = match;
|
73 | position += chars.length;
|
74 | return chars;
|
75 | }
|
76 | }
|
77 |
|
78 |
|
79 | const candidates = [];
|
80 |
|
81 |
|
82 |
|
83 |
|
84 | while (true) {
|
85 | collectCharacters(regexLeadingCommasOrSpaces);
|
86 |
|
87 | if (position >= inputLength) {
|
88 | if (candidates.length === 0) {
|
89 | throw new Error('Must contain one or more image candidate strings');
|
90 | }
|
91 |
|
92 |
|
93 | return candidates;
|
94 | }
|
95 |
|
96 |
|
97 |
|
98 | startUrlPosition = position;
|
99 | url = collectCharacters(regexLeadingNotSpaces);
|
100 |
|
101 | descriptors = [];
|
102 |
|
103 |
|
104 |
|
105 | if (url.slice(-1) === ',') {
|
106 | url = url.replace(regexTrailingCommas, '');
|
107 |
|
108 | parseDescriptors();
|
109 | }
|
110 | else {
|
111 | tokenize();
|
112 | }
|
113 |
|
114 | }
|
115 | |
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 | function tokenize() {
|
122 |
|
123 | collectCharacters(regexLeadingSpaces);
|
124 |
|
125 | currentDescriptor = '';
|
126 |
|
127 | state = 'in descriptor';
|
128 |
|
129 | while (true) {
|
130 |
|
131 | c = input.charAt(position);
|
132 |
|
133 |
|
134 |
|
135 |
|
136 | if (state === 'in descriptor') {
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 | if (isASCIIWhitespace(c)) {
|
143 | if (currentDescriptor) {
|
144 | descriptors.push(currentDescriptor);
|
145 | currentDescriptor = '';
|
146 | state = 'after descriptor';
|
147 | }
|
148 | }
|
149 |
|
150 |
|
151 |
|
152 | else if (c === ',') {
|
153 | position += 1;
|
154 |
|
155 | if (currentDescriptor) {
|
156 | descriptors.push(currentDescriptor);
|
157 | }
|
158 |
|
159 | parseDescriptors();
|
160 | return;
|
161 | }
|
162 |
|
163 | else if (c === '\u0028') {
|
164 | currentDescriptor += c;
|
165 | state = 'in parens';
|
166 | }
|
167 |
|
168 |
|
169 | else if (c === '') {
|
170 | if (currentDescriptor) {
|
171 | descriptors.push(currentDescriptor);
|
172 | }
|
173 |
|
174 | parseDescriptors();
|
175 | return;
|
176 |
|
177 | } else {
|
178 | currentDescriptor += c;
|
179 | }
|
180 | }
|
181 | else if (state === 'in parens') {
|
182 |
|
183 |
|
184 | if (c === ')') {
|
185 | currentDescriptor += c;
|
186 | state = 'in descriptor';
|
187 | }
|
188 |
|
189 |
|
190 | else if (c === '') {
|
191 | descriptors.push(currentDescriptor);
|
192 | parseDescriptors();
|
193 | return;
|
194 | }
|
195 |
|
196 | else {
|
197 | currentDescriptor += c;
|
198 | }
|
199 | }
|
200 | else if (state === 'after descriptor') {
|
201 |
|
202 | if (isASCIIWhitespace(c)) {
|
203 | }
|
204 | else if (c === '') {
|
205 | parseDescriptors();
|
206 | return;
|
207 | }
|
208 |
|
209 | else {
|
210 | state = 'in descriptor';
|
211 | position -= 1;
|
212 | }
|
213 | }
|
214 |
|
215 |
|
216 | position += 1;
|
217 | }
|
218 | }
|
219 | |
220 |
|
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 | function parseDescriptors() {
|
227 |
|
228 | let pError = false;
|
229 |
|
230 |
|
231 |
|
232 | let w;
|
233 | let d;
|
234 | let h;
|
235 | let i;
|
236 | const candidate = {};
|
237 | let desc;
|
238 | let lastChar;
|
239 | let value;
|
240 | let intVal;
|
241 | let floatVal;
|
242 |
|
243 |
|
244 | for (i = 0; i < descriptors.length; i++) {
|
245 | desc = descriptors[i];
|
246 | lastChar = desc[desc.length - 1];
|
247 | value = desc.substring(0, desc.length - 1);
|
248 | intVal = parseInt(value, 10);
|
249 | floatVal = parseFloat(value);
|
250 |
|
251 |
|
252 | if (regexNonNegativeInteger.test(value) && lastChar === 'w') {
|
253 |
|
254 | if (w || d) {
|
255 | pError = true;
|
256 | }
|
257 |
|
258 |
|
259 |
|
260 |
|
261 | if (intVal === 0) {
|
262 | pError = true;
|
263 | } else {
|
264 | w = intVal;
|
265 | }
|
266 | }
|
267 |
|
268 | else if (regexFloatingPoint.test(value) && lastChar === 'x') {
|
269 |
|
270 |
|
271 | if (w || d || h) {
|
272 | pError = true;
|
273 | }
|
274 |
|
275 |
|
276 |
|
277 |
|
278 | if (floatVal < 0) {
|
279 | pError = true;
|
280 | } else {
|
281 | d = floatVal;
|
282 | }
|
283 | }
|
284 |
|
285 | else if (regexNonNegativeInteger.test(value) && lastChar === 'h') {
|
286 |
|
287 | if (h || d) {
|
288 | pError = true;
|
289 | }
|
290 |
|
291 |
|
292 |
|
293 |
|
294 | if (intVal === 0) {
|
295 | pError = true;
|
296 | } else {
|
297 | h = intVal;
|
298 | }
|
299 |
|
300 | } else {
|
301 | pError = true;
|
302 | }
|
303 | }
|
304 |
|
305 |
|
306 |
|
307 |
|
308 | if (!pError) {
|
309 | candidate.source = {
|
310 | value: url,
|
311 | startIndex: startUrlPosition
|
312 | };
|
313 |
|
314 | if (w) {
|
315 | candidate.width = {
|
316 | value: w
|
317 | };
|
318 | }
|
319 |
|
320 | if (d) {
|
321 | candidate.density = {
|
322 | value: d
|
323 | };
|
324 | }
|
325 |
|
326 | if (h) {
|
327 | candidate.height = {
|
328 | value: h
|
329 | };
|
330 | }
|
331 |
|
332 | candidates.push(candidate);
|
333 | } else {
|
334 | throw new Error(`Invalid srcset descriptor found in '${input}' at '${desc}'`);
|
335 | }
|
336 | }
|
337 | }
|
338 |
|
339 | function parseSrc(input) {
|
340 | if (!input) {
|
341 | throw new Error('Must be non-empty');
|
342 | }
|
343 |
|
344 | let startIndex = 0;
|
345 | let value = input;
|
346 |
|
347 | while (isASCIIWhitespace(value.substring(0, 1))) {
|
348 | startIndex += 1;
|
349 | value = value.substring(1, value.length);
|
350 | }
|
351 |
|
352 | while (isASCIIWhitespace(value.substring(value.length - 1, value.length))) {
|
353 | value = value.substring(0, value.length - 1);
|
354 | }
|
355 |
|
356 | if (!value) {
|
357 | throw new Error('Must be non-empty');
|
358 | }
|
359 |
|
360 | return {
|
361 | value,
|
362 | startIndex
|
363 | };
|
364 | }
|
365 |
|
366 | const moduleRequestRegex = /^[^?]*~/;
|
367 | const matchNativeWin32Path = /^[A-Z]:[/\\]|^\\\\/i;
|
368 |
|
369 | function normalizeUrl(url) {
|
370 | return matchNativeWin32Path.test(url) ? decodeURI(url).replace(/[\t\n\r]/g, '') : decodeURI(url).replace(/[\t\n\r]/g, '').replace(/\\/g, '/');
|
371 | }
|
372 |
|
373 | function requestify(url) {
|
374 | if (matchNativeWin32Path.test(url) || url[0] === '/') {
|
375 | return url;
|
376 | }
|
377 |
|
378 | if (/^file:/i.test(url)) {
|
379 | return url;
|
380 | }
|
381 |
|
382 | if (/^\.\.?\//.test(url)) {
|
383 | return url;
|
384 | }
|
385 |
|
386 |
|
387 | if (moduleRequestRegex.test(url)) {
|
388 | return url.replace(moduleRequestRegex, '');
|
389 | }
|
390 |
|
391 |
|
392 | return `./${url}`;
|
393 | }
|
394 |
|
395 | function isUrlRequestable(url) {
|
396 |
|
397 | if (/^\/\//.test(url)) {
|
398 | return false;
|
399 | }
|
400 |
|
401 |
|
402 | if (/^file:/i.test(url)) {
|
403 | return true;
|
404 | }
|
405 |
|
406 |
|
407 | if (/^[a-z][a-z0-9+.-]*:/i.test(url) && !matchNativeWin32Path.test(url)) {
|
408 | return false;
|
409 | }
|
410 |
|
411 |
|
412 | if (/^[{}[\]#*;,'§$%&(=?`´^°<>]/.test(url)) {
|
413 | return false;
|
414 | }
|
415 |
|
416 | return true;
|
417 | }
|
418 |
|
419 | const matchRelativePath = /^\.\.?[/\\]/;
|
420 |
|
421 | function isAbsolutePath(str) {
|
422 | return matchNativeWin32Path.test(str) && _path.default.win32.isAbsolute(str);
|
423 | }
|
424 |
|
425 | function isRelativePath(str) {
|
426 | return matchRelativePath.test(str);
|
427 | }
|
428 |
|
429 | function stringifyRequest(context, request) {
|
430 | const splitted = request.split('!');
|
431 | return JSON.stringify(splitted.map(part => {
|
432 |
|
433 | const splittedPart = part.match(/^(.*?)(\?.*)/);
|
434 | const query = splittedPart ? splittedPart[2] : '';
|
435 | let singlePath = splittedPart ? splittedPart[1] : part;
|
436 |
|
437 | if (isAbsolutePath(singlePath) && context) {
|
438 | singlePath = _path.default.relative(context, singlePath);
|
439 |
|
440 | if (isAbsolutePath(singlePath)) {
|
441 |
|
442 |
|
443 |
|
444 | return singlePath + query;
|
445 | }
|
446 |
|
447 | if (isRelativePath(singlePath) === false) {
|
448 |
|
449 | singlePath = `./${singlePath}`;
|
450 | }
|
451 | }
|
452 |
|
453 | return singlePath.replace(/\\/g, '/') + query;
|
454 | }).join('!'));
|
455 | }
|
456 |
|
457 | function isProductionMode(loaderContext) {
|
458 | return loaderContext.mode === 'production' || !loaderContext.mode;
|
459 | }
|
460 |
|
461 | const defaultMinimizerOptions = {
|
462 | caseSensitive: true,
|
463 |
|
464 | collapseWhitespace: true,
|
465 | conservativeCollapse: true,
|
466 | keepClosingSlash: true,
|
467 |
|
468 | minifyCSS: true,
|
469 | minifyJS: true,
|
470 |
|
471 |
|
472 | removeComments: true,
|
473 |
|
474 |
|
475 | removeScriptTypeAttributes: true,
|
476 | removeStyleLinkTypeAttributes: true
|
477 |
|
478 | };
|
479 |
|
480 | function getMinimizeOption(rawOptions, loaderContext) {
|
481 | if (typeof rawOptions.minimize === 'undefined') {
|
482 | return isProductionMode(loaderContext) ? defaultMinimizerOptions : false;
|
483 | }
|
484 |
|
485 | if (typeof rawOptions.minimize === 'boolean') {
|
486 | return rawOptions.minimize === true ? defaultMinimizerOptions : false;
|
487 | }
|
488 |
|
489 | return rawOptions.minimize;
|
490 | }
|
491 |
|
492 | function getAttributeValue(attributes, name) {
|
493 | const [result] = attributes.filter(i => i.name.toLowerCase() === name);
|
494 | return typeof result === 'undefined' ? result : result.value;
|
495 | }
|
496 |
|
497 | function scriptSrcFilter(tag, attribute, attributes) {
|
498 | let type = getAttributeValue(attributes, 'type');
|
499 |
|
500 | if (!type) {
|
501 | return true;
|
502 | }
|
503 |
|
504 | type = type.trim();
|
505 |
|
506 | if (!type) {
|
507 | return false;
|
508 | }
|
509 |
|
510 | if (type !== 'module' && type !== 'text/javascript' && type !== 'application/javascript') {
|
511 | return false;
|
512 | }
|
513 |
|
514 | return true;
|
515 | }
|
516 |
|
517 | function linkHrefFilter(tag, attribute, attributes) {
|
518 | let rel = getAttributeValue(attributes, 'rel');
|
519 |
|
520 | if (!rel) {
|
521 | return false;
|
522 | }
|
523 |
|
524 | rel = rel.trim();
|
525 |
|
526 | if (!rel) {
|
527 | return false;
|
528 | }
|
529 |
|
530 | rel = rel.toLowerCase();
|
531 | const usedRels = rel.split(' ').filter(value => value);
|
532 | const allowedRels = ['stylesheet', 'icon', 'mask-icon', 'apple-touch-icon', 'apple-touch-icon-precomposed', 'apple-touch-startup-image', 'manifest', 'prefetch', 'preload'];
|
533 | return allowedRels.filter(value => usedRels.includes(value)).length > 0;
|
534 | }
|
535 |
|
536 | const META = new Map([['name', new Set([
|
537 | 'msapplication-tileimage', 'msapplication-square70x70logo', 'msapplication-square150x150logo', 'msapplication-wide310x150logo', 'msapplication-square310x310logo', 'msapplication-config', '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'])], ['name', new Set(['msapplication-task'])]]);
|
538 |
|
539 | function linkItempropFilter(tag, attribute, attributes) {
|
540 | let name = getAttributeValue(attributes, 'itemprop');
|
541 |
|
542 | if (name) {
|
543 | name = name.trim();
|
544 |
|
545 | if (!name) {
|
546 | return false;
|
547 | }
|
548 |
|
549 | name = name.toLowerCase();
|
550 | return META.get('itemprop').has(name);
|
551 | }
|
552 |
|
553 | return false;
|
554 | }
|
555 |
|
556 | function linkUnionFilter(tag, attribute, attributes) {
|
557 | return linkHrefFilter(tag, attribute, attributes) || linkItempropFilter(tag, attribute, attributes);
|
558 | }
|
559 |
|
560 | function metaContentFilter(tag, attribute, attributes) {
|
561 | for (const item of META) {
|
562 | const [key, allowedNames] = item;
|
563 | let name = getAttributeValue(attributes, key);
|
564 |
|
565 | if (name) {
|
566 | name = name.trim();
|
567 |
|
568 | if (!name) {
|
569 |
|
570 | continue;
|
571 | }
|
572 |
|
573 | name = name.toLowerCase();
|
574 | return allowedNames.has(name);
|
575 | }
|
576 | }
|
577 |
|
578 | return false;
|
579 | }
|
580 |
|
581 | function typeSrc({
|
582 | name,
|
583 | attribute,
|
584 | node,
|
585 | target,
|
586 | html,
|
587 | options
|
588 | }) {
|
589 | const {
|
590 | tagName,
|
591 | sourceCodeLocation
|
592 | } = node;
|
593 | const {
|
594 | value
|
595 | } = attribute;
|
596 | const result = [];
|
597 | let source;
|
598 |
|
599 | try {
|
600 | source = parseSrc(value);
|
601 | } catch (error) {
|
602 | options.errors.push(new _HtmlSourceError.default(`Bad value for attribute "${attribute.name}" on element "${tagName}": ${error.message}`, sourceCodeLocation.attrs[name].startOffset, sourceCodeLocation.attrs[name].endOffset, html));
|
603 | return result;
|
604 | }
|
605 |
|
606 | source = c0ControlCodesExclude(source);
|
607 |
|
608 | if (!isUrlRequestable(source.value)) {
|
609 | return result;
|
610 | }
|
611 |
|
612 | const startOffset = sourceCodeLocation.attrs[name].startOffset + target.indexOf(source.value, name.length);
|
613 | result.push({
|
614 | value: source.value,
|
615 | startIndex: startOffset,
|
616 | endIndex: startOffset + source.value.length
|
617 | });
|
618 | return result;
|
619 | }
|
620 |
|
621 | function typeSrcset({
|
622 | name,
|
623 | attribute,
|
624 | node,
|
625 | target,
|
626 | html,
|
627 | options
|
628 | }) {
|
629 | const {
|
630 | tagName,
|
631 | sourceCodeLocation
|
632 | } = node;
|
633 | const {
|
634 | value
|
635 | } = attribute;
|
636 | const result = [];
|
637 | let sourceSet;
|
638 |
|
639 | try {
|
640 | sourceSet = parseSrcset(value);
|
641 | } catch (error) {
|
642 | options.errors.push(new _HtmlSourceError.default(`Bad value for attribute "${attribute.name}" on element "${tagName}": ${error.message}`, sourceCodeLocation.attrs[name].startOffset, sourceCodeLocation.attrs[name].endOffset, html));
|
643 | return result;
|
644 | }
|
645 |
|
646 | sourceSet = sourceSet.map(item => {
|
647 | return {
|
648 | source: c0ControlCodesExclude(item.source)
|
649 | };
|
650 | });
|
651 | let searchFrom = name.length;
|
652 | sourceSet.forEach(sourceItem => {
|
653 | const {
|
654 | source
|
655 | } = sourceItem;
|
656 |
|
657 | if (!isUrlRequestable(source.value)) {
|
658 | return false;
|
659 | }
|
660 |
|
661 | const startOffset = sourceCodeLocation.attrs[name].startOffset + target.indexOf(source.value, searchFrom);
|
662 | searchFrom = target.indexOf(source.value, searchFrom) + 1;
|
663 | result.push({
|
664 | value: source.value,
|
665 | startIndex: startOffset,
|
666 | endIndex: startOffset + source.value.length
|
667 | });
|
668 | return false;
|
669 | });
|
670 | return result;
|
671 | }
|
672 |
|
673 | function typeMsapplicationTask({
|
674 | name,
|
675 | attribute,
|
676 | node,
|
677 | target,
|
678 | html,
|
679 | options
|
680 | }) {
|
681 | const {
|
682 | tagName,
|
683 | sourceCodeLocation
|
684 | } = node;
|
685 | const [content] = typeSrc({
|
686 | name,
|
687 | attribute,
|
688 | node,
|
689 | target,
|
690 | html,
|
691 | options
|
692 | });
|
693 | const result = [];
|
694 |
|
695 | if (!content) {
|
696 | return result;
|
697 | }
|
698 |
|
699 | let startIndex = 0;
|
700 | let endIndex = 0;
|
701 | let foundIconUri;
|
702 | let source;
|
703 | content.value.split(';').forEach(i => {
|
704 | if (foundIconUri) {
|
705 | return;
|
706 | }
|
707 |
|
708 | if (!i.includes('icon-uri')) {
|
709 |
|
710 | startIndex += i.length + 1;
|
711 | return;
|
712 | }
|
713 |
|
714 | foundIconUri = true;
|
715 | const [, aValue] = i.split('=');
|
716 |
|
717 | try {
|
718 | source = parseSrc(aValue);
|
719 | } catch (error) {
|
720 | options.errors.push(new _HtmlSourceError.default(`Bad value for attribute "icon-uri" on element "${tagName}": ${error.message}`, sourceCodeLocation.attrs[name].startOffset, sourceCodeLocation.attrs[name].endOffset, html));
|
721 | return;
|
722 | }
|
723 |
|
724 |
|
725 | startIndex += i.indexOf('=') + source.startIndex + 1;
|
726 | endIndex = startIndex + source.value.length;
|
727 | });
|
728 |
|
729 | if (!source) {
|
730 | return result;
|
731 | }
|
732 |
|
733 | result.push({ ...content,
|
734 | startIndex: content.startIndex + startIndex,
|
735 | endIndex: content.startIndex + endIndex,
|
736 | name: 'icon-uri',
|
737 | value: source.value
|
738 | });
|
739 | return result;
|
740 | }
|
741 |
|
742 | function metaContentType({
|
743 | name,
|
744 | attribute,
|
745 | node,
|
746 | target,
|
747 | html,
|
748 | options
|
749 | }) {
|
750 | const isMsapplicationTask = node.attrs.filter(i => i.name.toLowerCase() === 'name' && i.value.toLowerCase() === 'msapplication-task');
|
751 | return isMsapplicationTask.length === 0 ? typeSrc({
|
752 | name,
|
753 | attribute,
|
754 | node,
|
755 | target,
|
756 | html,
|
757 | options
|
758 | }) : typeMsapplicationTask({
|
759 | name,
|
760 | attribute,
|
761 | node,
|
762 | target,
|
763 | html,
|
764 | options
|
765 | });
|
766 | }
|
767 |
|
768 | const defaultAttributes = [{
|
769 | tag: 'audio',
|
770 | attribute: 'src',
|
771 | type: 'src'
|
772 | }, {
|
773 | tag: 'embed',
|
774 | attribute: 'src',
|
775 | type: 'src'
|
776 | }, {
|
777 | tag: 'img',
|
778 | attribute: 'src',
|
779 | type: 'src'
|
780 | }, {
|
781 | tag: 'img',
|
782 | attribute: 'srcset',
|
783 | type: 'srcset'
|
784 | }, {
|
785 | tag: 'input',
|
786 | attribute: 'src',
|
787 | type: 'src'
|
788 | }, {
|
789 | tag: 'link',
|
790 | attribute: 'href',
|
791 | type: 'src',
|
792 | filter: linkUnionFilter
|
793 | }, {
|
794 | tag: 'link',
|
795 | attribute: 'imagesrcset',
|
796 | type: 'srcset',
|
797 | filter: linkHrefFilter
|
798 | }, {
|
799 | tag: 'meta',
|
800 | attribute: 'content',
|
801 | type: metaContentType,
|
802 | filter: metaContentFilter
|
803 | }, {
|
804 | tag: 'object',
|
805 | attribute: 'data',
|
806 | type: 'src'
|
807 | }, {
|
808 | tag: 'script',
|
809 | attribute: 'src',
|
810 | type: 'src',
|
811 | filter: scriptSrcFilter
|
812 | },
|
813 | {
|
814 | tag: 'script',
|
815 | attribute: 'href',
|
816 | type: 'src',
|
817 | filter: scriptSrcFilter
|
818 | }, {
|
819 | tag: 'script',
|
820 | attribute: 'xlink:href',
|
821 | type: 'src',
|
822 | filter: scriptSrcFilter
|
823 | }, {
|
824 | tag: 'source',
|
825 | attribute: 'src',
|
826 | type: 'src'
|
827 | }, {
|
828 | tag: 'source',
|
829 | attribute: 'srcset',
|
830 | type: 'srcset'
|
831 | }, {
|
832 | tag: 'track',
|
833 | attribute: 'src',
|
834 | type: 'src'
|
835 | }, {
|
836 | tag: 'video',
|
837 | attribute: 'poster',
|
838 | type: 'src'
|
839 | }, {
|
840 | tag: 'video',
|
841 | attribute: 'src',
|
842 | type: 'src'
|
843 | },
|
844 | {
|
845 | tag: 'image',
|
846 | attribute: 'xlink:href',
|
847 | type: 'src'
|
848 | }, {
|
849 | tag: 'image',
|
850 | attribute: 'href',
|
851 | type: 'src'
|
852 | }, {
|
853 | tag: 'use',
|
854 | attribute: 'xlink:href',
|
855 | type: 'src'
|
856 | }, {
|
857 | tag: 'use',
|
858 | attribute: 'href',
|
859 | type: 'src'
|
860 | }];
|
861 |
|
862 | function rewriteSourcesList(sourcesList, attribute, source) {
|
863 | for (const key of sourcesList.keys()) {
|
864 | const item = sourcesList.get(key);
|
865 |
|
866 | if (!item.has(attribute)) {
|
867 |
|
868 | continue;
|
869 | }
|
870 |
|
871 | item.set(attribute, { ...item.get(attribute),
|
872 | ...source
|
873 | });
|
874 | sourcesList.set(key, item);
|
875 | }
|
876 | }
|
877 |
|
878 | function createSourcesList(sources, accumulator = new Map()) {
|
879 | for (const source of sources) {
|
880 | if (source === '...') {
|
881 |
|
882 | continue;
|
883 | }
|
884 |
|
885 | let {
|
886 | tag = '*',
|
887 | attribute = '*'
|
888 | } = source;
|
889 | tag = tag.toLowerCase();
|
890 | attribute = attribute.toLowerCase();
|
891 |
|
892 | if (tag === '*') {
|
893 | rewriteSourcesList(accumulator, attribute, source);
|
894 | }
|
895 |
|
896 | if (!accumulator.has(tag)) {
|
897 | accumulator.set(tag, new Map());
|
898 | }
|
899 |
|
900 | accumulator.get(tag).set(attribute, source);
|
901 | }
|
902 |
|
903 | return accumulator;
|
904 | }
|
905 |
|
906 | function smartMergeSources(array, factory) {
|
907 | if (typeof array === 'undefined') {
|
908 | return factory();
|
909 | }
|
910 |
|
911 | const result = array.some(i => i === '...') ? createSourcesList(array, factory()) : createSourcesList(array);
|
912 | return result;
|
913 | }
|
914 |
|
915 | function getSourcesOption(rawOptions) {
|
916 | if (typeof rawOptions.sources === 'undefined') {
|
917 | return {
|
918 | list: createSourcesList(defaultAttributes)
|
919 | };
|
920 | }
|
921 |
|
922 | if (typeof rawOptions.sources === 'boolean') {
|
923 | return rawOptions.sources === true ? {
|
924 | list: createSourcesList(defaultAttributes)
|
925 | } : false;
|
926 | }
|
927 |
|
928 | const sources = smartMergeSources(rawOptions.sources.list, () => createSourcesList(defaultAttributes));
|
929 | return {
|
930 | list: sources,
|
931 | urlFilter: rawOptions.sources.urlFilter,
|
932 | root: rawOptions.sources.root
|
933 | };
|
934 | }
|
935 |
|
936 | function normalizeOptions(rawOptions, loaderContext) {
|
937 | return {
|
938 | preprocessor: rawOptions.preprocessor,
|
939 | sources: getSourcesOption(rawOptions),
|
940 | minimize: getMinimizeOption(rawOptions, loaderContext),
|
941 | esModule: typeof rawOptions.esModule === 'undefined' ? true : rawOptions.esModule
|
942 | };
|
943 | }
|
944 |
|
945 | function pluginRunner(plugins) {
|
946 | return {
|
947 | process: content => {
|
948 | const result = {};
|
949 |
|
950 | for (const plugin of plugins) {
|
951 |
|
952 | content = plugin(content, result);
|
953 | }
|
954 |
|
955 | result.html = content;
|
956 | return result;
|
957 | }
|
958 | };
|
959 | }
|
960 |
|
961 | function getFilter(filter, defaultFilter = null) {
|
962 | return (attribute, value, resourcePath) => {
|
963 | if (defaultFilter && !defaultFilter(value)) {
|
964 | return false;
|
965 | }
|
966 |
|
967 | if (typeof filter === 'function') {
|
968 | return filter(attribute, value, resourcePath);
|
969 | }
|
970 |
|
971 | return true;
|
972 | };
|
973 | }
|
974 |
|
975 | const GET_SOURCE_FROM_IMPORT_NAME = '___HTML_LOADER_GET_SOURCE_FROM_IMPORT___';
|
976 |
|
977 | function getImportCode(html, loaderContext, imports, options) {
|
978 | if (imports.length === 0) {
|
979 | return '';
|
980 | }
|
981 |
|
982 | const stringifiedHelperRequest = `"${_path.default.relative(loaderContext.context, require.resolve('./runtime/getUrl.js')).replace(/\\/g, '/')}"`;
|
983 | let code = options.esModule ? `import ${GET_SOURCE_FROM_IMPORT_NAME} from ${stringifiedHelperRequest};\n` : `var ${GET_SOURCE_FROM_IMPORT_NAME} = require(${stringifiedHelperRequest});\n`;
|
984 |
|
985 | for (const item of imports) {
|
986 | const {
|
987 | importName,
|
988 | source
|
989 | } = item;
|
990 | code += options.esModule ? `var ${importName} = new URL(${source}, import.meta.url);\n` : `var ${importName} = require(${source});\n`;
|
991 | }
|
992 |
|
993 | return `// Imports\n${code}`;
|
994 | }
|
995 |
|
996 | function getModuleCode(html, replacements) {
|
997 | let code = JSON.stringify(html)
|
998 | .replace(/[\u2028\u2029]/g, str => str === '\u2029' ? '\\u2029' : '\\u2028');
|
999 | let replacersCode = '';
|
1000 |
|
1001 | for (const item of replacements) {
|
1002 | const {
|
1003 | importName,
|
1004 | replacementName,
|
1005 | unquoted,
|
1006 | hash
|
1007 | } = item;
|
1008 | const getUrlOptions = [].concat(hash ? [`hash: ${JSON.stringify(hash)}`] : []).concat(unquoted ? 'maybeNeedQuotes: true' : []);
|
1009 | const preparedOptions = getUrlOptions.length > 0 ? `, { ${getUrlOptions.join(', ')} }` : '';
|
1010 | replacersCode += `var ${replacementName} = ${GET_SOURCE_FROM_IMPORT_NAME}(${importName}${preparedOptions});\n`;
|
1011 | code = code.replace(new RegExp(replacementName, 'g'), () => `" + ${replacementName} + "`);
|
1012 | }
|
1013 |
|
1014 | return `// Module\n${replacersCode}var code = ${code};\n`;
|
1015 | }
|
1016 |
|
1017 | function getExportCode(html, options) {
|
1018 | if (options.esModule) {
|
1019 | return `// Exports\nexport default code;`;
|
1020 | }
|
1021 |
|
1022 | return `// Exports\nmodule.exports = code;`;
|
1023 | }
|
1024 |
|
1025 | function isASCIIC0group(character) {
|
1026 |
|
1027 |
|
1028 | return /^[\u0001-\u0019\u00a0]/.test(character);
|
1029 | }
|
1030 |
|
1031 | function c0ControlCodesExclude(source) {
|
1032 | let {
|
1033 | value,
|
1034 | startIndex
|
1035 | } = source;
|
1036 |
|
1037 | if (!value) {
|
1038 | throw new Error('Must be non-empty');
|
1039 | }
|
1040 |
|
1041 | while (isASCIIC0group(value.substring(0, 1))) {
|
1042 | startIndex += 1;
|
1043 | value = value.substring(1, value.length);
|
1044 | }
|
1045 |
|
1046 | while (isASCIIC0group(value.substring(value.length - 1, value.length))) {
|
1047 | value = value.substring(0, value.length - 1);
|
1048 | }
|
1049 |
|
1050 | if (!value) {
|
1051 | throw new Error('Must be non-empty');
|
1052 | }
|
1053 |
|
1054 | return {
|
1055 | value,
|
1056 | startIndex
|
1057 | };
|
1058 | } |
\ | No newline at end of file |