From d090a3801bb92e44593b4d4fd3571dae7323ceab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Mon, 10 Jan 2022 12:17:05 +0100 Subject: [PATCH 1/4] feat: refactor modern loader --- package-lock.json | 4 ---- packages/core/package.json | 14 +------------- packages/utils/package-lock.json | 19 ++++++++++++++++++- packages/utils/package.json | 7 ++++++- packages/utils/src/index.ts | 1 + .../index.ts => utils/src/loader/modern.ts} | 13 +++++++------ 6 files changed, 33 insertions(+), 25 deletions(-) rename packages/{core/src/modern/index.ts => utils/src/loader/modern.ts} (81%) diff --git a/package-lock.json b/package-lock.json index 09e0824..f908b5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4460,7 +4460,6 @@ "minimist": "^1.2.5", "neo-async": "^2.6.0", "source-map": "^0.6.1", - "uglify-js": "^3.1.4", "wordwrap": "^1.0.0" }, "bin": { @@ -5245,9 +5244,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, - "dependencies": { - "graceful-fs": "^4.1.6" - }, "optionalDependencies": { "graceful-fs": "^4.1.6" } diff --git a/packages/core/package.json b/packages/core/package.json index d383654..b30f1d5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -90,14 +90,6 @@ "require": "./lib/icon/sort.js", "import": "./lib/icon/sort.mjs" }, - "./lib/modern": { - "require": "./lib/modern/index.js", - "import": "./lib/modern/index.mjs" - }, - "./lib/modern/index": { - "require": "./lib/modern/index.js", - "import": "./lib/modern/index.mjs" - }, "./lib/storage/functions": { "require": "./lib/storage/functions.js", "import": "./lib/storage/functions.mjs" @@ -108,17 +100,13 @@ } }, "dependencies": { - "@antfu/utils": "^0.3.0", "@iconify/api-redundancy": "^1.0.2", "@iconify/types": "^1.0.10", "@iconify/utils": "^1.0.16", - "cross-fetch": "^3.1.4", - "debug": "^4.3.3", - "local-pkg": "^0.4.0" + "cross-fetch": "^3.1.4" }, "devDependencies": { "@iconify/library-builder": "^1.0.3", - "@types/debug": "^4.1.7", "@types/jest": "^27.0.2", "@types/node": "^15.3.0", "@typescript-eslint/eslint-plugin": "^4.31.1", diff --git a/packages/utils/package-lock.json b/packages/utils/package-lock.json index bd91973..804fdc4 100644 --- a/packages/utils/package-lock.json +++ b/packages/utils/package-lock.json @@ -13,7 +13,8 @@ "@antfu/utils": "^0.3.0", "@iconify/types": "^1.0.12", "debug": "^4.3.3", - "kolorist": "^1.5.0" + "kolorist": "^1.5.0", + "local-pkg": "^0.4.0" }, "devDependencies": { "@iconify/library-builder": "^1.0.4", @@ -3972,6 +3973,17 @@ "node": ">= 0.8.0" } }, + "node_modules/local-pkg": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.1.tgz", + "integrity": "sha512-lL87ytIGP2FU5PWwNDo0w3WhIo2gopIAxPg9RxDYF7m4rr5ahuZxP22xnJHIvaLTe4Z9P6uKKY2UHiwyB4pcrw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -8412,6 +8424,11 @@ "type-check": "~0.4.0" } }, + "local-pkg": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.1.tgz", + "integrity": "sha512-lL87ytIGP2FU5PWwNDo0w3WhIo2gopIAxPg9RxDYF7m4rr5ahuZxP22xnJHIvaLTe4Z9P6uKKY2UHiwyB4pcrw==" + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", diff --git a/packages/utils/package.json b/packages/utils/package.json index c24a5d4..ae6e620 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -128,6 +128,10 @@ "require": "./lib/loader/loaders.js", "import": "./lib/loader/loaders.mjs" }, + "./lib/loader/modern": { + "require": "./lib/loader/modern.js", + "import": "./lib/loader/modern.mjs" + }, "./lib/loader/types": { "require": "./lib/loader/types.js", "import": "./lib/loader/types.mjs" @@ -158,7 +162,8 @@ "@antfu/utils": "^0.3.0", "@iconify/types": "^1.0.12", "debug": "^4.3.3", - "kolorist": "^1.5.0" + "kolorist": "^1.5.0", + "local-pkg": "^0.4.0" }, "devDependencies": { "@iconify/library-builder": "^1.0.4", diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index b7be4a8..9dfd319 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -53,6 +53,7 @@ export type { export { tryInstallPkg } from './loader/utils'; export { FileSystemIconLoader } from './loader/loaders'; export { getCustomIcon } from './loader/custom'; +export { loadCollection, searchForIcon } from './loader/modern'; // Misc export { camelize, camelToKebab, pascalize } from './misc/strings'; diff --git a/packages/core/src/modern/index.ts b/packages/utils/src/loader/modern.ts similarity index 81% rename from packages/core/src/modern/index.ts rename to packages/utils/src/loader/modern.ts index 8a6ac26..957f43a 100644 --- a/packages/core/src/modern/index.ts +++ b/packages/utils/src/loader/modern.ts @@ -1,14 +1,15 @@ import { promises as fs } from 'fs'; import type { IconifyJSON } from '@iconify/types'; -import type { FullIconifyIcon } from '@iconify/utils/lib/icon'; -import { defaultCustomisations as DefaultIconCustomizations, iconToSVG, getIconData, tryInstallPkg } from '@iconify/utils'; +import type { FullIconifyIcon } from '../icon'; +import { iconToSVG, getIconData, tryInstallPkg } from '../index'; import createDebugger from 'debug'; import { isPackageExists, resolveModule } from 'local-pkg'; -import type { FullIconCustomisations } from '@iconify/utils/lib/customisations'; +import { defaults as DefaultIconCustomizations } from '../customisations'; +import type { FullIconCustomisations } from '../customisations'; -const debug = createDebugger('@iconify-core:icon'); -const debugModern = createDebugger('@iconify-core:modern'); -const debugLegacy = createDebugger('@iconify-core:legacy'); +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'); From 2d06165da357e6e7f13c692c951a39f68a8cedcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Mon, 10 Jan 2022 12:21:34 +0100 Subject: [PATCH 2/4] chore: update imports --- packages/utils/src/loader/modern.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/utils/src/loader/modern.ts b/packages/utils/src/loader/modern.ts index 957f43a..acfa095 100644 --- a/packages/utils/src/loader/modern.ts +++ b/packages/utils/src/loader/modern.ts @@ -1,7 +1,9 @@ import { promises as fs } from 'fs'; import type { IconifyJSON } from '@iconify/types'; import type { FullIconifyIcon } from '../icon'; -import { iconToSVG, getIconData, tryInstallPkg } from '../index'; +import { iconToSVG } from '../svg/build'; +import { getIconData } from '../icon-set/get-icon'; +import { tryInstallPkg } from './utils'; import createDebugger from 'debug'; import { isPackageExists, resolveModule } from 'local-pkg'; import { defaults as DefaultIconCustomizations } from '../customisations'; From c281ebf7f1c6d744c28aaedd8c3e5632381e5741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Mon, 10 Jan 2022 13:43:35 +0100 Subject: [PATCH 3/4] chore: add icons customizations --- packages/utils/src/index.ts | 4 +- packages/utils/src/loader/custom.ts | 22 ++++++---- packages/utils/src/loader/loaders.ts | 2 +- packages/utils/src/loader/modern.ts | 21 ++++++---- packages/utils/src/loader/types.ts | 44 +++++++++++++++++++- packages/utils/src/loader/utils.ts | 22 +++++++++- packages/utils/tests/get-custom-icon-test.ts | 7 +++- 7 files changed, 102 insertions(+), 20 deletions(-) diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 9dfd319..495ae24 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -48,9 +48,11 @@ export { stringToColor, compareColors, colorToString } from './colors/index'; export type { CustomIconLoader, CustomCollections, + IconCustomizer, + IconCustomizations, InlineCollection, } from './loader/types'; -export { tryInstallPkg } from './loader/utils'; +export { tryInstallPkg, mergeIconProps } from './loader/utils'; export { FileSystemIconLoader } from './loader/loaders'; export { getCustomIcon } from './loader/custom'; export { loadCollection, searchForIcon } from './loader/modern'; diff --git a/packages/utils/src/loader/custom.ts b/packages/utils/src/loader/custom.ts index 26137c8..a02d512 100644 --- a/packages/utils/src/loader/custom.ts +++ b/packages/utils/src/loader/custom.ts @@ -1,6 +1,6 @@ -import type { Awaitable } from '@antfu/utils'; import createDebugger from 'debug'; -import type { CustomIconLoader, InlineCollection } from './types'; +import type { CustomIconLoader, IconCustomizations, InlineCollection } from './types'; +import { mergeIconProps } from './utils'; const debug = createDebugger('@iconify-loader:custom'); @@ -11,7 +11,7 @@ export async function getCustomIcon( custom: CustomIconLoader | InlineCollection, collection: string, icon: string, - transform?: (svg: string) => Awaitable + iconsCustomizations?: IconCustomizations, ): Promise { let result: string | undefined | null; @@ -26,10 +26,18 @@ export async function getCustomIcon( if (result) { if (!result.startsWith(' FullIconCustomisations -): string | null { + iconCustomizactions?: IconCustomizations, +): Promise { let iconData: FullIconifyIcon | null; + const { customize, additionalProps = {}, iconCustomizer } = iconCustomizactions || {} for (const id of ids) { iconData = getIconData(iconSet, id, true); if (iconData) { @@ -67,8 +68,14 @@ export function searchForIcon( iconData, typeof customize === 'function' ? customize(defaultCustomizations) : defaultCustomizations ); - return ` `${i[0]}="${i[1]}"`).join(' ')}>${body}`; + return await mergeIconProps( + `${body}`, + collection, + id, + additionalProps, + () => attributes, + iconCustomizer, + ) } } - return null; } diff --git a/packages/utils/src/loader/types.ts b/packages/utils/src/loader/types.ts index 90fe59c..700131e 100644 --- a/packages/utils/src/loader/types.ts +++ b/packages/utils/src/loader/types.ts @@ -1,10 +1,52 @@ import type { Awaitable } from '@antfu/utils'; +import type { FullIconCustomisations } from '../customisations'; /** - * Custom icon loader, used by getCustomIcon() + * Custom icon loader, used by `getCustomIcon`. */ export type CustomIconLoader = (name: string) => Awaitable; +/** + * Custom icon customizer, it will allow to customize all icons on a collection or individual icons. + */ +export type IconCustomizer = (collection: string, icon: string, props: Record) => Awaitable; + +/** + * Icon customizations: will be applied to all resolved icons. + * + * For each loaded icon, the customizations will be applied in this order: + * - apply `transform` to raw `svg`, if provided + * - apply `customize` with default customizations, if provided + * - apply `iconCustomizer` with `customize` customizations, if provided + * - apply `additionalProps` with `iconCustomizer` customizations, if provided + */ +export type IconCustomizations = { + /** + * Transform raw `svg`. + * + * @param svg The loaded `svg` + * @return The transformed `svg`. + */ + transform?: (svg: string) => Awaitable + /** + * Change default icon customizations values. + * + * @param defaultCustomizations Default icon customisations values. + * @return The modified icon customisations values. + */ + customize?: (defaultCustomizations: FullIconCustomisations) => FullIconCustomisations + /** + * Custom icon customizer. + */ + iconCustomizer?: IconCustomizer + /** + * Additional icon properties. + * + * All properties without value will not be applied. + */ + additionalProps?: Record +}; + /** * List of icons as object. Key is icon name, value is icon data or callback (can be async) to get icon data */ diff --git a/packages/utils/src/loader/utils.ts b/packages/utils/src/loader/utils.ts index 282b1e9..8950116 100644 --- a/packages/utils/src/loader/utils.ts +++ b/packages/utils/src/loader/utils.ts @@ -1,6 +1,7 @@ import { installPackage } from '@antfu/install-pkg'; -import { sleep } from '@antfu/utils'; +import { Awaitable, sleep } from '@antfu/utils'; import { cyan, yellow } from 'kolorist'; +import type { IconCustomizer } from './types'; const warned = new Set(); @@ -14,6 +15,25 @@ export function warnOnce(msg: string): void { let pending: Promise | undefined; const tasks: Record | undefined> = {}; +export async function mergeIconProps( + svg: string, + collection: string, + icon: string, + additionalProps: Record, + propsProvider?: () => Awaitable>, + iconCustomizer?: IconCustomizer, +): Promise { + const props: Record = await propsProvider?.() ?? {} + await iconCustomizer?.(collection, icon, props) + Object.keys(additionalProps).forEach((p) => { + const v = additionalProps[p] + if (v !== undefined && v !== null) + props[p] = v + }) + const replacement = svg.startsWith(' `${p}="${props[p]}"`).join(' ')}`) +} + export async function tryInstallPkg(name: string): Promise { if (pending) { await pending; diff --git a/packages/utils/tests/get-custom-icon-test.ts b/packages/utils/tests/get-custom-icon-test.ts index e505820..26a5576 100644 --- a/packages/utils/tests/get-custom-icon-test.ts +++ b/packages/utils/tests/get-custom-icon-test.ts @@ -16,9 +16,12 @@ describe('Testing getCustomIcon', () => { () => svg, 'a', 'b', - (icon) => { - return icon.replace(' -1).toBeTruthy(); expect(result && result.indexOf('height="1em"') > -1).toBeTruthy(); From fe67da6a9cde8a534ae802bf51e7cdb8ac0f2d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez=20Jim=C3=A9nez?= Date: Mon, 10 Jan 2022 13:57:22 +0100 Subject: [PATCH 4/4] fix: jsdocs for `IconCustomizations` + some typo --- packages/utils/src/loader/modern.ts | 4 ++-- packages/utils/src/loader/types.ts | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/utils/src/loader/modern.ts b/packages/utils/src/loader/modern.ts index 9e42a91..eb21311 100644 --- a/packages/utils/src/loader/modern.ts +++ b/packages/utils/src/loader/modern.ts @@ -55,10 +55,10 @@ export async function searchForIcon( iconSet: IconifyJSON, collection: string, ids: string[], - iconCustomizactions?: IconCustomizations, + iconCustomizations?: IconCustomizations, ): Promise { let iconData: FullIconifyIcon | null; - const { customize, additionalProps = {}, iconCustomizer } = iconCustomizactions || {} + const { customize, additionalProps = {}, iconCustomizer } = iconCustomizations || {} 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 700131e..e7a68bf 100644 --- a/packages/utils/src/loader/types.ts +++ b/packages/utils/src/loader/types.ts @@ -15,7 +15,7 @@ export type IconCustomizer = (collection: string, icon: string, props: Record FullIconCustomisations /**