2
0
mirror of https://github.com/iconify/iconify.git synced 2025-01-26 08:38:30 +00:00

Support storage for simple names without provider and prefix

This commit is contained in:
Vjacheslav Trushkin 2021-04-23 23:31:36 +03:00
parent 257d3d60f0
commit a846dbcc8c
4 changed files with 235 additions and 18 deletions

View File

@ -22,7 +22,8 @@ const match = /^[a-z0-9]+(-[a-z0-9]+)*$/;
*/ */
export const stringToIcon = ( export const stringToIcon = (
value: string, value: string,
validate?: boolean validate?: boolean,
allowSimpleName?: boolean
): IconifyIconName | null => { ): IconifyIconName | null => {
let provider = ''; let provider = '';
const colonSeparated = value.split(':'); const colonSeparated = value.split(':');
@ -55,7 +56,8 @@ export const stringToIcon = (
} }
// Attempt to split by dash: "prefix-name" // Attempt to split by dash: "prefix-name"
const dashSeparated = colonSeparated[0].split('-'); const name = colonSeparated[0];
const dashSeparated = name.split('-');
if (dashSeparated.length > 1) { if (dashSeparated.length > 1) {
const result: IconifyIconName = { const result: IconifyIconName = {
provider: provider, provider: provider,
@ -65,6 +67,18 @@ export const stringToIcon = (
return validate && !validateIcon(result) ? null : result; return validate && !validateIcon(result) ? null : result;
} }
// If allowEmpty is set, allow empty provider and prefix, allowing names like "home"
if (allowSimpleName && provider === '') {
const result: IconifyIconName = {
provider: provider,
prefix: '',
name,
};
return validate && !validateIcon(result, allowSimpleName)
? null
: result;
}
return null; return null;
}; };
@ -73,14 +87,17 @@ export const stringToIcon = (
* *
* This function is not part of stringToIcon because validation is not needed for most code. * This function is not part of stringToIcon because validation is not needed for most code.
*/ */
export const validateIcon = (icon: IconifyIconName | null): boolean => { export const validateIcon = (
icon: IconifyIconName | null,
allowSimpleName?: boolean
): boolean => {
if (!icon) { if (!icon) {
return false; return false;
} }
return !!( return !!(
(icon.provider === '' || icon.provider.match(match)) && (icon.provider === '' || icon.provider.match(match)) &&
icon.prefix.match(match) && ((allowSimpleName && icon.prefix === '') || icon.prefix.match(match)) &&
icon.name.match(match) icon.name.match(match)
); );
}; };

View File

@ -1,9 +1,17 @@
import type { IconifyJSON } from '@iconify/types'; import type { IconifyJSON } from '@iconify/types';
import type { FullIconifyIcon, IconifyIcon } from '../icon'; import type { FullIconifyIcon, IconifyIcon } from '../icon';
import { parseIconSet } from '../icon/icon-set';
import type { IconifyIconName } from '../icon/name'; import type { IconifyIconName } from '../icon/name';
import { stringToIcon, validateIcon } from '../icon/name'; import { stringToIcon, validateIcon } from '../icon/name';
import { merge } from '../misc/merge'; import { merge } from '../misc/merge';
import { getStorage, getIcon, listIcons, addIcon, addIconSet } from './storage'; import {
getStorage,
getIcon,
listIcons,
addIcon as storeIcon,
addIconSet,
} from './storage';
// import { parseIconSet } from '../icon';
/** /**
* Interface for exported storage functions * Interface for exported storage functions
@ -36,31 +44,75 @@ export interface IconifyStorageFunctions {
addCollection: (data: IconifyJSON, provider?: string) => boolean; addCollection: (data: IconifyJSON, provider?: string) => boolean;
} }
/**
* Allow storing icons without provider or prefix, making it possible to store icons like "home"
*/
let simpleNames = false;
export function allowSimpleNames(allow?: boolean): boolean {
if (typeof allow === 'boolean') {
simpleNames = allow;
}
return simpleNames;
}
/** /**
* Get icon data * Get icon data
*/ */
export function getIconData( export function getIconData(
name: string | IconifyIconName name: string | IconifyIconName
): FullIconifyIcon | null { ): FullIconifyIcon | null {
const icon = typeof name === 'string' ? stringToIcon(name, true) : name; const icon =
typeof name === 'string' ? stringToIcon(name, true, simpleNames) : name;
return icon return icon
? getIcon(getStorage(icon.provider, icon.prefix), icon.name) ? getIcon(getStorage(icon.provider, icon.prefix), icon.name)
: null; : null;
} }
/**
* Add one icon
*/
export function addIcon(name: string, data: IconifyIcon): boolean {
const icon = stringToIcon(name, true, simpleNames);
if (!icon) {
return false;
}
const storage = getStorage(icon.provider, icon.prefix);
return storeIcon(storage, icon.name, data);
}
/** /**
* Add icon set * Add icon set
*/ */
export function addCollection(data: IconifyJSON, provider?: string): boolean { export function addCollection(data: IconifyJSON, provider?: string): boolean {
if (typeof data !== 'object') {
return false;
}
// Get provider
if (typeof provider !== 'string') { if (typeof provider !== 'string') {
provider = typeof data.provider === 'string' ? data.provider : ''; provider = typeof data.provider === 'string' ? data.provider : '';
} }
// Check for simple names: requires empty provider and prefix
if (
simpleNames &&
provider === '' &&
(typeof data.prefix !== 'string' || data.prefix === '')
) {
// Simple names: add icons one by one
let added = false;
parseIconSet(data, (name, icon) => {
if (icon !== null && addIcon(name, icon)) {
added = true;
}
});
return added;
}
// Validate provider and prefix
if ( if (
typeof data !== 'object' ||
// Prefix must be present
typeof data.prefix !== 'string' || typeof data.prefix !== 'string' ||
// Validate provider and prefix
!validateIcon({ !validateIcon({
provider, provider,
prefix: data.prefix, prefix: data.prefix,
@ -91,14 +143,7 @@ export const storageFunctions: IconifyStorageFunctions = {
listIcons, listIcons,
// Add icon // Add icon
addIcon: (name, data) => { addIcon,
const icon = stringToIcon(name, true);
if (!icon) {
return false;
}
const storage = getStorage(icon.provider, icon.prefix);
return addIcon(storage, icon.name, data);
},
// Add icon set // Add icon set
addCollection, addCollection,

View File

@ -34,16 +34,47 @@ describe('Testing icon name', () => {
}); });
expect(validateIcon(icon)).to.be.equal(true); expect(validateIcon(icon)).to.be.equal(true);
// Simple word without prefix
icon = stringToIcon('home');
expect(icon).to.be.eql(null);
expect(validateIcon(icon)).to.be.equal(false);
// Same as above, but with empty names enabled
icon = stringToIcon('home', false, true);
expect(icon).to.be.eql({
provider: '',
prefix: '',
name: 'home',
});
expect(validateIcon(icon)).to.be.equal(false);
expect(validateIcon(icon, true)).to.be.equal(true);
// Missing icon name // Missing icon name
icon = stringToIcon('@iconify-home-icon'); icon = stringToIcon('@iconify-home-icon');
expect(icon).to.be.eql(null); expect(icon).to.be.eql(null);
expect(validateIcon(icon)).to.be.equal(false); expect(validateIcon(icon)).to.be.equal(false);
// Same as above, but with empty names enabled
icon = stringToIcon('@iconify-home-icon', false, true);
expect(icon).to.be.eql(null);
expect(validateIcon(icon)).to.be.equal(false);
expect(validateIcon(icon, true)).to.be.equal(false);
// Underscore is not an acceptable separator // Underscore is not an acceptable separator
icon = stringToIcon('fa_home'); icon = stringToIcon('fa_home');
expect(icon).to.be.eql(null); expect(icon).to.be.eql(null);
expect(validateIcon(icon)).to.be.equal(false); expect(validateIcon(icon)).to.be.equal(false);
// Same as above, but with empty names enabled
icon = stringToIcon('fa_home', false, true);
expect(icon).to.be.eql({
provider: '',
prefix: '',
name: 'fa_home',
});
expect(validateIcon(icon)).to.be.equal(false);
expect(validateIcon(icon, true)).to.be.equal(false);
// Invalid character '_': fail validateIcon // Invalid character '_': fail validateIcon
icon = stringToIcon('fa:home_outline') as IconifyIconName; icon = stringToIcon('fa:home_outline') as IconifyIconName;
expect(icon).to.be.eql({ expect(icon).to.be.eql({
@ -134,6 +165,10 @@ describe('Testing icon name', () => {
expect(icon).to.be.eql(null); expect(icon).to.be.eql(null);
expect(validateIcon(icon)).to.be.equal(false); expect(validateIcon(icon)).to.be.equal(false);
// Same as above, empty names allowed
icon = stringToIcon('@mdi:light:home:outline', false, true);
expect(icon).to.be.eql(null);
// Upper case: fail validateIcon // Upper case: fail validateIcon
icon = stringToIcon('@MD:home-outline') as IconifyIconName; icon = stringToIcon('@MD:home-outline') as IconifyIconName;
expect(icon).to.be.eql({ expect(icon).to.be.eql({

View File

@ -1,6 +1,10 @@
import 'mocha'; import 'mocha';
import { expect } from 'chai'; import { expect } from 'chai';
import { storageFunctions } from '../../lib/storage/functions'; import {
storageFunctions,
allowSimpleNames,
} from '../../lib/storage/functions';
import { fullIcon } from '../../lib/icon';
describe('Testing IconifyStorageFunctions', () => { describe('Testing IconifyStorageFunctions', () => {
let count = 0; let count = 0;
@ -27,4 +31,120 @@ describe('Testing IconifyStorageFunctions', () => {
expect(storageFunctions.iconExists(testName)).to.be.equal(true); expect(storageFunctions.iconExists(testName)).to.be.equal(true);
expect(storageFunctions.listIcons(provider)).to.be.eql([testName]); expect(storageFunctions.listIcons(provider)).to.be.eql([testName]);
}); });
it('Invalid icon name', () => {
const testName = 'storage' + count++;
// Reset module
allowSimpleNames(false);
// Empty
expect(storageFunctions.iconExists(testName)).to.be.equal(false);
expect(storageFunctions.getIcon(testName)).to.be.equal(null);
// Add and test one icon (icon should not be added)
expect(
storageFunctions.addIcon(testName, {
body: '<g />',
})
).to.be.equal(false);
expect(storageFunctions.iconExists(testName)).to.be.equal(false);
});
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(
storageFunctions.addCollection({
prefix: '',
icons: {
foo: {
body: '<g />',
},
},
})
).to.be.equal(false);
});
it('Simple icon name', () => {
const testName = 'storage' + count++;
// Enable empty storage
allowSimpleNames(true);
// Empty
expect(storageFunctions.iconExists(testName)).to.be.equal(false);
expect(storageFunctions.getIcon(testName)).to.be.equal(null);
// Add and test one icon
expect(
storageFunctions.addIcon(testName, {
body: '<g />',
})
).to.be.equal(true);
expect(storageFunctions.iconExists(testName)).to.be.equal(true);
// Reset config after test
allowSimpleNames(false);
});
it('Collection with simple icon name', () => {
const n = count++;
const n2 = count++;
let name: string;
// Enable empty storage
allowSimpleNames(true);
// Add icon set
const name1 = 'test' + n;
const prefix2 = `prefixed${n}`;
const name2 = `icon${n2}`;
expect(
storageFunctions.addCollection({
prefix: '',
icons: {
[name1]: {
body: '<g data-icon="basic-icon" />',
},
[`${prefix2}-${name2}`]: {
body: '<g data-icon="prefixed-icon" />',
},
},
})
).to.be.equal(true);
// Test 'test'
name = name1;
expect(storageFunctions.iconExists(name)).to.be.equal(true);
expect(storageFunctions.getIcon(name)).to.be.eql(
fullIcon({
body: '<g data-icon="basic-icon" />',
})
);
// Test prefixed icon, using ':' separator
name = `${prefix2}:${name2}`;
expect(storageFunctions.listIcons('', prefix2)).to.be.eql([name]);
expect(storageFunctions.iconExists(name)).to.be.equal(true);
expect(storageFunctions.getIcon(name)).to.be.eql(
fullIcon({
body: '<g data-icon="prefixed-icon" />',
})
);
// Test prefixed icon, using '-' separator
name = `${prefix2}-${name2}`;
expect(storageFunctions.iconExists(name)).to.be.equal(true);
expect(storageFunctions.getIcon(name)).to.be.eql(
fullIcon({
body: '<g data-icon="prefixed-icon" />',
})
);
// Reset config after test
allowSimpleNames(false);
});
}); });