UNPKG

@waynehartman/recipe-scraper

Version:
364 lines (359 loc) 22.1 kB
// src/index.ts import * as cheerio from "cheerio"; import axios from "axios"; import { validate } from "jsonschema"; import microdata from "microdata-node"; // src/schema.json var schema_default = { $schema: "http://json-schema.org/draft-07/schema#", description: "A recipe scraped from the web", type: "object", required: ["name", "recipeIngredients"], properties: { name: { type: "string", minLength: 1 }, recipeIngredients: { type: "array", minItems: 1, items: { type: "string" } } } }; // src/utils.ts var MATCH_HTML_TAGS = /<[^>]*>?/gm; var MATCH_LINE_BREAK = /(\r\n|\n|\r)/gm; var MATCH_MULTI_SPACE = /&nbsp;|\s\s+/gm; function isValidHttpUrl(string) { let url; try { url = new URL(string); } catch (_) { return false; } return url.protocol === "http:" || url.protocol === "https:"; } // src/propertyTransforrmer.ts import { parse } from "iso8601-duration"; var htmlCodes = { "&#9;": " ", "&#10;": "\n", "&#32;": " ", "&#33;": "!", "&#35;": "#", "&#36;": "$", "&#37;": "%", "&#38;": "&", "&#39;": "'", "&#40;": "(", "&#41;": ")", "&#42;": "*", "&#43;": "+", "&#44;": ",", "&#45;": "-", "&#46;": ".", "&#47;": "/", "&#58;": ":", "&#59;": ";", "&#60;": "<", "&#61;": "=", "&#62;": ">", "&#63;": "?", "&#64;": "@", "&#91;": "[", "&#92;": "\\", "&#93;": "]", "&#94;": "^", "&#95;": "_", "&#96;": "`", "&#123;": "{", "&#124;": "|", "&#125;": "}", "&#126;": "~", "&#128;": "\u20AC", "&#130;": "\u201A", "&#131;": "\u0192", "&#132;": "\u201E", "&#133;": "\u2026", "&#134;": "\u2020", "&#135;": "\u2021", "&#136;": "\u02C6", "&#137;": "\u2030", "&#138;": "\u0160", "&#139;": "\u2039", "&#140;": "\u0152", "&#142;": "\u017D", "&#145;": "\u2018", "&#146;": "\u2019", "&#147;": "\u201C", "&#148;": "\u201D", "&#149;": "\u2022", "&#150;": "\u2013", "&#151;": "\u2014", "&#152;": "\u02DC", "&#153;": "\u2122", "&#154;": "\u0161", "&#155;": "\u203A", "&#156;": "\u0153", "&#158;": "\u017E", "&#159;": "\u0178", "&#160;": " ", "&#161;": "\xA1", "&#162;": "\xA2", "&#163;": "\xA3", "&#164;": "\xA4", "&#165;": "\xA5", "&#166;": "\xA6", "&#167;": "\xA7", "&#168;": "\xA8", "&#169;": "\xA9", "&#170;": "\xAA", "&#171;": "\xAB", "&#172;": "\xAC", "&#173;": "-", "&#174;": "\xAE", "&#175;": "\xAF", "&#176;": "\xB0", "&#177;": "\xB1", "&#178;": "\xB2", "&#179;": "\xB3", "&#180;": "\xB4", "&#181;": "\xB5", "&#182;": "\xB6", "&#183;": "\xB7", "&#184;": "\xB8", "&#185;": "\xB9", "&#186;": "\xBA", "&#187;": "\xBB", "&#188;": "\xBC", "&#189;": "\xBD", "&#190;": "\xBE", "&#191;": "\xBF", "&#192;": "\xC0", "&#193;": "\xC1", "&#194;": "\xC2", "&#195;": "\xC3", "&#196;": "\xC4", "&#197;": "\xC5", "&#198;": "\xC6", "&#199;": "\xC7", "&#200;": "\xC8", "&#201;": "\xC9", "&#202;": "\xCA", "&#203;": "\xCB", "&#204;": "\xCC", "&#205;": "\xCD", "&#206;": "\xCE", "&#207;": "\xCF", "&#208;": "\xD0", "&#209;": "\xD1", "&#210;": "\xD2", "&#211;": "\xD3", "&#212;": "\xD4", "&#213;": "\xD5", "&#214;": "\xD6", "&#215;": "\xD7", "&#216;": "\xD8", "&#217;": "\xD9", "&#218;": "\xDA", "&#219;": "\xDB", "&#220;": "\xDC", "&#221;": "\xDD", "&#222;": "\xDE", "&#223;": "\xDF", "&#224;": "\xE0", "&#225;": "\xE1", "&#226;": "\xE2", "&#227;": "\xE3", "&#228;": "\xE4", "&#229;": "\xE5", "&#230;": "\xE6", "&#231;": "\xE7", "&#232;": "\xE8", "&#233;": "\xE9", "&#234;": "\xEA", "&#235;": "\xEB", "&#236;": "\xEC", "&#237;": "\xED", "&#238;": "\xEE", "&#239;": "\xEF", "&#240;": "\xF0", "&#241;": "\xF1", "&#242;": "\xF2", "&#243;": "\xF3", "&#244;": "\xF4", "&#245;": "\xF5", "&#246;": "\xF6", "&#247;": "\xF7", "&#248;": "\xF8", "&#249;": "\xF9", "&#250;": "\xFA", "&#251;": "\xFB", "&#252;": "\xFC", "&#253;": "\xFD", "&#254;": "\xFE", "&#255;": "\xFF", "&#8364;": "\u20AC", "&#8482;": "\u2122", "&#8592;": "\u2190", "&#8593;": "\u2191", "&#8594;": "\u2192", "&#8595;": "\u2193", "&#8596;": "\u2194", "&#8629;": "\u21B5", "&#8704;": "\u2200", "&#8706;": "\u2202", "&#8707;": "\u2203", "&#8709;": "\u2205", "&#8711;": "\u2207", "&#8712;": "\u2208", "&#8713;": "\u2209", "&#8715;": "\u220B", "&#8719;": "\u220F", "&#8721;": "\u2211", "&#8722;": "\u2212", "&#8727;": "\u2217", "&#8730;": "\u221A", "&#8733;": "\u221D", "&#8734;": "\u221E", "&#8736;": "\u2220", "&#8743;": "\u2227", "&#8744;": "\u2228", "&#8745;": "\u2229", "&#8746;": "\u222A", "&#8747;": "\u222B", "&#8756;": "\u2234", "&#8764;": "\u223C", "&#8773;": "\u2245", "&#8776;": "\u2248", "&#8800;": "\u2260", "&#8801;": "\u2261", "&#8804;": "\u2264", "&#8805;": "\u2265", "&#8834;": "\u2282", "&#8835;": "\u2283", "&#8836;": "\u2284", "&#8838;": "\u2286", "&#8839;": "\u2287", "&#8853;": "\u2295", "&#8855;": "\u2297", "&#8869;": "\u22A5", "&#8901;": "\u22C5", "&#8968;": "\u2308", "&#8969;": "\u2309", "&#8970;": "\u230A", "&#8971;": "\u230B", "&#9674;": "\u25CA", "&#9824;": "\u2660", "&#9827;": "\u2663", "&#9829;": "\u2665", "&#9830;": "\u2666", "&Aacute;": "\xC1", "&aacute;": "\xE1", "&Abreve;": "\u0102", "&abreve;": "\u0103", "&Acirc;": "\xC2", "&acirc;": "\xE2", "&acute;": "\xB4", "&AElig;": "\xC6", "&aelig;": "\xE6", "&Agrave;": "\xC0", "&agrave;": "\xE0", "&Alpha;": "\u0391", "&alpha;": "\u03B1", "&Amacr;": "\u0100", "&amacr;": "\u0101", "&amp;": "&", "&and;": "\u2227", "&ang;": "\u2220", "&Aogon;": "\u0104", "&aogon;": "\u0105", "&Aring;": "\xC5", "&aring;": "\xE5", "&asymp;": "\u2248", "&Atilde;": "\xC3", "&atilde;": "\xE3", "&Auml;": "\xC4", "&auml;": "\xE4", "&bdquo;": "\u201E", "&Beta;": "\u0392", "&beta;": "\u03B2", "&brvbar;": "\xA6", "&bull;": "\u2022", "&Cacute;": "\u0106", "&cacute;": "\u0107", "&cap;": "\u2229", "&Ccaron;": "\u010C", "&ccaron;": "\u010D", "&Ccedil;": "\xC7", "&ccedil;": "\xE7", "&Ccirc;": "\u0108", "&ccirc;": "\u0109", "&Cdot;": "\u010A", "&cdot;": "\u010B", "&cedil;": "\xB8", "&cent;": "\xA2", "&Chi;": "\u03A7", "&chi;": "\u03C7", "&circ;": "\u02C6", "&clubs;": "\u2663", "&cong;": "\u2245", "&copy;": "\xA9", "&crarr;": "\u21B5", "&cup;": "\u222A", "&curren;": "\xA4", "&dagger;": "\u2020", "&Dagger;": "\u2021", "&darr;": "\u2193", "&Dcaron;": "\u010E", "&dcaron;": "\u010F", "&deg;": "\xB0", "&Delta;": "\u0394", "&delta;": "\u03B4", "&diams;": "\u2666", "&divide;": "\xF7", "&dot;": "\xB7", "&Dstrok;": "\u0110", "&dstrok;": "\u0111", "&Eacute;": "\xC9", "&eacute;": "\xE9", "&Ebreve;": "\u0114", "&ebreve;": "\u0115", "&Ecaron;": "\u011A", "&ecaron;": "\u011B", "&Ecirc;": "\xCA", "&ecirc;": "\xEA", "&Edot;": "\u0116", "&edot;": "\u0117", "&Egrave;": "\xC8", "&egrave;": "\xE8", "&Emacr;": "\u0112", "&emacr;": "\u0113", "&empty;": "\u2205", "&emsp;": "\u2003", "&ENG;": "\u014A", "&eng;": "\u014B", "&ensp;": "\u2002", "&Eogon;": "\u0118", "&eogon;": "\u0119", "&Epsilon;": "\u0395", "&epsilon;": "\u03B5", "&equiv;": "\u2261", "&Eta;": "\u0397", "&eta;": "\u03B7", "&ETH;": "\xD0", "&eth;": "\xF0", "&Euml;": "\xCB", "&euml;": "\xEB", "&euro;": "\u20AC", "&exist;": "\u2203", "&fnof;": "\u0192", "&forall;": "\u2200", "&frac12;": "\xBD", "&frac14;": "\xBC", "&frac34;": "\xBE", "&Gamma;": "\u0393", "&gamma;": "\u03B3", "&Gbreve;": "\u011E", "&gbreve;": "\u011F", "&Gcedil;": "\u0122", "&gcedil;": "\u0123", "&Gcirc;": "\u011C", "&gcirc;": "\u011D", "&Gdot;": "\u0120", "&gdot;": "\u0121", "&ge;": "\u2265", "&gt;": ">", "&harr;": "\u2194", "&Hcirc;": "\u0124", "&hcirc;": "\u0125", "&hearts;": "\u2665", "&hellip;": "\u2026", "&Hstrok;": "\u0126", "&hstrok;": "\u0127", "&Iacute;": "\xCD", "&iacute;": "\xED", "&Ibreve;": "\u012C", "&ibreve;": "\u012D", "&Icirc;": "\xCE", "&icirc;": "\xEE", "&Idot;": "\u0130", "&iexcl;": "\xA1", "&Igrave;": "\xCC", "&igrave;": "\xEC", "&IJlig;": "\u0132", "&ijlig;": "\u0133", "&Imacr;": "\u012A", "&imacr;": "\u012B", "&inodot;": "\u0131", "&infin;": "\u221E", "&int;": "\u222B", "&Iogon;": "\u012E", "&iogon;": "\u012F", "&Iota;": "\u0399", "&iota;": "\u03B9", "&iquest;": "\xBF", "&isin;": "\u2208", "&Itilde;": "\u0128", "&itilde;": "\u0129", "&Iuml;": "\xCF", "&iuml;": "\xEF", "&Jcirc;": "\u0134", "&jcirc;": "\u0135", "&Kappa;": "\u039A", "&kappa;": "\u03BA", "&Kcedil;": "\u0136", "&kcedil;": "\u0137", "&kgreen;": "\u0138", "&Lacute;": "\u0139", "&lacute;": "\u013A", "&Lambda;": "\u039B", "&lambda;": "\u03BB", "&laquo;": "\xAB", "&larr;": "\u2190", "&Lcaron;": "\u013D", "&lcaron;": "\u013E", "&Lcedil;": "\u013B", "&lcedil;": "\u013C", "&lceil;": "\u2308", "&ldquo;": "\u201C", "&le;": "\u2264", "&lfloor;": "\u230A", "&Lmidot;": "\u013F", "&lmidot;": "\u0140", "&lowast;": "\u2217", "&loz;": "\u25CA", "&lrm;": "\u200E", "&lsaquo;": "\u2039", "&lsquo;": "\u2018", "&Lstrok;": "\u0141", "&lstrok;": "\u0142", "&lt;": "<", "&macr;": "\xAF", "&mdash;": "\u2014", "&micro;": "\xB5", "&minus;": "\u2212", "&Mu;": "\u039C", "&mu;": "\u03BC", "&nabla;": "\u2207", "&Nacute;": "\u0143", "&nacute;": "\u0144", "&napos;": "\u0149", "&nbsp;": " ", "&Ncaron;": "\u0147", "&ncaron;": "\u0148", "&Ncedil;": "\u0145", "&ncedil;": "\u0146", "&ndash;": "\u2013", "&ne;": "\u2260", "&NewLine;": "\n", "&ni;": "\u220B", "&not;": "\xAC", "&notin;": "\u2209", "&nsub;": "\u2284", "&Ntilde;": "\xD1", "&ntilde;": "\xF1", "&Nu;": "\u039D", "&nu;": "\u03BD", "&Oacute;": "\xD3", "&oacute;": "\xF3", "&Obreve;": "\u014E", "&obreve;": "\u014F", "&Ocirc;": "\xD4", "&ocirc;": "\xF4", "&Odblac;": "\u0150", "&odblac;": "\u0151", "&OElig;": "\u0152", "&oelig;": "\u0153", "&Ograve;": "\xD2", "&ograve;": "\xF2", "&oline;": "\u203E", "&Omacr;": "\u014C", "&omacr;": "\u014D", "&Omega;": "\u03A9", "&omega;": "\u03C9", "&Omicron;": "\u039F", "&omicron;": "\u03BF", "&oplus;": "\u2295", "&or;": "\u2228", "&ordf;": "\xAA", "&ordm;": "\xBA", "&Oslash;": "\xD8", "&oslash;": "\xF8", "&Otilde;": "\xD5", "&otilde;": "\xF5", "&otimes;": "\u2297", "&Ouml;": "\xD6", "&ouml;": "\xF6", "&para;": "\xB6", "&part;": "\u2202", "&permil;": "\u2030", "&perp;": "\u22A5", "&Phi;": "\u03A6", "&phi;": "\u03C6", "&Pi;": "\u03A0", "&pi;": "\u03C0", "&piv;": "\u03D6", "&plusmn;": "\xB1", "&pound;": "\xA3", "&prime;": "\u2032", "&Prime;": "\u2033", "&prod;": "\u220F", "&prop;": "\u221D", "&Psi;": "\u03A8", "&psi;": "\u03C8", "&quot;": '"', "&Racute;": "\u0154", "&racute;": "\u0155", "&radic;": "\u221A", "&raquo;": "\xBB", "&rarr;": "\u2192", "&Rcaron;": "\u0158", "&rcaron;": "\u0159", "&Rcedil;": "\u0156", "&rcedil;": "\u0157", "&rceil;": "\u2309", "&rdquo;": "\u201D", "&reg;": "\xAE", "&rfloor;": "\u230B", "&Rho;": "\u03A1", "&rho;": "\u03C1", "&rlm;": "\u200F", "&rsaquo;": "\u203A", "&rsquo;": "\u2019", "&Sacute;": "\u015A", "&sacute;": "\u015B", "&sbquo;": "\u201A", "&Scaron;": "\u0160", "&scaron;": "\u0161", "&Scedil;": "\u015E", "&scedil;": "\u015F", "&Scirc;": "\u015C", "&scirc;": "\u015D", "&sdot;": "\u22C5", "&sect;": "\xA7", "&shy;": "\xAD", "&Sigma;": "\u03A3", "&sigma;": "\u03C3", "&sigmaf;": "\u03C2", "&sim;": "\u223C", "&spades;": "\u2660", "&sub;": "\u2282", "&sube;": "\u2286", "&sum;": "\u2211", "&sup;": "\u2283", "&sup1;": "\xB9", "&sup2;": "\xB2", "&sup3;": "\xB3", "&supe;": "\u2287", "&szlig;": "\xDF", "&Tab;": " ", "&Tau;": "\u03A4", "&tau;": "\u03C4", "&Tcaron;": "\u0164", "&tcaron;": "\u0165", "&Tcedil;": "\u0162", "&tcedil;": "\u0163", "&there4;": "\u2234", "&Theta;": "\u0398", "&theta;": "\u03B8", "&thetasym;": "\u03D1", "&thinsp;": "\u2009", "&THORN;": "\xDE", "&thorn;": "\xFE", "&tilde;": "\u02DC", "&times;": "\xD7", "&trade;": "\u2122", "&Tstrok;": "\u0166", "&tstrok;": "\u0167", "&Uacute;": "\xDA", "&uacute;": "\xFA", "&uarr;": "\u2191", "&Ubreve;": "\u016C", "&ubreve;": "\u016D", "&Ucirc;": "\xDB", "&ucirc;": "\xFB", "&Udblac;": "\u0170", "&udblac;": "\u0171", "&Ugrave;": "\xD9", "&ugrave;": "\xF9", "&Umacr;": "\u016A", "&umacr;": "\u016B", "&uml;": "\xA8", "&Uogon;": "\u0172", "&uogon;": "\u0173", "&upsih;": "\u03D2", "&Upsilon;": "\u03A5", "&upsilon;": "\u03C5", "&Uring;": "\u016E", "&uring;": "\u016F", "&Utilde;": "\u0168", "&utilde;": "\u0169", "&Uuml;": "\xDC", "&uuml;": "\xFC", "&Wcirc;": "\u0174", "&wcirc;": "\u0175", "&Xi;": "\u039E", "&xi;": "\u03BE", "&Yacute;": "\xDD", "&yacute;": "\xFD", "&Ycirc;": "\u0176", "&ycirc;": "\u0177", "&yen;": "\xA5", "&yuml;": "\xFF", "&Yuml;": "\u0178", "&Zeta;": "\u0396", "&zeta;": "\u03B6", "&zwj;": "\u200D", "&zwnj;": "\u200C" }; function transformImage(value) { if (typeof value === "string") return [value]; if (value.url) return [value.url]; if (Array.isArray(value)) { return value.map((item) => { if (typeof item === "string") { return item; } if (typeof item === "object") { if (item.url) { return item.url; } } return item; }).filter((item) => item !== void 0 && item !== ""); } return value; } function transformToList(value) { if (typeof value === "string") { if (value.includes(",")) return value.split(",").map((item) => item.trim()); return [value]; } if (Array.isArray(value)) { const array = value; const filtered = array.filter((item) => { return typeof item === "string"; }); return filtered; } return value; } function transformToString(value) { if (typeof value === "string") return cleanHtmlCodes(value); if (Array.isArray(value)) { const array = value; if (array.length > 0) { const filtered = array.filter((item) => { return typeof item === "string"; }); if (filtered.length > 0) { return cleanHtmlCodes(filtered[0]); } } } if (typeof value === "number") { return `${value}`; } return value; } function transformToAuthor(value) { if (!value) { return void 0; } if (typeof value === "string") return cleanHtmlCodes(value); if (value.name) return cleanHtmlCodes(value.name); if (Array.isArray(value)) { const array = value; if (array.length > 0) { const first = array[0]; if (typeof first === "string") { return first; } if (first.name) { return cleanHtmlCodes(first.name); } } } else if (typeof value === "object") { if (value.name) { return cleanHtmlCodes(value.name); } } return void 0; } function transformISOToString(dateObj) { let date = ""; if (dateObj.days) date += dateObj.days > 1 ? `${dateObj.days} days ` : `${dateObj.days} day `; if (dateObj.hours) date += dateObj.hours > 1 ? `${dateObj.hours} hours ` : `${dateObj.hours} hour `; if (dateObj.minutes) date += dateObj.minutes > 1 ? `${dateObj.minutes} minutes ` : `${dateObj.minutes} minute `; if (dateObj.seconds) date += dateObj.seconds > 1 ? `${dateObj.seconds} seconds ` : `${dateObj.seconds} second `; const final = date.trim(); if (final.length === 0) { return void 0; } return final; } function transformToTime(value) { if (!value) { return null; } const time = transformToString(value); if (!time) { return null; } try { const parsedISODuration = parse(time); if (parsedISODuration) return transformISOToString(parsedISODuration); } catch { } return time; } function cleanString(str) { let cleaned = str.replace(MATCH_HTML_TAGS, "").replace(MATCH_LINE_BREAK, " ").replace(MATCH_MULTI_SPACE, " ").trim(); if (cleaned.startsWith(", ")) { cleaned = cleaned.slice(1).trim(); } return cleanHtmlCodes(cleaned); } function transformToCleanString(value) { return cleanString(transformToString(value)); } function transformInstructions(value) { if (typeof value === "string") { const cleanedValue = cleanString(value); if (cleanedValue.includes(".,")) return cleanedValue.split(".,").map((item) => item.trim()); return [cleanedValue]; } if (Array.isArray(value)) { const firstItem = value[0]; if (typeof firstItem === "string") return value.map((item) => cleanString(item)).filter((item) => item !== void 0); if (typeof firstItem === "object" && !Array.isArray(firstItem)) { const itemList = firstItem.itemListElement; if (itemList && Array.isArray(itemList)) { return itemList.map((item) => { if (item.text) return cleanString(item.text); return void 0; }).filter((item) => item !== void 0); } } return value.map((item) => { if (item.text) return cleanString(item.text); return void 0; }).filter((item) => item !== void 0); } } function cleanIngredientAmounts(line) { let cleaned = line.replace(/¼/g, "1/4").replace(/½/g, "1/2").replace(/¾/g, "3/4").replace(/⅔/g, "2/3").replace(MATCH_HTML_TAGS, "").replace(MATCH_MULTI_SPACE, " ").trim(); if (cleaned.startsWith(", ")) { cleaned = cleaned.slice(1).trim(); } if (cleaned.startsWith("unit ")) { cleaned = cleaned.slice(4).trim(); } return cleaned; } function transformIngredients(value) { if (value && typeof value[0] === "string") return value.map((item) => cleanIngredientAmounts(item)); const mappedItems = []; Object.entries(value).forEach(([, item]) => { if (item.properties) { const { name, amount } = item.properties; if (name || amount) { const _name = name && name[0]; const _amount = amount && amount[0]; const singleLine = _amount ? `${_amount} ${_name}` : _name; mappedItems.push(cleanIngredientAmounts(singleLine)); } } }); if (mappedItems.length) return mappedItems; return []; } function cleanHtmlCodes(str) { if (!str || typeof str !== "string") { return str; } const regex = new RegExp(Object.keys(htmlCodes).join("|"), "g"); return str.replace(regex, (match) => htmlCodes[match]); } var propertyTransformerMap = { name: transformToString, author: transformToAuthor, image: transformImage, description: transformToCleanString, cookTime: transformToTime, prepTime: transformToTime, totalTime: transformToTime, recipeYield: transformToString, recipeIngredients: transformIngredients, recipeInstructions: transformInstructions, recipeCategories: transformToList, recipeCuisines: transformToList, keywords: transformToList }; var propertyTransforrmer_default = propertyTransformerMap; // src/index.ts var DEFAULT_OPTIONS = { maxRedirects: 5, timeout: 1e4, lang: "en-US,en;q=0.9", userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3" }; function consolidateRecipeProperties(recipe) { return { url: recipe.url, author: recipe.author, name: recipe.name, image: recipe.image || recipe.thumbnailUrl, description: recipe.description, cookTime: recipe.cookTime, prepTime: recipe.prepTime, totalTime: recipe.totalTime, recipeYield: recipe.recipeYield, recipeIngredients: recipe.recipeIngredient, recipeInstructions: recipe.recipeInstructions, recipeCategories: recipe.recipeCategory, recipeCuisines: recipe.recipeCuisine, keywords: recipe.keywords }; } function prettifyRecipe(recipe, url) { const transformedRecipe = {}; const consolidatedRecipe = consolidateRecipeProperties(recipe); transformedRecipe.url = recipe.url?.toString() || (isValidHttpUrl(url) ? url : void 0); Object.entries(consolidatedRecipe).forEach(([key, value]) => { const propertyTransformer = propertyTransforrmer_default[key]; if (value && propertyTransformer) transformedRecipe[key] = propertyTransformer(value); }); return transformedRecipe; } async function getRecipeData(input, inputOptions = {}) { let siteUrl, html, recipe; if (typeof input === "object") { inputOptions = input; siteUrl = input; } else { siteUrl = input; } const options = { ...DEFAULT_OPTIONS, ...inputOptions }; if (!isValidHttpUrl(siteUrl) && !options.html) throw new Error("Url must start with http:// or https://"); try { const response = await axios.get(siteUrl, { responseType: "text", headers: { "User-Agent": options.userAgent, "Accept-Language": options.lang, "Accept-Encoding": "gzip, deflate, br" }, timeout: options.timeout, maxRedirects: options.maxRedirects }); html = response.data; } catch (err) { const message = err instanceof Error ? err.message : "Unknown error"; if (options.html) html = options.html; else throw new Error(message); } try { const $ = cheerio.load(html); const tags = $('script[type="application/ld+json"]'); if (tags.length > 0) { for (let i = 0; i < tags.length; i++) { const tag = tags[i]; const textContent = $(tag).text(); if (textContent) { const data = JSON.parse(textContent); if (data["@graph"] && Array.isArray(data["@graph"])) { data["@graph"].forEach((g) => { if (g["@type"] === "Recipe") { recipe = g; } }); } if (data["@type"] === "Recipe") recipe = data; if (Array.isArray(data["@type"]) && data["@type"].includes("Recipe")) recipe = data; if (Array.isArray(data)) recipe = data.find((obj) => obj["@type"] && obj["@type"].includes("Recipe")); } else { throw new Error("Something went wrong while scraping"); } } } else { throw new Error("Trying search for microdata next"); } } catch { const data = microdata.toJson(html); if (!data || !data.items || !data.items[0]) throw new Error("HTML tags provided has no valid recipe schema"); const recipeData = Object.values(data.items).find((item) => item.type[0].includes("Recipe")); if (!recipeData?.properties) throw new Error("Recipe not found on page"); recipe = recipeData.properties; } if (!recipe) { throw new Error("Recipe not found on page"); } const prettifiedRecipe = prettifyRecipe(recipe, siteUrl); if (prettifiedRecipe !== void 0) { const response = validate({ name: prettifiedRecipe.name, recipeIngredients: prettifiedRecipe.recipeIngredients }, schema_default); if (!response.valid) throw new Error("Recipe is not valid"); return prettifiedRecipe; } } export { getRecipeData as default };