@realfavicongenerator/check-favicon
Version:
Check the favicon of a website
226 lines (225 loc) • 9.11 kB
JavaScript
;
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.reportHasWarnings = exports.reportHasErrors = exports.fetchFetcher = exports.filePathToDataUrl = exports.bufferToDataUrl = exports.parseSizesAttribute = exports.mergeUrlAndPath = exports.checkIcon = exports.pathToMimeType = exports.readableStreamToBuffer = exports.readableStreamToString = exports.stringToReadableStream = exports.filePathToString = exports.filePathToReadableStream = void 0;
const promises_1 = __importDefault(require("fs/promises"));
const types_1 = require("./types");
const sharp_1 = __importDefault(require("sharp"));
const filePathToReadableStream = (path) => __awaiter(void 0, void 0, void 0, function* () {
const file = yield promises_1.default.open(path, 'r');
const stream = file.createReadStream();
return new ReadableStream({
start(controller) {
stream.on('data', (chunk) => {
controller.enqueue(chunk);
});
stream.on('close', () => {
controller.close();
});
}
});
});
exports.filePathToReadableStream = filePathToReadableStream;
const filePathToString = (path) => __awaiter(void 0, void 0, void 0, function* () {
return (promises_1.default.readFile(path, 'utf-8'));
});
exports.filePathToString = filePathToString;
const stringToReadableStream = (str) => {
const encoder = new TextEncoder();
const uint8Array = encoder.encode(str);
return new ReadableStream({
start(controller) {
controller.enqueue(uint8Array);
controller.close();
}
});
};
exports.stringToReadableStream = stringToReadableStream;
const readableStreamToString = (readableStream) => __awaiter(void 0, void 0, void 0, function* () {
const reader = readableStream.getReader();
const chunks = [];
let done = false;
while (!done) {
const { value, done: doneValue } = yield reader.read();
done = doneValue;
if (value) {
chunks.push(value);
}
}
const concatenatedChunks = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
let offset = 0;
for (const chunk of chunks) {
concatenatedChunks.set(chunk, offset);
offset += chunk.length;
}
return new TextDecoder("utf-8").decode(concatenatedChunks);
});
exports.readableStreamToString = readableStreamToString;
const readableStreamToBuffer = (readableStream) => __awaiter(void 0, void 0, void 0, function* () {
const reader = readableStream.getReader();
const chunks = [];
let done = false;
while (!done) {
const { value, done: doneValue } = yield reader.read();
done = doneValue;
if (value) {
chunks.push(value);
}
}
const concatenatedChunks = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
let offset = 0;
for (const chunk of chunks) {
concatenatedChunks.set(chunk, offset);
offset += chunk.length;
}
return Buffer.from(concatenatedChunks);
});
exports.readableStreamToBuffer = readableStreamToBuffer;
const pathToMimeType = (path) => {
const ext = path.split('.').pop();
switch (ext) {
case 'png':
return 'image/png';
case 'svg':
return 'image/svg+xml';
case 'ico':
return 'image/x-icon';
case 'jpg':
case 'jpeg':
return 'image/jpeg';
default:
return 'application/octet-stream';
}
};
exports.pathToMimeType = pathToMimeType;
const checkIcon = (iconUrl, processor, fetcher, mimeType, expectedWidthHeight) => __awaiter(void 0, void 0, void 0, function* () {
if (!iconUrl) {
processor.noHref();
return null;
}
const res = yield fetcher(iconUrl, mimeType);
if (res.status === 404) {
processor.icon404();
}
else if (res.status >= 300) {
processor.cannotGet(res.status);
}
else if (res.readableStream) {
processor.downloadable();
const rawContent = yield (0, exports.readableStreamToBuffer)(res.readableStream);
const meta = yield (0, sharp_1.default)(rawContent).metadata();
const contentType = res.contentType || (0, exports.pathToMimeType)(iconUrl);
const content = yield (0, exports.bufferToDataUrl)(rawContent, contentType);
if (meta.width && meta.height) {
if (meta.width !== meta.height) {
processor.notSquare(meta.width, meta.height);
}
else {
processor.square(meta.width);
if (expectedWidthHeight) {
if (meta.width === expectedWidthHeight) {
processor.rightSize(meta.width);
}
else {
processor.wrongSize(meta.width);
}
}
}
}
return {
content,
url: iconUrl,
width: meta.width || null,
height: meta.height || null
};
}
return {
content: null,
url: iconUrl,
width: null,
height: null
};
});
exports.checkIcon = checkIcon;
const mergeUrlAndPath = (baseUrl, absoluteOrRelativePath) => {
// If the path is a full URL, return it as is
if (absoluteOrRelativePath.startsWith('http://') || absoluteOrRelativePath.startsWith('https://')) {
return absoluteOrRelativePath;
}
const url = new URL(baseUrl);
// Protocol-relative URL
if (absoluteOrRelativePath.startsWith('//')) {
return `${url.protocol}${absoluteOrRelativePath}`;
}
else if (absoluteOrRelativePath.startsWith('/')) {
// If the path starts with a slash, replace the pathname
return `${url.origin}${absoluteOrRelativePath}`;
}
else {
// Otherwise, append the path to the existing pathname
return `${url.href}${url.href.endsWith('/') ? '' : '/'}${absoluteOrRelativePath}`;
}
};
exports.mergeUrlAndPath = mergeUrlAndPath;
const parseSizesAttribute = (sizes) => {
if (!sizes) {
return null;
}
const match = sizes.match(/(\d+)x(\d+)/);
if (match) {
if (match[1] !== match[2]) {
return null;
}
return parseInt(match[1]);
}
return null;
};
exports.parseSizesAttribute = parseSizesAttribute;
const bufferToDataUrl = (buffer, mimeType) => {
return `data:${mimeType};base64,${buffer.toString('base64')}`;
};
exports.bufferToDataUrl = bufferToDataUrl;
const filePathToDataUrl = (filePath) => __awaiter(void 0, void 0, void 0, function* () {
const readStream = yield (0, exports.filePathToReadableStream)(filePath);
const rawContent = yield (0, exports.readableStreamToBuffer)(readStream);
const contentType = (0, exports.pathToMimeType)(filePath);
return (0, exports.bufferToDataUrl)(rawContent, contentType);
});
exports.filePathToDataUrl = filePathToDataUrl;
const fetchFetcher = (url, contentType) => __awaiter(void 0, void 0, void 0, function* () {
const res = yield fetch(url, {
headers: {
'Content-Type': contentType || (0, exports.pathToMimeType)(url),
'user-agent': 'RealFaviconGenerator Favicon Checker'
}
});
return {
status: res.status,
contentType: res.headers.get('Content-Type') || null,
readableStream: res.body
};
});
exports.fetchFetcher = fetchFetcher;
const reportHasErrors = (report) => {
return report.desktop.messages.some(message => message.status === types_1.CheckerStatus.Error) ||
report.touchIcon.messages.some(message => message.status === types_1.CheckerStatus.Error) ||
report.webAppManifest.messages.some(message => message.status === types_1.CheckerStatus.Error);
};
exports.reportHasErrors = reportHasErrors;
const reportHasWarnings = (report) => {
return report.desktop.messages.some(message => message.status === types_1.CheckerStatus.Warning) ||
report.touchIcon.messages.some(message => message.status === types_1.CheckerStatus.Warning) ||
report.webAppManifest.messages.some(message => message.status === types_1.CheckerStatus.Warning);
};
exports.reportHasWarnings = reportHasWarnings;