@flexis/favicons
Version:
A tool for generating icons for the modern web.
1,060 lines (940 loc) • 30.3 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var _Promise = require('@babel/runtime-corejs3/core-js-stable/promise');
var _Reflect$deleteProperty = require('@babel/runtime-corejs3/core-js-stable/reflect/delete-property');
var _concatInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/concat');
var _filterInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/filter');
var _Object$entries = require('@babel/runtime-corejs3/core-js-stable/object/entries');
var _mapInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/map');
var _Array$isArray = require('@babel/runtime-corejs3/core-js-stable/array/is-array');
var _Object$assign = require('@babel/runtime-corejs3/core-js-stable/object/assign');
var _awaitAsyncGenerator = require('@babel/runtime-corejs3/helpers/awaitAsyncGenerator');
var _wrapAsyncGenerator = require('@babel/runtime-corejs3/helpers/wrapAsyncGenerator');
var _asyncIterator = require('@babel/runtime-corejs3/helpers/asyncIterator');
var Vinyl = require('vinyl');
var toIco = require('to-ico');
var path = require('path');
var _sortInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/sort');
var _indexOfInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/index-of');
var _reduceInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/reduce');
var _findInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/find');
var Sharp = require('sharp');
var xmldom = require('xmldom');
var canvas = require('canvas');
var fetch = require('node-fetch');
var Canvg = require('canvg');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () {
return e[k];
}
});
}
});
}
n['default'] = e;
return Object.freeze(n);
}
var _Promise__default = /*#__PURE__*/_interopDefaultLegacy(_Promise);
var _Reflect$deleteProperty__default = /*#__PURE__*/_interopDefaultLegacy(_Reflect$deleteProperty);
var _concatInstanceProperty__default = /*#__PURE__*/_interopDefaultLegacy(_concatInstanceProperty);
var _filterInstanceProperty__default = /*#__PURE__*/_interopDefaultLegacy(_filterInstanceProperty);
var _Object$entries__default = /*#__PURE__*/_interopDefaultLegacy(_Object$entries);
var _mapInstanceProperty__default = /*#__PURE__*/_interopDefaultLegacy(_mapInstanceProperty);
var _Array$isArray__default = /*#__PURE__*/_interopDefaultLegacy(_Array$isArray);
var _Object$assign__default = /*#__PURE__*/_interopDefaultLegacy(_Object$assign);
var _awaitAsyncGenerator__default = /*#__PURE__*/_interopDefaultLegacy(_awaitAsyncGenerator);
var _wrapAsyncGenerator__default = /*#__PURE__*/_interopDefaultLegacy(_wrapAsyncGenerator);
var _asyncIterator__default = /*#__PURE__*/_interopDefaultLegacy(_asyncIterator);
var Vinyl__default = /*#__PURE__*/_interopDefaultLegacy(Vinyl);
var toIco__default = /*#__PURE__*/_interopDefaultLegacy(toIco);
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
var _sortInstanceProperty__default = /*#__PURE__*/_interopDefaultLegacy(_sortInstanceProperty);
var _indexOfInstanceProperty__default = /*#__PURE__*/_interopDefaultLegacy(_indexOfInstanceProperty);
var _reduceInstanceProperty__default = /*#__PURE__*/_interopDefaultLegacy(_reduceInstanceProperty);
var _findInstanceProperty__default = /*#__PURE__*/_interopDefaultLegacy(_findInstanceProperty);
var Sharp__default = /*#__PURE__*/_interopDefaultLegacy(Sharp);
var canvas__namespace = /*#__PURE__*/_interopNamespace(canvas);
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
var Canvg__default = /*#__PURE__*/_interopDefaultLegacy(Canvg);
/**
* Add path to file name.
* @param path - Path to destination folder.
* @param filename - Target file name.
* @returns Full path to file.
*/
function applyPath(path$1, filename) {
return path$1 ? path.join(path$1, filename) : filename;
}
/**
* Get complete icon config from shirt config.
* @param iconsType - Icons type name.
* @param iconsConfig - Icons shirt config.
* @param manifestConfig - Config to get background color.
* @returns Complete config.
*/
function getCompleteIconConfig(iconsType, iconsConfig, {
background_color: background
}) {
const iconConfigSource = iconsConfig[iconsType];
if (!iconConfigSource) {
return null;
}
const {
offset: iconOffsetSource,
background: iconBackgroundSource
} = iconConfigSource === true ? {} : iconConfigSource;
const completeOffset = typeof iconOffsetSource !== 'undefined' ? iconOffsetSource : 0;
let completeBackground = typeof iconBackgroundSource !== 'undefined' ? iconBackgroundSource : false;
if (typeof completeBackground === 'boolean') {
if (iconsTransparency[iconsType] && !completeBackground) {
completeBackground = 'transparent';
} else {
completeBackground = background;
}
}
return {
offset: completeOffset,
background: completeBackground
};
}
const iconsTransparency = {
favicon: true,
android: true,
apple: false,
appleStartup: false
};
const iconsToGenerate = {
favicon: {
'favicon.ico': {
sizes: [{
width: 16,
height: 16
}, {
width: 24,
height: 24
}, {
width: 32,
height: 32
}, {
width: 48,
height: 48
}, {
width: 64,
height: 64
}]
},
'favicon-16x16.png': {
width: 16,
height: 16
},
'favicon-32x32.png': {
width: 32,
height: 32
}
},
android: {
'android-chrome-36x36.png': {
width: 36,
height: 36
},
'android-chrome-48x48.png': {
width: 48,
height: 48
},
'android-chrome-72x72.png': {
width: 72,
height: 72
},
'android-chrome-96x96.png': {
width: 96,
height: 96
},
'android-chrome-144x144.png': {
width: 144,
height: 144
},
'android-chrome-192x192.png': {
width: 192,
height: 192
},
'android-chrome-256x256.png': {
width: 256,
height: 256
},
'android-chrome-384x384.png': {
width: 384,
height: 384
},
'android-chrome-512x512.png': {
width: 512,
height: 512
}
},
// Sources of information:
// - https://stackoverflow.com/a/19933647
// - https://docs.axway.com/bundle/Titanium_SDK_allOS_en/page/icons_and_splash_screens.html
// - https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/app-icon/
apple: {
// 'apple-touch-icon-57x57.png' - non-retina iPhone/iPod touch
// 'apple-touch-icon-60x60.png' - unknown or notification icon for native apps
// 'apple-touch-icon-72x72.png' - non-retina iPad
// 'apple-touch-icon-76x76.png' - also non-retina iPad
// 'apple-touch-icon-114x114.png' - retina iPhone with iOS < 7
// modern retina iPhone
'apple-touch-icon-120x120.png': {
width: 120,
height: 120
},
// 'apple-touch-icon-144x144.png' - retina iPad with iOS < 7
// modern retina iPad
'apple-touch-icon-152x152.png': {
width: 152,
height: 152
},
// iPad Pro
'apple-touch-icon-167x167.png': {
width: 167,
height: 167
},
// iPhone X, iPhone [N] Plus
'apple-touch-icon-180x180.png': {
width: 180,
height: 180
},
// generic?
'apple-touch-icon.png': {
width: 180,
height: 180
} // 'apple-touch-icon-precomposed.png' - iOS < 7
},
// Sources of information:
// - https://www.paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions
// - https://docs.axway.com/bundle/Titanium_SDK_allOS_en/page/icons_and_splash_screens.html
// - https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/launch-screen/
// - https://appsco.pe/developer/splash-screens
appleStartup: {
// iPhone 5
'apple-touch-startup-image-640x1136.png': {
width: 640,
height: 1136,
pixelRatio: 2
},
// iPhone
'apple-touch-startup-image-750x1334.png': {
width: 750,
height: 1334,
pixelRatio: 2
},
// iPhone [N] Plus
'apple-touch-startup-image-1242x2208.png': {
width: 1242,
height: 2208,
pixelRatio: 3,
rotate: false
},
'apple-touch-startup-image-2208x1242.png': {
width: 2208,
height: 1242,
pixelRatio: 3,
rotate: true
},
// iPhone X
'apple-touch-startup-image-1125x2436.png': {
width: 1125,
height: 2436,
pixelRatio: 3,
rotate: false
},
'apple-touch-startup-image-2436x1125.png': {
width: 2436,
height: 1125,
pixelRatio: 3,
rotate: true
},
// iPhone Xr
'apple-touch-startup-image-828x1792.png': {
width: 828,
height: 1792,
pixelRatio: 2,
rotate: false
},
'apple-touch-startup-image-1792x828.png': {
width: 1792,
height: 828,
pixelRatio: 2,
rotate: true
},
// iPhone Xs Max
'apple-touch-startup-image-1242x2688.png': {
width: 1242,
height: 2688,
pixelRatio: 3,
rotate: false
},
'apple-touch-startup-image-2688x1242.png': {
width: 2688,
height: 1242,
pixelRatio: 3,
rotate: true
},
// iPad Mini, Air
'apple-touch-startup-image-1536x2048.png': {
width: 1536,
height: 2048,
pixelRatio: 2,
rotate: false
},
'apple-touch-startup-image-2048x1536.png': {
width: 2048,
height: 1536,
pixelRatio: 2,
rotate: true
},
// iPad Pro 10.5"
'apple-touch-startup-image-1668x2224.png': {
width: 1668,
height: 2224,
pixelRatio: 2,
rotate: false
},
'apple-touch-startup-image-2224x1668.png': {
width: 2224,
height: 1668,
pixelRatio: 2,
rotate: true
},
// iPad Pro 11"
'apple-touch-startup-image-1668x2388.png': {
width: 1668,
height: 2388,
pixelRatio: 2,
rotate: false
},
'apple-touch-startup-image-2388x1668.png': {
width: 2224,
height: 2388,
pixelRatio: 2,
rotate: true
},
// iPad Pro 12.9"
'apple-touch-startup-image-2048x2732.png': {
width: 2048,
height: 2732,
pixelRatio: 2,
rotate: false
},
'apple-touch-startup-image-2732x2048.png': {
width: 2732,
height: 2048,
pixelRatio: 2,
rotate: true
}
}
};
var htmlHeaders = {
favicon: getFaviconHeaders,
android: getAndroidHeaders,
apple: getAppleHeaders,
appleStartup: getAppleStartupHeaders
};
/**
* Get "favicon" headers.
* @param headersConfig - Config params.
* @returns Array of headers objects.
*/
function getFaviconHeaders({
path
}) {
var _context;
return _mapInstanceProperty__default['default'](_context = _Object$entries__default['default'](iconsToGenerate.favicon)).call(_context, ([filename, {
width,
height
}]) => {
const header = {
tagName: 'link',
rel: 'icon',
href: applyPath(path, filename)
};
if (width && height) {
header.type = 'image/png';
header.sizes = `${width}x${height}`;
}
return header;
});
}
/**
* Get "android" headers.
* @param headersConfig - Config params.
* @returns Array of headers objects.
*/
function getAndroidHeaders({
path,
webAppCapable = 'yes',
manifest: {
name: applicationName = null,
theme_color: themeColor = null
} = {}
}) {
const headers = [{
tagName: 'link',
rel: 'manifest',
href: applyPath(path, 'manifest.json')
}, {
tagName: 'meta',
name: 'mobile-web-app-capable',
content: webAppCapable
}];
if (typeof applicationName === 'string') {
headers.push({
tagName: 'meta',
name: 'application-name',
content: applicationName
});
}
if (typeof themeColor === 'string') {
headers.push({
tagName: 'meta',
name: 'theme-color',
content: themeColor
});
}
return headers;
}
/**
* Get "apple" headers.
* @param headersConfig - Config params.
* @returns Array of headers objects.
*/
function getAppleHeaders({
path,
webAppCapable = 'yes',
webAppStatusBarStyle = 'black-translucent',
manifest: {
name: applicationName = null
} = {}
}) {
var _context2;
const headers = [{
tagName: 'meta',
name: 'apple-mobile-web-app-capable',
content: webAppCapable
}, {
tagName: 'meta',
name: 'apple-mobile-web-app-status-bar-style',
content: webAppStatusBarStyle
}];
if (typeof applicationName === 'string') {
headers.push({
tagName: 'meta',
name: 'apple-mobile-web-app-title',
content: applicationName
});
}
const iconsHeaders = _mapInstanceProperty__default['default'](_context2 = _Object$entries__default['default'](iconsToGenerate.apple)).call(_context2, ([filename, {
width,
height
}]) => ({
tagName: 'link',
rel: 'apple-touch-icon',
sizes: `${width}x${height}`,
href: applyPath(path, filename)
}));
headers.push(...iconsHeaders);
return headers;
}
/**
* Calculate media query for apple startup image.
* @param iconToGenerateConfig - Config with icon info.
* @returns Media query for device.
*/
function getAppleStartupMediaQuery({
width,
height,
pixelRatio: maybePixelRatio,
rotate
}) {
const pixelRatio = typeof maybePixelRatio === 'number' ? maybePixelRatio : 1;
const screenWidth = width / pixelRatio;
const screenHeight = height / pixelRatio;
const deviceWidth = rotate ? screenHeight : screenWidth;
const deviceHeight = rotate ? screenWidth : screenHeight;
let query = `(device-width: ${deviceWidth}px) and (device-height: ${deviceHeight}px)`;
if (typeof rotate === 'boolean') {
query += ` and (orientation: ${rotate ? 'landscape' : 'portrait'})`;
}
if (pixelRatio > 1) {
query += ` and (-webkit-device-pixel-ratio: ${pixelRatio})`;
}
return query;
}
/**
* Get "apple startup" headers.
* @param headersConfig - Config params.
* @returns Array of headers objects.
*/
function getAppleStartupHeaders({
path
}) {
var _context3;
return _mapInstanceProperty__default['default'](_context3 = _Object$entries__default['default'](iconsToGenerate.appleStartup)).call(_context3, ([filename, config]) => ({
tagName: 'link',
rel: 'apple-touch-startup-image',
media: getAppleStartupMediaQuery(config),
href: applyPath(path, filename)
}));
}
const extensions = {
png: /^png$/,
svg: /^svg$/
};
/**
* Check image type
* @param type - Image extension without dot.
* @returns Image type is supported or not.
*/
function isSupportedType(type) {
return extensions.hasOwnProperty(type);
}
/**
* Check is "ico" or not.
* @param filename - File name to check.
* @returns Result of checking.
*/
function isIco(filename) {
return path__default['default'].extname(filename) === '.ico';
}
/**
* Check is "svg" or not.
* @param filename - File name to check.
* @returns Result of checking.
*/
function isSvg(filename) {
return path__default['default'].extname(filename) === '.svg';
}
const icons = {
favicon: true,
android: true,
apple: true,
appleStartup: true
};
class FaviconsGenerator {
constructor({
path,
manifest,
icons: icons$1
} = {}) {
this.config = {};
_Object$assign__default['default'](this.config, {
path,
manifest: {
background_color: '#fff',
...manifest
},
icons: icons$1 || icons
});
}
/**
* Create favicons form sources.
* @param sources - Favicons sources.
* @returns Results of handling.
*/
generateIcons(source) {
var _this = this;
return _wrapAsyncGenerator__default['default'](function* () {
const sources = _Array$isArray__default['default'](source) ? source : [source];
if (!sources.length) {
throw new Error('No sources provided.');
}
for (const source of sources) {
if (!Vinyl__default['default'].isVinyl(source) || source.isNull() || source.isStream()) {
throw new Error('Invalid source.');
}
const sourceType = source.extname.replace(/^\./, '');
if (!isSupportedType(sourceType)) {
throw new Error(`"${sourceType}" is not supported.`);
}
yield _awaitAsyncGenerator__default['default'](_this.attachMetadata(source));
}
const {
icons
} = _this.config;
for (const iconsType in icons) {
const iconsOfType = _this.generateIconsOfType(iconsType, sources);
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError;
try {
for (var _iterator = _asyncIterator__default['default'](iconsOfType), _step, _value; _step = yield _awaitAsyncGenerator__default['default'](_iterator.next()), _iteratorNormalCompletion = _step.done, _value = yield _awaitAsyncGenerator__default['default'](_step.value), !_iteratorNormalCompletion; _iteratorNormalCompletion = true) {
const icon = _value;
yield icon;
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
yield _awaitAsyncGenerator__default['default'](_iterator.return());
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
})();
}
/**
* Create full manifest object with icons.
* @returns Manifest object.
*/
generateManifset() {
const {
path,
manifest: manifestConfig,
icons: {
android
}
} = this.config;
const manifest = { ...manifestConfig
};
if (android) {
var _context;
manifest.icons = _mapInstanceProperty__default['default'](_context = _Object$entries__default['default'](iconsToGenerate.android)).call(_context, ([filename, {
width,
height
}]) => ({
src: applyPath(path, filename),
sizes: `${width}x${height}`,
type: 'image/png'
}));
}
return manifest;
}
/**
* Create HTML-headers for target icons.
* @param headersConfig - Custom headers config.
* @returns Array of headers objects. You can get HTML-markup with `getHtmlHeadersMarkup` helper.
*/
generateHtmlHeaders(headersConfig = {}) {
var _context2, _context3, _context4;
const {
icons,
...config
} = { ...this.config,
...headersConfig,
manifest: { ...this.config.manifest,
...headersConfig.manifest
}
};
const headers = _mapInstanceProperty__default['default'](_context2 = _filterInstanceProperty__default['default'](_context3 = _Object$entries__default['default'](icons)).call(_context3, ([, value]) => value)).call(_context2, ([type]) => htmlHeaders[type](config));
return _concatInstanceProperty__default['default'](_context4 = []).call(_context4, ...headers);
}
/**
* Create icons of given type.
* @param iconsType - Type of needed icons.
* @param sources - Icons sources.
* @returns Icons of given type.
*/
generateIconsOfType(iconsType, sources) {
var _this2 = this;
return _wrapAsyncGenerator__default['default'](function* () {
const {
manifest,
icons
} = _this2.config;
const iconConfig = getCompleteIconConfig(iconsType, icons, manifest);
if (iconConfig === null) {
return;
}
const iconsOfTypeToGenerate = iconsToGenerate[iconsType];
for (const filename in iconsOfTypeToGenerate) {
yield _this2.generateIcon(filename, sources, iconConfig, iconsOfTypeToGenerate[filename]);
}
})();
}
/**
* Create icon.
* @param filename - File name of icon.
* @param sources - Sources of icon.
* @param iconConfig - Icon config.
* @param iconToGenerateConfig - Icon to generate config.
*/
async generateIcon(filename, sources, iconConfig, iconToGenerateConfig) {
if (isIco(filename)) {
return this.generateIco(filename, sources, iconConfig, iconToGenerateConfig);
}
const target = sources[0].clone({
contents: false
});
const {
offset,
background
} = iconConfig;
const {
width,
height
} = iconToGenerateConfig;
const icon = await this.renderIcon(sources, {
width,
height,
background: background,
offset
});
target.basename = filename;
target.contents = icon;
_Reflect$deleteProperty__default['default'](target, 'metadata');
return target;
}
/**
* Create ".ico" icon.
* @param filename - File name of icon.
* @param sources - Sources of icon.
* @param iconConfig - Icon config.
* @param iconToGenerateConfig - Icon to generate config.
*/
async generateIco(filename, sources, iconConfig, iconToGenerateConfig) {
var _context5;
const target = sources[0].clone({
contents: false
});
const icons = await _Promise__default['default'].all(_mapInstanceProperty__default['default'](_context5 = iconToGenerateConfig.sizes).call(_context5, ({
width,
height
}) => this.generateIcon(`${width}x${height}.png`, sources, iconConfig, { ...iconToGenerateConfig,
width,
height
})));
const ico = await toIco__default['default'](_mapInstanceProperty__default['default'](icons).call(icons, ({
contents
}) => contents));
target.basename = filename;
target.contents = ico;
_Reflect$deleteProperty__default['default'](target, 'metadata');
return target;
}
}
const attributeNamesOrder = ['name', 'content', 'rel', 'type', 'sizes', 'media', 'href'];
/**
* Get order index of attribute name.
* @param attributeName - Target attribute name.
* @returns Index.
*/
function getAttributeOrder(attributeName) {
const index = _indexOfInstanceProperty__default['default'](attributeNamesOrder).call(attributeNamesOrder, attributeName);
return index === -1 ? attributeNamesOrder.length : index;
}
/**
* Comparator function to sort attributes entries.
* @param a - Left attribute entry.
* @param b - Right attribute entry.
* @returns Result of comparation.
*/
function comparator([a], [b]) {
return getAttributeOrder(a) - getAttributeOrder(b);
}
/**
* Make attribute string from entry.
* @param entry - Entry with attribute name and value.
* @returns Attribute string.
*/
function entryToString([key, value]) {
return `${key}="${String(value)}"`;
}
/**
* Get HTML-markup from objects.
* @param header - Object or array of objects with header info.
* @returns HTML-markup.
*/
function getHtmlHeadersMarkup(header) {
var _context, _context2;
if (_Array$isArray__default['default'](header)) {
return _mapInstanceProperty__default['default'](header).call(header, getHtmlHeadersMarkup).join('\n');
}
const {
tagName,
...attributes
} = header;
const attributesString = _mapInstanceProperty__default['default'](_context = _sortInstanceProperty__default['default'](_context2 = _Object$entries__default['default'](attributes)).call(_context2, comparator)).call(_context, entryToString).join(' ');
const markup = `<${tagName} ${attributesString}>`;
return markup;
}
const nodeCanvgPreset = Canvg.presets.node({
DOMParser: xmldom.DOMParser,
canvas: canvas__namespace,
fetch: fetch__default['default']
});
/**
* Attach image metadata to the vinyl file.
* @param source - Image file.
* @returns Source image file with attached metadata.
*/
async function attachMetadata(source) {
if (typeof source.metadata === 'object') {
return source;
}
if (isIco(source.basename)) {
source.metadata = {
format: 'ico',
width: 16,
height: 16
};
} else {
source.metadata = await Sharp__default['default'](source.contents).metadata();
}
return source;
}
/**
* Render icon.
* @param sources - Array of sources.
* @param renderConfig - Render config.
* @returns Rendered icon.
*/
async function renderIcon(sources, {
width,
height,
background,
offset
}) {
const maximumSide = Math.max(width, height);
const offsetPx = Math.round(maximumSide / 100 * offset) || 0;
const canvas = await createCanvas(width, height, background);
const sprite = await createSprite(sources, width, height, offsetPx);
const renderedSprite = await sprite.png().toBuffer();
canvas.composite([{
input: renderedSprite
}]);
return canvas.png().toBuffer();
}
/**
* Create canvas for rendering.
* @param width - Width of canvas.
* @param height - Height of canvas.
* @param background - Background color of canvas.
* @returns Canvas.
*/
function createCanvas(width, height, background) {
return Sharp__default['default']({
create: {
width,
height,
background,
channels: 4
}
});
}
/**
* Create sprite.
* @param sources - Sprite sources.
* @param width - Width of sprite.
* @param height - Height of sprite.
* @param offset - Offset from canvas edges.
* @returns Sprite.
*/
async function createSprite(sources, width, height, offset) {
const spriteWidth = width - offset * 2;
const spriteHeight = height - offset * 2;
const source = await getSuitableSourceBuffer(sources, spriteWidth, spriteHeight);
const sprite = Sharp__default['default'](source);
sprite.resize(spriteWidth, spriteHeight, {
fit: 'inside'
});
return sprite;
}
/**
* Get suitable source by size.
* @param sources - Array of sources.
* @param width - Needed width.
* @param height - Needed height.
* @returns Siutable source.
*/
async function getSuitableSourceBuffer(sources, width, height) {
const svgSource = _findInstanceProperty__default['default'](sources).call(sources, ({
basename
}) => isSvg(basename));
if (svgSource) {
await attachMetadata(svgSource);
const svg = svgSource.contents.toString('utf8');
const {
width: desiredWidth,
height: desiredHeight
} = getContainSize({
width,
height
}, svgSource.metadata);
const c = nodeCanvgPreset.createCanvas(0, 0);
const ctx = c.getContext('2d');
const v = Canvg__default['default'].fromString(ctx, svg, nodeCanvgPreset);
v.resize(desiredWidth, desiredHeight, 'xMidYMid meet');
await v.render();
return c.toBuffer();
}
for (const source of sources) {
await attachMetadata(source);
}
const maximumSide = Math.max(width, height);
const nearestIcon = _reduceInstanceProperty__default['default'](sources).call(sources, (nearestIcon, source) => {
const {
width: nearestIconWidth,
height: nearestIconHeight
} = nearestIcon.metadata;
const {
width: sourceWidth,
height: sourceHeight
} = source.metadata;
const nearestIconMaximumSide = Math.max(nearestIconWidth, nearestIconHeight);
const sourceMaximumSide = Math.max(sourceWidth, sourceHeight);
if ((nearestIconMaximumSide > sourceMaximumSide || nearestIconMaximumSide < maximumSide) && sourceMaximumSide >= maximumSide) {
return source;
}
return nearestIcon;
}, sources[0]);
return nearestIcon.contents;
}
/**
* Get size of icon to contain in box.
* @param boxSize - Size of box.
* @param iconsSize - Size of icon.
*/
function getContainSize({
width: boxWidth,
height: boxHeight
}, {
width: iconWidth,
height: iconHeight
}) {
const minBoxSide = Math.min(boxWidth, boxHeight);
const isWidthMin = minBoxSide === boxWidth;
return isWidthMin ? {
width: boxWidth,
height: Math.round(iconHeight / iconWidth * boxWidth)
} : {
height: boxHeight,
width: Math.round(iconWidth / iconHeight * boxHeight)
};
}
class FaviconsGenerator$1 extends FaviconsGenerator {
constructor() {
super(...arguments);
this.attachMetadata = attachMetadata;
this.renderIcon = renderIcon;
}
}
exports.FaviconsGenerator = FaviconsGenerator;
exports.default = FaviconsGenerator$1;
exports.extensions = extensions;
exports.getHtmlHeadersMarkup = getHtmlHeadersMarkup;
exports.isIco = isIco;
exports.isSupportedType = isSupportedType;
exports.isSvg = isSvg;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzIjpbXSwic291cmNlc0NvbnRlbnQiOltdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=