2
0
mirror of https://github.com/iconify/iconify.git synced 2025-02-13 00:58:30 +00:00

Add icon set info conversion function to Utils package

This commit is contained in:
Vjacheslav Trushkin 2021-10-05 15:02:06 +03:00
parent 1a67950032
commit f8d9f4bc00
4 changed files with 767 additions and 1 deletions

View File

@ -2,7 +2,7 @@
"name": "@iconify/utils",
"description": "Common functions for working with Iconify icon sets used by various packages.",
"author": "Vjacheslav Trushkin",
"version": "1.0.10",
"version": "1.0.11",
"license": "MIT",
"bugs": "https://github.com/iconify/iconify/issues",
"homepage": "https://iconify.design/",
@ -46,6 +46,10 @@
"require": "./lib/customisations/shorthand.js",
"import": "./lib/customisations/shorthand.mjs"
},
"./lib/icon-set/convert-info": {
"require": "./lib/icon-set/convert-info.js",
"import": "./lib/icon-set/convert-info.mjs"
},
"./lib/icon-set/expand": {
"require": "./lib/icon-set/expand.js",
"import": "./lib/icon-set/expand.mjs"

View File

@ -0,0 +1,263 @@
import { convertIconSetInfo } from '@iconify/utils/lib/icon-set/convert-info';
describe('Testing convertIconSetInfo', () => {
it('Simple info', () => {
let result, expected;
// Empty block
result = convertIconSetInfo({});
expect(result).toBeNull();
// Block with name
result = convertIconSetInfo({
name: 'Foo',
});
expected = {
name: 'Foo',
version: '',
total: 0,
author: {
name: 'Unknown',
url: '',
},
license: {
title: 'Unknown',
spdx: '',
url: '',
},
samples: [],
palette: false,
category: '',
};
expect(result).toEqual(expected);
// Block with name passed as "title" and prefix
result = convertIconSetInfo({
prefix: 'foo',
title: 'Foo',
});
expect(result).toEqual(expected);
// Mismatched prefixes
result = convertIconSetInfo(
{
name: 'Foo',
prefix: 'bar',
},
'foo'
);
expect(result).toEqual(null);
// Author in old format
// License in old format
// Height as string
// Long samples list (limited to 3)
// Palette as boolean
result = convertIconSetInfo({
prefix: 'foo',
title: 'Foo',
author: 'Author',
url: 'https://localhost/',
license: 'MIT',
licenseID: 'MIT',
licenseURL: 'https://license.local/',
height: '24',
samples: ['arrow-left', 'arrow-right', 'arrow-up', 'arrow-down'],
palette: true,
});
expected = {
name: 'Foo',
version: '',
total: 0,
author: {
name: 'Author',
url: 'https://localhost/',
},
license: {
title: 'MIT',
spdx: 'MIT',
url: 'https://license.local/',
},
height: 24,
samples: ['arrow-left', 'arrow-right', 'arrow-up'],
displayHeight: 24,
palette: true,
category: '',
};
expect(result).toEqual(expected);
// Author in new format, missing optional fields
// License in new format, missing optional fields
// Height as array of numbers and strings
// Palette as string
// Total as string
result = convertIconSetInfo({
prefix: 'foo',
title: 'Foo',
total: '100',
author: {
name: 'Author',
},
license: {
title: 'BSD',
},
height: [16, '18'],
palette: 'Colorful',
});
expected = {
name: 'Foo',
version: '',
total: 100,
author: {
name: 'Author',
url: '',
},
license: {
title: 'BSD',
spdx: '',
url: '',
},
height: [16, 18],
samples: [],
palette: true,
category: '',
};
expect(result).toEqual(expected);
// All data in new format
result = convertIconSetInfo({
name: 'Foo',
version: '1.0.0',
total: 100,
author: {
name: 'Author',
url: 'https://author.local/',
},
license: {
title: 'BSD',
spdx: 'BSD',
url: 'https://license.local/',
},
height: 32,
displayHeight: 24,
samples: ['home', 'pin', 'alert'],
palette: false,
category: 'Thematic',
});
expected = {
name: 'Foo',
version: '1.0.0',
total: 100,
author: {
name: 'Author',
url: 'https://author.local/',
},
license: {
title: 'BSD',
spdx: 'BSD',
url: 'https://license.local/',
},
height: 32,
displayHeight: 24,
samples: ['home', 'pin', 'alert'],
palette: false,
category: 'Thematic',
};
expect(result).toEqual(expected);
});
it('Testing legacy format', () => {
// Test "info" field from ant-design.json
const raw = {
name: 'Ant Design Icons',
total: 728,
author: 'HeskeyBaozi',
url: 'https://github.com/ant-design/ant-design-icons',
license: 'MIT',
height: 16,
samples: ['pushpin', 'pie-chart-outline', 'user-add-outline'],
palette: 'Colorless',
category: 'General',
};
const result = convertIconSetInfo(raw);
const expected = {
name: 'Ant Design Icons',
version: '',
total: 728,
author: {
name: 'HeskeyBaozi',
url: 'https://github.com/ant-design/ant-design-icons',
},
license: {
title: 'MIT',
spdx: '',
url: '',
},
height: 16,
samples: ['pushpin', 'pie-chart-outline', 'user-add-outline'],
displayHeight: 16,
palette: false,
category: 'General',
};
expect(result).toEqual(expected);
});
it('Testing FontAwesome 4', () => {
// Test "info" field from legacy fa.json
const raw = {
name: 'Font Awesome 4',
total: 678,
author: 'Dave Gandy',
url: 'http://fontawesome.io/',
license: 'Open Font License',
licenseURL:
'http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL',
samples: ['wrench', 'bell-o', 'user-o'],
version: '4.7.0',
palette: 'Colorless',
category: 'General',
};
const result = convertIconSetInfo(raw);
const expected = {
name: 'Font Awesome 4',
total: 678,
author: {
name: 'Dave Gandy',
url: 'http://fontawesome.io/',
},
license: {
title: 'Open Font License',
spdx: '',
url: 'http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL',
},
samples: ['wrench', 'bell-o', 'user-o'],
version: '4.7.0',
palette: false,
category: 'General',
};
expect(result).toEqual(expected);
});
it('Already converted item', () => {
const item = {
name: 'Font Awesome 4',
total: 678,
author: {
name: 'Dave Gandy',
url: 'http://fontawesome.io/',
},
license: {
title: 'Open Font License',
spdx: '',
url: 'http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL',
},
samples: ['wrench', 'bell-o', 'user-o'],
version: '4.7.0',
palette: false,
category: 'General',
};
const result = convertIconSetInfo(item);
expect(result).toEqual(item);
});
});

View File

@ -0,0 +1,235 @@
import type { IconifyInfo } from '@iconify/types';
const minDisplayHeight = 16;
const maxDisplayHeight = 24;
/**
* Item provided by API or loaded from collections.json, slightly different from IconifyInfo
*/
export interface LegacyIconifyInfo {
// Icon set name.
name: string;
// Total number of icons.
total?: number;
// Version string.
version?: string;
// Author
author?: string;
url?: string;
// License
license?: string;
licenseURL?: string;
licenseSPDX?: string;
// Samples
samples?: string[];
// Icon grid
height?: number | number[];
displayHeight?: number;
// Category
category?: string;
palette?: 'Colorless' | 'Colorful';
}
/**
* Convert data to valid CollectionInfo
*/
export function convertIconSetInfo(
data: unknown,
expectedPrefix = ''
): IconifyInfo | null {
if (typeof data !== 'object' || data === null) {
return null;
}
const source = data as Record<string, unknown>;
const getSourceNestedString = (
field: string,
key: string,
defaultValue = ''
): string => {
if (typeof source[field] !== 'object') {
return defaultValue;
}
const obj = source[field] as Record<string, string>;
return typeof obj[key] === 'string' ? obj[key] : defaultValue;
};
// Get name
let name: string;
if (typeof source.name === 'string') {
name = source.name as string;
} else if (typeof source.title === 'string') {
name = source.title as string;
} else {
return null;
}
// Validate prefix
if (
expectedPrefix !== '' &&
typeof source.prefix === 'string' &&
source.prefix !== expectedPrefix
) {
// Prefixes do not match
return null;
}
// Generate data
const result: IconifyInfo = {
name: name,
total: typeof source.total === 'number' ? source.total : 0,
version: typeof source.version === 'string' ? source.version : '',
author: {
name: getSourceNestedString(
'author',
'name',
typeof source.author === 'string' ? source.author : 'Unknown'
),
url: getSourceNestedString('author', 'url', ''),
},
license: {
title: getSourceNestedString(
'license',
'title',
typeof source.license === 'string' ? source.license : 'Unknown'
),
spdx: getSourceNestedString('license', 'spdx', ''),
url: getSourceNestedString('license', 'url', ''),
},
samples: [],
category: typeof source.category === 'string' ? source.category : '',
palette: typeof source.palette === 'boolean' ? source.palette : false,
};
// Total as string
if (typeof source.total === 'string') {
const num = parseInt(source.total);
if (num > 0) {
result.total = num;
}
}
// Add samples
if (source.samples instanceof Array) {
source.samples.forEach((item) => {
if (result.samples.length < 3 && typeof item === 'string') {
result.samples.push(item);
}
});
}
// Add height
if (
typeof source.height === 'number' ||
typeof source.height === 'string'
) {
const num = parseInt(source.height as string);
if (num > 0) {
result.height = num;
}
}
if (source.height instanceof Array) {
source.height.forEach((item) => {
const num = parseInt(item);
if (num > 0) {
if (!(result.height instanceof Array)) {
result.height = [];
}
result.height.push(num);
}
});
switch ((result.height as number[]).length) {
case 0:
delete result.height;
break;
case 1:
result.height = (result.height as number[])[0];
}
}
// Add display height
if (typeof result.height === 'number') {
// Convert from height
result.displayHeight = result.height;
while (result.displayHeight < minDisplayHeight) {
result.displayHeight *= 2;
}
while (result.displayHeight > maxDisplayHeight) {
result.displayHeight /= 2;
}
if (
result.displayHeight !== Math.round(result.displayHeight) ||
result.displayHeight < minDisplayHeight ||
result.displayHeight > maxDisplayHeight
) {
delete result.displayHeight;
}
}
if (
typeof source.displayHeight === 'number' ||
typeof source.displayHeight === 'string'
) {
// Convert from source.displayHeight
const num = parseInt(source.displayHeight as string);
if (
num >= minDisplayHeight &&
num <= maxDisplayHeight &&
Math.round(num) === num
) {
result.displayHeight = num;
}
}
// Convert palette from string value
if (typeof source.palette === 'string') {
switch (source.palette.toLowerCase()) {
case 'colorless': // Iconify v1
case 'false': // Boolean as string
result.palette = false;
break;
case 'colorful': // Iconify v1
case 'true': // Boolean as string
result.palette = true;
}
}
// Parse all old keys
Object.keys(source).forEach((key) => {
const value = source[key];
if (typeof value !== 'string') {
return;
}
switch (key) {
case 'url':
case 'uri':
result.author.url = value;
break;
case 'licenseURL':
case 'licenseURI':
result.license.url = value;
break;
case 'licenseID':
case 'licenseSPDX':
result.license.spdx = value;
break;
}
});
return result;
}

View File

@ -0,0 +1,264 @@
import type { IconifyInfo } from '@iconify/types';
import { convertIconSetInfo } from '../lib/icon-set/convert-info';
describe('Testing convertIconSetInfo', () => {
test('Simple info', () => {
let result, expected: IconifyInfo;
// Empty block
result = convertIconSetInfo({});
expect(result).toBeNull();
// Block with name
result = convertIconSetInfo({
name: 'Foo',
});
expected = {
name: 'Foo',
version: '',
total: 0,
author: {
name: 'Unknown',
url: '',
},
license: {
title: 'Unknown',
spdx: '',
url: '',
},
samples: [],
palette: false,
category: '',
};
expect(result).toEqual(expected);
// Block with name passed as "title" and prefix
result = convertIconSetInfo({
prefix: 'foo',
title: 'Foo',
});
expect(result).toEqual(expected);
// Mismatched prefixes
result = convertIconSetInfo(
{
name: 'Foo',
prefix: 'bar',
},
'foo'
);
expect(result).toEqual(null);
// Author in old format
// License in old format
// Height as string
// Long samples list (limited to 3)
// Palette as boolean
result = convertIconSetInfo({
prefix: 'foo',
title: 'Foo',
author: 'Author',
url: 'https://localhost/',
license: 'MIT',
licenseID: 'MIT',
licenseURL: 'https://license.local/',
height: '24',
samples: ['arrow-left', 'arrow-right', 'arrow-up', 'arrow-down'],
palette: true,
});
expected = {
name: 'Foo',
version: '',
total: 0,
author: {
name: 'Author',
url: 'https://localhost/',
},
license: {
title: 'MIT',
spdx: 'MIT',
url: 'https://license.local/',
},
height: 24,
samples: ['arrow-left', 'arrow-right', 'arrow-up'],
displayHeight: 24,
palette: true,
category: '',
};
expect(result).toEqual(expected);
// Author in new format, missing optional fields
// License in new format, missing optional fields
// Height as array of numbers and strings
// Palette as string
// Total as string
result = convertIconSetInfo({
prefix: 'foo',
title: 'Foo',
total: '100',
author: {
name: 'Author',
},
license: {
title: 'BSD',
},
height: [16, '18'],
palette: 'Colorful',
});
expected = {
name: 'Foo',
version: '',
total: 100,
author: {
name: 'Author',
url: '',
},
license: {
title: 'BSD',
spdx: '',
url: '',
},
height: [16, 18],
samples: [],
palette: true,
category: '',
};
expect(result).toEqual(expected);
// All data in new format
result = convertIconSetInfo({
name: 'Foo',
version: '1.0.0',
total: 100,
author: {
name: 'Author',
url: 'https://author.local/',
},
license: {
title: 'BSD',
spdx: 'BSD',
url: 'https://license.local/',
},
height: 32,
displayHeight: 24,
samples: ['home', 'pin', 'alert'],
palette: false,
category: 'Thematic',
});
expected = {
name: 'Foo',
version: '1.0.0',
total: 100,
author: {
name: 'Author',
url: 'https://author.local/',
},
license: {
title: 'BSD',
spdx: 'BSD',
url: 'https://license.local/',
},
height: 32,
displayHeight: 24,
samples: ['home', 'pin', 'alert'],
palette: false,
category: 'Thematic',
};
expect(result).toEqual(expected);
});
test('Testing legacy format', () => {
// Test "info" field from ant-design.json
const raw = {
name: 'Ant Design Icons',
total: 728,
author: 'HeskeyBaozi',
url: 'https://github.com/ant-design/ant-design-icons',
license: 'MIT',
height: 16,
samples: ['pushpin', 'pie-chart-outline', 'user-add-outline'],
palette: 'Colorless',
category: 'General',
};
const result = convertIconSetInfo(raw);
const expected: IconifyInfo = {
name: 'Ant Design Icons',
version: '',
total: 728,
author: {
name: 'HeskeyBaozi',
url: 'https://github.com/ant-design/ant-design-icons',
},
license: {
title: 'MIT',
spdx: '',
url: '',
},
height: 16,
samples: ['pushpin', 'pie-chart-outline', 'user-add-outline'],
displayHeight: 16,
palette: false,
category: 'General',
};
expect(result).toEqual(expected);
});
test('Testing FontAwesome 4', () => {
// Test "info" field from legacy fa.json
const raw = {
name: 'Font Awesome 4',
total: 678,
author: 'Dave Gandy',
url: 'http://fontawesome.io/',
license: 'Open Font License',
licenseURL:
'http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL',
samples: ['wrench', 'bell-o', 'user-o'],
version: '4.7.0',
palette: 'Colorless',
category: 'General',
};
const result = convertIconSetInfo(raw);
const expected: IconifyInfo = {
name: 'Font Awesome 4',
total: 678,
author: {
name: 'Dave Gandy',
url: 'http://fontawesome.io/',
},
license: {
title: 'Open Font License',
spdx: '',
url: 'http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL',
},
samples: ['wrench', 'bell-o', 'user-o'],
version: '4.7.0',
palette: false,
category: 'General',
};
expect(result).toEqual(expected);
});
test('Already converted item', () => {
const item: IconifyInfo = {
name: 'Font Awesome 4',
total: 678,
author: {
name: 'Dave Gandy',
url: 'http://fontawesome.io/',
},
license: {
title: 'Open Font License',
spdx: '',
url: 'http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL',
},
samples: ['wrench', 'bell-o', 'user-o'],
version: '4.7.0',
palette: false,
category: 'General',
};
const result = convertIconSetInfo(item);
expect(result).toEqual(item);
});
});