Skip to content

Commit

Permalink
feat: support absolute URLs and DataURI (#519)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait authored Jul 25, 2024
1 parent 536a204 commit cc34b06
Show file tree
Hide file tree
Showing 12 changed files with 1,281 additions and 628 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ type sources =

Default: `true`

By default every loadable attribute (for example - `<img src="image.png">`) is imported (`const img = require('./image.png')` or `import img from "./image.png""`).
By default every loadable attribute (for example - `<img src="image.png">`) is imported (`const img = require('./image.png')` or `new URL("./image.png", import.meta.url)`).
You may need to specify loaders for images in your configuration (recommended [`asset modules`](https://webpack.js.org/guides/asset-modules/)).

Supported tags and attributes:
Expand Down
14 changes: 14 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,23 @@ export default async function loader(content) {
const imports = [];
const replacements = [];

let isSupportAbsoluteURL = false;

// TODO enable by default in the next major release
if (
this._compilation &&
this._compilation.options &&
this._compilation.options.experiments &&
this._compilation.options.experiments.buildHttp
) {
isSupportAbsoluteURL = true;
}

if (options.sources) {
plugins.push(
sourcesPlugin({
isSupportAbsoluteURL,
isSupportDataURL: options.esModule,
sources: options.sources,
resourcePath: this.resourcePath,
context: this.context,
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/sources-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ export default (options) =>
attributeStartOffset: sourceCodeLocation.attrs[name].startOffset,
attributeEndOffset: sourceCodeLocation.attrs[name].endOffset,
value: attribute.value,
isSupportAbsoluteURL: options.isSupportAbsoluteURL,
isSupportDataURL: options.isSupportDataURL,
isValueQuoted,
valueEndOffset,
valueStartOffset,
Expand Down
91 changes: 40 additions & 51 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,15 @@ export function parseSrc(input) {

const WINDOWS_ABS_PATH_REGEXP = /^[a-zA-Z]:[\\/]|^\\\\/;

export function isUrlRequestable(url) {
function isDataUrl(url) {
if (/^data:/i.test(url)) {
return true;
}

return false;
}

export function isURLRequestable(url, options = {}) {
// Protocol-relative URLs
if (/^\/\//.test(url)) {
return false;
Expand All @@ -389,8 +397,26 @@ export function isUrlRequestable(url) {
return true;
}

if (isDataUrl(url) && options.isSupportDataURL) {
try {
decodeURIComponent(url);
} catch (ignoreError) {
return false;
}

return true;
}

if (/^file:/i.test(url)) {
return true;
}

// Absolute URLs
if (/^[a-z][a-z0-9+.-]*:/i.test(url) && !WINDOWS_ABS_PATH_REGEXP.test(url)) {
if (/^https?:/i.test(url)) {
return options.isSupportAbsoluteURL;
}

return false;
}

Expand Down Expand Up @@ -483,7 +509,7 @@ export function requestify(context, request) {
return newRequest;
}

if (/^file:/i.test(newRequest)) {
if (/^[a-z]+:/i.test(newRequest)) {
return newRequest;
}

Expand Down Expand Up @@ -710,7 +736,12 @@ export function srcType(options) {
);
}

if (!isUrlRequestable(source.value)) {
if (
!isURLRequestable(source.value, {
isSupportDataURL: options.isSupportDataURL,
isSupportAbsoluteURL: options.isSupportAbsoluteURL,
})
) {
return [];
}

Expand Down Expand Up @@ -750,7 +781,12 @@ export function srcsetType(options) {
);
}

if (!isUrlRequestable(source.value)) {
if (
!isURLRequestable(source.value, {
isSupportDataURL: options.isSupportDataURL,
isSupportAbsoluteURL: options.isSupportAbsoluteURL,
})
) {
return false;
}

Expand Down Expand Up @@ -832,53 +868,6 @@ function metaContentType(options) {
return srcType(options);
}

// function webpackImportType(options) {
// let source;
//
// try {
// source = trimASCIIWhitespace(options.value);
// } catch (error) {
// throw new HtmlSourceError(
// `Bad value for attribute "${options.attribute}" on element "${options.tag}": ${error.message}`,
// options.attributeStartOffset,
// options.attributeEndOffset,
// options.html
// );
// }
//
// try {
// source = c0ControlCodesExclude(source);
// } catch (error) {
// throw new HtmlSourceError(
// `Bad value for attribute "${options.attribute}" on element "${options.tag}": ${error.message}`,
// options.attributeStartOffset,
// options.attributeEndOffset,
// options.html
// );
// }
//
// if (!isUrlRequestable(source.value)) {
// return [];
// }
//
// const { startOffset } = options.startTag;
// let { endOffset } = options.startTag;
//
// if (options.endTag) {
// ({ endOffset } = options.endTag);
// }
//
// return [
// {
// format: 'import',
// runtime: false,
// value: source.value,
// startOffset,
// endOffset,
// },
// ];
// }

const defaultSources = new Map([
[
"audio",
Expand Down
132 changes: 70 additions & 62 deletions test/__snapshots__/esModule-option.test.js.snap

Large diffs are not rendered by default.

596 changes: 566 additions & 30 deletions test/__snapshots__/loader.test.js.snap

Large diffs are not rendered by default.

374 changes: 193 additions & 181 deletions test/__snapshots__/minimize-option.test.js.snap

Large diffs are not rendered by default.

656 changes: 354 additions & 302 deletions test/__snapshots__/sources-option.test.js.snap

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion test/fixtures/simple.html
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ <h2>An Ordered HTML List</h2>
<input type="image" src="image.png" alt="Submit">
</form>

<script src="https://domain.org/script.js"></script>
<script src="https://github.com/webpack-contrib/css-loader/blob/master/src/index.js"></script>

<audio controls>
<source src="example.ogg" type="audio/ogg">
Expand Down Expand Up @@ -452,3 +452,5 @@ <h2>An Ordered HTML List</h2>
<noscript>
<img src="./noscript.png" alt="noscript content" />
</noscript>

<img src="https://raw.githubusercontent.com/webpack-contrib/html-loader/master/test/fixtures/image.png">
27 changes: 27 additions & 0 deletions test/loader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,31 @@ describe("loader", () => {
expect(getWarnings(stats)).toMatchSnapshot("warnings");
expect(getErrors(stats)).toMatchSnapshot("errors");
});

it("should work with `experiments.buildHttp`", async () => {
const compiler = getCompiler(
"simple.js",
{},
{
experiments: {
buildHttp: {
allowedUris: [() => true],
lockfileLocation: path.resolve(
__dirname,
"./lock-files/url/lock.json",
),
cacheLocation: path.resolve(__dirname, "./lock-files/url"),
},
},
},
);
const stats = await compile(compiler);

expect(getModuleSource("./simple.html", stats)).toMatchSnapshot("module");
expect(
execute(readAsset("main.bundle.js", compiler, stats)),
).toMatchSnapshot("result");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
expect(getErrors(stats)).toMatchSnapshot("errors");
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions test/lock-files/url/lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"https://github.com/webpack-contrib/css-loader/blob/master/src/index.js": {
"integrity": "sha512-1wkV5mn4eBNNjlMgq1+MyEUf8ucfKfJBsq+0oZelJZ7Y5L/ksHDSBojxLj6YbgrWNQfWfp3/SxCYLd/jS+u17g==",
"contentType": "text/html; charset=utf-8"
},
"https://raw.githubusercontent.com/webpack-contrib/html-loader/master/test/fixtures/image.png": {
"integrity": "sha512-bHqIPBYwzPsVLYcTDqJzwgvIaxLjmezufiCVXAMI0Naelf3eWVdydMA40hXbSuB0dZCGjCepuGaI7Ze8kLM+Ew==",
"contentType": "image/png"
},
"version": 1
}

0 comments on commit cc34b06

Please sign in to comment.