@openscript/unplugin-favicons
Version:
Generate favicons for your project with caching for blazing fast rebuilds.
107 lines • 4.38 kB
JavaScript
import { basename, join, resolve } from "node:path";
import { colorize } from "consola/utils";
import { favicons } from "favicons";
import { DEFAULT_FAVICONS_OPTIONS, DEFAULT_ICON_OPTIONS } from "../const.js";
import consola from "../utils/consola.js";
import generateCacheKeyForJob from "./generate-cache-key-for-job.js";
import getCacheResponse from "./get-cache-response.js";
import putCacheResponse from "./put-cache-response.js";
/**
* @private
*
* Provided a `FaviconsPluginOptions` object, returns an array of
* [source, FaviconOptions] tuples, each representing a distinct `favicons` job
* to run.
*/
const mapOptionsToJobs = (options) => {
const { icons: notUsed, ...others } = (options.favicons ?? {});
// eslint-disable-next-line unicorn/no-array-reduce
return Object.entries(options.icons).reduce((jobs, [iconType, sourceAndIconOptions]) => {
const { source, ...iconOptions } = sourceAndIconOptions;
// Create a new 'icons' configuration key that contains only the current
// icon type.
const icons = { ...DEFAULT_ICON_OPTIONS, [iconType]: iconOptions };
const config = { ...others, icons };
const job = { config, iconType, source };
return [...jobs, job];
}, []);
};
/**
* @private
*
* Provided an array of `FaviconResponse` objects from each job, merges them
* into a single `FaviconResponse` object.
*/
const reduceResponses = (responses) =>
// eslint-disable-next-line unicorn/no-array-reduce
responses.reduce((response, currentResponse) => {
currentResponse.files.forEach((file) => {
response.files.push(file);
});
currentResponse.images.forEach((image) => {
response.images.push(image);
});
currentResponse.html.forEach((html) => {
response.html.push(html);
});
return response;
}, {
files: [],
html: [],
images: [],
});
/**
* Provided a FaviconsPluginOptions object, generates assets and returns a
* FaviconResponse object.
*/
const generateFavicons = async (options) => {
// Because favicons only allows a single source asset per invocation, map
// incoming configuration into a list of jobs we will need to run.
const jobConfigs = [];
if (options.icons && !options.logo) {
jobConfigs.push(...mapOptionsToJobs(options));
}
else {
const config = {
...DEFAULT_FAVICONS_OPTIONS,
...options.favicons,
icons: {
...DEFAULT_FAVICONS_OPTIONS.icons,
...options.favicons?.icons,
},
};
// eslint-disable-next-line no-param-reassign
options.logo = resolve(options.logo ?? join("assets", "logo.png"));
jobConfigs.push({ config, iconType: "logo", source: options.logo });
}
const jobs = jobConfigs.map(async (jobConfig) => {
// N.B. Favicons modifies the `config` object in-place, so we need to
// generate a key now to ensure that our cache get/put functions use the
// same one.
const cacheKeyForJob = await generateCacheKeyForJob(jobConfig);
const { config, iconType, source } = jobConfig;
const sourceFileName = basename(source);
if (options.cache) {
// If we have a cached response for this source asset, return it.
const cachedResponse = await getCacheResponse(cacheKeyForJob, iconType);
if (cachedResponse) {
consola.verbose(`Using cached ${colorize("bold", iconType)} assets from source ${colorize("green", sourceFileName)}.`);
return cachedResponse;
}
}
const response = await favicons(source, config);
if (options.cache) {
// Return the response immediately without awaiting the cache write.
// eslint-disable-next-line no-void,promise/always-return
void putCacheResponse(cacheKeyForJob, response, iconType).then(() => {
consola.verbose(`Cached rendered ${colorize("bold", iconType)} assets from source ${colorize("green", sourceFileName)}.`);
});
}
return response;
});
// Merge each response into a single `FaviconsResponse`.
// eslint-disable-next-line compat/compat
return reduceResponses(await Promise.all(jobs));
};
export default generateFavicons;
//# sourceMappingURL=generate-favicons.js.map