diff --git a/_11ty/img-dim.js b/_11ty/img-dim.js
index 7013669..b837aa4 100644
--- a/_11ty/img-dim.js
+++ b/_11ty/img-dim.js
@@ -19,7 +19,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-const { JSDOM } = require("jsdom");
+const { parse } = require("node-html-parser");
const { promisify } = require("util");
const sizeOf = promisify(require("image-size"));
const blurryPlaceholder = require("./blurry-placeholder");
@@ -69,13 +69,14 @@ const processImage = async (img, outputPath) => {
}
if (inputType == "gif") {
const videoSrc = await gif2mp4(src);
- const video = img.ownerDocument.createElement(
- /AMP/i.test(img.tagName) ? "amp-video" : "video"
- );
- [...img.attributes].map(({ name, value }) => {
+ const tagName = /AMP/i.test(img.tagName) ? "amp-video" : "video";
+ const video = parse(`<${tagName}>${tagName}>`).firstChild;
+
+ Object.entries(img.attributes).forEach(([name, value]) => {
video.setAttribute(name, value);
});
- video.src = videoSrc;
+
+ video.setAttribute("src", videoSrc);
video.setAttribute("autoplay", "");
video.setAttribute("muted", "");
video.setAttribute("loop", "");
@@ -83,7 +84,7 @@ const processImage = async (img, outputPath) => {
video.setAttribute("aria-label", img.getAttribute("alt"));
video.removeAttribute("alt");
}
- img.parentElement.replaceChild(video, img);
+ img.replaceWith(video);
return;
}
// When the input is a PNG, we keep the fallback image a PNG because JPEG does
@@ -98,28 +99,30 @@ const processImage = async (img, outputPath) => {
`background-size:cover;` +
`background-image:url("${await blurryPlaceholder(src)}")`
);
- const doc = img.ownerDocument;
- const picture = doc.createElement("picture");
- const avif = doc.createElement("source");
- const webp = doc.createElement("source");
- const jpeg = doc.createElement("source");
- const fallback = await setSrcset(jpeg, src, fallbackType);
+
+ const picture = parse("").firstChild;
+ const avifNode = parse("").firstChild;
+ const webpNode = parse("").firstChild;
+ const jpegNode = parse("").firstChild;
+
+ const fallback = await setSrcset(jpegNode, src, fallbackType);
if (!fallback) {
return;
}
- const avifFallback = await setSrcset(avif, src, "avif");
+ const avifFallback = await setSrcset(avifNode, src, "avif");
if (avifFallback) {
- avif.setAttribute("type", "image/avif");
- picture.appendChild(avif);
+ avifNode.setAttribute("type", "image/avif");
+ picture.appendChild(avifNode);
}
- const webpFallback = await setSrcset(webp, src, "webp");
+ const webpFallback = await setSrcset(webpNode, src, "webp");
if (webpFallback) {
- webp.setAttribute("type", "image/webp");
- picture.appendChild(webp);
+ webpNode.setAttribute("type", "image/webp");
+ picture.appendChild(webpNode);
}
- jpeg.setAttribute("type", `image/${fallbackType}`);
- picture.appendChild(jpeg);
- img.parentElement.replaceChild(picture, img);
+ jpegNode.setAttribute("type", `image/${fallbackType}`);
+ picture.appendChild(jpegNode);
+
+ img.replaceWith(picture);
picture.appendChild(img);
img.setAttribute("src", fallback);
} else if (!img.getAttribute("srcset")) {
@@ -149,12 +152,12 @@ const dimImages = async (rawContent, outputPath) => {
let content = rawContent;
if (outputPath && outputPath.endsWith(".html")) {
- const dom = new JSDOM(content);
- const images = [...dom.window.document.querySelectorAll("img,amp-img")];
+ const root = parse(content);
+ const images = root.querySelectorAll("img,amp-img");
if (images.length > 0) {
await Promise.all(images.map((i) => processImage(i, outputPath)));
- content = dom.serialize();
+ content = root.toString();
}
}
diff --git a/package-lock.json b/package-lock.json
index 5b4c1da..2611c0a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,6 +23,7 @@
"image-size": "^0.8.3",
"lru-cache": "^5.1.1",
"mocha": "^10.1.0",
+ "node-html-parser": "^7.0.2",
"phin": "^3.5.0",
"purge-from-html": "^1.0.3",
"purgecss": "^4.0.3",
@@ -1082,7 +1083,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
- "dev": true,
"license": "ISC"
},
"node_modules/brace-expansion": {
@@ -1806,7 +1806,6 @@
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
"integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
- "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">= 6"
@@ -5041,6 +5040,87 @@
"webidl-conversions": "^3.0.0"
}
},
+ "node_modules/node-html-parser": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-7.0.2.tgz",
+ "integrity": "sha512-DxodLVh7a6JMkYzWyc8nBX9MaF4M0lLFYkJHlWOiu7+9/I6mwNK9u5TbAMC7qfqDJEPX9OIoWA2A9t4C2l1mUQ==",
+ "license": "MIT",
+ "dependencies": {
+ "css-select": "^5.1.0",
+ "he": "1.2.0"
+ }
+ },
+ "node_modules/node-html-parser/node_modules/css-select": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
+ "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/node-html-parser/node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/node-html-parser/node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/node-html-parser/node_modules/domutils": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
+ "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/node-html-parser/node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/nopt": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz",
@@ -5079,7 +5159,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
- "dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0"
diff --git a/package.json b/package.json
index a92a741..aa8b7ee 100644
--- a/package.json
+++ b/package.json
@@ -61,6 +61,7 @@
"image-size": "^0.8.3",
"lru-cache": "^5.1.1",
"mocha": "^10.1.0",
+ "node-html-parser": "^7.0.2",
"phin": "^3.5.0",
"purge-from-html": "^1.0.3",
"purgecss": "^4.0.3",