Skip to content

Commit 6654c9d

Browse files
refactor: code
1 parent e732324 commit 6654c9d

File tree

6 files changed

+1540
-993
lines changed

6 files changed

+1540
-993
lines changed

README.md

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,9 @@ module.exports = {
282282
};
283283
```
284284

285-
Filter can also be used to extend the supported elements and attributes. For example, filter can help process meta tags that reference assets:
285+
Filter can also be used to extend the supported elements and attributes.
286+
287+
For example, filter can help process meta tags that reference assets:
286288

287289
```js
288290
module.exports = {
@@ -305,6 +307,7 @@ module.exports = {
305307
) {
306308
return true;
307309
}
310+
308311
return false;
309312
},
310313
},
@@ -317,6 +320,38 @@ module.exports = {
317320
};
318321
```
319322

323+
**Note:** source with a `tag` option takes precedence over source without.
324+
325+
Filter can be used to disable default sources.
326+
327+
For example:
328+
329+
```js
330+
module.exports = {
331+
module: {
332+
rules: [
333+
{
334+
test: /\.html$/i,
335+
loader: 'html-loader',
336+
options: {
337+
sources: {
338+
list: [
339+
'...',
340+
{
341+
tag: 'img',
342+
attribute: 'src',
343+
type: 'src',
344+
filter: () => false,
345+
},
346+
],
347+
},
348+
},
349+
},
350+
],
351+
},
352+
};
353+
```
354+
320355
#### `urlFilter`
321356

322357
Type: `Function`
@@ -609,7 +644,7 @@ module.exports = {
609644
],
610645
},
611646
output: {
612-
publicPath: 'http://cdn.example.com/[hash]/',
647+
publicPath: 'http://cdn.example.com/[fullhash]/',
613648
},
614649
};
615650
```
@@ -640,14 +675,6 @@ require('html-loader?{"sources":{"list":[{"tag":"img","attribute":"src","type":"
640675
// => '<img src="http://cdn.example.com/49eba9f/a992ca.jpg" data-src="data:image/png;base64,..." >'
641676
```
642677

643-
```js
644-
require('html-loader?-sources!./file.html');
645-
646-
// => '<img src="image.jpg" data-src="image2x.png" >'
647-
```
648-
649-
> :warning: `-sources` sets `sources: false`.
650-
651678
### Process `script` and `link` tags
652679

653680
**script.file.js**

src/HtmlSourceError.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,17 @@ function offsetToPosition(source, offset) {
3636
}
3737

3838
export default class HtmlSourceError extends Error {
39-
constructor(error, startIndex, endIndex, source) {
39+
constructor(error, startOffset, endOffset, source) {
4040
super(error);
4141

4242
this.name = 'HtmlSourceError';
4343
this.message = `${this.name}: ${this.message}`;
44-
this.startIndex = startIndex;
45-
this.endIndex = endIndex;
44+
this.startOffset = startOffset;
45+
this.endOffset = endOffset;
4646
this.source = source;
4747

48-
const startPosition = offsetToPosition(source, this.startIndex);
49-
const endPosition = offsetToPosition(source, this.endIndex);
48+
const startPosition = offsetToPosition(source, this.startOffset);
49+
const endPosition = offsetToPosition(source, this.endOffset);
5050

5151
this.message += ` (From line ${startPosition.line}, column ${startPosition.column}; to line ${endPosition.line}, column ${endPosition.column})`;
5252

src/plugins/sources-plugin.js

Lines changed: 65 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -4,129 +4,104 @@ import {
44
getFilter,
55
normalizeUrl,
66
requestify,
7-
isUrlRequestable,
87
stringifyRequest,
9-
typeSrc,
10-
typeSrcset,
118
} from '../utils';
129

1310
export default (options) =>
1411
function process(html) {
15-
const { list, urlFilter: maybeUrlFilter } = options.sources;
16-
const sources = [];
17-
const urlFilter = getFilter(maybeUrlFilter, (value) =>
18-
isUrlRequestable(value)
19-
);
20-
const getAttribute = (tag, attribute, attributes, resourcePath) => {
21-
const foundTag = list.get(tag.toLowerCase()) || list.get('*');
22-
23-
if (!foundTag) {
24-
return false;
25-
}
26-
27-
const foundAttribute = foundTag.get(attribute.toLowerCase());
28-
29-
if (!foundAttribute) {
30-
return false;
31-
}
32-
33-
const result = foundAttribute.filter
34-
? foundAttribute.filter(tag, attribute, attributes, resourcePath)
35-
: true;
36-
37-
return result ? foundAttribute : false;
38-
};
39-
40-
const { resourcePath } = options;
4112
const parser5 = new SAXParser({ sourceCodeLocationInfo: true });
13+
const sources = [];
4214

4315
parser5.on('startTag', (node) => {
44-
const { tagName, attrs, sourceCodeLocation } = node;
16+
const { tagName, attrs: attributes, sourceCodeLocation } = node;
4517

46-
attrs.forEach((attribute) => {
47-
const { prefix } = attribute;
18+
attributes.forEach((attribute) => {
4819
let { name } = attribute;
4920

50-
name = prefix ? `${prefix}:${name}` : name;
21+
name = attribute.prefix ? `${attribute.prefix}:${name}` : name;
5122

52-
if (!sourceCodeLocation.attrs[name]) {
23+
const handlers =
24+
options.sources.list.get(tagName.toLowerCase()) ||
25+
options.sources.list.get('*');
26+
27+
if (!handlers) {
5328
return;
5429
}
5530

56-
const foundAttribute = getAttribute(tagName, name, attrs, resourcePath);
31+
const handler = handlers.get(name.toLowerCase());
5732

58-
if (!foundAttribute) {
33+
if (!handler) {
5934
return;
6035
}
6136

62-
const { type } = foundAttribute;
37+
if (
38+
handler.filter &&
39+
!handler.filter(tagName, name, attributes, options.resourcePath)
40+
) {
41+
return;
42+
}
6343

64-
const target = html.slice(
44+
const attributeAndValue = html.slice(
6545
sourceCodeLocation.attrs[name].startOffset,
6646
sourceCodeLocation.attrs[name].endOffset
6747
);
48+
const isValueQuoted =
49+
attributeAndValue[attributeAndValue.length - 1] === '"' ||
50+
attributeAndValue[attributeAndValue.length - 1] === "'";
51+
const valueStartOffset =
52+
sourceCodeLocation.attrs[name].startOffset +
53+
attributeAndValue.indexOf(attribute.value);
54+
const valueEndOffset =
55+
sourceCodeLocation.attrs[name].endOffset - (isValueQuoted ? 1 : 0);
56+
const optionsForTypeFn = {
57+
tag: tagName,
58+
isSelfClosing: node.selfClosing,
59+
tagStartOffset: sourceCodeLocation.startOffset,
60+
tagEndOffset: sourceCodeLocation.endOffset,
61+
attributes,
62+
attribute: name,
63+
attributePrefix: attribute.prefix,
64+
attributeNamespace: attribute.namespace,
65+
attributeStartOffset: sourceCodeLocation.attrs[name].startOffset,
66+
attributeEndOffset: sourceCodeLocation.attrs[name].endOffset,
67+
value: attribute.value,
68+
isValueQuoted,
69+
valueEndOffset,
70+
valueStartOffset,
71+
html,
72+
};
73+
74+
let result;
75+
76+
try {
77+
result = handler.type(optionsForTypeFn);
78+
} catch (error) {
79+
options.errors.push(error);
80+
}
6881

69-
const unquoted =
70-
target[target.length - 1] !== '"' &&
71-
target[target.length - 1] !== "'";
72-
73-
const result = [];
74-
75-
// eslint-disable-next-line default-case
76-
switch (type) {
77-
case 'src': {
78-
typeSrc({ name, attribute, node, target, html, options }).forEach(
79-
(i) => {
80-
result.push(i);
81-
}
82-
);
83-
break;
84-
}
85-
86-
case 'srcset': {
87-
typeSrcset({
88-
name,
89-
attribute,
90-
node,
91-
target,
92-
html,
93-
options,
94-
}).forEach((i) => {
95-
result.push(i);
96-
});
97-
break;
98-
}
82+
result = Array.isArray(result) ? result : [result];
9983

100-
default: {
101-
type({ name, attribute, node, target, html, options }).forEach(
102-
(i) => {
103-
result.push(i);
104-
}
105-
);
84+
for (const source of result) {
85+
if (!source) {
86+
// eslint-disable-next-line no-continue
87+
continue;
10688
}
107-
}
10889

109-
for (const i of result) {
110-
if (i) {
111-
sources.push({
112-
...i,
113-
name,
114-
unquoted,
115-
});
116-
}
90+
sources.push({ ...source, name, isValueQuoted });
11791
}
11892
});
11993
});
12094

12195
parser5.end(html);
12296

97+
const urlFilter = getFilter(options.sources.urlFilter);
12398
const imports = new Map();
12499
const replacements = new Map();
125100

126101
let offset = 0;
127102

128103
for (const source of sources) {
129-
const { name, value, unquoted, startIndex, endIndex } = source;
104+
const { name, value, isValueQuoted, startOffset, endOffset } = source;
130105

131106
let normalizedUrl = value;
132107
let prefix = '';
@@ -140,7 +115,7 @@ export default (options) =>
140115

141116
normalizedUrl = normalizeUrl(normalizedUrl);
142117

143-
if (!urlFilter(name, value, resourcePath)) {
118+
if (!urlFilter(name, value, options.resourcePath)) {
144119
// eslint-disable-next-line no-continue
145120
continue;
146121
}
@@ -168,7 +143,7 @@ export default (options) =>
168143
});
169144
}
170145

171-
const replacementKey = JSON.stringify({ newUrl, unquoted, hash });
146+
const replacementKey = JSON.stringify({ newUrl, isValueQuoted, hash });
172147
let replacementName = replacements.get(replacementKey);
173148

174149
if (!replacementName) {
@@ -179,17 +154,17 @@ export default (options) =>
179154
replacementName,
180155
importName,
181156
hash,
182-
unquoted,
157+
isValueQuoted,
183158
});
184159
}
185160

186161
// eslint-disable-next-line no-param-reassign
187162
html =
188-
html.slice(0, startIndex + offset) +
163+
html.slice(0, startOffset + offset) +
189164
replacementName +
190-
html.slice(endIndex + offset);
165+
html.slice(endOffset + offset);
191166

192-
offset += startIndex + replacementName.length - endIndex;
167+
offset += startOffset + replacementName.length - endOffset;
193168
}
194169

195170
return html;

0 commit comments

Comments
 (0)