react-icons-sprite 1.2.0 → 1.3.0

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.
@@ -1,4 +1,4 @@
1
- import { o as resolveIconImport, t as computeIconId } from "./compute-icon-id-CGZBEP3i.mjs";
1
+ import { o as resolveIconImport, t as computeIconId } from "./compute-icon-id-1Gop_fRM.mjs";
2
2
  import { existsSync, readFileSync, statSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { pathToFileURL } from "node:url";
@@ -1,4 +1,4 @@
1
- import { s as createCollector } from "./compute-icon-id-CGZBEP3i.mjs";
1
+ import { s as createCollector } from "./compute-icon-id-1Gop_fRM.mjs";
2
2
  //#region src/collector.ts
3
3
  const collector = createCollector();
4
4
  //#endregion
@@ -52,7 +52,8 @@ const DEFAULT_ICON_SOURCES = [
52
52
  /^@ant-design\/icons(?:\/.*)?$/,
53
53
  /^@fluentui\/react-icons(?:\/svg\/[\w-]+)?$/,
54
54
  /^@primer\/octicons-react$/,
55
- /^@hugeicons\/core-free-icons(?:\/.*)?$/
55
+ /^@hugeicons\/core-free-icons(?:\/.*)?$/,
56
+ /^@pillage-first\/graphics$/
56
57
  ];
57
58
  const phosphorIconPathName = (name) => name.endsWith("Icon") ? name.slice(0, -4) : name;
58
59
  const fluentIconPathName = (name) => {
@@ -71,7 +72,7 @@ const exactResolvers = {
71
72
  "devicons-react": (pack, name) => `${pack}/icons/${name}`,
72
73
  "@carbon/icons-react": (pack, name) => `${pack}/es/${name}.js`,
73
74
  "@ant-design/icons": (pack, name) => `${pack}/lib/icons/${name}.js`,
74
- "@fluentui/react-icons": (pack, name) => `${pack}/lib-cjs/atoms/svg/${fluentIconPathName(name)}.js`,
75
+ "@fluentui/react-icons": (pack, name) => `${pack}/lib/atoms/svg/${fluentIconPathName(name)}`,
75
76
  "@primer/octicons-react": (pack) => pack,
76
77
  "@hugeicons/core-free-icons": (pack, name) => `${pack}/${name}`
77
78
  };
@@ -81,7 +82,7 @@ const resolveIconImport = (pack, exportName) => {
81
82
  if (/^@mui\/icons-material(?:\/.*)?$/.test(pack)) return pack.split("/").length > 2 ? pack : `${pack}/${exportName}`;
82
83
  if (/^@ant-design\/icons\/.+$/.test(pack)) return `@ant-design/icons/lib/icons/${exportName === "default" ? pack.split("/").at(-1) : exportName}.js`;
83
84
  const fluentSubpath = /^@fluentui\/react-icons\/svg\/(.+)$/.exec(pack);
84
- if (fluentSubpath) return `@fluentui/react-icons/lib-cjs/atoms/svg/${fluentSubpath[1]}.js`;
85
+ if (fluentSubpath) return `@fluentui/react-icons/lib/atoms/svg/${fluentSubpath[1]}`;
85
86
  if (/^@hugeicons\/core-free-icons\/.+$/.test(pack)) return pack;
86
87
  if (/^@heroicons\/react\/(?:\d{2})\/(?:outline|solid)$/.test(pack)) return `${pack}/${exportName}`;
87
88
  if (/^@fortawesome\/[\w-]+-svg-icons$/.test(pack)) return `${pack}/${exportName}`;
@@ -1,4 +1,4 @@
1
- import { a as DEFAULT_ICON_SOURCES, n as normalizePackAlias } from "./compute-icon-id-CGZBEP3i.mjs";
1
+ import { a as DEFAULT_ICON_SOURCES, n as normalizePackAlias } from "./compute-icon-id-1Gop_fRM.mjs";
2
2
  import MagicString from "magic-string";
3
3
  //#region src/transform/edit-applier.ts
4
4
  const applyEdits = (code, edits) => {
@@ -94,10 +94,26 @@ const parseNamedSpecifiers = (specifier, specifierOffset, imports) => {
94
94
  });
95
95
  }
96
96
  };
97
+ const parseImportSpecifiers = (specifier, specifierOffset) => {
98
+ const specifiers = [];
99
+ const braceStart = specifier.indexOf("{");
100
+ const defaultText = (braceStart === -1 ? specifier : specifier.slice(0, braceStart)).replace(/,\s*$/, "");
101
+ const defaultRange = trimRange(defaultText, specifierOffset);
102
+ if (defaultRange && !specifier.slice(defaultRange[0] - specifierOffset, defaultRange[1] - specifierOffset).startsWith("type ")) {
103
+ const local = specifier.slice(defaultRange[0] - specifierOffset, defaultRange[1] - specifierOffset);
104
+ if (local && /^[A-Za-z_$][\w$]*$/.test(local)) specifiers.push({
105
+ local,
106
+ exportName: "default",
107
+ range: defaultRange
108
+ });
109
+ }
110
+ parseNamedSpecifiers(specifier, specifierOffset, specifiers);
111
+ return specifiers;
112
+ };
97
113
  const countImportSpecifiers = (specifier) => {
98
114
  let count = 0;
99
115
  const braceStart = specifier.indexOf("{");
100
- if ((braceStart === -1 ? specifier : specifier.slice(0, braceStart)).replace(/,$/, "").trim()) count += 1;
116
+ if ((braceStart === -1 ? specifier : specifier.slice(0, braceStart)).replace(/,\s*$/, "").trim()) count += 1;
101
117
  const braceEnd = specifier.lastIndexOf("}");
102
118
  if (braceStart !== -1 && braceEnd > braceStart) {
103
119
  const named = specifier.slice(braceStart + 1, braceEnd);
@@ -114,22 +130,11 @@ const scanImportsDetailed = (code, sources) => {
114
130
  if (specifier.trim().startsWith("type ")) continue;
115
131
  const matchStart = match.index;
116
132
  const specifierOffset = matchStart + statement.indexOf(specifier);
117
- const specifiers = [];
118
- const braceStart = specifier.indexOf("{");
119
- const defaultText = (braceStart === -1 ? specifier : specifier.slice(0, braceStart)).replace(/,$/, "");
120
- const defaultRange = trimRange(defaultText, specifierOffset);
121
- if (defaultRange && !specifier.slice(defaultRange[0] - specifierOffset, defaultRange[1] - specifierOffset).startsWith("type ")) {
122
- const local = specifier.slice(defaultRange[0] - specifierOffset, defaultRange[1] - specifierOffset);
123
- if (local && /^[A-Za-z_$][\w$]*$/.test(local)) specifiers.push({
124
- local,
125
- exportName: "default",
126
- range: defaultRange
127
- });
128
- }
129
- parseNamedSpecifiers(specifier, specifierOffset, specifiers);
133
+ const specifiers = parseImportSpecifiers(specifier, specifierOffset);
130
134
  if (specifiers.length) imports.push({
131
135
  pack,
132
136
  declarationRange: [matchStart, matchStart + statement.length],
137
+ specifierRange: [specifierOffset, specifierOffset + specifier.length],
133
138
  specifiers,
134
139
  specifierCount: countImportSpecifiers(specifier)
135
140
  });
@@ -484,43 +489,71 @@ const cleanupScannedImports = (code, imports, usedLocals) => {
484
489
  });
485
490
  continue;
486
491
  }
487
- for (const specifier of usedSpecifiers) {
488
- const [from, to] = specifierRemovalRange(code, specifier.range);
489
- edits.push({
490
- type: "remove",
491
- from,
492
- to
493
- });
494
- }
492
+ const edit = removeImportSpecifiers(code, item, usedLocals);
493
+ if (edit) edits.push(edit);
495
494
  }
496
495
  return edits;
497
496
  };
