@robosophy/ui-vue 1.1.9 → 1.1.10
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/ai-context.md +8 -0
- package/components.md +95 -0
- package/dist/icon-manifest.json +0 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +220 -7
- package/dist/vite.js.map +1 -1
- package/icon-scanner.md +293 -0
- package/package.json +4 -2
package/ai-context.md
CHANGED
|
@@ -27,6 +27,14 @@ Use this package for shared Vue 3 UI components.
|
|
|
27
27
|
- Use PrimeVue only when it provides real behavior for that component; actions should use PrimeVue `Button` in PrimeVue apps.
|
|
28
28
|
- `RsSidebar` and `RsAccountSelector` use native PrimeVue controls for interactive UI; PrimeVue apps should register the Robosophy PrimeVue preset and load `@robosophy/prime-theme/prime.css` so Button/Menu states receive Robosophy styling.
|
|
29
29
|
- `RsIcon` needs `robosophyMaterialSymbols()` from `@robosophy/ui-vue/vite` in consuming Vite apps so builds generate an SVG sprite with only the needed icons. The plugin auto-detects icon usage from source — no manual declaration needed for static patterns. Use `rsPrimeIcon()` for PrimeVue APIs that accept icon class strings. Add dynamic icons via `include`. For full control over icon props, use the `#icon` slot instead of `rsPrimeIcon()`.
|
|
30
|
+
- **Icon detection rules:** the scanner detects 5 patterns. Read `icon-scanner.md` for the full reference. Key rules for agent work:
|
|
31
|
+
- `<RsIcon name="..." />` — always detected (Pattern 1)
|
|
32
|
+
- `rsPrimeIcon("...")` — detected (Pattern 2)
|
|
33
|
+
- `{ icon: "..." }` in objects — detected with weight/family extraction (Pattern 3)
|
|
34
|
+
- `// @robosophy-icon: name` magic comments — detected (Pattern 4, use for props/variables)
|
|
35
|
+
- Arrays like `const iconNames = ["home", ...]` — detected if variable name contains "icon" (Pattern 5)
|
|
36
|
+
- `<RsIcon :name="variable" />` — NOT detected; add magic comment or `include`
|
|
37
|
+
- `<RsIcon :name="item.icon" />` — NOT detected from template, but the `icon: "..."` in the data source IS detected by Pattern 3
|
|
30
38
|
- Use `RsSidebar` for reusable module navigation. `item-click` stays in the consuming app; the optional `modules` dropdown can redirect with the provided `link` when `navigateOnModuleSelect` is `true`.
|
|
31
39
|
- Keep props focused on stable component API. Use slots for flexible content.
|
|
32
40
|
- Export every public component and public type from `src/index.ts`.
|
package/components.md
CHANGED
|
@@ -80,6 +80,101 @@ icons and inlines it in the HTML. No fonts are loaded.
|
|
|
80
80
|
For dynamic icon names that the scanner cannot see, add them to
|
|
81
81
|
`robosophyMaterialSymbols({ include: [...] })`.
|
|
82
82
|
|
|
83
|
+
### How the scanner works
|
|
84
|
+
|
|
85
|
+
The plugin scans source files and detects icons using five patterns:
|
|
86
|
+
|
|
87
|
+
| Pattern | Detects | Example |
|
|
88
|
+
|---------|---------|---------|
|
|
89
|
+
| 1 | `<RsIcon name="..." />` tags | `<RsIcon name="home" />` |
|
|
90
|
+
| 2 | `rsPrimeIcon("...")` calls | `rsPrimeIcon('settings', { family: 'rounded' })` |
|
|
91
|
+
| 3 | `icon: "..."` in objects | `{ icon: "inbox" }` |
|
|
92
|
+
| 4 | Magic comments | `// @robosophy-icon: home` |
|
|
93
|
+
| 5 | Arrays of strings (name has "icon") | `const iconNames = ["home", "search"]` |
|
|
94
|
+
|
|
95
|
+
**Pattern 3** also extracts `weight` and `family` from the enclosing object:
|
|
96
|
+
```ts
|
|
97
|
+
{ icon: "home", weight: 200, family: "rounded" }
|
|
98
|
+
// → weight 200, family "rounded"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Pattern 4** (magic comments) is for cases where the scanner cannot detect an icon
|
|
102
|
+
statically — typically when icon names are passed as props, stored in variables, or
|
|
103
|
+
returned from functions:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
// @robosophy-icon: manage_accounts
|
|
107
|
+
// @robosophy-icon: settings family=rounded weight=200
|
|
108
|
+
// @robosophy-icon: logout
|
|
109
|
+
|
|
110
|
+
const action = { icon: "manage_accounts", label: "Perfil" };
|
|
111
|
+
const getIcon = () => "settings";
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Pattern 5** detects arrays assigned to variables whose name contains "icon":
|
|
115
|
+
```ts
|
|
116
|
+
const icons = ["home", "search"]; // detected
|
|
117
|
+
const sidebarIconNames = ["warning"]; // detected
|
|
118
|
+
const names = ["home", "search"]; // NOT detected (no "icon" in name)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### When to use `include`
|
|
122
|
+
|
|
123
|
+
Use the `include` option when an icon is used in many places or is controlled by
|
|
124
|
+
remote configuration:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
robosophyMaterialSymbols({
|
|
128
|
+
include: [
|
|
129
|
+
"home",
|
|
130
|
+
{ family: "rounded", name: "settings", weight: 200 },
|
|
131
|
+
],
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
If `weight` is omitted, all valid weights (100–700) are included.
|
|
136
|
+
|
|
137
|
+
### When to use magic comments
|
|
138
|
+
|
|
139
|
+
Use `// @robosophy-icon:` when the icon is used in a single file and the variable
|
|
140
|
+
name doesn't contain "icon":
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
// @robosophy-icon: manage_accounts
|
|
144
|
+
// @robosophy-icon: logout
|
|
145
|
+
const actions = [
|
|
146
|
+
{ id: "profile", label: "Perfil", icon: "manage_accounts" },
|
|
147
|
+
{ id: "logout", label: "Salir", icon: "logout" },
|
|
148
|
+
];
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Special cases: props and iteration
|
|
152
|
+
|
|
153
|
+
**Props to RsSidebar:** icons defined in the `items` array's `icon` property
|
|
154
|
+
are detected by Pattern 3:
|
|
155
|
+
```ts
|
|
156
|
+
const items = [
|
|
157
|
+
{ id: "home", label: "Home", icon: "home" }, // ✅ detected
|
|
158
|
+
];
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Props to RsAccountSelector:** same — `icon` in action objects is detected.
|
|
162
|
+
|
|
163
|
+
**v-for iteration with inline objects:** objects in the iterated array are detected:
|
|
164
|
+
```vue
|
|
165
|
+
<RsIcon v-for="item in items" :name="item.icon" />
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**v-for with external variable:** not detectable — use magic comments:
|
|
169
|
+
```vue
|
|
170
|
+
<RsIcon v-for="name in iconNames" :name="name" /> <!-- add @robosophy-icon -->
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Full documentation
|
|
174
|
+
|
|
175
|
+
For the complete technical reference (regex patterns, scanner architecture, plugin
|
|
176
|
+
options, and known limitations), read `icon-scanner.md` in this package.
|
|
177
|
+
|
|
83
178
|
For full control over icon props (fill, grade, etc.), use the slot approach:
|
|
84
179
|
|
|
85
180
|
```vue
|
package/dist/icon-manifest.json
CHANGED
package/dist/vite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAA4B,MAAM,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAEhG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,KAAK,SAAS,GAAG,MAAM,GAAG;IAAE,MAAM,CAAC,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AACnF,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;AAE3E,MAAM,MAAM,+BAA+B,GAAG;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,KAAK,YAAY,GAAG,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AAyBhE,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,+BAAoC,GAC5C,MAAM,CAwHR;AA4LD,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,EACpC,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,YAAY,CAAC,CA0CvB;AAED,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,GAClB,iBAAiB,EAAE,CAUrB;
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAA4B,MAAM,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAEhG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,KAAK,SAAS,GAAG,MAAM,GAAG;IAAE,MAAM,CAAC,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AACnF,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;AAE3E,MAAM,MAAM,+BAA+B,GAAG;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,KAAK,YAAY,GAAG,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AAyBhE,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,+BAAoC,GAC5C,MAAM,CAwHR;AA4LD,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,EACpC,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,YAAY,CAAC,CA0CvB;AAED,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,GAClB,iBAAiB,EAAE,CAUrB;AAmbD,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAY1E"}
|
package/dist/vite.js
CHANGED
|
@@ -304,6 +304,121 @@ function buildMaterialSymbolsCss(iconClasses) {
|
|
|
304
304
|
}
|
|
305
305
|
];
|
|
306
306
|
}
|
|
307
|
+
function skipStringBackward(source, pos, quote) {
|
|
308
|
+
pos--;
|
|
309
|
+
while (pos >= 0) {
|
|
310
|
+
if (source[pos] === "\\") {
|
|
311
|
+
pos -= 2;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (source[pos] === quote) {
|
|
315
|
+
let backslashCount = 0;
|
|
316
|
+
let k = pos - 1;
|
|
317
|
+
while (k >= 0 && source[k] === "\\") {
|
|
318
|
+
backslashCount++;
|
|
319
|
+
k--;
|
|
320
|
+
}
|
|
321
|
+
if (backslashCount % 2 === 1) {
|
|
322
|
+
pos = pos - backslashCount - 1;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
return pos - 1;
|
|
326
|
+
}
|
|
327
|
+
pos--;
|
|
328
|
+
}
|
|
329
|
+
return -1;
|
|
330
|
+
}
|
|
331
|
+
function skipStringForward(source, pos, quote) {
|
|
332
|
+
pos++;
|
|
333
|
+
while (pos < source.length) {
|
|
334
|
+
if (source[pos] === "\\") {
|
|
335
|
+
pos += 2;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (source[pos] === quote) {
|
|
339
|
+
return pos + 1;
|
|
340
|
+
}
|
|
341
|
+
pos++;
|
|
342
|
+
}
|
|
343
|
+
return source.length;
|
|
344
|
+
}
|
|
345
|
+
function findEnclosingObject(source, fromIndex) {
|
|
346
|
+
let depth = 0;
|
|
347
|
+
let i = fromIndex;
|
|
348
|
+
while (i >= 0) {
|
|
349
|
+
const char = source[i];
|
|
350
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
351
|
+
i = skipStringBackward(source, i, char);
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
if (char === "/" && i > 0 && source[i - 1] === "/") {
|
|
355
|
+
i -= 2;
|
|
356
|
+
while (i >= 0 && source[i] !== "\n") i--;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (char === "/" && i > 0 && source[i - 1] === "*") {
|
|
360
|
+
i -= 2;
|
|
361
|
+
while (i >= 0) {
|
|
362
|
+
if (source[i] === "/" && i + 1 < source.length && source[i + 1] === "*") {
|
|
363
|
+
i--;
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
i--;
|
|
367
|
+
}
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (char === "}") {
|
|
371
|
+
depth++;
|
|
372
|
+
} else if (char === "{") {
|
|
373
|
+
if (depth === 0) {
|
|
374
|
+
const start = i;
|
|
375
|
+
let j = i + 1;
|
|
376
|
+
let innerDepth = 1;
|
|
377
|
+
while (j < source.length) {
|
|
378
|
+
const c = source[j];
|
|
379
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
380
|
+
j = skipStringForward(source, j, c);
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (c === "/") {
|
|
384
|
+
if (j + 1 < source.length && source[j + 1] === "/") {
|
|
385
|
+
j += 2;
|
|
386
|
+
while (j < source.length && source[j] !== "\n") j++;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (j + 1 < source.length && source[j + 1] === "*") {
|
|
390
|
+
j += 2;
|
|
391
|
+
while (j + 1 < source.length) {
|
|
392
|
+
if (source[j] === "*" && source[j + 1] === "/") {
|
|
393
|
+
j += 2;
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
j++;
|
|
397
|
+
}
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (c === "{") innerDepth++;
|
|
402
|
+
else if (c === "}") {
|
|
403
|
+
innerDepth--;
|
|
404
|
+
if (innerDepth === 0) {
|
|
405
|
+
return {
|
|
406
|
+
start,
|
|
407
|
+
end: j,
|
|
408
|
+
body: source.slice(start + 1, j)
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
j++;
|
|
413
|
+
}
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
depth--;
|
|
417
|
+
}
|
|
418
|
+
i--;
|
|
419
|
+
}
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
307
422
|
function collectFromSource(source, usages) {
|
|
308
423
|
const rsIconTagPattern = /<RsIcon\b([^>]*)>/g;
|
|
309
424
|
let tagMatch;
|
|
@@ -316,8 +431,9 @@ function collectFromSource(source, usages) {
|
|
|
316
431
|
const weight = rawWeight ? parseInt(rawWeight, 10) : DEFAULT_WEIGHT;
|
|
317
432
|
const resolvedWeight = !isNaN(weight) && weight > 0 ? weight : DEFAULT_WEIGHT;
|
|
318
433
|
if (rawName) {
|
|
434
|
+
const isNameDynamic = ":name" in attrs;
|
|
319
435
|
const familyList = resolveFamilies(rawFamily, hasFamily);
|
|
320
|
-
const names = resolveNames(rawName);
|
|
436
|
+
const names = resolveNames(rawName, isNameDynamic);
|
|
321
437
|
for (const family of familyList) {
|
|
322
438
|
for (const name of names) {
|
|
323
439
|
addUsage(usages, { family, name, weight: resolvedWeight });
|
|
@@ -337,11 +453,107 @@ function collectFromSource(source, usages) {
|
|
|
337
453
|
const objectIconPattern = /\bicon\s*:\s*["']([a-z0-9_]+)["']/g;
|
|
338
454
|
let objectMatch;
|
|
339
455
|
while (objectMatch = objectIconPattern.exec(source)) {
|
|
456
|
+
const name = objectMatch[1] ?? "";
|
|
457
|
+
const obj = findEnclosingObject(source, objectMatch.index);
|
|
458
|
+
if (obj) {
|
|
459
|
+
const cleanBody = obj.body.replace(/\/\/.*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
460
|
+
const weightMatch = cleanBody.match(/\bweight\s*:\s*(\d+)/);
|
|
461
|
+
const familyMatch = cleanBody.match(/\bfamily\s*:\s*["'](\w+)["']/);
|
|
462
|
+
addUsage(usages, {
|
|
463
|
+
family: familyMatch ? familyMatch[1] : defaultFamily,
|
|
464
|
+
name,
|
|
465
|
+
weight: weightMatch ? parseInt(weightMatch[1], 10) : void 0
|
|
466
|
+
});
|
|
467
|
+
} else {
|
|
468
|
+
addUsage(usages, { family: defaultFamily, name });
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const magicCommentPattern = /\/\/\s*@robosophy-icon:\s*([a-z0-9_]+)/g;
|
|
472
|
+
let magicMatch;
|
|
473
|
+
while (magicMatch = magicCommentPattern.exec(source)) {
|
|
474
|
+
const name = magicMatch[1] ?? "";
|
|
475
|
+
const lineEnd = source.indexOf("\n", magicMatch.index);
|
|
476
|
+
const line = lineEnd === -1 ? source.slice(magicMatch.index) : source.slice(magicMatch.index, lineEnd);
|
|
477
|
+
const familyMatch = line.match(/\bfamily\s*=\s*["']?(\w+)["']?/);
|
|
478
|
+
const weightMatch = line.match(/\bweight\s*=\s*(\d+)/);
|
|
340
479
|
addUsage(usages, {
|
|
341
|
-
family: defaultFamily,
|
|
342
|
-
name
|
|
480
|
+
family: familyMatch ? familyMatch[1] : defaultFamily,
|
|
481
|
+
name,
|
|
482
|
+
weight: weightMatch ? parseInt(weightMatch[1], 10) : void 0
|
|
343
483
|
});
|
|
344
484
|
}
|
|
485
|
+
const iconArrayPattern = /(?:const|let|var)\s+(\w*[Ii]con\w*)\s*=\s*\[/g;
|
|
486
|
+
let arrayMatch;
|
|
487
|
+
while (arrayMatch = iconArrayPattern.exec(source)) {
|
|
488
|
+
const bracketStart = arrayMatch.index + arrayMatch[0].length - 1;
|
|
489
|
+
let depth = 1;
|
|
490
|
+
let j = bracketStart + 1;
|
|
491
|
+
while (j < source.length) {
|
|
492
|
+
const c = source[j];
|
|
493
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
494
|
+
j = skipStringForward(source, j, c);
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
if (c === "/") {
|
|
498
|
+
if (j + 1 < source.length && source[j + 1] === "/") {
|
|
499
|
+
j += 2;
|
|
500
|
+
while (j < source.length && source[j] !== "\n") j++;
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
if (j + 1 < source.length && source[j + 1] === "*") {
|
|
504
|
+
j += 2;
|
|
505
|
+
while (j + 1 < source.length) {
|
|
506
|
+
if (source[j] === "*" && source[j + 1] === "/") {
|
|
507
|
+
j += 2;
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
j++;
|
|
511
|
+
}
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if (c === "[") depth++;
|
|
516
|
+
else if (c === "]") {
|
|
517
|
+
depth--;
|
|
518
|
+
if (depth === 0) break;
|
|
519
|
+
}
|
|
520
|
+
j++;
|
|
521
|
+
}
|
|
522
|
+
if (depth === 0) {
|
|
523
|
+
const arrayContent = source.slice(bracketStart + 1, j);
|
|
524
|
+
let braceDepth = 0;
|
|
525
|
+
let k = 0;
|
|
526
|
+
while (k < arrayContent.length) {
|
|
527
|
+
const c = arrayContent[k];
|
|
528
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
529
|
+
if (braceDepth === 0) {
|
|
530
|
+
const quote = c;
|
|
531
|
+
let strEnd = k + 1;
|
|
532
|
+
while (strEnd < arrayContent.length) {
|
|
533
|
+
if (arrayContent[strEnd] === "\\") {
|
|
534
|
+
strEnd += 2;
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
if (arrayContent[strEnd] === quote) break;
|
|
538
|
+
strEnd++;
|
|
539
|
+
}
|
|
540
|
+
const iconName = arrayContent.slice(k + 1, strEnd);
|
|
541
|
+
if (iconNamePattern.test(iconName)) {
|
|
542
|
+
addUsage(usages, { family: defaultFamily, name: iconName });
|
|
543
|
+
}
|
|
544
|
+
k = strEnd + 1;
|
|
545
|
+
continue;
|
|
546
|
+
} else {
|
|
547
|
+
k = skipStringForward(arrayContent, k, c);
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (c === "{") braceDepth++;
|
|
552
|
+
else if (c === "}") braceDepth--;
|
|
553
|
+
k++;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
345
557
|
}
|
|
346
558
|
function parseStaticAttributes(source) {
|
|
347
559
|
const attrs = {};
|
|
@@ -353,7 +565,7 @@ function parseStaticAttributes(source) {
|
|
|
353
565
|
return attrs;
|
|
354
566
|
}
|
|
355
567
|
function unwrapStaticValue(value) {
|
|
356
|
-
return value.replace(/^['"](.+)['"]$/, "$1");
|
|
568
|
+
return value.replace(/^['"`](.+)['"`]$/, "$1");
|
|
357
569
|
}
|
|
358
570
|
const VALID_WEIGHTS = [100, 200, 300, 400, 500, 600, 700];
|
|
359
571
|
function addUsage(usages, icon) {
|
|
@@ -387,8 +599,8 @@ function toFamily(value) {
|
|
|
387
599
|
const normalized = value ? unwrapStaticValue(value) : "";
|
|
388
600
|
return families.includes(normalized) ? normalized : void 0;
|
|
389
601
|
}
|
|
390
|
-
function resolveNames(rawName) {
|
|
391
|
-
const stringLiterals = /["']([a-z0-9_]+)["']/g;
|
|
602
|
+
function resolveNames(rawName, isDynamic = false) {
|
|
603
|
+
const stringLiterals = /["'`]([a-z0-9_]+)["'`]/g;
|
|
392
604
|
let strMatch;
|
|
393
605
|
const names = [];
|
|
394
606
|
while (strMatch = stringLiterals.exec(rawName)) {
|
|
@@ -398,6 +610,7 @@ function resolveNames(rawName) {
|
|
|
398
610
|
}
|
|
399
611
|
}
|
|
400
612
|
if (names.length === 0) {
|
|
613
|
+
if (isDynamic) return names;
|
|
401
614
|
const name = unwrapStaticValue(rawName);
|
|
402
615
|
if (iconNamePattern.test(name)) {
|
|
403
616
|
names.push(name);
|
|
@@ -408,7 +621,7 @@ function resolveNames(rawName) {
|
|
|
408
621
|
function resolveFamilies(rawFamily, hasFamily = true) {
|
|
409
622
|
const family = toFamily(rawFamily);
|
|
410
623
|
if (family) return [family];
|
|
411
|
-
const stringLiterals = /["'](\w+)["']/g;
|
|
624
|
+
const stringLiterals = /["'`](\w+)["'`]/g;
|
|
412
625
|
let strMatch;
|
|
413
626
|
const found = /* @__PURE__ */ new Set();
|
|
414
627
|
while (strMatch = stringLiterals.exec(rawFamily)) {
|
package/dist/vite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.js","sources":["../src/vite.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, readdir, writeFile } from \"node:fs/promises\";\nimport { extname, join, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { HtmlTagDescriptor, IndexHtmlTransformResult, Plugin, ResolvedConfig } from \"vite\";\n\nimport type { RsIconFamily } from \"./components/RsIcon\";\n\ntype IconInput = string | { family?: RsIconFamily; name: string; weight?: number };\nexport type RsIconFontDisplay = \"block\" | \"swap\" | \"fallback\" | \"optional\";\n\nexport type RobosophyMaterialSymbolsOptions = {\n outputDir?: string;\n fontDisplay?: RsIconFontDisplay;\n fontVariant?: string;\n include?: IconInput[];\n preload?: boolean;\n roots?: string[];\n scan?: boolean;\n};\n\ntype IconUsageMap = Map<RsIconFamily, Map<string, Set<number>>>;\n\nconst families = [\"outlined\", \"rounded\", \"sharp\"] as const satisfies readonly RsIconFamily[];\nconst defaultFamily: RsIconFamily = \"outlined\";\nconst iconNamePattern = /^[a-z0-9_]+$/;\nconst sourceExtensions = new Set([\".js\", \".jsx\", \".mjs\", \".ts\", \".tsx\", \".vue\"]);\nconst skippedDirs = new Set([\".git\", \"dist\", \"node_modules\", \"storybook-static\"]);\nconst internalIconNames = [\n \"arrow_drop_down\",\n \"arrow_drop_up\",\n \"arrow_left\",\n \"chevron_right\",\n \"close\",\n \"login\",\n \"nest_eco_leaf\",\n];\n\nconst GITHUB_RAW =\n \"https://raw.githubusercontent.com/google/material-design-icons/master/symbols/web\";\nconst CACHE_BASE = join(\"node_modules\", \".cache\", \"robosophy-svgs\");\nconst BASE_OPSZ = 24;\nconst DEFAULT_WEIGHT = 400;\n\nconst GRADE_VARIANTS = [0, -25, 200] as const;\n\nexport function robosophyMaterialSymbols(\n options: RobosophyMaterialSymbolsOptions = {},\n): Plugin {\n const raw = (options.outputDir ?? \"\").trim().replace(/^\\/+/, \"\");\n const outputDir = raw\n .split(\"/\")\n .filter((part) => part && part !== \"..\")\n .join(\"/\") || \"assets\";\n let config: ResolvedConfig;\n let cacheDir: string;\n let cachedAssets: GeneratedMaterialSymbolsAssets | null = null;\n let cachedUsagesKey = \"\";\n let needsRegeneration = true;\n\n function usageMapKey(usages: IconUsageMap): string {\n const parts: string[] = [];\n for (const family of families) {\n const iconWeights = usages.get(family);\n if (!iconWeights || iconWeights.size === 0) continue;\n const entries = [...iconWeights.entries()]\n .map(([name, weights]) => `${name}[${[...weights].sort().join(\",\")}]`)\n .sort();\n parts.push(`${family}:${entries.join(\",\")}`);\n }\n return parts.join(\"|\") || \"(empty)\";\n }\n\n async function getAssets(): Promise<GeneratedMaterialSymbolsAssets> {\n if (!needsRegeneration && cachedAssets) return cachedAssets;\n\n const usages = await collectMaterialSymbolUsages(config, options);\n const newKey = usageMapKey(usages);\n\n if (newKey === cachedUsagesKey && cachedAssets) {\n needsRegeneration = false;\n return cachedAssets;\n }\n\n if (config.command !== \"build\" && cachedUsagesKey !== \"\") {\n config.logger.info(\n \"[robosophy-material-symbols] icon usage changed, regenerating SVG sprite\",\n );\n }\n\n cachedUsagesKey = newKey;\n cachedAssets = await ensureGeneratedAssets(config, options, cacheDir);\n needsRegeneration = false;\n return cachedAssets;\n }\n\n return {\n name: \"robosophy-material-symbols\",\n async configResolved(resolvedConfig) {\n config = resolvedConfig;\n cacheDir = join(config.root, CACHE_BASE);\n await mkdir(cacheDir, { recursive: true });\n },\n async buildStart() {\n if (config.command === \"serve\" || config.command === \"build\") {\n await getAssets();\n }\n },\n async transformIndexHtml(): Promise<IndexHtmlTransformResult> {\n const assets = await getAssets();\n\n const tag: HtmlTagDescriptor = {\n tag: \"svg\",\n attrs: {\n xmlns: \"http://www.w3.org/2000/svg\",\n style: \"display:none;\",\n \"aria-hidden\": \"true\",\n },\n children: assets.spriteContent,\n injectTo: \"body-prepend\",\n };\n\n return [tag, ...buildMaterialSymbolsCss(assets.css)];\n },\n configureServer(server) {\n server.watcher.on(\"all\", (_event, file) => {\n if (sourceExtensions.has(extname(file))) {\n needsRegeneration = true;\n }\n });\n server.middlewares.use(async (request, response, next) => {\n if (!request.url?.startsWith(\"/@robosophy-material-symbols/\")) {\n next();\n return;\n }\n\n try {\n const assets = await getAssets();\n const fileName = decodeURIComponent(\n request.url.replace(\"/@robosophy-material-symbols/\", \"\").split(\"?\")[0] ?? \"\",\n );\n\n if (fileName === \"rs-material-symbols.svg\") {\n response.setHeader(\"Content-Type\", \"image/svg+xml; charset=utf-8\");\n response.setHeader(\"Cache-Control\", \"no-cache\");\n response.end(assets.fullSprite);\n return;\n }\n\n next();\n } catch (error) {\n next(error);\n }\n });\n },\n async generateBundle(_outputOptions) {\n const assets = await getAssets();\n\n if (assets.fullSprite) {\n this.emitFile({\n fileName: `${outputDir}/rs-material-symbols.svg`,\n name: \"rs-material-symbols.svg\",\n source: assets.fullSprite,\n type: \"asset\",\n });\n }\n },\n };\n}\n\ntype GeneratedMaterialSymbolsAssets = {\n fullSprite: string;\n spriteContent: string;\n css: string;\n};\n\ntype SvgVariant = {\n suffix: string;\n symbolSuffix: string;\n};\n\nfunction buildSvgVariants(weight: number = DEFAULT_WEIGHT): SvgVariant[] {\n const variants: SvgVariant[] = [];\n for (const grade of GRADE_VARIANTS) {\n const gradeSuffix = grade === 0 ? \"\" : grade > 0 ? `grad${grade}` : `gradN${Math.abs(grade)}`;\n const symbolGradeSuffix = grade === 0 ? \"\" : `-${gradeSuffix}`;\n\n for (const fill of [false, true]) {\n const fillSuffix = fill ? \"fill1\" : \"\";\n const symbolFillSuffix = fill ? \"-fill\" : \"\";\n const weightSuffix = weight !== DEFAULT_WEIGHT ? `wght${weight}` : \"\";\n const weightSymbolSuffix = weight !== DEFAULT_WEIGHT ? `-wght${weight}` : \"\";\n const parts = [gradeSuffix, fillSuffix, weightSuffix].filter(Boolean);\n\n variants.push({\n suffix: parts.length > 0 ? `${parts.join(\"\")}_${BASE_OPSZ}px` : `${BASE_OPSZ}px`,\n symbolSuffix: `${symbolGradeSuffix}${symbolFillSuffix}${weightSymbolSuffix}`,\n });\n }\n }\n return variants;\n}\n\nfunction buildSvgUrl(name: string, family: RsIconFamily, suffix: string): string {\n const style = family === \"outlined\" ? \"materialsymbolsoutlined\"\n : family === \"rounded\" ? \"materialsymbolsrounded\"\n : \"materialsymbolssharp\";\n return `${GITHUB_RAW}/${name.toLowerCase()}/${style}/${name.toLowerCase()}_${suffix}.svg`;\n}\n\nfunction buildSymbolId(name: string, family: RsIconFamily, symbolSuffix: string): string {\n return `rs-icon-${family}-${name}${symbolSuffix}`;\n}\n\nasync function fetchSvg(url: string): Promise<string | null> {\n try {\n const response = await fetch(url);\n if (!response.ok) return null;\n return await response.text();\n } catch {\n return null;\n }\n}\n\nasync function loadSvgFromCache(cacheDir: string, cacheKey: string): Promise<string | null> {\n const cachePath = join(cacheDir, `${cacheKey}.svg`);\n try {\n return await readFile(cachePath, \"utf8\");\n } catch {\n return null;\n }\n}\n\nasync function saveSvgToCache(cacheDir: string, cacheKey: string, content: string): Promise<void> {\n const cachePath = join(cacheDir, `${cacheKey}.svg`);\n try {\n await writeFile(cachePath, content, \"utf8\");\n } catch {\n // Ignore cache write errors\n }\n}\n\nfunction parseSvgPaths(svgContent: string): string | null {\n const viewBoxMatch = svgContent.match(/viewBox=\"([^\"]+)\"/i);\n if (!viewBoxMatch) return null;\n\n const paths: string[] = [];\n const pathRegex = /<path\\s+([^>]*)>/g;\n let pathMatch: RegExpExecArray | null;\n\n while ((pathMatch = pathRegex.exec(svgContent))) {\n const attrs = pathMatch[1] ?? \"\";\n const dMatch = attrs.match(/d=\"([^\"]*)\"/);\n if (dMatch) {\n paths.push(dMatch[1] ?? \"\");\n }\n }\n\n if (paths.length === 0) return null;\n\n return paths\n .map((d) => `<path fill=\"currentColor\" d=\"${d}\"/>`)\n .join(\"\");\n}\n\nasync function generateSpriteAssets(\n usages: IconUsageMap,\n cacheDir: string,\n config: ResolvedConfig,\n): Promise<GeneratedMaterialSymbolsAssets> {\n const symbols: string[] = [];\n const iconClasses: string[] = [];\n let totalFetched = 0;\n let totalBytes = 0;\n\n for (const family of families) {\n const iconWeights = usages.get(family);\n if (!iconWeights || iconWeights.size === 0) continue;\n\n for (const [name, weights] of [...iconWeights.entries()].sort()) {\n for (const weight of [...weights].sort()) {\n const variants = buildSvgVariants(weight);\n for (const variant of variants) {\n const symbolId = buildSymbolId(name, family, variant.symbolSuffix);\n const url = buildSvgUrl(name, family, variant.suffix);\n const cacheKey = createHash(\"sha256\")\n .update(url)\n .digest(\"hex\")\n .slice(0, 12);\n\n let svgContent = cacheDir ? await loadSvgFromCache(cacheDir, cacheKey) : null;\n\n if (!svgContent) {\n svgContent = await fetchSvg(url);\n if (!svgContent) continue;\n\n if (cacheDir) {\n await saveSvgToCache(cacheDir, cacheKey, svgContent);\n }\n }\n\n const pathContent = parseSvgPaths(svgContent);\n if (!pathContent) continue;\n\n symbols.push(\n `<symbol id=\"${symbolId}\" viewBox=\"0 -960 960 960\" fill=\"currentColor\">${pathContent}</symbol>`,\n );\n\n totalFetched++;\n totalBytes += svgContent.length;\n }\n }\n }\n }\n\n const iconNames = new Set<string>();\n for (const family of families) {\n const iconWeights = usages.get(family);\n if (iconWeights) {\n for (const name of iconWeights.keys()) {\n iconNames.add(name);\n }\n }\n }\n for (const name of [...iconNames].sort()) {\n iconClasses.push(`.rs-icon-${name} { --rs-icon-content: \"${name}\"; }`);\n }\n\n const spriteContent = `<defs>${symbols.join(\"\")}</defs>`;\n const fullSprite = `<svg xmlns=\"http://www.w3.org/2000/svg\" style=\"display:none\" aria-hidden=\"true\">${spriteContent}</svg>`;\n const css = iconClasses.join(\"\\n\");\n\n const totalIcons = [...usages.values()].reduce((sum, m) => sum + m.size, 0);\n if (totalIcons > 0) {\n const summary = formatDetectedMaterialSymbols(usages);\n config.logger.info(\n `[robosophy-material-symbols] ${totalIcons} icons detected, ${totalFetched} SVG variants (${(totalBytes / 1024).toFixed(1)} KB)\\n${summary}`,\n );\n } else {\n config.logger.warn(\n \"[robosophy-material-symbols] No Material Symbols icons detected in source. Add them via the include option.\",\n );\n }\n\n return { fullSprite, spriteContent, css };\n}\n\nasync function ensureGeneratedAssets(\n config: ResolvedConfig,\n options: RobosophyMaterialSymbolsOptions,\n cacheDir: string,\n): Promise<GeneratedMaterialSymbolsAssets> {\n const usages = await collectMaterialSymbolUsages(config, options);\n return generateSpriteAssets(usages, cacheDir, config);\n}\n\nexport async function collectMaterialSymbolUsages(\n config: Pick<ResolvedConfig, \"root\">,\n options: RobosophyMaterialSymbolsOptions = {},\n): Promise<IconUsageMap> {\n const usages = createUsageMap();\n\n const manifest = await loadIconManifest();\n\n if (manifest) {\n for (const family of families) {\n for (const name of manifest[family] ?? []) {\n addUsage(usages, { family, name });\n }\n }\n } else {\n for (const name of internalIconNames) {\n for (const family of families) {\n addUsage(usages, { family, name });\n }\n }\n }\n\n for (const icon of options.include ?? []) {\n const resolved = typeof icon === \"string\" ? { family: defaultFamily, name: icon } : icon;\n if (resolved.weight !== undefined) {\n addUsage(usages, resolved);\n } else {\n for (const w of VALID_WEIGHTS) {\n addUsage(usages, { ...resolved, weight: w });\n }\n }\n }\n\n if (options.scan !== false) {\n const roots = options.roots ?? [join(config.root, \"src\")];\n\n for (const root of roots) {\n for (const filePath of await listSourceFiles(root)) {\n const source = await readFile(filePath, \"utf8\");\n collectFromSource(source, usages);\n }\n }\n }\n\n return usages;\n}\n\nexport function buildMaterialSymbolsCss(\n iconClasses: string,\n): HtmlTagDescriptor[] {\n if (!iconClasses) return [];\n\n return [\n {\n tag: \"style\",\n children: iconClasses,\n injectTo: \"head\",\n },\n ];\n}\n\nfunction collectFromSource(source: string, usages: IconUsageMap): void {\n const rsIconTagPattern = /<RsIcon\\b([^>]*)>/g;\n let tagMatch: RegExpExecArray | null;\n\n while ((tagMatch = rsIconTagPattern.exec(source))) {\n const attrs = parseStaticAttributes(tagMatch[1] ?? \"\");\n const rawName = attrs.name ?? attrs[\":name\"];\n const hasFamily = \"family\" in attrs || \":family\" in attrs;\n const rawFamily = attrs.family ?? attrs[\":family\"] ?? \"\";\n const rawWeight = attrs.weight ?? attrs[\":weight\"] ?? \"\";\n const weight = rawWeight ? parseInt(rawWeight, 10) : DEFAULT_WEIGHT;\n const resolvedWeight = !isNaN(weight) && weight > 0 ? weight : DEFAULT_WEIGHT;\n\n if (rawName) {\n const familyList = resolveFamilies(rawFamily, hasFamily);\n const names = resolveNames(rawName);\n\n for (const family of familyList) {\n for (const name of names) {\n addUsage(usages, { family, name, weight: resolvedWeight });\n }\n }\n }\n }\n\n const primeHelperPattern =\n /\\brsPrimeIcon\\(\\s*[\"']([a-z0-9_]+)[\"'](?:\\s*,\\s*\\{([^)]*)\\})?/g;\n let helperMatch: RegExpExecArray | null;\n\n while ((helperMatch = primeHelperPattern.exec(source))) {\n const family = toFamily(helperMatch[2]?.match(/\\bfamily\\s*:\\s*[\"'](\\w+)[\"']/)?.[1]);\n addUsage(usages, {\n family: family ?? defaultFamily,\n name: helperMatch[1] ?? \"\",\n });\n }\n\n const objectIconPattern = /\\bicon\\s*:\\s*[\"']([a-z0-9_]+)[\"']/g;\n let objectMatch: RegExpExecArray | null;\n\n while ((objectMatch = objectIconPattern.exec(source))) {\n addUsage(usages, {\n family: defaultFamily,\n name: objectMatch[1] ?? \"\",\n });\n }\n}\n\nfunction parseStaticAttributes(source: string): Record<string, string> {\n const attrs: Record<string, string> = {};\n const attrPattern = /([:@\\w-]+)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/g;\n let match: RegExpExecArray | null;\n\n while ((match = attrPattern.exec(source))) {\n attrs[match[1] ?? \"\"] = match[2] ?? match[3] ?? \"\";\n }\n\n return attrs;\n}\n\nfunction unwrapStaticValue(value: string): string {\n return value.replace(/^['\"](.+)['\"]$/, \"$1\");\n}\n\nconst VALID_WEIGHTS = [100, 200, 300, 400, 500, 600, 700] as const;\n\nfunction addUsage(\n usages: IconUsageMap,\n icon: { family?: RsIconFamily; name: string; weight?: number },\n): void {\n const family = icon.family ?? defaultFamily;\n const name = icon.name.trim();\n const weight = icon.weight ?? DEFAULT_WEIGHT;\n\n if (!families.includes(family) || !iconNamePattern.test(name)) {\n return;\n }\n\n if (!VALID_WEIGHTS.includes(weight as never)) return;\n\n const iconMap = usages.get(family);\n if (!iconMap) return;\n\n const existing = iconMap.get(name);\n if (existing) {\n existing.add(weight);\n } else {\n iconMap.set(name, new Set([weight]));\n }\n}\n\nasync function loadIconManifest(): Promise<Record<RsIconFamily, string[]> | null> {\n try {\n const dir = join(fileURLToPath(import.meta.url), \"..\");\n const manifestPath = join(dir, \"icon-manifest.json\");\n const source = await readFile(manifestPath, \"utf8\");\n return JSON.parse(source) as Record<RsIconFamily, string[]>;\n } catch {\n return null;\n }\n}\n\nfunction toFamily(value?: string): RsIconFamily | undefined {\n const normalized = value ? unwrapStaticValue(value) : \"\";\n return families.includes(normalized as RsIconFamily)\n ? (normalized as RsIconFamily)\n : undefined;\n}\n\nfunction resolveNames(rawName: string): string[] {\n const stringLiterals = /[\"']([a-z0-9_]+)[\"']/g;\n let strMatch: RegExpExecArray | null;\n const names: string[] = [];\n\n while ((strMatch = stringLiterals.exec(rawName))) {\n const candidate = strMatch[1] ?? \"\";\n if (iconNamePattern.test(candidate)) {\n names.push(candidate);\n }\n }\n\n if (names.length === 0) {\n const name = unwrapStaticValue(rawName);\n if (iconNamePattern.test(name)) {\n names.push(name);\n }\n }\n\n return names;\n}\n\nfunction resolveFamilies(rawFamily: string, hasFamily = true): RsIconFamily[] {\n const family = toFamily(rawFamily);\n if (family) return [family];\n\n const stringLiterals = /[\"'](\\w+)[\"']/g;\n let strMatch: RegExpExecArray | null;\n const found = new Set<RsIconFamily>();\n\n while ((strMatch = stringLiterals.exec(rawFamily))) {\n const candidate = strMatch[1] ?? \"\";\n const f = toFamily(candidate);\n if (f) {\n found.add(f);\n }\n }\n\n if (found.size > 0) return [...found];\n\n if (!hasFamily) return [defaultFamily];\n\n return [...families];\n}\n\nfunction createUsageMap(): IconUsageMap {\n return new Map(families.map((family) => [family, new Map<string, Set<number>>()]));\n}\n\nasync function listSourceFiles(root: string): Promise<string[]> {\n try {\n const entries = await readdir(root, { withFileTypes: true });\n const files = await Promise.all(\n entries.map(async (entry) => {\n const path = join(root, entry.name);\n\n if (entry.isDirectory()) {\n return skippedDirs.has(entry.name) ? [] : listSourceFiles(path);\n }\n\n return sourceExtensions.has(extname(entry.name)) ? [path] : [];\n }),\n );\n\n return files.flat();\n } catch {\n return [];\n }\n}\n\nfunction withBase(base: string, path: string): string {\n const cleanBase = base.endsWith(\"/\") ? base : `${base}/`;\n return `${cleanBase}${path}`.replaceAll(`${sep}`, \"/\");\n}\n\nexport function formatDetectedMaterialSymbols(usages: IconUsageMap): string {\n return families\n .map((family) => {\n const iconWeights = usages.get(family);\n if (!iconWeights || iconWeights.size === 0) return `${family}: (none)`;\n const entries = [...iconWeights.entries()]\n .map(([name, weights]) => `${name}[${[...weights].sort().join(\",\")}]`)\n .sort()\n .join(\", \");\n return `${family}: ${entries}`;\n })\n .join(\"\\n\");\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vite.js","sources":["../src/vite.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, readdir, writeFile } from \"node:fs/promises\";\nimport { extname, join, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { HtmlTagDescriptor, IndexHtmlTransformResult, Plugin, ResolvedConfig } from \"vite\";\n\nimport type { RsIconFamily } from \"./components/RsIcon\";\n\ntype IconInput = string | { family?: RsIconFamily; name: string; weight?: number };\nexport type RsIconFontDisplay = \"block\" | \"swap\" | \"fallback\" | \"optional\";\n\nexport type RobosophyMaterialSymbolsOptions = {\n outputDir?: string;\n fontDisplay?: RsIconFontDisplay;\n fontVariant?: string;\n include?: IconInput[];\n preload?: boolean;\n roots?: string[];\n scan?: boolean;\n};\n\ntype IconUsageMap = Map<RsIconFamily, Map<string, Set<number>>>;\n\nconst families = [\"outlined\", \"rounded\", \"sharp\"] as const satisfies readonly RsIconFamily[];\nconst defaultFamily: RsIconFamily = \"outlined\";\nconst iconNamePattern = /^[a-z0-9_]+$/;\nconst sourceExtensions = new Set([\".js\", \".jsx\", \".mjs\", \".ts\", \".tsx\", \".vue\"]);\nconst skippedDirs = new Set([\".git\", \"dist\", \"node_modules\", \"storybook-static\"]);\nconst internalIconNames = [\n \"arrow_drop_down\",\n \"arrow_drop_up\",\n \"arrow_left\",\n \"chevron_right\",\n \"close\",\n \"login\",\n \"nest_eco_leaf\",\n];\n\nconst GITHUB_RAW =\n \"https://raw.githubusercontent.com/google/material-design-icons/master/symbols/web\";\nconst CACHE_BASE = join(\"node_modules\", \".cache\", \"robosophy-svgs\");\nconst BASE_OPSZ = 24;\nconst DEFAULT_WEIGHT = 400;\n\nconst GRADE_VARIANTS = [0, -25, 200] as const;\n\nexport function robosophyMaterialSymbols(\n options: RobosophyMaterialSymbolsOptions = {},\n): Plugin {\n const raw = (options.outputDir ?? \"\").trim().replace(/^\\/+/, \"\");\n const outputDir = raw\n .split(\"/\")\n .filter((part) => part && part !== \"..\")\n .join(\"/\") || \"assets\";\n let config: ResolvedConfig;\n let cacheDir: string;\n let cachedAssets: GeneratedMaterialSymbolsAssets | null = null;\n let cachedUsagesKey = \"\";\n let needsRegeneration = true;\n\n function usageMapKey(usages: IconUsageMap): string {\n const parts: string[] = [];\n for (const family of families) {\n const iconWeights = usages.get(family);\n if (!iconWeights || iconWeights.size === 0) continue;\n const entries = [...iconWeights.entries()]\n .map(([name, weights]) => `${name}[${[...weights].sort().join(\",\")}]`)\n .sort();\n parts.push(`${family}:${entries.join(\",\")}`);\n }\n return parts.join(\"|\") || \"(empty)\";\n }\n\n async function getAssets(): Promise<GeneratedMaterialSymbolsAssets> {\n if (!needsRegeneration && cachedAssets) return cachedAssets;\n\n const usages = await collectMaterialSymbolUsages(config, options);\n const newKey = usageMapKey(usages);\n\n if (newKey === cachedUsagesKey && cachedAssets) {\n needsRegeneration = false;\n return cachedAssets;\n }\n\n if (config.command !== \"build\" && cachedUsagesKey !== \"\") {\n config.logger.info(\n \"[robosophy-material-symbols] icon usage changed, regenerating SVG sprite\",\n );\n }\n\n cachedUsagesKey = newKey;\n cachedAssets = await ensureGeneratedAssets(config, options, cacheDir);\n needsRegeneration = false;\n return cachedAssets;\n }\n\n return {\n name: \"robosophy-material-symbols\",\n async configResolved(resolvedConfig) {\n config = resolvedConfig;\n cacheDir = join(config.root, CACHE_BASE);\n await mkdir(cacheDir, { recursive: true });\n },\n async buildStart() {\n if (config.command === \"serve\" || config.command === \"build\") {\n await getAssets();\n }\n },\n async transformIndexHtml(): Promise<IndexHtmlTransformResult> {\n const assets = await getAssets();\n\n const tag: HtmlTagDescriptor = {\n tag: \"svg\",\n attrs: {\n xmlns: \"http://www.w3.org/2000/svg\",\n style: \"display:none;\",\n \"aria-hidden\": \"true\",\n },\n children: assets.spriteContent,\n injectTo: \"body-prepend\",\n };\n\n return [tag, ...buildMaterialSymbolsCss(assets.css)];\n },\n configureServer(server) {\n server.watcher.on(\"all\", (_event, file) => {\n if (sourceExtensions.has(extname(file))) {\n needsRegeneration = true;\n }\n });\n server.middlewares.use(async (request, response, next) => {\n if (!request.url?.startsWith(\"/@robosophy-material-symbols/\")) {\n next();\n return;\n }\n\n try {\n const assets = await getAssets();\n const fileName = decodeURIComponent(\n request.url.replace(\"/@robosophy-material-symbols/\", \"\").split(\"?\")[0] ?? \"\",\n );\n\n if (fileName === \"rs-material-symbols.svg\") {\n response.setHeader(\"Content-Type\", \"image/svg+xml; charset=utf-8\");\n response.setHeader(\"Cache-Control\", \"no-cache\");\n response.end(assets.fullSprite);\n return;\n }\n\n next();\n } catch (error) {\n next(error);\n }\n });\n },\n async generateBundle(_outputOptions) {\n const assets = await getAssets();\n\n if (assets.fullSprite) {\n this.emitFile({\n fileName: `${outputDir}/rs-material-symbols.svg`,\n name: \"rs-material-symbols.svg\",\n source: assets.fullSprite,\n type: \"asset\",\n });\n }\n },\n };\n}\n\ntype GeneratedMaterialSymbolsAssets = {\n fullSprite: string;\n spriteContent: string;\n css: string;\n};\n\ntype SvgVariant = {\n suffix: string;\n symbolSuffix: string;\n};\n\nfunction buildSvgVariants(weight: number = DEFAULT_WEIGHT): SvgVariant[] {\n const variants: SvgVariant[] = [];\n for (const grade of GRADE_VARIANTS) {\n const gradeSuffix = grade === 0 ? \"\" : grade > 0 ? `grad${grade}` : `gradN${Math.abs(grade)}`;\n const symbolGradeSuffix = grade === 0 ? \"\" : `-${gradeSuffix}`;\n\n for (const fill of [false, true]) {\n const fillSuffix = fill ? \"fill1\" : \"\";\n const symbolFillSuffix = fill ? \"-fill\" : \"\";\n const weightSuffix = weight !== DEFAULT_WEIGHT ? `wght${weight}` : \"\";\n const weightSymbolSuffix = weight !== DEFAULT_WEIGHT ? `-wght${weight}` : \"\";\n const parts = [gradeSuffix, fillSuffix, weightSuffix].filter(Boolean);\n\n variants.push({\n suffix: parts.length > 0 ? `${parts.join(\"\")}_${BASE_OPSZ}px` : `${BASE_OPSZ}px`,\n symbolSuffix: `${symbolGradeSuffix}${symbolFillSuffix}${weightSymbolSuffix}`,\n });\n }\n }\n return variants;\n}\n\nfunction buildSvgUrl(name: string, family: RsIconFamily, suffix: string): string {\n const style = family === \"outlined\" ? \"materialsymbolsoutlined\"\n : family === \"rounded\" ? \"materialsymbolsrounded\"\n : \"materialsymbolssharp\";\n return `${GITHUB_RAW}/${name.toLowerCase()}/${style}/${name.toLowerCase()}_${suffix}.svg`;\n}\n\nfunction buildSymbolId(name: string, family: RsIconFamily, symbolSuffix: string): string {\n return `rs-icon-${family}-${name}${symbolSuffix}`;\n}\n\nasync function fetchSvg(url: string): Promise<string | null> {\n try {\n const response = await fetch(url);\n if (!response.ok) return null;\n return await response.text();\n } catch {\n return null;\n }\n}\n\nasync function loadSvgFromCache(cacheDir: string, cacheKey: string): Promise<string | null> {\n const cachePath = join(cacheDir, `${cacheKey}.svg`);\n try {\n return await readFile(cachePath, \"utf8\");\n } catch {\n return null;\n }\n}\n\nasync function saveSvgToCache(cacheDir: string, cacheKey: string, content: string): Promise<void> {\n const cachePath = join(cacheDir, `${cacheKey}.svg`);\n try {\n await writeFile(cachePath, content, \"utf8\");\n } catch {\n // Ignore cache write errors\n }\n}\n\nfunction parseSvgPaths(svgContent: string): string | null {\n const viewBoxMatch = svgContent.match(/viewBox=\"([^\"]+)\"/i);\n if (!viewBoxMatch) return null;\n\n const paths: string[] = [];\n const pathRegex = /<path\\s+([^>]*)>/g;\n let pathMatch: RegExpExecArray | null;\n\n while ((pathMatch = pathRegex.exec(svgContent))) {\n const attrs = pathMatch[1] ?? \"\";\n const dMatch = attrs.match(/d=\"([^\"]*)\"/);\n if (dMatch) {\n paths.push(dMatch[1] ?? \"\");\n }\n }\n\n if (paths.length === 0) return null;\n\n return paths\n .map((d) => `<path fill=\"currentColor\" d=\"${d}\"/>`)\n .join(\"\");\n}\n\nasync function generateSpriteAssets(\n usages: IconUsageMap,\n cacheDir: string,\n config: ResolvedConfig,\n): Promise<GeneratedMaterialSymbolsAssets> {\n const symbols: string[] = [];\n const iconClasses: string[] = [];\n let totalFetched = 0;\n let totalBytes = 0;\n\n for (const family of families) {\n const iconWeights = usages.get(family);\n if (!iconWeights || iconWeights.size === 0) continue;\n\n for (const [name, weights] of [...iconWeights.entries()].sort()) {\n for (const weight of [...weights].sort()) {\n const variants = buildSvgVariants(weight);\n for (const variant of variants) {\n const symbolId = buildSymbolId(name, family, variant.symbolSuffix);\n const url = buildSvgUrl(name, family, variant.suffix);\n const cacheKey = createHash(\"sha256\")\n .update(url)\n .digest(\"hex\")\n .slice(0, 12);\n\n let svgContent = cacheDir ? await loadSvgFromCache(cacheDir, cacheKey) : null;\n\n if (!svgContent) {\n svgContent = await fetchSvg(url);\n if (!svgContent) continue;\n\n if (cacheDir) {\n await saveSvgToCache(cacheDir, cacheKey, svgContent);\n }\n }\n\n const pathContent = parseSvgPaths(svgContent);\n if (!pathContent) continue;\n\n symbols.push(\n `<symbol id=\"${symbolId}\" viewBox=\"0 -960 960 960\" fill=\"currentColor\">${pathContent}</symbol>`,\n );\n\n totalFetched++;\n totalBytes += svgContent.length;\n }\n }\n }\n }\n\n const iconNames = new Set<string>();\n for (const family of families) {\n const iconWeights = usages.get(family);\n if (iconWeights) {\n for (const name of iconWeights.keys()) {\n iconNames.add(name);\n }\n }\n }\n for (const name of [...iconNames].sort()) {\n iconClasses.push(`.rs-icon-${name} { --rs-icon-content: \"${name}\"; }`);\n }\n\n const spriteContent = `<defs>${symbols.join(\"\")}</defs>`;\n const fullSprite = `<svg xmlns=\"http://www.w3.org/2000/svg\" style=\"display:none\" aria-hidden=\"true\">${spriteContent}</svg>`;\n const css = iconClasses.join(\"\\n\");\n\n const totalIcons = [...usages.values()].reduce((sum, m) => sum + m.size, 0);\n if (totalIcons > 0) {\n const summary = formatDetectedMaterialSymbols(usages);\n config.logger.info(\n `[robosophy-material-symbols] ${totalIcons} icons detected, ${totalFetched} SVG variants (${(totalBytes / 1024).toFixed(1)} KB)\\n${summary}`,\n );\n } else {\n config.logger.warn(\n \"[robosophy-material-symbols] No Material Symbols icons detected in source. Add them via the include option.\",\n );\n }\n\n return { fullSprite, spriteContent, css };\n}\n\nasync function ensureGeneratedAssets(\n config: ResolvedConfig,\n options: RobosophyMaterialSymbolsOptions,\n cacheDir: string,\n): Promise<GeneratedMaterialSymbolsAssets> {\n const usages = await collectMaterialSymbolUsages(config, options);\n return generateSpriteAssets(usages, cacheDir, config);\n}\n\nexport async function collectMaterialSymbolUsages(\n config: Pick<ResolvedConfig, \"root\">,\n options: RobosophyMaterialSymbolsOptions = {},\n): Promise<IconUsageMap> {\n const usages = createUsageMap();\n\n const manifest = await loadIconManifest();\n\n if (manifest) {\n for (const family of families) {\n for (const name of manifest[family] ?? []) {\n addUsage(usages, { family, name });\n }\n }\n } else {\n for (const name of internalIconNames) {\n for (const family of families) {\n addUsage(usages, { family, name });\n }\n }\n }\n\n for (const icon of options.include ?? []) {\n const resolved = typeof icon === \"string\" ? { family: defaultFamily, name: icon } : icon;\n if (resolved.weight !== undefined) {\n addUsage(usages, resolved);\n } else {\n for (const w of VALID_WEIGHTS) {\n addUsage(usages, { ...resolved, weight: w });\n }\n }\n }\n\n if (options.scan !== false) {\n const roots = options.roots ?? [join(config.root, \"src\")];\n\n for (const root of roots) {\n for (const filePath of await listSourceFiles(root)) {\n const source = await readFile(filePath, \"utf8\");\n collectFromSource(source, usages);\n }\n }\n }\n\n return usages;\n}\n\nexport function buildMaterialSymbolsCss(\n iconClasses: string,\n): HtmlTagDescriptor[] {\n if (!iconClasses) return [];\n\n return [\n {\n tag: \"style\",\n children: iconClasses,\n injectTo: \"head\",\n },\n ];\n}\n\nfunction skipStringBackward(source: string, pos: number, quote: string): number {\n pos--;\n while (pos >= 0) {\n if (source[pos] === \"\\\\\") {\n pos -= 2;\n continue;\n }\n if (source[pos] === quote) {\n let backslashCount = 0;\n let k = pos - 1;\n while (k >= 0 && source[k] === \"\\\\\") {\n backslashCount++;\n k--;\n }\n if (backslashCount % 2 === 1) {\n pos = pos - backslashCount - 1;\n continue;\n }\n return pos - 1;\n }\n pos--;\n }\n return -1;\n}\n\nfunction skipStringForward(source: string, pos: number, quote: string): number {\n pos++;\n while (pos < source.length) {\n if (source[pos] === \"\\\\\") {\n pos += 2;\n continue;\n }\n if (source[pos] === quote) {\n return pos + 1;\n }\n pos++;\n }\n return source.length;\n}\n\nfunction findEnclosingObject(\n source: string,\n fromIndex: number,\n): { start: number; end: number; body: string } | null {\n let depth = 0;\n let i = fromIndex;\n\n while (i >= 0) {\n const char = source[i];\n\n if (char === '\"' || char === \"'\" || char === \"`\") {\n i = skipStringBackward(source, i, char);\n continue;\n }\n\n // Line comment //\n if (char === \"/\" && i > 0 && source[i - 1] === \"/\") {\n i -= 2;\n while (i >= 0 && source[i] !== \"\\n\") i--;\n continue;\n }\n\n // Block comment */\n if (char === \"/\" && i > 0 && source[i - 1] === \"*\") {\n i -= 2;\n while (i >= 0) {\n if (source[i] === \"/\" && i + 1 < source.length && source[i + 1] === \"*\") {\n i--;\n break;\n }\n i--;\n }\n continue;\n }\n\n if (char === \"}\") {\n depth++;\n } else if (char === \"{\") {\n if (depth === 0) {\n const start = i;\n let j = i + 1;\n let innerDepth = 1;\n\n while (j < source.length) {\n const c = source[j];\n\n if (c === '\"' || c === \"'\" || c === \"`\") {\n j = skipStringForward(source, j, c);\n continue;\n }\n\n if (c === \"/\") {\n // Line comment //\n if (j + 1 < source.length && source[j + 1] === \"/\") {\n j += 2;\n while (j < source.length && source[j] !== \"\\n\") j++;\n continue;\n }\n // Block comment /*\n if (j + 1 < source.length && source[j + 1] === \"*\") {\n j += 2;\n while (j + 1 < source.length) {\n if (source[j] === \"*\" && source[j + 1] === \"/\") {\n j += 2;\n break;\n }\n j++;\n }\n continue;\n }\n }\n\n if (c === \"{\") innerDepth++;\n else if (c === \"}\") {\n innerDepth--;\n if (innerDepth === 0) {\n return {\n start,\n end: j,\n body: source.slice(start + 1, j),\n };\n }\n }\n\n j++;\n }\n\n return null;\n }\n depth--;\n }\n\n i--;\n }\n\n return null;\n}\n\nfunction collectFromSource(source: string, usages: IconUsageMap): void {\n const rsIconTagPattern = /<RsIcon\\b([^>]*)>/g;\n let tagMatch: RegExpExecArray | null;\n\n while ((tagMatch = rsIconTagPattern.exec(source))) {\n const attrs = parseStaticAttributes(tagMatch[1] ?? \"\");\n const rawName = attrs.name ?? attrs[\":name\"];\n const hasFamily = \"family\" in attrs || \":family\" in attrs;\n const rawFamily = attrs.family ?? attrs[\":family\"] ?? \"\";\n const rawWeight = attrs.weight ?? attrs[\":weight\"] ?? \"\";\n const weight = rawWeight ? parseInt(rawWeight, 10) : DEFAULT_WEIGHT;\n const resolvedWeight = !isNaN(weight) && weight > 0 ? weight : DEFAULT_WEIGHT;\n\n if (rawName) {\n const isNameDynamic = \":name\" in attrs;\n const familyList = resolveFamilies(rawFamily, hasFamily);\n const names = resolveNames(rawName, isNameDynamic);\n\n for (const family of familyList) {\n for (const name of names) {\n addUsage(usages, { family, name, weight: resolvedWeight });\n }\n }\n }\n }\n\n const primeHelperPattern =\n /\\brsPrimeIcon\\(\\s*[\"']([a-z0-9_]+)[\"'](?:\\s*,\\s*\\{([^)]*)\\})?/g;\n let helperMatch: RegExpExecArray | null;\n\n while ((helperMatch = primeHelperPattern.exec(source))) {\n const family = toFamily(helperMatch[2]?.match(/\\bfamily\\s*:\\s*[\"'](\\w+)[\"']/)?.[1]);\n addUsage(usages, {\n family: family ?? defaultFamily,\n name: helperMatch[1] ?? \"\",\n });\n }\n\n const objectIconPattern = /\\bicon\\s*:\\s*[\"']([a-z0-9_]+)[\"']/g;\n let objectMatch: RegExpExecArray | null;\n\n while ((objectMatch = objectIconPattern.exec(source))) {\n const name = objectMatch[1] ?? \"\";\n const obj = findEnclosingObject(source, objectMatch.index);\n if (obj) {\n const cleanBody = obj.body.replace(/\\/\\/.*/g, \"\").replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n const weightMatch = cleanBody.match(/\\bweight\\s*:\\s*(\\d+)/);\n const familyMatch = cleanBody.match(/\\bfamily\\s*:\\s*[\"'](\\w+)[\"']/);\n addUsage(usages, {\n family: familyMatch ? (familyMatch[1] as RsIconFamily) : defaultFamily,\n name,\n weight: weightMatch ? parseInt(weightMatch[1], 10) : undefined,\n });\n } else {\n addUsage(usages, { family: defaultFamily, name });\n }\n }\n\n // Pattern 4: magic comments @robosophy-icon\n // Use for icons that cannot be detected statically (props, variables, API, etc.)\n const magicCommentPattern = /\\/\\/\\s*@robosophy-icon:\\s*([a-z0-9_]+)/g;\n let magicMatch: RegExpExecArray | null;\n\n while ((magicMatch = magicCommentPattern.exec(source))) {\n const name = magicMatch[1] ?? \"\";\n const lineEnd = source.indexOf(\"\\n\", magicMatch.index);\n const line = lineEnd === -1 ? source.slice(magicMatch.index) : source.slice(magicMatch.index, lineEnd);\n const familyMatch = line.match(/\\bfamily\\s*=\\s*[\"']?(\\w+)[\"']?/);\n const weightMatch = line.match(/\\bweight\\s*=\\s*(\\d+)/);\n addUsage(usages, {\n family: familyMatch ? (familyMatch[1] as RsIconFamily) : defaultFamily,\n name,\n weight: weightMatch ? parseInt(weightMatch[1], 10) : undefined,\n });\n }\n\n // Pattern 5: icon arrays — detect arrays assigned to variables with \"icon\" in the name\n // e.g. const iconNames = [\"home\", \"search\"]\n const iconArrayPattern = /(?:const|let|var)\\s+(\\w*[Ii]con\\w*)\\s*=\\s*\\[/g;\n let arrayMatch: RegExpExecArray | null;\n\n while ((arrayMatch = iconArrayPattern.exec(source))) {\n const bracketStart = arrayMatch.index + arrayMatch[0].length - 1;\n let depth = 1;\n let j = bracketStart + 1;\n\n while (j < source.length) {\n const c = source[j];\n\n if (c === '\"' || c === \"'\" || c === \"`\") {\n j = skipStringForward(source, j, c);\n continue;\n }\n\n if (c === \"/\") {\n if (j + 1 < source.length && source[j + 1] === \"/\") {\n j += 2;\n while (j < source.length && source[j] !== \"\\n\") j++;\n continue;\n }\n if (j + 1 < source.length && source[j + 1] === \"*\") {\n j += 2;\n while (j + 1 < source.length) {\n if (source[j] === \"*\" && source[j + 1] === \"/\") {\n j += 2;\n break;\n }\n j++;\n }\n continue;\n }\n }\n\n if (c === \"[\") depth++;\n else if (c === \"]\") {\n depth--;\n if (depth === 0) break;\n }\n\n j++;\n }\n\n if (depth === 0) {\n const arrayContent = source.slice(bracketStart + 1, j);\n let braceDepth = 0;\n let k = 0;\n\n while (k < arrayContent.length) {\n const c = arrayContent[k];\n\n if (c === '\"' || c === \"'\" || c === \"`\") {\n if (braceDepth === 0) {\n const quote = c;\n let strEnd = k + 1;\n while (strEnd < arrayContent.length) {\n if (arrayContent[strEnd] === \"\\\\\") {\n strEnd += 2;\n continue;\n }\n if (arrayContent[strEnd] === quote) break;\n strEnd++;\n }\n const iconName = arrayContent.slice(k + 1, strEnd);\n if (iconNamePattern.test(iconName)) {\n addUsage(usages, { family: defaultFamily, name: iconName });\n }\n k = strEnd + 1;\n continue;\n } else {\n k = skipStringForward(arrayContent, k, c);\n continue;\n }\n }\n\n if (c === \"{\") braceDepth++;\n else if (c === \"}\") braceDepth--;\n\n k++;\n }\n }\n }\n}\n\nfunction parseStaticAttributes(source: string): Record<string, string> {\n const attrs: Record<string, string> = {};\n const attrPattern = /([:@\\w-]+)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/g;\n let match: RegExpExecArray | null;\n\n while ((match = attrPattern.exec(source))) {\n attrs[match[1] ?? \"\"] = match[2] ?? match[3] ?? \"\";\n }\n\n return attrs;\n}\n\nfunction unwrapStaticValue(value: string): string {\n return value.replace(/^['\"`](.+)['\"`]$/, \"$1\");\n}\n\nconst VALID_WEIGHTS = [100, 200, 300, 400, 500, 600, 700] as const;\n\nfunction addUsage(\n usages: IconUsageMap,\n icon: { family?: RsIconFamily; name: string; weight?: number },\n): void {\n const family = icon.family ?? defaultFamily;\n const name = icon.name.trim();\n const weight = icon.weight ?? DEFAULT_WEIGHT;\n\n if (!families.includes(family) || !iconNamePattern.test(name)) {\n return;\n }\n\n if (!VALID_WEIGHTS.includes(weight as never)) return;\n\n const iconMap = usages.get(family);\n if (!iconMap) return;\n\n const existing = iconMap.get(name);\n if (existing) {\n existing.add(weight);\n } else {\n iconMap.set(name, new Set([weight]));\n }\n}\n\nasync function loadIconManifest(): Promise<Record<RsIconFamily, string[]> | null> {\n try {\n const dir = join(fileURLToPath(import.meta.url), \"..\");\n const manifestPath = join(dir, \"icon-manifest.json\");\n const source = await readFile(manifestPath, \"utf8\");\n return JSON.parse(source) as Record<RsIconFamily, string[]>;\n } catch {\n return null;\n }\n}\n\nfunction toFamily(value?: string): RsIconFamily | undefined {\n const normalized = value ? unwrapStaticValue(value) : \"\";\n return families.includes(normalized as RsIconFamily)\n ? (normalized as RsIconFamily)\n : undefined;\n}\n\nfunction resolveNames(rawName: string, isDynamic = false): string[] {\n const stringLiterals = /[\"'`]([a-z0-9_]+)[\"'`]/g;\n let strMatch: RegExpExecArray | null;\n const names: string[] = [];\n\n while ((strMatch = stringLiterals.exec(rawName))) {\n const candidate = strMatch[1] ?? \"\";\n if (iconNamePattern.test(candidate)) {\n names.push(candidate);\n }\n }\n\n if (names.length === 0) {\n if (isDynamic) return names;\n const name = unwrapStaticValue(rawName);\n if (iconNamePattern.test(name)) {\n names.push(name);\n }\n }\n\n return names;\n}\n\nfunction resolveFamilies(rawFamily: string, hasFamily = true): RsIconFamily[] {\n const family = toFamily(rawFamily);\n if (family) return [family];\n\n const stringLiterals = /[\"'`](\\w+)[\"'`]/g;\n let strMatch: RegExpExecArray | null;\n const found = new Set<RsIconFamily>();\n\n while ((strMatch = stringLiterals.exec(rawFamily))) {\n const candidate = strMatch[1] ?? \"\";\n const f = toFamily(candidate);\n if (f) {\n found.add(f);\n }\n }\n\n if (found.size > 0) return [...found];\n\n if (!hasFamily) return [defaultFamily];\n\n return [...families];\n}\n\nfunction createUsageMap(): IconUsageMap {\n return new Map(families.map((family) => [family, new Map<string, Set<number>>()]));\n}\n\nasync function listSourceFiles(root: string): Promise<string[]> {\n try {\n const entries = await readdir(root, { withFileTypes: true });\n const files = await Promise.all(\n entries.map(async (entry) => {\n const path = join(root, entry.name);\n\n if (entry.isDirectory()) {\n return skippedDirs.has(entry.name) ? [] : listSourceFiles(path);\n }\n\n return sourceExtensions.has(extname(entry.name)) ? [path] : [];\n }),\n );\n\n return files.flat();\n } catch {\n return [];\n }\n}\n\nexport function formatDetectedMaterialSymbols(usages: IconUsageMap): string {\n return families\n .map((family) => {\n const iconWeights = usages.get(family);\n if (!iconWeights || iconWeights.size === 0) return `${family}: (none)`;\n const entries = [...iconWeights.entries()]\n .map(([name, weights]) => `${name}[${[...weights].sort().join(\",\")}]`)\n .sort()\n .join(\", \");\n return `${family}: ${entries}`;\n })\n .join(\"\\n\");\n}\n"],"names":[],"mappings":";;;;AAuBA,MAAM,WAAW,CAAC,YAAY,WAAW,OAAO;AAChD,MAAM,gBAA8B;AACpC,MAAM,kBAAkB;AACxB,MAAM,mBAAmB,oBAAI,IAAI,CAAC,OAAO,QAAQ,QAAQ,OAAO,QAAQ,MAAM,CAAC;AAC/E,MAAM,kCAAkB,IAAI,CAAC,QAAQ,QAAQ,gBAAgB,kBAAkB,CAAC;AAChF,MAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,aACJ;AACF,MAAM,aAAa,KAAK,gBAAgB,UAAU,gBAAgB;AAClE,MAAM,YAAY;AAClB,MAAM,iBAAiB;AAEvB,MAAM,iBAAiB,CAAC,GAAG,KAAK,GAAG;AAE5B,SAAS,yBACd,UAA2C,IACnC;AACR,QAAM,OAAO,QAAQ,aAAa,IAAI,OAAO,QAAQ,QAAQ,EAAE;AAC/D,QAAM,YAAY,IACf,MAAM,GAAG,EACT,OAAO,CAAC,SAAS,QAAQ,SAAS,IAAI,EACtC,KAAK,GAAG,KAAK;AAChB,MAAI;AACJ,MAAI;AACJ,MAAI,eAAsD;AAC1D,MAAI,kBAAkB;AACtB,MAAI,oBAAoB;AAExB,WAAS,YAAY,QAA8B;AACjD,UAAM,QAAkB,CAAA;AACxB,eAAW,UAAU,UAAU;AAC7B,YAAM,cAAc,OAAO,IAAI,MAAM;AACrC,UAAI,CAAC,eAAe,YAAY,SAAS,EAAG;AAC5C,YAAM,UAAU,CAAC,GAAG,YAAY,QAAA,CAAS,EACtC,IAAI,CAAC,CAAC,MAAM,OAAO,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,OAAO,EAAE,KAAA,EAAO,KAAK,GAAG,CAAC,GAAG,EACpE,KAAA;AACH,YAAM,KAAK,GAAG,MAAM,IAAI,QAAQ,KAAK,GAAG,CAAC,EAAE;AAAA,IAC7C;AACA,WAAO,MAAM,KAAK,GAAG,KAAK;AAAA,EAC5B;AAEA,iBAAe,YAAqD;AAClE,QAAI,CAAC,qBAAqB,aAAc,QAAO;AAE/C,UAAM,SAAS,MAAM,4BAA4B,QAAQ,OAAO;AAChE,UAAM,SAAS,YAAY,MAAM;AAEjC,QAAI,WAAW,mBAAmB,cAAc;AAC9C,0BAAoB;AACpB,aAAO;AAAA,IACT;AAEA,QAAI,OAAO,YAAY,WAAW,oBAAoB,IAAI;AACxD,aAAO,OAAO;AAAA,QACZ;AAAA,MAAA;AAAA,IAEJ;AAEA,sBAAkB;AAClB,mBAAe,MAAM,sBAAsB,QAAQ,SAAS,QAAQ;AACpE,wBAAoB;AACpB,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,eAAe,gBAAgB;AACnC,eAAS;AACT,iBAAW,KAAK,OAAO,MAAM,UAAU;AACvC,YAAM,MAAM,UAAU,EAAE,WAAW,MAAM;AAAA,IAC3C;AAAA,IACA,MAAM,aAAa;AACjB,UAAI,OAAO,YAAY,WAAW,OAAO,YAAY,SAAS;AAC5D,cAAM,UAAA;AAAA,MACR;AAAA,IACF;AAAA,IACA,MAAM,qBAAwD;AAC5D,YAAM,SAAS,MAAM,UAAA;AAErB,YAAM,MAAyB;AAAA,QAC7B,KAAK;AAAA,QACL,OAAO;AAAA,UACL,OAAO;AAAA,UACP,OAAO;AAAA,UACP,eAAe;AAAA,QAAA;AAAA,QAEjB,UAAU,OAAO;AAAA,QACjB,UAAU;AAAA,MAAA;AAGZ,aAAO,CAAC,KAAK,GAAG,wBAAwB,OAAO,GAAG,CAAC;AAAA,IACrD;AAAA,IACA,gBAAgB,QAAQ;AACtB,aAAO,QAAQ,GAAG,OAAO,CAAC,QAAQ,SAAS;AACzC,YAAI,iBAAiB,IAAI,QAAQ,IAAI,CAAC,GAAG;AACvC,8BAAoB;AAAA,QACtB;AAAA,MACF,CAAC;AACD,aAAO,YAAY,IAAI,OAAO,SAAS,UAAU,SAAS;AACxD,YAAI,CAAC,QAAQ,KAAK,WAAW,+BAA+B,GAAG;AAC7D,eAAA;AACA;AAAA,QACF;AAEA,YAAI;AACF,gBAAM,SAAS,MAAM,UAAA;AACrB,gBAAM,WAAW;AAAA,YACf,QAAQ,IAAI,QAAQ,iCAAiC,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC,KAAK;AAAA,UAAA;AAG5E,cAAI,aAAa,2BAA2B;AAC1C,qBAAS,UAAU,gBAAgB,8BAA8B;AACjE,qBAAS,UAAU,iBAAiB,UAAU;AAC9C,qBAAS,IAAI,OAAO,UAAU;AAC9B;AAAA,UACF;AAEA,eAAA;AAAA,QACF,SAAS,OAAO;AACd,eAAK,KAAK;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,MAAM,eAAe,gBAAgB;AACnC,YAAM,SAAS,MAAM,UAAA;AAErB,UAAI,OAAO,YAAY;AACrB,aAAK,SAAS;AAAA,UACZ,UAAU,GAAG,SAAS;AAAA,UACtB,MAAM;AAAA,UACN,QAAQ,OAAO;AAAA,UACf,MAAM;AAAA,QAAA,CACP;AAAA,MACH;AAAA,IACF;AAAA,EAAA;AAEJ;AAaA,SAAS,iBAAiB,SAAiB,gBAA8B;AACvE,QAAM,WAAyB,CAAA;AAC/B,aAAW,SAAS,gBAAgB;AAClC,UAAM,cAAc,UAAU,IAAI,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,KAAK,IAAI,KAAK,CAAC;AAC3F,UAAM,oBAAoB,UAAU,IAAI,KAAK,IAAI,WAAW;AAE5D,eAAW,QAAQ,CAAC,OAAO,IAAI,GAAG;AAChC,YAAM,aAAa,OAAO,UAAU;AACpC,YAAM,mBAAmB,OAAO,UAAU;AAC1C,YAAM,eAAe,WAAW,iBAAiB,OAAO,MAAM,KAAK;AACnE,YAAM,qBAAqB,WAAW,iBAAiB,QAAQ,MAAM,KAAK;AAC1E,YAAM,QAAQ,CAAC,aAAa,YAAY,YAAY,EAAE,OAAO,OAAO;AAEpE,eAAS,KAAK;AAAA,QACZ,QAAQ,MAAM,SAAS,IAAI,GAAG,MAAM,KAAK,EAAE,CAAC,IAAI,SAAS,OAAO,GAAG,SAAS;AAAA,QAC5E,cAAc,GAAG,iBAAiB,GAAG,gBAAgB,GAAG,kBAAkB;AAAA,MAAA,CAC3E;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,YAAY,MAAc,QAAsB,QAAwB;AAC/E,QAAM,QAAQ,WAAW,aAAa,4BAClC,WAAW,YAAY,2BACvB;AACJ,SAAO,GAAG,UAAU,IAAI,KAAK,YAAA,CAAa,IAAI,KAAK,IAAI,KAAK,YAAA,CAAa,IAAI,MAAM;AACrF;AAEA,SAAS,cAAc,MAAc,QAAsB,cAA8B;AACvF,SAAO,WAAW,MAAM,IAAI,IAAI,GAAG,YAAY;AACjD;AAEA,eAAe,SAAS,KAAqC;AAC3D,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG;AAChC,QAAI,CAAC,SAAS,GAAI,QAAO;AACzB,WAAO,MAAM,SAAS,KAAA;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,iBAAiB,UAAkB,UAA0C;AAC1F,QAAM,YAAY,KAAK,UAAU,GAAG,QAAQ,MAAM;AAClD,MAAI;AACF,WAAO,MAAM,SAAS,WAAW,MAAM;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,eAAe,UAAkB,UAAkB,SAAgC;AAChG,QAAM,YAAY,KAAK,UAAU,GAAG,QAAQ,MAAM;AAClD,MAAI;AACF,UAAM,UAAU,WAAW,SAAS,MAAM;AAAA,EAC5C,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,cAAc,YAAmC;AACxD,QAAM,eAAe,WAAW,MAAM,oBAAoB;AAC1D,MAAI,CAAC,aAAc,QAAO;AAE1B,QAAM,QAAkB,CAAA;AACxB,QAAM,YAAY;AAClB,MAAI;AAEJ,SAAQ,YAAY,UAAU,KAAK,UAAU,GAAI;AAC/C,UAAM,QAAQ,UAAU,CAAC,KAAK;AAC9B,UAAM,SAAS,MAAM,MAAM,aAAa;AACxC,QAAI,QAAQ;AACV,YAAM,KAAK,OAAO,CAAC,KAAK,EAAE;AAAA,IAC5B;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,SAAO,MACJ,IAAI,CAAC,MAAM,gCAAgC,CAAC,KAAK,EACjD,KAAK,EAAE;AACZ;AAEA,eAAe,qBACb,QACA,UACA,QACyC;AACzC,QAAM,UAAoB,CAAA;AAC1B,QAAM,cAAwB,CAAA;AAC9B,MAAI,eAAe;AACnB,MAAI,aAAa;AAEjB,aAAW,UAAU,UAAU;AAC7B,UAAM,cAAc,OAAO,IAAI,MAAM;AACrC,QAAI,CAAC,eAAe,YAAY,SAAS,EAAG;AAE5C,eAAW,CAAC,MAAM,OAAO,KAAK,CAAC,GAAG,YAAY,QAAA,CAAS,EAAE,QAAQ;AAC/D,iBAAW,UAAU,CAAC,GAAG,OAAO,EAAE,QAAQ;AACxC,cAAM,WAAW,iBAAiB,MAAM;AACxC,mBAAW,WAAW,UAAU;AAC9B,gBAAM,WAAW,cAAc,MAAM,QAAQ,QAAQ,YAAY;AACjE,gBAAM,MAAM,YAAY,MAAM,QAAQ,QAAQ,MAAM;AACpD,gBAAM,WAAW,WAAW,QAAQ,EACjC,OAAO,GAAG,EACV,OAAO,KAAK,EACZ,MAAM,GAAG,EAAE;AAEd,cAAI,aAAa,WAAW,MAAM,iBAAiB,UAAU,QAAQ,IAAI;AAEzE,cAAI,CAAC,YAAY;AACf,yBAAa,MAAM,SAAS,GAAG;AAC/B,gBAAI,CAAC,WAAY;AAEjB,gBAAI,UAAU;AACZ,oBAAM,eAAe,UAAU,UAAU,UAAU;AAAA,YACrD;AAAA,UACF;AAEA,gBAAM,cAAc,cAAc,UAAU;AAC5C,cAAI,CAAC,YAAa;AAElB,kBAAQ;AAAA,YACN,eAAe,QAAQ,kDAAkD,WAAW;AAAA,UAAA;AAGtF;AACA,wBAAc,WAAW;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,gCAAgB,IAAA;AACtB,aAAW,UAAU,UAAU;AAC7B,UAAM,cAAc,OAAO,IAAI,MAAM;AACrC,QAAI,aAAa;AACf,iBAAW,QAAQ,YAAY,QAAQ;AACrC,kBAAU,IAAI,IAAI;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AACA,aAAW,QAAQ,CAAC,GAAG,SAAS,EAAE,QAAQ;AACxC,gBAAY,KAAK,YAAY,IAAI,0BAA0B,IAAI,MAAM;AAAA,EACvE;AAEA,QAAM,gBAAgB,SAAS,QAAQ,KAAK,EAAE,CAAC;AAC/C,QAAM,aAAa,mFAAmF,aAAa;AACnH,QAAM,MAAM,YAAY,KAAK,IAAI;AAEjC,QAAM,aAAa,CAAC,GAAG,OAAO,QAAQ,EAAE,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,MAAM,CAAC;AAC1E,MAAI,aAAa,GAAG;AAClB,UAAM,UAAU,8BAA8B,MAAM;AACpD,WAAO,OAAO;AAAA,MACZ,gCAAgC,UAAU,oBAAoB,YAAY,mBAAmB,aAAa,MAAM,QAAQ,CAAC,CAAC;AAAA,EAAS,OAAO;AAAA,IAAA;AAAA,EAE9I,OAAO;AACL,WAAO,OAAO;AAAA,MACZ;AAAA,IAAA;AAAA,EAEJ;AAEA,SAAO,EAAE,YAAY,eAAe,IAAA;AACtC;AAEA,eAAe,sBACb,QACA,SACA,UACyC;AACzC,QAAM,SAAS,MAAM,4BAA4B,QAAQ,OAAO;AAChE,SAAO,qBAAqB,QAAQ,UAAU,MAAM;AACtD;AAEA,eAAsB,4BACpB,QACA,UAA2C,IACpB;AACvB,QAAM,SAAS,eAAA;AAEf,QAAM,WAAW,MAAM,iBAAA;AAEvB,MAAI,UAAU;AACZ,eAAW,UAAU,UAAU;AAC7B,iBAAW,QAAQ,SAAS,MAAM,KAAK,CAAA,GAAI;AACzC,iBAAS,QAAQ,EAAE,QAAQ,KAAA,CAAM;AAAA,MACnC;AAAA,IACF;AAAA,EACF,OAAO;AACL,eAAW,QAAQ,mBAAmB;AACpC,iBAAW,UAAU,UAAU;AAC7B,iBAAS,QAAQ,EAAE,QAAQ,KAAA,CAAM;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAEA,aAAW,QAAQ,QAAQ,WAAW,CAAA,GAAI;AACxC,UAAM,WAAW,OAAO,SAAS,WAAW,EAAE,QAAQ,eAAe,MAAM,KAAA,IAAS;AACpF,QAAI,SAAS,WAAW,QAAW;AACjC,eAAS,QAAQ,QAAQ;AAAA,IAC3B,OAAO;AACL,iBAAW,KAAK,eAAe;AAC7B,iBAAS,QAAQ,EAAE,GAAG,UAAU,QAAQ,GAAG;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS,OAAO;AAC1B,UAAM,QAAQ,QAAQ,SAAS,CAAC,KAAK,OAAO,MAAM,KAAK,CAAC;AAExD,eAAW,QAAQ,OAAO;AACxB,iBAAW,YAAY,MAAM,gBAAgB,IAAI,GAAG;AAClD,cAAM,SAAS,MAAM,SAAS,UAAU,MAAM;AAC9C,0BAAkB,QAAQ,MAAM;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,wBACd,aACqB;AACrB,MAAI,CAAC,YAAa,QAAO,CAAA;AAEzB,SAAO;AAAA,IACL;AAAA,MACE,KAAK;AAAA,MACL,UAAU;AAAA,MACV,UAAU;AAAA,IAAA;AAAA,EACZ;AAEJ;AAEA,SAAS,mBAAmB,QAAgB,KAAa,OAAuB;AAC9E;AACA,SAAO,OAAO,GAAG;AACf,QAAI,OAAO,GAAG,MAAM,MAAM;AACxB,aAAO;AACP;AAAA,IACF;AACA,QAAI,OAAO,GAAG,MAAM,OAAO;AACzB,UAAI,iBAAiB;AACrB,UAAI,IAAI,MAAM;AACd,aAAO,KAAK,KAAK,OAAO,CAAC,MAAM,MAAM;AACnC;AACA;AAAA,MACF;AACA,UAAI,iBAAiB,MAAM,GAAG;AAC5B,cAAM,MAAM,iBAAiB;AAC7B;AAAA,MACF;AACA,aAAO,MAAM;AAAA,IACf;AACA;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,kBAAkB,QAAgB,KAAa,OAAuB;AAC7E;AACA,SAAO,MAAM,OAAO,QAAQ;AAC1B,QAAI,OAAO,GAAG,MAAM,MAAM;AACxB,aAAO;AACP;AAAA,IACF;AACA,QAAI,OAAO,GAAG,MAAM,OAAO;AACzB,aAAO,MAAM;AAAA,IACf;AACA;AAAA,EACF;AACA,SAAO,OAAO;AAChB;AAEA,SAAS,oBACP,QACA,WACqD;AACrD,MAAI,QAAQ;AACZ,MAAI,IAAI;AAER,SAAO,KAAK,GAAG;AACb,UAAM,OAAO,OAAO,CAAC;AAErB,QAAI,SAAS,OAAO,SAAS,OAAO,SAAS,KAAK;AAChD,UAAI,mBAAmB,QAAQ,GAAG,IAAI;AACtC;AAAA,IACF;AAGA,QAAI,SAAS,OAAO,IAAI,KAAK,OAAO,IAAI,CAAC,MAAM,KAAK;AAClD,WAAK;AACL,aAAO,KAAK,KAAK,OAAO,CAAC,MAAM,KAAM;AACrC;AAAA,IACF;AAGA,QAAI,SAAS,OAAO,IAAI,KAAK,OAAO,IAAI,CAAC,MAAM,KAAK;AAClD,WAAK;AACL,aAAO,KAAK,GAAG;AACb,YAAI,OAAO,CAAC,MAAM,OAAO,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,CAAC,MAAM,KAAK;AACvE;AACA;AAAA,QACF;AACA;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,SAAS,KAAK;AAChB;AAAA,IACF,WAAW,SAAS,KAAK;AACvB,UAAI,UAAU,GAAG;AACf,cAAM,QAAQ;AACd,YAAI,IAAI,IAAI;AACZ,YAAI,aAAa;AAEjB,eAAO,IAAI,OAAO,QAAQ;AACxB,gBAAM,IAAI,OAAO,CAAC;AAElB,cAAI,MAAM,OAAO,MAAM,OAAO,MAAM,KAAK;AACvC,gBAAI,kBAAkB,QAAQ,GAAG,CAAC;AAClC;AAAA,UACF;AAEA,cAAI,MAAM,KAAK;AAEb,gBAAI,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,CAAC,MAAM,KAAK;AAClD,mBAAK;AACL,qBAAO,IAAI,OAAO,UAAU,OAAO,CAAC,MAAM,KAAM;AAChD;AAAA,YACF;AAEA,gBAAI,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,CAAC,MAAM,KAAK;AAClD,mBAAK;AACL,qBAAO,IAAI,IAAI,OAAO,QAAQ;AAC5B,oBAAI,OAAO,CAAC,MAAM,OAAO,OAAO,IAAI,CAAC,MAAM,KAAK;AAC9C,uBAAK;AACL;AAAA,gBACF;AACA;AAAA,cACF;AACA;AAAA,YACF;AAAA,UACF;AAEA,cAAI,MAAM,IAAK;AAAA,mBACN,MAAM,KAAK;AAClB;AACA,gBAAI,eAAe,GAAG;AACpB,qBAAO;AAAA,gBACL;AAAA,gBACA,KAAK;AAAA,gBACL,MAAM,OAAO,MAAM,QAAQ,GAAG,CAAC;AAAA,cAAA;AAAA,YAEnC;AAAA,UACF;AAEA;AAAA,QACF;AAEA,eAAO;AAAA,MACT;AACA;AAAA,IACF;AAEA;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,kBAAkB,QAAgB,QAA4B;AACrE,QAAM,mBAAmB;AACzB,MAAI;AAEJ,SAAQ,WAAW,iBAAiB,KAAK,MAAM,GAAI;AACjD,UAAM,QAAQ,sBAAsB,SAAS,CAAC,KAAK,EAAE;AACrD,UAAM,UAAU,MAAM,QAAQ,MAAM,OAAO;AAC3C,UAAM,YAAY,YAAY,SAAS,aAAa;AACpD,UAAM,YAAY,MAAM,UAAU,MAAM,SAAS,KAAK;AACtD,UAAM,YAAY,MAAM,UAAU,MAAM,SAAS,KAAK;AACtD,UAAM,SAAS,YAAY,SAAS,WAAW,EAAE,IAAI;AACrD,UAAM,iBAAiB,CAAC,MAAM,MAAM,KAAK,SAAS,IAAI,SAAS;AAE/D,QAAI,SAAS;AACX,YAAM,gBAAgB,WAAW;AACjC,YAAM,aAAa,gBAAgB,WAAW,SAAS;AACvD,YAAM,QAAQ,aAAa,SAAS,aAAa;AAEjD,iBAAW,UAAU,YAAY;AAC/B,mBAAW,QAAQ,OAAO;AACxB,mBAAS,QAAQ,EAAE,QAAQ,MAAM,QAAQ,gBAAgB;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,qBACJ;AACF,MAAI;AAEJ,SAAQ,cAAc,mBAAmB,KAAK,MAAM,GAAI;AACtD,UAAM,SAAS,SAAS,YAAY,CAAC,GAAG,MAAM,8BAA8B,IAAI,CAAC,CAAC;AAClF,aAAS,QAAQ;AAAA,MACf,QAAQ,UAAU;AAAA,MAClB,MAAM,YAAY,CAAC,KAAK;AAAA,IAAA,CACzB;AAAA,EACH;AAEA,QAAM,oBAAoB;AAC1B,MAAI;AAEJ,SAAQ,cAAc,kBAAkB,KAAK,MAAM,GAAI;AACrD,UAAM,OAAO,YAAY,CAAC,KAAK;AAC/B,UAAM,MAAM,oBAAoB,QAAQ,YAAY,KAAK;AACzD,QAAI,KAAK;AACP,YAAM,YAAY,IAAI,KAAK,QAAQ,WAAW,EAAE,EAAE,QAAQ,qBAAqB,EAAE;AACjF,YAAM,cAAc,UAAU,MAAM,sBAAsB;AAC1D,YAAM,cAAc,UAAU,MAAM,8BAA8B;AAClE,eAAS,QAAQ;AAAA,QACf,QAAQ,cAAe,YAAY,CAAC,IAAqB;AAAA,QACzD;AAAA,QACA,QAAQ,cAAc,SAAS,YAAY,CAAC,GAAG,EAAE,IAAI;AAAA,MAAA,CACtD;AAAA,IACH,OAAO;AACL,eAAS,QAAQ,EAAE,QAAQ,eAAe,MAAM;AAAA,IAClD;AAAA,EACF;AAIA,QAAM,sBAAsB;AAC5B,MAAI;AAEJ,SAAQ,aAAa,oBAAoB,KAAK,MAAM,GAAI;AACtD,UAAM,OAAO,WAAW,CAAC,KAAK;AAC9B,UAAM,UAAU,OAAO,QAAQ,MAAM,WAAW,KAAK;AACrD,UAAM,OAAO,YAAY,KAAK,OAAO,MAAM,WAAW,KAAK,IAAI,OAAO,MAAM,WAAW,OAAO,OAAO;AACrG,UAAM,cAAc,KAAK,MAAM,gCAAgC;AAC/D,UAAM,cAAc,KAAK,MAAM,sBAAsB;AACrD,aAAS,QAAQ;AAAA,MACf,QAAQ,cAAe,YAAY,CAAC,IAAqB;AAAA,MACzD;AAAA,MACA,QAAQ,cAAc,SAAS,YAAY,CAAC,GAAG,EAAE,IAAI;AAAA,IAAA,CACtD;AAAA,EACH;AAIA,QAAM,mBAAmB;AACzB,MAAI;AAEJ,SAAQ,aAAa,iBAAiB,KAAK,MAAM,GAAI;AACnD,UAAM,eAAe,WAAW,QAAQ,WAAW,CAAC,EAAE,SAAS;AAC/D,QAAI,QAAQ;AACZ,QAAI,IAAI,eAAe;AAEvB,WAAO,IAAI,OAAO,QAAQ;AACxB,YAAM,IAAI,OAAO,CAAC;AAElB,UAAI,MAAM,OAAO,MAAM,OAAO,MAAM,KAAK;AACvC,YAAI,kBAAkB,QAAQ,GAAG,CAAC;AAClC;AAAA,MACF;AAEA,UAAI,MAAM,KAAK;AACb,YAAI,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,CAAC,MAAM,KAAK;AAClD,eAAK;AACL,iBAAO,IAAI,OAAO,UAAU,OAAO,CAAC,MAAM,KAAM;AAChD;AAAA,QACF;AACA,YAAI,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,CAAC,MAAM,KAAK;AAClD,eAAK;AACL,iBAAO,IAAI,IAAI,OAAO,QAAQ;AAC5B,gBAAI,OAAO,CAAC,MAAM,OAAO,OAAO,IAAI,CAAC,MAAM,KAAK;AAC9C,mBAAK;AACL;AAAA,YACF;AACA;AAAA,UACF;AACA;AAAA,QACF;AAAA,MACF;AAEA,UAAI,MAAM,IAAK;AAAA,eACN,MAAM,KAAK;AAClB;AACA,YAAI,UAAU,EAAG;AAAA,MACnB;AAEA;AAAA,IACF;AAEA,QAAI,UAAU,GAAG;AACf,YAAM,eAAe,OAAO,MAAM,eAAe,GAAG,CAAC;AACrD,UAAI,aAAa;AACjB,UAAI,IAAI;AAER,aAAO,IAAI,aAAa,QAAQ;AAC9B,cAAM,IAAI,aAAa,CAAC;AAExB,YAAI,MAAM,OAAO,MAAM,OAAO,MAAM,KAAK;AACvC,cAAI,eAAe,GAAG;AACpB,kBAAM,QAAQ;AACd,gBAAI,SAAS,IAAI;AACjB,mBAAO,SAAS,aAAa,QAAQ;AACnC,kBAAI,aAAa,MAAM,MAAM,MAAM;AACjC,0BAAU;AACV;AAAA,cACF;AACA,kBAAI,aAAa,MAAM,MAAM,MAAO;AACpC;AAAA,YACF;AACA,kBAAM,WAAW,aAAa,MAAM,IAAI,GAAG,MAAM;AACjD,gBAAI,gBAAgB,KAAK,QAAQ,GAAG;AAClC,uBAAS,QAAQ,EAAE,QAAQ,eAAe,MAAM,UAAU;AAAA,YAC5D;AACA,gBAAI,SAAS;AACb;AAAA,UACF,OAAO;AACL,gBAAI,kBAAkB,cAAc,GAAG,CAAC;AACxC;AAAA,UACF;AAAA,QACF;AAEA,YAAI,MAAM,IAAK;AAAA,iBACN,MAAM,IAAK;AAEpB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,sBAAsB,QAAwC;AACrE,QAAM,QAAgC,CAAA;AACtC,QAAM,cAAc;AACpB,MAAI;AAEJ,SAAQ,QAAQ,YAAY,KAAK,MAAM,GAAI;AACzC,UAAM,MAAM,CAAC,KAAK,EAAE,IAAI,MAAM,CAAC,KAAK,MAAM,CAAC,KAAK;AAAA,EAClD;AAEA,SAAO;AACT;AAEA,SAAS,kBAAkB,OAAuB;AAChD,SAAO,MAAM,QAAQ,oBAAoB,IAAI;AAC/C;AAEA,MAAM,gBAAgB,CAAC,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;AAExD,SAAS,SACP,QACA,MACM;AACN,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,OAAO,KAAK,KAAK,KAAA;AACvB,QAAM,SAAS,KAAK,UAAU;AAE9B,MAAI,CAAC,SAAS,SAAS,MAAM,KAAK,CAAC,gBAAgB,KAAK,IAAI,GAAG;AAC7D;AAAA,EACF;AAEA,MAAI,CAAC,cAAc,SAAS,MAAe,EAAG;AAE9C,QAAM,UAAU,OAAO,IAAI,MAAM;AACjC,MAAI,CAAC,QAAS;AAEd,QAAM,WAAW,QAAQ,IAAI,IAAI;AACjC,MAAI,UAAU;AACZ,aAAS,IAAI,MAAM;AAAA,EACrB,OAAO;AACL,YAAQ,IAAI,MAAM,oBAAI,IAAI,CAAC,MAAM,CAAC,CAAC;AAAA,EACrC;AACF;AAEA,eAAe,mBAAmE;AAChF,MAAI;AACF,UAAM,MAAM,KAAK,cAAc,YAAY,GAAG,GAAG,IAAI;AACrD,UAAM,eAAe,KAAK,KAAK,oBAAoB;AACnD,UAAM,SAAS,MAAM,SAAS,cAAc,MAAM;AAClD,WAAO,KAAK,MAAM,MAAM;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,SAAS,OAA0C;AAC1D,QAAM,aAAa,QAAQ,kBAAkB,KAAK,IAAI;AACtD,SAAO,SAAS,SAAS,UAA0B,IAC9C,aACD;AACN;AAEA,SAAS,aAAa,SAAiB,YAAY,OAAiB;AAClE,QAAM,iBAAiB;AACvB,MAAI;AACJ,QAAM,QAAkB,CAAA;AAExB,SAAQ,WAAW,eAAe,KAAK,OAAO,GAAI;AAChD,UAAM,YAAY,SAAS,CAAC,KAAK;AACjC,QAAI,gBAAgB,KAAK,SAAS,GAAG;AACnC,YAAM,KAAK,SAAS;AAAA,IACtB;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,QAAI,UAAW,QAAO;AACtB,UAAM,OAAO,kBAAkB,OAAO;AACtC,QAAI,gBAAgB,KAAK,IAAI,GAAG;AAC9B,YAAM,KAAK,IAAI;AAAA,IACjB;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,gBAAgB,WAAmB,YAAY,MAAsB;AAC5E,QAAM,SAAS,SAAS,SAAS;AACjC,MAAI,OAAQ,QAAO,CAAC,MAAM;AAE1B,QAAM,iBAAiB;AACvB,MAAI;AACJ,QAAM,4BAAY,IAAA;AAElB,SAAQ,WAAW,eAAe,KAAK,SAAS,GAAI;AAClD,UAAM,YAAY,SAAS,CAAC,KAAK;AACjC,UAAM,IAAI,SAAS,SAAS;AAC5B,QAAI,GAAG;AACL,YAAM,IAAI,CAAC;AAAA,IACb;AAAA,EACF;AAEA,MAAI,MAAM,OAAO,EAAG,QAAO,CAAC,GAAG,KAAK;AAEpC,MAAI,CAAC,UAAW,QAAO,CAAC,aAAa;AAErC,SAAO,CAAC,GAAG,QAAQ;AACrB;AAEA,SAAS,iBAA+B;AACtC,SAAO,IAAI,IAAI,SAAS,IAAI,CAAC,WAAW,CAAC,QAAQ,oBAAI,IAAA,CAA0B,CAAC,CAAC;AACnF;AAEA,eAAe,gBAAgB,MAAiC;AAC9D,MAAI;AACF,UAAM,UAAU,MAAM,QAAQ,MAAM,EAAE,eAAe,MAAM;AAC3D,UAAM,QAAQ,MAAM,QAAQ;AAAA,MAC1B,QAAQ,IAAI,OAAO,UAAU;AAC3B,cAAM,OAAO,KAAK,MAAM,MAAM,IAAI;AAElC,YAAI,MAAM,eAAe;AACvB,iBAAO,YAAY,IAAI,MAAM,IAAI,IAAI,CAAA,IAAK,gBAAgB,IAAI;AAAA,QAChE;AAEA,eAAO,iBAAiB,IAAI,QAAQ,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAA;AAAA,MAC9D,CAAC;AAAA,IAAA;AAGH,WAAO,MAAM,KAAA;AAAA,EACf,QAAQ;AACN,WAAO,CAAA;AAAA,EACT;AACF;AAEO,SAAS,8BAA8B,QAA8B;AAC1E,SAAO,SACJ,IAAI,CAAC,WAAW;AACf,UAAM,cAAc,OAAO,IAAI,MAAM;AACrC,QAAI,CAAC,eAAe,YAAY,SAAS,EAAG,QAAO,GAAG,MAAM;AAC5D,UAAM,UAAU,CAAC,GAAG,YAAY,QAAA,CAAS,EACtC,IAAI,CAAC,CAAC,MAAM,OAAO,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,OAAO,EAAE,KAAA,EAAO,KAAK,GAAG,CAAC,GAAG,EACpE,OACA,KAAK,IAAI;AACZ,WAAO,GAAG,MAAM,KAAK,OAAO;AAAA,EAC9B,CAAC,EACA,KAAK,IAAI;AACd;"}
|
package/icon-scanner.md
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# Icon Scanner — @robosophy/ui-vue
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Vite plugin `robosophyMaterialSymbols()` automatically detects which Material Symbols
|
|
6
|
+
icons are used in source code and generates an SVG sprite with only those icons.
|
|
7
|
+
No fonts are loaded.
|
|
8
|
+
|
|
9
|
+
The scanner works at **build time** (and in dev mode via file watching).
|
|
10
|
+
It scans every `.vue`, `.ts`, `.tsx`, `.js`, `.jsx`, and `.mjs` file in the project's `src/` directory
|
|
11
|
+
(or custom `roots`), extracts icon names, families, and weights, then downloads the matching SVGs
|
|
12
|
+
from the Google Material Design Icons repository.
|
|
13
|
+
|
|
14
|
+
When the package is built (`npm run build`), a **pre-scan** (the manifest script in
|
|
15
|
+
`scripts/generate-icon-manifest.mjs`) identifies icons used by the library's own components
|
|
16
|
+
(`RsSidebar`, `RsAccountSelector`, etc.) and writes them to `dist/icon-manifest.json`.
|
|
17
|
+
The Vite plugin loads this manifest at dev/build time as a baseline, then adds icons found
|
|
18
|
+
by scanning the consuming project's source.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Detection Patterns
|
|
23
|
+
|
|
24
|
+
The scanner uses five patterns to find icon usage in source code.
|
|
25
|
+
Patterns 4 and 5 were added for cases that the original three could not handle.
|
|
26
|
+
|
|
27
|
+
### Pattern 1: `<RsIcon>` Component Tag
|
|
28
|
+
|
|
29
|
+
**Regex:** `/ <RsIcon\b([^>]*)>/g`
|
|
30
|
+
|
|
31
|
+
Parses the attributes with `parseStaticAttributes` and reads `name`, `:name`, `family`,
|
|
32
|
+
`:family`, `weight`, `:weight`.
|
|
33
|
+
|
|
34
|
+
**Detected:**
|
|
35
|
+
```vue
|
|
36
|
+
<RsIcon name="home" />
|
|
37
|
+
<RsIcon name="home" family="rounded" />
|
|
38
|
+
<RsIcon :name="cond ? 'home' : 'settings'" family="rounded" />
|
|
39
|
+
<RsIcon name="home" weight="200" />
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Not detected** (variable references):
|
|
43
|
+
```vue
|
|
44
|
+
<RsIcon :name="iconVar" />
|
|
45
|
+
<RsIcon :name="item.icon" />
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Filter:** icon names must match `/^[a-z0-9_]+$/` (lowercase with underscores).
|
|
49
|
+
|
|
50
|
+
**Dynamic `:name` with ternaries:** any string literal inside the `:name` expression
|
|
51
|
+
is extracted. For example `:name="cond ? 'add' : 'remove'"` extracts both `add` and `remove`.
|
|
52
|
+
|
|
53
|
+
**Dynamic `:name` without string literals:** if the binding is a plain variable reference
|
|
54
|
+
like `:name="current_icon"`, the scanner no longer treats it as an icon name
|
|
55
|
+
(improvement 3, see changelog). Use `include` or magic comments for these cases.
|
|
56
|
+
|
|
57
|
+
### Pattern 2: `rsPrimeIcon()` Helper
|
|
58
|
+
|
|
59
|
+
**Regex:** `/\brsPrimeIcon\(\s*["']([a-z0-9_]+)["'](?:\s*,\s*\{([^)]*)\})?/g`
|
|
60
|
+
|
|
61
|
+
**Detected:**
|
|
62
|
+
```ts
|
|
63
|
+
rsPrimeIcon("home")
|
|
64
|
+
rsPrimeIcon('settings', { family: 'rounded' })
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Not detected:**
|
|
68
|
+
```ts
|
|
69
|
+
rsPrimeIcon(dynamicName)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Pattern 3: Object Literal `icon:` Property
|
|
73
|
+
|
|
74
|
+
**Regex:** `/\bicon\s*:\s*["']([a-z0-9_]+)["']/g`
|
|
75
|
+
|
|
76
|
+
After matching, the scanner finds the enclosing `{...}` object and extracts `weight` and
|
|
77
|
+
`family` from its body (using `findEnclosingObject`). Comments are stripped from the body
|
|
78
|
+
before searching for these properties.
|
|
79
|
+
|
|
80
|
+
**Detected:**
|
|
81
|
+
```ts
|
|
82
|
+
const items = [
|
|
83
|
+
{ label: "Inbox", icon: "inbox" },
|
|
84
|
+
{ label: "Settings", icon: "settings", family: "rounded", weight: 200 },
|
|
85
|
+
];
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Not detected** (no `icon:` string literal):
|
|
89
|
+
```ts
|
|
90
|
+
const items = [{ icon: dynamicVar }]; // variable reference
|
|
91
|
+
{ icon } // shorthand property
|
|
92
|
+
{ ["icon"]: "home" } // computed property
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**`findEnclosingObject` behavior:**
|
|
96
|
+
- Scans backward from the `icon:` match to find the nearest opening `{`
|
|
97
|
+
- Skips over strings, template literals, escaped quotes, and comments (`//` and `/* */`)
|
|
98
|
+
- Scans forward to find the matching `}`
|
|
99
|
+
- Returns the body content between `{` and `}`
|
|
100
|
+
- The body is then cleaned of comments before searching for `weight:` and `family:`
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
// This correctly reads weight=200 and family="rounded":
|
|
104
|
+
{ icon: "home", weight: 200, family: "rounded" }
|
|
105
|
+
|
|
106
|
+
// Comments are ignored:
|
|
107
|
+
{ /* weight: 200, family: "rounded" */ icon: "home" }
|
|
108
|
+
// → default weight 400, default family "outlined"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Pattern 4: Magic Comments
|
|
112
|
+
|
|
113
|
+
**Regex:** `/\/\/\s*@robosophy-icon:\s*([a-z0-9_]+)(?:\s+family=(\w+))?(?:\s+weight=(\d+))?/g`
|
|
114
|
+
|
|
115
|
+
For cases where the scanner cannot detect an icon statically (props, variables, API responses,
|
|
116
|
+
computed properties), use magic comments to declare them explicitly.
|
|
117
|
+
|
|
118
|
+
**Usage:**
|
|
119
|
+
```ts
|
|
120
|
+
// @robosophy-icon: home
|
|
121
|
+
// @robosophy-icon: settings family=rounded
|
|
122
|
+
// @robosophy-icon: warning weight=200
|
|
123
|
+
// @robosophy-icon: manage_accounts family=rounded weight=200
|
|
124
|
+
|
|
125
|
+
const sidebarItems = [
|
|
126
|
+
{ id: "home", label: "Home", icon: "home" },
|
|
127
|
+
{ id: "settings", label: "Settings", icon: "settings" },
|
|
128
|
+
];
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Best for:**
|
|
132
|
+
- Icon names passed as props to parent components
|
|
133
|
+
- Icon names in constants, enums, or configuration objects
|
|
134
|
+
- Icon names from API responses
|
|
135
|
+
- Icon names in computed properties or functions
|
|
136
|
+
|
|
137
|
+
**Placement:** anywhere in the source file. The scanner reads the entire file content.
|
|
138
|
+
|
|
139
|
+
### Pattern 5: Arrays of Icon Strings
|
|
140
|
+
|
|
141
|
+
**Regex:** `/\/\/\s*@robosophy-icon:\s*([a-z0-9_]+)(?:\s+family=(\w+))?(?:\s+weight=(\d+))?/g`
|
|
142
|
+
|
|
143
|
+
Wait, that's the same as Pattern 4. Let me check the actual regex I used...
|
|
144
|
+
|
|
145
|
+
Actually, Pattern 5 detects arrays assigned to variables whose name contains "icon" (case-insensitive).
|
|
146
|
+
|
|
147
|
+
**Regex:** `/(?:const|let|var)\s+(\w*[Ii]con\w*)\s*=\s*\[/g`
|
|
148
|
+
|
|
149
|
+
After finding the start of an icon array, it extracts all string literals from the brackets.
|
|
150
|
+
|
|
151
|
+
**Detected:**
|
|
152
|
+
```ts
|
|
153
|
+
const iconNames = ["home", "settings", "info"];
|
|
154
|
+
const sidebarIcons = ["home", "search"];
|
|
155
|
+
const ICON_LIST = ["warning", "error"];
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Not detected** (no "icon" in variable name):
|
|
159
|
+
```ts
|
|
160
|
+
const names = ["home", "settings"]; // variable name doesn't contain "icon"
|
|
161
|
+
const items = [{ icon: "home" }]; // already handled by Pattern 3
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Caveat:** This pattern uses a heuristic (variable name contains "icon"). It may miss icons
|
|
165
|
+
stored in arrays with unrelated names. Use magic comments for those.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Plugin Options
|
|
170
|
+
|
|
171
|
+
| Option | Type | Default | Description |
|
|
172
|
+
|--------|------|---------|-------------|
|
|
173
|
+
| `include` | `IconInput[]` | `[]` | Icons to include regardless of detection. Each entry is a string (name) or `{ family, name, weight? }`. If weight is omitted, all valid weights (100-700) are included. |
|
|
174
|
+
| `scan` | `boolean` | `true` | Set to `false` to disable source scanning entirely. All icons must be in `include`. |
|
|
175
|
+
| `roots` | `string[]` | `["./src"]` | Directories to scan for icon usage. |
|
|
176
|
+
| `outputDir` | `string` | `"assets"` | Output directory for the SVG sprite. |
|
|
177
|
+
| `fontDisplay` | `string` | — | CSS `font-display` value. |
|
|
178
|
+
| `preload` | `boolean` | `false` | Preload the SVG sprite in the `<head>`. |
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## `icon-manifest.json`
|
|
183
|
+
|
|
184
|
+
The build script (`scripts/generate-icon-manifest.mjs`) scans the library's own components
|
|
185
|
+
under `src/components/` and produces `dist/icon-manifest.json`.
|
|
186
|
+
|
|
187
|
+
**File format:**
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"outlined": ["home", "search", "settings"],
|
|
191
|
+
"rounded": ["settings"],
|
|
192
|
+
"sharp": []
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Role in the pipeline:**
|
|
197
|
+
1. Build generates the manifest from the library's component source
|
|
198
|
+
2. Vite plugin loads the manifest as a baseline
|
|
199
|
+
3. Plugin then scans the consuming project's source for additional icons
|
|
200
|
+
4. Plugin fetches SVGs for the combined set
|
|
201
|
+
|
|
202
|
+
**Why not just use the manifest alone?**
|
|
203
|
+
The manifest ensures that icons required by library components are always available,
|
|
204
|
+
even if the consuming project doesn't use those components. However, the manifest may
|
|
205
|
+
include icons that the consumer never uses (it's a conservative baseline). The plugin's
|
|
206
|
+
source scan adds only the icons that the consumer's own code references.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Known Limitations
|
|
211
|
+
|
|
212
|
+
### Icon names passed as props
|
|
213
|
+
|
|
214
|
+
```vue
|
|
215
|
+
<!-- RsSidebar uses :name="icon" which is a prop -->
|
|
216
|
+
<RsIcon :name="icon" />
|
|
217
|
+
|
|
218
|
+
<!-- RsSidebar uses :name="item.icon" from v-for iteration -->
|
|
219
|
+
<RsIcon :name="item.icon" />
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The scanner cannot resolve the runtime value of `icon` or `item.icon`.
|
|
223
|
+
|
|
224
|
+
**Solutions:**
|
|
225
|
+
1. Use `include` in the Vite plugin config
|
|
226
|
+
2. Add magic comments (`// @robosophy-icon: icon_name`) in the component file
|
|
227
|
+
3. Declare the icon in the same file as the data source (Pattern 3 will detect it)
|
|
228
|
+
|
|
229
|
+
### Icon names in arrays of strings
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
const iconNames = ["home", "settings"];
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Pattern 5 detects these only if the variable name contains "icon".
|
|
236
|
+
|
|
237
|
+
### Icon names in enums or constants
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
export enum Icons {
|
|
241
|
+
HOME = "home",
|
|
242
|
+
SETTINGS = "settings",
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Not detected. Use magic comments or `include`.
|
|
247
|
+
|
|
248
|
+
### Icon names from API responses
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
const config = await fetchConfig();
|
|
252
|
+
return <RsIcon :name="config.iconName" />;
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Cannot be detected statically. Use `include` or magic comments.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Best Practices
|
|
260
|
+
|
|
261
|
+
### For AI agents editing consuming projects
|
|
262
|
+
|
|
263
|
+
1. **Static icons:** use `<RsIcon name="icon_name" />` — always detected by Pattern 1
|
|
264
|
+
2. **Object configs:** use `{ icon: "icon_name" }` — detected by Pattern 3
|
|
265
|
+
3. **Dynamic icons:** use magic comments or `include`
|
|
266
|
+
4. **PrimeVue buttons:** use `rsPrimeIcon('icon_name')` — detected by Pattern 2
|
|
267
|
+
5. **Props to RsSidebar:** icons in the `items` array objects are detected by Pattern 3
|
|
268
|
+
|
|
269
|
+
### For humans
|
|
270
|
+
|
|
271
|
+
When adding a new icon to the codebase:
|
|
272
|
+
1. Check if it's statically detectable (direct string literal)
|
|
273
|
+
2. If not, add a magic comment near the usage
|
|
274
|
+
3. If the icon is used in many places, add it to the plugin config's `include`
|
|
275
|
+
|
|
276
|
+
### Tag helper
|
|
277
|
+
|
|
278
|
+
To quickly test if an icon name exists in the Material Symbols set:
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
https://fonts.google.com/icons?icon.query=icon_name
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Changelog
|
|
287
|
+
|
|
288
|
+
| Date | Change |
|
|
289
|
+
|------|--------|
|
|
290
|
+
| 2026-07-13 | **Pattern 4** (magic comments) and **Pattern 5** (icon arrays) added |
|
|
291
|
+
| 2026-07-13 | `findEnclosingObject`: now handles comments, escaped quotes, and template literals |
|
|
292
|
+
| 2026-07-13 | `resolveNames`: dynamic `:name` bindings no longer fall back to treating variable names as icon names |
|
|
293
|
+
| 2026-07-13 | Pattern 3: weight and family are now extracted from the enclosing object literal |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robosophy/ui-vue",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.10",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://robosophy.gitlab.io/robosophy-basf/robosophy-library/",
|
|
@@ -25,12 +25,14 @@
|
|
|
25
25
|
},
|
|
26
26
|
"./AGENTS.md": "./AGENTS.md",
|
|
27
27
|
"./ai-context.md": "./ai-context.md",
|
|
28
|
-
"./components.md": "./components.md"
|
|
28
|
+
"./components.md": "./components.md",
|
|
29
|
+
"./icon-scanner.md": "./icon-scanner.md"
|
|
29
30
|
},
|
|
30
31
|
"files": [
|
|
31
32
|
"AGENTS.md",
|
|
32
33
|
"ai-context.md",
|
|
33
34
|
"components.md",
|
|
35
|
+
"icon-scanner.md",
|
|
34
36
|
"dist",
|
|
35
37
|
"README.md",
|
|
36
38
|
"package.json"
|