diff --git a/packages/core/src/api/icons.ts b/packages/core/src/api/icons.ts index 36c94ac..9bffa63 100644 --- a/packages/core/src/api/icons.ts +++ b/packages/core/src/api/icons.ts @@ -8,9 +8,9 @@ import type { SortedIcons } from '../icon/sort'; import { sortIcons } from '../icon/sort'; import { storeCallback, updateCallbacks } from './callbacks'; import { getAPIModule } from './modules'; -import { getStorage, addIconSet, getIconFromStorage } from '../storage/storage'; +import { getStorage, addIconSet } from '../storage/storage'; import { listToIcons } from '../icon/list'; -import { allowSimpleNames } from '../storage/functions'; +import { allowSimpleNames, getIconData } from '../storage/functions'; import { sendAPIQuery } from './query'; import { cache } from '../cache'; @@ -364,10 +364,11 @@ export const loadIcon = ( const iconObj = typeof icon === 'string' ? stringToIcon(icon) : icon; loadIcons([iconObj || icon], (loaded) => { if (loaded.length && iconObj) { - const storage = getStorage(iconObj.provider, iconObj.prefix); - const data = getIconFromStorage(storage, iconObj.name); + const data = getIconData(iconObj); if (data) { - fulfill(data); + fulfill({ + ...data, + }); return; } } diff --git a/packages/core/src/api/modules/fetch.ts b/packages/core/src/api/modules/fetch.ts index 7219cc7..616a3b0 100644 --- a/packages/core/src/api/modules/fetch.ts +++ b/packages/core/src/api/modules/fetch.ts @@ -27,7 +27,7 @@ const detectFetch = (): FetchType | null => { if (typeof callback === 'function') { return callback; } - } catch (err) { + } catch { // } diff --git a/packages/core/src/browser-storage/index.ts b/packages/core/src/browser-storage/index.ts index dc5894f..b0494b6 100644 --- a/packages/core/src/browser-storage/index.ts +++ b/packages/core/src/browser-storage/index.ts @@ -89,7 +89,7 @@ function getGlobal(key: keyof StorageConfig): typeof localStorage | null { ) { return _window[attr]; } - } catch (err) { + } catch { // } @@ -110,9 +110,10 @@ function setCount( storage.setItem(countKey, value.toString()); count[key] = value; return true; - } catch (err) { - return false; + } catch { + // } + return false; } /** @@ -141,7 +142,7 @@ function initCache( ): void { try { storage.setItem(versionKey, cacheVersion); - } catch (err) { + } catch { // } setCount(storage, key, 0); @@ -158,7 +159,7 @@ function destroyCache(storage: typeof localStorage): void { for (let i = 0; i < total; i++) { storage.removeItem(cachePrefix + i.toString()); } - } catch (err) { + } catch { // } } @@ -213,7 +214,7 @@ export const loadCache: LoadIconsCache = (): void => { const storage = getStorage(provider, prefix); valid = addIconSet(storage, data.data).length > 0; } - } catch (err) { + } catch { valid = false; } @@ -253,7 +254,7 @@ export const loadCache: LoadIconsCache = (): void => { // Update total setCount(func, key, total); - } catch (err) { + } catch { // } } @@ -302,7 +303,7 @@ export const storeCache: CacheIcons = ( data, }; func.setItem(cachePrefix + index.toString(), JSON.stringify(item)); - } catch (err) { + } catch { return false; } return true; diff --git a/packages/core/src/storage/functions.ts b/packages/core/src/storage/functions.ts index a05c9f9..e7471b8 100644 --- a/packages/core/src/storage/functions.ts +++ b/packages/core/src/storage/functions.ts @@ -4,12 +4,7 @@ import { parseIconSet } from '@iconify/utils/lib/icon-set/parse'; import { quicklyValidateIconSet } from '@iconify/utils/lib/icon-set/validate-basic'; import type { IconifyIconName } from '@iconify/utils/lib/icon/name'; import { stringToIcon, validateIcon } from '@iconify/utils/lib/icon/name'; -import { - getStorage, - getIconFromStorage, - addIconToStorage, - addIconSet, -} from './storage'; +import { getStorage, addIconToStorage, addIconSet } from './storage'; /** * Interface for exported storage functions @@ -65,15 +60,26 @@ export function allowSimpleNames(allow?: boolean): boolean { /** * Get icon data + * + * Returns: + * - Required on success, object directly from storage so don't modify it + * - null if icon is marked as missing (returned in `not_found` property from API, so don't bother sending API requests) + * - undefined if icon is missing */ export function getIconData( name: string | IconifyIconName -): FullIconifyIcon | null { +): FullIconifyIcon | null | undefined { const icon = typeof name === 'string' ? stringToIcon(name, true, simpleNames) : name; - return icon - ? getIconFromStorage(getStorage(icon.provider, icon.prefix), icon.name) - : null; + + if (!icon) { + return; + } + const storage = getStorage(icon.provider, icon.prefix); + const iconName = icon.name; + return ( + storage.icons[iconName] || (storage.missing[iconName] ? null : void 0) + ); } /** @@ -143,9 +149,8 @@ export function addCollection(data: IconifyJSON, provider?: string): boolean { * Check if icon exists */ export function iconExists(name: string): boolean { - return getIconData(name) !== null; + return !!getIconData(name); } - /** * Get icon */ diff --git a/packages/core/src/storage/storage.ts b/packages/core/src/storage/storage.ts index 840d64a..d1ee649 100644 --- a/packages/core/src/storage/storage.ts +++ b/packages/core/src/storage/storage.ts @@ -47,7 +47,7 @@ try { if (w && w._iconifyStorage.version === storageVersion) { storage = w._iconifyStorage.storage; } -} catch (err) { +} catch { // } @@ -63,7 +63,7 @@ export function shareStorage(): void { storage, }; } - } catch (err) { + } catch { // } } @@ -128,7 +128,7 @@ export function addIconToStorage( storage.icons[name] = Object.freeze(fullIcon(icon)); return true; } - } catch (err) { + } catch { // Do nothing } return false; @@ -141,17 +141,6 @@ export function iconExists(storage: IconStorage, name: string): boolean { return storage.icons[name] !== void 0; } -/** - * Get icon data - */ -export function getIconFromStorage( - storage: IconStorage, - name: string -): Readonly | null { - const value = storage.icons[name]; - return value === void 0 ? null : value; -} - /** * List available icons */ diff --git a/packages/core/tests/storage/storage-functions-test.ts b/packages/core/tests/storage/storage-functions-test.ts index 88db3df..fcffe71 100644 --- a/packages/core/tests/storage/storage-functions-test.ts +++ b/packages/core/tests/storage/storage-functions-test.ts @@ -1,11 +1,12 @@ import { fullIcon } from '@iconify/utils/lib/icon'; -import { listIcons } from '../../lib/storage/storage'; +import { addIconSet, getStorage, listIcons } from '../../lib/storage/storage'; import { iconExists, getIcon, addIcon, addCollection, allowSimpleNames, + getIconData, } from '../../lib/storage/functions'; describe('Testing IconifyStorageFunctions', () => { @@ -15,6 +16,13 @@ describe('Testing IconifyStorageFunctions', () => { return 'storage-test-' + (count++).toString(); } + beforeEach(() => { + allowSimpleNames(false); + }); + afterAll(() => { + allowSimpleNames(false); + }); + it('Storage functions', () => { const provider = nextProvider(); const testName = `@${provider}:foo:bar`; @@ -22,6 +30,7 @@ describe('Testing IconifyStorageFunctions', () => { // Empty expect(iconExists(testName)).toBe(false); expect(getIcon(testName)).toBeNull(); + expect(getIconData(testName)).toBeUndefined(); expect(listIcons(provider)).toEqual([]); // Add and test one icon @@ -32,17 +41,52 @@ describe('Testing IconifyStorageFunctions', () => { ).toBe(true); expect(iconExists(testName)).toBe(true); expect(listIcons(provider)).toEqual([testName]); + + let expected = fullIcon({ + body: '', + }); + expect(getIconData(testName)).toEqual(expected); + expect(getIcon(testName)).toEqual(expected); + + // Add icon set + const prefix = 'prefix' + (count++).toString(); + const storage = getStorage('', prefix); + addIconSet(storage, { + prefix, + icons: { + home: { + body: '', + }, + }, + not_found: ['missing'], + }); + + // Test 'home' icon + expect(iconExists(`${prefix}:home`)).toBe(true); + expected = fullIcon({ + body: '', + }); + expect(getIconData(`${prefix}:home`)).toEqual(expected); + expect(getIcon(`${prefix}:home`)).toEqual(expected); + + // Test 'missing' icon + expect(iconExists(`${prefix}:missing`)).toBe(false); + expect(getIconData(`${prefix}:missing`)).toBeNull(); + expect(getIcon(`${prefix}:missing`)).toBeNull(); + + // Test 'invalid' icon + expect(iconExists(`${prefix}:invalid`)).toBe(false); + expect(getIconData(`${prefix}:invalid`)).toBeUndefined(); + expect(getIcon(`${prefix}:invalid`)).toBeNull(); }); it('Invalid icon name', () => { const testName = 'storage' + (count++).toString(); - // Reset module - allowSimpleNames(false); - // Empty expect(iconExists(testName)).toBe(false); expect(getIcon(testName)).toBeNull(); + expect(getIconData(testName)).toBeUndefined(); // Add and test one icon (icon should not be added) expect( @@ -54,9 +98,6 @@ describe('Testing IconifyStorageFunctions', () => { }); it('Invalid icon set', () => { - // Reset module - allowSimpleNames(false); - // Icon set without prefix (should work only when simple names are allowed, tested later in this file) expect( addCollection({ @@ -87,9 +128,6 @@ describe('Testing IconifyStorageFunctions', () => { }) ).toBe(true); expect(iconExists(testName)).toBe(true); - - // Reset config after test - allowSimpleNames(false); }); it('Collection with simple icon name', () => { @@ -104,6 +142,7 @@ describe('Testing IconifyStorageFunctions', () => { const name1 = 'test' + n.toString(); const prefix2 = `prefixed${n}`; const name2 = `icon${n2}`; + const missing = `missing${n}`; expect( addCollection({ prefix: '', @@ -115,38 +154,41 @@ describe('Testing IconifyStorageFunctions', () => { body: '', }, }, + not_found: [missing], }) ).toBe(true); // Test 'test' name = name1; expect(iconExists(name)).toBe(true); - expect(getIcon(name)).toEqual( - fullIcon({ - body: '', - }) - ); + let expected = fullIcon({ + body: '', + }); + expect(getIcon(name)).toEqual(expected); + expect(getIconData(name)).toEqual(expected); // Test prefixed icon, using ':' separator name = `${prefix2}:${name2}`; expect(listIcons('', prefix2)).toEqual([name]); expect(iconExists(name)).toBe(true); - expect(getIcon(name)).toEqual( - fullIcon({ - body: '', - }) - ); + expected = fullIcon({ + body: '', + }); + expect(getIcon(name)).toEqual(expected); + expect(getIconData(name)).toEqual(expected); // Test prefixed icon, using '-' separator name = `${prefix2}-${name2}`; expect(iconExists(name)).toBe(true); - expect(getIcon(name)).toEqual( - fullIcon({ - body: '', - }) - ); + expected = fullIcon({ + body: '', + }); + expect(getIcon(name)).toEqual(expected); + expect(getIconData(name)).toEqual(expected); - // Reset config after test - allowSimpleNames(false); + // Test missing icon: should not exist because without provider missing icon cannot be added + expect(iconExists(missing)).toBe(false); + expect(getIcon(missing)).toBeNull(); + expect(getIconData(missing)).toBeUndefined(); }); }); diff --git a/packages/core/tests/storage/storage-test.ts b/packages/core/tests/storage/storage-test.ts index 52aae34..0681318 100644 --- a/packages/core/tests/storage/storage-test.ts +++ b/packages/core/tests/storage/storage-test.ts @@ -3,7 +3,6 @@ import { newStorage, addIconToStorage, iconExists, - getIconFromStorage, addIconSet, getStorage, listIcons, @@ -65,7 +64,7 @@ describe('Testing storage', () => { vFlip: false, rotate: 0, }; - const icon = getIconFromStorage(storage, 'test'); + const icon = storage.icons['test']; expect(icon).toEqual(expected); // Test icon mutation @@ -88,10 +87,9 @@ describe('Testing storage', () => { vFlip: false, rotate: 1, }; - expect(getIconFromStorage(storage, 'constructor')).toEqual(expected); + expect(storage.icons['constructor']).toEqual(expected); - expect(getIconFromStorage(storage, 'invalid')).toBeNull(); - expect(getIconFromStorage(storage, 'missing')).toBeNull(); + expect(storage.icons['invalid']).toBeUndefined(); }); it('Adding simple icon set', () => { @@ -134,7 +132,7 @@ describe('Testing storage', () => { vFlip: false, rotate: 0, }; - expect(getIconFromStorage(storage, 'icon1')).toEqual(expected); + expect(storage.icons['icon1']).toEqual(expected); expected = { body: '', width: 24, @@ -145,9 +143,7 @@ describe('Testing storage', () => { vFlip: false, rotate: 0, }; - expect(getIconFromStorage(storage, 'icon2')).toEqual(expected); - expect(getIconFromStorage(storage, 'invalid')).toBeNull(); - expect(getIconFromStorage(storage, 'missing')).toBeNull(); + expect(storage.icons['icon2']).toEqual(expected); }); it('Icon set with aliases that use transformations', () => { @@ -190,9 +186,7 @@ describe('Testing storage', () => { vFlip: false, rotate: 0, }; - expect(getIconFromStorage(storage, '16-chevron-left')).toEqual( - expected - ); + expect(storage.icons['16-chevron-left']).toEqual(expected); // Test alias expected = { @@ -205,9 +199,7 @@ describe('Testing storage', () => { vFlip: false, rotate: 0, }; - expect(getIconFromStorage(storage, '16-chevron-right')).toEqual( - expected - ); + expect(storage.icons['16-chevron-right']).toEqual(expected); }); it('List icons in a global storage', () => { diff --git a/packages/iconify/src/scanner/index.ts b/packages/iconify/src/scanner/index.ts index c58e4e6..9139833 100644 --- a/packages/iconify/src/scanner/index.ts +++ b/packages/iconify/src/scanner/index.ts @@ -1,7 +1,4 @@ -import { - getIconFromStorage, - getStorage, -} from '@iconify/core/lib/storage/storage'; +import { getStorage } from '@iconify/core/lib/storage/storage'; import { isPending, loadIcons } from '@iconify/core/lib/api/icons'; import { findRootNode, listRootNodes } from '../observer/root'; import type { ObservedNode } from '../observer/types'; @@ -65,10 +62,11 @@ export function scanDOM(rootNode?: ObservedNode, addTempNode = false): void { const { provider, prefix, name } = icon; const storage = getStorage(provider, prefix); - if (storage.icons[name]) { + const storedIcon = storage.icons[name]; + if (storedIcon) { return { status: 'loaded', - icon: getIconFromStorage(storage, name), + icon: storedIcon, }; } diff --git a/packages/iconify/tests/helpers.ts b/packages/iconify/tests/helpers.ts index ddca4fc..1013dd4 100644 --- a/packages/iconify/tests/helpers.ts +++ b/packages/iconify/tests/helpers.ts @@ -4,7 +4,7 @@ import { addAPIProvider } from '@iconify/core/lib/api/config'; import { setAPIModule } from '@iconify/core/lib/api/modules'; import { removeRootNode, listRootNodes } from '../src/observer/root'; import { onReady } from '../src/helpers/ready'; -import { stopObserver, stopObserving } from '../src/observer'; +import { stopObserver } from '../src/observer'; /** * Generate next prefix diff --git a/packages/react/src/iconify.ts b/packages/react/src/iconify.ts index aa023db..323958a 100644 --- a/packages/react/src/iconify.ts +++ b/packages/react/src/iconify.ts @@ -316,20 +316,23 @@ class IconComponent extends React.Component< // Load icon const data = getIconData(iconName); - if (data === null) { - // Icon needs to be loaded + if (!data) { + // Icon data is not available if (!this._loading || this._loading.name !== icon) { // New icon to load this._abortLoading(); this._icon = ''; this._setData(null); - this._loading = { - name: icon, - abort: loadIcons( - [iconName], - this._checkIcon.bind(this, false) - ), - }; + if (data !== null) { + // Icon was not loaded + this._loading = { + name: icon, + abort: loadIcons( + [iconName], + this._checkIcon.bind(this, false) + ), + }; + } } return; } diff --git a/packages/svelte/src/functions.ts b/packages/svelte/src/functions.ts index 5ad6f6b..a02c6c9 100644 --- a/packages/svelte/src/functions.ts +++ b/packages/svelte/src/functions.ts @@ -293,8 +293,8 @@ export function checkIconState( // Load icon const data = getIconData(iconName); - if (data === null) { - // Icon needs to be loaded + if (!data) { + // Icon data is not available // Do not load icon until component is mounted if (mounted && (!state.loading || state.loading.name !== icon)) { // New icon to load diff --git a/packages/vue/src/iconify.ts b/packages/vue/src/iconify.ts index c4ef9cf..1b0f535 100644 --- a/packages/vue/src/iconify.ts +++ b/packages/vue/src/iconify.ts @@ -311,18 +311,21 @@ export const Icon = defineComponent({ // Load icon const data = getIconData(iconName); - if (data === null) { - // Icon needs to be loaded + if (!data) { + // Icon data is not available if (!this._loadingIcon || this._loadingIcon.name !== icon) { // New icon to load this.abortLoading(); this._name = ''; - this._loadingIcon = { - name: icon, - abort: loadIcons([iconName], () => { - this.counter++; - }), - }; + if (data !== null) { + // Icon was not loaded + this._loadingIcon = { + name: icon, + abort: loadIcons([iconName], () => { + this.counter++; + }), + }; + } } return null; } diff --git a/packages/vue2/src/iconify.ts b/packages/vue2/src/iconify.ts index 10387ae..598a019 100644 --- a/packages/vue2/src/iconify.ts +++ b/packages/vue2/src/iconify.ts @@ -297,18 +297,21 @@ export const Icon = Vue.extend({ // Load icon const data = getIconData(iconName); - if (data === null) { - // Icon needs to be loaded + if (!data) { + // Icon data is not available if (!this._loadingIcon || this._loadingIcon.name !== icon) { // New icon to load this.abortLoading(); this._name = ''; - this._loadingIcon = { - name: icon, - abort: loadIcons([iconName], () => { - this.$forceUpdate(); - }), - }; + if (data !== null) { + // Icon was not loaded + this._loadingIcon = { + name: icon, + abort: loadIcons([iconName], () => { + this.$forceUpdate(); + }), + }; + } } return null; }