@realfavicongenerator/check-favicon
Version:
Check the favicon of a website
197 lines (169 loc) • 5.9 kB
text/typescript
import { CheckerMessage, CheckerStatus, Fetcher, MessageId, TouchIconIconReport, TouchIconReport, TouchIconTitleReport } from "./types";
import { HTMLElement } from 'node-html-parser'
import { CheckIconOutput, CheckIconProcessor, checkIcon, fetchFetcher, mergeUrlAndPath } from "./helper";
export const checkTouchIconTitle = async (baseUrl: string, head: HTMLElement | null, fetcher: Fetcher = fetchFetcher): Promise<TouchIconTitleReport> => {
const messages: CheckerMessage[] = [];
let appTitle = undefined;
if (!head) {
messages.push({
status: CheckerStatus.Error,
id: MessageId.noHead,
text: 'No <head> element'
});
return { messages };
}
const titleMarkup = head.querySelectorAll("meta[name='apple-mobile-web-app-title']");
if (titleMarkup.length === 0) {
messages.push({
status: CheckerStatus.Warning,
id: MessageId.noTouchWebAppTitle,
text: 'No touch web app title declared'
});
return { messages };
}
if (titleMarkup.length > 1) {
messages.push({
status: CheckerStatus.Error,
id: MessageId.multipleTouchWebAppTitles,
text: `The touch web app title is declared ${titleMarkup.length} times`
});
return { messages };
}
if (!titleMarkup[0].getAttribute('content')) {
messages.push({
status: CheckerStatus.Error,
id: MessageId.emptyTouchWebAppTitle,
text: 'The touch web app title has no content'
});
return { messages };
}
appTitle = titleMarkup[0].getAttribute('content');
messages.push({
status: CheckerStatus.Ok,
id: MessageId.touchWebAppTitleDeclared,
text: `The touch web app title is "${appTitle}"`
});
return { messages, appTitle };
}
export const checkTouchIconIcon = async (baseUrl: string, head: HTMLElement | null, fetcher: Fetcher = fetchFetcher): Promise<TouchIconIconReport> => {
const messages: CheckerMessage[] = [];
let icon: CheckIconOutput | null = null;
if (!head) {
messages.push({
status: CheckerStatus.Error,
id: MessageId.noHead,
text: 'No <head> element'
});
return { messages, icon };
}
const iconMarkup = head.querySelectorAll("link[rel='apple-touch-icon']");
if (iconMarkup.length === 0) {
messages.push({
status: CheckerStatus.Error,
id: MessageId.noTouchIcon,
text: 'No touch icon declared'
});
return { messages, icon };
}
messages.push({
status: CheckerStatus.Ok,
id: MessageId.touchIconDeclared,
text: 'The touch icon is declared'
});
const duplicatedSizes = getDuplicatedSizes(iconMarkup.map(icon => icon.getAttribute('sizes')));
if (duplicatedSizes.length > 0) {
messages.push({
status: CheckerStatus.Error,
id: MessageId.duplicatedTouchIconSizes,
text: `The touch icon sizes ${duplicatedSizes.map(s => s || '(no size)').join(', ')} are declared more than once`
});
}
const iconHref = iconMarkup[0].getAttribute('href');
if (!iconHref) {
messages.push({
status: CheckerStatus.Error,
id: MessageId.noTouchIconHref,
text: 'The touch icon has no href'
});
return { messages, icon };
}
const touchIconUrl = mergeUrlAndPath(baseUrl, iconHref);
const processor: CheckIconProcessor = {
cannotGet: (status) => {
messages.push({
status: CheckerStatus.Error,
id: MessageId.touchIconCannotGet,
text: `The touch icon cannot be fetched (${status})`
});
},
downloadable: () => {
messages.push({
status: CheckerStatus.Ok,
id: MessageId.touchIconDownloadable,
text: 'The touch icon is downloadable'
});
},
icon404: () => {
messages.push({
status: CheckerStatus.Error,
id: MessageId.touchIcon404,
text: `The touch icon at ${touchIconUrl} is not found`
});
},
noHref: () => {
messages.push({
status: CheckerStatus.Error,
id: MessageId.noTouchIconHref,
text: 'The touch icon markup has no href'
});
},
notSquare: (width, height) => {
messages.push({
status: CheckerStatus.Error,
id: MessageId.touchIconNotSquare,
text: `The touch icon is not square (${width}x${height})`
});
},
rightSize: (width) => {
messages.push({
status: CheckerStatus.Ok,
id: MessageId.touchIconSquare,
text: `The touch icon is square (${width}x${width})`
});
},
square: (width) => {
messages.push({
status: CheckerStatus.Ok,
id: MessageId.touchIconSquare,
text: `The touch icon is square (${width}x${width})`
});
},
wrongSize: (width) => {
messages.push({
status: CheckerStatus.Error,
id: MessageId.touchIconWrongSize,
text: `The touch icon has a wrong size (${width}x${width})`
});
}
}
icon = await checkIcon(
touchIconUrl,
processor,
fetcher,
undefined
);
return { messages, icon };
}
export const getDuplicatedSizes = (sizes: (string | undefined)[]): (string | undefined)[] => {
const duplicated = sizes.filter((size, index) => sizes.indexOf(size, index + 1) >= 0);
return duplicated.filter((size, index) => duplicated.indexOf(size) === index);
}
export const checkTouchIcon = async (baseUrl: string, head: HTMLElement | null, fetcher: Fetcher = fetchFetcher): Promise<TouchIconReport> => {
const titleReport = await checkTouchIconTitle(baseUrl, head, fetcher);
const iconReport = await checkTouchIconIcon(baseUrl, head, fetcher);
return {
messages: [...titleReport.messages, ...iconReport.messages],
appTitle: titleReport.appTitle,
icon: iconReport.icon
}
}