1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.default = void 0;
|
7 |
|
8 | var _htmlparser = require("htmlparser2");
|
9 |
|
10 | var _loaderUtils = require("loader-utils");
|
11 |
|
12 | var _HtmlSourceError = _interopRequireDefault(require("../HtmlSourceError"));
|
13 |
|
14 | var _utils = require("../utils");
|
15 |
|
16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
17 |
|
18 | var _default = options => function process(html) {
|
19 | const {
|
20 | list,
|
21 | urlFilter: maybeUrlFilter,
|
22 | root
|
23 | } = options.attributes;
|
24 | const sources = [];
|
25 | const urlFilter = (0, _utils.getFilter)(maybeUrlFilter, value => (0, _loaderUtils.isUrlRequest)(value, root));
|
26 |
|
27 | const getAttribute = (tag, attribute, attributes, resourcePath) => {
|
28 | return list.find(element => {
|
29 | const foundTag = typeof element.tag === 'undefined' || typeof element.tag !== 'undefined' && element.tag.toLowerCase() === tag.toLowerCase();
|
30 | const foundAttribute = element.attribute.toLowerCase() === attribute.toLowerCase();
|
31 | const isNotFiltered = element.filter ? element.filter(tag, attribute, attributes, resourcePath) : true;
|
32 | return foundTag && foundAttribute && isNotFiltered;
|
33 | });
|
34 | };
|
35 |
|
36 | const {
|
37 | resourcePath
|
38 | } = options;
|
39 | const parser = new _htmlparser.Parser({
|
40 | attributesMeta: {},
|
41 |
|
42 | onattribute(name, value) {
|
43 |
|
44 | const endIndex = parser._tokenizer._index;
|
45 | const startIndex = endIndex - value.length;
|
46 | const unquoted = html[endIndex] !== '"' && html[endIndex] !== "'";
|
47 | this.attributesMeta[name] = {
|
48 | startIndex,
|
49 | unquoted
|
50 | };
|
51 | },
|
52 |
|
53 | onopentag(tag, attributes) {
|
54 | Object.keys(attributes).forEach(attribute => {
|
55 | const value = attributes[attribute];
|
56 | const {
|
57 | startIndex: valueStartIndex,
|
58 | unquoted
|
59 | } = this.attributesMeta[attribute];
|
60 | const foundAttribute = getAttribute(tag, attribute, attributes, resourcePath);
|
61 |
|
62 | if (!foundAttribute) {
|
63 | return;
|
64 | }
|
65 |
|
66 | const {
|
67 | type
|
68 | } = foundAttribute;
|
69 |
|
70 | switch (type) {
|
71 | case 'src':
|
72 | {
|
73 | let source;
|
74 |
|
75 | try {
|
76 | source = (0, _utils.parseSrc)(value);
|
77 | } catch (error) {
|
78 | options.errors.push(new _HtmlSourceError.default(`Bad value for attribute "${attribute}" on element "${tag}": ${error.message}`, parser.startIndex, parser.endIndex, html));
|
79 | return;
|
80 | }
|
81 |
|
82 | const startIndex = valueStartIndex + source.startIndex;
|
83 | const endIndex = startIndex + source.value.length;
|
84 | sources.push({
|
85 | name: attribute,
|
86 | value: source.value,
|
87 | unquoted,
|
88 | startIndex,
|
89 | endIndex
|
90 | });
|
91 | break;
|
92 | }
|
93 |
|
94 | case 'srcset':
|
95 | {
|
96 | let sourceSet;
|
97 |
|
98 | try {
|
99 | sourceSet = (0, _utils.parseSrcset)(value);
|
100 | } catch (error) {
|
101 | options.errors.push(new _HtmlSourceError.default(`Bad value for attribute "${attribute}" on element "${tag}": ${error.message}`, parser.startIndex, parser.endIndex, html));
|
102 | return;
|
103 | }
|
104 |
|
105 | sourceSet.forEach(sourceItem => {
|
106 | const {
|
107 | source
|
108 | } = sourceItem;
|
109 | const startIndex = valueStartIndex + source.startIndex;
|
110 | const endIndex = startIndex + source.value.length;
|
111 | sources.push({
|
112 | name: attribute,
|
113 | value: source.value,
|
114 | unquoted,
|
115 | startIndex,
|
116 | endIndex
|
117 | });
|
118 | });
|
119 | break;
|
120 | }
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 | }
|
169 | });
|
170 | this.attributesMeta = {};
|
171 | },
|
172 |
|
173 | onerror(error) {
|
174 | options.errors.push(error);
|
175 | }
|
176 |
|
177 | }, {
|
178 | decodeEntities: false,
|
179 | lowerCaseTags: false,
|
180 | lowerCaseAttributeNames: false,
|
181 | recognizeCDATA: true,
|
182 | recognizeSelfClosing: true
|
183 | });
|
184 | parser.write(html);
|
185 | parser.end();
|
186 | const imports = new Map();
|
187 | const replacements = new Map();
|
188 | let offset = 0;
|
189 |
|
190 | for (const source of sources) {
|
191 | const {
|
192 | name,
|
193 | value,
|
194 | unquoted,
|
195 | startIndex,
|
196 | endIndex
|
197 | } = source;
|
198 | let normalizedUrl = value;
|
199 | let prefix = '';
|
200 | const queryParts = normalizedUrl.split('!');
|
201 |
|
202 | if (queryParts.length > 1) {
|
203 | normalizedUrl = queryParts.pop();
|
204 | prefix = queryParts.join('!');
|
205 | }
|
206 |
|
207 | normalizedUrl = (0, _utils.normalizeUrl)(normalizedUrl);
|
208 |
|
209 | if (!urlFilter(name, value, resourcePath)) {
|
210 |
|
211 | continue;
|
212 | }
|
213 |
|
214 | let hash;
|
215 | const indexHash = normalizedUrl.lastIndexOf('#');
|
216 |
|
217 | if (indexHash >= 0) {
|
218 | hash = normalizedUrl.substr(indexHash, indexHash);
|
219 | normalizedUrl = normalizedUrl.substr(0, indexHash);
|
220 | }
|
221 |
|
222 | const request = (0, _utils.requestify)(normalizedUrl, root);
|
223 | const newUrl = prefix ? `${prefix}!${request}` : request;
|
224 | const importKey = newUrl;
|
225 | let importName = imports.get(importKey);
|
226 |
|
227 | if (!importName) {
|
228 | importName = `___HTML_LOADER_IMPORT_${imports.size}___`;
|
229 | imports.set(importKey, importName);
|
230 | options.imports.push({
|
231 | importName,
|
232 | source: options.urlHandler(newUrl)
|
233 | });
|
234 | }
|
235 |
|
236 | const replacementKey = JSON.stringify({
|
237 | newUrl,
|
238 | unquoted,
|
239 | hash
|
240 | });
|
241 | let replacementName = replacements.get(replacementKey);
|
242 |
|
243 | if (!replacementName) {
|
244 | replacementName = `___HTML_LOADER_REPLACEMENT_${replacements.size}___`;
|
245 | replacements.set(replacementKey, replacementName);
|
246 | options.replacements.push({
|
247 | replacementName,
|
248 | importName,
|
249 | hash,
|
250 | unquoted
|
251 | });
|
252 | }
|
253 |
|
254 |
|
255 | html = html.slice(0, startIndex + offset) + replacementName + html.slice(endIndex + offset);
|
256 | offset += startIndex + replacementName.length - endIndex;
|
257 | }
|
258 |
|
259 | return html;
|
260 | };
|
261 |
|
262 | exports.default = _default; |
\ | No newline at end of file |