@genarou/blazir-icons 1.2.16 → 1.3.2
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/LICENSE +121 -0
- package/README.md +1206 -0
- package/dist/CustomIcon.svelte +30 -0
- package/dist/CustomIcon.svelte.d.ts +14 -0
- package/dist/Icon.svelte +282 -102
- package/dist/Icon.svelte.d.ts +12 -5
- package/dist/IconBadge.svelte +75 -0
- package/dist/IconBadge.svelte.d.ts +16 -0
- package/dist/IconBase.svelte +89 -57
- package/dist/effects.js +1 -3
- package/dist/icons/Camera.svelte +19 -0
- package/dist/icons/Camera.svelte.d.ts +4 -0
- package/dist/icons/Cards.svelte +19 -0
- package/dist/icons/Cards.svelte.d.ts +4 -0
- package/dist/icons/CloudAlert.svelte +19 -0
- package/dist/icons/CloudAlert.svelte.d.ts +4 -0
- package/dist/icons/CloudCheck.svelte +19 -0
- package/dist/icons/CloudCheck.svelte.d.ts +4 -0
- package/dist/icons/CloudDownload.svelte +19 -0
- package/dist/icons/CloudDownload.svelte.d.ts +4 -0
- package/dist/icons/Colors.svelte +13 -0
- package/dist/icons/Colors.svelte.d.ts +4 -0
- package/dist/icons/CreditCard.svelte +19 -0
- package/dist/icons/CreditCard.svelte.d.ts +4 -0
- package/dist/icons/Desktop.svelte +19 -0
- package/dist/icons/Desktop.svelte.d.ts +4 -0
- package/dist/icons/DoughnutChart.svelte +19 -0
- package/dist/icons/DoughnutChart.svelte.d.ts +4 -0
- package/dist/icons/Earth.svelte +19 -0
- package/dist/icons/Earth.svelte.d.ts +4 -0
- package/dist/icons/Globe.svelte +19 -0
- package/dist/icons/Globe.svelte.d.ts +4 -0
- package/dist/icons/LightHub.svelte +19 -0
- package/dist/icons/LightHub.svelte.d.ts +4 -0
- package/dist/icons/Link.svelte +19 -0
- package/dist/icons/Link.svelte.d.ts +4 -0
- package/dist/icons/More.svelte +13 -0
- package/dist/icons/More.svelte.d.ts +4 -0
- package/dist/icons/Power.svelte +19 -0
- package/dist/icons/Power.svelte.d.ts +4 -0
- package/dist/icons/Receipt.svelte +19 -0
- package/dist/icons/Receipt.svelte.d.ts +4 -0
- package/dist/icons/SharedFolder.svelte +13 -0
- package/dist/icons/SharedFolder.svelte.d.ts +4 -0
- package/dist/icons/Sidebar.svelte +13 -0
- package/dist/icons/Sidebar.svelte.d.ts +4 -0
- package/dist/icons/Sync.svelte +19 -0
- package/dist/icons/Sync.svelte.d.ts +4 -0
- package/dist/icons/Tags.svelte +13 -0
- package/dist/icons/Tags.svelte.d.ts +4 -0
- package/dist/icons/Tools.svelte +20 -0
- package/dist/icons/Tools.svelte.d.ts +4 -0
- package/dist/icons/Upload.svelte +12 -57
- package/dist/icons/Wifi.svelte +19 -0
- package/dist/icons/Wifi.svelte.d.ts +4 -0
- package/dist/icons/lazy-registry.d.ts +21 -0
- package/dist/icons/lazy-registry.js +251 -0
- package/dist/icons/registry.d.ts +146 -129
- package/dist/icons/registry.js +184 -132
- package/dist/icons-api.d.ts +67 -257
- package/dist/icons-api.js +84 -453
- package/dist/index.d.ts +5 -5
- package/dist/index.js +14 -11
- package/dist/plugin/index.d.ts +46 -0
- package/dist/plugin/index.js +327 -0
- package/dist/smart-cache.d.ts +35 -0
- package/dist/smart-cache.js +192 -0
- package/dist/types.d.ts +19 -2
- package/dist/utils/sanitize.d.ts +25 -0
- package/dist/utils/sanitize.js +109 -0
- package/package.json +23 -13
- package/dist/icons/Aws.svelte +0 -19
- package/dist/icons/Aws.svelte.d.ts +0 -4
- package/dist/icons/Facebook.svelte +0 -18
- package/dist/icons/Facebook.svelte.d.ts +0 -4
- package/dist/icons/Golang.svelte +0 -17
- package/dist/icons/Golang.svelte.d.ts +0 -4
- package/dist/icons/Google.svelte +0 -18
- package/dist/icons/Google.svelte.d.ts +0 -4
- package/dist/icons/Paypal.svelte +0 -21
- package/dist/icons/Paypal.svelte.d.ts +0 -4
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* blazir-icons — Vite / Rollup plugin
|
|
3
|
+
*
|
|
4
|
+
* Escanea tu código fuente en build-time y genera un registry mínimo
|
|
5
|
+
* que solo incluye los íconos que realmente usas.
|
|
6
|
+
*
|
|
7
|
+
* Compatible con: Vite, Rollup.
|
|
8
|
+
* Sin plugin: funciona exactamente igual que antes (registry completo).
|
|
9
|
+
*
|
|
10
|
+
* Uso en vite.config.ts:
|
|
11
|
+
* import { blazirIconsPlugin } from '@genarou/blazir-icons/plugin'
|
|
12
|
+
* plugins: [blazirIconsPlugin()]
|
|
13
|
+
*/
|
|
14
|
+
export interface BlazerIconsPluginOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Directorio raíz del proyecto a escanear.
|
|
17
|
+
* Por defecto: process.cwd()
|
|
18
|
+
*/
|
|
19
|
+
root?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Extensiones a escanear. Por defecto: ['.svelte', '.ts', '.tsx', '.js', '.jsx']
|
|
22
|
+
*/
|
|
23
|
+
extensions?: string[];
|
|
24
|
+
}
|
|
25
|
+
export declare function blazirIconsPlugin(options?: BlazerIconsPluginOptions): {
|
|
26
|
+
name: string;
|
|
27
|
+
enforce: "pre";
|
|
28
|
+
configResolved(config: {
|
|
29
|
+
root?: string;
|
|
30
|
+
}): void;
|
|
31
|
+
buildStart(): void;
|
|
32
|
+
resolveId(id: string, importer?: string): "\0virtual:blazir-icons/registry" | null | undefined;
|
|
33
|
+
load(id: string): string | undefined;
|
|
34
|
+
handleHotUpdate({ file, read, server }: {
|
|
35
|
+
file: string;
|
|
36
|
+
read: () => Promise<string>;
|
|
37
|
+
server: {
|
|
38
|
+
moduleGraph: {
|
|
39
|
+
getModuleById: (id: string) => unknown;
|
|
40
|
+
invalidateModule: (mod: unknown) => void;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
}): void;
|
|
44
|
+
watchChange(id: string): void;
|
|
45
|
+
};
|
|
46
|
+
export default blazirIconsPlugin;
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* blazir-icons — Vite / Rollup plugin
|
|
3
|
+
*
|
|
4
|
+
* Escanea tu código fuente en build-time y genera un registry mínimo
|
|
5
|
+
* que solo incluye los íconos que realmente usas.
|
|
6
|
+
*
|
|
7
|
+
* Compatible con: Vite, Rollup.
|
|
8
|
+
* Sin plugin: funciona exactamente igual que antes (registry completo).
|
|
9
|
+
*
|
|
10
|
+
* Uso en vite.config.ts:
|
|
11
|
+
* import { blazirIconsPlugin } from '@genarou/blazir-icons/plugin'
|
|
12
|
+
* plugins: [blazirIconsPlugin()]
|
|
13
|
+
*/
|
|
14
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// MAPA COMPLETO: clave del registry → nombre del archivo .svelte
|
|
18
|
+
// (necesario porque no siempre coinciden, e.g. search → MagnifiyingGlass)
|
|
19
|
+
// ============================================================================
|
|
20
|
+
const ICON_FILE_MAP = {
|
|
21
|
+
ai: 'Ai',
|
|
22
|
+
alternate: 'Alternate',
|
|
23
|
+
animatedArrowLeft: 'AnimatedArrowLeft',
|
|
24
|
+
attachment: 'Attachment',
|
|
25
|
+
bag: 'Bag',
|
|
26
|
+
bank: 'Bank',
|
|
27
|
+
bell: 'Bell',
|
|
28
|
+
blaze: 'Blaze',
|
|
29
|
+
book: 'Book',
|
|
30
|
+
box: 'Box',
|
|
31
|
+
boxAdd: 'BoxAdd',
|
|
32
|
+
building: 'Building',
|
|
33
|
+
calendar: 'Calendar',
|
|
34
|
+
calendarEdit: 'CalendarEdit',
|
|
35
|
+
calendarPlus: 'CalendarPlus',
|
|
36
|
+
cart: 'Cart',
|
|
37
|
+
category: 'Category',
|
|
38
|
+
categoryAdd: 'CategoryAdd',
|
|
39
|
+
categorySearch: 'CategorySearch',
|
|
40
|
+
chart: 'Chart',
|
|
41
|
+
chartDoc: 'ChartDoc',
|
|
42
|
+
chat: 'Chat',
|
|
43
|
+
check: 'Check',
|
|
44
|
+
checkList: 'CheckList',
|
|
45
|
+
checkO: 'CheckO',
|
|
46
|
+
chevronDown: 'ChevronDown',
|
|
47
|
+
chevronUpDown: 'ChevronUpDown',
|
|
48
|
+
circleCheck: 'CircleCheck',
|
|
49
|
+
circleExclamation: 'CircleExclamation',
|
|
50
|
+
circleExclamationO: 'CircleExclamationOutlined',
|
|
51
|
+
circleInfo: 'CircleInfo',
|
|
52
|
+
circleInfoO: 'CircleInfoOutlined',
|
|
53
|
+
circleQuestion: 'CircleQuestion',
|
|
54
|
+
circleQuestionO: 'CircleQuestionOutlined',
|
|
55
|
+
close: 'Close',
|
|
56
|
+
colors: 'Colors',
|
|
57
|
+
contact: 'Contact',
|
|
58
|
+
copy: 'Copy',
|
|
59
|
+
costIcon: 'CostIcon',
|
|
60
|
+
csv: 'Csv',
|
|
61
|
+
dashboard: 'Dashboard',
|
|
62
|
+
dashboardO: 'DashboardOutlined',
|
|
63
|
+
db: 'Db',
|
|
64
|
+
download: 'Download',
|
|
65
|
+
downloadAnimated: 'DownloadAnimated',
|
|
66
|
+
edit: 'Edit',
|
|
67
|
+
editOutline: 'EditOutline',
|
|
68
|
+
email: 'Email',
|
|
69
|
+
emailAnimated: 'EmailAnimated',
|
|
70
|
+
enterprise: 'Enterprise',
|
|
71
|
+
error: 'Error',
|
|
72
|
+
errorO: 'ErrorO',
|
|
73
|
+
excel: 'Excel',
|
|
74
|
+
excelAnimated: 'ExcelAnimated',
|
|
75
|
+
exchange: 'Exchange',
|
|
76
|
+
eye: 'Eye',
|
|
77
|
+
eyeOff: 'EyeOff',
|
|
78
|
+
favorites: 'Favorites',
|
|
79
|
+
file: 'File',
|
|
80
|
+
fileUpdateAnimated: 'FileUploadAnimated',
|
|
81
|
+
filter: 'FilterOutline',
|
|
82
|
+
filterOutline: 'FilterOutline',
|
|
83
|
+
fingerprint: 'Fingerprint',
|
|
84
|
+
folder: 'Folder',
|
|
85
|
+
form: 'Form',
|
|
86
|
+
formatListGroup: 'FormatListGroup',
|
|
87
|
+
group: 'Group',
|
|
88
|
+
hamburguer: 'Hamburguer',
|
|
89
|
+
handShake: 'HandShake',
|
|
90
|
+
heart: 'Heart',
|
|
91
|
+
height: 'Height',
|
|
92
|
+
home: 'Home',
|
|
93
|
+
image: 'Image',
|
|
94
|
+
imageAnimated: 'ImageAnimated',
|
|
95
|
+
key: 'Key',
|
|
96
|
+
list: 'List',
|
|
97
|
+
listDots: 'ListDots',
|
|
98
|
+
loadingDots: 'LoadingDots',
|
|
99
|
+
loadingRegular: 'RegularSpinner',
|
|
100
|
+
location: 'Location',
|
|
101
|
+
locationAnimated: 'LocationAnimated',
|
|
102
|
+
lock: 'Lock',
|
|
103
|
+
lockOpen: 'LockOpen',
|
|
104
|
+
logout: 'Logout',
|
|
105
|
+
lose: 'Lose',
|
|
106
|
+
magnifyingGlass: 'MagnifiyingGlass',
|
|
107
|
+
measure: 'Measure',
|
|
108
|
+
money: 'Money',
|
|
109
|
+
moon: 'Moon',
|
|
110
|
+
more: 'More',
|
|
111
|
+
notes: 'Notes',
|
|
112
|
+
objectGroup: 'ObjectGroup',
|
|
113
|
+
pay: 'Pay',
|
|
114
|
+
pdf: 'Pdf',
|
|
115
|
+
phone: 'Phone',
|
|
116
|
+
plus: 'Plus',
|
|
117
|
+
png: 'Png',
|
|
118
|
+
pointSale: 'PointSale',
|
|
119
|
+
powerPoint: 'PowerPoint',
|
|
120
|
+
print: 'Print',
|
|
121
|
+
product: 'Product',
|
|
122
|
+
profit: 'Profit',
|
|
123
|
+
project: 'Project',
|
|
124
|
+
refresh: 'Update',
|
|
125
|
+
reset: 'Reset',
|
|
126
|
+
rightArrow: 'RightArrow',
|
|
127
|
+
rocket: 'Rocket',
|
|
128
|
+
safeSolid: 'SafeSolid',
|
|
129
|
+
save: 'Save',
|
|
130
|
+
scan: 'Scan',
|
|
131
|
+
search: 'MagnifiyingGlass',
|
|
132
|
+
security: 'Security',
|
|
133
|
+
send: 'Send',
|
|
134
|
+
server: 'Server',
|
|
135
|
+
settings: 'Settings',
|
|
136
|
+
share: 'Share',
|
|
137
|
+
sharedFolder: 'SharedFolder',
|
|
138
|
+
shield: 'Shield',
|
|
139
|
+
sidebar: 'Sidebar',
|
|
140
|
+
squareChart: 'SquareChart',
|
|
141
|
+
star: 'Star',
|
|
142
|
+
sun: 'Sun',
|
|
143
|
+
supervisor: 'Supervisor',
|
|
144
|
+
swap: 'Swap',
|
|
145
|
+
table: 'Table',
|
|
146
|
+
tags: 'Tags',
|
|
147
|
+
team: 'Team',
|
|
148
|
+
timer: 'Timer',
|
|
149
|
+
tools: 'Tools',
|
|
150
|
+
trash: 'Trash',
|
|
151
|
+
trashOutline: 'TrashOutline',
|
|
152
|
+
truck: 'Truck',
|
|
153
|
+
truckReturn: 'TruckReturn',
|
|
154
|
+
upArrow: 'UpArrow',
|
|
155
|
+
upDownArrow: 'UpDownArrow',
|
|
156
|
+
upload: 'Upload',
|
|
157
|
+
uploadAnimated: 'UploadAnimated',
|
|
158
|
+
uploadLoader: 'UploadLoader',
|
|
159
|
+
user: 'User',
|
|
160
|
+
userTie: 'UserTie',
|
|
161
|
+
wallet: 'Wallet',
|
|
162
|
+
warehouse: 'Warehouse',
|
|
163
|
+
warning: 'Warning',
|
|
164
|
+
word: 'Word',
|
|
165
|
+
world: 'World',
|
|
166
|
+
xml: 'Xml',
|
|
167
|
+
zip: 'Zip',
|
|
168
|
+
};
|
|
169
|
+
const ALL_KEYS = new Set(Object.keys(ICON_FILE_MAP));
|
|
170
|
+
// ============================================================================
|
|
171
|
+
// PATRONES DE DETECCIÓN
|
|
172
|
+
// ============================================================================
|
|
173
|
+
// Detecta: name="check" | name='check' | name={"check"} | name={'check'}
|
|
174
|
+
const STATIC_RE = /\bname=(?:"([a-zA-Z][a-zA-Z0-9]*)"|'([a-zA-Z][a-zA-Z0-9]*)'|\{["']([a-zA-Z][a-zA-Z0-9]*)["']\})/g;
|
|
175
|
+
// Detecta uso dinámico: name={expr} (no string literal)
|
|
176
|
+
const DYNAMIC_RE = /\bname=\{(?!["'])/;
|
|
177
|
+
// ID del módulo virtual que reemplaza al registry completo
|
|
178
|
+
const VIRTUAL_ID = 'virtual:blazir-icons/registry';
|
|
179
|
+
const RESOLVED_ID = '\0virtual:blazir-icons/registry';
|
|
180
|
+
// ============================================================================
|
|
181
|
+
// PLUGIN
|
|
182
|
+
// ============================================================================
|
|
183
|
+
export function blazirIconsPlugin(options = {}) {
|
|
184
|
+
const detected = new Set();
|
|
185
|
+
let hasDynamic = false;
|
|
186
|
+
let scanned = false;
|
|
187
|
+
let root = options.root ?? process.cwd();
|
|
188
|
+
const extensions = options.extensions ?? ['.svelte', '.ts', '.tsx', '.js', '.jsx'];
|
|
189
|
+
// --------------------------------------------------------------------------
|
|
190
|
+
// Extrae nombres de íconos de un bloque de código fuente
|
|
191
|
+
// --------------------------------------------------------------------------
|
|
192
|
+
function scanContent(content) {
|
|
193
|
+
STATIC_RE.lastIndex = 0;
|
|
194
|
+
for (const m of content.matchAll(STATIC_RE)) {
|
|
195
|
+
const name = m[1] ?? m[2] ?? m[3];
|
|
196
|
+
if (name && ALL_KEYS.has(name))
|
|
197
|
+
detected.add(name);
|
|
198
|
+
}
|
|
199
|
+
if (!hasDynamic && DYNAMIC_RE.test(content)) {
|
|
200
|
+
hasDynamic = true;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// --------------------------------------------------------------------------
|
|
204
|
+
// Escanea recursivamente un directorio (ignora node_modules, .git, dist)
|
|
205
|
+
// --------------------------------------------------------------------------
|
|
206
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', '.svelte-kit', '.vite', 'build']);
|
|
207
|
+
function scanDir(dir) {
|
|
208
|
+
let entries;
|
|
209
|
+
try {
|
|
210
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
for (const entry of entries) {
|
|
216
|
+
const full = join(dir, entry.name);
|
|
217
|
+
if (entry.isDirectory()) {
|
|
218
|
+
if (!SKIP_DIRS.has(entry.name))
|
|
219
|
+
scanDir(full);
|
|
220
|
+
}
|
|
221
|
+
else if (entry.isFile() && extensions.some((ext) => entry.name.endsWith(ext))) {
|
|
222
|
+
try {
|
|
223
|
+
scanContent(readFileSync(full, 'utf-8'));
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// archivo ilegible — ignorar
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// --------------------------------------------------------------------------
|
|
232
|
+
// Asegura que el scan se haya hecho al menos una vez
|
|
233
|
+
// --------------------------------------------------------------------------
|
|
234
|
+
function ensureScanned() {
|
|
235
|
+
if (scanned)
|
|
236
|
+
return;
|
|
237
|
+
scanned = true;
|
|
238
|
+
scanDir(root);
|
|
239
|
+
}
|
|
240
|
+
// --------------------------------------------------------------------------
|
|
241
|
+
// Genera el código del registry mínimo
|
|
242
|
+
// --------------------------------------------------------------------------
|
|
243
|
+
function generateRegistry() {
|
|
244
|
+
const lines = [];
|
|
245
|
+
const entries = [];
|
|
246
|
+
for (const key of detected) {
|
|
247
|
+
const file = ICON_FILE_MAP[key];
|
|
248
|
+
if (!file)
|
|
249
|
+
continue;
|
|
250
|
+
lines.push(`import _${key} from '@genarou/blazir-icons/icons/${file}';`);
|
|
251
|
+
entries.push(` ${key}: _${key}`);
|
|
252
|
+
}
|
|
253
|
+
lines.push(`\nexport const iconRegistry = {\n${entries.join(',\n')}\n};`);
|
|
254
|
+
lines.push(`export type IconName = keyof typeof iconRegistry;`);
|
|
255
|
+
lines.push(`export type IconComponent = any;`);
|
|
256
|
+
return lines.join('\n');
|
|
257
|
+
}
|
|
258
|
+
// --------------------------------------------------------------------------
|
|
259
|
+
// Plugin object (compatible con Vite y Rollup)
|
|
260
|
+
// --------------------------------------------------------------------------
|
|
261
|
+
return {
|
|
262
|
+
name: 'blazir-icons',
|
|
263
|
+
enforce: 'pre',
|
|
264
|
+
// Vite: captura el root configurado
|
|
265
|
+
configResolved(config) {
|
|
266
|
+
if (config.root)
|
|
267
|
+
root = config.root;
|
|
268
|
+
},
|
|
269
|
+
// Prod build: escanear todos los archivos antes de comenzar
|
|
270
|
+
buildStart() {
|
|
271
|
+
ensureScanned();
|
|
272
|
+
},
|
|
273
|
+
// Intercepta la importación del registry desde Icon.svelte
|
|
274
|
+
resolveId(id, importer) {
|
|
275
|
+
if (id === VIRTUAL_ID)
|
|
276
|
+
return RESOLVED_ID;
|
|
277
|
+
// Solo interceptar cuando podemos optimizar:
|
|
278
|
+
// - el importer es Icon.svelte de blazir-icons
|
|
279
|
+
// - el id es el registry
|
|
280
|
+
const isRegistryImport = (id.includes('registry') || id === './icons/registry.js') &&
|
|
281
|
+
importer?.includes('blazir-icons') &&
|
|
282
|
+
importer?.includes('Icon.svelte');
|
|
283
|
+
if (isRegistryImport) {
|
|
284
|
+
ensureScanned();
|
|
285
|
+
// Si hay uso dinámico o no detectamos nada → dejar pasar (registry completo)
|
|
286
|
+
if (hasDynamic || detected.size === 0)
|
|
287
|
+
return null;
|
|
288
|
+
return RESOLVED_ID;
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
// Genera el módulo virtual con solo los íconos detectados
|
|
292
|
+
load(id) {
|
|
293
|
+
if (id !== RESOLVED_ID)
|
|
294
|
+
return;
|
|
295
|
+
ensureScanned();
|
|
296
|
+
return generateRegistry();
|
|
297
|
+
},
|
|
298
|
+
// Vite dev server: re-escanear archivos modificados
|
|
299
|
+
handleHotUpdate({ file, read, server }) {
|
|
300
|
+
if (!file.includes('node_modules') && extensions.some((ext) => file.endsWith(ext))) {
|
|
301
|
+
read().then((content) => {
|
|
302
|
+
const prevSize = detected.size;
|
|
303
|
+
const prevDynamic = hasDynamic;
|
|
304
|
+
scanContent(content);
|
|
305
|
+
// Si se detectaron nuevos íconos o cambió el estado dinámico → invalidar el módulo virtual
|
|
306
|
+
if (detected.size !== prevSize || hasDynamic !== prevDynamic) {
|
|
307
|
+
const mod = server.moduleGraph.getModuleById(RESOLVED_ID);
|
|
308
|
+
if (mod)
|
|
309
|
+
server.moduleGraph.invalidateModule(mod);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
// Rollup watch mode: re-escanear archivos modificados
|
|
315
|
+
watchChange(id) {
|
|
316
|
+
if (!id.includes('node_modules') && extensions.some((ext) => id.endsWith(ext))) {
|
|
317
|
+
try {
|
|
318
|
+
scanContent(readFileSync(id, 'utf-8'));
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
// ignorar archivos ilegibles
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
export default blazirIconsPlugin;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { IconProps } from './types.js';
|
|
2
|
+
export declare class LRU<K, V> {
|
|
3
|
+
private readonly max;
|
|
4
|
+
private readonly map;
|
|
5
|
+
constructor(max: number);
|
|
6
|
+
get(key: K): V | undefined;
|
|
7
|
+
set(key: K, val: V): void;
|
|
8
|
+
has(key: K): boolean;
|
|
9
|
+
get size(): number;
|
|
10
|
+
clear(): void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Devuelve un objeto FROZEN y ESTABLE para la combinación (preset, variant).
|
|
14
|
+
*
|
|
15
|
+
* Garantías:
|
|
16
|
+
* - Mismos argumentos → EXACTAMENTE la misma referencia (Object.is === true)
|
|
17
|
+
* - El objeto es inmutable (Object.freeze)
|
|
18
|
+
* - Sin TTL: la validez es estructural (los presets son constantes de módulo)
|
|
19
|
+
*/
|
|
20
|
+
export declare function getPresetVariantMerge(preset: string | undefined, variant: string | undefined): Readonly<Partial<IconProps>>;
|
|
21
|
+
/**
|
|
22
|
+
* Retorna true si todos los valores de props son primitivos seguros para cache.
|
|
23
|
+
* Complejidad: O(n) donde n = número de props.
|
|
24
|
+
*/
|
|
25
|
+
export declare function arePropsStable(props: Record<string, unknown>): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Construye una clave estructural para el cache completo de props.
|
|
28
|
+
* Solo incluye props primitivas y estables.
|
|
29
|
+
*
|
|
30
|
+
* ⚠️ Solo llamar cuando arePropsStable() === true.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildStructuralKey(props: Record<string, unknown>): string;
|
|
33
|
+
export declare function getCachedMergedProps(key: string): Readonly<IconProps> | undefined;
|
|
34
|
+
export declare function setCachedMergedProps(key: string, props: Readonly<IconProps>): void;
|
|
35
|
+
export declare function toSizePx(s: number | string): string;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// src/lib/smart-cache.ts
|
|
2
|
+
// Cache adaptativo inteligente para blazir-icons
|
|
3
|
+
// Diseñado para máxima estabilidad referencial y rendimiento en listas grandes
|
|
4
|
+
import { iconPresets, iconVariants } from './presets.js';
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// MICRO-LRU — O(1) get/set con Map de inserción-orden como cola
|
|
7
|
+
// Map en V8 mantiene orden de inserción; delete + re-insert = mover al final
|
|
8
|
+
// ============================================================================
|
|
9
|
+
export class LRU {
|
|
10
|
+
constructor(max) {
|
|
11
|
+
this.max = max;
|
|
12
|
+
this.map = new Map();
|
|
13
|
+
}
|
|
14
|
+
get(key) {
|
|
15
|
+
if (!this.map.has(key))
|
|
16
|
+
return undefined;
|
|
17
|
+
// Promover a "más reciente": delete + re-insert
|
|
18
|
+
const val = this.map.get(key);
|
|
19
|
+
this.map.delete(key);
|
|
20
|
+
this.map.set(key, val);
|
|
21
|
+
return val;
|
|
22
|
+
}
|
|
23
|
+
set(key, val) {
|
|
24
|
+
if (this.map.has(key)) {
|
|
25
|
+
this.map.delete(key);
|
|
26
|
+
}
|
|
27
|
+
else if (this.map.size >= this.max) {
|
|
28
|
+
// Evictar el LRU (primer elemento = menos reciente)
|
|
29
|
+
this.map.delete(this.map.keys().next().value);
|
|
30
|
+
}
|
|
31
|
+
this.map.set(key, val);
|
|
32
|
+
}
|
|
33
|
+
has(key) {
|
|
34
|
+
return this.map.has(key);
|
|
35
|
+
}
|
|
36
|
+
get size() {
|
|
37
|
+
return this.map.size;
|
|
38
|
+
}
|
|
39
|
+
clear() {
|
|
40
|
+
this.map.clear();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// CACHE PRESET × VARIANT
|
|
45
|
+
//
|
|
46
|
+
// Propósito: devolver EXACTAMENTE la misma referencia de objeto para la misma
|
|
47
|
+
// combinación (preset, variant). Esto permite que $derived en Icon.svelte NO
|
|
48
|
+
// cree nuevos objetos si preset/variant no cambiaron.
|
|
49
|
+
//
|
|
50
|
+
// Bounds: len(presets) × len(variants) ≈ 12 × 8 = 96 entradas máximo.
|
|
51
|
+
// → No necesita evicción. Plain Map, determinista.
|
|
52
|
+
// ============================================================================
|
|
53
|
+
const _pvCache = new Map();
|
|
54
|
+
/**
|
|
55
|
+
* Devuelve un objeto FROZEN y ESTABLE para la combinación (preset, variant).
|
|
56
|
+
*
|
|
57
|
+
* Garantías:
|
|
58
|
+
* - Mismos argumentos → EXACTAMENTE la misma referencia (Object.is === true)
|
|
59
|
+
* - El objeto es inmutable (Object.freeze)
|
|
60
|
+
* - Sin TTL: la validez es estructural (los presets son constantes de módulo)
|
|
61
|
+
*/
|
|
62
|
+
export function getPresetVariantMerge(preset, variant) {
|
|
63
|
+
// Usar \x01 como separador para evitar colisiones "ab|c" vs "a|bc"
|
|
64
|
+
const key = `${preset ?? '\x00'}\x01${variant ?? '\x00'}`;
|
|
65
|
+
const hit = _pvCache.get(key);
|
|
66
|
+
if (hit)
|
|
67
|
+
return hit;
|
|
68
|
+
const pp = preset
|
|
69
|
+
? (iconPresets[preset] ?? {})
|
|
70
|
+
: {};
|
|
71
|
+
const vp = variant
|
|
72
|
+
? (iconVariants[variant] ?? {})
|
|
73
|
+
: {};
|
|
74
|
+
// Orden: variant gana sobre preset (igual que el merge original)
|
|
75
|
+
const merged = Object.freeze({ ...pp, ...vp });
|
|
76
|
+
_pvCache.set(key, merged);
|
|
77
|
+
return merged;
|
|
78
|
+
}
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// DETECCIÓN DE ESTABILIDAD DE PROPS
|
|
81
|
+
//
|
|
82
|
+
// Props "estables": todos sus valores son primitivos (string, number, boolean)
|
|
83
|
+
// Props "inestables": contienen objetos, funciones o arrays
|
|
84
|
+
//
|
|
85
|
+
// Solo las props estables son candidatas al cache estructural completo.
|
|
86
|
+
// ============================================================================
|
|
87
|
+
/**
|
|
88
|
+
* Props que SIEMPRE son inestables (objetos, funciones, arrays).
|
|
89
|
+
* Si aparecen en los props del usuario → no cachear el resultado completo.
|
|
90
|
+
*/
|
|
91
|
+
const UNSTABLE_KEYS = new Set([
|
|
92
|
+
'actions',
|
|
93
|
+
'effects',
|
|
94
|
+
'attrs',
|
|
95
|
+
'parentHoverContext',
|
|
96
|
+
'children',
|
|
97
|
+
]);
|
|
98
|
+
/**
|
|
99
|
+
* Props excluidas del cache key (se pasan al hijo pero no afectan
|
|
100
|
+
* a la merge de base, o son strings que varían libremente).
|
|
101
|
+
*/
|
|
102
|
+
const SKIP_IN_KEY = new Set([
|
|
103
|
+
'style',
|
|
104
|
+
'class',
|
|
105
|
+
'className',
|
|
106
|
+
'actions',
|
|
107
|
+
'effects',
|
|
108
|
+
'attrs',
|
|
109
|
+
'parentHoverContext',
|
|
110
|
+
'children',
|
|
111
|
+
// preset/variant se resuelven por separado (ya están en baseProps)
|
|
112
|
+
'preset',
|
|
113
|
+
'variant',
|
|
114
|
+
// lazy es meta-prop de Icon.svelte, no llega al hijo
|
|
115
|
+
'lazy',
|
|
116
|
+
]);
|
|
117
|
+
/**
|
|
118
|
+
* Retorna true si todos los valores de props son primitivos seguros para cache.
|
|
119
|
+
* Complejidad: O(n) donde n = número de props.
|
|
120
|
+
*/
|
|
121
|
+
export function arePropsStable(props) {
|
|
122
|
+
for (const k in props) {
|
|
123
|
+
if (UNSTABLE_KEYS.has(k))
|
|
124
|
+
return false;
|
|
125
|
+
const v = props[k];
|
|
126
|
+
if (v !== null && v !== undefined && typeof v === 'object')
|
|
127
|
+
return false;
|
|
128
|
+
if (typeof v === 'function')
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
// ============================================================================
|
|
134
|
+
// CLAVE ESTRUCTURAL
|
|
135
|
+
//
|
|
136
|
+
// Clave determinista derivada de las props primitivas (excluye las inestables
|
|
137
|
+
// y las meta-props). Las claves se ordenan alfabéticamente para asegurar
|
|
138
|
+
// que dos objetos con las mismas props en distinto orden generen la misma clave.
|
|
139
|
+
// ============================================================================
|
|
140
|
+
/**
|
|
141
|
+
* Construye una clave estructural para el cache completo de props.
|
|
142
|
+
* Solo incluye props primitivas y estables.
|
|
143
|
+
*
|
|
144
|
+
* ⚠️ Solo llamar cuando arePropsStable() === true.
|
|
145
|
+
*/
|
|
146
|
+
export function buildStructuralKey(props) {
|
|
147
|
+
const parts = [];
|
|
148
|
+
// Ordenar para determinismo (orden de props puede variar)
|
|
149
|
+
const keys = Object.keys(props).sort();
|
|
150
|
+
for (const k of keys) {
|
|
151
|
+
if (SKIP_IN_KEY.has(k))
|
|
152
|
+
continue;
|
|
153
|
+
const v = props[k];
|
|
154
|
+
if (v === undefined)
|
|
155
|
+
continue;
|
|
156
|
+
// Separador: \x02 es poco probable en valores reales
|
|
157
|
+
parts.push(`${k}\x02${v}`);
|
|
158
|
+
}
|
|
159
|
+
return parts.join('\x03');
|
|
160
|
+
}
|
|
161
|
+
// ============================================================================
|
|
162
|
+
// CACHE COMPLETO DE MERGED PROPS (LRU, solo props estables)
|
|
163
|
+
//
|
|
164
|
+
// Scope: global (compartido entre instancias del mismo componente).
|
|
165
|
+
// Esto es correcto porque la función de merge es pura cuando los inputs
|
|
166
|
+
// son solo primitivos.
|
|
167
|
+
//
|
|
168
|
+
// Tamaño: 128 entradas. Para listas con M iconos distintos: hasta 128 configs
|
|
169
|
+
// únicas se cachean. Más allá, el LRU evicta las menos usadas.
|
|
170
|
+
// ============================================================================
|
|
171
|
+
const _mergedPropsCache = new LRU(128);
|
|
172
|
+
export function getCachedMergedProps(key) {
|
|
173
|
+
return _mergedPropsCache.get(key);
|
|
174
|
+
}
|
|
175
|
+
export function setCachedMergedProps(key, props) {
|
|
176
|
+
_mergedPropsCache.set(key, props);
|
|
177
|
+
}
|
|
178
|
+
// ============================================================================
|
|
179
|
+
// ESTABILIDAD REFERENCIAL PARA SIZE
|
|
180
|
+
//
|
|
181
|
+
// El tamaño de la caja se deriva de (p.size ?? preset.size ?? variant.size).
|
|
182
|
+
// Cacheamos el string resultado para evitar crear nuevos strings en cada render.
|
|
183
|
+
// ============================================================================
|
|
184
|
+
const _sizeStringCache = new Map();
|
|
185
|
+
export function toSizePx(s) {
|
|
186
|
+
const hit = _sizeStringCache.get(s);
|
|
187
|
+
if (hit)
|
|
188
|
+
return hit;
|
|
189
|
+
const result = typeof s === 'number' ? `${s}px` : s;
|
|
190
|
+
_sizeStringCache.set(s, result);
|
|
191
|
+
return result;
|
|
192
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -34,6 +34,14 @@ export interface IconProps {
|
|
|
34
34
|
elastic?: boolean;
|
|
35
35
|
hoverColor?: string;
|
|
36
36
|
activeColor?: string;
|
|
37
|
+
/** Apaga el icono visualmente (opacity 0.4) y bloquea hover/pointer-events. */
|
|
38
|
+
disabled?: boolean;
|
|
39
|
+
/** Reemplaza el icono con un spinner hasta que sea false. Reutiliza la animación spin interna. */
|
|
40
|
+
loading?: boolean;
|
|
41
|
+
/** Texto del tooltip. Se muestra al hacer hover sobre el icono. */
|
|
42
|
+
tooltip?: string;
|
|
43
|
+
/** Delay antes de mostrar el tooltip en ms. Default: 0 (instantáneo). */
|
|
44
|
+
tooltipDelay?: number;
|
|
37
45
|
parentHoverContext?: {
|
|
38
46
|
hovered: boolean;
|
|
39
47
|
} | null;
|
|
@@ -53,9 +61,18 @@ export interface IconProps {
|
|
|
53
61
|
effects?: IconEffectOptions;
|
|
54
62
|
children?: any;
|
|
55
63
|
mode?: IconMode;
|
|
56
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* Velocidad de transición hover — preset semántico.
|
|
66
|
+
* - `"instant"` → 0ms
|
|
67
|
+
* - `"fast"` → 120ms
|
|
68
|
+
* - `"normal"` → 250ms ← default
|
|
69
|
+
* - `"slow"` → 400ms
|
|
70
|
+
* - `"sluggish"` → 600ms
|
|
71
|
+
*/
|
|
72
|
+
hoverSpeed?: "instant" | "fast" | "normal" | "slow" | "sluggish";
|
|
73
|
+
/** Duración exacta de la transición hover (ms o '300ms'/'0.3s'). Prevalece sobre hoverSpeed. */
|
|
57
74
|
transitionMs?: number | string;
|
|
58
|
-
/** Easing para
|
|
75
|
+
/** Easing CSS para la transición hover. Ej: 'ease', 'ease-in-out', 'cubic-bezier(...)'. */
|
|
59
76
|
transitionEasing?: string;
|
|
60
77
|
}
|
|
61
78
|
export type { IconName } from "./icons/registry";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sanitize.ts — Lightweight SVG sanitizer. Zero dependencies.
|
|
3
|
+
*
|
|
4
|
+
* Browser: uses the native DOMParser with image/svg+xml for robust, spec-compliant parsing.
|
|
5
|
+
* SSR: regex-based fallback that strips the most dangerous vectors.
|
|
6
|
+
*
|
|
7
|
+
* Threat model (SVG inner content — not the <svg> tag itself):
|
|
8
|
+
* ✗ <script> blocks
|
|
9
|
+
* ✗ <foreignObject> — embeds arbitrary HTML
|
|
10
|
+
* ✗ <iframe>, <object>, <embed>, <link>, <base>
|
|
11
|
+
* ✗ Event handlers — on*, xlink:actuate, etc.
|
|
12
|
+
* ✗ javascript: / vbscript: / data: URIs in href / src / action
|
|
13
|
+
* ✗ CSS expression() / javascript: in style attributes
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Sanitizes raw SVG inner markup against XSS vectors.
|
|
17
|
+
*
|
|
18
|
+
* Safe to call with content from external APIs or user input.
|
|
19
|
+
* For static, trusted SVG strings defined in your own code, pass
|
|
20
|
+
* `sanitize={false}` to `CustomIcon` to skip the overhead entirely.
|
|
21
|
+
*
|
|
22
|
+
* @param raw - SVG inner markup (paths, circles, etc. — not the <svg> tag)
|
|
23
|
+
* @returns Sanitized SVG markup string, empty string on parse failure
|
|
24
|
+
*/
|
|
25
|
+
export declare function sanitizeSvg(raw: string): string;
|