@vite-pwa/assets-generator
Version:
Zero-config PWA Assets Generator
405 lines (399 loc) • 12.5 kB
JavaScript
'use strict';
const index = require('../index.cjs');
const splash = require('../shared/assets-generator.ByZ3i-gX.cjs');
const favicon = require('../shared/assets-generator.B7iBT7Rs.cjs');
require('sharp');
require('sharp-ico');
function resolveAppleSplashScreensInstructions(image, imageAssets, instructions, useAppleSplashScreens) {
const appleSplashScreens = resolveAppleSplashScreens(
imageAssets,
useAppleSplashScreens
);
if (!appleSplashScreens || !appleSplashScreens.sizes.length)
return;
const { linkMediaOptions, name: resolveName, sizes, png } = appleSplashScreens;
const sizesMap = /* @__PURE__ */ new Map();
const splashScreens = sizes.reduce((acc, size) => {
if (sizesMap.get(size.width) === size.height)
return acc;
sizesMap.set(size.width, size.height);
const { width: height, height: width, ...restSize } = size;
const {
width: lheight,
height: lwidth,
...restResizeOptions
} = size.resizeOptions || {};
const landscapeSize = {
...restSize,
width,
height,
resizeOptions: {
...restResizeOptions,
width: lwidth,
height: lheight
}
};
acc.push({
size,
landscape: false,
dark: size.darkResizeOptions ? false : void 0,
resizeOptions: size.resizeOptions,
padding: size.padding ?? 0.3,
png: size.png ?? png
});
acc.push({
size: landscapeSize,
landscape: true,
dark: size.darkResizeOptions ? false : void 0,
resizeOptions: landscapeSize.resizeOptions,
padding: size.padding ?? 0.3,
png: size.png ?? png
});
if (size.darkResizeOptions) {
const {
width: dlheight,
height: dlwidth,
...restDarkResizeOptions
} = size.darkResizeOptions;
const landscapeDarkResizeOptions = { ...restDarkResizeOptions, width: dlwidth, height: dlheight };
const landscapeDarkSize = {
...restSize,
width,
height,
resizeOptions: landscapeDarkResizeOptions,
darkResizeOptions: void 0
};
acc.push({
size,
landscape: false,
dark: true,
resizeOptions: size.darkResizeOptions,
padding: size.padding ?? 0.3,
png: size.png ?? png
});
acc.push({
size: landscapeDarkSize,
landscape: true,
dark: true,
resizeOptions: landscapeDarkResizeOptions,
padding: size.padding ?? 0.3,
png: size.png ?? png
});
}
return acc;
}, []);
sizesMap.clear();
const cache = {};
const originalName = imageAssets.originalName;
const imageResolver = (dark) => {
if (!dark || typeof appleSplashScreens.darkImageResolver !== "function")
return Promise.resolve(image);
const cached = cache[originalName];
if (cached)
return cached;
return cache[originalName] = appleSplashScreens.darkImageResolver(originalName).then((darkImage) => Promise.resolve(darkImage ?? image));
};
for (const size of splashScreens) {
const basePath = linkMediaOptions.basePath;
const name = resolveName(size.landscape, size.size, size.dark);
const url = `${basePath}${name}`;
const promise = () => imageResolver(size.dark === true).then((i) => favicon.generateMaskableAsset("png", i, size.size, {
padding: size.padding,
resizeOptions: {
...size.resizeOptions,
background: size.resizeOptions?.background ?? (size.dark ? "black" : "white")
},
outputOptions: size.png
}));
instructions.appleSplashScreen[url] = {
name,
url,
width: size.size.width,
height: size.size.height,
mimeType: "image/png",
link: favicon.createAppleSplashScreenHtmlLink("string", {
size: size.size,
landscape: size.landscape,
addMediaScreen: linkMediaOptions.addMediaScreen,
xhtml: linkMediaOptions.xhtml,
name: resolveName,
basePath,
dark: size.dark,
includeId: imageAssets.htmlLinks.includeId
}),
linkObject: favicon.createAppleSplashScreenHtmlLink("link", {
size: size.size,
landscape: size.landscape,
addMediaScreen: linkMediaOptions.addMediaScreen,
xhtml: linkMediaOptions.xhtml,
name: resolveName,
basePath,
dark: size.dark,
includeId: imageAssets.htmlLinks.includeId
}),
buffer: () => promise().then((m) => m.toBuffer())
};
}
}
function resolveAppleSplashScreens(imageAssets, useAppleSplashScreens) {
let appleSplashScreens;
if (useAppleSplashScreens) {
const {
padding = 0.3,
resizeOptions: useResizeOptions = {},
darkResizeOptions: useDarkResizeOptions,
linkMediaOptions: useLinkMediaOptions = {},
sizes,
name = splash.defaultSplashScreenName,
png: usePng = {},
darkImageResolver
} = useAppleSplashScreens;
const resizeOptions = index.createResizeOptions(false, useResizeOptions);
const darkResizeOptions = useDarkResizeOptions ? index.createResizeOptions(true, useDarkResizeOptions) : void 0;
const png = index.createPngCompressionOptions(usePng);
for (const size of sizes) {
if (typeof size.padding === "undefined")
size.padding = padding;
if (typeof size.png === "undefined")
size.png = png;
if (typeof size.resizeOptions === "undefined")
size.resizeOptions = resizeOptions;
if (typeof size.darkResizeOptions === "undefined" && darkResizeOptions)
size.darkResizeOptions = darkResizeOptions;
}
const {
log = true,
addMediaScreen = true,
basePath = imageAssets.basePath ?? "/",
xhtml = false
} = useLinkMediaOptions;
appleSplashScreens = {
darkImageResolver,
padding,
sizes,
linkMediaOptions: {
log,
addMediaScreen,
basePath,
xhtml
},
name,
png
};
}
return appleSplashScreens;
}
function resolveTransparentIcons(imageAssets, image, assets, htmlPreset, instructions) {
const asset = assets.assets.transparent;
const { sizes, padding, resizeOptions } = asset;
const { basePath, htmlLinks: { xhtml, includeId } } = imageAssets;
for (const size of sizes) {
const name = assets.assetName("transparent", size);
const url = `${basePath}${name}`;
const promise = () => favicon.generateTransparentAsset("png", image, size, {
padding,
resizeOptions,
outputOptions: assets.png
});
instructions.transparent[url] = {
name,
url,
width: size.width,
height: size.height,
mimeType: "image/png",
buffer: () => promise().then((m) => m.toBuffer())
};
}
const favicons = asset.favicons;
if (!favicons)
return;
for (const [size, name] of favicons) {
const url = `${basePath}${name}`;
const promise = () => favicon.generateTransparentAsset("png", image, size, {
padding,
resizeOptions,
outputOptions: assets.png
}).then((m) => m.toBuffer()).then((b) => favicon.generateFavicon("png", b));
const resolvedSize = index.toResolvedSize(size);
instructions.favicon[url] = {
name,
url,
width: resolvedSize.width,
height: resolvedSize.height,
mimeType: "image/x-icon",
link: favicon.createFaviconHtmlLink("string", htmlPreset, {
name,
size,
basePath,
xhtml,
includeId
}),
linkObject: favicon.createFaviconHtmlLink("link", htmlPreset, {
name,
size,
basePath,
xhtml,
includeId
}),
buffer: () => promise()
};
}
}
function resolveMaskableIcons(type, imageAssets, image, assets, htmlPreset, instructions) {
const asset = assets.assets[type];
const { sizes, padding, resizeOptions } = asset;
const { basePath, htmlLinks: { xhtml, includeId } } = imageAssets;
for (const size of sizes) {
const name = assets.assetName(type, size);
const url = `${basePath}${name}`;
const promise = () => favicon.generateMaskableAsset("png", image, size, {
padding,
resizeOptions,
outputOptions: assets.png
});
const buffer = () => promise().then((m) => m.toBuffer());
if (type === "apple") {
instructions.apple[url] = {
name,
url,
width: size.width,
height: size.height,
mimeType: "image/png",
link: favicon.createAppleTouchIconHtmlLink("string", {
name,
size,
basePath,
xhtml,
includeId
}),
linkObject: favicon.createAppleTouchIconHtmlLink("link", {
name,
size,
basePath,
xhtml,
includeId
}),
buffer
};
} else {
instructions.maskable[url] = {
name,
url,
width: size.width,
height: size.height,
mimeType: "image/png",
buffer
};
}
}
const favicons = asset.favicons;
if (!favicons)
return;
for (const [size, name] of favicons) {
const url = `${basePath}${name}`;
const resolvedSize = index.toResolvedSize(size);
instructions.favicon[url] = {
name,
url,
width: resolvedSize.width,
height: resolvedSize.height,
mimeType: "image/x-icon",
link: favicon.createFaviconHtmlLink("string", htmlPreset, {
name,
size,
basePath,
xhtml,
includeId
}),
linkObject: favicon.createFaviconHtmlLink("link", htmlPreset, {
name,
size,
basePath,
xhtml,
includeId
}),
buffer: () => favicon.generateMaskableAsset("png", image, size, {
padding,
resizeOptions,
outputOptions: assets.png
}).then((m) => m.toBuffer()).then((b) => favicon.generateFavicon("png", b))
};
}
}
async function resolveInstructions(imageAssets) {
const {
imageResolver,
imageName,
originalName,
preset = "minimal",
faviconPreset
} = imageAssets;
const [usePreset, htmlPreset] = await resolvePreset(preset, faviconPreset);
const {
assetName = index.defaultAssetName,
png = index.defaultPngCompressionOptions,
appleSplashScreens: useAppleSplashScreens
} = usePreset;
const assets = {
assets: {
transparent: index.toResolvedAsset("transparent", usePreset.transparent),
maskable: index.toResolvedAsset("maskable", usePreset.maskable),
apple: index.toResolvedAsset("apple", usePreset.apple)
},
png,
assetName
};
const instructions = {
image: imageName,
originalName,
favicon: {},
transparent: {},
maskable: {},
apple: {},
appleSplashScreen: {}
};
const image = await imageResolver();
resolveTransparentIcons(imageAssets, image, assets, htmlPreset, instructions);
resolveMaskableIcons("maskable", imageAssets, image, assets, htmlPreset, instructions);
resolveMaskableIcons("apple", imageAssets, image, assets, htmlPreset, instructions);
if (imageName.endsWith(".svg")) {
const name = imageAssets.resolveSvgName(imageName);
const url = `${imageAssets.basePath}${name}`;
instructions.favicon[url] = {
name,
url,
width: 0,
height: 0,
mimeType: "image/svg+xml",
link: favicon.createFaviconHtmlLink("string", htmlPreset, {
name,
basePath: imageAssets.basePath,
xhtml: imageAssets.htmlLinks.xhtml,
includeId: imageAssets.htmlLinks.includeId
}),
linkObject: favicon.createFaviconHtmlLink("link", htmlPreset, {
name,
basePath: imageAssets.basePath,
xhtml: imageAssets.htmlLinks.xhtml,
includeId: imageAssets.htmlLinks.includeId
}),
buffer: () => Promise.resolve(image)
};
}
resolveAppleSplashScreensInstructions(image, imageAssets, instructions, useAppleSplashScreens);
return instructions;
}
async function resolvePreset(preset, faviconPreset) {
const htmlLinkPreset = faviconPreset ?? "default";
if (typeof preset === "object")
return [preset, htmlLinkPreset];
switch (preset) {
case "minimal":
return [await import('../presets/minimal.cjs').then((m) => m.minimalPreset), htmlLinkPreset];
case "minimal-2023":
return [await import('../presets/minimal-2023.cjs').then((m) => m.minimal2023Preset), faviconPreset ?? "2023"];
default:
throw new Error(`Preset ${preset} not yet implemented`);
}
}
exports.resolveInstructions = resolveInstructions;