From ef2273857454cea861d1ff8ee5de5ef1a6efea2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 15:12:13 +0100 Subject: [PATCH 01/17] feat: split modern loader for node and browser support --- packages/utils/package.json | 12 ++++ packages/utils/src/index.ts | 6 +- packages/utils/src/loader/custom.ts | 14 ++-- packages/utils/src/loader/fs.ts | 39 +++++++++++ packages/utils/src/loader/install-pkg.ts | 42 ++++++++++++ packages/utils/src/loader/loader.ts | 65 ++++++++++++++++++ packages/utils/src/loader/modern.ts | 52 ++------------- packages/utils/src/loader/types.ts | 49 +++++++++++++- packages/utils/src/loader/utils.ts | 62 ++++++----------- packages/utils/tests/get-custom-icon-test.ts | 8 ++- packages/utils/tests/load-icon-test.ts | 70 ++++++++++++++++++++ 11 files changed, 313 insertions(+), 106 deletions(-) create mode 100644 packages/utils/src/loader/fs.ts create mode 100644 packages/utils/src/loader/install-pkg.ts create mode 100644 packages/utils/src/loader/loader.ts create mode 100644 packages/utils/tests/load-icon-test.ts diff --git a/packages/utils/package.json b/packages/utils/package.json index 74a722f..5f12c4f 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -124,6 +124,18 @@ "require": "./lib/loader/custom.js", "import": "./lib/loader/custom.mjs" }, + "./lib/loader/fs": { + "require": "./lib/loader/fs.js", + "import": "./lib/loader/fs.mjs" + }, + "./lib/loader/install-pkg": { + "require": "./lib/loader/install-pkg.js", + "import": "./lib/loader/install-pkg.mjs" + }, + "./lib/loader/loader": { + "require": "./lib/loader/loader.js", + "import": "./lib/loader/loader.mjs" + }, "./lib/loader/loaders": { "require": "./lib/loader/loaders.js", "import": "./lib/loader/loaders.mjs" diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 495ae24..a5e2ce0 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -50,12 +50,14 @@ export type { CustomCollections, IconCustomizer, IconCustomizations, + IconifyLoaderOptions, InlineCollection, } from './loader/types'; -export { tryInstallPkg, mergeIconProps } from './loader/utils'; +export { encodeCssSvg, mergeIconProps } from './loader/utils'; +export { loadIcon } from './loader/loader'; export { FileSystemIconLoader } from './loader/loaders'; export { getCustomIcon } from './loader/custom'; -export { loadCollection, searchForIcon } from './loader/modern'; +export { searchForIcon } from './loader/modern'; // Misc export { camelize, camelToKebab, pascalize } from './misc/strings'; diff --git a/packages/utils/src/loader/custom.ts b/packages/utils/src/loader/custom.ts index 84e3ed4..6037e8a 100644 --- a/packages/utils/src/loader/custom.ts +++ b/packages/utils/src/loader/custom.ts @@ -1,9 +1,5 @@ import createDebugger from 'debug'; -import type { - CustomIconLoader, - IconCustomizations, - InlineCollection, -} from './types'; +import type { CustomIconLoader, IconifyLoaderOptions, InlineCollection } from './types'; import { mergeIconProps } from './utils'; const debug = createDebugger('@iconify-loader:custom'); @@ -15,7 +11,7 @@ export async function getCustomIcon( custom: CustomIconLoader | InlineCollection, collection: string, icon: string, - iconsCustomizations?: IconCustomizations + options?: IconifyLoaderOptions, ): Promise { let result: string | undefined | null; @@ -29,7 +25,7 @@ export async function getCustomIcon( } if (result) { - if (!result.startsWith('> = {}; +const isLegacyExists = isPackageExists('@iconify/json'); + +export async function loadCollectionFromFS(name: string, autoInstall = false): Promise { + if (!_collections[name]) { + _collections[name] = task(); + } + return _collections[name]; + + async function task() { + let jsonPath = resolveModule(`@iconify-json/${name}/icons.json`); + if (!jsonPath && isLegacyExists) { + jsonPath = resolveModule(`@iconify/json/json/${name}.json`); + } + + if (!jsonPath && !isLegacyExists && autoInstall) { + await tryInstallPkg(`@iconify-json/${name}`); + jsonPath = resolveModule(`@iconify-json/${name}/icons.json`); + } + + let stat: Stats | undefined; + try { + stat = jsonPath ? await fs.lstat(jsonPath) : undefined; + } catch (err) { + return undefined; + } + if (stat && stat.isFile()) { + return JSON.parse(await fs.readFile(jsonPath as string, 'utf8')) as IconifyJSON; + } + else { + return undefined; + } + } +} diff --git a/packages/utils/src/loader/install-pkg.ts b/packages/utils/src/loader/install-pkg.ts new file mode 100644 index 0000000..282b1e9 --- /dev/null +++ b/packages/utils/src/loader/install-pkg.ts @@ -0,0 +1,42 @@ +import { installPackage } from '@antfu/install-pkg'; +import { sleep } from '@antfu/utils'; +import { cyan, yellow } from 'kolorist'; + +const warned = new Set(); + +export function warnOnce(msg: string): void { + if (!warned.has(msg)) { + warned.add(msg); + console.warn(yellow(`[@iconify-loader] ${msg}`)); + } +} + +let pending: Promise | undefined; +const tasks: Record | undefined> = {}; + +export async function tryInstallPkg(name: string): Promise { + if (pending) { + await pending; + } + + if (!tasks[name]) { + // eslint-disable-next-line no-console + console.log(cyan(`Installing ${name}...`)); + tasks[name] = pending = installPackage(name, { + dev: true, + preferOffline: true, + }) + .then(() => sleep(300)) + // eslint-disable-next-line + .catch((e: any) => { + warnOnce(`Failed to install ${name}`); + console.error(e); + }) + .finally(() => { + pending = undefined; + }); + } + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return tasks[name]!; +} diff --git a/packages/utils/src/loader/loader.ts b/packages/utils/src/loader/loader.ts new file mode 100644 index 0000000..09a9682 --- /dev/null +++ b/packages/utils/src/loader/loader.ts @@ -0,0 +1,65 @@ +import { getCustomIcon } from './custom'; +import { isNode } from './utils'; +import { searchForIcon } from './modern'; +import { warnOnce } from './install-pkg'; +import type { IconifyLoaderOptions } from './types'; + +export async function loadIcon( + collection: string, + icon: string, + options?: IconifyLoaderOptions +): Promise { + const custom = options?.customCollections?.[collection]; + + if (custom) { + const result = await getCustomIcon(custom, collection, icon, options); + if (result) { + return result; + } + } + + return await loadNodeBuiltinIcon(collection, icon, options); +} + +async function importFsModule(): Promise { + try { + return await import('./fs'); + } catch { + try { + // cjs environments + return require('./fs.js'); + } + catch { + return undefined; + } + } +} + +async function loadNodeBuiltinIcon( + collection: string, + icon: string, + options?: IconifyLoaderOptions, + warn = true, +): Promise { + + if (isNode) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const { loadCollectionFromFS } = await importFsModule(); + const iconSet = loadCollectionFromFS(collection, options?.autoInstall); + if (iconSet) { + // possible icon names + const ids = [ + icon, + icon.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(), + icon.replace(/([a-z])(\d+)/g, '$1-$2'), + ]; + return await searchForIcon(iconSet, collection, ids, options); + } + + if (warn) { + warnOnce(`failed to load \`@iconify-json/${collection}\`, have you installed it?`); + } + } +} + diff --git a/packages/utils/src/loader/modern.ts b/packages/utils/src/loader/modern.ts index de9983a..083541b 100644 --- a/packages/utils/src/loader/modern.ts +++ b/packages/utils/src/loader/modern.ts @@ -1,70 +1,26 @@ -import { promises as fs } from 'fs'; import type { IconifyJSON } from '@iconify/types'; import type { FullIconifyIcon } from '../icon'; import { iconToSVG } from '../svg/build'; import { getIconData } from '../icon-set/get-icon'; -import { mergeIconProps, tryInstallPkg } from './utils'; +import { mergeIconProps } from './utils'; import createDebugger from 'debug'; -import { isPackageExists, resolveModule } from 'local-pkg'; import { defaults as DefaultIconCustomizations } from '../customisations'; -import type { IconCustomizations } from './types'; +import type { IconifyLoaderOptions } from './types'; const debug = createDebugger('@iconify-loader:icon'); -const debugModern = createDebugger('@iconify-loader:modern'); -const debugLegacy = createDebugger('@iconify-loader:legacy'); - -const _collections: Record> = {}; -const isLegacyExists = isPackageExists('@iconify/json'); - -export async function loadCollection( - name: string, - autoInstall = false -): Promise { - if (!_collections[name]) { - _collections[name] = task(); - } - - return _collections[name]; - - async function task(): Promise { - let jsonPath = resolveModule(`@iconify-json/${name}/icons.json`); - if (jsonPath) { - debugModern(name); - } - - if (!jsonPath && isLegacyExists) { - jsonPath = resolveModule(`@iconify/json/json/${name}.json`); - if (jsonPath) { - debugLegacy(name); - } - } - - if (!jsonPath && !isLegacyExists && autoInstall) { - await tryInstallPkg(`@iconify-json/${name}`); - jsonPath = resolveModule(`@iconify-json/${name}/icons.json`); - } - - if (jsonPath) { - return JSON.parse(await fs.readFile(jsonPath, 'utf8')); - } else { - debugModern(`failed to load ${name}`); - return undefined; - } - } -} export async function searchForIcon( iconSet: IconifyJSON, collection: string, ids: string[], - iconCustomizations?: IconCustomizations + options?: IconifyLoaderOptions, ): Promise { let iconData: FullIconifyIcon | null; const { customize, additionalProps = {}, iconCustomizer, - } = iconCustomizations || {}; + } = options?.customizations ?? {}; for (const id of ids) { iconData = getIconData(iconSet, id, true); if (iconData) { diff --git a/packages/utils/src/loader/types.ts b/packages/utils/src/loader/types.ts index 5784f3d..724658d 100644 --- a/packages/utils/src/loader/types.ts +++ b/packages/utils/src/loader/types.ts @@ -56,7 +56,7 @@ export type IconCustomizations = { }; /** - * List of icons as object. Key is icon name, value is icon data or callback (can be async) to get icon data + * List of icons as object. Key is the icon name, the value is the icon data or callback (can be async) to get icon data */ export type InlineCollection = Record< string, @@ -64,9 +64,54 @@ export type InlineCollection = Record< >; /** - * Collection of custom icons. Key is collection name, value is loader or InlineCollection object + * Collection of custom icons. Key is the collection name, the value is the loader or InlineCollection object */ export type CustomCollections = Record< string, CustomIconLoader | InlineCollection >; + +/** + * Options to use with the modern loader. + */ +export type IconifyLoaderOptions = { + /** + * Scale of icons against 1em + * + * @default 1.2 + */ + scale?: number + + /** + * Style to apply to icons by default + * + * @default '' + */ + defaultStyle?: string + + /** + * Class names to apply to icons by default + * + * @default '' + */ + defaultClass?: string + + /** + * Loader for custom loaders + */ + customCollections?: Record + + /** + * Icon customizer + */ + customizations?: IconCustomizations + + /** + * Auto install icon sources package when the usages is detected + * + * **WARNING**: only on `node` environment, on `browser` this option will be ignored + * + * @default false + */ + autoInstall?: boolean +} diff --git a/packages/utils/src/loader/utils.ts b/packages/utils/src/loader/utils.ts index 8caf7aa..5c00fc7 100644 --- a/packages/utils/src/loader/utils.ts +++ b/packages/utils/src/loader/utils.ts @@ -1,19 +1,7 @@ -import { installPackage } from '@antfu/install-pkg'; -import { Awaitable, sleep } from '@antfu/utils'; -import { cyan, yellow } from 'kolorist'; +import type { Awaitable } from '@antfu/utils'; import type { IconCustomizer } from './types'; -const warned = new Set(); - -export function warnOnce(msg: string): void { - if (!warned.has(msg)) { - warned.add(msg); - console.warn(yellow(`[@iconify-loader] ${msg}`)); - } -} - -let pending: Promise | undefined; -const tasks: Record | undefined> = {}; +export const isNode = typeof process < 'u' && typeof process.stdout < 'u' export async function mergeIconProps( svg: string, @@ -29,38 +17,28 @@ export async function mergeIconProps( const v = additionalProps[p]; if (v !== undefined && v !== null) props[p] = v; }); + // add svg xmlns if missing + if (!svg.includes(' xmlns=') && !additionalProps['xmlns']) { + additionalProps['xmlns'] = 'http://www.w3.org/2000/svg'; + } + // add xmlns:xlink if xlink present and the xmlns missing + if (!svg.includes('xmlns:xlink') && svg.includes('xlink:') && !additionalProps['xmlns:xlink']) { + additionalProps['xmlns:xlink'] = 'http://www.w3.org/1999/xlink'; + } const replacement = svg.startsWith(' `${p}="${props[p]}"`) - .join(' ')}` + `${replacement}${Object.keys(props).map((p) => `${p}="${props[p]}"`).join(' ')}` ); } -export async function tryInstallPkg(name: string): Promise { - if (pending) { - await pending; - } - - if (!tasks[name]) { - // eslint-disable-next-line no-console - console.log(cyan(`Installing ${name}...`)); - tasks[name] = pending = installPackage(name, { - dev: true, - preferOffline: true, - }) - .then(() => sleep(300)) - // eslint-disable-next-line - .catch((e: any) => { - warnOnce(`Failed to install ${name}`); - console.error(e); - }) - .finally(() => { - pending = undefined; - }); - } - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return tasks[name]!; +// https://bl.ocks.org/jennyknuth/222825e315d45a738ed9d6e04c7a88d0 +export function encodeCssSvg(svg: string): string { + return svg.replace(/"/g, '\'') + .replace(/%/g, '%25') + .replace(/#/g, '%23') + .replace(/{/g, '%7B') + .replace(/}/g, '%7D') + .replace(//g, '%3E') } diff --git a/packages/utils/tests/get-custom-icon-test.ts b/packages/utils/tests/get-custom-icon-test.ts index f2ae0fe..bbf43cb 100644 --- a/packages/utils/tests/get-custom-icon-test.ts +++ b/packages/utils/tests/get-custom-icon-test.ts @@ -13,9 +13,11 @@ describe('Testing getCustomIcon', () => { test('CustomIconLoader with transform', async () => { const svg = await fs.readFile(fixturesDir + '/circle.svg', 'utf8'); const result = await getCustomIcon(() => svg, 'a', 'b', { - transform(icon) { - return icon.replace(' -1).toBeTruthy(); expect(result && result.indexOf('height="1em"') > -1).toBeTruthy(); diff --git a/packages/utils/tests/load-icon-test.ts b/packages/utils/tests/load-icon-test.ts new file mode 100644 index 0000000..b982f41 --- /dev/null +++ b/packages/utils/tests/load-icon-test.ts @@ -0,0 +1,70 @@ +import { promises as fs } from 'fs'; +import { CustomIconLoader, loadIcon } from '../lib'; + +const fixturesDir = __dirname + '/fixtures'; + +const loader: CustomIconLoader = async(name) => { + return await fs.readFile(`${fixturesDir}/${name}.svg`, 'utf8'); +} + +describe('Testing loadIcon', () => { + + test('CustomCollection', async () => { + const svg = await loader('circle'); + expect(svg).toBeTruthy() + const result = await loadIcon('a', 'circle', { + customCollections: { + 'a': { + 'circle': svg as string, + }, + }, + }); + expect(result).toBeTruthy() + expect(svg).toEqual(result); + }); + + test('CustomCollection with transform', async () => { + const svg = await loader('circle') + expect(svg).toBeTruthy() + const result = await loadIcon('a', 'circle', { + customCollections: { + 'a': { + 'circle': svg as string, + }, + }, + customizations: { + transform(icon) { + return icon.replace(' -1).toBeTruthy(); + expect(result && result.indexOf('height="1em"') > -1).toBeTruthy(); + }); + + test('CustomCollection Icon with XML heading', async () => { + const svg = await loader('1f3eb') + expect(svg).toBeTruthy() + // Intercept console.warn + let warned = false; + const warn = console.warn; + console.warn = (/*...args*/) => { + // warn.apply(this, args); + warned = true; + }; + + const result = await loadIcon('a', '1f3eb', { + customCollections: { + 'a': { + '1f3eb': svg as string, + }, + }, + }); + // Restore console.warn + console.warn = warn; + + expect(svg).toEqual(result); + expect(warned).toEqual(true); + }); +}); From 8b7641839e536dab3664a2b97e8cee028e4ff2b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 15:39:56 +0100 Subject: [PATCH 02/17] chore: move `encodeCssSvg` to its own module --- packages/utils/src/index.ts | 3 ++- packages/utils/src/loader/utils.ts | 11 ----------- packages/utils/src/svg/encode-svg-for-css.ts | 10 ++++++++++ 3 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 packages/utils/src/svg/encode-svg-for-css.ts diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index a5e2ce0..2004380 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -39,6 +39,7 @@ export { convertIconSetInfo } from './icon-set/convert-info'; export { iconToSVG } from './svg/build'; export { replaceIDs } from './svg/id'; export { calculateSize } from './svg/size'; +export { encodeSvgForCss } from './svg/encode-svg-for-css'; // Colors export { colorKeywords } from './colors/keywords'; @@ -53,7 +54,7 @@ export type { IconifyLoaderOptions, InlineCollection, } from './loader/types'; -export { encodeCssSvg, mergeIconProps } from './loader/utils'; +export { mergeIconProps } from './loader/utils'; export { loadIcon } from './loader/loader'; export { FileSystemIconLoader } from './loader/loaders'; export { getCustomIcon } from './loader/custom'; diff --git a/packages/utils/src/loader/utils.ts b/packages/utils/src/loader/utils.ts index 5c00fc7..426d2f2 100644 --- a/packages/utils/src/loader/utils.ts +++ b/packages/utils/src/loader/utils.ts @@ -31,14 +31,3 @@ export async function mergeIconProps( `${replacement}${Object.keys(props).map((p) => `${p}="${props[p]}"`).join(' ')}` ); } - -// https://bl.ocks.org/jennyknuth/222825e315d45a738ed9d6e04c7a88d0 -export function encodeCssSvg(svg: string): string { - return svg.replace(/"/g, '\'') - .replace(/%/g, '%25') - .replace(/#/g, '%23') - .replace(/{/g, '%7B') - .replace(/}/g, '%7D') - .replace(//g, '%3E') -} diff --git a/packages/utils/src/svg/encode-svg-for-css.ts b/packages/utils/src/svg/encode-svg-for-css.ts new file mode 100644 index 0000000..8ddabe9 --- /dev/null +++ b/packages/utils/src/svg/encode-svg-for-css.ts @@ -0,0 +1,10 @@ +// https://bl.ocks.org/jennyknuth/222825e315d45a738ed9d6e04c7a88d0 +export function encodeSvgForCss(svg: string): string { + return svg.replace(/"/g, '\'') + .replace(/%/g, '%25') + .replace(/#/g, '%23') + .replace(/{/g, '%7B') + .replace(/}/g, '%7D') + .replace(//g, '%3E') +} From a1592498bb8ad3a21af675cddb8150acd4b97bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 15:57:00 +0100 Subject: [PATCH 03/17] chore: handle `defaultStyle` and `defaultClass` --- packages/utils/src/loader/loader.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/utils/src/loader/loader.ts b/packages/utils/src/loader/loader.ts index 09a9682..518bf12 100644 --- a/packages/utils/src/loader/loader.ts +++ b/packages/utils/src/loader/loader.ts @@ -18,7 +18,20 @@ export async function loadIcon( } } - return await loadNodeBuiltinIcon(collection, icon, options); + let svg = await loadNodeBuiltinIcon(collection, icon, options); + + if (svg && options) { + const { defaultStyle, defaultClass } = options + // additional props and iconCustomizer takes precedence + if (defaultClass && !svg.includes(' class=')) + svg = svg.replace(' { From f5665ede706a460e52c5a371bf562f7dbfe37729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 16:27:20 +0100 Subject: [PATCH 04/17] chore: handle `scale` option when merging svg icon props --- packages/utils/src/loader/custom.ts | 1 + packages/utils/src/loader/modern.ts | 1 + packages/utils/src/loader/utils.ts | 8 ++++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/utils/src/loader/custom.ts b/packages/utils/src/loader/custom.ts index 6037e8a..10c820f 100644 --- a/packages/utils/src/loader/custom.ts +++ b/packages/utils/src/loader/custom.ts @@ -41,6 +41,7 @@ export async function getCustomIcon( collection, icon, additionalProps, + options?.scale ?? 1.2, undefined, iconCustomizer, ); diff --git a/packages/utils/src/loader/modern.ts b/packages/utils/src/loader/modern.ts index 083541b..7913d27 100644 --- a/packages/utils/src/loader/modern.ts +++ b/packages/utils/src/loader/modern.ts @@ -37,6 +37,7 @@ export async function searchForIcon( collection, id, additionalProps, + options?.scale ?? 1.2, () => attributes, iconCustomizer ); diff --git a/packages/utils/src/loader/utils.ts b/packages/utils/src/loader/utils.ts index 426d2f2..21dbbe4 100644 --- a/packages/utils/src/loader/utils.ts +++ b/packages/utils/src/loader/utils.ts @@ -8,10 +8,14 @@ export async function mergeIconProps( collection: string, icon: string, additionalProps: Record, + scale: number, propsProvider?: () => Awaitable>, - iconCustomizer?: IconCustomizer + iconCustomizer?: IconCustomizer, ): Promise { - const props: Record = (await propsProvider?.()) ?? {}; + const props: Record = (await propsProvider?.()) ?? { + height: `${scale ?? 1}em`, + width: `${scale ?? 1}em`, + }; await iconCustomizer?.(collection, icon, props); Object.keys(additionalProps).forEach((p) => { const v = additionalProps[p]; From 308c26c7741530fc7f3ffbdc7709ff6d80134d9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 16:28:20 +0100 Subject: [PATCH 05/17] chore: add `@iconify-json/flat-color-icons` for testing --- packages/utils/package-lock.json | 19 +++++++++++++++++++ packages/utils/package.json | 1 + 2 files changed, 20 insertions(+) diff --git a/packages/utils/package-lock.json b/packages/utils/package-lock.json index 53d9381..0f58f15 100644 --- a/packages/utils/package-lock.json +++ b/packages/utils/package-lock.json @@ -17,6 +17,7 @@ "local-pkg": "^0.4.0" }, "devDependencies": { + "@iconify-json/flat-color-icons": "^1.0.2", "@iconify/library-builder": "^1.0.5", "@types/debug": "^4.1.7", "@types/jest": "^27.0.1", @@ -740,6 +741,15 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "node_modules/@iconify-json/flat-color-icons": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@iconify-json/flat-color-icons/-/flat-color-icons-1.1.0.tgz", + "integrity": "sha512-UWbgi+4T0wkOQHNKxfxfGiDevGPrgD369kzC/dHGaz6WJxlwqxxRQvb6L8tTVCLdwIcmcNCV1Wy0SKgX0R/pHw==", + "dev": true, + "dependencies": { + "@iconify/types": "^1.0.12" + } + }, "node_modules/@iconify/library-builder": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@iconify/library-builder/-/library-builder-1.0.5.tgz", @@ -5951,6 +5961,15 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "@iconify-json/flat-color-icons": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@iconify-json/flat-color-icons/-/flat-color-icons-1.1.0.tgz", + "integrity": "sha512-UWbgi+4T0wkOQHNKxfxfGiDevGPrgD369kzC/dHGaz6WJxlwqxxRQvb6L8tTVCLdwIcmcNCV1Wy0SKgX0R/pHw==", + "dev": true, + "requires": { + "@iconify/types": "^1.0.12" + } + }, "@iconify/library-builder": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@iconify/library-builder/-/library-builder-1.0.5.tgz", diff --git a/packages/utils/package.json b/packages/utils/package.json index 5f12c4f..6248114 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -178,6 +178,7 @@ "local-pkg": "^0.4.0" }, "devDependencies": { + "@iconify-json/flat-color-icons": "^1.0.2", "@iconify/library-builder": "^1.0.5", "@types/debug": "^4.1.7", "@types/jest": "^27.0.1", From bb285192a2dc2fc00dc6919dfb23b24ec09a0f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 16:44:24 +0100 Subject: [PATCH 06/17] chore: undo handle `scale`, should be done from outside --- packages/utils/src/loader/custom.ts | 1 - packages/utils/src/loader/modern.ts | 1 - packages/utils/src/loader/utils.ts | 6 +----- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/utils/src/loader/custom.ts b/packages/utils/src/loader/custom.ts index 10c820f..6037e8a 100644 --- a/packages/utils/src/loader/custom.ts +++ b/packages/utils/src/loader/custom.ts @@ -41,7 +41,6 @@ export async function getCustomIcon( collection, icon, additionalProps, - options?.scale ?? 1.2, undefined, iconCustomizer, ); diff --git a/packages/utils/src/loader/modern.ts b/packages/utils/src/loader/modern.ts index 7913d27..083541b 100644 --- a/packages/utils/src/loader/modern.ts +++ b/packages/utils/src/loader/modern.ts @@ -37,7 +37,6 @@ export async function searchForIcon( collection, id, additionalProps, - options?.scale ?? 1.2, () => attributes, iconCustomizer ); diff --git a/packages/utils/src/loader/utils.ts b/packages/utils/src/loader/utils.ts index 21dbbe4..b50cfb3 100644 --- a/packages/utils/src/loader/utils.ts +++ b/packages/utils/src/loader/utils.ts @@ -8,14 +8,10 @@ export async function mergeIconProps( collection: string, icon: string, additionalProps: Record, - scale: number, propsProvider?: () => Awaitable>, iconCustomizer?: IconCustomizer, ): Promise { - const props: Record = (await propsProvider?.()) ?? { - height: `${scale ?? 1}em`, - width: `${scale ?? 1}em`, - }; + const props: Record = (await propsProvider?.()) ?? {}; await iconCustomizer?.(collection, icon, props); Object.keys(additionalProps).forEach((p) => { const v = additionalProps[p]; From bbe56e31996c87fe9c891cba856482b2ad2f82d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 16:58:48 +0100 Subject: [PATCH 07/17] chore: add `await` loading collection from fs --- packages/utils/src/loader/loader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/src/loader/loader.ts b/packages/utils/src/loader/loader.ts index 518bf12..222ad0c 100644 --- a/packages/utils/src/loader/loader.ts +++ b/packages/utils/src/loader/loader.ts @@ -59,7 +59,7 @@ async function loadNodeBuiltinIcon( // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const { loadCollectionFromFS } = await importFsModule(); - const iconSet = loadCollectionFromFS(collection, options?.autoInstall); + const iconSet = await loadCollectionFromFS(collection, options?.autoInstall); if (iconSet) { // possible icon names const ids = [ From 7b5ccb13b64783895906bf364382d07cde546b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 17:02:00 +0100 Subject: [PATCH 08/17] chore: add hint to keep space on `` --- packages/utils/src/loader/modern.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/utils/src/loader/modern.ts b/packages/utils/src/loader/modern.ts index 083541b..0d3e1d9 100644 --- a/packages/utils/src/loader/modern.ts +++ b/packages/utils/src/loader/modern.ts @@ -33,7 +33,8 @@ export async function searchForIcon( : defaultCustomizations ); return await mergeIconProps( - `${body}`, + // DON'T remove space on + `${body}`, collection, id, additionalProps, From 1e854f74ee28ed635f6daf45c7a60b6816cf02d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 17:20:16 +0100 Subject: [PATCH 09/17] chore: add option to add svg and xlink xml namespaces --- packages/utils/src/loader/custom.ts | 1 + packages/utils/src/loader/modern.ts | 1 + packages/utils/src/loader/types.ts | 9 +++++++-- packages/utils/src/loader/utils.ts | 24 ++++++++++++++---------- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/packages/utils/src/loader/custom.ts b/packages/utils/src/loader/custom.ts index 6037e8a..37fb304 100644 --- a/packages/utils/src/loader/custom.ts +++ b/packages/utils/src/loader/custom.ts @@ -41,6 +41,7 @@ export async function getCustomIcon( collection, icon, additionalProps, + options?.addXmlNs === true, undefined, iconCustomizer, ); diff --git a/packages/utils/src/loader/modern.ts b/packages/utils/src/loader/modern.ts index 0d3e1d9..46f7425 100644 --- a/packages/utils/src/loader/modern.ts +++ b/packages/utils/src/loader/modern.ts @@ -38,6 +38,7 @@ export async function searchForIcon( collection, id, additionalProps, + options?.addXmlNs === true, () => attributes, iconCustomizer ); diff --git a/packages/utils/src/loader/types.ts b/packages/utils/src/loader/types.ts index 724658d..ce0018c 100644 --- a/packages/utils/src/loader/types.ts +++ b/packages/utils/src/loader/types.ts @@ -76,9 +76,14 @@ export type CustomCollections = Record< */ export type IconifyLoaderOptions = { /** - * Scale of icons against 1em + * Add svg and xlink xml namespace when necessary. * - * @default 1.2 + * @default false + */ + addXmlNs?: boolean + + /** + * Scale of icons against 1em */ scale?: number diff --git a/packages/utils/src/loader/utils.ts b/packages/utils/src/loader/utils.ts index b50cfb3..4d0bb10 100644 --- a/packages/utils/src/loader/utils.ts +++ b/packages/utils/src/loader/utils.ts @@ -8,6 +8,7 @@ export async function mergeIconProps( collection: string, icon: string, additionalProps: Record, + addXmlNs: boolean, propsProvider?: () => Awaitable>, iconCustomizer?: IconCustomizer, ): Promise { @@ -17,17 +18,20 @@ export async function mergeIconProps( const v = additionalProps[p]; if (v !== undefined && v !== null) props[p] = v; }); - // add svg xmlns if missing - if (!svg.includes(' xmlns=') && !additionalProps['xmlns']) { - additionalProps['xmlns'] = 'http://www.w3.org/2000/svg'; + // add xml namespaces if necessary + if (addXmlNs) { + // add svg xmlns if missing + if (!svg.includes(' xmlns=') && !props['xmlns']) { + props['xmlns'] = 'http://www.w3.org/2000/svg'; + } + // add xmlns:xlink if xlink present and the xmlns missing + if (!svg.includes(' xmlns:xlink=') && svg.includes('xlink:') && !props['xmlns:xlink']) { + props['xmlns:xlink'] = 'http://www.w3.org/1999/xlink'; + } } - // add xmlns:xlink if xlink present and the xmlns missing - if (!svg.includes('xmlns:xlink') && svg.includes('xlink:') && !additionalProps['xmlns:xlink']) { - additionalProps['xmlns:xlink'] = 'http://www.w3.org/1999/xlink'; - } - const replacement = svg.startsWith(' `${p}="${props[p]}"`).join(' ')}` + ' `${p}="${props[p]}"`).join(' ')}` ); } From 85d9c1e8b77296cd34171fd6645ede0ab5a86318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 17:20:57 +0100 Subject: [PATCH 10/17] chore: add missing `;` --- packages/utils/tests/load-icon-test.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/utils/tests/load-icon-test.ts b/packages/utils/tests/load-icon-test.ts index b982f41..514208d 100644 --- a/packages/utils/tests/load-icon-test.ts +++ b/packages/utils/tests/load-icon-test.ts @@ -8,10 +8,9 @@ const loader: CustomIconLoader = async(name) => { } describe('Testing loadIcon', () => { - test('CustomCollection', async () => { const svg = await loader('circle'); - expect(svg).toBeTruthy() + expect(svg).toBeTruthy(); const result = await loadIcon('a', 'circle', { customCollections: { 'a': { @@ -19,13 +18,13 @@ describe('Testing loadIcon', () => { }, }, }); - expect(result).toBeTruthy() + expect(result).toBeTruthy(); expect(svg).toEqual(result); }); test('CustomCollection with transform', async () => { - const svg = await loader('circle') - expect(svg).toBeTruthy() + const svg = await loader('circle'); + expect(svg).toBeTruthy(); const result = await loadIcon('a', 'circle', { customCollections: { 'a': { @@ -36,16 +35,16 @@ describe('Testing loadIcon', () => { transform(icon) { return icon.replace(' -1).toBeTruthy(); expect(result && result.indexOf('height="1em"') > -1).toBeTruthy(); }); test('CustomCollection Icon with XML heading', async () => { - const svg = await loader('1f3eb') - expect(svg).toBeTruthy() + const svg = await loader('1f3eb'); + expect(svg).toBeTruthy(); // Intercept console.warn let warned = false; const warn = console.warn; From e507252841113dc921e7483140e0d8ab3a212b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 17:32:17 +0100 Subject: [PATCH 11/17] chore: svg on css requires svg and xlink namespaces --- packages/utils/src/svg/encode-svg-for-css.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/utils/src/svg/encode-svg-for-css.ts b/packages/utils/src/svg/encode-svg-for-css.ts index 8ddabe9..845e549 100644 --- a/packages/utils/src/svg/encode-svg-for-css.ts +++ b/packages/utils/src/svg/encode-svg-for-css.ts @@ -1,10 +1,17 @@ // https://bl.ocks.org/jennyknuth/222825e315d45a738ed9d6e04c7a88d0 export function encodeSvgForCss(svg: string): string { - return svg.replace(/"/g, '\'') + let useSvg = svg.startsWith('') ? svg.replace('', '') : svg; + if (!useSvg.includes(' xmlns:xlink=') && useSvg.includes(' xlink:')) { + useSvg = useSvg.replace('/g, '%3E') + .replace(/>/g, '%3E'); } From affaf1bea0f128b1e19a400e903e80c9a5cc5659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 17:47:57 +0100 Subject: [PATCH 12/17] chore: handle scale --- packages/utils/src/loader/custom.ts | 1 + packages/utils/src/loader/modern.ts | 1 + packages/utils/src/loader/utils.ts | 9 +++++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/utils/src/loader/custom.ts b/packages/utils/src/loader/custom.ts index 37fb304..6e7c9c7 100644 --- a/packages/utils/src/loader/custom.ts +++ b/packages/utils/src/loader/custom.ts @@ -42,6 +42,7 @@ export async function getCustomIcon( icon, additionalProps, options?.addXmlNs === true, + options?.scale, undefined, iconCustomizer, ); diff --git a/packages/utils/src/loader/modern.ts b/packages/utils/src/loader/modern.ts index 46f7425..3965653 100644 --- a/packages/utils/src/loader/modern.ts +++ b/packages/utils/src/loader/modern.ts @@ -39,6 +39,7 @@ export async function searchForIcon( id, additionalProps, options?.addXmlNs === true, + options?.scale, () => attributes, iconCustomizer ); diff --git a/packages/utils/src/loader/utils.ts b/packages/utils/src/loader/utils.ts index 4d0bb10..0978c3f 100644 --- a/packages/utils/src/loader/utils.ts +++ b/packages/utils/src/loader/utils.ts @@ -1,18 +1,23 @@ import type { Awaitable } from '@antfu/utils'; import type { IconCustomizer } from './types'; -export const isNode = typeof process < 'u' && typeof process.stdout < 'u' - export async function mergeIconProps( svg: string, collection: string, icon: string, additionalProps: Record, addXmlNs: boolean, + scale?: number, propsProvider?: () => Awaitable>, iconCustomizer?: IconCustomizer, ): Promise { const props: Record = (await propsProvider?.()) ?? {}; + if (!svg.includes(" width=") && !svg.includes(" height=") && typeof scale === 'number') { + if ((typeof props.width === 'undefined' || props.width === null) && (typeof props.height === 'undefined' || props.height === null)) { + props.width = `${scale}em`; + props.height = `${scale}em`; + } + } await iconCustomizer?.(collection, icon, props); Object.keys(additionalProps).forEach((p) => { const v = additionalProps[p]; From 6efa9af556e912fef7b273037b9bceec0d742932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 17:48:30 +0100 Subject: [PATCH 13/17] chore: export also `isNode` from `loader.ts` --- packages/utils/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 2004380..badee3e 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -55,7 +55,7 @@ export type { InlineCollection, } from './loader/types'; export { mergeIconProps } from './loader/utils'; -export { loadIcon } from './loader/loader'; +export { isNode, loadIcon } from './loader/loader'; export { FileSystemIconLoader } from './loader/loaders'; export { getCustomIcon } from './loader/custom'; export { searchForIcon } from './loader/modern'; From 3fb00b30c8f6ab83ee8aced19ab55c160967ad19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 17:49:13 +0100 Subject: [PATCH 14/17] chore: move `isLoader` to `loader.ts` and change the logic --- packages/utils/src/loader/loader.ts | 40 +++++++++++++++-------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/packages/utils/src/loader/loader.ts b/packages/utils/src/loader/loader.ts index 222ad0c..526bfb4 100644 --- a/packages/utils/src/loader/loader.ts +++ b/packages/utils/src/loader/loader.ts @@ -1,9 +1,10 @@ import { getCustomIcon } from './custom'; -import { isNode } from './utils'; import { searchForIcon } from './modern'; import { warnOnce } from './install-pkg'; import type { IconifyLoaderOptions } from './types'; +export const isNode = typeof process < 'u' && typeof process.stdout < 'u' + export async function loadIcon( collection: string, icon: string, @@ -18,6 +19,10 @@ export async function loadIcon( } } + if (!isNode) { + return undefined; + } + let svg = await loadNodeBuiltinIcon(collection, icon, options); if (svg && options) { @@ -54,25 +59,22 @@ async function loadNodeBuiltinIcon( options?: IconifyLoaderOptions, warn = true, ): Promise { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const { loadCollectionFromFS } = await importFsModule(); + const iconSet = await loadCollectionFromFS(collection, options?.autoInstall); + if (iconSet) { + // possible icon names + const ids = [ + icon, + icon.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(), + icon.replace(/([a-z])(\d+)/g, '$1-$2'), + ]; + return await searchForIcon(iconSet, collection, ids, options); + } - if (isNode) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const { loadCollectionFromFS } = await importFsModule(); - const iconSet = await loadCollectionFromFS(collection, options?.autoInstall); - if (iconSet) { - // possible icon names - const ids = [ - icon, - icon.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(), - icon.replace(/([a-z])(\d+)/g, '$1-$2'), - ]; - return await searchForIcon(iconSet, collection, ids, options); - } - - if (warn) { - warnOnce(`failed to load \`@iconify-json/${collection}\`, have you installed it?`); - } + if (warn) { + warnOnce(`failed to load \`@iconify-json/${collection}\`, have you installed it?`); } } From b7072a174930ba70de3dd1866eded2af787ec01b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Sat, 26 Feb 2022 18:31:45 +0100 Subject: [PATCH 15/17] chore: move svg style and class logic to `mergeIconProps` --- packages/utils/src/loader/custom.ts | 13 +++---------- packages/utils/src/loader/loader.ts | 15 +-------------- packages/utils/src/loader/modern.ts | 11 ++--------- packages/utils/src/loader/utils.ts | 29 +++++++++++++++++++++++------ 4 files changed, 29 insertions(+), 39 deletions(-) diff --git a/packages/utils/src/loader/custom.ts b/packages/utils/src/loader/custom.ts index 6e7c9c7..321a6c2 100644 --- a/packages/utils/src/loader/custom.ts +++ b/packages/utils/src/loader/custom.ts @@ -31,20 +31,13 @@ export async function getCustomIcon( ); return result; } - const { - transform, - additionalProps = {}, - iconCustomizer, - } = options?.customizations ?? {}; + const { transform } = options?.customizations ?? {}; return await mergeIconProps( - transform ? await transform(result) : result, + typeof transform === 'function' ? await transform(result) : result, collection, icon, - additionalProps, - options?.addXmlNs === true, - options?.scale, + options, undefined, - iconCustomizer, ); } } diff --git a/packages/utils/src/loader/loader.ts b/packages/utils/src/loader/loader.ts index 526bfb4..830a799 100644 --- a/packages/utils/src/loader/loader.ts +++ b/packages/utils/src/loader/loader.ts @@ -23,20 +23,7 @@ export async function loadIcon( return undefined; } - let svg = await loadNodeBuiltinIcon(collection, icon, options); - - if (svg && options) { - const { defaultStyle, defaultClass } = options - // additional props and iconCustomizer takes precedence - if (defaultClass && !svg.includes(' class=')) - svg = svg.replace(' { diff --git a/packages/utils/src/loader/modern.ts b/packages/utils/src/loader/modern.ts index 3965653..a5b5f51 100644 --- a/packages/utils/src/loader/modern.ts +++ b/packages/utils/src/loader/modern.ts @@ -16,11 +16,7 @@ export async function searchForIcon( options?: IconifyLoaderOptions, ): Promise { let iconData: FullIconifyIcon | null; - const { - customize, - additionalProps = {}, - iconCustomizer, - } = options?.customizations ?? {}; + const { customize } = options?.customizations ?? {}; for (const id of ids) { iconData = getIconData(iconSet, id, true); if (iconData) { @@ -37,11 +33,8 @@ export async function searchForIcon( `${body}`, collection, id, - additionalProps, - options?.addXmlNs === true, - options?.scale, + options, () => attributes, - iconCustomizer ); } } diff --git a/packages/utils/src/loader/utils.ts b/packages/utils/src/loader/utils.ts index 0978c3f..fec837b 100644 --- a/packages/utils/src/loader/utils.ts +++ b/packages/utils/src/loader/utils.ts @@ -1,16 +1,18 @@ import type { Awaitable } from '@antfu/utils'; -import type { IconCustomizer } from './types'; +import type { IconifyLoaderOptions } from './types'; export async function mergeIconProps( svg: string, collection: string, icon: string, - additionalProps: Record, - addXmlNs: boolean, - scale?: number, + options?: IconifyLoaderOptions, propsProvider?: () => Awaitable>, - iconCustomizer?: IconCustomizer, ): Promise { + const { scale, addXmlNs = false } = options ?? {} + const { + additionalProps = {}, + iconCustomizer, + } = options?.customizations ?? {}; const props: Record = (await propsProvider?.()) ?? {}; if (!svg.includes(" width=") && !svg.includes(" height=") && typeof scale === 'number') { if ((typeof props.width === 'undefined' || props.width === null) && (typeof props.height === 'undefined' || props.height === null)) { @@ -18,6 +20,7 @@ export async function mergeIconProps( props.height = `${scale}em`; } } + await iconCustomizer?.(collection, icon, props); Object.keys(additionalProps).forEach((p) => { const v = additionalProps[p]; @@ -35,8 +38,22 @@ export async function mergeIconProps( } } - return svg.replace( + svg = svg.replace( ' `${p}="${props[p]}"`).join(' ')}` ); + + if (svg && options) { + const { defaultStyle, defaultClass } = options + // additional props and iconCustomizer takes precedence + if (defaultClass && !svg.includes(' class=')) { + svg = svg.replace(' Date: Sat, 26 Feb 2022 18:33:01 +0100 Subject: [PATCH 16/17] chore: add `encode-svg-for-css` to `package.json` test: add `loadIcon` tests for `@iconify-json` icons --- packages/utils/package.json | 4 ++ packages/utils/tests/iconify-icon-test.ts | 57 +++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 packages/utils/tests/iconify-icon-test.ts diff --git a/packages/utils/package.json b/packages/utils/package.json index 6248114..453ab2c 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -160,6 +160,10 @@ "require": "./lib/svg/build.js", "import": "./lib/svg/build.mjs" }, + "./lib/svg/encode-svg-for-css": { + "require": "./lib/svg/encode-svg-for-css.js", + "import": "./lib/svg/encode-svg-for-css.mjs" + }, "./lib/svg/id": { "require": "./lib/svg/id.js", "import": "./lib/svg/id.mjs" diff --git a/packages/utils/tests/iconify-icon-test.ts b/packages/utils/tests/iconify-icon-test.ts new file mode 100644 index 0000000..529d46d --- /dev/null +++ b/packages/utils/tests/iconify-icon-test.ts @@ -0,0 +1,57 @@ +import { loadIcon } from '../lib'; + +describe('Testing loadIcon with @iconify-json/flat-color-icons>', () => { + + test('loadIcon works', async () => { + const result = await loadIcon('flat-color-icons', 'up-right'); + expect(result).toBeTruthy(); + }); + + test('loadIcon adds xmlns:xlink', async () => { + const result = await loadIcon('flat-color-icons', 'up-right', { addXmlNs: true }); + expect(result).toBeTruthy(); + expect(result && result.indexOf('xmlns:xlink=') > - 1).toBeTruthy(); + }); + + test('loadIcon with transform with default style and class', async () => { + const result = await loadIcon('flat-color-icons', 'up-right', { + defaultStyle: 'margin-top: 1rem;', + defaultClass: 'clazz', + customizations: { + transform(icon) { + return icon.replace(' - 1).toBeTruthy(); + expect(result && result.indexOf('class="clazz"') > - 1).toBeTruthy(); + expect(result && result.indexOf('width="1em"') > - 1).toBeTruthy(); + expect(result && result.indexOf('height="1em"') > - 1).toBeTruthy(); + }); + + test('loadIcon preserves customizations order', async () => { + const result = await loadIcon('flat-color-icons', 'up-right', { + scale: 1, + defaultStyle: 'color: red;', + defaultClass: 'clazz1', + customizations: { + additionalProps: { + 'width': '2em', + 'height': '2em', + 'style': 'color: blue;', + 'class': 'clazz2', + }, + // it will never be called, it is not a custom icon + transform(icon) { + return icon.replace(' Date: Sat, 26 Feb 2022 18:44:03 +0100 Subject: [PATCH 17/17] test: add `customize` hook for `@iconify-json` icon --- packages/utils/tests/iconify-icon-test.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/utils/tests/iconify-icon-test.ts b/packages/utils/tests/iconify-icon-test.ts index 529d46d..75a284a 100644 --- a/packages/utils/tests/iconify-icon-test.ts +++ b/packages/utils/tests/iconify-icon-test.ts @@ -13,21 +13,23 @@ describe('Testing loadIcon with @iconify-json/flat-color-icons>', () => { expect(result && result.indexOf('xmlns:xlink=') > - 1).toBeTruthy(); }); - test('loadIcon with transform with default style and class', async () => { + test('loadIcon with customize with default style and class', async () => { const result = await loadIcon('flat-color-icons', 'up-right', { defaultStyle: 'margin-top: 1rem;', defaultClass: 'clazz', customizations: { - transform(icon) { - return icon.replace(' - 1).toBeTruthy(); expect(result && result.indexOf('class="clazz"') > - 1).toBeTruthy(); - expect(result && result.indexOf('width="1em"') > - 1).toBeTruthy(); - expect(result && result.indexOf('height="1em"') > - 1).toBeTruthy(); + expect(result && result.indexOf('width="2em"') > - 1).toBeTruthy(); + expect(result && result.indexOf('height="2em"') > - 1).toBeTruthy(); }); test('loadIcon preserves customizations order', async () => {