react-icons-sprite 1.1.1 → 1.2.0-rc.1

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.
@@ -237,6 +237,47 @@ const scanJsxIconEdits = (code, symbols, componentName, usedSymbols, register, h
237
237
  count
238
238
  };
239
239
  };
240
+ const scanJsxReferencePropEdits = (code, symbols, componentName, usedSymbols, register, ignoredComponentLocals) => {
241
+ if (symbols.size === 0) return {
242
+ edits: [],
243
+ count: 0
244
+ };
245
+ const edits = [];
246
+ let count = 0;
247
+ for (let index = code.indexOf("<"); index !== -1; index = code.indexOf("<", index + 1)) {
248
+ let cursor = index + 1;
249
+ while (isWhitespace(code[cursor])) cursor += 1;
250
+ if (code[cursor] === "/") continue;
251
+ if (!isIdentifierStart(code[cursor])) continue;
252
+ const componentStart = cursor;
253
+ cursor += 1;
254
+ while (isIdentifierPart(code[cursor])) cursor += 1;
255
+ const componentLocal = code.slice(componentStart, cursor);
256
+ if (symbols.items[componentLocal] || ignoredComponentLocals.has(componentLocal)) continue;
257
+ const tagEnd = findJsxOpeningTagEnd(code, cursor);
258
+ if (tagEnd === -1) continue;
259
+ const attributes = code.slice(cursor, tagEnd);
260
+ for (const match of attributes.matchAll(/(\s[A-Za-z_$][\w$.-]*(?::[A-Za-z_$][\w$.-]*)?\s*=\s*\{\s*)([A-Za-z_$][\w$]*)(\s*\})/g)) {
261
+ const iconLocal = match[2];
262
+ const symbol = symbols.items[iconLocal];
263
+ if (!symbol || isFontAwesomeIconPack(symbol.pack) || isHugeiconsIconPack(symbol.pack)) continue;
264
+ const iconLocalStart = cursor + match.index + match[1].length;
265
+ edits.push({
266
+ type: "replace",
267
+ from: iconLocalStart,
268
+ to: iconLocalStart + iconLocal.length,
269
+ value: `(props) => <${componentName} {...props} iconId="${symbol.iconId}" />`
270
+ });
271
+ count += 1;
272
+ usedSymbols.add(iconLocal);
273
+ register(symbol.pack, symbol.exportName);
274
+ }
275
+ }
276
+ return {
277
+ edits,
278
+ count
279
+ };
280
+ };
240
281
  const hasIconIdAttribute = (code, start, end) => {
241
282
  for (let index = start; index < end; index += 1) {
242
283
  if (code.charCodeAt(index) !== 105 || code.charCodeAt(index + 1) !== 99 || code.charCodeAt(index + 2) !== 111 || code.charCodeAt(index + 3) !== 110 || code.charCodeAt(index + 4) !== 73 || code.charCodeAt(index + 5) !== 100) continue;
@@ -466,6 +507,82 @@ const consumeTrailingWhitespace = (code, start) => {
466
507
  while (to < code.length && isWhitespaceCode(code.charCodeAt(to))) to += 1;
467
508
  return to;
468
509
  };
510
+ const editRange = (edit) => {
511
+ if (edit.type === "insert") return null;
512
+ return [edit.from, edit.to];
513
+ };
514
+ const rangeContains = ([start, end], index) => {
515
+ return index >= start && index < end;
516
+ };
517
+ const collectUsedLocalsOutsideRanges = (code, usedLocals, ignoredRanges) => {
518
+ const ranges = [...ignoredRanges].sort((left, right) => left[0] - right[0]);
519
+ const found = /* @__PURE__ */ new Set();
520
+ let rangeIndex = 0;
521
+ let quote = null;
522
+ let lineComment = false;
523
+ let blockComment = false;
524
+ for (let index = 0; index < code.length; index += 1) {
525
+ while (rangeIndex < ranges.length && ranges[rangeIndex][1] <= index) rangeIndex += 1;
526
+ const ignoredRange = ranges[rangeIndex];
527
+ if (ignoredRange && rangeContains(ignoredRange, index)) {
528
+ index = ignoredRange[1] - 1;
529
+ continue;
530
+ }
531
+ const char = code[index];
532
+ const next = code[index + 1];
533
+ if (lineComment) {
534
+ if (char === "\n" || char === "\r") lineComment = false;
535
+ continue;
536
+ }
537
+ if (blockComment) {
538
+ if (char === "*" && next === "/") {
539
+ blockComment = false;
540
+ index += 1;
541
+ }
542
+ continue;
543
+ }
544
+ if (quote) {
545
+ if (char === "\\") {
546
+ index += 1;
547
+ continue;
548
+ }
549
+ if (char === quote) quote = null;
550
+ continue;
551
+ }
552
+ if (char === "/" && next === "/") {
553
+ lineComment = true;
554
+ index += 1;
555
+ continue;
556
+ }
557
+ if (char === "/" && next === "*") {
558
+ blockComment = true;
559
+ index += 1;
560
+ continue;
561
+ }
562
+ if (char === "\"" || char === "'") {
563
+ quote = char;
564
+ continue;
565
+ }
566
+ if (!isIdentifierStart(char)) continue;
567
+ const start = index;
568
+ index += 1;
569
+ while (isIdentifierPart(code[index])) index += 1;
570
+ const local = code.slice(start, index);
571
+ if (usedLocals.has(local)) found.add(local);
572
+ index -= 1;
573
+ }
574
+ return found;
575
+ };
576
+ const filterRemovableUsedLocals = (code, imports, usedLocals, usageEdits) => {
577
+ const ignoredRanges = [...imports.map((item) => item.declarationRange), ...usageEdits.flatMap((edit) => {
578
+ const range = editRange(edit);
579
+ return range ? [range] : [];
580
+ })];
581
+ const liveLocals = collectUsedLocalsOutsideRanges(code, usedLocals, ignoredRanges);
582
+ const removable = /* @__PURE__ */ new Set();
583
+ for (const local of usedLocals) if (!liveLocals.has(local)) removable.add(local);
584
+ return removable;
585
+ };
469
586
  const transformModule = (code, id, register, sources = DEFAULT_ICON_SOURCES, options = {}) => {
470
587
  const { sourceMap = false } = options;
471
588
  if (!fastFilter(code)) return {
@@ -492,17 +609,21 @@ const transformModule = (code, id, register, sources = DEFAULT_ICON_SOURCES, opt
492
609
  localName: ICON_COMPONENT_NAME
493
610
  };
494
611
  const used = /* @__PURE__ */ new Set();
612
+ const fontAwesomeComponents = hasPotentialFontAwesomeUsage ? scanFontAwesomeComponents(code) : /* @__PURE__ */ new Set();
613
+ const hugeiconsComponents = hasPotentialHugeiconsUsage ? scanHugeiconsComponents(code) : /* @__PURE__ */ new Set();
495
614
  const usedFontAwesomeComponents = /* @__PURE__ */ new Set();
496
615
  const usedHugeiconsComponents = /* @__PURE__ */ new Set();
616
+ const ignoredIconPropComponentLocals = /* @__PURE__ */ new Set([...fontAwesomeComponents, ...hugeiconsComponents]);
497
617
  const jsxScan = scanJsxIconEdits(code, table, spriteIconImport.localName, used, register, code.includes("iconId"));
498
- const fontAwesomeUsages = hasPotentialFontAwesomeUsage ? scanFontAwesomeUsages(code, table, scanFontAwesomeComponents(code)) : [];
499
- const hugeiconsUsages = hasPotentialHugeiconsUsage ? scanHugeiconsUsages(code, table, scanHugeiconsComponents(code)) : [];
500
- if (jsxScan.count === 0 && !fontAwesomeUsages.length && !hugeiconsUsages.length) return {
618
+ const referencePropScan = scanJsxReferencePropEdits(code, table, spriteIconImport.localName, used, register, ignoredIconPropComponentLocals);
619
+ const fontAwesomeUsages = hasPotentialFontAwesomeUsage ? scanFontAwesomeUsages(code, table, fontAwesomeComponents) : [];
620
+ const hugeiconsUsages = hasPotentialHugeiconsUsage ? scanHugeiconsUsages(code, table, hugeiconsComponents) : [];
621
+ if (jsxScan.count === 0 && referencePropScan.count === 0 && !fontAwesomeUsages.length && !hugeiconsUsages.length) return {
501
622
  code,
502
623
  map: null,
503
624
  anyReplacements: false
504
625
  };
505
- const edits = jsxScan.edits;
626
+ const edits = [...jsxScan.edits, ...referencePropScan.edits];
506
627
  const registeredFontAwesomeIcons = /* @__PURE__ */ new Set();
507
628
  for (const usage of fontAwesomeUsages) {
508
629
  edits.push({
@@ -555,10 +676,11 @@ const transformModule = (code, id, register, sources = DEFAULT_ICON_SOURCES, opt
555
676
  register(usage.pack, usage.exportName);
556
677
  }
557
678
  }
558
- const cleanupEdits = cleanupScannedImports(code, scannedImports, used);
679
+ const removableUsed = filterRemovableUsedLocals(code, scannedImports, used, edits);
680
+ const cleanupEdits = cleanupScannedImports(code, scannedImports, removableUsed);
559
681
  const cleanupFontAwesomeEdits = usedFontAwesomeComponents.size ? cleanupScannedFontAwesomeComponentImports(code, usedFontAwesomeComponents) : [];
560
682
  const cleanupHugeiconsEdits = usedHugeiconsComponents.size ? cleanupScannedHugeiconsComponentImports(code, usedHugeiconsComponents) : [];
561
- const canUsePresortedEdits = cleanupFontAwesomeEdits.length === 0 && cleanupHugeiconsEdits.length === 0;
683
+ const canUsePresortedEdits = referencePropScan.count === 0 && cleanupFontAwesomeEdits.length === 0 && cleanupHugeiconsEdits.length === 0;
562
684
  const allEdits = canUsePresortedEdits ? [...cleanupEdits, ...edits] : [
563
685
  ...edits,
564
686
  ...cleanupEdits,
@@ -1,7 +1,7 @@
1
1
  import { REACT_ICONS_SPRITE_URL_PLACEHOLDER } from "../index.mjs";
2
2
  import { a as createCollector, r as DEFAULT_ICON_SOURCES } from "../compute-icon-id-Xx4G3fva.mjs";
3
3
  import { t as buildSprite } from "../build-sprite-CxqvU2LE.mjs";
4
- import { t as transformModule } from "../transform-module-1am5uV8-.mjs";
4
+ import { t as transformModule } from "../transform-module-Chln1wen.mjs";
5
5
  import { createHash } from "node:crypto";
6
6
  //#region src/vite/plugin.ts
7
7
  const reactIconsSprite = (options = {}) => {
@@ -1,4 +1,4 @@
1
- import { t as transformModule } from "../transform-module-1am5uV8-.mjs";
1
+ import { t as transformModule } from "../transform-module-Chln1wen.mjs";
2
2
  import { t as collector } from "../collector-CBRz9R_h.mjs";
3
3
  //#region src/webpack/loader.ts
4
4
  const reactIconsSpriteLoader = async function(source) {
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.1.1",
4
+ "version": "1.2.0-rc.1",
5
5
  "type": "module",
6
6
  "description": "A lightweight Vite, Rsbuild and Webpack plugin for react-icons 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>",