UNPKG

@realfavicongenerator/check-favicon

Version:

Check the favicon of a website

170 lines (169 loc) 7.07 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.checkIcoFavicon = exports.IcoFaviconSizes = void 0; const types_1 = require("../types"); const helper_1 = require("../helper"); const decode_ico_1 = __importDefault(require("decode-ico")); exports.IcoFaviconSizes = [48, 32, 16]; const checkIcoFavicon = (url, head, fetcher) => __awaiter(void 0, void 0, void 0, function* () { const messages = []; if (!head) { messages.push({ status: types_1.CheckerStatus.Error, id: types_1.MessageId.noHead, text: 'No <head> element' }); return { messages, icon: { content: null, url: null, width: null, height: null } }; } const icos = [ ...head.querySelectorAll('link[rel="shortcut icon"]'), ...head.querySelectorAll('link[rel="icon"][type="image/x-icon"]') ]; let iconUrl = null; let images; let isDeclared = false; if (icos.length > 1) { messages.push({ status: types_1.CheckerStatus.Error, id: types_1.MessageId.multipleIcoFavicons, text: `There are ${icos.length} ICO favicons` }); } else if (icos.length === 1) { isDeclared = true; messages.push({ status: types_1.CheckerStatus.Ok, id: types_1.MessageId.icoFaviconDeclared, text: 'The ICO favicon is declared' }); const href = icos[0].attributes.href; if (!href) { messages.push({ status: types_1.CheckerStatus.Error, id: types_1.MessageId.noIcoFaviconHref, text: 'The ICO markup has no href attribute' }); } else { iconUrl = (0, helper_1.mergeUrlAndPath)(url, href); } } else { // No declared ICO favicon, try the implicit /favicon.ico convention iconUrl = (0, helper_1.mergeUrlAndPath)(url, '/favicon.ico'); } // If we have an iconUrl (either from declaration or implicit), try to fetch it if (iconUrl) { const iconResponse = yield fetcher(iconUrl, 'image/x-icon'); if (iconResponse.status === 404) { if (isDeclared) { messages.push({ status: types_1.CheckerStatus.Error, id: types_1.MessageId.icoFavicon404, text: `ICO favicon not found at ${iconUrl}` }); } else { // Implicit favicon not found, report no ICO favicon messages.push({ status: types_1.CheckerStatus.Error, id: types_1.MessageId.noIcoFavicon, text: 'There is no ICO favicon' }); iconUrl = null; } } else if (iconResponse.status >= 300 || !iconResponse.readableStream) { if (isDeclared) { messages.push({ status: types_1.CheckerStatus.Error, id: types_1.MessageId.icoFaviconCannotGet, text: `Error fetching ICO favicon at ${iconUrl} (status ${iconResponse.status})` }); } else { // Implicit favicon cannot be fetched, report no ICO favicon messages.push({ status: types_1.CheckerStatus.Error, id: types_1.MessageId.noIcoFavicon, text: 'There is no ICO favicon' }); iconUrl = null; } } else { if (!isDeclared) { messages.push({ status: types_1.CheckerStatus.Ok, id: types_1.MessageId.icoFaviconImplicitInRoot, text: 'An implicit ICO favicon is found at /favicon.ico' }); } messages.push({ status: types_1.CheckerStatus.Ok, id: types_1.MessageId.icoFaviconDownloadable, text: 'ICO favicon found' }); const iconBuffer = yield (0, helper_1.readableStreamToBuffer)(iconResponse.readableStream); images = (0, decode_ico_1.default)(new Uint8Array(iconBuffer)); const imageSizes = images.map(image => `${image.width}x${image.height}`); const expectedSizes = exports.IcoFaviconSizes.map(size => `${size}x${size}`); const extraSizes = imageSizes.filter(size => !expectedSizes.includes(size)); if (extraSizes.length > 0) { messages.push({ status: types_1.CheckerStatus.Warning, id: types_1.MessageId.icoFaviconExtraSizes, text: `Extra sizes found in ICO favicon: ${extraSizes.join(', ')}` }); } const missingSizes = expectedSizes.filter(size => !imageSizes.includes(size)); if (missingSizes.length > 0) { messages.push({ status: types_1.CheckerStatus.Warning, id: types_1.MessageId.icoFaviconMissingSizes, text: `Missing sizes in ICO favicon: ${missingSizes.join(', ')}` }); } if (extraSizes.length === 0 && missingSizes.length === 0) { messages.push({ status: types_1.CheckerStatus.Ok, id: types_1.MessageId.icoFaviconExpectedSizes, text: `The ICO favicon has the expected sizes (${imageSizes.join(', ')})` }); } } } const theIcon = { content: null, url: iconUrl, width: null, height: null }; if (images) { const image = images[0]; const mimeType = (image.type === "bmp") ? "image/bmp" : "image/png"; theIcon.content = (0, helper_1.bufferToDataUrl)(Buffer.from(image.data.buffer, image.data.byteOffset, image.data.byteLength), mimeType); theIcon.width = image.width; theIcon.height = image.height; } return { messages, icon: theIcon, }; }); exports.checkIcoFavicon = checkIcoFavicon;