497
+ const removeImportSpecifiers = (code, item, usedLocals) => {
498
+ const specifierText = code.slice(item.specifierRange[0], item.specifierRange[1]);
499
+ const braceStart = specifierText.indexOf("{");
500
+ const braceEnd = specifierText.lastIndexOf("}");
501
+ const defaultSpecifier = item.specifiers.find((specifier) => specifier.exportName === "default");
502
+ const defaultLocal = (braceStart === -1 ? specifierText : specifierText.slice(0, braceStart)).replace(/,\s*$/, "").trim();
503
+ const parts = [];
504
+ if (defaultLocal && (!defaultSpecifier || !usedLocals.has(defaultSpecifier.local))) parts.push(defaultLocal);
505
+ if (braceStart !== -1 && braceEnd > braceStart) {
506
+ const namedSpecifiers = collectKeptNamedSpecifiers(specifierText.slice(braceStart + 1, braceEnd), usedLocals);
507
+ if (namedSpecifiers.length) parts.push(`{ ${namedSpecifiers.join(", ")} }`);
508
+ }
509
+ if (!parts.length) return {
510
+ type: "remove",
511
+ from: item.declarationRange[0],
512
+ to: extendToLineEnd(code, item.declarationRange[1])
513
+ };
514
+ return {
515
+ type: "replace",
516
+ from: item.specifierRange[0],
517
+ to: item.specifierRange[1],
518
+ value: parts.join(", ")
519
+ };
520
+ };
521
+ const collectKeptNamedSpecifiers = (namedText, usedLocals) => {
522
+ const kept = [];
523
+ let segmentStart = 0;
524
+ for (let index = 0; index <= namedText.length; index += 1) {
525
+ if (index !== namedText.length && namedText[index] !== ",") continue;
526
+ const text = namedText.slice(segmentStart, index).trim();
527
+ segmentStart = index + 1;
528
+ if (!text) continue;
529
+ if (text.startsWith("type ")) {
530
+ kept.push(text);
531
+ continue;
532
+ }
533
+ const aliasMatch = /^(.*?)\s+as\s+([A-Za-z_$][\w$]*)$/.exec(text);
534
+ const local = aliasMatch ? aliasMatch[2] : text;
535
+ if (!usedLocals.has(local)) kept.push(text);
536
+ }
537
+ return kept;
538
+ };
498
539
  const cleanupScannedFontAwesomeComponentImports = (code, usedLocals) => {
499
540
  const edits = [];
500
541
  IMPORT_RE.lastIndex = 0;
501
542
  for (const match of code.matchAll(IMPORT_RE)) {
502
543
  const [statement, specifier, , source] = match;
503
544
  if (source !== FONTAWESOME_REACT_PACK) continue;
504
- const specifiers = [];
505
- parseNamedSpecifiers(specifier, match.index + statement.indexOf(specifier), specifiers);
545
+ const specifierOffset = match.index + statement.indexOf(specifier);
546
+ const specifiers = parseImportSpecifiers(specifier, specifierOffset);
506
547
  const removableSpecifiers = specifiers.filter((item) => item.exportName === "FontAwesomeIcon" && usedLocals.has(item.local));
507
548
  if (!removableSpecifiers.length) continue;
508
- if (removableSpecifiers.length === specifiers.length) {
509
- edits.push({
510
- type: "remove",
511
- from: match.index,
512
- to: extendToLineEnd(code, match.index + statement.length)
513
- });
514
- continue;
515
- }
516
- for (const specifier of removableSpecifiers) {
517
- const [from, to] = specifierRemovalRange(code, specifier.range);
518
- edits.push({
519
- type: "remove",
520
- from,
521
- to
522
- });
523
- }
549
+ const edit = removeImportSpecifiers(code, {
550
+ pack: source,
551
+ declarationRange: [match.index, match.index + statement.length],
552
+ specifierRange: [specifierOffset, specifierOffset + specifier.length],
553
+ specifiers,
554
+ specifierCount: countImportSpecifiers(specifier)
555
+ }, new Set(removableSpecifiers.map((item) => item.local)));
556
+ if (edit) edits.push(edit);
524
557
  }
525
558
  return edits;
526
559
  };
