diff --git a/src/asset-inliner.mjs b/src/asset-inliner.mjs index b4f5ddc..15fa2d9 100644 --- a/src/asset-inliner.mjs +++ b/src/asset-inliner.mjs @@ -206,11 +206,11 @@ export class AssetInliner { const rel = getAttribute(tag, "rel") || ""; const href = getAttribute(tag, "href"); const asValue = getAttribute(tag, "as") || ""; - if (!href) { - return tag; - } if (/\bstylesheet\b/i.test(rel)) { + if (!href) { + return tag; + } const absolute = resolveUrl(href, baseUrl); if (!absolute || absolute.startsWith("data:")) { return ""; @@ -237,12 +237,19 @@ export class AssetInliner { } let output = tag; if (/\bpreload\b/i.test(rel) && /^image$/i.test(asValue)) { + if (!href) { + return ""; + } const imageSrcset = getAttribute(output, "imagesrcset"); if (imageSrcset) { output = setAttribute(output, "imagesrcset", await this.inlineSrcset(imageSrcset, baseUrl)); } } + if (!href) { + return tag; + } + const dataUri = await this.toDataUri(href, baseUrl, linkResourceType(asValue)); if (!dataUri) { return ""; diff --git a/test/asset-inliner.test.mjs b/test/asset-inliner.test.mjs index 6d97b34..2237c4c 100644 --- a/test/asset-inliner.test.mjs +++ b/test/asset-inliner.test.mjs @@ -82,6 +82,27 @@ test("external asset reporting parses srcset-like attributes without splitting U ]); }); +test("removes image preload srcsets when the link has no href", async () => { + const fetched = []; + const inliner = new AssetInliner(); + inliner.fetchAsset = async (rawUrl) => { + fetched.push(rawUrl); + return { + bytes: Buffer.from("asset"), + contentType: "image/jpeg" + }; + }; + + const output = await inliner.inlineHtml(` + + `, "https://example.com/article"); + + assert.deepEqual(fetched, []); + assert.doesNotMatch(output, /https:\/\/media\.example/); + assert.doesNotMatch(output, /imagesrcset/); + assert.deepEqual(findExternalAssetRefs(output), []); +}); + test("asset inliner skips URLs blocked by the filter hook", async () => { const blocked = []; const inliner = new AssetInliner({