mirror of
https://github.com/antfu-collective/icones.git
synced 2026-01-09 07:40:49 +08:00
feat: move pack logic to a web worker (#367)
This commit is contained in:
parent
96d65bb177
commit
21603701a3
@ -34,6 +34,7 @@
|
|||||||
"hotkeys-js": "^3.13.9",
|
"hotkeys-js": "^3.13.9",
|
||||||
"iconify-icon": "^2.3.0",
|
"iconify-icon": "^2.3.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
|
"ultrahtml": "^1.6.0",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-chemistry": "^0.2.2",
|
"vue-chemistry": "^0.2.2",
|
||||||
"vue-router": "^4.5.0"
|
"vue-router": "^4.5.0"
|
||||||
|
|||||||
100
pnpm-lock.yaml
generated
100
pnpm-lock.yaml
generated
@ -35,6 +35,9 @@ importers:
|
|||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.5.3
|
specifier: ^3.5.3
|
||||||
version: 3.5.3
|
version: 3.5.3
|
||||||
|
ultrahtml:
|
||||||
|
specifier: ^1.6.0
|
||||||
|
version: 1.6.0
|
||||||
vue:
|
vue:
|
||||||
specifier: ^3.5.13
|
specifier: ^3.5.13
|
||||||
version: 3.5.13(typescript@5.8.2)
|
version: 3.5.13(typescript@5.8.2)
|
||||||
@ -3302,10 +3305,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==}
|
resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
is-regex@1.1.4:
|
|
||||||
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
|
|
||||||
is-regex@1.2.1:
|
is-regex@1.2.1:
|
||||||
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
|
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -3840,10 +3839,6 @@ packages:
|
|||||||
nth-check@2.1.1:
|
nth-check@2.1.1:
|
||||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||||
|
|
||||||
object-inspect@1.13.2:
|
|
||||||
resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
|
|
||||||
object-inspect@1.13.4:
|
object-inspect@1.13.4:
|
||||||
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
|
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -4243,10 +4238,6 @@ packages:
|
|||||||
safe-buffer@5.2.1:
|
safe-buffer@5.2.1:
|
||||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||||
|
|
||||||
safe-regex-test@1.0.3:
|
|
||||||
resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
|
|
||||||
safe-regex-test@1.1.0:
|
safe-regex-test@1.1.0:
|
||||||
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
|
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -4324,10 +4315,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
|
resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
side-channel@1.0.6:
|
|
||||||
resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
|
|
||||||
side-channel@1.1.0:
|
side-channel@1.1.0:
|
||||||
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
|
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -4656,6 +4643,9 @@ packages:
|
|||||||
ufo@1.5.4:
|
ufo@1.5.4:
|
||||||
resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==}
|
resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==}
|
||||||
|
|
||||||
|
ultrahtml@1.6.0:
|
||||||
|
resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==}
|
||||||
|
|
||||||
unbox-primitive@1.0.2:
|
unbox-primitive@1.0.2:
|
||||||
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
|
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
|
||||||
|
|
||||||
@ -7174,7 +7164,7 @@ snapshots:
|
|||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
es-abstract: 1.23.3
|
es-abstract: 1.23.3
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.3.0
|
||||||
is-array-buffer: 3.0.4
|
is-array-buffer: 3.0.4
|
||||||
is-shared-array-buffer: 1.0.3
|
is-shared-array-buffer: 1.0.3
|
||||||
|
|
||||||
@ -7841,36 +7831,36 @@ snapshots:
|
|||||||
data-view-buffer: 1.0.1
|
data-view-buffer: 1.0.1
|
||||||
data-view-byte-length: 1.0.1
|
data-view-byte-length: 1.0.1
|
||||||
data-view-byte-offset: 1.0.0
|
data-view-byte-offset: 1.0.0
|
||||||
es-define-property: 1.0.0
|
es-define-property: 1.0.1
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
es-object-atoms: 1.0.0
|
es-object-atoms: 1.1.1
|
||||||
es-set-tostringtag: 2.0.3
|
es-set-tostringtag: 2.0.3
|
||||||
es-to-primitive: 1.2.1
|
es-to-primitive: 1.2.1
|
||||||
function.prototype.name: 1.1.6
|
function.prototype.name: 1.1.6
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.3.0
|
||||||
get-symbol-description: 1.0.2
|
get-symbol-description: 1.0.2
|
||||||
globalthis: 1.0.4
|
globalthis: 1.0.4
|
||||||
gopd: 1.0.1
|
gopd: 1.2.0
|
||||||
has-property-descriptors: 1.0.2
|
has-property-descriptors: 1.0.2
|
||||||
has-proto: 1.0.3
|
has-proto: 1.0.3
|
||||||
has-symbols: 1.0.3
|
has-symbols: 1.1.0
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
internal-slot: 1.0.7
|
internal-slot: 1.0.7
|
||||||
is-array-buffer: 3.0.4
|
is-array-buffer: 3.0.4
|
||||||
is-callable: 1.2.7
|
is-callable: 1.2.7
|
||||||
is-data-view: 1.0.1
|
is-data-view: 1.0.1
|
||||||
is-negative-zero: 2.0.3
|
is-negative-zero: 2.0.3
|
||||||
is-regex: 1.1.4
|
is-regex: 1.2.1
|
||||||
is-shared-array-buffer: 1.0.3
|
is-shared-array-buffer: 1.0.3
|
||||||
is-string: 1.0.7
|
is-string: 1.0.7
|
||||||
is-typed-array: 1.1.13
|
is-typed-array: 1.1.13
|
||||||
is-weakref: 1.0.2
|
is-weakref: 1.0.2
|
||||||
object-inspect: 1.13.2
|
object-inspect: 1.13.4
|
||||||
object-keys: 1.1.1
|
object-keys: 1.1.1
|
||||||
object.assign: 4.1.5
|
object.assign: 4.1.5
|
||||||
regexp.prototype.flags: 1.5.2
|
regexp.prototype.flags: 1.5.2
|
||||||
safe-array-concat: 1.1.2
|
safe-array-concat: 1.1.2
|
||||||
safe-regex-test: 1.0.3
|
safe-regex-test: 1.1.0
|
||||||
string.prototype.trim: 1.2.9
|
string.prototype.trim: 1.2.9
|
||||||
string.prototype.trimend: 1.0.8
|
string.prototype.trimend: 1.0.8
|
||||||
string.prototype.trimstart: 1.0.8
|
string.prototype.trimstart: 1.0.8
|
||||||
@ -7901,7 +7891,7 @@ snapshots:
|
|||||||
|
|
||||||
es-set-tostringtag@2.0.3:
|
es-set-tostringtag@2.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.3.0
|
||||||
has-tostringtag: 1.0.2
|
has-tostringtag: 1.0.2
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
|
|
||||||
@ -8506,7 +8496,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.7
|
call-bind: 1.0.7
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.3.0
|
||||||
|
|
||||||
get-tsconfig@4.10.0:
|
get-tsconfig@4.10.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -8743,7 +8733,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
side-channel: 1.0.6
|
side-channel: 1.1.0
|
||||||
|
|
||||||
ip-address@9.0.5:
|
ip-address@9.0.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -8758,7 +8748,7 @@ snapshots:
|
|||||||
is-array-buffer@3.0.4:
|
is-array-buffer@3.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.7
|
call-bind: 1.0.7
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.3.0
|
||||||
|
|
||||||
is-bigint@1.0.4:
|
is-bigint@1.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -8829,11 +8819,6 @@ snapshots:
|
|||||||
|
|
||||||
is-obj@1.0.1: {}
|
is-obj@1.0.1: {}
|
||||||
|
|
||||||
is-regex@1.1.4:
|
|
||||||
dependencies:
|
|
||||||
call-bind: 1.0.7
|
|
||||||
has-tostringtag: 1.0.2
|
|
||||||
|
|
||||||
is-regex@1.2.1:
|
is-regex@1.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
@ -8855,7 +8840,7 @@ snapshots:
|
|||||||
|
|
||||||
is-symbol@1.0.4:
|
is-symbol@1.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
has-symbols: 1.0.3
|
has-symbols: 1.1.0
|
||||||
|
|
||||||
is-typed-array@1.1.13:
|
is-typed-array@1.1.13:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -9542,8 +9527,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
boolbase: 1.0.0
|
boolbase: 1.0.0
|
||||||
|
|
||||||
object-inspect@1.13.2: {}
|
|
||||||
|
|
||||||
object-inspect@1.13.4: {}
|
object-inspect@1.13.4: {}
|
||||||
|
|
||||||
object-is@1.1.6:
|
object-is@1.1.6:
|
||||||
@ -9986,20 +9969,14 @@ snapshots:
|
|||||||
safe-array-concat@1.1.2:
|
safe-array-concat@1.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.7
|
call-bind: 1.0.7
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.3.0
|
||||||
has-symbols: 1.0.3
|
has-symbols: 1.1.0
|
||||||
isarray: 2.0.5
|
isarray: 2.0.5
|
||||||
|
|
||||||
safe-buffer@5.1.2: {}
|
safe-buffer@5.1.2: {}
|
||||||
|
|
||||||
safe-buffer@5.2.1: {}
|
safe-buffer@5.2.1: {}
|
||||||
|
|
||||||
safe-regex-test@1.0.3:
|
|
||||||
dependencies:
|
|
||||||
call-bind: 1.0.7
|
|
||||||
es-errors: 1.3.0
|
|
||||||
is-regex: 1.1.4
|
|
||||||
|
|
||||||
safe-regex-test@1.1.0:
|
safe-regex-test@1.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
@ -10098,13 +10075,6 @@ snapshots:
|
|||||||
object-inspect: 1.13.4
|
object-inspect: 1.13.4
|
||||||
side-channel-map: 1.0.1
|
side-channel-map: 1.0.1
|
||||||
|
|
||||||
side-channel@1.0.6:
|
|
||||||
dependencies:
|
|
||||||
call-bind: 1.0.7
|
|
||||||
es-errors: 1.3.0
|
|
||||||
get-intrinsic: 1.2.4
|
|
||||||
object-inspect: 1.13.2
|
|
||||||
|
|
||||||
side-channel@1.1.0:
|
side-channel@1.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
@ -10229,33 +10199,33 @@ snapshots:
|
|||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
es-abstract: 1.23.3
|
es-abstract: 1.23.3
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
es-object-atoms: 1.0.0
|
es-object-atoms: 1.1.1
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.3.0
|
||||||
gopd: 1.0.1
|
gopd: 1.2.0
|
||||||
has-symbols: 1.0.3
|
has-symbols: 1.1.0
|
||||||
internal-slot: 1.0.7
|
internal-slot: 1.0.7
|
||||||
regexp.prototype.flags: 1.5.2
|
regexp.prototype.flags: 1.5.2
|
||||||
set-function-name: 2.0.2
|
set-function-name: 2.0.2
|
||||||
side-channel: 1.0.6
|
side-channel: 1.1.0
|
||||||
|
|
||||||
string.prototype.trim@1.2.9:
|
string.prototype.trim@1.2.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.7
|
call-bind: 1.0.7
|
||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
es-abstract: 1.23.3
|
es-abstract: 1.23.3
|
||||||
es-object-atoms: 1.0.0
|
es-object-atoms: 1.1.1
|
||||||
|
|
||||||
string.prototype.trimend@1.0.8:
|
string.prototype.trimend@1.0.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.7
|
call-bind: 1.0.7
|
||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
es-object-atoms: 1.0.0
|
es-object-atoms: 1.1.1
|
||||||
|
|
||||||
string.prototype.trimstart@1.0.8:
|
string.prototype.trimstart@1.0.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.7
|
call-bind: 1.0.7
|
||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
es-object-atoms: 1.0.0
|
es-object-atoms: 1.1.1
|
||||||
|
|
||||||
string_decoder@1.1.1:
|
string_decoder@1.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -10475,7 +10445,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.7
|
call-bind: 1.0.7
|
||||||
for-each: 0.3.3
|
for-each: 0.3.3
|
||||||
gopd: 1.0.1
|
gopd: 1.2.0
|
||||||
has-proto: 1.0.3
|
has-proto: 1.0.3
|
||||||
is-typed-array: 1.1.13
|
is-typed-array: 1.1.13
|
||||||
|
|
||||||
@ -10484,7 +10454,7 @@ snapshots:
|
|||||||
available-typed-arrays: 1.0.7
|
available-typed-arrays: 1.0.7
|
||||||
call-bind: 1.0.7
|
call-bind: 1.0.7
|
||||||
for-each: 0.3.3
|
for-each: 0.3.3
|
||||||
gopd: 1.0.1
|
gopd: 1.2.0
|
||||||
has-proto: 1.0.3
|
has-proto: 1.0.3
|
||||||
is-typed-array: 1.1.13
|
is-typed-array: 1.1.13
|
||||||
|
|
||||||
@ -10492,7 +10462,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.7
|
call-bind: 1.0.7
|
||||||
for-each: 0.3.3
|
for-each: 0.3.3
|
||||||
gopd: 1.0.1
|
gopd: 1.2.0
|
||||||
has-proto: 1.0.3
|
has-proto: 1.0.3
|
||||||
is-typed-array: 1.1.13
|
is-typed-array: 1.1.13
|
||||||
possible-typed-array-names: 1.0.0
|
possible-typed-array-names: 1.0.0
|
||||||
@ -10501,11 +10471,13 @@ snapshots:
|
|||||||
|
|
||||||
ufo@1.5.4: {}
|
ufo@1.5.4: {}
|
||||||
|
|
||||||
|
ultrahtml@1.6.0: {}
|
||||||
|
|
||||||
unbox-primitive@1.0.2:
|
unbox-primitive@1.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.7
|
call-bind: 1.0.7
|
||||||
has-bigints: 1.0.2
|
has-bigints: 1.0.2
|
||||||
has-symbols: 1.0.3
|
has-symbols: 1.1.0
|
||||||
which-boxed-primitive: 1.0.2
|
which-boxed-primitive: 1.0.2
|
||||||
|
|
||||||
unconfig@7.3.1:
|
unconfig@7.3.1:
|
||||||
|
|||||||
@ -32,6 +32,7 @@ async function packIconFont() {
|
|||||||
progressMessage.value = 'Packing up...'
|
progressMessage.value = 'Packing up...'
|
||||||
await nextTick()
|
await nextTick()
|
||||||
await PackIconFont(
|
await PackIconFont(
|
||||||
|
[props.collection],
|
||||||
props.collection.icons.map(i => `${props.collection!.id}:${i}`),
|
props.collection.icons.map(i => `${props.collection!.id}:${i}`),
|
||||||
{ fontName: props.collection.name, fileName: props.collection.id },
|
{ fontName: props.collection.name, fileName: props.collection.id },
|
||||||
)
|
)
|
||||||
@ -49,6 +50,7 @@ async function packSvgs() {
|
|||||||
progressMessage.value = 'Packing up...'
|
progressMessage.value = 'Packing up...'
|
||||||
await nextTick()
|
await nextTick()
|
||||||
await PackSvgZip(
|
await PackSvgZip(
|
||||||
|
[props.collection],
|
||||||
props.collection.icons.map(i => `${props.collection!.id}:${i}`),
|
props.collection.icons.map(i => `${props.collection!.id}:${i}`),
|
||||||
props.collection.id,
|
props.collection.id,
|
||||||
)
|
)
|
||||||
@ -66,6 +68,7 @@ async function packJson() {
|
|||||||
progressMessage.value = 'Packing up...'
|
progressMessage.value = 'Packing up...'
|
||||||
await nextTick()
|
await nextTick()
|
||||||
await PackJsonZip(
|
await PackJsonZip(
|
||||||
|
[props.collection],
|
||||||
props.collection.icons.map(i => `${props.collection!.id}:${i}`),
|
props.collection.icons.map(i => `${props.collection!.id}:${i}`),
|
||||||
props.collection.id,
|
props.collection.id,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
import type { PackType } from '../utils/pack'
|
import type { PackType } from '../utils/svg'
|
||||||
|
import { collections } from '../data'
|
||||||
import { bags, clearBag } from '../store'
|
import { bags, clearBag } from '../store'
|
||||||
import { PackIconFont, PackSVGSprite, PackZip } from '../utils/pack'
|
import { PackIconFont, PackSVGSprite, PackZip } from '../utils/pack'
|
||||||
|
|
||||||
@ -21,18 +22,21 @@ function clear() {
|
|||||||
async function packIconFont() {
|
async function packIconFont() {
|
||||||
// TODO: customzie
|
// TODO: customzie
|
||||||
await PackIconFont(
|
await PackIconFont(
|
||||||
|
collections,
|
||||||
bags.value,
|
bags.value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function packSVGSprite() {
|
async function packSVGSprite() {
|
||||||
await PackSVGSprite(
|
await PackSVGSprite(
|
||||||
|
collections,
|
||||||
bags.value,
|
bags.value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function PackSvgs(type: PackType = 'svg') {
|
async function PackSvgs(type: PackType = 'svg') {
|
||||||
await PackZip(
|
await PackZip(
|
||||||
|
collections,
|
||||||
bags.value,
|
bags.value,
|
||||||
'icones-bags',
|
'icones-bags',
|
||||||
type,
|
type,
|
||||||
|
|||||||
@ -3,8 +3,7 @@ import { collections } from '../data'
|
|||||||
import { activeMode, copyPreviewColor, getTransformedId, inBag, preferredCase, previewColor, pushRecentIcon, showCaseSelect, showHelp, toggleBag } from '../store'
|
import { activeMode, copyPreviewColor, getTransformedId, inBag, preferredCase, previewColor, pushRecentIcon, showCaseSelect, showHelp, toggleBag } from '../store'
|
||||||
import { idCases } from '../utils/case'
|
import { idCases } from '../utils/case'
|
||||||
import { dataUrlToBlob } from '../utils/dataUrlToBlob'
|
import { dataUrlToBlob } from '../utils/dataUrlToBlob'
|
||||||
import { getIconSnippet, SnippetMap, toComponentName } from '../utils/icons'
|
import { Download, getIconSnippet, SnippetMap, toComponentName } from '../utils/icons'
|
||||||
import { Download } from '../utils/pack'
|
|
||||||
import InstallIconSet from './InstallIconSet.vue'
|
import InstallIconSet from './InstallIconSet.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -70,7 +69,7 @@ async function copyPng(dataUrl: string): Promise<boolean> {
|
|||||||
async function copy(type: string) {
|
async function copy(type: string) {
|
||||||
pushRecentIcon(props.icon)
|
pushRecentIcon(props.icon)
|
||||||
|
|
||||||
const svg = await getIconSnippet(props.icon, type, true, color.value)
|
const svg = await getIconSnippet(collections, props.icon, type, true, color.value)
|
||||||
if (!svg)
|
if (!svg)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -84,7 +83,7 @@ async function copy(type: string) {
|
|||||||
|
|
||||||
async function download(type: string) {
|
async function download(type: string) {
|
||||||
pushRecentIcon(props.icon)
|
pushRecentIcon(props.icon)
|
||||||
const text = await getIconSnippet(props.icon, type, false, color.value)
|
const text = await getIconSnippet(collections, props.icon, type, false, color.value)
|
||||||
if (!text)
|
if (!text)
|
||||||
return
|
return
|
||||||
const ext = (type === 'solid' || type === 'qwik' || type === 'react-native') ? 'tsx' : type
|
const ext = (type === 'solid' || type === 'qwik' || type === 'react-native') ? 'tsx' : type
|
||||||
@ -212,6 +211,7 @@ const collection = computed(() => {
|
|||||||
<div class="flex gap-1">
|
<div class="flex gap-1">
|
||||||
<template v-for="(snippet, type) in group" :key="`${icon}-${groupName}-${type}`">
|
<template v-for="(snippet, type) in group" :key="`${icon}-${groupName}-${type}`">
|
||||||
<SnippetPreview
|
<SnippetPreview
|
||||||
|
:collection="collection"
|
||||||
:icon="icon"
|
:icon="icon"
|
||||||
:snippet="snippet"
|
:snippet="snippet"
|
||||||
:type="type"
|
:type="type"
|
||||||
|
|||||||
@ -70,7 +70,12 @@ async function onSelect(icon: string) {
|
|||||||
toggleBag(icon)
|
toggleBag(icon)
|
||||||
break
|
break
|
||||||
case 'copy':
|
case 'copy':
|
||||||
onCopy(await copyText(await getIconSnippet(icon, 'id', true) || icon))
|
onCopy(await copyText(await getIconSnippet(
|
||||||
|
[collection.value!],
|
||||||
|
icon,
|
||||||
|
'id',
|
||||||
|
true,
|
||||||
|
) || icon))
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
current.value = icon
|
current.value = icon
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
<script lang='ts' setup>
|
<script lang='ts' setup>
|
||||||
|
import type { CollectionInfo } from '../data'
|
||||||
import type { Snippet } from '../utils/icons'
|
import type { Snippet } from '../utils/icons'
|
||||||
import { Menu } from 'floating-vue'
|
import { Menu } from 'floating-vue'
|
||||||
|
import { collections } from '../data'
|
||||||
import { getIconSnippet } from '../utils/icons'
|
import { getIconSnippet } from '../utils/icons'
|
||||||
import { prettierCode } from '../utils/prettier'
|
|
||||||
import { highlight } from '../utils/shiki'
|
import { highlight } from '../utils/shiki'
|
||||||
|
import { prettierCode } from '../utils/svg'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
collection?: CollectionInfo
|
||||||
icon: string
|
icon: string
|
||||||
snippet: Snippet
|
snippet: Snippet
|
||||||
type: string
|
type: string
|
||||||
@ -15,8 +18,15 @@ const props = defineProps<{
|
|||||||
const code = ref<string>('')
|
const code = ref<string>('')
|
||||||
|
|
||||||
async function onShow() {
|
async function onShow() {
|
||||||
if (!code.value)
|
if (!code.value) {
|
||||||
code.value = await getIconSnippet(props.icon, props.type, false, props.color) || ''
|
code.value = await getIconSnippet(
|
||||||
|
props.collection ? [props.collection] : collections,
|
||||||
|
props.icon,
|
||||||
|
props.type,
|
||||||
|
false,
|
||||||
|
props.color,
|
||||||
|
) || ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const highlightCode = computedAsync(async () => {
|
const highlightCode = computedAsync(async () => {
|
||||||
|
|||||||
@ -21,7 +21,7 @@ const router = createRouter({
|
|||||||
if (!isElectron && PWA) {
|
if (!isElectron && PWA) {
|
||||||
// disable local storage cache when there is PWA:
|
// disable local storage cache when there is PWA:
|
||||||
// we need to keep local storage when running dev server without PWA
|
// we need to keep local storage when running dev server without PWA
|
||||||
// to avoid send requests to iconify server api
|
// to avoid sending requests to iconify server api
|
||||||
disableCache('all')
|
disableCache('all')
|
||||||
router.isReady().then(async () => {
|
router.isReady().then(async () => {
|
||||||
const { registerSW } = await import('virtual:pwa-register')
|
const { registerSW } = await import('virtual:pwa-register')
|
||||||
|
|||||||
@ -1,11 +1,24 @@
|
|||||||
import type { BuiltInParserName as PrettierParser } from 'prettier'
|
import type { BuiltInParserName as PrettierParser } from 'prettier'
|
||||||
import { encodeSvgForCss } from '@iconify/utils'
|
import type { CollectionInfo } from '../data'
|
||||||
import { buildIcon, loadIcon } from 'iconify-icon'
|
import { isVSCode } from '../env'
|
||||||
import { collections } from '../data'
|
|
||||||
import { getTransformedId } from '../store'
|
import { getTransformedId } from '../store'
|
||||||
import Base64 from './base64'
|
import { getSvgSymbol } from './pack'
|
||||||
import { HtmlToJSX } from './htmlToJsx'
|
import {
|
||||||
import { prettierCode } from './prettier'
|
API_ENTRY,
|
||||||
|
bufferToString,
|
||||||
|
ClearSvg,
|
||||||
|
getSvg,
|
||||||
|
SvgToAstro,
|
||||||
|
SvgToDataURL,
|
||||||
|
SvgToJSX,
|
||||||
|
SvgToQwik,
|
||||||
|
SvgToReactNative,
|
||||||
|
SvgToSolid,
|
||||||
|
SvgToSvelte,
|
||||||
|
SvgToTSX,
|
||||||
|
SvgToVue,
|
||||||
|
toComponentName,
|
||||||
|
} from './svg'
|
||||||
import { svgToPngDataUrl } from './svgToPng'
|
import { svgToPngDataUrl } from './svgToPng'
|
||||||
|
|
||||||
export interface Snippet {
|
export interface Snippet {
|
||||||
@ -14,6 +27,27 @@ export interface Snippet {
|
|||||||
lang: string // for shiki
|
lang: string // for shiki
|
||||||
prettierParser: PrettierParser // for prettier
|
prettierParser: PrettierParser // for prettier
|
||||||
}
|
}
|
||||||
|
export { toComponentName }
|
||||||
|
|
||||||
|
export async function Download(blob: Blob, name: string) {
|
||||||
|
if (isVSCode) {
|
||||||
|
blob.arrayBuffer().then(
|
||||||
|
buffer => vscode.postMessage({
|
||||||
|
command: 'download',
|
||||||
|
name,
|
||||||
|
text: bufferToString(buffer),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
const a = document.createElement('a')
|
||||||
|
a.href = url
|
||||||
|
a.download = name
|
||||||
|
a.click()
|
||||||
|
a.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const SnippetMap: Record<string, Record<string, Snippet>> = {
|
export const SnippetMap: Record<string, Record<string, Snippet>> = {
|
||||||
Snippets: {
|
Snippets: {
|
||||||
@ -41,230 +75,13 @@ export const SnippetMap: Record<string, Record<string, Snippet>> = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const API_ENTRY = 'https://api.iconify.design'
|
export async function getIconSnippet(
|
||||||
|
collections: CollectionInfo[],
|
||||||
function getLicenseComment(icon: string) {
|
icon: string,
|
||||||
const [id] = icon.split(':')
|
type: string,
|
||||||
const collection = collections.find(i => i.id === id)
|
snippet = true,
|
||||||
if (!collection) {
|
color = 'currentColor',
|
||||||
return ''
|
): Promise<string | undefined> {
|
||||||
}
|
|
||||||
return `<!-- Icon from ${collection?.name} by ${collection?.author?.name} - ${collection?.license?.url} -->`
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getSvgLocal(icon: string, size = '1em', color = 'currentColor') {
|
|
||||||
const data = await loadIcon(icon)
|
|
||||||
if (!data)
|
|
||||||
return
|
|
||||||
const built = buildIcon(data, { height: size })
|
|
||||||
if (!built)
|
|
||||||
return
|
|
||||||
const license = getLicenseComment(icon)
|
|
||||||
const xlink = built.body.includes('xlink:') ? ' xmlns:xlink="http://www.w3.org/1999/xlink"' : ''
|
|
||||||
return `<svg xmlns="http://www.w3.org/2000/svg"${xlink} ${Object.entries(built.attributes).map(([k, v]) => `${k}="${v}"`).join(' ')}>${license}${built.body}</svg>`.replaceAll('currentColor', color)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getSvg(icon: string, size = '1em', color = 'currentColor') {
|
|
||||||
return await getSvgLocal(icon, size, color)
|
|
||||||
|| await fetch(`${API_ENTRY}/${icon}.svg?inline=false&height=${size}&color=${encodeURIComponent(color)}`).then(r => r.text()) || ''
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getSvgSymbol(icon: string, size = '1em', color = 'currentColor') {
|
|
||||||
const svgMarkup = await getSvg(icon, size, color)
|
|
||||||
|
|
||||||
const symbolElem = document.createElementNS('http://www.w3.org/2000/svg', 'symbol')
|
|
||||||
const node = document.createElement('div') // Create any old element
|
|
||||||
node.innerHTML = svgMarkup
|
|
||||||
|
|
||||||
// Grab the inner HTML and move into a symbol element
|
|
||||||
symbolElem.innerHTML = node.querySelector('svg')!.innerHTML
|
|
||||||
symbolElem.setAttribute('viewBox', node.querySelector('svg')!.getAttribute('viewBox')!)
|
|
||||||
symbolElem.id = icon.replace(/:/, '-') // Simple slugify for quick symbol lookup
|
|
||||||
|
|
||||||
return symbolElem?.outerHTML
|
|
||||||
}
|
|
||||||
|
|
||||||
export function toComponentName(icon: string) {
|
|
||||||
return icon.split(/[:\-_]/).filter(Boolean).map(s => s[0].toUpperCase() + s.slice(1).toLowerCase()).join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ClearSvg(svgCode: string, reactJSX?: boolean) {
|
|
||||||
const el = document.createElement('div')
|
|
||||||
el.innerHTML = svgCode
|
|
||||||
const svg = el.getElementsByTagName('svg')[0]
|
|
||||||
const keep = ['viewBox', 'width', 'height', 'focusable', 'xmlns', 'xlink']
|
|
||||||
for (const key of Object.values(svg.attributes)) {
|
|
||||||
if (keep.includes(key.localName))
|
|
||||||
continue
|
|
||||||
svg.removeAttributeNode(key)
|
|
||||||
}
|
|
||||||
return HtmlToJSX(el.innerHTML, reactJSX)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SvgToJSX(svg: string, name: string, snippet: boolean) {
|
|
||||||
const code = `
|
|
||||||
export function ${name}(props) {
|
|
||||||
return (
|
|
||||||
${ClearSvg(svg, true).replace(/<svg (.*?)>/, '<svg $1 {...props}>')}
|
|
||||||
)
|
|
||||||
}`
|
|
||||||
if (snippet)
|
|
||||||
return prettierCode(code, 'babel-ts')
|
|
||||||
else
|
|
||||||
return prettierCode(`import React from 'react'\n${code}\nexport default ${name}`, 'babel-ts')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SvgToTSX(svg: string, name: string, snippet: boolean, reactJSX = true) {
|
|
||||||
let code = `
|
|
||||||
export function ${name}(props: SVGProps<SVGSVGElement>) {
|
|
||||||
return (
|
|
||||||
${ClearSvg(svg, reactJSX).replace(/<svg (.*?)>/, '<svg $1 {...props}>')}
|
|
||||||
)
|
|
||||||
}`
|
|
||||||
|
|
||||||
code = snippet ? code : `import React, { SVGProps } from 'react'\n${code}\nexport default ${name}`
|
|
||||||
return prettierCode(code, 'babel-ts')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SvgToQwik(svg: string, name: string, snippet: boolean) {
|
|
||||||
let code = `
|
|
||||||
export function ${name}(props: QwikIntrinsicElements['svg'], key: string) {
|
|
||||||
return (
|
|
||||||
${ClearSvg(svg, false).replace(/<svg (.*?)>/, '<svg $1 {...props} key={key}>')}
|
|
||||||
)
|
|
||||||
}`
|
|
||||||
|
|
||||||
code = snippet ? code : `import type { QwikIntrinsicElements } from '@builder.io/qwik'\n${code}\nexport default ${name}`
|
|
||||||
return prettierCode(code, 'babel-ts')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SvgToVue(svg: string, name: string, isTs?: boolean) {
|
|
||||||
const content = `
|
|
||||||
<template>
|
|
||||||
${ClearSvg(svg)}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: '${name}'
|
|
||||||
}
|
|
||||||
</script>`
|
|
||||||
const code = isTs ? content.replace('<script>', '<script lang="ts">') : content
|
|
||||||
return prettierCode(code, 'vue')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SvgToSolid(svg: string, name: string, snippet: boolean) {
|
|
||||||
let code = `
|
|
||||||
export function ${name}(props: JSX.IntrinsicElements['svg']) {
|
|
||||||
return (
|
|
||||||
${svg.replace(/<svg (.*?)>/, '<svg $1 {...props}>')}
|
|
||||||
)
|
|
||||||
}`
|
|
||||||
|
|
||||||
code = snippet ? code : `import type { JSX } from 'solid-js'\n${code}\nexport default ${name}`
|
|
||||||
return prettierCode(code, 'babel-ts')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SvgToSvelte(svg: string) {
|
|
||||||
return `${svg.replace(/<svg (.*?)>/, '<svg $1 {...$$$props}>')}`
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SvgToAstro(svg: string) {
|
|
||||||
return `
|
|
||||||
---
|
|
||||||
const props = Astro.props
|
|
||||||
---
|
|
||||||
|
|
||||||
${svg.replace(/<svg (.*?)>/, '<svg $1 {...props}>')}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SvgToReactNative(svg: string, name: string, snippet: boolean) {
|
|
||||||
function replaceTags(svg: string, replacements: {
|
|
||||||
from: string
|
|
||||||
to: string
|
|
||||||
}[]): string {
|
|
||||||
let result = svg
|
|
||||||
replacements.forEach(({ from, to }) => {
|
|
||||||
result = result.replace(new RegExp(`<${from}(.*?)>`, 'g'), `<${to}$1>`)
|
|
||||||
.replace(new RegExp(`</${from}>`, 'g'), `</${to}>`)
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateImports(usedComponents: string[]): string {
|
|
||||||
// Separate Svg from the other components
|
|
||||||
const svgIndex = usedComponents.indexOf('Svg')
|
|
||||||
if (svgIndex !== -1)
|
|
||||||
usedComponents.splice(svgIndex, 1)
|
|
||||||
|
|
||||||
// Join all other component names with a comma and wrap them in curly braces
|
|
||||||
const componentsString = usedComponents.length > 0 ? `{ ${usedComponents.join(', ')} }` : ''
|
|
||||||
|
|
||||||
// Return the consolidated import statement, ensuring Svg is imported as a default import
|
|
||||||
return `import Svg, ${componentsString} from 'react-native-svg';`
|
|
||||||
}
|
|
||||||
|
|
||||||
const replacements: {
|
|
||||||
from: string
|
|
||||||
to: string
|
|
||||||
}[] = [
|
|
||||||
{ from: 'svg', to: 'Svg' },
|
|
||||||
{ from: 'path', to: 'Path' },
|
|
||||||
{ from: 'g', to: 'G' },
|
|
||||||
{ from: 'circle', to: 'Circle' },
|
|
||||||
{ from: 'rect', to: 'Rect' },
|
|
||||||
{ from: 'line', to: 'Line' },
|
|
||||||
{ from: 'polyline', to: 'Polyline' },
|
|
||||||
{ from: 'polygon', to: 'Polygon' },
|
|
||||||
{ from: 'ellipse', to: 'Ellipse' },
|
|
||||||
{ from: 'text', to: 'Text' },
|
|
||||||
{ from: 'tspan', to: 'Tspan' },
|
|
||||||
{ from: 'textPath', to: 'TextPath' },
|
|
||||||
{ from: 'defs', to: 'Defs' },
|
|
||||||
{ from: 'use', to: 'Use' },
|
|
||||||
{ from: 'symbol', to: 'Symbol' },
|
|
||||||
{ from: 'linearGradient', to: 'LinearGradient' },
|
|
||||||
{ from: 'radialGradient', to: 'RadialGradient' },
|
|
||||||
{ from: 'stop', to: 'Stop' },
|
|
||||||
]
|
|
||||||
|
|
||||||
const reactNativeSvgCode = replaceTags(ClearSvg(svg, true), replacements)
|
|
||||||
.replace(/className=/g, '')
|
|
||||||
.replace(/href=/g, 'xlinkHref=')
|
|
||||||
.replace(/clip-path=/g, 'clipPath=')
|
|
||||||
.replace(/fill-opacity=/g, 'fillOpacity=')
|
|
||||||
.replace(/stroke-width=/g, 'strokeWidth=')
|
|
||||||
.replace(/stroke-linecap=/g, 'strokeLinecap=')
|
|
||||||
.replace(/stroke-linejoin=/g, 'strokeLinejoin=')
|
|
||||||
.replace(/stroke-miterlimit=/g, 'strokeMiterlimit=')
|
|
||||||
|
|
||||||
const svgComponents = replacements.map(({ to }) => to)
|
|
||||||
const imports = generateImports(svgComponents.filter(component => reactNativeSvgCode.includes(component)))
|
|
||||||
|
|
||||||
let code = `
|
|
||||||
${imports}
|
|
||||||
|
|
||||||
export function ${name}(props) {
|
|
||||||
return (
|
|
||||||
${reactNativeSvgCode}
|
|
||||||
)
|
|
||||||
}`
|
|
||||||
|
|
||||||
if (!snippet)
|
|
||||||
code = `import React from 'react';\n${code}\nexport default ${name}`
|
|
||||||
|
|
||||||
return prettierCode(code, 'babel-ts')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SvgToDataURL(svg: string) {
|
|
||||||
const base64 = `data:image/svg+xml;base64,${Base64.encode(svg)}`
|
|
||||||
const plain = `data:image/svg+xml,${encodeSvgForCss(svg)}`
|
|
||||||
// Return the shorter of the two data URLs
|
|
||||||
return base64.length < plain.length ? base64 : plain
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getIconSnippet(icon: string, type: string, snippet = true, color = 'currentColor'): Promise<string | undefined> {
|
|
||||||
if (!icon)
|
if (!icon)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -282,33 +99,33 @@ export async function getIconSnippet(icon: string, type: string, snippet = true,
|
|||||||
case 'css':
|
case 'css':
|
||||||
return `background: url('${url}') no-repeat center center / contain;`
|
return `background: url('${url}') no-repeat center center / contain;`
|
||||||
case 'svg':
|
case 'svg':
|
||||||
return await getSvg(icon, '32', color)
|
return await getSvg(collections, icon, '32', color)
|
||||||
case 'png':
|
case 'png':
|
||||||
return await svgToPngDataUrl(await getSvg(icon, '32', color))
|
return await svgToPngDataUrl(await getSvg(collections, icon, '32', color))
|
||||||
case 'svg-symbol':
|
case 'svg-symbol':
|
||||||
return await getSvgSymbol(icon, '32', color)
|
return await getSvgSymbol(collections, icon, '32', color)
|
||||||
case 'data_url':
|
case 'data_url':
|
||||||
return SvgToDataURL(await getSvg(icon, undefined, color))
|
return SvgToDataURL(await getSvg(collections, icon, undefined, color))
|
||||||
case 'pure-jsx':
|
case 'pure-jsx':
|
||||||
return ClearSvg(await getSvg(icon, undefined, color))
|
return ClearSvg(await getSvg(collections, icon, undefined, color))
|
||||||
case 'jsx':
|
case 'jsx':
|
||||||
return SvgToJSX(await getSvg(icon, undefined, color), toComponentName(icon), snippet)
|
return SvgToJSX(await getSvg(collections, icon, undefined, color), toComponentName(icon), snippet)
|
||||||
case 'tsx':
|
case 'tsx':
|
||||||
return SvgToTSX(await getSvg(icon, undefined, color), toComponentName(icon), snippet)
|
return SvgToTSX(await getSvg(collections, icon, undefined, color), toComponentName(icon), snippet)
|
||||||
case 'qwik':
|
case 'qwik':
|
||||||
return SvgToQwik(await getSvg(icon, undefined, color), toComponentName(icon), snippet)
|
return SvgToQwik(await getSvg(collections, icon, undefined, color), toComponentName(icon), snippet)
|
||||||
case 'vue':
|
case 'vue':
|
||||||
return SvgToVue(await getSvg(icon, undefined, color), toComponentName(icon))
|
return SvgToVue(await getSvg(collections, icon, undefined, color), toComponentName(icon))
|
||||||
case 'vue-ts':
|
case 'vue-ts':
|
||||||
return SvgToVue(await getSvg(icon, undefined, color), toComponentName(icon), true)
|
return SvgToVue(await getSvg(collections, icon, undefined, color), toComponentName(icon), true)
|
||||||
case 'solid':
|
case 'solid':
|
||||||
return SvgToSolid(await getSvg(icon, undefined, color), toComponentName(icon), snippet)
|
return SvgToSolid(await getSvg(collections, icon, undefined, color), toComponentName(icon), snippet)
|
||||||
case 'svelte':
|
case 'svelte':
|
||||||
return SvgToSvelte(await getSvg(icon, undefined, color))
|
return SvgToSvelte(await getSvg(collections, icon, undefined, color))
|
||||||
case 'astro':
|
case 'astro':
|
||||||
return SvgToAstro(await getSvg(icon, undefined, color))
|
return SvgToAstro(await getSvg(collections, icon, undefined, color))
|
||||||
case 'react-native':
|
case 'react-native':
|
||||||
return SvgToReactNative(await getSvg(icon, undefined, color), toComponentName(icon), snippet)
|
return SvgToReactNative(await getSvg(collections, icon, undefined, color), toComponentName(icon), snippet)
|
||||||
case 'unplugin':
|
case 'unplugin':
|
||||||
return `import ${toComponentName(icon)} from '~icons/${icon.split(':')[0]}/${icon.split(':')[1]}'`
|
return `import ${toComponentName(icon)} from '~icons/${icon.split(':')[0]}/${icon.split(':')[1]}'`
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/utils/pack-worker-client.ts
Normal file
5
src/utils/pack-worker-client.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import PackerWorker from './worker?worker'
|
||||||
|
|
||||||
|
export const packerWorker = new PackerWorker({
|
||||||
|
name: 'IconesPackWorker',
|
||||||
|
})
|
||||||
@ -1,79 +1,40 @@
|
|||||||
import { isVSCode } from '../env'
|
import type { CollectionInfo } from '../data'
|
||||||
import { bufferToString } from './bufferToString'
|
import type { PackType } from './svg'
|
||||||
import {
|
import { Download } from './icons'
|
||||||
getSvg,
|
import { getSvg, LoadIconSvgs } from './svg'
|
||||||
getSvgSymbol,
|
|
||||||
SvgToJSX,
|
|
||||||
SvgToTSX,
|
|
||||||
SvgToVue,
|
|
||||||
toComponentName,
|
|
||||||
} from './icons'
|
|
||||||
|
|
||||||
export async function LoadIconSvgs(icons: string[]) {
|
export async function getSvgSymbol(
|
||||||
return await Promise.all(
|
collections: CollectionInfo[],
|
||||||
icons
|
icon: string,
|
||||||
.filter(Boolean)
|
size = '1em',
|
||||||
.sort()
|
color = 'currentColor',
|
||||||
.map(async (name) => {
|
) {
|
||||||
return {
|
const svgMarkup = await getSvg(collections, icon, size, color)
|
||||||
name,
|
|
||||||
svg: await getSvg(name),
|
const symbolElem = document.createElementNS('http://www.w3.org/2000/svg', 'symbol')
|
||||||
}
|
const node = document.createElement('div') // Create any old element
|
||||||
}),
|
node.innerHTML = svgMarkup
|
||||||
)
|
|
||||||
|
// Grab the inner HTML and move into a symbol element
|
||||||
|
symbolElem.innerHTML = node.querySelector('svg')!.innerHTML
|
||||||
|
symbolElem.setAttribute('viewBox', node.querySelector('svg')!.getAttribute('viewBox')!)
|
||||||
|
symbolElem.id = icon.replace(/:/, '-') // Simple slugify for quick symbol lookup
|
||||||
|
|
||||||
|
return symbolElem?.outerHTML
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function* PrepareIconSvgs(icons: string[], format: 'svg' | 'json', name?: string) {
|
export async function PackSVGSprite(
|
||||||
if (format === 'json') {
|
collections: CollectionInfo[],
|
||||||
const svgs = await LoadIconSvgs(icons)
|
icons: string[],
|
||||||
yield {
|
options: any = {},
|
||||||
name: `${name}.json`,
|
) {
|
||||||
input: new Blob([JSON.stringify(svgs, null, 2)], { type: 'application/json; charset=utf-8' }),
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const icon of icons) {
|
|
||||||
if (!icon)
|
|
||||||
continue
|
|
||||||
|
|
||||||
const svg = await getSvg(icon)
|
|
||||||
|
|
||||||
yield {
|
|
||||||
name: `${normalizeZipFleName(icon)}.svg`,
|
|
||||||
input: new Blob([svg], { type: 'image/svg+xml' }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function Download(blob: Blob, name: string) {
|
|
||||||
if (isVSCode) {
|
|
||||||
blob.arrayBuffer().then(
|
|
||||||
buffer => vscode.postMessage({
|
|
||||||
command: 'download',
|
|
||||||
name,
|
|
||||||
text: bufferToString(buffer),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const url = URL.createObjectURL(blob)
|
|
||||||
const a = document.createElement('a')
|
|
||||||
a.href = url
|
|
||||||
a.download = name
|
|
||||||
a.click()
|
|
||||||
a.remove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function PackSVGSprite(icons: string[], options: any = {}) {
|
|
||||||
if (!icons.length)
|
if (!icons.length)
|
||||||
return
|
return
|
||||||
const data = await LoadIconSvgs(icons)
|
const data = await LoadIconSvgs(collections, icons)
|
||||||
|
|
||||||
let symbols = ''
|
let symbols = ''
|
||||||
for (const { name } of data)
|
for (const { name } of data)
|
||||||
symbols += `${await getSvgSymbol(name, options.size, options.color)}\n`
|
symbols += `${await getSvgSymbol(collections, name, options.size, options.color)}\n`
|
||||||
|
|
||||||
const svg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
const svg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<defs>
|
<defs>
|
||||||
@ -85,96 +46,112 @@ ${symbols}
|
|||||||
Download(blob, 'sprite.svg')
|
Download(blob, 'sprite.svg')
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeZipFleName(svgName: string): string {
|
export async function PackIconFont(
|
||||||
return svgName.replace(':', '-')
|
collections: CollectionInfo[],
|
||||||
}
|
icons: string[],
|
||||||
|
options: any = {},
|
||||||
export async function PackIconFont(icons: string[], options: any = {}) {
|
) {
|
||||||
if (!icons.length)
|
if (!icons.length)
|
||||||
return
|
return
|
||||||
|
|
||||||
const [data, { SvgPacker }] = await Promise.all([
|
const { packerWorker } = await import('./pack-worker-client')
|
||||||
LoadIconSvgs(icons),
|
|
||||||
import('svg-packer'),
|
return new Promise<void>((resolve, reject) => {
|
||||||
])
|
packerWorker.addEventListener('message', (event) => {
|
||||||
const result = await SvgPacker({
|
if (event.data.error) {
|
||||||
fontName: 'Iconify Explorer Font',
|
reject(event.data.error)
|
||||||
fileName: 'iconfont',
|
return
|
||||||
cssPrefix: 'i',
|
}
|
||||||
...options,
|
const { blob, name } = event.data as { blob: ArrayBuffer, name: string }
|
||||||
icons: data,
|
Download(
|
||||||
|
new Blob([blob]),
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
resolve()
|
||||||
|
}, { once: true })
|
||||||
|
const arrayBuffer = createArrayBufferFromCollections(collections)
|
||||||
|
packerWorker.postMessage({
|
||||||
|
operation: 'pack-font-zip',
|
||||||
|
collections: arrayBuffer,
|
||||||
|
payload: {
|
||||||
|
icons: toRaw(icons),
|
||||||
|
...toRaw(options),
|
||||||
|
},
|
||||||
|
}, [arrayBuffer])
|
||||||
})
|
})
|
||||||
|
|
||||||
Download(result.zip.blob, result.zip.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function PackSvgZip(icons: string[], name: string) {
|
export async function PackSvgZip(
|
||||||
if (!icons.length)
|
collections: CollectionInfo[],
|
||||||
return
|
|
||||||
|
|
||||||
Download(
|
|
||||||
await import('client-zip').then(({ downloadZip }) => downloadZip(
|
|
||||||
PrepareIconSvgs(icons, 'svg'),
|
|
||||||
).blob()),
|
|
||||||
`${name}.zip`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function PackJsonZip(icons: string[], name: string) {
|
|
||||||
if (!icons.length)
|
|
||||||
return
|
|
||||||
|
|
||||||
Download(
|
|
||||||
await import('client-zip').then(({ downloadZip }) => downloadZip(
|
|
||||||
PrepareIconSvgs(icons, 'json', name),
|
|
||||||
).blob()),
|
|
||||||
`${name}.zip`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PackType = 'svg' | 'tsx' | 'jsx' | 'vue' | 'json'
|
|
||||||
|
|
||||||
async function* PreparePackZip(
|
|
||||||
icons: string[],
|
icons: string[],
|
||||||
name: string,
|
name: string,
|
||||||
type: PackType,
|
|
||||||
) {
|
) {
|
||||||
if (type === 'json' || type === 'svg') {
|
if (!icons.length)
|
||||||
yield* PrepareIconSvgs(icons, type, name)
|
|
||||||
return
|
return
|
||||||
}
|
|
||||||
|
|
||||||
for (const name of icons) {
|
const { packerWorker } = await import('./pack-worker-client')
|
||||||
if (!name)
|
|
||||||
continue
|
|
||||||
|
|
||||||
const svg = await getSvg(name)
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
packerWorker.addEventListener('message', (event) => {
|
||||||
|
if (event.data.error) {
|
||||||
|
reject(event.data.error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const { blob } = event.data as { blob: ArrayBuffer }
|
||||||
|
Download(
|
||||||
|
new Blob([blob]),
|
||||||
|
`${name}.zip`,
|
||||||
|
)
|
||||||
|
resolve()
|
||||||
|
}, { once: true })
|
||||||
|
const arrayBuffer = createArrayBufferFromCollections(collections)
|
||||||
|
packerWorker.postMessage({
|
||||||
|
operation: 'pack-svg-zip',
|
||||||
|
collections: arrayBuffer,
|
||||||
|
payload: {
|
||||||
|
icons: toRaw(icons),
|
||||||
|
},
|
||||||
|
}, [arrayBuffer])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const componentName = toComponentName(normalizeZipFleName(name))
|
export async function PackJsonZip(
|
||||||
let content: string
|
collections: CollectionInfo[],
|
||||||
|
icons: string[],
|
||||||
|
name: string,
|
||||||
|
) {
|
||||||
|
if (!icons.length)
|
||||||
|
return
|
||||||
|
|
||||||
switch (type) {
|
const { packerWorker } = await import('./pack-worker-client')
|
||||||
case 'vue':
|
|
||||||
content = await SvgToVue(svg, componentName)
|
|
||||||
break
|
|
||||||
case 'jsx':
|
|
||||||
content = await SvgToJSX(svg, componentName, false)
|
|
||||||
break
|
|
||||||
case 'tsx':
|
|
||||||
content = await SvgToTSX(svg, componentName, false)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
yield {
|
return new Promise<void>((resolve, reject) => {
|
||||||
name: `${componentName}.${type}`,
|
packerWorker.addEventListener('message', (event) => {
|
||||||
input: new Blob([content], { type: 'text/plain' }),
|
if (event.data.error) {
|
||||||
}
|
reject(event.data.error)
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
const { blob } = event.data as { blob: ArrayBuffer }
|
||||||
|
Download(
|
||||||
|
new Blob([blob]),
|
||||||
|
`${name}.zip`,
|
||||||
|
)
|
||||||
|
resolve()
|
||||||
|
}, { once: true })
|
||||||
|
const arrayBuffer = createArrayBufferFromCollections(collections)
|
||||||
|
packerWorker.postMessage({
|
||||||
|
operation: 'pack-json-zip',
|
||||||
|
collections: arrayBuffer,
|
||||||
|
payload: {
|
||||||
|
icons: toRaw(icons),
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
}, [arrayBuffer])
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function PackZip(
|
export async function PackZip(
|
||||||
|
collections: CollectionInfo[],
|
||||||
icons: string[],
|
icons: string[],
|
||||||
name: string,
|
name: string,
|
||||||
type: PackType = 'svg',
|
type: PackType = 'svg',
|
||||||
@ -182,10 +159,36 @@ export async function PackZip(
|
|||||||
if (!icons.length)
|
if (!icons.length)
|
||||||
return
|
return
|
||||||
|
|
||||||
Download(
|
const { packerWorker } = await import('./pack-worker-client')
|
||||||
await import('client-zip').then(({ downloadZip }) => downloadZip(
|
|
||||||
PreparePackZip(icons, name, type),
|
return new Promise<void>((resolve, reject) => {
|
||||||
).blob()),
|
packerWorker.addEventListener('message', (event) => {
|
||||||
`${name}-${type}.zip`,
|
if (event.data.error) {
|
||||||
)
|
reject(event.data.error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const { blob } = event.data as { blob: ArrayBuffer }
|
||||||
|
Download(
|
||||||
|
new Blob([blob]),
|
||||||
|
`${name}-${type}.zip`,
|
||||||
|
)
|
||||||
|
resolve()
|
||||||
|
}, { once: true })
|
||||||
|
const arrayBuffer = createArrayBufferFromCollections(collections)
|
||||||
|
packerWorker.postMessage({
|
||||||
|
operation: 'pack-zip',
|
||||||
|
collections: arrayBuffer,
|
||||||
|
payload: {
|
||||||
|
icons: toRaw(icons),
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
},
|
||||||
|
}, [arrayBuffer])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function createArrayBufferFromCollections(
|
||||||
|
collections: CollectionInfo[],
|
||||||
|
) {
|
||||||
|
return new TextEncoder().encode(JSON.stringify(collections)).buffer
|
||||||
}
|
}
|
||||||
|
|||||||
222
src/utils/svg/helpers.ts
Normal file
222
src/utils/svg/helpers.ts
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
import type { Node } from 'ultrahtml'
|
||||||
|
import type { CollectionInfo } from '../../data'
|
||||||
|
import { encodeSvgForCss } from '@iconify/utils'
|
||||||
|
import { parse, transformSync } from 'ultrahtml'
|
||||||
|
import Base64 from './base64'
|
||||||
|
import { HtmlToJSX } from './htmlToJsx'
|
||||||
|
import { getSvg } from './loader'
|
||||||
|
import { prettierCode } from './prettier'
|
||||||
|
|
||||||
|
export type PackType = 'svg' | 'tsx' | 'jsx' | 'vue' | 'json'
|
||||||
|
|
||||||
|
export function normalizeZipFleName(svgName: string): string {
|
||||||
|
return svgName.replace(':', '-')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toComponentName(icon: string) {
|
||||||
|
return icon.split(/[:\-_]/).filter(Boolean).map(s => s[0].toUpperCase() + s.slice(1).toLowerCase()).join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ClearSvg(svgCode: string, reactJSX?: boolean) {
|
||||||
|
const result = transformSync(parse(svgCode).children[0] as Node, [
|
||||||
|
(node: Node): Node => {
|
||||||
|
if (node.name !== 'svg')
|
||||||
|
return node
|
||||||
|
|
||||||
|
const attributes = node.attributes || {}
|
||||||
|
// keep only 'viewBox', 'width', 'height', 'focusable', 'xmlns', 'xlink' attributes
|
||||||
|
const allowedAttributes = ['viewBox', 'width', 'height', 'focusable', 'xmlns', 'xlink']
|
||||||
|
for (const key of Object.keys(attributes)) {
|
||||||
|
if (!allowedAttributes.includes(key)) {
|
||||||
|
delete attributes[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.attributes = attributes
|
||||||
|
|
||||||
|
return node
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
return HtmlToJSX(result, reactJSX)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SvgToDataURL(svg: string) {
|
||||||
|
const base64 = `data:image/svg+xml;base64,${Base64.encode(svg)}`
|
||||||
|
const plain = `data:image/svg+xml,${encodeSvgForCss(svg)}`
|
||||||
|
// Return the shorter of the two data URLs
|
||||||
|
return base64.length < plain.length ? base64 : plain
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SvgToJSX(svg: string, name: string, snippet: boolean) {
|
||||||
|
const code = `
|
||||||
|
export function ${name}(props) {
|
||||||
|
return (
|
||||||
|
${ClearSvg(svg, true).replace(/<svg (.*?)>/, '<svg $1 {...props}>')}
|
||||||
|
)
|
||||||
|
}`
|
||||||
|
if (snippet)
|
||||||
|
return prettierCode(code, 'babel-ts')
|
||||||
|
else
|
||||||
|
return prettierCode(`import React from 'react'\n${code}\nexport default ${name}`, 'babel-ts')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SvgToTSX(svg: string, name: string, snippet: boolean, reactJSX = true) {
|
||||||
|
let code = `
|
||||||
|
export function ${name}(props: SVGProps<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
${ClearSvg(svg, reactJSX).replace(/<svg (.*?)>/, '<svg $1 {...props}>')}
|
||||||
|
)
|
||||||
|
}`
|
||||||
|
|
||||||
|
code = snippet ? code : `import React, { SVGProps } from 'react'\n${code}\nexport default ${name}`
|
||||||
|
return prettierCode(code, 'babel-ts')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SvgToQwik(svg: string, name: string, snippet: boolean) {
|
||||||
|
let code = `
|
||||||
|
export function ${name}(props: QwikIntrinsicElements['svg'], key: string) {
|
||||||
|
return (
|
||||||
|
${ClearSvg(svg, false).replace(/<svg (.*?)>/, '<svg $1 {...props} key={key}>')}
|
||||||
|
)
|
||||||
|
}`
|
||||||
|
|
||||||
|
code = snippet ? code : `import type { QwikIntrinsicElements } from '@builder.io/qwik'\n${code}\nexport default ${name}`
|
||||||
|
return prettierCode(code, 'babel-ts')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SvgToVue(svg: string, name: string, isTs?: boolean) {
|
||||||
|
const content = `
|
||||||
|
<template>
|
||||||
|
${ClearSvg(svg)}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: '${name}'
|
||||||
|
}
|
||||||
|
</script>`
|
||||||
|
const code = isTs ? content.replace('<script>', '<script lang="ts">') : content
|
||||||
|
return prettierCode(code, 'vue')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SvgToSolid(svg: string, name: string, snippet: boolean) {
|
||||||
|
let code = `
|
||||||
|
export function ${name}(props: JSX.IntrinsicElements['svg']) {
|
||||||
|
return (
|
||||||
|
${svg.replace(/<svg (.*?)>/, '<svg $1 {...props}>')}
|
||||||
|
)
|
||||||
|
}`
|
||||||
|
|
||||||
|
code = snippet ? code : `import type { JSX } from 'solid-js'\n${code}\nexport default ${name}`
|
||||||
|
return prettierCode(code, 'babel-ts')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SvgToSvelte(svg: string) {
|
||||||
|
return `${svg.replace(/<svg (.*?)>/, '<svg $1 {...$$$props}>')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SvgToAstro(svg: string) {
|
||||||
|
return `
|
||||||
|
---
|
||||||
|
const props = Astro.props
|
||||||
|
---
|
||||||
|
|
||||||
|
${svg.replace(/<svg (.*?)>/, '<svg $1 {...props}>')}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SvgToReactNative(svg: string, name: string, snippet: boolean) {
|
||||||
|
function replaceTags(svg: string, replacements: {
|
||||||
|
from: string
|
||||||
|
to: string
|
||||||
|
}[]): string {
|
||||||
|
let result = svg
|
||||||
|
replacements.forEach(({ from, to }) => {
|
||||||
|
result = result.replace(new RegExp(`<${from}(.*?)>`, 'g'), `<${to}$1>`)
|
||||||
|
.replace(new RegExp(`</${from}>`, 'g'), `</${to}>`)
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateImports(usedComponents: string[]): string {
|
||||||
|
// Separate Svg from the other components
|
||||||
|
const svgIndex = usedComponents.indexOf('Svg')
|
||||||
|
if (svgIndex !== -1)
|
||||||
|
usedComponents.splice(svgIndex, 1)
|
||||||
|
|
||||||
|
// Join all other component names with a comma and wrap them in curly braces
|
||||||
|
const componentsString = usedComponents.length > 0 ? `{ ${usedComponents.join(', ')} }` : ''
|
||||||
|
|
||||||
|
// Return the consolidated import statement, ensuring Svg is imported as a default import
|
||||||
|
return `import Svg, ${componentsString} from 'react-native-svg';`
|
||||||
|
}
|
||||||
|
|
||||||
|
const replacements: {
|
||||||
|
from: string
|
||||||
|
to: string
|
||||||
|
}[] = [
|
||||||
|
{ from: 'svg', to: 'Svg' },
|
||||||
|
{ from: 'path', to: 'Path' },
|
||||||
|
{ from: 'g', to: 'G' },
|
||||||
|
{ from: 'circle', to: 'Circle' },
|
||||||
|
{ from: 'rect', to: 'Rect' },
|
||||||
|
{ from: 'line', to: 'Line' },
|
||||||
|
{ from: 'polyline', to: 'Polyline' },
|
||||||
|
{ from: 'polygon', to: 'Polygon' },
|
||||||
|
{ from: 'ellipse', to: 'Ellipse' },
|
||||||
|
{ from: 'text', to: 'Text' },
|
||||||
|
{ from: 'tspan', to: 'Tspan' },
|
||||||
|
{ from: 'textPath', to: 'TextPath' },
|
||||||
|
{ from: 'defs', to: 'Defs' },
|
||||||
|
{ from: 'use', to: 'Use' },
|
||||||
|
{ from: 'symbol', to: 'Symbol' },
|
||||||
|
{ from: 'linearGradient', to: 'LinearGradient' },
|
||||||
|
{ from: 'radialGradient', to: 'RadialGradient' },
|
||||||
|
{ from: 'stop', to: 'Stop' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const reactNativeSvgCode = replaceTags(ClearSvg(svg, true), replacements)
|
||||||
|
.replace(/className=/g, '')
|
||||||
|
.replace(/href=/g, 'xlinkHref=')
|
||||||
|
.replace(/clip-path=/g, 'clipPath=')
|
||||||
|
.replace(/fill-opacity=/g, 'fillOpacity=')
|
||||||
|
.replace(/stroke-width=/g, 'strokeWidth=')
|
||||||
|
.replace(/stroke-linecap=/g, 'strokeLinecap=')
|
||||||
|
.replace(/stroke-linejoin=/g, 'strokeLinejoin=')
|
||||||
|
.replace(/stroke-miterlimit=/g, 'strokeMiterlimit=')
|
||||||
|
|
||||||
|
const svgComponents = replacements.map(({ to }) => to)
|
||||||
|
const imports = generateImports(svgComponents.filter(component => reactNativeSvgCode.includes(component)))
|
||||||
|
|
||||||
|
let code = `
|
||||||
|
${imports}
|
||||||
|
|
||||||
|
export function ${name}(props) {
|
||||||
|
return (
|
||||||
|
${reactNativeSvgCode}
|
||||||
|
)
|
||||||
|
}`
|
||||||
|
|
||||||
|
if (!snippet)
|
||||||
|
code = `import React from 'react';\n${code}\nexport default ${name}`
|
||||||
|
|
||||||
|
return prettierCode(code, 'babel-ts')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function LoadIconSvgs(
|
||||||
|
collections: CollectionInfo[],
|
||||||
|
icons: string[],
|
||||||
|
) {
|
||||||
|
return await Promise.all(
|
||||||
|
icons
|
||||||
|
.filter(Boolean)
|
||||||
|
.sort()
|
||||||
|
.map(async (name) => {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
svg: await getSvg(collections, name),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
21
src/utils/svg/index.ts
Normal file
21
src/utils/svg/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export { default } from './base64'
|
||||||
|
export { bufferToString } from './bufferToString'
|
||||||
|
export type { PackType } from './helpers'
|
||||||
|
export {
|
||||||
|
ClearSvg,
|
||||||
|
LoadIconSvgs,
|
||||||
|
normalizeZipFleName,
|
||||||
|
SvgToAstro,
|
||||||
|
SvgToDataURL,
|
||||||
|
SvgToJSX,
|
||||||
|
SvgToQwik,
|
||||||
|
SvgToReactNative,
|
||||||
|
SvgToSolid,
|
||||||
|
SvgToSvelte,
|
||||||
|
SvgToTSX,
|
||||||
|
SvgToVue,
|
||||||
|
toComponentName,
|
||||||
|
} from './helpers'
|
||||||
|
export { HtmlToJSX } from './htmlToJsx'
|
||||||
|
export { API_ENTRY, getLicenseComment, getSvg, getSvgLocal } from './loader'
|
||||||
|
export { prettierCode } from './prettier'
|
||||||
46
src/utils/svg/loader.ts
Normal file
46
src/utils/svg/loader.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import type { CollectionInfo } from '../../data'
|
||||||
|
import { buildIcon, loadIcon } from 'iconify-icon'
|
||||||
|
|
||||||
|
export const API_ENTRY = 'https://api.iconify.design'
|
||||||
|
|
||||||
|
export async function getLicenseComment(collections: CollectionInfo[], icon: string) {
|
||||||
|
const [id] = icon.split(':')
|
||||||
|
const collection = collections.find(i => i.id === id)
|
||||||
|
if (!collection) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return `<!-- Icon from ${collection?.name} by ${collection?.author?.name} - ${collection?.license?.url} -->`
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSvgLocal(
|
||||||
|
collections: CollectionInfo[],
|
||||||
|
icon: string,
|
||||||
|
size = '1em',
|
||||||
|
color = 'currentColor',
|
||||||
|
) {
|
||||||
|
const data = await loadIcon(icon)
|
||||||
|
if (!data)
|
||||||
|
return
|
||||||
|
const built = buildIcon(data, { height: size })
|
||||||
|
if (!built)
|
||||||
|
return
|
||||||
|
const license = await getLicenseComment(collections, icon)
|
||||||
|
const xlink = built.body.includes('xlink:') ? ' xmlns:xlink="http://www.w3.org/1999/xlink"' : ''
|
||||||
|
return `<svg xmlns="http://www.w3.org/2000/svg"${xlink} ${Object.entries(built.attributes).map(([k, v]) => `${k}="${v}"`).join(' ')}>${license}${built.body}</svg>`.replaceAll('currentColor', color)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSvg(
|
||||||
|
collections: CollectionInfo[],
|
||||||
|
icon: string,
|
||||||
|
size = '1em',
|
||||||
|
color = 'currentColor',
|
||||||
|
) {
|
||||||
|
const local = await getSvgLocal(collections, icon, size, color)
|
||||||
|
if (local)
|
||||||
|
return local
|
||||||
|
|
||||||
|
const mode = import.meta.env.DEV && !PWA ? 'no-cors' : undefined
|
||||||
|
return await fetch(`${API_ENTRY}/${icon}.svg?inline=false&height=${size}&color=${encodeURIComponent(color)}`, {
|
||||||
|
mode,
|
||||||
|
}).then(r => r.text()) || ''
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import type { BuiltInParserName } from 'prettier'
|
import type { BuiltInParserName } from 'prettier'
|
||||||
import { isElectron } from '../env'
|
import { isElectron } from '../../env'
|
||||||
|
|
||||||
export async function prettierCode(code: string, parser: BuiltInParserName) {
|
export async function prettierCode(code: string, parser: BuiltInParserName) {
|
||||||
if (!isElectron)
|
if (!isElectron)
|
||||||
203
src/utils/worker/index.ts
Normal file
203
src/utils/worker/index.ts
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
/// <reference lib="webworker" />
|
||||||
|
|
||||||
|
import type { CollectionInfo } from '../../data'
|
||||||
|
import type { PackType } from '../svg'
|
||||||
|
import type { PackOperation, WorkerPackMessage } from './types'
|
||||||
|
import { downloadZip } from 'client-zip'
|
||||||
|
import {
|
||||||
|
getSvg,
|
||||||
|
LoadIconSvgs,
|
||||||
|
normalizeZipFleName,
|
||||||
|
SvgToJSX,
|
||||||
|
SvgToTSX,
|
||||||
|
SvgToVue,
|
||||||
|
toComponentName,
|
||||||
|
} from '../svg'
|
||||||
|
|
||||||
|
globalThis.onmessage = async (event: MessageEvent<WorkerPackMessage<PackOperation>>) => {
|
||||||
|
const message = event.data
|
||||||
|
let blob: Blob | undefined
|
||||||
|
let name: string | undefined
|
||||||
|
try {
|
||||||
|
const collections: CollectionInfo[] = JSON.parse(
|
||||||
|
new TextDecoder().decode(message.collections),
|
||||||
|
)
|
||||||
|
if (isPackZipMessage(message)) {
|
||||||
|
blob = await downloadZip(
|
||||||
|
PreparePackZip(
|
||||||
|
collections,
|
||||||
|
message.payload.icons,
|
||||||
|
message.payload.name,
|
||||||
|
message.payload.type,
|
||||||
|
),
|
||||||
|
).blob()
|
||||||
|
}
|
||||||
|
else if (isPackJsonZipMessage(message)) {
|
||||||
|
blob = await downloadZip(
|
||||||
|
PrepareIconSvgs(
|
||||||
|
collections,
|
||||||
|
message.payload.icons,
|
||||||
|
'json',
|
||||||
|
message.payload.name,
|
||||||
|
),
|
||||||
|
).blob()
|
||||||
|
}
|
||||||
|
else if (isPackSvgZipMessage(message)) {
|
||||||
|
blob = await downloadZip(
|
||||||
|
PrepareIconSvgs(
|
||||||
|
collections,
|
||||||
|
message.payload.icons,
|
||||||
|
'svg',
|
||||||
|
),
|
||||||
|
).blob()
|
||||||
|
}
|
||||||
|
else if (isPackFontZipMessage(message)) {
|
||||||
|
const result = await PackIconFont(
|
||||||
|
collections,
|
||||||
|
message.payload.icons,
|
||||||
|
message.payload.options,
|
||||||
|
)
|
||||||
|
if (result) {
|
||||||
|
([blob, name] = result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: any) {
|
||||||
|
console.error('PackWorker: error while generating the zip', e)
|
||||||
|
globalThis.postMessage({ error: e && 'message' in e ? e.message : String(e) })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blob) {
|
||||||
|
try {
|
||||||
|
const arrayBuffer = await blob.arrayBuffer()
|
||||||
|
globalThis.postMessage({
|
||||||
|
blob: arrayBuffer,
|
||||||
|
name,
|
||||||
|
}, [arrayBuffer])
|
||||||
|
}
|
||||||
|
catch (e: any) {
|
||||||
|
console.error('PackWorker: error while transferring generated zip', e)
|
||||||
|
globalThis.postMessage({ error: e && 'message' in e ? e.message : String(e) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
globalThis.postMessage({ error: 'No blob generated' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPackZipMessage(
|
||||||
|
message: WorkerPackMessage<PackOperation>,
|
||||||
|
): message is WorkerPackMessage<'pack-zip'> {
|
||||||
|
return message.operation === 'pack-zip'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPackJsonZipMessage(
|
||||||
|
message: WorkerPackMessage<PackOperation>,
|
||||||
|
): message is WorkerPackMessage<'pack-json-zip'> {
|
||||||
|
return message.operation === 'pack-json-zip'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPackSvgZipMessage(
|
||||||
|
message: WorkerPackMessage<PackOperation>,
|
||||||
|
): message is WorkerPackMessage<'pack-svg-zip'> {
|
||||||
|
return message.operation === 'pack-svg-zip'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPackFontZipMessage(
|
||||||
|
message: WorkerPackMessage<PackOperation>,
|
||||||
|
): message is WorkerPackMessage<'pack-font-zip'> {
|
||||||
|
return message.operation === 'pack-font-zip'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* PrepareIconSvgs(
|
||||||
|
collections: CollectionInfo[],
|
||||||
|
icons: string[],
|
||||||
|
format: 'svg' | 'json',
|
||||||
|
name?: string,
|
||||||
|
) {
|
||||||
|
if (format === 'json') {
|
||||||
|
const svgs = await LoadIconSvgs(collections, icons)
|
||||||
|
yield {
|
||||||
|
name: `${name}.json`,
|
||||||
|
input: new Blob([JSON.stringify(svgs, null, 2)], { type: 'application/json; charset=utf-8' }),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const icon of icons) {
|
||||||
|
if (!icon)
|
||||||
|
continue
|
||||||
|
|
||||||
|
const svg = await getSvg(collections, icon)
|
||||||
|
|
||||||
|
yield {
|
||||||
|
name: `${normalizeZipFleName(icon)}.svg`,
|
||||||
|
input: new Blob([svg], { type: 'image/svg+xml' }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* PreparePackZip(
|
||||||
|
collections: CollectionInfo[],
|
||||||
|
icons: string[],
|
||||||
|
name: string,
|
||||||
|
type: PackType,
|
||||||
|
) {
|
||||||
|
if (type === 'json' || type === 'svg') {
|
||||||
|
yield* PrepareIconSvgs(collections, icons, type, name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const name of icons) {
|
||||||
|
if (!name)
|
||||||
|
continue
|
||||||
|
|
||||||
|
const svg = await getSvg(collections, name)
|
||||||
|
|
||||||
|
const componentName = toComponentName(normalizeZipFleName(name))
|
||||||
|
let content: string
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'vue':
|
||||||
|
content = await SvgToVue(svg, componentName)
|
||||||
|
break
|
||||||
|
case 'jsx':
|
||||||
|
content = await SvgToJSX(svg, componentName, false)
|
||||||
|
break
|
||||||
|
case 'tsx':
|
||||||
|
content = await SvgToTSX(svg, componentName, false)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
yield {
|
||||||
|
name: `${componentName}.${type}`,
|
||||||
|
input: new Blob([content], { type: 'text/plain' }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function PackIconFont(
|
||||||
|
collections: CollectionInfo[],
|
||||||
|
icons: string[],
|
||||||
|
options: any = {},
|
||||||
|
) {
|
||||||
|
if (!icons.length)
|
||||||
|
return
|
||||||
|
|
||||||
|
const [data, { SvgPacker }] = await Promise.all([
|
||||||
|
LoadIconSvgs(collections, icons),
|
||||||
|
import('svg-packer'),
|
||||||
|
])
|
||||||
|
const result = await SvgPacker({
|
||||||
|
fontName: 'Iconify Explorer Font',
|
||||||
|
fileName: 'iconfont',
|
||||||
|
cssPrefix: 'i',
|
||||||
|
...options,
|
||||||
|
icons: data,
|
||||||
|
})
|
||||||
|
|
||||||
|
return [result.zip.blob, result.zip.name] as const
|
||||||
|
}
|
||||||
36
src/utils/worker/types.ts
Normal file
36
src/utils/worker/types.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import type { PackType } from '../svg'
|
||||||
|
|
||||||
|
export type PackOperation = 'pack-zip' | 'pack-json-zip' | 'pack-svg-zip' | 'pack-font-zip'
|
||||||
|
|
||||||
|
export interface PackZipPayload {
|
||||||
|
icons: string[]
|
||||||
|
name: string
|
||||||
|
type: PackType
|
||||||
|
}
|
||||||
|
export interface PackJsonZipPayload {
|
||||||
|
icons: string[]
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
export interface PackSvgZipPayload {
|
||||||
|
icons: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PackFontZipPayload {
|
||||||
|
icons: string[]
|
||||||
|
options: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WorkerPackMessage<O extends PackOperation> {
|
||||||
|
payload: O extends 'pack-zip' ? PackZipPayload :
|
||||||
|
O extends 'pack-json-zip' ? PackJsonZipPayload :
|
||||||
|
O extends 'pack-svg-zip' ? PackSvgZipPayload :
|
||||||
|
O extends 'pack-font-zip' ? PackFontZipPayload :
|
||||||
|
never
|
||||||
|
operation: O
|
||||||
|
collections: ArrayBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WorkerPackResponse {
|
||||||
|
blob: ArrayBuffer
|
||||||
|
name?: string
|
||||||
|
}
|
||||||
@ -112,5 +112,14 @@ export default defineConfig(({ mode }) => {
|
|||||||
'iconify-icon': resolve(__dirname, 'node_modules/iconify-icon/dist/iconify-icon.mjs'),
|
'iconify-icon': resolve(__dirname, 'node_modules/iconify-icon/dist/iconify-icon.mjs'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
worker: {
|
||||||
|
format: 'es',
|
||||||
|
rollupOptions: {
|
||||||
|
treeshake: true,
|
||||||
|
},
|
||||||
|
plugins: () => [
|
||||||
|
SvgPackerVitePlugin(),
|
||||||
|
],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user