@@ -530,26 +563,18 @@ const cleanupScannedHugeiconsComponentImports = (code, usedLocals) => {
530
563
  for (const match of code.matchAll(IMPORT_RE)) {
531
564
  const [statement, specifier, , source] = match;
532
565
  if (source !== HUGEICONS_REACT_PACK) continue;
533
- const specifiers = [];
534
- parseNamedSpecifiers(specifier, match.index + statement.indexOf(specifier), specifiers);
566
+ const specifierOffset = match.index + statement.indexOf(specifier);
567
+ const specifiers = parseImportSpecifiers(specifier, specifierOffset);
535
568
  const removableSpecifiers = specifiers.filter((item) => item.exportName === "HugeiconsIcon" && usedLocals.has(item.local));
536
569
  if (!removableSpecifiers.length) continue;
537
- if (removableSpecifiers.length === specifiers.length) {
538
- edits.push({
539
- type: "remove",
540
- from: match.index,
541
- to: extendToLineEnd(code, match.index + statement.length)
542
- });
543
- continue;
544
- }
545
- for (const specifier of removableSpecifiers) {
546
- const [from, to] = specifierRemovalRange(code, specifier.range);
547
- edits.push({
548
- type: "remove",
549
- from,
550
- to
551
- });
552
- }
570
+ const edit = removeImportSpecifiers(code, {
571
+ pack: source,
572
+ declarationRange: [match.index, match.index + statement.length],
573
+ specifierRange: [specifierOffset, specifierOffset + specifier.length],
574
+ specifiers,
575
+ specifierCount: countImportSpecifiers(specifier)
576
+ }, new Set(removableSpecifiers.map((item) => item.local)));
577
+ if (edit) edits.push(edit);
553
578
  }
554
579
  return edits;
555
580
  };
