2
0
mirror of https://github.com/iconify/iconify.git synced 2025-01-28 09:38:41 +00:00

Refactor addCollection in core: move it to separate reusable function. Publish RC.1

This commit is contained in:
Vjacheslav Trushkin 2020-09-03 14:11:17 +03:00
parent d5f199a42e
commit 9e2e2d315e
6 changed files with 645 additions and 439 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@iconify/core",
"version": "1.0.0-beta.5",
"version": "1.0.0-rc.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -48,6 +48,47 @@
"resolved": "https://registry.npmjs.org/@cyberalien/redundancy/-/redundancy-1.0.0.tgz",
"integrity": "sha512-/tx5GpGSyMn5FrOSggDSm9yJDLcEXiK0+zdCBssST6w9QgdJjoQ9lRBSxql/3vgQoI+7XbubWsP86jjbuVnFcA=="
},
"@eslint/eslintrc": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz",
"integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
"debug": "^4.1.1",
"espree": "^7.3.0",
"globals": "^12.1.0",
"ignore": "^4.0.6",
"import-fresh": "^3.2.1",
"js-yaml": "^3.13.1",
"lodash": "^4.17.19",
"minimatch": "^3.0.4",
"strip-json-comments": "^3.1.1"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"ignore": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
"dev": true
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
}
}
},
"@iconify/types": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@iconify/types/-/types-1.0.3.tgz",
@ -98,9 +139,9 @@
"dev": true
},
"@types/json-schema": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz",
"integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==",
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
"dev": true
},
"@types/mocha": {
@ -110,9 +151,9 @@
"dev": true
},
"@types/node": {
"version": "14.6.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.2.tgz",
"integrity": "sha512-onlIwbaeqvZyniGPfdw/TEhKIh79pz66L1q06WUQqJLnAb6wbjvOtepLYTGHTqzdXgBYIE3ZdmqHDGsRsbBz7A==",
"version": "14.6.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.3.tgz",
"integrity": "sha512-pC/hkcREG6YfDfui1FBmj8e20jFU5Exjw4NYDm8kEdrW+mOh0T1Zve8DWKnS7ZIZvgncrctcNCXF4Q2I+loyww==",
"dev": true
},
"@types/request": {
@ -134,13 +175,13 @@
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.0.0.tgz",
"integrity": "sha512-5e6q1TR7gS2P+8W2xndCu7gBh3BzmYEo70OyIdsmCmknHha/yNbz2vdevl+tP1uoaMOcrzg4gyrAijuV3DDBHA==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.0.1.tgz",
"integrity": "sha512-pQZtXupCn11O4AwpYVUX4PDFfmIJl90ZgrEBg0CEcqlwvPiG0uY81fimr1oMFblZnpKAq6prrT9a59pj1x58rw==",
"dev": true,
"requires": {
"@typescript-eslint/experimental-utils": "4.0.0",
"@typescript-eslint/scope-manager": "4.0.0",
"@typescript-eslint/experimental-utils": "4.0.1",
"@typescript-eslint/scope-manager": "4.0.1",
"debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0",
@ -166,28 +207,28 @@
}
},
"@typescript-eslint/experimental-utils": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.0.0.tgz",
"integrity": "sha512-hbX6zR+a/vcpFVNJYN/Nbd7gmaMosDTxHEKcvmhWeWcq/0UDifrqmCfkkodbAKL46Fn4ekSBMTyq2zlNDzcQxw==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.0.1.tgz",
"integrity": "sha512-gAqOjLiHoED79iYTt3F4uSHrYmg/GPz/zGezdB0jAdr6S6gwNiR/j7cTZ8nREKVzMVKLd9G3xbg1sV9GClW3sw==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/scope-manager": "4.0.0",
"@typescript-eslint/types": "4.0.0",
"@typescript-eslint/typescript-estree": "4.0.0",
"@typescript-eslint/scope-manager": "4.0.1",
"@typescript-eslint/types": "4.0.1",
"@typescript-eslint/typescript-estree": "4.0.1",
"eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0"
}
},
"@typescript-eslint/parser": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.0.0.tgz",
"integrity": "sha512-KuBwTUzc3G3dD5k9ybTjgqIQjjJPY6WPaEoxdNS5vN5XV/Tixp0itA14+NQlbeswTHvsELaKXZhynxD/O2wHFA==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.0.1.tgz",
"integrity": "sha512-1+qLmXHNAWSQ7RB6fdSQszAiA7JTwzakj5cNYjBTUmpH2cqilxMZEIV+DRKjVZs8NzP3ALmKexB0w/ExjcK9Iw==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "4.0.0",
"@typescript-eslint/types": "4.0.0",
"@typescript-eslint/typescript-estree": "4.0.0",
"@typescript-eslint/scope-manager": "4.0.1",
"@typescript-eslint/types": "4.0.1",
"@typescript-eslint/typescript-estree": "4.0.1",
"debug": "^4.1.1"
},
"dependencies": {
@ -209,29 +250,29 @@
}
},
"@typescript-eslint/scope-manager": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.0.0.tgz",
"integrity": "sha512-9gcWUPoWo7gk/+ZQPg7L1ySRmR5HLIy3Vu6/LfhQbuzIkGm6v2CGIjpVRISoDLFRovNRDImd4aP/sa8O4yIEBg==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.0.1.tgz",
"integrity": "sha512-u3YEXVJ8jsj7QCJk3om0Y457fy2euEOkkzxIB/LKU3MdyI+FJ2gI0M4aKEaXzwCSfNDiZ13a3lDo5DVozc+XLQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.0.0",
"@typescript-eslint/visitor-keys": "4.0.0"
"@typescript-eslint/types": "4.0.1",
"@typescript-eslint/visitor-keys": "4.0.1"
}
},
"@typescript-eslint/types": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.0.0.tgz",
"integrity": "sha512-bK+c2VLzznX2fUWLK6pFDv3cXGTp7nHIuBMq1B9klA+QCsqLHOOqe5TQReAQDl7DN2RfH+neweo0oC5hYlG7Rg==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.0.1.tgz",
"integrity": "sha512-S+gD3fgbkZYW2rnbjugNMqibm9HpEjqZBZkTiI3PwbbNGWmAcxolWIUwZ0SKeG4Dy2ktpKKaI/6+HGYVH8Qrlg==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.0.0.tgz",
"integrity": "sha512-ewFMPi2pMLDNIXGMPdf8r7El2oPSZw9PEYB0j+WcpKd7AX2ARmajGa7RUHTukllWX2bj4vWX6JLE1Oih2BMokA==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.0.1.tgz",
"integrity": "sha512-zGzleORFXrRWRJAMLTB2iJD1IZbCPkg4hsI8mGdpYlKaqzvKYSEWVAYh14eauaR+qIoZVWrXgYSXqLtTlxotiw==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.0.0",
"@typescript-eslint/visitor-keys": "4.0.0",
"@typescript-eslint/types": "4.0.1",
"@typescript-eslint/visitor-keys": "4.0.1",
"debug": "^4.1.1",
"globby": "^11.0.1",
"is-glob": "^4.0.1",
@ -258,12 +299,12 @@
}
},
"@typescript-eslint/visitor-keys": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.0.0.tgz",
"integrity": "sha512-sTouJbv6rjVJeTE4lpSBVYXq/u5K3gbB6LKt7ccFEZPTZB/VeQ0ssUz9q5Hx++sCqBbdF8PzrrgvEnicXAR6NQ==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.0.1.tgz",
"integrity": "sha512-yBSqd6FjnTzbg5RUy9J+9kJEyQjTI34JdGMJz+9ttlJzLCnGkBikxw+N5n2VDcc3CesbIEJ0MnZc5uRYnrEnCw==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.0.0",
"@typescript-eslint/types": "4.0.1",
"eslint-visitor-keys": "^2.0.0"
}
},
@ -724,12 +765,13 @@
"dev": true
},
"eslint": {
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.7.0.tgz",
"integrity": "sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg==",
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.8.1.tgz",
"integrity": "sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@eslint/eslintrc": "^0.1.3",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@ -739,7 +781,7 @@
"eslint-scope": "^5.1.0",
"eslint-utils": "^2.1.0",
"eslint-visitor-keys": "^1.3.0",
"espree": "^7.2.0",
"espree": "^7.3.0",
"esquery": "^1.2.0",
"esutils": "^2.0.2",
"file-entry-cache": "^5.0.1",
@ -872,12 +914,20 @@
}
},
"esrecurse": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
"integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
"dev": true,
"requires": {
"estraverse": "^4.1.0"
"estraverse": "^5.2.0"
},
"dependencies": {
"estraverse": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
"dev": true
}
}
},
"estraverse": {

View File

@ -2,7 +2,7 @@
"name": "@iconify/core",
"description": "Reusable files used by multiple Iconify packages",
"author": "Vjacheslav Trushkin <cyberalien@gmail.com> (https://iconify.design)",
"version": "1.0.0-beta.5",
"version": "1.0.0-rc.1",
"license": "(Apache-2.0 OR GPL-2.0)",
"bugs": "https://github.com/iconify/iconify/issues",
"homepage": "https://iconify.design/",
@ -24,12 +24,12 @@
"devDependencies": {
"@types/chai": "^4.2.12",
"@types/mocha": "^8.0.3",
"@types/node": "^14.6.2",
"@types/node": "^14.6.3",
"@types/request": "^2.48.5",
"@typescript-eslint/eslint-plugin": "^4.0.0",
"@typescript-eslint/parser": "^4.0.0",
"@typescript-eslint/eslint-plugin": "^4.0.1",
"@typescript-eslint/parser": "^4.0.1",
"chai": "^4.2.0",
"eslint": "^7.7.0",
"eslint": "^7.8.1",
"mocha": "^8.1.3",
"typescript": "^4.0.2"
},

View File

@ -0,0 +1,132 @@
import {
IconifyJSON,
IconifyIcon,
IconifyOptional,
IconifyAlias,
IconifyIcons,
IconifyAliases,
} from '@iconify/types';
import { iconDefaults, FullIconifyIcon } from '.';
import { merge } from '../misc/merge';
import { mergeIcons } from './merge';
/**
* What to track when adding icon set:
*
* none - do not track anything, return true on success
* added - track added icons, return list of added icons on success
* all - track added and missing icons, return full list on success
*/
export type AddIconSetTracking = 'none' | 'added' | 'all';
/**
* Callback to add icon to storage.
*
* If data === null, icon is missing.
*/
export type SplitIconSetCallback = (
name: string,
data: FullIconifyIcon | null
) => void;
/**
* Get list of defaults keys
*/
const defaultsKeys = Object.keys(iconDefaults) as (keyof IconifyOptional)[];
/**
* Resolve alias
*/
function resolveAlias(
alias: IconifyAlias,
icons: IconifyIcons,
aliases: IconifyAliases,
level = 0
): IconifyIcon | null {
const parent = alias.parent;
if (icons[parent] !== void 0) {
return mergeIcons(icons[parent], (alias as unknown) as IconifyIcon);
}
if (aliases[parent] !== void 0) {
if (level > 2) {
// icon + alias + alias + alias = too much nesting, possibly infinite
return null;
}
const icon = resolveAlias(aliases[parent], icons, aliases, level + 1);
if (icon) {
return mergeIcons(icon, (alias as unknown) as IconifyIcon);
}
}
return null;
}
/**
* Extract icons from an icon set
*/
export function parseIconSet(
data: IconifyJSON,
callback: SplitIconSetCallback,
list: AddIconSetTracking = 'none'
): boolean | string[] {
const added: string[] = [];
// Must be an object
if (typeof data !== 'object') {
return list === 'none' ? false : added;
}
// Check for missing icons list returned by API
if (data.not_found instanceof Array) {
data.not_found.forEach((name) => {
callback(name, null);
if (list === 'all') {
added.push(name);
}
});
}
// Must have 'icons' object
if (typeof data.icons !== 'object') {
return list === 'none' ? false : added;
}
// Get default values
const defaults = Object.create(null);
defaultsKeys.forEach((key) => {
if (data[key] !== void 0 && typeof data[key] !== 'object') {
defaults[key] = data[key];
}
});
// Get icons
const icons = data.icons;
Object.keys(icons).forEach((name) => {
const icon = icons[name];
if (typeof icon.body !== 'string') {
return;
}
// Freeze icon to make sure it will not be modified
callback(name, Object.freeze(merge(iconDefaults, defaults, icon)));
added.push(name);
});
// Get aliases
if (typeof data.aliases === 'object') {
const aliases = data.aliases;
Object.keys(aliases).forEach((name) => {
const icon = resolveAlias(aliases[name], icons, aliases, 1);
if (icon) {
// Freeze icon to make sure it will not be modified
callback(
name,
Object.freeze(merge(iconDefaults, defaults, icon))
);
added.push(name);
}
});
}
return list === 'none' ? added.length > 0 : added;
}

View File

@ -1,19 +1,6 @@
import {
IconifyJSON,
IconifyIcon,
IconifyOptional,
IconifyIcons,
IconifyAliases,
IconifyAlias,
} from '@iconify/types';
import { FullIconifyIcon, iconDefaults, fullIcon } from '../icon';
import { mergeIcons } from '../icon/merge';
import { merge } from '../misc/merge';
/**
* Get list of defaults keys
*/
const defaultsKeys = Object.keys(iconDefaults) as (keyof IconifyOptional)[];
import { IconifyJSON, IconifyIcon } from '@iconify/types';
import { FullIconifyIcon, fullIcon } from '../icon';
import { AddIconSetTracking, parseIconSet } from '../icon/icon-set';
/**
* List of icons
@ -63,42 +50,6 @@ export function getStorage(provider: string, prefix: string): IconStorage {
return providerStorage[prefix];
}
/**
* Resolve alias
*/
function resolveAlias(
alias: IconifyAlias,
icons: IconifyIcons,
aliases: IconifyAliases,
level = 0
): IconifyIcon | null {
const parent = alias.parent;
if (icons[parent] !== void 0) {
return mergeIcons(icons[parent], (alias as unknown) as IconifyIcon);
}
if (aliases[parent] !== void 0) {
if (level > 2) {
// icon + alias + alias + alias = too much nesting, possibly infinite
throw new Error('Invalid alias');
}
const icon = resolveAlias(aliases[parent], icons, aliases, level + 1);
if (icon) {
return mergeIcons(icon, (alias as unknown) as IconifyIcon);
}
}
return null;
}
/**
* What to track when adding icon set:
*
* none - do not track anything, return true on success
* added - track added icons, return list of added icons on success
* all - track added and missing icons, return full list on success
*/
export type AddIconSetTracking = 'none' | 'added' | 'all';
/**
* Add icon set to storage
*
@ -109,76 +60,18 @@ export function addIconSet(
data: IconifyJSON,
list: AddIconSetTracking = 'none'
): boolean | string[] {
const added: string[] = [];
try {
// Must be an object
if (typeof data !== 'object') {
return false;
}
// Check for missing icons list returned by API
if (data.not_found instanceof Array) {
const t = Date.now();
data.not_found.forEach((name) => {
const t = Date.now();
return parseIconSet(
data,
(name, icon: FullIconifyIcon | null) => {
if (icon === null) {
storage.missing[name] = t;
if (list === 'all') {
added.push(name);
}
});
}
// Must have 'icons' object
if (typeof data.icons !== 'object') {
return false;
}
// Get default values
const defaults = Object.create(null);
defaultsKeys.forEach((key) => {
if (data[key] !== void 0 && typeof data[key] !== 'object') {
defaults[key] = data[key];
} else {
storage.icons[name] = icon;
}
});
// Get icons
const icons = data.icons;
Object.keys(icons).forEach((name) => {
const icon = icons[name];
if (typeof icon.body !== 'string') {
throw new Error('Invalid icon');
}
// Freeze icon to make sure it will not be modified
storage.icons[name] = Object.freeze(
merge(iconDefaults, defaults, icon)
);
if (list !== 'none') {
added.push(name);
}
});
// Get aliases
if (typeof data.aliases === 'object') {
const aliases = data.aliases;
Object.keys(aliases).forEach((name) => {
const icon = resolveAlias(aliases[name], icons, aliases, 1);
if (icon) {
// Freeze icon to make sure it will not be modified
storage.icons[name] = Object.freeze(
merge(iconDefaults, defaults, icon)
);
if (list !== 'none') {
added.push(name);
}
}
});
}
} catch (err) {
return false;
}
return list === 'none' ? true : added;
},
list
);
}
/**

View File

@ -0,0 +1,396 @@
import 'mocha';
import { expect } from 'chai';
import { parseIconSet } from '../../lib/icon/icon-set';
import { FullIconifyIcon } from '../../lib/icon';
import { IconifyJSON } from '@iconify/types';
describe('Testing parsing icon set', () => {
it('Simple icon set', () => {
// Names list
const names: string[] = ['missing', 'icon1', 'icon2'];
// Resolved data
const expected: Record<string, FullIconifyIcon | null> = {
icon1: {
body: '<path d="icon1" />',
width: 20,
height: 24,
top: 0,
left: 0,
hFlip: false,
vFlip: false,
rotate: 0,
},
icon2: {
body: '<path d="icon2" />',
width: 24,
height: 24,
top: 0,
left: 0,
hFlip: false,
vFlip: false,
rotate: 0,
},
missing: null,
};
// Do stuff
expect(
parseIconSet(
{
prefix: 'foo',
not_found: ['missing'],
icons: {
icon1: {
body: '<path d="icon1" />',
width: 20,
},
icon2: {
body: '<path d="icon2" />',
width: 24,
},
},
height: 24,
},
(name, data) => {
// Make sure name matches
expect(names.length).to.be.at.least(1);
expect(name).to.be.equal(names.shift());
// Check icon data
expect(data).to.be.eql(expected[name]);
}
)
).to.be.equal(true);
// All names should have been parsed
expect(names).to.be.eql([], 'Not all icons have been parsed');
});
it('Aliases', () => {
// Names list
const names: string[] = ['icon1', 'icon2', 'alias1', 'alias2'];
const namesCopy = names.slice(0);
// Resolved data
const expected: Record<string, FullIconifyIcon | null> = {
icon1: {
body: '<path d="icon1" />',
width: 20,
height: 24,
top: 0,
left: 0,
hFlip: false,
vFlip: false,
rotate: 0,
},
icon2: {
body: '<path d="icon2" />',
width: 24,
height: 24,
top: 0,
left: 0,
hFlip: false,
vFlip: true,
rotate: 3,
},
alias1: {
body: '<path d="icon1" />',
width: 20,
height: 24,
top: 0,
left: 0,
hFlip: false,
vFlip: false,
rotate: 0,
},
alias2: {
body: '<path d="icon2" />',
width: 20,
height: 24,
top: 0,
left: 0,
hFlip: true,
vFlip: true,
rotate: 3,
},
};
// Do stuff
expect(
parseIconSet(
{
prefix: 'foo',
icons: {
icon1: {
body: '<path d="icon1" />',
width: 20,
},
icon2: {
body: '<path d="icon2" />',
width: 24,
vFlip: true,
rotate: 3,
},
},
aliases: {
alias1: {
parent: 'icon1',
},
alias2: {
parent: 'icon2',
hFlip: true,
width: 20,
},
},
height: 24,
},
(name, data) => {
// Make sure name matches
expect(names.length).to.be.at.least(1);
expect(name).to.be.equal(names.shift());
// Check icon data
expect(data).to.be.eql(expected[name]);
},
'all'
)
).to.be.eql(namesCopy);
// All names should have been parsed
expect(names).to.be.eql([], 'Not all icons have been parsed');
});
it('Nested aliases', () => {
// Names list
const names: string[] = [
'icon1',
'icon2',
'alias2a',
'alias2f',
'alias2z',
'alias2z3',
];
const namesCopy = names.slice(0);
// Resolved data
const expected: Record<string, FullIconifyIcon | null> = {
icon1: {
body: '<path d="icon1" />',
width: 20,
height: 20,
top: 0,
left: 0,
hFlip: false,
vFlip: false,
rotate: 0,
},
icon2: {
body: '<path d="icon2" />',
width: 24,
height: 24,
top: 0,
left: 0,
hFlip: true,
vFlip: false,
rotate: 1,
},
alias2f: {
// icon2 + alias2f
body: '<path d="icon2" />',
width: 22,
height: 24,
top: 0,
left: 0,
hFlip: false,
vFlip: true,
rotate: 2,
},
alias2a: {
// icon2 + alias2f + alias2a
body: '<path d="icon2" />',
width: 20,
height: 20,
top: 0,
left: 0,
hFlip: false,
vFlip: true,
rotate: 2,
},
alias2z: {
// icon2 + alias2f + alias2z
body: '<path d="icon2" />',
width: 21,
height: 24,
top: 0,
left: 0,
hFlip: false,
vFlip: true,
rotate: 1,
},
alias2z3: {
// icon2 + alias2f + alias2z
body: '<path d="icon2" />',
width: 21,
height: 24,
top: 0,
left: 0,
hFlip: false,
vFlip: true,
rotate: 1,
},
};
// Do stuff
expect(
parseIconSet(
{
prefix: 'foo',
icons: {
icon1: {
body: '<path d="icon1" />',
width: 20,
height: 20,
},
icon2: {
body: '<path d="icon2" />',
width: 24,
rotate: 1,
hFlip: true,
},
},
aliases: {
alias2a: {
// Alias before parent
parent: 'alias2f',
width: 20,
height: 20,
},
alias2f: {
parent: 'icon2',
width: 22,
rotate: 1,
hFlip: true,
vFlip: true,
},
alias2z: {
// Alias after parent
parent: 'alias2f',
width: 21,
rotate: 3,
},
alias2z3: {
// 3 parents: alias2z, alias2f, icon2
parent: 'alias2z',
},
alias2z4: {
// 4 parents: alias2z3, alias2z, alias2f, icon2
// nesting is too deep and should not be parsed
parent: 'alias2z3',
},
alias3: {
// invalid parent
parent: 'icon3',
},
},
height: 24,
},
(name, data) => {
// Make sure name matches
expect(names.length).to.be.at.least(
1,
`Unexpected icon ${name}`
);
expect(name).to.be.equal(
names.shift(),
`Expected icon ${name} to be next`
);
// Check icon data
expect(data).to.be.eql(
expected[name],
`Invalid data for ${name}`
);
},
'added'
)
).to.be.eql(namesCopy);
// All names should have been parsed
expect(names).to.be.eql([], 'Not all icons have been parsed');
});
it('Invalid default values', () => {
// Names list
const names: string[] = ['icon1', 'icon2'];
const namesCopy = names.slice(0);
// Resolved data
const expected: Record<string, FullIconifyIcon | null> = {
icon1: {
body: '<path d="icon1" />',
width: 20,
height: 20,
top: 0,
left: 0,
hFlip: false,
vFlip: false,
rotate: 0,
},
icon2: {
body: '<path d="icon2" />',
width: 24,
height: 24,
top: 0,
left: 0,
hFlip: false,
vFlip: false,
rotate: 0,
},
};
const iconSet: IconifyJSON = ({
icons: {
icon1: {
body: '<path d="icon1" />',
width: 20,
// Default should not override this
height: 20,
},
icon2: {
body: '<path d="icon2" />',
width: 24,
},
icon3: {
// Missing 'body'
width: 24,
},
},
height: 24,
// Objects should be ignored. Not testing other types because validation is done only for objects
rotate: {
foo: 1,
},
hFlip: null,
} as unknown) as IconifyJSON;
// Do stuff
expect(
parseIconSet(
iconSet,
(name, data) => {
// Make sure name matches
expect(names.length).to.be.at.least(1);
expect(name).to.be.equal(names.shift());
// Check icon data
expect(data).to.be.eql(expected[name]);
},
'all'
)
).to.be.eql(namesCopy);
// All names should have been parsed
expect(names).to.be.eql([], 'Not all icons have been parsed');
});
});

View File

@ -11,7 +11,6 @@ import {
listIcons,
} from '../../lib/storage';
import { FullIconifyIcon, IconifyIcon } from '../../lib/icon';
import { IconifyJSON } from '@iconify/types';
describe('Testing storage', () => {
it('Adding icon', () => {
@ -139,270 +138,6 @@ describe('Testing storage', () => {
expect(getIcon(storage, 'missing')).to.be.equal(null);
});
it('Icon set with invalid default values', () => {
const storage = newStorage('', 'foo');
// Missing prefix, invalid default values
expect(
addIconSet(storage, ({
icons: {
icon1: {
body: '<path d="icon1" />',
width: 20,
// Default should not override this
height: 20,
},
icon2: {
body: '<path d="icon2" />',
width: 24,
},
icon3: {
// Missing 'body'
width: 24,
},
},
height: 24,
// Objects should be ignored. Not testing other types because validation is done only for objects
rotate: {
foo: 1,
},
hFlip: null,
} as unknown) as IconifyJSON)
// Should return false because of exception, but still add icon1 and icon2 before failing on icon3
).to.be.equal(false);
expect(Object.keys(storage.icons)).to.be.eql(['icon1', 'icon2']);
// Test iconExists
expect(iconExists(storage, 'icon1')).to.be.equal(true);
expect(iconExists(storage, 'icon2')).to.be.equal(true);
expect(iconExists(storage, 'invalid')).to.be.equal(false);
expect(iconExists(storage, 'missing')).to.be.equal(false);
// Test getIcon
let expected: FullIconifyIcon = {
body: '<path d="icon1" />',
width: 20,
height: 20,
top: 0,
left: 0,
hFlip: false,
vFlip: false,
rotate: 0,
};
expect(getIcon(storage, 'icon1')).to.be.eql(expected);
expected = {
body: '<path d="icon2" />',
width: 24,
height: 24,
top: 0,
left: 0,
hFlip: false,
vFlip: false,
rotate: 0,
};
expect(getIcon(storage, 'icon2')).to.be.eql(expected);
expect(getIcon(storage, 'invalid')).to.be.equal(null);
expect(getIcon(storage, 'missing')).to.be.equal(null);
});
it('Icon set with simple aliases', () => {
const storage = newStorage('', 'foo');
expect(
addIconSet(storage, {
prefix: 'foo',
icons: {
icon1: {
body: '<path d="icon1" />',
width: 20,
height: 20,
},
icon2: {
body: '<path d="icon2" />',
width: 24,
rotate: 1,
hFlip: true,
},
},
aliases: {
alias1: {
parent: 'icon1',
},
alias2: {
parent: 'icon2',
rotate: 1,
hFlip: true,
vFlip: true,
},
alias3: {
parent: 'icon3',
},
},
height: 24,
})
).to.be.equal(true);
expect(Object.keys(storage.icons)).to.be.eql([
'icon1',
'icon2',
'alias1',
'alias2',
]);
// Test getIcon
let expected: FullIconifyIcon = {
body: '<path d="icon1" />',
width: 20,
height: 20,
top: 0,
left: 0,
hFlip: false,
vFlip: false,
rotate: 0,
};
expect(getIcon(storage, 'alias1')).to.be.eql(expected);
expected = {
body: '<path d="icon2" />',
width: 24,
height: 24,
top: 0,
left: 0,
hFlip: false,
vFlip: true,
rotate: 2,
};
expect(getIcon(storage, 'alias2')).to.be.eql(expected);
expect(getIcon(storage, 'alias3')).to.be.equal(null);
});
it('Icon set with nested aliases', () => {
const storage = newStorage('', 'foo');
expect(
addIconSet(storage, {
prefix: 'foo',
icons: {
icon1: {
body: '<path d="icon1" />',
width: 20,
height: 20,
},
icon2: {
body: '<path d="icon2" />',
width: 24,
rotate: 1,
hFlip: true,
},
},
aliases: {
alias2a: {
// Alias before parent
parent: 'alias2f',
width: 20,
height: 20,
},
alias2f: {
parent: 'icon2',
width: 22,
rotate: 1,
hFlip: true,
vFlip: true,
},
alias2z: {
// Alias after parent
parent: 'alias2f',
width: 21,
rotate: 3,
},
alias2z3: {
// 3 parents: alias2z, alias2f, icon2
parent: 'alias2z',
},
alias2z4: {
// 4 parents: alias2z3, alias2z, alias2f, icon2
parent: 'alias2z3',
},
},
height: 24,
})
// Should have thrown exception on 'alias2z4'
).to.be.equal(false);
expect(Object.keys(storage.icons)).to.be.eql([
'icon1',
'icon2',
'alias2a',
'alias2f',
'alias2z',
'alias2z3',
]);
// Test icon
let expected: FullIconifyIcon = {
body: '<path d="icon2" />',
width: 24,
height: 24,
top: 0,
left: 0,
hFlip: true,
vFlip: false,
rotate: 1,
};
expect(getIcon(storage, 'icon2')).to.be.eql(expected);
// Test simple alias
expected = {
body: '<path d="icon2" />',
width: 22,
height: 24,
top: 0,
left: 0,
hFlip: false,
vFlip: true,
rotate: 2,
};
expect(getIcon(storage, 'alias2f')).to.be.eql(expected);
// Test nested aliases
expected = {
body: '<path d="icon2" />',
width: 21,
height: 24,
top: 0,
left: 0,
hFlip: false,
vFlip: true,
rotate: 1, // 5
};
expect(getIcon(storage, 'alias2z')).to.be.eql(expected);
expected = {
body: '<path d="icon2" />',
width: 20,
height: 20,
top: 0,
left: 0,
hFlip: false,
vFlip: true,
rotate: 2,
};
expect(getIcon(storage, 'alias2a')).to.be.eql(expected);
// 3 levels
expected = {
body: '<path d="icon2" />',
width: 21,
height: 24,
top: 0,
left: 0,
hFlip: false,
vFlip: true,
rotate: 1, // 5
};
expect(getIcon(storage, 'alias2z3')).to.be.eql(expected);
});
it('Icon set with aliases that use transformations', () => {
const storage = newStorage('iconify', 'arty-animated');
const iconBody =