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.normalizeOptions = normalizeOptions;
|
12 | exports.pluginRunner = pluginRunner;
|
13 | exports.getFilter = getFilter;
|
14 | exports.getImportCode = getImportCode;
|
15 | exports.getModuleCode = getModuleCode;
|
16 | exports.getExportCode = getExportCode;
|
17 |
|
18 | var _loaderUtils = require("loader-utils");
|
19 |
|
20 | function isASCIIWhitespace(character) {
|
21 | return (
|
22 | character === '\u0009' ||
|
23 | character === '\u000A' ||
|
24 | character === '\u000C' ||
|
25 | character === '\u000D' ||
|
26 | character === '\u0020'
|
27 | );
|
28 | }
|
29 |
|
30 |
|
31 |
|
32 | const regexLeadingSpaces = /^[ \t\n\r\u000c]+/;
|
33 |
|
34 | const regexLeadingCommasOrSpaces = /^[, \t\n\r\u000c]+/;
|
35 |
|
36 | const regexLeadingNotSpaces = /^[^ \t\n\r\u000c]+/;
|
37 | const regexTrailingCommas = /[,]+$/;
|
38 | const regexNonNegativeInteger = /^\d+$/;
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 | const regexFloatingPoint = /^-?(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/;
|
45 |
|
46 | function parseSrcset(input) {
|
47 |
|
48 | const inputLength = input.length;
|
49 | let url;
|
50 | let descriptors;
|
51 | let currentDescriptor;
|
52 | let state;
|
53 | let c;
|
54 |
|
55 |
|
56 | let position = 0;
|
57 | let startUrlPosition;
|
58 |
|
59 | function collectCharacters(regEx) {
|
60 | let chars;
|
61 | const match = regEx.exec(input.substring(position));
|
62 |
|
63 | if (match) {
|
64 | [chars] = match;
|
65 | position += chars.length;
|
66 | return chars;
|
67 | }
|
68 | }
|
69 |
|
70 |
|
71 | const candidates = [];
|
72 |
|
73 |
|
74 |
|
75 |
|
76 | while (true) {
|
77 | collectCharacters(regexLeadingCommasOrSpaces);
|
78 |
|
79 | if (position >= inputLength) {
|
80 | if (candidates.length === 0) {
|
81 | throw new Error('Must contain one or more image candidate strings');
|
82 | }
|
83 |
|
84 |
|
85 | return candidates;
|
86 | }
|
87 |
|
88 |
|
89 |
|
90 | startUrlPosition = position;
|
91 | url = collectCharacters(regexLeadingNotSpaces);
|
92 |
|
93 | descriptors = [];
|
94 |
|
95 |
|
96 |
|
97 | if (url.slice(-1) === ',') {
|
98 | url = url.replace(regexTrailingCommas, '');
|
99 |
|
100 | parseDescriptors();
|
101 | }
|
102 | else {
|
103 | tokenize();
|
104 | }
|
105 |
|
106 | }
|
107 | |
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 | function tokenize() {
|
114 |
|
115 | collectCharacters(regexLeadingSpaces);
|
116 |
|
117 | currentDescriptor = '';
|
118 |
|
119 | state = 'in descriptor';
|
120 |
|
121 | while (true) {
|
122 |
|
123 | c = input.charAt(position);
|
124 |
|
125 |
|
126 |
|
127 |
|
128 | if (state === 'in descriptor') {
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 | if (isASCIIWhitespace(c)) {
|
135 | if (currentDescriptor) {
|
136 | descriptors.push(currentDescriptor);
|
137 | currentDescriptor = '';
|
138 | state = 'after descriptor';
|
139 | }
|
140 | }
|
141 |
|
142 |
|
143 |
|
144 | else if (c === ',') {
|
145 | position += 1;
|
146 |
|
147 | if (currentDescriptor) {
|
148 | descriptors.push(currentDescriptor);
|
149 | }
|
150 |
|
151 | parseDescriptors();
|
152 | return;
|
153 | }
|
154 |
|
155 | else if (c === '\u0028') {
|
156 | currentDescriptor += c;
|
157 | state = 'in parens';
|
158 | }
|
159 |
|
160 |
|
161 | else if (c === '') {
|
162 | if (currentDescriptor) {
|
163 | descriptors.push(currentDescriptor);
|
164 | }
|
165 |
|
166 | parseDescriptors();
|
167 | return;
|
168 |
|
169 | } else {
|
170 | currentDescriptor += c;
|
171 | }
|
172 | }
|
173 | else if (state === 'in parens') {
|
174 |
|
175 |
|
176 | if (c === ')') {
|
177 | currentDescriptor += c;
|
178 | state = 'in descriptor';
|
179 | }
|
180 |
|
181 |
|
182 | else if (c === '') {
|
183 | descriptors.push(currentDescriptor);
|
184 | parseDescriptors();
|
185 | return;
|
186 | }
|
187 |
|
188 | else {
|
189 | currentDescriptor += c;
|
190 | }
|
191 | }
|
192 | else if (state === 'after descriptor') {
|
193 |
|
194 | if (isASCIIWhitespace(c)) {
|
195 | }
|
196 | else if (c === '') {
|
197 | parseDescriptors();
|
198 | return;
|
199 | }
|
200 |
|
201 | else {
|
202 | state = 'in descriptor';
|
203 | position -= 1;
|
204 | }
|
205 | }
|
206 |
|
207 |
|
208 | position += 1;
|
209 | }
|
210 | }
|
211 | |
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 | function parseDescriptors() {
|
219 |
|
220 | let pError = false;
|
221 |
|
222 |
|
223 |
|
224 | let w;
|
225 | let d;
|
226 | let h;
|
227 | let i;
|
228 | const candidate = {};
|
229 | let desc;
|
230 | let lastChar;
|
231 | let value;
|
232 | let intVal;
|
233 | let floatVal;
|
234 |
|
235 |
|
236 | for (i = 0; i < descriptors.length; i++) {
|
237 | desc = descriptors[i];
|
238 | lastChar = desc[desc.length - 1];
|
239 | value = desc.substring(0, desc.length - 1);
|
240 | intVal = parseInt(value, 10);
|
241 | floatVal = parseFloat(value);
|
242 |
|
243 |
|
244 | if (regexNonNegativeInteger.test(value) && lastChar === 'w') {
|
245 |
|
246 | if (w || d) {
|
247 | pError = true;
|
248 | }
|
249 |
|
250 |
|
251 |
|
252 |
|
253 | if (intVal === 0) {
|
254 | pError = true;
|
255 | } else {
|
256 | w = intVal;
|
257 | }
|
258 | }
|
259 |
|
260 | else if (regexFloatingPoint.test(value) && lastChar === 'x') {
|
261 |
|
262 |
|
263 | if (w || d || h) {
|
264 | pError = true;
|
265 | }
|
266 |
|
267 |
|
268 |
|
269 |
|
270 | if (floatVal < 0) {
|
271 | pError = true;
|
272 | } else {
|
273 | d = floatVal;
|
274 | }
|
275 | }
|
276 |
|
277 | else if (regexNonNegativeInteger.test(value) && lastChar === 'h') {
|
278 |
|
279 | if (h || d) {
|
280 | pError = true;
|
281 | }
|
282 |
|
283 |
|
284 |
|
285 |
|
286 | if (intVal === 0) {
|
287 | pError = true;
|
288 | } else {
|
289 | h = intVal;
|
290 | }
|
291 |
|
292 | } else {
|
293 | pError = true;
|
294 | }
|
295 | }
|
296 |
|
297 |
|
298 |
|
299 |
|
300 | if (!pError) {
|
301 | candidate.source = {
|
302 | value: url,
|
303 | startIndex: startUrlPosition
|
304 | };
|
305 |
|
306 | if (w) {
|
307 | candidate.width = {
|
308 | value: w
|
309 | };
|
310 | }
|
311 |
|
312 | if (d) {
|
313 | candidate.density = {
|
314 | value: d
|
315 | };
|
316 | }
|
317 |
|
318 | if (h) {
|
319 | candidate.height = {
|
320 | value: h
|
321 | };
|
322 | }
|
323 |
|
324 | candidates.push(candidate);
|
325 | } else {
|
326 | throw new Error(`Invalid srcset descriptor found in '${input}' at '${desc}'`);
|
327 | }
|
328 | }
|
329 | }
|
330 |
|
331 | function parseSrc(input) {
|
332 | if (!input) {
|
333 | throw new Error('Must be non-empty');
|
334 | }
|
335 |
|
336 | let startIndex = 0;
|
337 | let value = input;
|
338 |
|
339 | while (isASCIIWhitespace(value.substring(0, 1))) {
|
340 | startIndex += 1;
|
341 | value = value.substring(1, value.length);
|
342 | }
|
343 |
|
344 | while (isASCIIWhitespace(value.substring(value.length - 1, value.length))) {
|
345 | value = value.substring(0, value.length - 1);
|
346 | }
|
347 |
|
348 | if (!value) {
|
349 | throw new Error('Must be non-empty');
|
350 | }
|
351 |
|
352 | return {
|
353 | value,
|
354 | startIndex
|
355 | };
|
356 | }
|
357 |
|
358 | function normalizeUrl(url) {
|
359 | return decodeURIComponent(url).replace(/[\t\n\r]/g, '');
|
360 | }
|
361 |
|
362 | function requestify(url, root) {
|
363 | return (0, _loaderUtils.urlToRequest)(url, root);
|
364 | }
|
365 |
|
366 | function isUrlRequestable(url, root) {
|
367 | return (0, _loaderUtils.isUrlRequest)(url, root);
|
368 | }
|
369 |
|
370 | function isProductionMode(loaderContext) {
|
371 | return loaderContext.mode === 'production' || !loaderContext.mode;
|
372 | }
|
373 |
|
374 | const defaultMinimizerOptions = {
|
375 | caseSensitive: true,
|
376 |
|
377 | collapseWhitespace: true,
|
378 | conservativeCollapse: true,
|
379 | keepClosingSlash: true,
|
380 |
|
381 | minifyCSS: true,
|
382 | minifyJS: true,
|
383 |
|
384 |
|
385 | removeComments: true,
|
386 |
|
387 |
|
388 | removeScriptTypeAttributes: true,
|
389 | removeStyleLinkTypeAttributes: true
|
390 |
|
391 | };
|
392 |
|
393 | function getMinimizeOption(rawOptions, loaderContext) {
|
394 | if (typeof rawOptions.minimize === 'undefined') {
|
395 | return isProductionMode(loaderContext) ? defaultMinimizerOptions : false;
|
396 | }
|
397 |
|
398 | if (typeof rawOptions.minimize === 'boolean') {
|
399 | return rawOptions.minimize === true ? defaultMinimizerOptions : false;
|
400 | }
|
401 |
|
402 | return rawOptions.minimize;
|
403 | }
|
404 |
|
405 | function getAttributeValue(attributes, name) {
|
406 | const lowercasedAttributes = Object.keys(attributes).reduce((keys, k) => {
|
407 |
|
408 | keys[k.toLowerCase()] = k;
|
409 | return keys;
|
410 | }, {});
|
411 | return attributes[lowercasedAttributes[name.toLowerCase()]];
|
412 | }
|
413 |
|
414 | function scriptFilter(tag, attribute, attributes) {
|
415 | if (attributes.type) {
|
416 | const type = getAttributeValue(attributes, 'type').trim().toLowerCase();
|
417 |
|
418 | if (type !== 'module' && type !== 'text/javascript' && type !== 'application/javascript') {
|
419 | return false;
|
420 | }
|
421 | }
|
422 |
|
423 | return true;
|
424 | }
|
425 |
|
426 | const defaultAttributes = [{
|
427 | tag: 'audio',
|
428 | attribute: 'src',
|
429 | type: 'src'
|
430 | }, {
|
431 | tag: 'embed',
|
432 | attribute: 'src',
|
433 | type: 'src'
|
434 | }, {
|
435 | tag: 'img',
|
436 | attribute: 'src',
|
437 | type: 'src'
|
438 | }, {
|
439 | tag: 'img',
|
440 | attribute: 'srcset',
|
441 | type: 'srcset'
|
442 | }, {
|
443 | tag: 'input',
|
444 | attribute: 'src',
|
445 | type: 'src'
|
446 | }, {
|
447 | tag: 'link',
|
448 | attribute: 'href',
|
449 | type: 'src',
|
450 | filter: (tag, attribute, attributes) => {
|
451 | if (!/stylesheet/i.test(getAttributeValue(attributes, 'rel'))) {
|
452 | return false;
|
453 | }
|
454 |
|
455 | if (attributes.type && getAttributeValue(attributes, 'type').trim().toLowerCase() !== 'text/css') {
|
456 | return false;
|
457 | }
|
458 |
|
459 | return true;
|
460 | }
|
461 | }, {
|
462 | tag: 'object',
|
463 | attribute: 'data',
|
464 | type: 'src'
|
465 | }, {
|
466 | tag: 'script',
|
467 | attribute: 'src',
|
468 | type: 'src',
|
469 | filter: scriptFilter
|
470 | },
|
471 | {
|
472 | tag: 'script',
|
473 | attribute: 'href',
|
474 | type: 'src',
|
475 | filter: scriptFilter
|
476 | }, {
|
477 | tag: 'script',
|
478 | attribute: 'xlink:href',
|
479 | type: 'src',
|
480 | filter: scriptFilter
|
481 | }, {
|
482 | tag: 'source',
|
483 | attribute: 'src',
|
484 | type: 'src'
|
485 | }, {
|
486 | tag: 'source',
|
487 | attribute: 'srcset',
|
488 | type: 'srcset'
|
489 | }, {
|
490 | tag: 'track',
|
491 | attribute: 'src',
|
492 | type: 'src'
|
493 | }, {
|
494 | tag: 'video',
|
495 | attribute: 'poster',
|
496 | type: 'src'
|
497 | }, {
|
498 | tag: 'video',
|
499 | attribute: 'src',
|
500 | type: 'src'
|
501 | },
|
502 | {
|
503 | tag: 'image',
|
504 | attribute: 'xlink:href',
|
505 | type: 'src'
|
506 | }, {
|
507 | tag: 'image',
|
508 | attribute: 'href',
|
509 | type: 'src'
|
510 | }, {
|
511 | tag: 'use',
|
512 | attribute: 'xlink:href',
|
513 | type: 'src'
|
514 | }, {
|
515 | tag: 'use',
|
516 | attribute: 'href',
|
517 | type: 'src'
|
518 | }];
|
519 |
|
520 | function smartMergeSources(array, factory) {
|
521 | if (typeof array === 'undefined') {
|
522 | return factory();
|
523 | }
|
524 |
|
525 | const newArray = [];
|
526 |
|
527 | for (let i = 0; i < array.length; i++) {
|
528 | const item = array[i];
|
529 |
|
530 | if (item === '...') {
|
531 | const items = factory();
|
532 |
|
533 | if (typeof items !== 'undefined') {
|
534 |
|
535 | for (const item of items) {
|
536 | newArray.push(item);
|
537 | }
|
538 | }
|
539 | } else if (typeof newArray !== 'undefined') {
|
540 | newArray.push(item);
|
541 | }
|
542 | }
|
543 |
|
544 | return newArray;
|
545 | }
|
546 |
|
547 | function getAttributesOption(rawOptions) {
|
548 | if (typeof rawOptions.attributes === 'undefined') {
|
549 | return {
|
550 | list: defaultAttributes
|
551 | };
|
552 | }
|
553 |
|
554 | if (typeof rawOptions.attributes === 'boolean') {
|
555 | return rawOptions.attributes === true ? {
|
556 | list: defaultAttributes
|
557 | } : false;
|
558 | }
|
559 |
|
560 | const sources = smartMergeSources(rawOptions.attributes.list, () => defaultAttributes);
|
561 | return {
|
562 | list: sources,
|
563 | urlFilter: rawOptions.attributes.urlFilter,
|
564 | root: rawOptions.attributes.root
|
565 | };
|
566 | }
|
567 |
|
568 | function normalizeOptions(rawOptions, loaderContext) {
|
569 | return {
|
570 | preprocessor: rawOptions.preprocessor,
|
571 | attributes: getAttributesOption(rawOptions),
|
572 | minimize: getMinimizeOption(rawOptions, loaderContext),
|
573 | esModule: typeof rawOptions.esModule === 'undefined' ? false : rawOptions.esModule
|
574 | };
|
575 | }
|
576 |
|
577 | function pluginRunner(plugins) {
|
578 | return {
|
579 | process: content => {
|
580 | const result = {};
|
581 |
|
582 | for (const plugin of plugins) {
|
583 |
|
584 | content = plugin(content, result);
|
585 | }
|
586 |
|
587 | result.html = content;
|
588 | return result;
|
589 | }
|
590 | };
|
591 | }
|
592 |
|
593 | function getFilter(filter, defaultFilter = null) {
|
594 | return (attribute, value, resourcePath) => {
|
595 | if (defaultFilter && !defaultFilter(value)) {
|
596 | return false;
|
597 | }
|
598 |
|
599 | if (typeof filter === 'function') {
|
600 | return filter(attribute, value, resourcePath);
|
601 | }
|
602 |
|
603 | return true;
|
604 | };
|
605 | }
|
606 |
|
607 | const GET_SOURCE_FROM_IMPORT_NAME = '___HTML_LOADER_GET_SOURCE_FROM_IMPORT___';
|
608 |
|
609 | function getImportCode(html, loaderContext, imports, options) {
|
610 | if (imports.length === 0) {
|
611 | return '';
|
612 | }
|
613 |
|
614 | const stringifiedHelperRequest = (0, _loaderUtils.stringifyRequest)(loaderContext, require.resolve('./runtime/getUrl.js'));
|
615 | let code = options.esModule ? `import ${GET_SOURCE_FROM_IMPORT_NAME} from ${stringifiedHelperRequest};\n` : `var ${GET_SOURCE_FROM_IMPORT_NAME} = require(${stringifiedHelperRequest});\n`;
|
616 |
|
617 | for (const item of imports) {
|
618 | const {
|
619 | importName,
|
620 | source
|
621 | } = item;
|
622 | code += options.esModule ? `import ${importName} from ${source};\n` : `var ${importName} = require(${source});\n`;
|
623 | }
|
624 |
|
625 | return `// Imports\n${code}`;
|
626 | }
|
627 |
|
628 | function getModuleCode(html, replacements) {
|
629 | let code = JSON.stringify(html)
|
630 | .replace(/[\u2028\u2029]/g, str => str === '\u2029' ? '\\u2029' : '\\u2028');
|
631 | let replacersCode = '';
|
632 |
|
633 | for (const item of replacements) {
|
634 | const {
|
635 | importName,
|
636 | replacementName,
|
637 | unquoted,
|
638 | hash
|
639 | } = item;
|
640 | const getUrlOptions = [].concat(hash ? [`hash: ${JSON.stringify(hash)}`] : []).concat(unquoted ? 'maybeNeedQuotes: true' : []);
|
641 | const preparedOptions = getUrlOptions.length > 0 ? `, { ${getUrlOptions.join(', ')} }` : '';
|
642 | replacersCode += `var ${replacementName} = ${GET_SOURCE_FROM_IMPORT_NAME}(${importName}${preparedOptions});\n`;
|
643 | code = code.replace(new RegExp(replacementName, 'g'), () => `" + ${replacementName} + "`);
|
644 | }
|
645 |
|
646 | return `// Module\n${replacersCode}var code = ${code};\n`;
|
647 | }
|
648 |
|
649 | function getExportCode(html, options) {
|
650 | if (options.esModule) {
|
651 | return `// Exports\nexport default code;`;
|
652 | }
|
653 |
|
654 | return `// Exports\nmodule.exports = code;`;
|
655 | } |
\ | No newline at end of file |