Skip to content

Commit c247cfa

Browse files
authoredAug 18, 2020
fix: inline syntax for sources (#310)
1 parent b92ed21 commit c247cfa

11 files changed

+227
-134
lines changed
 

‎package-lock.json

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
"posthtml-webp": "^1.5.0",
7676
"prettier": "^2.0.5",
7777
"standard-version": "^8.0.2",
78+
"url-loader": "^4.1.0",
7879
"webpack": "^4.44.1"
7980
},
8081
"keywords": [

‎src/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getOptions } from 'loader-utils';
1+
import { getOptions, stringifyRequest } from 'loader-utils';
22
import validateOptions from 'schema-utils';
33

44
import { sourcePlugin, minimizerPlugin } from './plugins';
@@ -35,6 +35,7 @@ export default async function loader(content) {
3535
if (options.attributes) {
3636
plugins.push(
3737
sourcePlugin({
38+
urlHandler: (url) => stringifyRequest(this, url),
3839
attributes: options.attributes,
3940
resourcePath: this.resourcePath,
4041
imports,

‎src/plugins/source-plugin.js

+83-86
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,14 @@
1-
import { parse } from 'url';
2-
31
import { Parser } from 'htmlparser2';
4-
import { isUrlRequest, urlToRequest } from 'loader-utils';
2+
import { isUrlRequest } from 'loader-utils';
53

64
import HtmlSourceError from '../HtmlSourceError';
7-
import { getFilter, parseSrc, parseSrcset } from '../utils';
8-
9-
function parseSource(source) {
10-
const URLObject = parse(source);
11-
const { hash } = URLObject;
12-
13-
if (!hash) {
14-
return { sourceValue: source };
15-
}
16-
17-
URLObject.hash = null;
18-
19-
const sourceWithoutHash = URLObject.format();
20-
21-
return { sourceValue: sourceWithoutHash, hash };
22-
}
5+
import {
6+
getFilter,
7+
parseSrc,
8+
parseSrcset,
9+
normalizeUrl,
10+
requestify,
11+
} from '../utils';
2312

2413
export default (options) =>
2514
function process(html) {
@@ -44,45 +33,6 @@ export default (options) =>
4433
});
4534
};
4635
const { resourcePath } = options;
47-
const imports = new Map();
48-
const getImportItem = (value) => {
49-
const key = urlToRequest(decodeURIComponent(value), root);
50-
51-
let name = imports.get(key);
52-
53-
if (name) {
54-
return { key, name };
55-
}
56-
57-
name = `___HTML_LOADER_IMPORT_${imports.size}___`;
58-
imports.set(key, name);
59-
60-
options.imports.push({ importName: name, source: key });
61-
62-
return { key, name };
63-
};
64-
const replacements = new Map();
65-
const getReplacementItem = (importItem, unquoted, hash) => {
66-
const key = JSON.stringify({ key: importItem.key, unquoted, hash });
67-
68-
let name = replacements.get(key);
69-
70-
if (name) {
71-
return { key, name };
72-
}
73-
74-
name = `___HTML_LOADER_REPLACEMENT_${replacements.size}___`;
75-
replacements.set(key, name);
76-
77-
options.replacements.push({
78-
replacementName: name,
79-
importName: importItem.name,
80-
hash,
81-
unquoted,
82-
});
83-
84-
return { key, name };
85-
};
8636
const parser = new Parser(
8737
{
8838
attributesMeta: {},
@@ -135,21 +85,16 @@ export default (options) =>
13585
return;
13686
}
13787

138-
if (!urlFilter(attribute, source.value, resourcePath)) {
139-
return;
140-
}
141-
142-
const { sourceValue, hash } = parseSource(source.value);
143-
const importItem = getImportItem(sourceValue);
144-
const replacementItem = getReplacementItem(
145-
importItem,
146-
unquoted,
147-
hash
148-
);
14988
const startIndex = valueStartIndex + source.startIndex;
15089
const endIndex = startIndex + source.value.length;
15190

152-
sources.push({ replacementItem, startIndex, endIndex });
91+
sources.push({
92+
name: attribute,
93+
value: source.value,
94+
unquoted,
95+
startIndex,
96+
endIndex,
97+
});
15398

15499
break;
155100
}
@@ -173,22 +118,16 @@ export default (options) =>
173118

174119
sourceSet.forEach((sourceItem) => {
175120
const { source } = sourceItem;
176-
177-
if (!urlFilter(attribute, source.value, resourcePath)) {
178-
return;
179-
}
180-
181-
const { sourceValue, hash } = parseSource(source.value);
182-
const importItem = getImportItem(sourceValue);
183-
const replacementItem = getReplacementItem(
184-
importItem,
185-
unquoted,
186-
hash
187-
);
188121
const startIndex = valueStartIndex + source.startIndex;
189122
const endIndex = startIndex + source.value.length;
190123

191-
sources.push({ replacementItem, startIndex, endIndex });
124+
sources.push({
125+
name: attribute,
126+
value: source.value,
127+
unquoted,
128+
startIndex,
129+
endIndex,
130+
});
192131
});
193132

194133
break;
@@ -261,18 +200,76 @@ export default (options) =>
261200
parser.write(html);
262201
parser.end();
263202

203+
const imports = new Map();
204+
const replacements = new Map();
205+
264206
let offset = 0;
265207

266208
for (const source of sources) {
267-
const { startIndex, endIndex, replacementItem } = source;
209+
const { name, value, unquoted, startIndex, endIndex } = source;
210+
211+
let normalizedUrl = value;
212+
let prefix = '';
213+
214+
const queryParts = normalizedUrl.split('!');
215+
216+
if (queryParts.length > 1) {
217+
normalizedUrl = queryParts.pop();
218+
prefix = queryParts.join('!');
219+
}
220+
221+
normalizedUrl = normalizeUrl(normalizedUrl);
222+
223+
if (!urlFilter(name, value, resourcePath)) {
224+
// eslint-disable-next-line no-continue
225+
continue;
226+
}
227+
228+
let hash;
229+
const indexHash = normalizedUrl.lastIndexOf('#');
230+
231+
if (indexHash >= 0) {
232+
hash = normalizedUrl.substr(indexHash, indexHash);
233+
normalizedUrl = normalizedUrl.substr(0, indexHash);
234+
}
235+
236+
const request = requestify(normalizedUrl, root);
237+
const newUrl = prefix ? `${prefix}!${request}` : request;
238+
const importKey = newUrl;
239+
let importName = imports.get(importKey);
240+
241+
if (!importName) {
242+
importName = `___HTML_LOADER_IMPORT_${imports.size}___`;
243+
imports.set(importKey, importName);
244+
245+
options.imports.push({
246+
importName,
247+
source: options.urlHandler(newUrl),
248+
});
249+
}
250+
251+
const replacementKey = JSON.stringify({ newUrl, unquoted, hash });
252+
let replacementName = replacements.get(replacementKey);
253+
254+
if (!replacementName) {
255+
replacementName = `___HTML_LOADER_REPLACEMENT_${replacements.size}___`;
256+
replacements.set(replacementKey, replacementName);
257+
258+
options.replacements.push({
259+
replacementName,
260+
importName,
261+
hash,
262+
unquoted,
263+
});
264+
}
268265

269266
// eslint-disable-next-line no-param-reassign
270267
html =
271268
html.slice(0, startIndex + offset) +
272-
replacementItem.name +
269+
replacementName +
273270
html.slice(endIndex + offset);
274271

275-
offset += startIndex + replacementItem.name.length - endIndex;
272+
offset += startIndex + replacementName.length - endIndex;
276273
}
277274

278275
return html;

‎src/utils.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { stringifyRequest } from 'loader-utils';
1+
import { stringifyRequest, urlToRequest } from 'loader-utils';
22

33
function isASCIIWhitespace(character) {
44
return (
@@ -370,6 +370,14 @@ export function parseSrc(input) {
370370
return { value, startIndex };
371371
}
372372

373+
export function normalizeUrl(url) {
374+
return decodeURIComponent(url);
375+
}
376+
377+
export function requestify(url, root) {
378+
return urlToRequest(url, root);
379+
}
380+
373381
function isProductionMode(loaderContext) {
374382
return loaderContext.mode === 'production' || !loaderContext.mode;
375383
}
@@ -606,11 +614,10 @@ export function getImportCode(html, loaderContext, imports, options) {
606614

607615
for (const item of imports) {
608616
const { importName, source } = item;
609-
const stringifiedSourceRequest = stringifyRequest(loaderContext, source);
610617

611618
code += options.esModule
612-
? `import ${importName} from ${stringifiedSourceRequest};\n`
613-
: `var ${importName} = require(${stringifiedSourceRequest});\n`;
619+
? `import ${importName} from ${source};\n`
620+
: `var ${importName} = require(${source});\n`;
614621
}
615622

616623
return `// Imports\n${code}`;

‎test/__snapshots__/attributes-option.test.js.snap

+62-22
Large diffs are not rendered by default.

‎test/__snapshots__/esModule-option.test.js.snap

+18-6
Large diffs are not rendered by default.

‎test/__snapshots__/loader.test.js.snap

+6-2
Large diffs are not rendered by default.

‎test/__snapshots__/minimize-option.test.js.snap

+30-12
Large diffs are not rendered by default.

‎test/fixtures/pixel.png

70 Bytes
Loading

‎test/fixtures/simple.html

+3-1
Original file line numberDiff line numberDiff line change
@@ -317,4 +317,6 @@ <h2>An Ordered HTML List</h2>
317317

318318
<input type="text">
319319

320-
<button onclick=" document.getElementById('demo').innerHTML = Date()">The time is?</button>
320+
<button onclick=" document.getElementById('demo').innerHTML = Date()">The time is?</button>
321+
322+
<img src="!!url-loader!./pixel.png" alt="" />

0 commit comments

Comments
 (0)
Please sign in to comment.