Forked from vadimfedenko/analyze-images
Forked from vadimfedenko/analyze-images
dist / pageImages.js
dist / pageImages.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.httpUrl = httpUrl;
exports.selectPageImages = selectPageImages;
exports.readLimited = readLimited;
const cheerio_1 = require("cheerio");
function httpUrl(value, base) {
const url = new URL(value, base);
if (!["http:", "https:"].includes(url.protocol))
throw new Error("Only HTTP(S) URLs are supported.");
url.hash = "";
return url.href;
}
// Preserve Product.image array order; never align unrelated DOM and JSON-LD arrays by index.
function selectPageImages(html, pageUrl) {
const $ = (0, cheerio_1.load)(html);
const nodes = [];
function visit(value) {
if (Array.isArray(value)) {
value.forEach(visit);
return;
}
if (!value || typeof value !== "object")
return;
const node = value;
nodes.push(node);
Object.values(node).forEach(visit);
}
$('script[type="application/ld+json"]').each((_, element) => {
try {
visit(JSON.parse($(element).text()));
}
catch { /* Skip malformed JSON-LD. */ }
});
const products = nodes.filter(node => [node["@type"]].flat().includes("Product"));
const canonical = (value) => {
try {
return typeof value === "string" ? httpUrl(value, pageUrl).replace(/\/$/, "") : undefined;
}
catch {
return undefined;
}
};
const currentPage = canonical(pageUrl);
const product = products.find(node => canonical(node.url) === currentPage || canonical(node["@id"]) === currentPage)
?? products[0];
const result = [];
const seen = new Set();
function add(value, source) {
if (result.length >= 64)
return;
if (value && typeof value === "object") {
const obj = value;
value = obj.contentUrl ?? obj.url;
}
if (typeof value !== "string" || !value.trim())
return;
try {
const url = httpUrl(value, pageUrl);
if (!seen.has(url)) {
seen.add(url);
result.push({ url, source });
}
}
catch { /* Skip data URLs and unsupported schemes. */ }
}
if (product) {
const images = Array.isArray(product.image) ? product.image : [product.image];
images.forEach((image, index) => add(image, `Product.image[${index + 1}]`));
}
if (result.length)
return result;
$('meta[property="og:image"], meta[name="twitter:image"]').each((_, element) => {
add($(element).attr("content"), "page metadata image");
});
if (result.length)
return result;
$('main img, article img').each((_, element) => {
add($(element).attr("data-src") ?? $(element).attr("src"), "main/article img");
});
return result;
}
async function readLimited(response, limit) {
if (!response.ok || !response.body) {
await response.body?.cancel();
throw new Error(`HTTP ${response.status}: ${response.url}`);
}
const reader = response.body.getReader();
const chunks = [];
let size = 0;
try {
while (true) {
const { done, value } = await reader.read();
if (done)
break;
size += value.length;
if (size > limit)
throw new Error(`Response exceeds ${limit} bytes.`);
chunks.push(Buffer.from(value));
}
}
finally {
await reader.cancel();
reader.releaseLock();
}
if (!size)
throw new Error("Empty response body.");
return Buffer.concat(chunks);
}
//# sourceMappingURL=pageImages.js.map"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.httpUrl = httpUrl;
exports.selectPageImages = selectPageImages;
exports.readLimited = readLimited;
const cheerio_1 = require("cheerio");
function httpUrl(value, base) {
const url = new URL(value, base);
if (!["http:", "https:"].includes(url.protocol))
throw new Error("Only HTTP(S) URLs are supported.");
url.hash = "";
return url.href;
}
// Preserve Product.image array order; never align unrelated DOM and JSON-LD arrays by index.
function selectPageImages(html, pageUrl) {
const $ = (0, cheerio_1.load)(html);
const nodes = [];
function visit(value) {
if (Array.isArray(value)) {
value.forEach(visit);
return;
}
if (!value || typeof value !== "object")
return;
const node = value;
nodes.push(node);
Object.values(node).forEach(visit);
}
$('script[type="application/ld+json"]').each((_, element) => {
try {
visit(JSON.parse($(element).text()));
}
catch { /* Skip malformed JSON-LD. */ }
});
const products = nodes.filter(node => [node["@type"]].flat().includes("Product"));
const canonical = (value) => {
try {
return typeof value === "string" ? httpUrl(value, pageUrl).replace(/\/$/, "") : undefined;
}
catch {
return undefined;
}
};
const currentPage = canonical(pageUrl);
const product = products.find(node => canonical(node.url) === currentPage || canonical(node["@id"]) === currentPage)
?? products[0];
const result = [];
const seen = new Set();
function add(value, source) {
if (result.length >= 64)
return;
if (value && typeof value === "object") {
const obj = value;
value = obj.contentUrl ?? obj.url;
}
if (typeof value !== "string" || !value.trim())
return;
try {
const url = httpUrl(value, pageUrl);
if (!seen.has(url)) {
seen.add(url);
result.push({ url, source });
}
}
catch { /* Skip data URLs and unsupported schemes. */ }
}
if (product) {
const images = Array.isArray(product.image) ? product.image : [product.image];
images.forEach((image, index) => add(image, `Product.image[${index + 1}]`));
}
if (result.length)
return result;
$('meta[property="og:image"], meta[name="twitter:image"]').each((_, element) => {
add($(element).attr("content"), "page metadata image");
});
if (result.length)
return result;
$('main img, article img').each((_, element) => {
add($(element).attr("data-src") ?? $(element).attr("src"), "main/article img");
});
return result;
}
async function readLimited(response, limit) {
if (!response.ok || !response.body) {
await response.body?.cancel();
throw new Error(`HTTP ${response.status}: ${response.url}`);
}
const reader = response.body.getReader();
const chunks = [];
let size = 0;
try {
while (true) {
const { done, value } = await reader.read();
if (done)
break;
size += value.length;
if (size > limit)
throw new Error(`Response exceeds ${limit} bytes.`);
chunks.push(Buffer.from(value));
}
}
finally {
await reader.cancel();
reader.releaseLock();
}
if (!size)
throw new Error("Empty response body.");
return Buffer.concat(chunks);
}
//# sourceMappingURL=pageImages.js.map