From dbe23c60259cf21beaaca3e1f3ba6950d7e704ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez?= Date: Fri, 26 Jan 2024 22:20:31 +0100 Subject: [PATCH] feat: support for external collection packages (#342) * feat: custom external collection packages * chore: update readme section * chore: include an example in vite-vue3 * chore: include using resolvers fro auto-import * chore: update readme --- README.md | 44 ++++++++++ examples/vite-vue3/App.vue | 18 ++++ examples/vite-vue3/components.d.ts | 2 + examples/vite-vue3/vite.config.ts | 28 ++++++- package.json | 2 +- pnpm-lock.yaml | 127 ++--------------------------- src/loaders.ts | 6 ++ 7 files changed, 104 insertions(+), 123 deletions(-) diff --git a/README.md b/README.md index d8dc2a2f..380f6803 100644 --- a/README.md +++ b/README.md @@ -735,6 +735,50 @@ IconResolver({ See the [Vue 3 + Vite example](./examples/vite-vue3/vite.config.ts). +### Use Custom External Collection Packages + +From version `v0.18.3` you can use other packages to load icons from others authors. + +**WARNING**: external packages must include `icons.json` file with the `icons` data in `IconifyJSON` format, which can be exported with Iconify Tools. Check [Exporting icon set as JSON package](https://iconify.design/docs/libraries/tools/export/json-package.html) for more details. + +For example, you can use `an-awesome-collection` or `@my-awesome-collections/some-collection` to load your custom or third party icons: +```ts +// loader helpers +import { ExternalPackageIconLoader } from 'unplugin-icons/loaders' + +Icons({ customCollections: ExternalPackageIconLoader('my-awesome-collection') }) +``` + +When using with resolvers for auto-importing, remember you will need to tell it your custom collection names: +```ts +IconResolver({ + customCollections: [ + 'my-awesome-collection', + ], +}) +``` + + +You can also combine it with `FileSystemIconLoader` or with other custom icon loaders: +```ts +// loader helpers +import { ExternalPackageIconLoader, FileSystemIconLoader } from 'unplugin-icons/loaders' + +Icons({ + customCollections: { + ...ExternalPackageIconLoader('an-awesome-collection'), + ...ExternalPackageIconLoader('@my-awesome-collections/some-collection'), + ...ExternalPackageIconLoader('@my-awesome-collections/some-other-collection'), + 'my-yet-other-icons': FileSystemIconLoader( + './assets/icons', + svg => svg.replace(/^

Customizer via Config

@@ -80,6 +86,18 @@ import RawMdiAlarmOff3 from 'virtual:icons/mdi/alarm-off?raw&width=unset&height= 'virtual:icons/mdi/alarm-off?raw&width=unset&height=unset'
{{ RawMdiAlarmOff3 }}
+ +
+ import Custom1 from + 'virtual:icons/plain-color-icons/about?raw' +
{{ Custom1 }}
+
+ +
+ import Custom2 from + 'virtual:icons/test-color-icons/about?raw' +
{{ Custom2 }}
+

diff --git a/examples/vite-vue3/components.d.ts b/examples/vite-vue3/components.d.ts index f1120bd8..012f342e 100644 --- a/examples/vite-vue3/components.d.ts +++ b/examples/vite-vue3/components.d.ts @@ -26,7 +26,9 @@ declare module 'vue' { IMdiLightAlarm: typeof import('~icons/mdi-light/alarm')['default'] INotoV1FlagForFlagJapan: typeof import('~icons/noto-v1/flag-for-flag-japan')['default'] IParkAbnormal: typeof import('~icons/icon-park/abnormal')['default'] + 'IPlainColorIcons:about': typeof import('~icons/plain-color-icons/about')['default'] IRiApps2Line: typeof import('~icons/ri/apps2-line')['default'] + 'ITestColorIcons:about': typeof import('~icons/test-color-icons/about')['default'] ITwemoji1stPlaceMedal: typeof import('~icons/twemoji/1st-place-medal')['default'] } } diff --git a/examples/vite-vue3/vite.config.ts b/examples/vite-vue3/vite.config.ts index 16437e9f..7a45a49e 100644 --- a/examples/vite-vue3/vite.config.ts +++ b/examples/vite-vue3/vite.config.ts @@ -1,17 +1,33 @@ -import { promises as fs } from 'node:fs' +import { cpSync, promises as fs } from 'node:fs' import type { UserConfig } from 'vite' import Vue from '@vitejs/plugin-vue' import Icons from 'unplugin-icons/vite' -import { FileSystemIconLoader } from 'unplugin-icons/loaders' +import { ExternalPackageIconLoader, FileSystemIconLoader } from 'unplugin-icons/loaders' import IconsResolver from 'unplugin-icons/resolver' import Components from 'unplugin-vue-components/vite' +/************************************************************/ +// DON'T DO THIS IN YOUR CODE: IT IS FOR TESTING PURPOSES ONLY +cpSync( + './plain-color-icons', + './node_modules/plain-color-icons', + { recursive: true }, +) +cpSync( + './@test-scope', + './node_modules/@test-scope', + { recursive: true }, +) +/************************************************************/ + const config: UserConfig = { plugins: [ Vue(), Icons({ compiler: 'vue3', customCollections: { + ...ExternalPackageIconLoader('@test-scope/test-color-icons'), + ...ExternalPackageIconLoader('plain-color-icons'), custom: FileSystemIconLoader('assets/custom-a'), inline: { foo: ` @@ -37,7 +53,13 @@ const config: UserConfig = { alias: { park: 'icon-park', }, - customCollections: ['custom', 'inline'], + customCollections: [ + 'custom', + 'inline', + // custom external packages + 'plain-color-icons', + 'test-color-icons', + ], }), ], }), diff --git a/package.json b/package.json index 586e5a9b..c5b129e3 100644 --- a/package.json +++ b/package.json @@ -194,7 +194,7 @@ "dependencies": { "@antfu/install-pkg": "^0.3.0", "@antfu/utils": "^0.7.6", - "@iconify/utils": "^2.1.12", + "@iconify/utils": "^2.1.20", "debug": "^4.3.4", "kolorist": "^1.8.0", "local-pkg": "^0.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5a96b8b6..99c404b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: ^0.7.6 version: 0.7.6 '@iconify/utils': - specifier: ^2.1.12 - version: 2.1.12 + specifier: ^2.1.20 + version: 2.1.20 debug: specifier: ^4.3.4 version: 4.3.4 @@ -127,7 +127,7 @@ importers: version: 2.2.110 '@svgr/core': specifier: 8.1.0 - version: 8.1.0 + version: 8.1.0(typescript@5.2.2) '@svgr/plugin-jsx': specifier: ^8.1.0 version: 8.1.0(@svgr/core@8.1.0) @@ -3281,15 +3281,15 @@ packages: /@iconify/types@2.0.0: resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - /@iconify/utils@2.1.12: - resolution: {integrity: sha512-7vf3Uk6H7TKX4QMs2gbg5KR1X9J0NJzKSRNWhMZ+PWN92l0t6Q3tj2ZxLDG07rC3ppWBtTtA4FPmkQphuEmdsg==} + /@iconify/utils@2.1.20: + resolution: {integrity: sha512-t8TeKlYK/5i9yTY9VAGAE4P0qQHd/0vH+VSRO+bdpxlt8wqB6f2I0/IrciRsdeFZPMoL8IICgP7lgl2ZtbG8Tw==} dependencies: '@antfu/install-pkg': 0.1.1 '@antfu/utils': 0.7.6 '@iconify/types': 2.0.0 debug: 4.3.4 kolorist: 1.8.0 - local-pkg: 0.4.3 + local-pkg: 0.5.0 transitivePeerDependencies: - supports-color dev: false @@ -3872,15 +3872,6 @@ packages: - supports-color dev: true - /@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.22.8): - resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.8 - dev: true - /@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} engines: {node: '>=14'} @@ -3890,15 +3881,6 @@ packages: '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.22.8): - resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.8 - dev: true - /@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} engines: {node: '>=14'} @@ -3908,15 +3890,6 @@ packages: '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.22.8): - resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.8 - dev: true - /@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} engines: {node: '>=14'} @@ -3926,15 +3899,6 @@ packages: '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.22.8): - resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.8 - dev: true - /@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} engines: {node: '>=14'} @@ -3944,15 +3908,6 @@ packages: '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.22.8): - resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.8 - dev: true - /@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} engines: {node: '>=14'} @@ -3962,15 +3917,6 @@ packages: '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.22.8): - resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.8 - dev: true - /@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} engines: {node: '>=14'} @@ -3980,15 +3926,6 @@ packages: '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.22.8): - resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.8 - dev: true - /@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.23.2): resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} engines: {node: '>=14'} @@ -3998,15 +3935,6 @@ packages: '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.22.8): - resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} - engines: {node: '>=12'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.8 - dev: true - /@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} engines: {node: '>=12'} @@ -4016,23 +3944,6 @@ packages: '@babel/core': 7.23.2 dev: true - /@svgr/babel-preset@8.1.0(@babel/core@7.22.8): - resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.8 - '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.22.8) - '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.22.8) - '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.22.8) - '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.22.8) - '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.22.8) - '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.22.8) - '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.22.8) - '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.22.8) - dev: true - /@svgr/babel-preset@8.1.0(@babel/core@7.23.2): resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} engines: {node: '>=14'} @@ -4050,19 +3961,6 @@ packages: '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.23.2) dev: true - /@svgr/core@8.1.0: - resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} - engines: {node: '>=14'} - dependencies: - '@babel/core': 7.22.8 - '@svgr/babel-preset': 8.1.0(@babel/core@7.22.8) - camelcase: 6.3.0 - cosmiconfig: 8.1.3 - snake-case: 3.0.4 - transitivePeerDependencies: - - supports-color - dev: true - /@svgr/core@8.1.0(typescript@5.2.2): resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} engines: {node: '>=14'} @@ -4107,7 +4005,7 @@ packages: dependencies: '@babel/core': 7.23.2 '@svgr/babel-preset': 8.1.0(@babel/core@7.23.2) - '@svgr/core': 8.1.0 + '@svgr/core': 8.1.0(typescript@5.2.2) '@svgr/hast-util-to-babel-ast': 8.0.0 svg-parser: 2.0.4 transitivePeerDependencies: @@ -6991,16 +6889,6 @@ packages: yaml: 1.10.2 dev: true - /cosmiconfig@8.1.3: - resolution: {integrity: sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==} - engines: {node: '>=14'} - dependencies: - import-fresh: 3.3.0 - js-yaml: 4.1.0 - parse-json: 5.2.0 - path-type: 4.0.0 - dev: true - /cosmiconfig@8.3.6(typescript@5.2.2): resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} engines: {node: '>=14'} @@ -10223,6 +10111,7 @@ packages: /local-pkg@0.4.3: resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} engines: {node: '>=14'} + dev: true /local-pkg@0.5.0: resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} diff --git a/src/loaders.ts b/src/loaders.ts index 0e0df740..6757b416 100644 --- a/src/loaders.ts +++ b/src/loaders.ts @@ -1,7 +1,13 @@ import type { Awaitable } from '@antfu/utils' import { FileSystemIconLoader as IconifyFileSystemIconLoader } from '@iconify/utils/lib/loader/node-loaders' +import { createExternalPackageIconLoader } from '@iconify/utils/lib/loader/external-pkg' +import type { AutoInstall, ExternalPkgName } from '@iconify/utils/lib/loader/types' import type { CustomIconLoader } from '.' export function FileSystemIconLoader(dir: string, transform?: (svg: string) => Awaitable): CustomIconLoader { return IconifyFileSystemIconLoader(dir, transform) } + +export function ExternalPackageIconLoader(packageName: ExternalPkgName, autoInstall?: AutoInstall): Record { + return createExternalPackageIconLoader(packageName, autoInstall) +}