realfavicon
Version:
Check favicon with RealFaviconGenerator from the CLI
257 lines (240 loc) • 11 kB
JavaScript
import { Command } from 'commander';
import { checkFavicon, reportHasErrors, reportHasWarnings, CheckerStatus } from '@realfavicongenerator/check-favicon';
import { parse } from 'node-html-parser';
import open from 'open';
import fs from 'fs/promises';
import { generateFaviconFiles, generateFaviconHtml } from '@realfavicongenerator/generate-favicon';
import { getNodeImageAdapter, loadAndConvertToSvg } from '@realfavicongenerator/image-adapter-node';
import { injectMarkupInHtmlHead } from '@realfavicongenerator/inject-markups';
import path from 'path';
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol */
function __awaiter(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());
});
}
function __values(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
}
function __asyncValues(o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
const CheckExamples = `
Examples:
realfavicon check 3000 # Check the favicon at http://localhost:3000
realfavicon check https://example.com # Check the favicon at https://example.com
`;
const getUrl = (urlOrPort) => {
if (urlOrPort.match(/^\d+$/))
return `http://localhost:${urlOrPort}`;
return urlOrPort;
};
const RealFaviconGeneratorBaseUrl = 'https://realfavicongenerator.net/';
const statusToIcon = (status) => {
switch (status) {
case CheckerStatus.Error:
return '❌';
case CheckerStatus.Warning:
return '⚠️';
case CheckerStatus.Ok:
return '✅';
}
return '❓';
};
const printMessages = (report, indentation = 2) => {
const indent = ' '.repeat(indentation);
report.forEach((message) => {
console.log(`${indent}${statusToIcon(message.status)} ${message.text}`);
});
};
var Screen;
(function (Screen) {
Screen[Screen["Cli"] = 0] = "Cli";
Screen[Screen["RealFavicon"] = 1] = "RealFavicon";
})(Screen || (Screen = {}));
const stringToScreen = (screen) => {
switch (screen) {
case 'cli':
return Screen.Cli;
default:
case 'realfavicon':
return Screen.RealFavicon;
}
};
const printCliReport = (report) => {
console.log();
console.log("Desktop");
printMessages(report.desktop.messages);
console.log();
console.log("Touch");
printMessages(report.touchIcon.messages);
console.log();
console.log("Web Manifest");
printMessages(report.webAppManifest.messages);
};
const check = (urlOrPort_1, screen_1, ...args_1) => __awaiter(void 0, [urlOrPort_1, screen_1, ...args_1], void 0, function* (urlOrPort, screen, warningAsErrors = false) {
const url = getUrl(urlOrPort);
console.log(`Check favicon at ${url}`);
const response = yield fetch(url);
const html = yield response.text();
const root = parse(html);
const head = root.querySelector('head');
const report = yield checkFavicon(url, head);
console.log('Check completed');
switch (screen) {
case Screen.Cli:
printCliReport(report);
break;
case Screen.RealFavicon:
const result = yield fetch(`${RealFaviconGeneratorBaseUrl}api/check/report`, { method: 'POST', body: JSON.stringify(report) });
const json = yield result.json();
const reportUrl = `${RealFaviconGeneratorBaseUrl}checker/${json.id}`;
console.log(`Open report at ${reportUrl}`);
yield open(reportUrl);
break;
}
if (reportHasErrors(report)) {
return 2;
}
else if (warningAsErrors && reportHasWarnings(report)) {
return 1;
}
return 0;
});
var version = "0.6.3";
var p = {
version: version};
const toBuffer = (data) => __awaiter(void 0, void 0, void 0, function* () {
if (data instanceof Blob) {
return new Uint8Array(yield data.arrayBuffer());
}
if (data instanceof Buffer) {
return Uint8Array.from(data);
}
return Uint8Array.from(Buffer.from(data, 'utf8'));
});
const generate = (imagePath, settingsPath, outputData, assetsDir) => __awaiter(void 0, void 0, void 0, function* () {
var _a, e_1, _b, _c;
const imageAdapter = yield getNodeImageAdapter();
// Open master image
const masterIcon = {
icon: yield loadAndConvertToSvg(imagePath),
};
// Open settings
const faviconSettingsFile = yield fs.readFile(settingsPath, 'utf8');
const faviconSettings = JSON.parse(faviconSettingsFile);
// Create output directory
yield fs.mkdir(assetsDir, { recursive: true });
// Generate files
const files = yield generateFaviconFiles(masterIcon, faviconSettings, imageAdapter);
try {
for (var _d = true, _e = __asyncValues(Object.keys(files)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
_c = _f.value;
_d = false;
const fileName = _c;
const file = files[fileName];
const filePath = `${assetsDir}/${fileName}`;
yield fs.writeFile(filePath, yield toBuffer(file));
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
}
finally { if (e_1) throw e_1.error; }
}
// Generate HTML
const html = yield generateFaviconHtml(faviconSettings);
yield fs.writeFile(outputData, JSON.stringify(html, null, 2));
});
const inject = (markupsFile, outputDir, htmlFiles) => __awaiter(void 0, void 0, void 0, function* () {
var _a, htmlFiles_1, htmlFiles_1_1;
var _b, e_1, _c, _d;
const markupFile = yield fs.readFile(markupsFile, 'utf8');
const markups = JSON.parse(markupFile);
yield fs.mkdir(outputDir, { recursive: true });
try {
for (_a = true, htmlFiles_1 = __asyncValues(htmlFiles); htmlFiles_1_1 = yield htmlFiles_1.next(), _b = htmlFiles_1_1.done, !_b; _a = true) {
_d = htmlFiles_1_1.value;
_a = false;
const htmlFile = _d;
const content = yield fs.readFile(htmlFile, 'utf8');
const injected = injectMarkupInHtmlHead(content, markups.markups, markups.cssSelectors);
yield fs.writeFile(path.join(outputDir, path.basename(htmlFile)), injected);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_a && !_b && (_c = htmlFiles_1.return)) yield _c.call(htmlFiles_1);
}
finally { if (e_1) throw e_1.error; }
}
});
const program = new Command();
program
.name('realfavicon')
.description('Generate and check favicon with RealFaviconGenerator')
.version(p.version);
program.command('check')
.description('Check a favicon')
.argument('<URL or port>', 'URL to check, or simply a port to target localhost')
.option('-s, --screen <screen>', 'Screen where the report is displayed, can be cli or realfavicon (ie. the browser)', 'realfavicon')
.option('-w, --warningsAsErrors', 'Return a non-zero exit code if there are warnings')
.addHelpText('after', CheckExamples)
.action((urlOrPort, options) => __awaiter(void 0, void 0, void 0, function* () {
const code = yield check(urlOrPort, stringToScreen(options.screen), options.warningsAsErrors);
process.exit(code);
}));
program.command('generate').
description('Generate a favicon')
.argument('<image>', 'Path of the image file to use as the favicon')
.argument('<settings>', `Path of the favicon settings file, which can be created via ${RealFaviconGeneratorBaseUrl}favicon/command-line`)
.argument('<output data>', 'Path of the favicon data output file')
.argument('<assets directory>', 'Directory where the favicon files are stored')
.action((imagePath, settingsPath, outputData, assetsDir) => __awaiter(void 0, void 0, void 0, function* () {
yield generate(imagePath, settingsPath, outputData, assetsDir);
}));
program.command('inject').
description('Inject favicon markups in HTML files')
.argument('<markups>', `Path of the markups file, created via the 'generate' command`)
.argument('<output dir>', `Output directory`)
.argument('<HTML files...>', 'Path of the HTML files to inject the favicon markups into')
.action((markupsFile, outputDir, htmlFiles) => __awaiter(void 0, void 0, void 0, function* () {
yield inject(markupsFile, outputDir, htmlFiles);
}));
program.parse();