iconify-browser-cache
Version:
```html <script src="https://cdn.jsdelivr.net/npm/iconify-browser-cache@0.4.0/dist/iconify-browser-cache.umd.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/iconify-icon@3.0.0/dist/iconify-icon.min.js"></script> ```
105 lines (101 loc) • 4.04 kB
JavaScript
const defaultConfig = {
ttl: 6 * 30 * 24 * 60 * 60 * 1e3,
// 6 months in milliseconds
prefix: "iconify-browser-cache",
storage: "localStorage"
};
const json = (data, init) => {
const jsonData = JSON.stringify(data);
return new Response(jsonData, init);
};
const localStore = {
getItem: async (key) => {
const value = window.localStorage.getItem(key);
return value ? JSON.parse(value) : null;
},
setItemRaw: async (key, value) => {
window.localStorage.setItem(key, JSON.stringify(value));
},
remove: async (key) => {
window.localStorage.removeItem(key);
},
removeExpired: async (prefix) => {
const now = Date.now();
const keys = Object.keys(window.localStorage);
let collectionsForDelete = [];
const metadataItems = keys.filter((key) => key.startsWith(prefix) && key.endsWith(".metadata"));
metadataItems.map((key) => {
const item = window.localStorage.getItem(key);
if (item) {
const metadata = JSON.parse(item);
if (now >= metadata.expires) {
window.localStorage.removeItem(key);
const collectionKey = key.replace(/\.metadata$/, "");
collectionsForDelete.push(collectionKey);
}
}
});
const collectionKeys = keys.filter((key) => collectionsForDelete.some((collection) => key.startsWith(collection)));
collectionKeys.map((key) => window.localStorage.removeItem(key));
}
};
const iconifyBrowserCache = (config, libType) => {
if (typeof window === "undefined") return;
window.IconifyBrowserCache = {
fetch: window.fetch.bind(window),
config: {
ttl: config.ttl,
prefix: config.prefix,
storage: config.storage
}
};
window.fetch = async (url, init) => {
const originalFetch = window.IconifyBrowserCache.fetch.bind(window.IconifyBrowserCache);
if (url instanceof URL) url = url.toString();
else if (url instanceof Request) url = url.url;
else url = String(url);
if (!url.includes("https://api.iconify.design/")) {
return originalFetch(url, init);
}
const now = Date.now();
const prefix = window.IconifyBrowserCache.config.prefix;
const ttl = window.IconifyBrowserCache.config.ttl;
const storage = (await import('../chunks/composables.mjs')).useStorage(window.IconifyBrowserCache.config);
const collection = url.match(/\/([^\/?]+)\.json/)?.[1];
const icons = new URL(url).searchParams?.get("icons")?.split(",");
const cachedIcons = {};
const collectionKey = `${prefix}:${collection}`;
if (!collection || !icons?.length) {
return originalFetch(url, init);
}
await storage.removeExpired(prefix);
for (const icon of icons) {
const key = `${collectionKey}:${icon}`;
const body = await storage.getItem(key);
if (body) cachedIcons[icon] = { body };
}
const collectionData = await storage.getItem(`${collectionKey}.metadata`);
const { width, height, expires } = collectionData ? collectionData : {};
if (Object.keys(cachedIcons).length === icons.length && expires && now < (expires || 0)) {
return json({ icons: cachedIcons, prefix: collection, width, height });
}
const response = await originalFetch(url, init);
const data = await response.json();
for (const [icon, { body }] of Object.entries(data.icons)) {
await Promise.all([
storage.setItemRaw(`${collectionKey}:${icon}`, body),
storage.setItemRaw(`${collectionKey}.metadata`, { collection, width: data.width, height: data.height, expires: now + ttl })
]);
}
return json(data);
};
};
const { ttl, prefix, storage } = defaultConfig;
const setupIconifyCache = (options) => {
if (typeof window === "undefined") return;
window.IconifyBrowserCache.config.ttl = options?.ttl || ttl;
window.IconifyBrowserCache.config.prefix = options?.prefix || prefix;
window.IconifyBrowserCache.config.storage = options?.storage || storage;
};
iconifyBrowserCache({ ttl, prefix, storage });
export { localStore as l, setupIconifyCache as s };