This is an automated email from the ASF dual-hosted git repository. xuanwo pushed a commit to branch fix-image-url in repository https://gitbox.apache.org/repos/asf/opendal.git
commit 9ee2b811e130b0cbf1a860bc2473a7fd504b98c1 Author: Xuanwo <[email protected]> AuthorDate: Mon Mar 10 19:28:05 2025 +0800 fix(website): Handling svg image correctly Signed-off-by: Xuanwo <[email protected]> --- website/plugins/image-ssr-plugin.js | 167 +++++++++++++++++++++--------------- 1 file changed, 99 insertions(+), 68 deletions(-) diff --git a/website/plugins/image-ssr-plugin.js b/website/plugins/image-ssr-plugin.js index 42b8ae657..a2aee0fbd 100644 --- a/website/plugins/image-ssr-plugin.js +++ b/website/plugins/image-ssr-plugin.js @@ -22,105 +22,136 @@ const fs = require("fs-extra"); const axios = require("axios"); const { createHash } = require("crypto"); const cheerio = require("cheerio"); +const url = require("url"); -module.exports = function () { - return { - name: "docusaurus-image-ssr", +module.exports = function (context) { + const processedImages = new Map(); - async postBuild({ outDir }) { - console.log("Localizing external images in build output..."); + function getImageFilename(imageUrl) { + const hash = createHash("md5").update(imageUrl).digest("hex"); + let ext = ".jpg"; - const imagesDir = path.join(outDir, "img/external"); - await fs.ensureDir(imagesDir); + try { + const parsedUrl = url.parse(imageUrl); + const pathname = parsedUrl.pathname || ""; - const imageCache = new Map(); + const pathExt = path.extname(pathname); + if (pathExt) ext = pathExt; - async function downloadImage(url) { - if (imageCache.has(url)) { - return imageCache.get(url); - } - - try { - const hash = createHash("md5").update(url).digest("hex"); - const ext = path.extname(url) || ".jpg"; - const filename = `${hash}${ext}`; - const imagePath = path.join(imagesDir, filename); - - if (!fs.existsSync(imagePath)) { - console.log(`Downloading: ${url}`); - const response = await axios({ - url: url, - responseType: "arraybuffer", - timeout: 15000, - headers: { - "User-Agent": - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", - }, - }); - - await fs.writeFile(imagePath, response.data); - } + if ( + imageUrl.includes("img.shields.io") || + imageUrl.includes("actions?query") + ) { + ext = ".svg"; + } + } catch (e) {} + + return `${hash}${ext}`; + } + + async function downloadImage(imageUrl, buildDir) { + if (processedImages.has(imageUrl)) { + return processedImages.get(imageUrl); + } + + try { + const filename = getImageFilename(imageUrl); + const buildImagesDir = path.join(buildDir, "img/external"); + const buildOutputPath = path.join(buildImagesDir, filename); + + fs.ensureDirSync(buildImagesDir); + + if (!fs.existsSync(buildOutputPath)) { + console.log(`Downloading image: ${imageUrl}`); + + const response = await axios({ + url: imageUrl, + responseType: "arraybuffer", + timeout: 20000, + headers: { + "User-Agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", + Accept: "image/webp,image/apng,image/*,*/*;q=0.8", + }, + maxRedirects: 5, + validateStatus: (status) => status < 400, + }); - const localUrl = `/img/external/${filename}`; - imageCache.set(url, localUrl); - return localUrl; - } catch (error) { - console.error(`Failed to download ${url}: ${error.message}`); - return url; - } + await fs.writeFile(buildOutputPath, response.data); } - const htmlFiles = []; + const localUrl = `/img/external/${filename}`; + processedImages.set(imageUrl, localUrl); + return localUrl; + } catch (error) { + console.error(`Error downloading image ${imageUrl}: ${error.message}`); + return imageUrl; + } + } - async function findHtmlFiles(dir) { - const files = await fs.readdir(dir); + return { + name: "docusaurus-ssr-image-plugin", - for (const file of files) { - const filePath = path.join(dir, file); - const stat = await fs.stat(filePath); + async postBuild({ outDir }) { + console.log("Processing HTML files for external images..."); - if (stat.isDirectory()) { - await findHtmlFiles(filePath); - } else if (file.endsWith(".html")) { - htmlFiles.push(filePath); + const htmlFiles = []; + + async function findHtmlFiles(dir) { + const entries = await fs.readdir(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + await findHtmlFiles(fullPath); + } else if (entry.name.endsWith(".html")) { + htmlFiles.push(fullPath); } } } await findHtmlFiles(outDir); - console.log(`Found ${htmlFiles.length} HTML files`); for (const htmlFile of htmlFiles) { const html = await fs.readFile(htmlFile, "utf8"); - const $ = cheerio.load(html); + let $ = cheerio.load(html); + let modified = false; - const externalImages = $('img[src^="http"]'); - if (externalImages.length === 0) continue; + const externalImages = $("img").filter((_, el) => { + const src = $(el).attr("src"); + return src && src.startsWith("http"); + }); - console.log( - `Processing ${externalImages.length} images in ${htmlFile}`, - ); + if (externalImages.length === 0) continue; - const promises = []; + const downloadPromises = []; externalImages.each((_, img) => { const element = $(img); - const url = element.attr("src"); - - promises.push( - downloadImage(url).then((localUrl) => { - element.attr("src", localUrl); - element.attr("data-original-src", url); - }), + const imageUrl = element.attr("src"); + + if (!imageUrl || !imageUrl.startsWith("http")) return; + + downloadPromises.push( + downloadImage(imageUrl, outDir) + .then((localUrl) => { + if (localUrl !== imageUrl) { + element.attr("src", localUrl); + modified = true; + } + }) + .catch(() => {}), ); }); - await Promise.all(promises); + await Promise.all(downloadPromises); - await fs.writeFile(htmlFile, $.html()); + if (modified) { + await fs.writeFile(htmlFile, $.html()); + } } - console.log("Image localization complete!"); + console.log(`Processed ${processedImages.size} external images`); }, }; };
