@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 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
@@ -13,7 +13,6 @@
13
13
  "favorite",
14
14
  "help",
15
15
  "home",
16
- "icon",
17
16
  "info",
18
17
  "inventory",
19
18
  "login",
@@ -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;AA0LD,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAY1E"}
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: objectMatch[1] ?? ""
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":"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"}
@@ -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.9",
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"