2
0
mirror of https://github.com/iconify/iconify.git synced 2024-09-29 05:29:09 +00:00

Reuse storage for API data, clean up build scripts

This commit is contained in:
Vjacheslav Trushkin 2022-06-29 09:45:30 +03:00
parent 9f71691fd2
commit c1c849a61c
16 changed files with 207 additions and 478 deletions

View File

@ -1,16 +1,12 @@
/* eslint-disable */
const fs = require('fs');
const path = require('path');
const child_process = require('child_process');
const coreDir = path.dirname(require.resolve('@iconify/core/package.json'));
// List of commands to run
const commands = [];
// Parse command line
const compile = {
core: false,
lib: true,
rollup: true,
api: true,
@ -58,22 +54,9 @@ const fileExists = (file) => {
return true;
};
if (compile.lib && !fileExists(coreDir + '/lib/cache.mjs')) {
compile.core = true;
}
// Compile core before compiling this package
if (compile.core) {
commands.push({
cmd: 'npm',
args: ['run', 'build'],
cwd: coreDir,
});
}
// Compile other packages
// Compile packages
Object.keys(compile).forEach((key) => {
if (key !== 'core' && compile[key]) {
if (compile[key]) {
commands.push({
cmd: 'npm',
args: ['run', 'build:' + key],

View File

@ -1,17 +1,12 @@
/* eslint-disable */
const fs = require('fs');
const path = require('path');
const child_process = require('child_process');
const coreDir = path.dirname(require.resolve('@iconify/core/package.json'));
// List of commands to run
const commands = [];
// Build process
const compile = {
// Compile @iconify/core
core: false,
// Compile TypeScript src -> lib
lib: true,
// Fix types for icon components
@ -72,22 +67,9 @@ if (compile.api && !fileExists('./lib/icon.d.ts')) {
compile.lib = true;
}
if (compile.lib && !fileExists(coreDir + '/lib/cache.mjs')) {
compile.core = true;
}
// Compile core before compiling this package
if (compile.core) {
commands.push({
cmd: 'npm',
args: ['run', 'build'],
cwd: coreDir,
});
}
// Compile other packages
// Compile packages
Object.keys(compile).forEach((key) => {
if (key !== 'core' && compile[key]) {
if (compile[key]) {
commands.push({
cmd: 'npm',
args: ['run', 'build:' + key],

View File

@ -1,9 +1,6 @@
const fs = require('fs');
const path = require('path');
const child_process = require('child_process');
const coreDir = path.dirname(require.resolve('@iconify/core/package.json'));
// List of commands to run
const commands = [];
@ -13,7 +10,6 @@ const extractor = (name) =>
// Parse command line
const compile = {
core: false,
tsc: true,
bundles: true,
api: true,
@ -61,28 +57,12 @@ const fileExists = (file) => {
return true;
};
if (compile.dist && !fileExists(coreDir + '/lib/modules.mjs')) {
compile.core = true;
}
// Compile core before compiling this package
if (compile.core) {
commands.push({
cmd: 'npm',
args: ['run', 'build'],
cwd: coreDir,
});
}
// Compile other packages
// Compile packages
Object.keys(compile).forEach((key) => {
if (!compile[key]) {
return;
}
switch (key) {
case 'core':
break;
case 'api':
apiFiles().forEach((name) => {
const cmd = extractor(name).split(' ');

View File

@ -1,16 +1,12 @@
/* eslint-disable */
const fs = require('fs');
const path = require('path');
const child_process = require('child_process');
const coreDir = path.dirname(require.resolve('@iconify/core/package.json'));
// List of commands to run
const commands = [];
// Parse command line
const compile = {
core: false,
lib: true,
dist: true,
api: true,
@ -65,27 +61,14 @@ if (compile.api && !fileExists('./lib/iconify.d.ts')) {
compile.lib = true;
}
if (compile.lib && !fileExists(coreDir + '/lib/cache.mjs')) {
compile.core = true;
}
// Compile core before compiling this package
if (compile.core) {
commands.push({
cmd: 'npm',
args: ['run', 'build'],
cwd: coreDir,
});
}
// Add api2
if (compile.api) {
compile.api2 = true;
}
// Compile other packages
// Compile packages
Object.keys(compile).forEach((key) => {
if (key !== 'core' && compile[key]) {
if (compile[key]) {
commands.push({
cmd: 'npm',
args: ['run', 'build:' + key],

View File

@ -1,16 +1,12 @@
/* eslint-disable */
const fs = require('fs');
const path = require('path');
const child_process = require('child_process');
const coreDir = path.dirname(require.resolve('@iconify/core/package.json'));
// List of commands to run
const commands = [];
// Parse command line
const compile = {
core: false,
lib: true,
dist: true,
api: true,
@ -65,22 +61,9 @@ if (compile.api && !fileExists('./lib/IconifyIcon.d.ts')) {
compile.lib = true;
}
if (compile.lib && !fileExists(coreDir + '/lib/cache.mjs')) {
compile.core = true;
}
// Compile core before compiling this package
if (compile.core) {
commands.push({
cmd: 'npm',
args: ['run', 'build'],
cwd: coreDir,
});
}
// Compile other packages
// Compile packages
Object.keys(compile).forEach((key) => {
if (key !== 'core' && compile[key]) {
if (compile[key]) {
commands.push({
cmd: 'npm',
args: ['run', 'build:' + key],

View File

@ -1,21 +1,17 @@
/* eslint-disable */
const fs = require('fs');
const path = require('path');
const child_process = require('child_process');
const coreDir = path.dirname(require.resolve('@iconify/core/package.json'));
// List of commands to run
const commands = [];
// Parse command line
const compile = {
core: false,
lib: true,
dist: true,
api: true,
};
process.argv.slice(2).forEach(cmd => {
process.argv.slice(2).forEach((cmd) => {
if (cmd.slice(0, 2) !== '--') {
return;
}
@ -39,7 +35,7 @@ process.argv.slice(2).forEach(cmd => {
case 'only':
// disable other modules
Object.keys(compile).forEach(key2 => {
Object.keys(compile).forEach((key2) => {
compile[key2] = key2 === key;
});
break;
@ -48,7 +44,7 @@ process.argv.slice(2).forEach(cmd => {
});
// Check if required modules in same monorepo are available
const fileExists = file => {
const fileExists = (file) => {
try {
fs.statSync(file);
} catch (e) {
@ -65,22 +61,9 @@ if (compile.api && !fileExists('./lib/IconifyIcon.d.ts')) {
compile.lib = true;
}
if (compile.lib && !fileExists(coreDir + '/lib/cache.mjs')) {
compile.core = true;
}
// Compile core before compiling this package
if (compile.core) {
commands.push({
cmd: 'npm',
args: ['run', 'build'],
cwd: coreDir,
});
}
// Compile other packages
Object.keys(compile).forEach(key => {
if (key !== 'core' && compile[key]) {
// Compile packages
Object.keys(compile).forEach((key) => {
if (compile[key]) {
commands.push({
cmd: 'npm',
args: ['run', 'build:' + key],

View File

@ -1,16 +1,12 @@
/* eslint-disable */
const fs = require('fs');
const path = require('path');
const child_process = require('child_process');
const coreDir = path.dirname(require.resolve('@iconify/core/package.json'));
// List of commands to run
const commands = [];
// Parse command line
const compile = {
core: false,
lib: true,
dist: true,
api: true,
@ -65,22 +61,9 @@ if (compile.api && !fileExists('./lib/index.d.ts')) {
compile.lib = true;
}
if (compile.lib && !fileExists(coreDir + '/lib/cache.mjs')) {
compile.core = true;
}
// Compile core before compiling this package
if (compile.core) {
commands.push({
cmd: 'npm',
args: ['run', 'build'],
cwd: coreDir,
});
}
// Compile other packages
// Compile packages
Object.keys(compile).forEach((key) => {
if (key !== 'core' && compile[key]) {
if (compile[key]) {
commands.push({
cmd: 'npm',
args: ['run', 'build:' + key],

View File

@ -57,6 +57,10 @@
"require": "./lib/api/query.cjs",
"import": "./lib/api/query.mjs"
},
"./lib/api/types": {
"require": "./lib/api/types.cjs",
"import": "./lib/api/types.mjs"
},
"./lib/browser-storage/config": {
"require": "./lib/browser-storage/config.cjs",
"import": "./lib/browser-storage/config.mjs"

View File

@ -2,59 +2,17 @@ import type {
IconifyIconLoaderCallback,
IconifyIconLoaderAbort,
} from './icons';
import { getStorage } from '../storage/storage';
import type { SortedIcons } from '../icon/sort';
import type { IconifyIconSource } from '@iconify/utils/lib/icon/name';
/**
* Storage for callbacks
*/
interface CallbackItem {
// id
id: number;
// Icons
icons: SortedIcons;
// Callback to call on any update
callback: IconifyIconLoaderCallback;
// Callback to call to remove item from queue
abort: IconifyIconLoaderAbort;
}
type PrefixCallbackItems = CallbackItem[];
type ProviderCallbackItems = Record<string, PrefixCallbackItems>;
// Records sorted by provider and prefix
// This export is only for unit testing, should not be used
export const callbacks = Object.create(null) as Record<
string,
ProviderCallbackItems
>;
// List of provider/prefix combinations that need to be updated
type ProviderPendingUpdates = Record<string, boolean>;
const pendingUpdates = Object.create(null) as Record<
string,
ProviderPendingUpdates
>;
import type { APICallbackItem, IconStorageWithIcons } from './types';
/**
* Remove callback
*/
function removeCallback(sources: IconifyIconSource[], id: number): void {
sources.forEach((source) => {
const provider = source.provider;
if (callbacks[provider] === void 0) {
return;
}
const providerCallbacks = callbacks[provider];
const prefix = source.prefix;
const items = providerCallbacks[prefix];
function removeCallback(storages: IconStorageWithIcons[], id: number): void {
storages.forEach((storage) => {
const items = storage.loaderCallbacks;
if (items) {
providerCallbacks[prefix] = items.filter((row) => row.id !== id);
storage.loaderCallbacks = items.filter((row) => row.id !== id);
}
});
}
@ -62,37 +20,26 @@ function removeCallback(sources: IconifyIconSource[], id: number): void {
/**
* Update all callbacks for provider and prefix
*/
export function updateCallbacks(provider: string, prefix: string): void {
if (pendingUpdates[provider] === void 0) {
pendingUpdates[provider] = Object.create(
null
) as ProviderPendingUpdates;
}
const providerPendingUpdates = pendingUpdates[provider];
if (!providerPendingUpdates[prefix]) {
providerPendingUpdates[prefix] = true;
export function updateCallbacks(storage: IconStorageWithIcons): void {
if (!storage.pendingCallbacksFlag) {
storage.pendingCallbacksFlag = true;
setTimeout(() => {
providerPendingUpdates[prefix] = false;
if (
callbacks[provider] === void 0 ||
callbacks[provider][prefix] === void 0
) {
return;
}
storage.pendingCallbacksFlag = false;
// Get all items
const items = callbacks[provider][prefix].slice(0);
const items = storage.loaderCallbacks
? storage.loaderCallbacks.slice(0)
: [];
if (!items.length) {
return;
}
const storage = getStorage(provider, prefix);
// Check each item for changes
let hasPending = false;
items.forEach((item: CallbackItem) => {
const provider = storage.provider;
const prefix = storage.prefix;
items.forEach((item) => {
const icons = item.icons;
const oldLength = icons.pending.length;
icons.pending = icons.pending.filter((icon) => {
@ -129,15 +76,7 @@ export function updateCallbacks(provider: string, prefix: string): void {
if (icons.pending.length !== oldLength) {
if (!hasPending) {
// All icons have been loaded - remove callback from prefix
removeCallback(
[
{
provider,
prefix,
},
],
item.id
);
removeCallback([storage], item.id);
}
item.callback(
icons.loaded.slice(0),
@ -162,7 +101,7 @@ let idCounter = 0;
export function storeCallback(
callback: IconifyIconLoaderCallback,
icons: SortedIcons,
pendingSources: IconifyIconSource[]
pendingSources: IconStorageWithIcons[]
): IconifyIconLoaderAbort {
// Create unique id and abort function
const id = idCounter++;
@ -174,24 +113,15 @@ export function storeCallback(
}
// Create item and store it for all pending prefixes
const item: CallbackItem = {
const item: APICallbackItem = {
id,
icons,
callback,
abort: abort,
};
pendingSources.forEach((source) => {
const provider = source.provider;
const prefix = source.prefix;
if (callbacks[provider] === void 0) {
callbacks[provider] = Object.create(null) as ProviderCallbackItems;
}
const providerCallbacks = callbacks[provider];
if (providerCallbacks[prefix] === void 0) {
providerCallbacks[prefix] = [];
}
providerCallbacks[prefix].push(item);
pendingSources.forEach((storage) => {
(storage.loaderCallbacks || (storage.loaderCallbacks = [])).push(item);
});
return abort;

View File

@ -1,9 +1,5 @@
import type { IconifyIcon, IconifyJSON } from '@iconify/types';
import {
IconifyIconName,
IconifyIconSource,
stringToIcon,
} from '@iconify/utils/lib/icon/name';
import { IconifyIconName, stringToIcon } from '@iconify/utils/lib/icon/name';
import type { SortedIcons } from '../icon/sort';
import { sortIcons } from '../icon/sort';
import { storeCallback, updateCallbacks } from './callbacks';
@ -13,6 +9,7 @@ import { listToIcons } from '../icon/list';
import { allowSimpleNames, getIconData } from '../storage/functions';
import { sendAPIQuery } from './query';
import { storeInBrowserStorage } from '../browser-storage/store';
import type { IconStorageWithIcons } from './types';
// Empty abort callback for loadIcons()
function emptyCallback(): void {
@ -49,126 +46,46 @@ export type IconifyLoadIcons = (
*/
export type IsPending = (icon: IconifyIconName) => boolean;
/**
* List of icons that are being loaded.
*
* Icons are added to this list when they are being checked and
* removed from this list when they are added to storage as
* either an icon or a missing icon. This way same icon should
* never be requested twice.
*
* [provider][prefix][icon] = time when icon was added to queue
*/
type PrefixPendingIcons = Record<string, number>;
type ProviderPendingIcons = Record<string, PrefixPendingIcons>;
const pendingIcons = Object.create(null) as Record<
string,
ProviderPendingIcons
>;
/**
* List of icons that are waiting to be loaded.
*
* List is passed to API module, then cleared.
*
* This list should not be used for any checks, use pendingIcons to check
* if icons is being loaded.
*
* [provider][prefix] = array of icon names
*/
type IconsToLoadPrefixItem = string[];
type IconsToLoadProviderItem = Record<string, IconsToLoadPrefixItem>;
const iconsToLoad = Object.create(null) as Record<
string,
IconsToLoadProviderItem
>;
// Flags to merge multiple synchronous icon requests in one asynchronous request
type FlagsItem = Record<string, boolean>;
const loaderFlags = Object.create(null) as Record<string, FlagsItem>;
const queueFlags = Object.create(null) as Record<string, FlagsItem>;
/**
* Function called when new icons have been loaded
*/
function loadedNewIcons(provider: string, prefix: string): void {
function loadedNewIcons(storage: IconStorageWithIcons): void {
// Run only once per tick, possibly joining multiple API responses in one call
if (loaderFlags[provider] === void 0) {
loaderFlags[provider] = Object.create(null) as FlagsItem;
}
const providerLoaderFlags = loaderFlags[provider];
if (!providerLoaderFlags[prefix]) {
providerLoaderFlags[prefix] = true;
if (!storage.iconsLoaderFlag) {
storage.iconsLoaderFlag = true;
setTimeout(() => {
providerLoaderFlags[prefix] = false;
updateCallbacks(provider, prefix);
storage.iconsLoaderFlag = false;
updateCallbacks(storage);
});
}
}
// Storage for errors for loadNewIcons(). Used to avoid spamming log with identical errors.
const errorsCache = Object.create(null) as Record<string, number>;
/**
* Load icons
*/
function loadNewIcons(provider: string, prefix: string, icons: string[]): void {
function err(): void {
const key = (provider === '' ? '' : '@' + provider + ':') + prefix;
const time = Math.floor(Date.now() / 60000); // log once in a minute
if (errorsCache[key] < time) {
errorsCache[key] = time;
console.error(
'Unable to retrieve icons for "' +
key +
'" because API is not configured properly.'
);
}
}
// Create nested objects if needed
if (iconsToLoad[provider] === void 0) {
iconsToLoad[provider] = Object.create(null) as IconsToLoadProviderItem;
}
const providerIconsToLoad = iconsToLoad[provider];
if (queueFlags[provider] === void 0) {
queueFlags[provider] = Object.create(null) as FlagsItem;
}
const providerQueueFlags = queueFlags[provider];
if (pendingIcons[provider] === void 0) {
pendingIcons[provider] = Object.create(null) as ProviderPendingIcons;
}
const providerPendingIcons = pendingIcons[provider];
function loadNewIcons(storage: IconStorageWithIcons, icons: string[]): void {
// Add icons to queue
if (providerIconsToLoad[prefix] === void 0) {
providerIconsToLoad[prefix] = icons;
if (!storage.iconsToLoad) {
storage.iconsToLoad = icons;
} else {
providerIconsToLoad[prefix] = providerIconsToLoad[prefix]
.concat(icons)
.sort();
storage.iconsToLoad = storage.iconsToLoad.concat(icons).sort();
}
// Trigger update on next tick, mering multiple synchronous requests into one asynchronous request
if (!providerQueueFlags[prefix]) {
providerQueueFlags[prefix] = true;
if (!storage.iconsQueueFlag) {
storage.iconsQueueFlag = true;
setTimeout(() => {
providerQueueFlags[prefix] = false;
storage.iconsQueueFlag = false;
const { provider, prefix } = storage;
// Get icons and delete queue
const icons = providerIconsToLoad[prefix];
delete providerIconsToLoad[prefix];
const icons = storage.iconsToLoad;
delete storage.iconsToLoad;
// Get API module
const api = getAPIModule(provider);
if (!api) {
// No way to load icons!
err();
let api: ReturnType<typeof getAPIModule>;
if (!icons || !(api = getAPIModule(provider))) {
// No icons or no way to load icons!
return;
}
@ -176,8 +93,6 @@ function loadNewIcons(provider: string, prefix: string, icons: string[]): void {
const params = api.prepare(provider, prefix, icons);
params.forEach((item) => {
sendAPIQuery(provider, item, (data, error) => {
const storage = getStorage(provider, prefix);
// Check for error
if (typeof data !== 'object') {
if (error !== 404) {
@ -201,10 +116,12 @@ function loadNewIcons(provider: string, prefix: string, icons: string[]): void {
}
// Remove added icons from pending list
const pending = providerPendingIcons[prefix];
parsed.forEach((name) => {
delete pending[name];
});
const pending = storage.pendingIcons;
if (pending) {
parsed.forEach((name) => {
pending.delete(name);
});
}
// Cache API response
storeInBrowserStorage(
@ -217,7 +134,7 @@ function loadNewIcons(provider: string, prefix: string, icons: string[]): void {
}
// Trigger update on next tick
loadedNewIcons(provider, prefix);
loadedNewIcons(storage);
});
});
});
@ -228,13 +145,12 @@ function loadNewIcons(provider: string, prefix: string, icons: string[]): void {
* Check if icon is being loaded
*/
export const isPending: IsPending = (icon: IconifyIconName): boolean => {
const provider = icon.provider;
const prefix = icon.prefix;
return (
pendingIcons[provider] &&
pendingIcons[provider][prefix] &&
pendingIcons[provider][prefix][icon.name] !== void 0
);
const storage = getStorage(
icon.provider,
icon.prefix
) as IconStorageWithIcons;
const pending = storage.pendingIcons;
return !!(pending && pending.has(icon.name));
};
/**
@ -280,59 +196,41 @@ export const loadIcons: IconifyLoadIcons = (
string,
ProviderNewIconsList
>;
const sources: IconifyIconSource[] = [];
const sources: IconStorageWithIcons[] = [];
let lastProvider: string, lastPrefix: string;
sortedIcons.pending.forEach((icon) => {
const provider = icon.provider;
const prefix = icon.prefix;
const { provider, prefix } = icon;
if (prefix === lastPrefix && provider === lastProvider) {
return;
}
lastProvider = provider;
lastPrefix = prefix;
sources.push({
provider,
prefix,
});
sources.push(getStorage(provider, prefix));
if (pendingIcons[provider] === void 0) {
pendingIcons[provider] = Object.create(
null
) as ProviderPendingIcons;
}
const providerPendingIcons = pendingIcons[provider];
if (providerPendingIcons[prefix] === void 0) {
providerPendingIcons[prefix] = Object.create(
null
) as PrefixPendingIcons;
}
if (newIcons[provider] === void 0) {
newIcons[provider] = Object.create(null) as ProviderNewIconsList;
}
const providerNewIcons = newIcons[provider];
if (providerNewIcons[prefix] === void 0) {
const providerNewIcons =
newIcons[provider] ||
(newIcons[provider] = Object.create(null) as ProviderNewIconsList);
if (!providerNewIcons[prefix]) {
providerNewIcons[prefix] = [];
}
});
// List of new icons
const time = Date.now();
// Filter pending icons list: find icons that are not being loaded yet
// If icon was called before, it must exist in pendingIcons or storage, but because this
// function is called right after sortIcons() that checks storage, icon is definitely not in storage.
sortedIcons.pending.forEach((icon) => {
const provider = icon.provider;
const prefix = icon.prefix;
const name = icon.name;
const { provider, prefix, name } = icon;
const pendingQueue = pendingIcons[provider][prefix];
if (pendingQueue[name] === void 0) {
const storage = getStorage(provider, prefix) as IconStorageWithIcons;
const pendingQueue =
storage.pendingIcons || (storage.pendingIcons = new Set());
if (!pendingQueue.has(name)) {
// New icon - add to pending queue to mark it as being loaded
pendingQueue[name] = time;
pendingQueue.add(name);
// Add it to new icons list to pass it to API module for loading
newIcons[provider][prefix].push(name);
}
@ -340,11 +238,10 @@ export const loadIcons: IconifyLoadIcons = (
// Load icons on next tick to make sure result is not returned before callback is stored and
// to consolidate multiple synchronous loadIcons() calls into one asynchronous API call
sources.forEach((source) => {
const provider = source.provider;
const prefix = source.prefix;
sources.forEach((storage) => {
const { provider, prefix } = storage;
if (newIcons[provider][prefix].length) {
loadNewIcons(provider, prefix, newIcons[provider][prefix]);
loadNewIcons(storage, newIcons[provider][prefix]);
}
});

View File

@ -0,0 +1,60 @@
import type {
IconifyIconLoaderCallback,
IconifyIconLoaderAbort,
} from './icons';
import type { SortedIcons } from '../icon/sort';
import type { IconStorage } from '../storage/storage';
/**
* Storage for callbacks
*/
export interface APICallbackItem {
// id
id: number;
// Icons
icons: SortedIcons;
// Callback to call on any update
callback: IconifyIconLoaderCallback;
// Callback to call to remove item from queue
abort: IconifyIconLoaderAbort;
}
/**
* Add custom stuff to storage
*/
export interface IconStorageWithIcons extends IconStorage {
/**
* List of icons that are being loaded, added to storage
*
* Icons are added to this list when they are being checked and
* removed from this list when they are added to storage as
* either an icon or a missing icon. This way same icon should
* never be requested twice.
*/
pendingIcons?: Set<string>;
/**
* List of icons that are waiting to be loaded.
*
* List is passed to API module, then cleared.
*
* This list should not be used for any checks, use pendingIcons to check
* if icons is being loaded.
*
* [provider][prefix] = array of icon names
*/
iconsToLoad?: string[];
// Flags to merge multiple synchronous icon requests in one asynchronous request
iconsLoaderFlag?: boolean;
iconsQueueFlag?: boolean;
// Loader callbacks
loaderCallbacks?: APICallbackItem[];
// Pending callbacks update
pendingCallbacksFlag?: boolean;
}

View File

@ -2,7 +2,6 @@ import type {
BrowserStorageConfig,
BrowserStorageCount,
BrowserStorageEmptyList,
BrowserStorageStatus,
} from './types';
/**
@ -32,8 +31,8 @@ export const browserStorageEmptyItems: BrowserStorageEmptyList = {
/**
* Flag to check if storage has been loaded
*/
export let browserStorageStatus: BrowserStorageStatus = false;
export let browserStorageStatus = false;
export function setBrowserStorageStatus(status: BrowserStorageStatus) {
export function setBrowserStorageStatus(status: boolean) {
browserStorageStatus = status;
}

View File

@ -23,10 +23,10 @@ import type { BrowserStorageConfig, BrowserStorageItem } from './types';
* Load icons from cache
*/
export function initBrowserStorage() {
if (browserStorageStatus === true) {
if (browserStorageStatus) {
return;
}
setBrowserStorageStatus('loading');
setBrowserStorageStatus(true);
// Minimum time
const minTime =
@ -66,10 +66,12 @@ export function initBrowserStorage() {
valid = false;
} else {
// Add icon set
const iconSet = data.data;
const provider = data.provider;
const prefix = data.data.prefix;
const prefix = iconSet.prefix;
const storage = getStorage(provider, prefix);
valid = addIconSet(storage, data.data).length > 0;
valid = addIconSet(storage, iconSet).length > 0;
}
} catch (err) {
valid = false;
@ -129,9 +131,10 @@ export function initBrowserStorage() {
}
}
// Load each storage
for (const key in browserStorageConfig) {
load(key as keyof BrowserStorageConfig);
}
setBrowserStorageStatus(true);
// Check for update
}

View File

@ -18,9 +18,6 @@ export function storeInBrowserStorage(provider: string, data: IconifyJSON) {
if (!browserStorageStatus) {
initBrowserStorage();
}
if (browserStorageStatus === 'loading') {
return;
}
function store(key: BrowserStorageType): true | undefined {
if (!browserStorageConfig[key]) {

View File

@ -18,6 +18,3 @@ export interface BrowserStorageItem {
provider: string;
data: IconifyJSON;
}
// Status: not loaded, loading, loaded
export type BrowserStorageStatus = false | 'loading' | true;

View File

@ -1,8 +1,5 @@
import {
callbacks,
updateCallbacks,
storeCallback,
} from '../../lib/api/callbacks';
import { updateCallbacks, storeCallback } from '../../lib/api/callbacks';
import type { IconStorageWithIcons } from '../../lib/api/types';
import { sortIcons } from '../../lib/icon/sort';
import { getStorage, addIconSet } from '../../lib/storage/storage';
@ -22,7 +19,7 @@ describe('Testing API callbacks', () => {
const prefix = nextPrefix();
let counter = 0;
const storage = getStorage(provider, prefix);
const storage = getStorage(provider, prefix) as IconStorageWithIcons;
const abort = storeCallback(
(loaded, missing, pending, unsubscribe) => {
expect(unsubscribe).toBe(abort);
@ -52,7 +49,7 @@ describe('Testing API callbacks', () => {
name: 'icon2',
},
]);
expect(callbacks[provider][prefix].length).toBe(1);
expect(storage.loaderCallbacks?.length).toBe(1);
// Add icon2 and trigger update
addIconSet(storage, {
@ -64,7 +61,7 @@ describe('Testing API callbacks', () => {
},
});
updateCallbacks(provider, prefix);
updateCallbacks(storage);
return;
case 2:
@ -89,7 +86,7 @@ describe('Testing API callbacks', () => {
},
]);
expect(pending).toEqual([]);
expect(callbacks[provider][prefix].length).toBe(0);
expect(storage.loaderCallbacks?.length).toBe(0);
done();
}
},
@ -110,19 +107,14 @@ describe('Testing API callbacks', () => {
name: 'icon3',
},
]),
[
{
provider,
prefix,
},
]
[storage]
);
// Test callbacks
expect(callbacks[provider][prefix].length).toBe(1);
expect(storage.loaderCallbacks?.length).toBe(1);
// Test update - should do nothing
updateCallbacks(provider, prefix);
updateCallbacks(storage);
// Wait for tick because updateCallbacks will use one
setTimeout(() => {
@ -139,7 +131,7 @@ describe('Testing API callbacks', () => {
},
not_found: ['icon3'],
});
updateCallbacks(provider, prefix);
updateCallbacks(storage);
});
});
@ -147,7 +139,7 @@ describe('Testing API callbacks', () => {
const provider = '';
const prefix = nextPrefix();
const storage = getStorage(provider, prefix);
const storage = getStorage(provider, prefix) as IconStorageWithIcons;
addIconSet(storage, {
prefix,
icons: {
@ -182,16 +174,11 @@ describe('Testing API callbacks', () => {
name: 'icon3',
},
]),
[
{
provider,
prefix,
},
]
[storage]
);
// callbacks should not have been initialised
expect(callbacks[prefix]).toBeUndefined();
expect(storage.loaderCallbacks).toBeUndefined();
});
it('Cancel callback', (done) => {
@ -199,7 +186,7 @@ describe('Testing API callbacks', () => {
const prefix = nextPrefix();
let counter = 0;
const storage = getStorage(provider, prefix);
const storage = getStorage(provider, prefix) as IconStorageWithIcons;
const abort = storeCallback(
(loaded, missing, pending, unsubscribe) => {
expect(unsubscribe).toBe(abort);
@ -229,7 +216,7 @@ describe('Testing API callbacks', () => {
name: 'icon2',
},
]);
expect(callbacks[provider][prefix].length).toBe(1);
expect(storage.loaderCallbacks?.length).toBe(1);
// Add icon2 and trigger update
addIconSet(storage, {
@ -241,11 +228,11 @@ describe('Testing API callbacks', () => {
},
});
updateCallbacks(provider, prefix);
updateCallbacks(storage);
// Unsubscribe and set timer to call done()
unsubscribe();
expect(callbacks[provider][prefix].length).toBe(0);
expect(storage.loaderCallbacks?.length).toBe(0);
setTimeout(done);
},
sortIcons([
@ -265,19 +252,14 @@ describe('Testing API callbacks', () => {
name: 'icon3',
},
]),
[
{
provider,
prefix,
},
]
[storage]
);
// Test callbacks
expect(callbacks[provider][prefix].length).toBe(1);
expect(storage.loaderCallbacks?.length).toBe(1);
// Test update - should do nothing
updateCallbacks(provider, prefix);
updateCallbacks(storage);
// Wait for tick because updateCallbacks will use one
setTimeout(() => {
@ -294,7 +276,7 @@ describe('Testing API callbacks', () => {
},
not_found: ['icon3'],
});
updateCallbacks(provider, prefix);
updateCallbacks(storage);
});
});
@ -304,8 +286,8 @@ describe('Testing API callbacks', () => {
const prefix2 = nextPrefix();
let counter = 0;
const storage1 = getStorage(provider, prefix1);
const storage2 = getStorage(provider, prefix2);
const storage1 = getStorage(provider, prefix1) as IconStorageWithIcons;
const storage2 = getStorage(provider, prefix2) as IconStorageWithIcons;
const abort = storeCallback(
(loaded, missing, pending, unsubscribe) => {
@ -336,8 +318,8 @@ describe('Testing API callbacks', () => {
name: 'icon2',
},
]);
expect(callbacks[provider][prefix1].length).toBe(0);
expect(callbacks[provider][prefix2].length).toBe(1);
expect(storage1.loaderCallbacks?.length).toBe(0);
expect(storage2.loaderCallbacks?.length).toBe(1);
// Add icon2 and trigger update
addIconSet(storage2, {
@ -349,13 +331,13 @@ describe('Testing API callbacks', () => {
},
});
updateCallbacks(provider, prefix2);
updateCallbacks(storage2);
break;
case 2:
// Second run - icon2 should be loaded
expect(callbacks[provider][prefix1].length).toBe(0);
expect(callbacks[provider][prefix2].length).toBe(0);
expect(storage1.loaderCallbacks?.length).toBe(0);
expect(storage2.loaderCallbacks?.length).toBe(0);
done();
break;
@ -380,18 +362,15 @@ describe('Testing API callbacks', () => {
name: 'icon3',
},
]),
[
{ provider, prefix: prefix1 },
{ provider, prefix: prefix2 },
]
[storage1, storage2]
);
// Test callbacks
expect(callbacks[provider][prefix1].length).toBe(1);
expect(callbacks[provider][prefix2].length).toBe(1);
expect(storage1.loaderCallbacks?.length).toBe(1);
expect(storage2.loaderCallbacks?.length).toBe(1);
// Test update - should do nothing
updateCallbacks(provider, prefix1);
updateCallbacks(storage1);
// Wait for tick because updateCallbacks will use one
setTimeout(() => {
@ -408,7 +387,7 @@ describe('Testing API callbacks', () => {
},
not_found: ['icon3'],
});
updateCallbacks(provider, prefix1);
updateCallbacks(storage1);
});
});
@ -419,8 +398,8 @@ describe('Testing API callbacks', () => {
const prefix2 = nextPrefix();
let counter = 0;
const storage1 = getStorage(provider1, prefix1);
const storage2 = getStorage(provider2, prefix2);
const storage1 = getStorage(provider1, prefix1) as IconStorageWithIcons;
const storage2 = getStorage(provider2, prefix2) as IconStorageWithIcons;
const abort = storeCallback(
(loaded, missing, pending, unsubscribe) => {
@ -451,12 +430,8 @@ describe('Testing API callbacks', () => {
name: 'icon2',
},
]);
expect(callbacks[provider1][prefix1].length).toBe(0);
expect(callbacks[provider2][prefix2].length).toBe(1);
// Make sure providers/prefixes aren't mixed
expect(callbacks[provider1][prefix2]).toBeUndefined();
expect(callbacks[provider2][prefix1]).toBeUndefined();
expect(storage1.loaderCallbacks?.length).toBe(0);
expect(storage2.loaderCallbacks?.length).toBe(1);
// Add icon2 and trigger update
addIconSet(storage2, {
@ -468,17 +443,13 @@ describe('Testing API callbacks', () => {
},
});
updateCallbacks(provider2, prefix2);
updateCallbacks(storage2);
break;
case 2:
// Second run - icon2 should be loaded
expect(callbacks[provider1][prefix1].length).toBe(0);
expect(callbacks[provider2][prefix2].length).toBe(0);
// Make sure providers/prefixes aren't mixed
expect(callbacks[provider1][prefix2]).toBeUndefined();
expect(callbacks[provider2][prefix1]).toBeUndefined();
expect(storage1.loaderCallbacks?.length).toBe(0);
expect(storage2.loaderCallbacks?.length).toBe(0);
done();
break;
@ -504,21 +475,15 @@ describe('Testing API callbacks', () => {
name: 'icon3',
},
]),
[
{ provider: provider1, prefix: prefix1 },
{ provider: provider2, prefix: prefix2 },
]
[storage1, storage2]
);
// Test callbacks
expect(callbacks[provider1][prefix1].length).toBe(1);
expect(callbacks[provider2][prefix2].length).toBe(1);
expect(callbacks[provider1][prefix2]).toBeUndefined();
expect(callbacks[provider2][prefix1]).toBeUndefined();
expect(storage1.loaderCallbacks?.length).toBe(1);
expect(storage2.loaderCallbacks?.length).toBe(1);
// Test update - should do nothing
updateCallbacks(provider1, prefix1);
updateCallbacks(storage1);
// Wait for tick because updateCallbacks will use one
setTimeout(() => {
@ -535,7 +500,7 @@ describe('Testing API callbacks', () => {
},
not_found: ['icon3'],
});
updateCallbacks(provider1, prefix1);
updateCallbacks(storage1);
});
});
});