UNPKG

@croct/plug

Version:

A fully-featured devkit for building natively personalized applications.

354 lines (353 loc) 9.14 kB
const typeMap = { post: /* @__PURE__ */ new Set([ "BlogPosting", "LiveBlogPosting", "NewsArticle", "AnalysisNewsArticle", "AskPublicNewsArticle", "BackgroundNewsArticle", "OpinionNewsArticle", "ReportageNewsArticle", "ReviewNewsArticle", "SocialMediaPosting", "BlogPosting", "DiscussionForumPosting", "LiveBlogPosting" ]), article: /* @__PURE__ */ new Set([ "Article", "TechArticle", "APIReference", "Report", "AdvertiserContentArticle", "SatiricalArticle", "ScholarlyArticle", "MedicalScholarlyArticle" ]), product: /* @__PURE__ */ new Set([ "Product", "ProductCollection", "ProductModel", "ProductGroup", "SomeProducts", "Vehicle", "BusOrCoach", "Car", "Motorcycle", "MotorizedBicycle", "Car", "Motorcycle", "IndividualProduct", "DietarySupplement", "Drug" ]), service: /* @__PURE__ */ new Set([ "Service", "BroadcastService", "CableOrSatelliteService", "FinancialProduct", "FoodService", "GovernmentService", "TaxiService", "WebAPI" ]) }; function parseEntity(content) { const data = parseJsonLd(content); if (data === null) { return null; } return extractEntity(data); } function extractEntity(data) { const type = getEntityType(data); switch (type) { case "article": case "post": { return { type, ...extractArticle(data) }; } case "product": case "service": { return { type, ...extractProduct(data) }; } } return null; } function getEntityType(data) { const type = getValue(data, "@type"); for (const [entityType, typeSet] of Object.entries(typeMap)) { if (contains(typeSet, type)) { return entityType; } } return null; } function extractArticle(data) { const postId = getValue(data, "identifier"); const post = {}; if (typeof postId === "string") { post.id = postId; } const title = getValue(data, "headline", "name"); if (typeof title === "string") { post.title = title; } const datePublished = getValue(data, "datePublished", "dateCreated"); const dateModified = getValue(data, "dateModified"); const updateTime = parseTimestamp(dateModified); const publishTime = parseTimestamp(datePublished) ?? updateTime; if (publishTime !== null) { post.publishTime = publishTime; if (updateTime !== null && updateTime > publishTime) { post.updateTime = updateTime; } } const url = getValue(data, "url", "mainEntityOfPage"); if (typeof url === "string" && URL.canParse(url)) { post.url = url; } else if (isObject(url)) { const urlValue = getValue(url, "url", "@id"); if (typeof urlValue === "string" && URL.canParse(urlValue)) { post.url = urlValue; } } const tags = []; const keywords = getValue(data, "keywords"); if (typeof keywords === "string") { tags.push( ...keywords.split(",").map((keyword) => keyword.trim()).filter((keyword) => keyword.length > 0) ); } else if (Array.isArray(keywords)) { for (const keyword of keywords) { if (typeof keyword === "string") { tags.push(keyword); } } } if (tags.length > 0) { post.tags = tags; } const categories = []; for (const key of ["articleSection", "genre"]) { const value = getValue(data, key); if (typeof value === "string") { categories.push(value); } else if (Array.isArray(value)) { for (const item of value) { if (typeof item === "string") { categories.push(item); } } } } const about = getValue(data, "about"); if (isObject(about)) { const aboutName = getValue(about, "name"); if (typeof aboutName === "string") { categories.push(aboutName); } } else if (Array.isArray(about)) { for (const item of about) { if (isObject(item)) { const itemName = getValue(item, "name"); if (typeof itemName === "string") { categories.push(itemName); } } } } if (categories.length > 0) { post.categories = categories; } const authors = []; const authorData = getValue(data, "author", "creator"); const authorList = Array.isArray(authorData) ? authorData : [authorData]; for (const author of authorList) { if (typeof author === "string") { authors.push(author); } else if (isObject(author)) { const name = getValue(author, "name"); if (typeof name === "string") { authors.push(name); } } } if (authors.length > 0) { post.authors = authors; } return post; } function extractProduct(data) { const product = {}; const productId = getValue(data, "productID", "identifier"); if (typeof productId === "string") { product.id = productId; } const productName = getValue(data, "name"); if (typeof productName === "string") { product.name = productName; } const prices = []; const urls = []; const offersList = Array.isArray(data.offers) ? data.offers : [data.offers]; for (const offer of offersList) { if (!isObject(offer)) { continue; } const url = getValue(offer, "url"); if (typeof url === "string") { urls.push(url); } for (const key of ["price", "lowPrice", "highPrice"]) { const value = parseNumber(getValue(offer, key)); if (value !== null && value >= 0) { prices.push(value); } } const specifications = getValue(offer, "priceSpecification"); const specList = Array.isArray(specifications) ? specifications : [specifications]; for (const specification of specList) { if (!isObject(specification)) { continue; } for (const key of ["price", "minPrice", "maxPrice"]) { const value = parseNumber(getValue(specification, key)); if (value !== null && value >= 0) { prices.push(value); } } } } if (prices.length > 0) { const displayPrice = Math.min(...prices); const originalPrice = Math.max(...prices); product.displayPrice = displayPrice; if (originalPrice > displayPrice) { product.originalPrice = originalPrice; } } const variant = []; for (const key of ["color", "pattern", "size", "material", "model"]) { const value = getValue(data, key); if (typeof value === "string") { variant.push(value); } else if (isObject(value)) { const name = getValue(value, "name"); if (typeof name === "string") { variant.push(name); } } } if (variant.length > 0) { product.variant = variant.join(", "); } const productSku = getValue(data, "sku"); if (typeof productSku === "string") { product.sku = productSku; } const category = getValue(data, "category"); if (typeof category === "string") { product.category = category; } else if (isObject(category)) { const name = getValue(category, "name"); if (typeof name === "string") { product.category = name; } } const brand = getValue(data, "brand"); if (typeof brand === "string") { product.brand = brand; } else if (isObject(brand)) { const name = getValue(brand, "name"); if (typeof name === "string") { product.brand = name; } } const productUrl = getValue(data, "url"); if (typeof productUrl === "string") { urls.unshift(productUrl); } for (const url of urls) { if (URL.canParse(url)) { product.url = url; break; } } const images = Array.isArray(data.image) ? data.image : [data.image]; for (const image of images) { if (typeof image === "string" && URL.canParse(image)) { product.imageUrl = image; break; } if (isObject(image)) { const imageUrl = getValue(image, "url"); if (typeof imageUrl === "string" && URL.canParse(imageUrl)) { product.imageUrl = imageUrl; break; } } } return product; } function parseJsonLd(content) { try { const data = JSON.parse(content); if (isObject(data)) { return data; } } catch { } return null; } function contains(set, value) { if (typeof value === "string") { return set.has(value); } return false; } function isObject(value) { return typeof value === "object" && value !== null && !Array.isArray(value); } function parseTimestamp(value) { if (typeof value === "string") { const date = Date.parse(value); if (!Number.isNaN(date)) { return date; } } return null; } function parseNumber(value) { if (typeof value === "number") { return value; } if (typeof value === "string") { const parsedValue = Number.parseFloat(value); if (!Number.isNaN(parsedValue)) { return parsedValue; } } return null; } function getValue(object, ...keys) { for (const key of keys) { const value = object[key]; if (value === void 0 || value === null || typeof value === "string" && value.trim() === "") { continue; } return value; } return null; } export { extractArticle, extractEntity, extractProduct, parseEntity };