@@ -564,20 +589,6 @@ const extendToLineEnd = (code, end) => {
564
589
  if (code[to] === "\n") return to + 1;
565
590
  return to;
566
591
  };
567
- const specifierRemovalRange = (code, [start, end]) => {
568
- let from = start;
569
- let to = end;
570
- let before = start - 1;
571
- while (before >= 0 && isWhitespaceCode(code.charCodeAt(before))) before -= 1;
572
- if (before >= 0 && code[before] === ",") {
573
- from = before;
574
- return [from, to];
575
- }
576
- let after = end;
577
- while (after < code.length && isWhitespaceCode(code.charCodeAt(after))) after += 1;
578
- if (after < code.length && code[after] === ",") to = consumeTrailingWhitespace(code, after + 1);
579
- return [from, to];
580
- };
581
592
  const consumeTrailingWhitespace = (code, start) => {
582
593
  let to = start;
583
594
  while (to < code.length && isWhitespaceCode(code.charCodeAt(to))) to += 1;
@@ -590,11 +601,44 @@ const editRange = (edit) => {
590
601
  const rangeContains = ([start, end], index) => {
591
602
  return index >= start && index < end;
592
603
  };
604
+ const findPreviousJsxTextBoundary = (code, start) => {
605
+ let index = start - 1;
606
+ while (index >= 0) {
607
+ const char = code[index];
608
+ if (char === ">" || char === "<" || char === "{" || char === "}") return index;
609
+ index -= 1;
610
+ }
611
+ return -1;
612
+ };
613
+ const findNextJsxTextBoundary = (code, start) => {
614
+ let index = start;
615
+ while (index < code.length) {
616
+ const char = code[index];
617
+ if (char === "<" || char === "{") return index;
618
+ index += 1;
619
+ }
620
+ return -1;
621
+ };
622
+ const isLikelyJsxTagStart = (code, index) => {
623
+ const next = code[index + 1];
624
+ return next === "/" || next === ">" || isIdentifierStart(next);
625
+ };
626
+ const isLikelyJsxTextIdentifier = (code, start, end) => {
627
+ const previousBoundary = findPreviousJsxTextBoundary(code, start);
628
+ if (previousBoundary === -1 || code[previousBoundary] !== ">" && code[previousBoundary] !== "}") return false;
629
+ const nextBoundary = findNextJsxTextBoundary(code, end);
630
+ if (nextBoundary === -1) return false;
631
+ if (code[nextBoundary] === "<" && !isLikelyJsxTagStart(code, nextBoundary)) return false;
632
+ const text = code.slice(previousBoundary + 1, nextBoundary);
633
+ return !/[=()[\]{};,+*/%|?]/.test(text);
634
+ };
593
635
  const collectUsedLocalsOutsideRanges = (code, usedLocals, ignoredRanges) => {
594
636
  const ranges = [...ignoredRanges].sort((left, right) => left[0] - right[0]);
595
637
  const found = /* @__PURE__ */ new Set();
596
638
  let rangeIndex = 0;
597
639
  let quote = null;
640
+ let template = false;
641
+ let templateExpressionDepth = 0;
598
642
  let lineComment = false;
599
643
  let blockComment = false;
600
644
  for (let index = 0; index < code.length; index += 1) {
@@ -625,6 +669,21 @@ const collectUsedLocalsOutsideRanges = (code, usedLocals, ignoredRanges) => {
625
669
  if (char === quote) quote = null;
626
670
  continue;
627
671
  }
672
+ if (template && templateExpressionDepth === 0) {
673
+ if (char === "\\") {
674
+ index += 1;
675
+ continue;
676
+ }
677
+ if (char === "`") {
678
+ template = false;
679
+ continue;
680
+ }
681
+ if (char === "$" && next === "{") {
682
+ templateExpressionDepth = 1;
683
+ index += 1;
684
+ }
685
+ continue;
686
+ }
628
687
  if (char === "/" && next === "/") {
629
688
  lineComment = true;
630
689
  index += 1;
@@ -635,16 +694,27 @@ const collectUsedLocalsOutsideRanges = (code, usedLocals, ignoredRanges) => {
635
694
  index += 1;
636
695
  continue;
637
696
  }
697
+ if (template && templateExpressionDepth > 0) {
698
+ if (char === "{") templateExpressionDepth += 1;
699
+ if (char === "}") {
700
+ templateExpressionDepth -= 1;
701
+ if (templateExpressionDepth === 0) continue;
702
+ }
703
+ }
638
704
  if (char === "\"" || char === "'") {
639
705
  quote = char;
640
706
  continue;
641
707
  }
708
+ if (char === "`") {
709
+ template = true;
710
+ continue;
711
+ }
642
712
  if (!isIdentifierStart(char)) continue;
643
713
  const start = index;
644
714
  index += 1;
645
715
  while (isIdentifierPart(code[index])) index += 1;
646
716
  const local = code.slice(start, index);
647
- if (usedLocals.has(local)) found.add(local);
717
+ if (usedLocals.has(local) && !isLikelyJsxTextIdentifier(code, start, index)) found.add(local);
648
718
  index -= 1;
649
719
  }
650
720
  return found;
@@ -1,7 +1,7 @@
1
1
  import { REACT_ICONS_SPRITE_URL_PLACEHOLDER } from "../index.mjs";
2
- import { i as createSpriteAssetName, r as createIconSources, s as createCollector } from "../compute-icon-id-CGZBEP3i.mjs";
3
- import { t as buildSprite } from "../build-sprite-DpqABaHa.mjs";
4
- import { t as transformModule } from "../transform-module-DglDSarv.mjs";
2
+ import { i as createSpriteAssetName, r as createIconSources, s as createCollector } from "../compute-icon-id-1Gop_fRM.mjs";
3
+ import { t as buildSprite } from "../build-sprite-Dci5SlWn.mjs";
4
+ import { t as transformModule } from "../transform-module-BakS0Tzh.mjs";
5
5
  import { createHash } from "node:crypto";
6
6
  //#region src/vite/plugin.ts
7
7
  const reactIconsSprite = (options = {}) => {
@@ -1,6 +1,6 @@
1
- import { r as createIconSources } from "../compute-icon-id-CGZBEP3i.mjs";
2
- import { t as transformModule } from "../transform-module-DglDSarv.mjs";
3
- import { t as collector } from "../collector-D0VUmoK8.mjs";
1
+ import { r as createIconSources } from "../compute-icon-id-1Gop_fRM.mjs";
2
+ import { t as transformModule } from "../transform-module-BakS0Tzh.mjs";
3
+ import { t as collector } from "../collector-dvzR3TG1.mjs";
4
4
  //#region src/webpack/loader.ts
5
5
  const reactIconsSpriteLoader = async function(source) {
6
6
  if (this.mode === "development") return source;
@@ -1,7 +1,7 @@
1
1
  import { REACT_ICONS_SPRITE_URL_PLACEHOLDER } from "../index.mjs";
2
- import { i as createSpriteAssetName } from "../compute-icon-id-CGZBEP3i.mjs";
3
- import { t as buildSprite } from "../build-sprite-DpqABaHa.mjs";
4
- import { t as collector } from "../collector-D0VUmoK8.mjs";
2
+ import { i as createSpriteAssetName } from "../compute-icon-id-1Gop_fRM.mjs";
3
+ import { t as buildSprite } from "../build-sprite-Dci5SlWn.mjs";
4
+ import { t as collector } from "../collector-dvzR3TG1.mjs";
5
5
  import { createHash } from "node:crypto";
6
6
  //#region src/webpack/plugin.ts
7
7
  var ReactIconsSpriteWebpackPlugin = class {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://www.schemastore.org/package.json",
3
3
  "name": "react-icons-sprite",
4
- "version": "1.2.0",
4
+ "version": "1.3.0",
5
5
  "type": "module",
6
6
  "description": "A lightweight Vite, Rsbuild and Webpack plugin for popular icon libraries that builds a single SVG sprite and rewrites icons to <use>, reducing bundle size and runtime overhead.",
7
7
  "author": "Jure Rotar <hello@jurerotar.com>",