@pikacss/plugin-icons 0.0.45 → 0.0.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +57 -4
- package/dist/index.mjs +144 -62
- package/package.json +3 -4
package/dist/index.d.mts
CHANGED
|
@@ -1,14 +1,67 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { CustomCollections, IconCustomizations, IconifyLoaderOptions } from "@iconify/utils";
|
|
2
|
+
import { EnginePlugin, StyleItem } from "@pikacss/core";
|
|
3
3
|
|
|
4
4
|
//#region src/index.d.ts
|
|
5
5
|
interface IconMeta {
|
|
6
6
|
collection: string;
|
|
7
7
|
name: string;
|
|
8
8
|
svg: string;
|
|
9
|
+
source: IconSource;
|
|
9
10
|
mode?: IconsConfig['mode'];
|
|
10
11
|
}
|
|
11
|
-
type
|
|
12
|
+
type IconSource = 'custom' | 'local' | 'cdn';
|
|
13
|
+
interface IconsConfig {
|
|
14
|
+
/**
|
|
15
|
+
* Class name prefix for icon shortcuts.
|
|
16
|
+
*
|
|
17
|
+
* @default 'i-'
|
|
18
|
+
*/
|
|
19
|
+
prefix?: string | string[];
|
|
20
|
+
/**
|
|
21
|
+
* Default rendering mode.
|
|
22
|
+
*
|
|
23
|
+
* @default 'auto'
|
|
24
|
+
*/
|
|
25
|
+
mode?: 'auto' | 'mask' | 'bg';
|
|
26
|
+
/**
|
|
27
|
+
* Scale icons against 1em.
|
|
28
|
+
*
|
|
29
|
+
* @default 1
|
|
30
|
+
*/
|
|
31
|
+
scale?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Native Iconify custom collections.
|
|
34
|
+
*/
|
|
35
|
+
collections?: CustomCollections;
|
|
36
|
+
/**
|
|
37
|
+
* Native Iconify SVG customizations.
|
|
38
|
+
*/
|
|
39
|
+
customizations?: IconCustomizations;
|
|
40
|
+
/**
|
|
41
|
+
* Auto install missing Iconify JSON packages when supported by the runtime.
|
|
42
|
+
*
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
autoInstall?: IconifyLoaderOptions['autoInstall'];
|
|
46
|
+
/**
|
|
47
|
+
* Current working directory used to resolve local Iconify JSON packages.
|
|
48
|
+
*
|
|
49
|
+
* @default process.cwd()
|
|
50
|
+
*/
|
|
51
|
+
cwd?: IconifyLoaderOptions['cwd'];
|
|
52
|
+
/**
|
|
53
|
+
* Optional CDN base URL or URL template for collection JSON.
|
|
54
|
+
* Use `{collection}` as a placeholder to fully control the final URL.
|
|
55
|
+
*/
|
|
56
|
+
cdn?: string;
|
|
57
|
+
/**
|
|
58
|
+
* CSS unit used when width or height need to be synthesized.
|
|
59
|
+
*/
|
|
60
|
+
unit?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Additional CSS properties applied to every resolved icon.
|
|
63
|
+
*/
|
|
64
|
+
extraProperties?: Record<string, string>;
|
|
12
65
|
/**
|
|
13
66
|
* Processor for the CSS object before stringify
|
|
14
67
|
*/
|
|
@@ -17,7 +70,7 @@ type IconsConfig = Simplify<Omit<IconsOptions, 'warn' | 'layer' | 'processor' |
|
|
|
17
70
|
* Specify the icons for auto-completion.
|
|
18
71
|
*/
|
|
19
72
|
autocomplete?: string[];
|
|
20
|
-
}
|
|
73
|
+
}
|
|
21
74
|
declare module '@pikacss/core' {
|
|
22
75
|
interface EngineConfig {
|
|
23
76
|
icons?: IconsConfig;
|
package/dist/index.mjs
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
|
-
import { encodeSvgForCss, loadIcon } from "@iconify/utils";
|
|
2
|
+
import { encodeSvgForCss, loadIcon, quicklyValidateIconSet, searchForIcon, stringToIcon } from "@iconify/utils";
|
|
3
|
+
import { loadNodeIcon } from "@iconify/utils/lib/loader/node-loader";
|
|
3
4
|
import { defineEnginePlugin, log } from "@pikacss/core";
|
|
4
|
-
import { combineLoaders, createCDNFetchLoader, createNodeLoader, parseIconWithLoader } from "@unocss/preset-icons";
|
|
5
5
|
import { $fetch } from "ofetch";
|
|
6
6
|
|
|
7
7
|
//#region src/index.ts
|
|
8
8
|
/**
|
|
9
9
|
* Environment flags helper function to detect the current runtime environment.
|
|
10
|
-
* This replaces the removed `getEnvFlags` export from `@unocss/preset-icons` v66+.
|
|
11
|
-
*
|
|
12
|
-
* @returns An object containing:
|
|
13
|
-
* - `isNode`: Whether the code is running in a Node.js environment
|
|
14
|
-
* - `isVSCode`: Whether the code is running within VS Code (extension host)
|
|
15
|
-
* - `isESLint`: Whether the code is running within ESLint
|
|
16
10
|
*/
|
|
17
11
|
function getEnvFlags() {
|
|
18
12
|
const isNode = typeof process !== "undefined" && typeof process.versions?.node !== "undefined";
|
|
@@ -23,27 +17,133 @@ function getEnvFlags() {
|
|
|
23
17
|
};
|
|
24
18
|
}
|
|
25
19
|
function icons() {
|
|
26
|
-
return createIconsPlugin(
|
|
20
|
+
return createIconsPlugin();
|
|
27
21
|
}
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
const globalColonRE = /:/g;
|
|
23
|
+
const currentColorRE = /currentColor/;
|
|
24
|
+
function normalizePrefixes(prefix) {
|
|
25
|
+
const prefixes = [prefix ?? "i-"].flat().filter(Boolean);
|
|
26
|
+
return [...new Set(prefixes)];
|
|
27
|
+
}
|
|
28
|
+
function escapeRegExp(value) {
|
|
29
|
+
return value.replace(/[|\\{}()[\]^$+*?.-]/g, "\\$&");
|
|
30
|
+
}
|
|
31
|
+
function createShortcutRegExp(prefixes) {
|
|
32
|
+
return new RegExp(`^(?:${prefixes.map(escapeRegExp).join("|")})([\\w:-]+)(?:\\?(mask|bg|auto))?$`);
|
|
33
|
+
}
|
|
34
|
+
function getPossibleIconNames(iconName) {
|
|
35
|
+
return [
|
|
36
|
+
iconName,
|
|
37
|
+
iconName.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(),
|
|
38
|
+
iconName.replace(/([a-z])(\d+)/g, "$1-$2")
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
function createAutocomplete(prefixes, autocomplete = []) {
|
|
42
|
+
const prefixRE = new RegExp(`^(?:${prefixes.map(escapeRegExp).join("|")})`);
|
|
43
|
+
return [...prefixes, ...prefixes.flatMap((prefix) => autocomplete.map((icon) => `${prefix}${icon.replace(prefixRE, "")}`))];
|
|
44
|
+
}
|
|
45
|
+
function createAutocompletePatterns(prefixes) {
|
|
46
|
+
return prefixes.flatMap((prefix) => [
|
|
47
|
+
`\`${prefix}\${string}:\${string}\``,
|
|
48
|
+
`\`${prefix}\${string}:\${string}?mask\``,
|
|
49
|
+
`\`${prefix}\${string}:\${string}?bg\``,
|
|
50
|
+
`\`${prefix}\${string}:\${string}?auto\``
|
|
51
|
+
]);
|
|
52
|
+
}
|
|
53
|
+
function resolveCdnCollectionUrl(cdn, collection) {
|
|
54
|
+
if (cdn.includes("{collection}")) return cdn.replaceAll("{collection}", collection);
|
|
55
|
+
return `${cdn.replace(/\/$/, "")}/${collection}.json`;
|
|
56
|
+
}
|
|
57
|
+
function createLoaderOptions(config, usedProps) {
|
|
58
|
+
const { scale = 1, collections, autoInstall = false, cwd, unit, extraProperties = {}, customizations = {} } = config;
|
|
59
|
+
const iconCustomizer = customizations.iconCustomizer;
|
|
60
|
+
return {
|
|
61
|
+
addXmlNs: true,
|
|
62
|
+
scale,
|
|
63
|
+
customCollections: collections,
|
|
64
|
+
autoInstall,
|
|
65
|
+
cwd,
|
|
66
|
+
usedProps,
|
|
67
|
+
customizations: {
|
|
68
|
+
...customizations,
|
|
69
|
+
additionalProps: {
|
|
70
|
+
...customizations.additionalProps,
|
|
71
|
+
...extraProperties
|
|
72
|
+
},
|
|
73
|
+
trimCustomSvg: customizations.trimCustomSvg ?? true,
|
|
74
|
+
async iconCustomizer(collection, icon, props) {
|
|
75
|
+
await iconCustomizer?.(collection, icon, props);
|
|
76
|
+
if (unit) {
|
|
77
|
+
if (!props.width) props.width = `${scale}${unit}`;
|
|
78
|
+
if (!props.height) props.height = `${scale}${unit}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
async function loadCollectionFromCdn(cdn, collection, cache) {
|
|
85
|
+
if (!cache.has(collection)) cache.set(collection, (async () => {
|
|
86
|
+
try {
|
|
87
|
+
return quicklyValidateIconSet(await $fetch(resolveCdnCollectionUrl(cdn, collection))) ?? void 0;
|
|
88
|
+
} catch {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
})());
|
|
92
|
+
return cache.get(collection);
|
|
30
93
|
}
|
|
31
|
-
async function
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
94
|
+
async function resolveIcon(body, config, flags, cdnCollectionCache) {
|
|
95
|
+
const parsed = stringToIcon(body, true);
|
|
96
|
+
if (parsed == null || !parsed.prefix) return null;
|
|
97
|
+
const customProps = {};
|
|
98
|
+
const customSvg = await loadIcon(parsed.prefix, parsed.name, createLoaderOptions(config, customProps));
|
|
99
|
+
if (customSvg != null) return {
|
|
100
|
+
collection: parsed.prefix,
|
|
101
|
+
name: parsed.name,
|
|
102
|
+
svg: customSvg,
|
|
103
|
+
usedProps: customProps,
|
|
104
|
+
source: "custom"
|
|
105
|
+
};
|
|
106
|
+
if (flags.isNode && !flags.isVSCode && !flags.isESLint) {
|
|
107
|
+
const localProps = {};
|
|
108
|
+
const localSvg = await loadNodeIcon(parsed.prefix, parsed.name, {
|
|
109
|
+
...createLoaderOptions(config, localProps),
|
|
110
|
+
customCollections: void 0
|
|
111
|
+
});
|
|
112
|
+
if (localSvg != null) return {
|
|
113
|
+
collection: parsed.prefix,
|
|
114
|
+
name: parsed.name,
|
|
115
|
+
svg: localSvg,
|
|
116
|
+
usedProps: localProps,
|
|
117
|
+
source: "local"
|
|
118
|
+
};
|
|
38
119
|
}
|
|
39
|
-
if (cdn)
|
|
40
|
-
|
|
41
|
-
|
|
120
|
+
if (config.cdn) {
|
|
121
|
+
const iconSet = await loadCollectionFromCdn(config.cdn, parsed.prefix, cdnCollectionCache);
|
|
122
|
+
if (iconSet != null) {
|
|
123
|
+
const remoteProps = {};
|
|
124
|
+
const remoteSvg = await searchForIcon(iconSet, parsed.prefix, getPossibleIconNames(parsed.name), createLoaderOptions(config, remoteProps));
|
|
125
|
+
if (remoteSvg != null) return {
|
|
126
|
+
collection: parsed.prefix,
|
|
127
|
+
name: parsed.name,
|
|
128
|
+
svg: remoteSvg,
|
|
129
|
+
usedProps: remoteProps,
|
|
130
|
+
source: "cdn"
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
collection: parsed.prefix,
|
|
136
|
+
name: parsed.name,
|
|
137
|
+
svg: null,
|
|
138
|
+
usedProps: {},
|
|
139
|
+
source: null
|
|
140
|
+
};
|
|
42
141
|
}
|
|
43
|
-
|
|
44
|
-
function createIconsPlugin(lookupIconLoader) {
|
|
142
|
+
function createIconsPlugin() {
|
|
45
143
|
let engine;
|
|
46
|
-
let iconsConfig;
|
|
144
|
+
let iconsConfig = {};
|
|
145
|
+
const flags = getEnvFlags();
|
|
146
|
+
const cdnCollectionCache = /* @__PURE__ */ new Map();
|
|
47
147
|
return defineEnginePlugin({
|
|
48
148
|
name: "icons",
|
|
49
149
|
configureRawConfig: async (config) => {
|
|
@@ -51,46 +151,25 @@ function createIconsPlugin(lookupIconLoader) {
|
|
|
51
151
|
},
|
|
52
152
|
configureEngine: async (_engine) => {
|
|
53
153
|
engine = _engine;
|
|
54
|
-
const {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
autoInstall,
|
|
60
|
-
cwd: collectionsNodeResolvePath,
|
|
61
|
-
warn: void 0,
|
|
62
|
-
customizations: {
|
|
63
|
-
...customizations,
|
|
64
|
-
additionalProps: { ...extraProperties },
|
|
65
|
-
trimCustomSvg: true,
|
|
66
|
-
async iconCustomizer(collection, icon, props) {
|
|
67
|
-
await customizations.iconCustomizer?.(collection, icon, props);
|
|
68
|
-
if (unit) {
|
|
69
|
-
if (!props.width) props.width = `${scale}${unit}`;
|
|
70
|
-
if (!props.height) props.height = `${scale}${unit}`;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
const prefixRE = new RegExp(`^(${[prefix].flat().join("|")})`);
|
|
76
|
-
const autocompletePrefix = [prefix].flat();
|
|
77
|
-
const autocomplete = [...autocompletePrefix, ...autocompletePrefix.flatMap((p) => _autocomplete?.map((a) => `${p}${a.replace(prefixRE, "")}`) || [])];
|
|
78
|
-
let iconLoader;
|
|
154
|
+
const { mode = "auto", prefix = "i-", processor, autocomplete: _autocomplete } = iconsConfig;
|
|
155
|
+
const prefixes = normalizePrefixes(prefix);
|
|
156
|
+
const autocomplete = createAutocomplete(prefixes, _autocomplete);
|
|
157
|
+
const autocompletePatterns = createAutocompletePatterns(prefixes);
|
|
158
|
+
engine.appendAutocomplete({ patterns: { styleItemStrings: autocompletePatterns } });
|
|
79
159
|
engine.shortcuts.add({
|
|
80
|
-
shortcut:
|
|
160
|
+
shortcut: createShortcutRegExp(prefixes),
|
|
81
161
|
value: async (match) => {
|
|
82
162
|
let [full, body, _mode = mode] = match;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (parsed == null) {
|
|
163
|
+
const resolved = await resolveIcon(body, iconsConfig, flags, cdnCollectionCache);
|
|
164
|
+
if (resolved == null) {
|
|
165
|
+
log.warn(`invalid icon name "${full}"`);
|
|
166
|
+
return {};
|
|
167
|
+
}
|
|
168
|
+
if (resolved.svg == null) {
|
|
90
169
|
log.warn(`failed to load icon "${full}"`);
|
|
91
170
|
return {};
|
|
92
171
|
}
|
|
93
|
-
const url = `url("data:image/svg+xml;utf8,${encodeSvgForCss(
|
|
172
|
+
const url = `url("data:image/svg+xml;utf8,${encodeSvgForCss(resolved.svg)}")`;
|
|
94
173
|
const varName = `--${engine.config.prefix}svg-icon-${body.replace(globalColonRE, "-")}`;
|
|
95
174
|
if (engine.variables.store.has(varName) === false) engine.variables.add({ [varName]: {
|
|
96
175
|
value: url,
|
|
@@ -100,7 +179,7 @@ function createIconsPlugin(lookupIconLoader) {
|
|
|
100
179
|
},
|
|
101
180
|
pruneUnused: true
|
|
102
181
|
} });
|
|
103
|
-
if (_mode === "auto") _mode =
|
|
182
|
+
if (_mode === "auto") _mode = currentColorRE.test(resolved.svg) ? "mask" : "bg";
|
|
104
183
|
let styleItem;
|
|
105
184
|
if (_mode === "mask") styleItem = {
|
|
106
185
|
"--svg-icon": `var(${varName})`,
|
|
@@ -110,17 +189,20 @@ function createIconsPlugin(lookupIconLoader) {
|
|
|
110
189
|
"mask-size": "100% 100%",
|
|
111
190
|
"background-color": "currentColor",
|
|
112
191
|
"color": "inherit",
|
|
113
|
-
...usedProps
|
|
192
|
+
...resolved.usedProps
|
|
114
193
|
};
|
|
115
194
|
else styleItem = {
|
|
116
195
|
"--svg-icon": `var(${varName})`,
|
|
117
196
|
"background": "var(--svg-icon) no-repeat",
|
|
118
197
|
"background-size": "100% 100%",
|
|
119
198
|
"background-color": "transparent",
|
|
120
|
-
...usedProps
|
|
199
|
+
...resolved.usedProps
|
|
121
200
|
};
|
|
122
201
|
processor?.(styleItem, {
|
|
123
|
-
|
|
202
|
+
collection: resolved.collection,
|
|
203
|
+
name: resolved.name,
|
|
204
|
+
svg: resolved.svg,
|
|
205
|
+
source: resolved.source,
|
|
124
206
|
mode: _mode
|
|
125
207
|
});
|
|
126
208
|
return styleItem;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikacss/plugin-icons",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.47",
|
|
5
5
|
"author": "DevilTea <ch19980814@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -37,15 +37,14 @@
|
|
|
37
37
|
"dist"
|
|
38
38
|
],
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"@pikacss/core": "0.0.
|
|
40
|
+
"@pikacss/core": "0.0.47"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@iconify/utils": "^3.1.0",
|
|
44
|
-
"@unocss/preset-icons": "^66.6.1",
|
|
45
44
|
"ofetch": "^1.5.1"
|
|
46
45
|
},
|
|
47
46
|
"devDependencies": {
|
|
48
|
-
"@pikacss/core": "0.0.
|
|
47
|
+
"@pikacss/core": "0.0.47"
|
|
49
48
|
},
|
|
50
49
|
"scripts": {
|
|
51
50
|
"build": "tsdown && pnpm exec publint",
|