UNPKG

@pwa-manifest/core

Version:

Core generation for a Web App Manifest, all necessary icons, and more!

984 lines (972 loc) 41.2 kB
var $4TZjN$sharp = require("sharp"); var $4TZjN$fs = require("fs"); var $4TZjN$path = require("path"); var $4TZjN$events = require("events"); var $4TZjN$crypto = require("crypto"); var $4TZjN$pwamanifestpotrace = require("@pwa-manifest/potrace"); function $parcel$interopDefault(a) { return a && a.__esModule ? a.default : a; } function $parcel$defineInteropFlag(a) { Object.defineProperty(a, '__esModule', {value: true, configurable: true}); } function $parcel$export(e, n, v, s) { Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true}); } $parcel$defineInteropFlag(module.exports); $parcel$export(module.exports, "htmlInsertToString", () => $a4d2c672af78824a$export$2a8a07b604159d2e); $parcel$export(module.exports, "default", () => $a4d2c672af78824a$export$2e2bcd8739ae039); const $a4d2c672af78824a$var$createEvent = (img, filename)=>({ filename: Promise.resolve(filename), content: Promise.resolve(img) }); const $a4d2c672af78824a$var$isValidURL = (url)=>{ try { new URL(url); return true; } catch (e) { return false; } }; const $a4d2c672af78824a$export$2a8a07b604159d2e = (insert)=>{ let out = "<" + insert[0]; for(const k in insert[1])out += " " + k + '="' + insert[1][k] + '"'; return out + ">"; }; class $a4d2c672af78824a$export$2e2bcd8739ae039 extends (0, $4TZjN$events.EventEmitter) { set hashFunction(v) { this.intHashFunction = v; } get baseIconName() { return this.intBaseIconName; } /** * The hash method for the icon filename fingerprints. Can be 'none' (no * fingerprinting), 'name' (fingerprint based on filename), or 'content' ( * fingerprint based on content of the file). Defaults to 'name', but using * 'content' is strongly encouraged. Write-only. */ set hashMethod(v) { this.defaultHashMethod = v; } /** The generated `browserconfig.xml` string. Do not try to modify this property. */ get browserConfig() { return `<?xml version="1.0" encoding="utf-8"?><browserconfig><msapplication><tile>${this.intBrowserConfig}</tile></msapplication></browserconfig>`; } constructor(opts, { baseURL: baseURL = "/", resolveDir: resolveDir = "." } = {}, fallback = {}){ super(); this.intHashFunction = (v)=>(0, $4TZjN$crypto.createHash)("md5").update(v).digest("hex"); this.defaultHashMethod = "name"; this.icons = []; this.shortcuts = []; this.screenshots = []; this.screenshotPaths = []; /** The object mapping of filename to content for each generated icon file. */ this.generatedFiles = {}; /** The generated object that can be stringified and written to `manifest.webmanifest` */ this.manifest = {}; /** * The HTML insertion values. Should all be inserted into the <head>. * To convert to a string, just iterate through each element and use * the `htmlInsertToString` function. */ this.html = []; if (!baseURL.endsWith("/")) baseURL += "/"; this.meta = { baseURL: baseURL, resolveDir: resolveDir }; // eslint-disable-next-line @typescript-eslint/no-explicit-any const opt = (src, keys)=>{ for (const k of keys){ const v = src[k]; if (typeof v !== "undefined") return v; } if (src === opts) return fallback[keys[0]]; }; // istanbul ignore next if (typeof opts !== "object") { if (typeof opts === "undefined") throw "No PWA Manifest options found."; throw "The PWA Manifest options must be an object containing the desired parameters."; } const env = process.env.NODE_ENV; if (env && opts[env.toLowerCase()]) { const { [env.toLowerCase()]: envOpts, ...otherOpts } = opts; // istanbul ignore next if (typeof envOpts !== "object") throw `The specific options for environment "${env}" must be an object containing the desired parameters.`; opts = { ...otherOpts, ...envOpts }; } const disabled = opt(opts, [ "disable", "disabled" ]) || false; // istanbul ignore next if (typeof disabled !== "boolean") throw "The disable option in the PWA manifest options must be a boolean."; this.disabled = disabled; const name = opt(opts, [ "name", "appName", "app-name" ]); // istanbul ignore next if (typeof name !== "string") { if (typeof name === "undefined") throw "No name was found in the options."; throw "The name provided in the options must be a string."; } this.name = name; const shortName = opt(opts, [ "shortName", "short-name", "short_name", "appShortName", "app-short-name" ]) || name; // istanbul ignore next if (typeof shortName !== "string") throw "The short name provided in the options must be a string."; this.shortName = shortName; const desc = opt(opts, [ "desc", "description" ]) || ""; // istanbul ignore next if (typeof desc !== "string") throw "The description provided in the options must be a string."; this.desc = desc; const startURL = opt(opts, [ "startURL", "startUrl", "start-url", "start_url" ]) || baseURL; // istanbul ignore next if (typeof startURL !== "string") throw "The start URL provided in the options must be a string."; this.startURL = startURL; const scope = opt(opts, [ "scope" ]) || baseURL; // istanbul ignore next if (typeof scope !== "string") throw "The scope provided in the options must be a string."; this.scope = scope; const theme = opt(opts, [ "themeColor", "theme-color", "theme_color", "theme" ]) || "white"; // istanbul ignore next if (typeof theme !== "string") throw "The theme color provided in the options must be a string representing a valid CSS color."; this.theme = theme; const extraParams = {}; const screenshots = opt(opts, [ "screenshots", "images" ]); // istanbul ignore next if (screenshots instanceof Array && screenshots.every((v)=>typeof v === "string" || typeof v === "object" && v && (typeof v.size === "string" || typeof v.size === "undefined") && typeof v.src === "string")) { const manifestScreenshots = []; const screenshotPaths = []; for (const rawSC of screenshots){ let sc = rawSC; let sizes; if (typeof rawSC === "object") { sc = rawSC.src; sizes = rawSC.size; } let ext = sc.slice(sc.lastIndexOf(".") + 1); if (ext === "jpg") ext = "jpeg"; else if (![ "png", "jpeg", "webp" ].includes(ext)) throw "Each screenshot in the screenshots must be of type PNG, WebP, or JPEG. Ensure that the filenames have the correct extensions."; if ($a4d2c672af78824a$var$isValidURL(sc)) manifestScreenshots.push({ src: sc, type: "image/" + ext, ...sizes && { sizes: sizes } }); else if ((0, $4TZjN$fs.existsSync)(sc = (0, $4TZjN$path.resolve)(resolveDir, sc))) screenshotPaths.push({ src: sc, size: sizes }); else throw "Every screenshot in the screenshots array must include a valid filepath or absolute URL to a screenshot image."; } this.screenshots = manifestScreenshots; this.screenshotPaths = screenshotPaths; } else if (typeof screenshots !== "undefined") throw "The screenshots provided in the options must be an array of screenshot filepaths, absolute URLs, or objects with a 'src' and a 'size'."; const shortcuts = opt(opts, [ "shortcuts", "pages", "links" ]); if (shortcuts instanceof Array && shortcuts.every((v)=>typeof v === "object" && v)) { const outShortcuts = []; for (const shortcut of shortcuts){ const name = opt(shortcut, [ "name" ]); if (typeof name !== "string") { if (typeof name === "undefined") throw "No name was found in the shortcut options."; throw "The name provided in the shortcut options must be a string."; } const shortName = opt(shortcut, [ "shortName", "short-name", "short_name" ]) || name; if (typeof shortName !== "string") throw "The short name provided in the shortcut options must be a string."; const url = opt(shortcut, [ "url", "page", "link" ]); if (typeof url !== "string") { if (typeof url === "undefined") throw "No URL was found in the shortcut options."; throw "The URL provided in the shortcut options must be a string."; } const desc = opt(shortcut, [ "desc", "description" ]) || ""; if (typeof desc !== "string") throw "The description provided in the shortcut options must be a string."; const iconPath = opt(shortcut, [ "icon" ]); let icon; if (typeof iconPath !== "undefined") { if (typeof iconPath !== "string") throw "The icon parameter in the shortcut options must be a string that contains the path to the icon."; const iconFullPath = (0, $4TZjN$path.resolve)(resolveDir, iconPath); if (!(0, $4TZjN$fs.existsSync)(iconFullPath)) throw "No icon was found at the path " + iconPath + "."; icon = iconFullPath; } const purposes = opt(shortcut, [ "purpose", "purposes" ]); // istanbul ignore next if (typeof purposes !== "undefined") { if (!icon) throw "The purposes parameter in the shortcut options can only exist if the shortcut has an icon."; if (!(purposes instanceof Array && purposes.every((val)=>[ "badge", "maskable", "monochrome", "any" ].includes(val)))) throw "The purposes parameter in the shortcut options must be an array for which each element is one of 'badge', 'maskable', 'monochrome', or 'any'."; } outShortcuts.push({ name: name, shortName: shortName, desc: desc, url: url, purposes: purposes, icon: icon }); } this.shortcuts = outShortcuts; } else if (typeof shortcuts !== "undefined") throw "The shortcuts provided in the options must be an array of shortcut options with names, URLs, and an optional icon, short name, and description."; const genIconOpts = opt(opts, [ "genIcon", "gen-icon", "iconGen", "icon-gen", "genIconOpts", "gen-icon-opts", "iconGenOpts", "icon-gen-opts", "generateIconOptions", "generate-icon-options", "iconGenerationOptions", "icon-generation-options", "icons" ]); // istanbul ignore next if (typeof genIconOpts !== "object") { if (typeof genIconOpts === "undefined") throw "No icon generation options found in the PWA manifest options."; throw "The icon generation options in the PWA manifest options must be an object containing the desired parameters."; } const msTileColor = opt(genIconOpts, [ "msTileColor", "ms-tile-color", "microsoftTileColor", "microsoft-tile-color" ]) || theme; // istanbul ignore next if (typeof msTileColor !== "string") throw "The Microsoft tile color provided in the options must be a string representing the theme color for the application."; this.intBrowserConfig = `<TileColor>${msTileColor}</TileColor>`; const baseIconPath = opt(genIconOpts, [ "baseIcon", "base-icon", "base_icon", "fromIcon", "from-icon", "from_icon", "icon" ]); // istanbul ignore next if (typeof baseIconPath !== "string") { if (typeof baseIconPath === "undefined") throw "No base icon was found in the icon generation options."; throw "The base icon parameter in the icon generation options must be a string that contains the path to the icon."; } const baseIconName = (0, $4TZjN$path.basename)(baseIconPath, baseIconPath.slice(baseIconPath.lastIndexOf("."))); const baseIconFullPath = (0, $4TZjN$path.resolve)(resolveDir, baseIconPath); // istanbul ignore next if (!(0, $4TZjN$fs.existsSync)(baseIconFullPath)) throw "No icon was found at the base icon path " + baseIconPath + "."; this.intBaseIconName = baseIconName; let sizes = [ 96, 152, 192, 384, 512 ]; // Common sizes const tmpSizes = opt(genIconOpts, [ "sizes", "sizeList", "size-list", "size_list" ]); // istanbul ignore next if (tmpSizes instanceof Array && tmpSizes.every((v)=>typeof v === "number")) // Needed in all PWAs sizes = [ ...new Set(tmpSizes.concat(192, 512)) ]; else if (typeof tmpSizes !== "undefined") throw "The sizes parameter in the icon generation options must be an array of numeric pixel values for sizes of the images."; this.sizes = sizes; let shortcutSizes = [ 96, 192 ]; const tmpShortcutSizes = opt(genIconOpts, [ "shortcutSizes", "shortcut-sizes", "shortcut_sizes", "shortcutSizeList", "shortcut-size-list", "shortcut_size_list" ]); // istanbul ignore next if (tmpShortcutSizes instanceof Array && tmpShortcutSizes.every((v)=>typeof v === "number")) // Needed in all PWAs shortcutSizes = [ ...new Set(tmpShortcutSizes.concat(96)) ]; else if (typeof tmpShortcutSizes !== "undefined") throw "The shortcut sizes parameter in the icon generation options must be an array of numeric pixel values for sizes of the shortcut icons."; this.shortcutSizes = shortcutSizes; const png = { compressionLevel: 9 }; let formats = { webp: { quality: 85, effort: 6 }, png: png }; const tmpFormats = opt(genIconOpts, [ "formats", "formatList", "format-list" ]); // istanbul ignore next if (tmpFormats instanceof Object && Object.keys(tmpFormats).every((v)=>[ "png", "jpeg", "webp", "tiff" ].includes(v))) { formats = { ...tmpFormats }; if (!tmpFormats.png) formats.png = png; } else if (typeof tmpFormats !== "undefined") throw "The formats parameter in the icon generation options must be an object with each key being a supported file type (png, webp, jpeg, or tiff) for the output images, and each value being the options to pass to sharp."; this.formats = formats; const resizeMethod = opt(genIconOpts, [ "resizeMethod", "resize-method", "resize" ]) || "cover"; // istanbul ignore next if (![ "cover", "contain", "fill" ].includes(resizeMethod)) throw "The resize method parameter in the icon generation options must be one of 'cover', 'contain', or 'fill'."; this.resizeOptions = { fit: resizeMethod, background: "rgba(0, 0, 0, 0)" }; this.baseIcon = (0, ($parcel$interopDefault($4TZjN$sharp)))(baseIconFullPath).ensureAlpha(); const purposes = opt(genIconOpts, [ "purpose", "purposes" ]); // istanbul ignore next if (typeof purposes !== "undefined") { if (!(purposes instanceof Array && purposes.every((val)=>[ "badge", "maskable", "monochrome", "any" ].includes(val)))) throw "The purposes parameter in the icon generation options must be an array for which each element is one of 'badge', 'maskable', 'monochrome', or 'any'."; } this.purposes = purposes || []; this.html.push([ "meta", { name: "msapplication-config", content: baseURL + "browserconfig.xml" } ]); this.html.push([ "meta", { name: "theme-color", content: theme } ]); const appleTouchIconBG = opt(genIconOpts, [ "appleTouchIconBG", "appleTouchIconBg", "apple-touch-icon-bg", "appleTouchIconBackground", "apple-touch-icon-background", "atib" ]) || theme; // istanbul ignore next if (typeof appleTouchIconBG !== "string") throw "The Apple Touch Icon background color parameter must be a string representing a valid CSS color."; this.appleTouchIconBG = appleTouchIconBG; const appleTouchIconPadding = opt(genIconOpts, [ "appleTouchIconPadding", "apple-touch-icon-padding", "atip" ]) ?? 12; // istanbul ignore next if (typeof appleTouchIconPadding !== "number") throw "The Apple Touch Icon padding parameter must be a number of pixels to pad the image with on each side."; this.appleTouchIconPadding = appleTouchIconPadding; const genFavicons = opt(genIconOpts, [ "genFavicons", "gen-favicons", "generateFavicons", "generate-favicons" ]); // istanbul ignore next if (![ "boolean", "undefined" ].includes(typeof genFavicons)) throw "The favicon generation option in the icon generation options must be a boolean."; this.doGenFavicons = genFavicons; const genPinnedTab = opt(genIconOpts, [ "genSafariPinnedTab", "gen-safari-pinned-tab", "genPinnedTab", "gen-pinned-tab", "generateSafariPinnedTab", "generate-safari-pinned-tab", "gpt", "gspt" ]); // istanbul ignore next if (![ "boolean", "undefined" ].includes(typeof genPinnedTab)) throw "The favicon generation option in the icon generation options must be a boolean."; this.doGenPinnedTab = genPinnedTab; const pinnedTabColor = opt(genIconOpts, [ "safariPinnedTabColor", "safari-pinned-tab-color", "pinnedTabColor", "pinned-tab-color", "sptc" ]); // istanbul ignore next if (typeof pinnedTabColor !== "string") { if (typeof pinnedTabColor !== "undefined") throw "The pinned tab color provided in the icon generation options must be a string representing a valid CSS color."; } else if (!genPinnedTab) throw "The pinned tab color cannot be specified without enabling pinned tab generation in the icon generation options."; // Sketchy method of detecting default theme this.pinnedTabColor = pinnedTabColor || theme === "white" ? "black" : theme; // No custom modifications for the rest of the common parameters, so we just do type checking const extraTypes = [ [ [ "background_color", "backgroundColor", "background-color", "bgColor", "bg-color", "bg" ], "string", theme ], [ [ "categories", "ctgs" ], (v)=>v instanceof Array && v.every((el)=>typeof el === "string") ], [ [ "dir", "direction", "textDirection", "text-direction" ], (v)=>[ "rtl", "ltr", "auto" ].includes(v) ], [ [ "display", "displayMode", "display-mode" ], (v)=>[ "standalone", "minimal-ui", "fullscreen", "browser" ].includes(v), "standalone" ], [ [ "iarc_rating_id", "iarc", "iarcId", "iarcID", "iarc-id", "iarcRatingId", "iarcRatingID", "iarc-rating-id", "iarcRating", "iarc-rating" ], "string" ], [ [ "lang", "language" ], "string" ], [ [ "orientation", "rotated", "screenOrientation", "screen-orientation" ], (v)=>[ "any", "natural", "landscape", "landscape-primary", "landscape-secondary", "portrait", "portrait-primary", "portrait-secondary" ].includes(v) ], [ [ "prefer_related_applications", "preferRelated", "prefer-related", "preferRelatedApplications", "prefer-related-applications" ], "boolean" ], [ [ "related_applications", "related", "relatedApplications", "related-applications" ], (v)=>v instanceof Array && v.every((el)=>{ if (typeof el !== "object" || typeof el.platform !== "string") return false; if (typeof el.id === "string") return typeof el.url === "undefined" || $a4d2c672af78824a$var$isValidURL(el.url); if ($a4d2c672af78824a$var$isValidURL(el.url)) return typeof el.id === "undefined"; return false; }) ] ]; for (const type of extraTypes){ let val; for (const paramName of type[0]){ val = opts[paramName]; if (typeof val !== "undefined") break; } if (typeof val === "undefined") { if (type[2]) // Default extraParams[type[0][0]] = type[2]; continue; } let checker; if (typeof type[1] === "string") checker = (v)=>typeof v === type[1]; else checker = type[1]; // istanbul ignore next if (!checker(val)) throw 'Parameter "' + type[0][0] + '" provided in the options is invalid. Please check the official MDN documentation on the Web App Manifest.'; extraParams[type[0][0]] = val; } // When this config inevitably becomes outdated, use the include parameter to include any new parameters relevant to the Web App Manifest. const include = opt(opts, [ "include", "includeParams", "include-params" ]); // istanbul ignore next if (include instanceof Array && include.every((v)=>typeof v === "string")) for (const param of include)extraParams[param] = opts[param]; else if (typeof include !== "undefined") throw "The include parameter in the options must be an array of extra parameter names to include in the final manifest."; this.extraParams = extraParams; } // eslint-disable-next-line @typescript-eslint/no-explicit-any emit(ev, ...args) { if (ev !== "*") super.emit("*", ev, ...args); // Allows attaching a listener to all events return super.emit(ev, ...args); } // eslint-disable-next-line @typescript-eslint/no-explicit-any on(ev, listener) { return super.on(ev, listener); } fingerprint(filename, buf, method = this.defaultHashMethod) { const i = filename.lastIndexOf("."); const base = filename.slice(0, i); const ext = filename.slice(i + 1); return base + "." + (method === "name" ? this.intHashFunction("_pwa-manifest-" + filename).slice(-8) + "." : method === "content" ? this.intHashFunction(buf.toString()).slice(-8) + "." : "") + ext; // Similar to (but not the same as) Parcel itself } /** * Generates the icon files, browser config, HTML, and web manifest * @returns The generated manifest data */ async generate() { if (this.disabled) return { browserConfig: "", generatedFiles: {}, html: [], manifest: {} }; this.emit("start"); await this.genDefaultIcons(); await this.genShortcutIcons(); if (this.doGenFavicons) await this.genFavicons(); if (this.doGenPinnedTab) await this.genSafariPinnedTab(); await this.genScreenshots(); await this.genAppleTouchIcon(); await this.genMsTileIcons(); await this.genManifest(); this.emit("end"); return { browserConfig: this.browserConfig, generatedFiles: this.generatedFiles, html: this.html, manifest: this.manifest }; } /** * Generates the default icons (i.e. the ones that appear in the manifest), * adding them to the `generatedFiles` property. */ async genDefaultIcons() { this.emit("defaultIconsStart", `Generating icons for ${this.name}...`); let purpose; if (this.purposes.length) purpose = this.purposes.join(" "); for (const size of this.sizes){ const icon = this.baseIcon.clone().resize(size, size, this.resizeOptions); const saveSize = size + "x" + size; for (const format of Object.keys(this.formats)){ let buf; try { buf = icon.clone()[format](this.formats[format]).toBuffer(); } catch (e) { // istanbul ignore next throw "An unknown error ocurred during the icon creation process: " + e; } const fn = this.baseIconName + "-" + saveSize + "." + format; const ev = $a4d2c672af78824a$var$createEvent(buf, fn); const fnProm = ev.filename; this.emit("defaultIconsGen", ev); buf = await ev.content; let filename = ev.filename === fnProm ? undefined : await ev.filename; if (!filename) filename = this.fingerprint(fn, buf); this.generatedFiles[filename] = buf; const iconEntry = { src: this.meta.baseURL + filename, sizes: saveSize, type: "image/" + format }; if (purpose) iconEntry.purpose = purpose; this.icons.push(iconEntry); } } this.emit("defaultIconsEnd"); } /** * Generates the icons for each shortcut in the manifest, adding them to the * `generatedFiles` property. */ async genShortcutIcons() { this.emit("shortcutIconsStart", `Generating shortcut icons...`); for(let i = 0; i < this.shortcuts.length; ++i){ const shortcut = this.shortcuts[i]; if (shortcut.icon) { const icons = []; const si = shortcut.icon; const shortcutIconName = (0, $4TZjN$path.basename)(si, si.slice(si.lastIndexOf("."))); let purpose = ""; if (shortcut.purposes) purpose = shortcut.purposes.join(" "); const shortcutIcon = (0, ($parcel$interopDefault($4TZjN$sharp)))(si); for (const size of this.shortcutSizes){ const icon = shortcutIcon.clone().resize(size, size, this.resizeOptions); const saveSize = size + "x" + size; for (const format of Object.keys(this.formats)){ let buf; try { buf = icon.clone()[format](this.formats[format]).toBuffer(); } catch (e) { // istanbul ignore next throw "An unknown error ocurred during the icon creation process: " + e; } const fn = "shortcut-" + shortcutIconName + i + "-" + saveSize + "." + format; const ev = $a4d2c672af78824a$var$createEvent(buf, fn); const fnProm = ev.filename; this.emit("shortcutIconsGen", ev); buf = await ev.content; let filename = ev.filename === fnProm ? undefined : await ev.filename; if (!filename) filename = this.fingerprint(fn, buf); this.generatedFiles[filename] = buf; const iconEntry = { src: this.meta.baseURL + filename, sizes: saveSize, type: "image/" + format }; if (purpose) iconEntry.purpose = purpose; icons.push(iconEntry); } } shortcut.icon = icons; } } this.emit("shortcutIconsEnd"); } /** * Generates the Apple Touch Icon for this config, adding it to the * `generatedFiles` property. */ async genAppleTouchIcon() { this.emit("appleTouchIconStart", "Generating Apple Touch Icon..."); let buf; const atiSize = 180 - 2 * this.appleTouchIconPadding; try { const appleTouchIconTransparent = await this.baseIcon.clone().resize(atiSize, atiSize, this.resizeOptions).extend({ top: this.appleTouchIconPadding, bottom: this.appleTouchIconPadding, left: this.appleTouchIconPadding, right: this.appleTouchIconPadding, background: "rgba(0, 0, 0, 0)" }).toBuffer(); buf = (0, ($parcel$interopDefault($4TZjN$sharp)))(appleTouchIconTransparent).flatten({ background: this.appleTouchIconBG }).png(this.formats.png).toBuffer(); } catch (e) { // istanbul ignore next throw "An unknown error ocurred during the Apple Touch Icon creation process: " + e; } const fn = "apple-touch-icon.png"; const ev = $a4d2c672af78824a$var$createEvent(buf, fn); const fnProm = ev.filename; this.emit("appleTouchIconGen", ev); buf = await ev.content; let atiname = ev.filename === fnProm ? undefined : await ev.filename; if (!atiname) atiname = this.fingerprint(fn, buf); this.generatedFiles[atiname] = buf; this.html.push([ "link", { rel: "apple-touch-icon", sizes: "180x180", href: this.meta.baseURL + atiname } ]); this.emit("appleTouchIconEnd"); } /** * Generates the screenshots for this config, adding it to the * `generatedFiles` property. */ async genScreenshots() { this.emit("screenshotsStart", "Generating screenshots..."); for (let { src: src, size: size } of this.screenshotPaths){ const image = (0, ($parcel$interopDefault($4TZjN$sharp)))(src); if (!size) { const { height: height, width: width } = await image.metadata(); size = `${width}x${height}`; } let buf = image.toBuffer(); const fn = (0, $4TZjN$path.basename)(src); const ev = $a4d2c672af78824a$var$createEvent(buf, fn); const fnProm = ev.filename; this.emit("screenshotsGen", ev); buf = await ev.content; let scName = ev.filename === fnProm ? undefined : await ev.filename; if (!scName) scName = this.fingerprint(fn, buf); this.generatedFiles[scName] = buf; this.screenshots.push({ src: this.meta.baseURL + scName, sizes: size, type: "image/" + scName.slice(scName.lastIndexOf(".") + 1) }); } this.emit("screenshotsEnd"); } /** * Generates the Safari Pinned Tab icon for this config, adding it to the * `generatedFiles` property. */ async genSafariPinnedTab() { this.emit("safariPinnedTabStart", "Generating Safari Pinned Tab..."); let safariPinnedTab; try { const { height: height, width: width } = await this.baseIcon.metadata(); safariPinnedTab = Buffer.from((0, $4TZjN$pwamanifestpotrace.posterize)(await this.baseIcon.clone().raw().toBuffer(), width, height)); } catch (e) { throw "An unknown error ocurred during the Safari Pinned Tab creation process: " + e; } const fn = "safari-pinned-tab.svg"; const ev = $a4d2c672af78824a$var$createEvent(safariPinnedTab, fn); const fnProm = ev.filename; this.emit("safariPinnedTabGen", ev); safariPinnedTab = await ev.content; let sptname = ev.filename === fnProm ? undefined : await ev.filename; if (!sptname) sptname = this.fingerprint(fn, safariPinnedTab); this.generatedFiles[sptname] = safariPinnedTab; this.html.push([ "link", { rel: "mask-icon", href: this.meta.baseURL + sptname, color: this.pinnedTabColor } ]); this.emit("safariPinnedTabEnd"); } /** * Generates the favicons for this config, adding them to the * `generatedFiles` property. */ async genFavicons() { this.emit("faviconStart", "Generating favicons..."); for (const size of [ 32, 16 ]){ let favicon; try { favicon = this.baseIcon.clone().resize(size, size, this.resizeOptions).png(this.formats.png).toBuffer(); } catch (e) { // istanbul ignore next throw "An unknown error ocurred during the favicon creation process: " + e; } const sizes = size + "x" + size; const fn = "favicon-" + sizes + ".png"; const ev = $a4d2c672af78824a$var$createEvent(favicon, fn); const fnProm = ev.filename; this.emit("faviconGen", ev); favicon = await ev.content; let filename = ev.filename === fnProm ? undefined : await ev.filename; if (!filename) filename = this.fingerprint(fn, favicon); this.generatedFiles[filename] = favicon; this.html.push([ "link", { rel: "icon", sizes: sizes, href: this.meta.baseURL + filename } ]); } this.emit("faviconEnd"); } /** * Generates the Microsoft Tile icons for this config, adding them to the * `generatedFiles` property. */ async genMsTileIcons() { this.emit("msTileStart", "Generating Microsoft Tile Icons..."); for (const size of [ 70, 150, 310 ]){ let msTile; try { msTile = this.baseIcon.clone().resize(size, size, this.resizeOptions).png(this.formats.png).toBuffer(); } catch (e) { // istanbul ignore next throw "An unknown error ocurred during the Microsoft Tile Icon creation process: " + e; } const sizes = size + "x" + size; const fn = "mstile-" + sizes + ".png"; const ev = $a4d2c672af78824a$var$createEvent(msTile, fn); const fnProm = ev.filename; this.emit("msTileGen", ev); msTile = await ev.content; let filename = ev.filename === fnProm ? undefined : await ev.filename; if (!filename) filename = this.fingerprint(fn, msTile); this.generatedFiles[filename] = msTile; this.intBrowserConfig += `<square${sizes}logo src="${this.meta.baseURL + filename}"/>`; } let rectMsTile; try { rectMsTile = this.baseIcon.clone().resize(310, 150, this.resizeOptions).png(this.formats.png).toBuffer(); } catch (e) { // istanbul ignore next throw "An unknown error ocurred during the Microsoft Tile Icon creation process: " + e; } const rectFn = "mstile-310x150.png"; const ev = $a4d2c672af78824a$var$createEvent(rectMsTile, rectFn); const rectFnProm = ev.filename; this.emit("msTileGen", ev); rectMsTile = await ev.content; let rectMsTileFilename = ev.filename === rectFnProm ? undefined : await ev.filename; if (!rectMsTileFilename) rectMsTileFilename = this.fingerprint(rectFn, rectMsTile); this.generatedFiles[rectMsTileFilename] = rectMsTile; this.intBrowserConfig += `<wide310x150logo src="${this.meta.baseURL + rectMsTileFilename}"/>`; this.emit("msTileEnd"); } /** * Generates the manifest object for this config, setting it into the * `manifest` property. */ async genManifest() { this.manifest = { name: this.name, short_name: this.shortName, start_url: this.startURL, scope: this.scope, ...this.desc && { description: this.desc }, icons: this.icons, theme_color: this.theme, ...this.screenshots.length && { screenshots: this.screenshots }, ...this.shortcuts.length && { shortcuts: this.shortcuts.map((shortcut)=>({ name: shortcut.name, short_name: shortcut.shortName, ...shortcut.desc && { description: shortcut.desc }, url: shortcut.url, ...shortcut.icon instanceof Array && { icons: shortcut.icon } })) }, ...this.extraParams }; this.html.push([ "link", { rel: "manifest", href: this.meta.baseURL + "manifest.webmanifest" } ]); } }