react-icons-sprite 0.1.0 → 0.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.
package/dist/icon.js CHANGED
@@ -2,8 +2,7 @@ import { jsx } from "react/jsx-runtime";
2
2
 
3
3
  //#region src/icon.tsx
4
4
  const ReactIconsSpriteIcon = ({ iconId,...rest }) => {
5
- const spriteHref = "__SPRITE_URL_PLACEHOLDER__";
6
- const iconHref = `${spriteHref}#${iconId}`;
5
+ const iconHref = `__SPRITE_URL_PLACEHOLDER__#${iconId}`;
7
6
  return /* @__PURE__ */ jsx("svg", {
8
7
  height: "1em",
9
8
  width: "1em",
@@ -3,10 +3,11 @@ import { Plugin } from "vite";
3
3
  //#region src/vite/plugin.d.ts
4
4
  type ReactIconsSpriteVitePluginOptions = {
5
5
  /**
6
- * Append a cache-busting query parameter to the emitted sprite URL.
7
- * Example: { spriteUrlVersion: "1.2.3" } -> "/assets/react-icons-sprite.svg?v=1.2.3"
6
+ * If passed, this exact string will be used for the emitted file name.
7
+ * If fileName is omitted, name will be generated as `react-icons-sprite-[hash].svg.
8
+ * This is useful when, for example, multiple sprite sheets are generated during client and server builds.
8
9
  */
9
- spriteUrlVersion?: string;
10
+ fileName?: string;
10
11
  };
11
12
  declare const reactIconsSprite: (options?: ReactIconsSpriteVitePluginOptions) => Plugin;
12
13
  //#endregion
@@ -1,3 +1,4 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { createElement } from "react";
2
3
  import { renderToStaticMarkup } from "react-dom/server";
3
4
  import * as t from "@babel/types";
@@ -64,8 +65,7 @@ const replaceJsxWithSprite = (ast, localNameToImport, iconLocalName, register) =
64
65
  if (!meta) return;
65
66
  if (isAlreadyIcon(name)) return;
66
67
  path.node.name = t.jSXIdentifier(iconLocalName);
67
- const hasIconId = path.node.attributes.some((a) => t.isJSXAttribute(a) && t.isJSXIdentifier(a.name, { name: "iconId" }));
68
- if (!hasIconId) path.node.attributes.unshift(t.jSXAttribute(t.jSXIdentifier("iconId"), t.stringLiteral(`ri-${meta.exportName}`)));
68
+ if (!path.node.attributes.some((a) => t.isJSXAttribute(a) && t.isJSXIdentifier(a.name, { name: "iconId" }))) path.node.attributes.unshift(t.jSXAttribute(t.jSXIdentifier("iconId"), t.stringLiteral(`ri-${meta.exportName}`)));
69
69
  usedLocalNames.add(local);
70
70
  anyReplacements = true;
71
71
  register(meta.pack, meta.exportName);
@@ -147,11 +147,10 @@ const PRESENTATION_ATTRS = new Set([
147
147
  ]);
148
148
  const ATTR_RE = /([a-zA-Z_:.-]+)\s*=\s*"([^"]*)"/g;
149
149
  const renderOneIcon = async (pack, exportName) => {
150
- const mod = await import(
150
+ const Comp = (await import(
151
151
  /* @vite-ignore */
152
152
  pack
153
- );
154
- const Comp = mod[exportName];
153
+ ))[exportName];
155
154
  if (!Comp) throw new Error(`Icon export not found: ${pack} -> ${exportName}`);
156
155
  const id = `ri-${exportName}`;
157
156
  const html = renderToStaticMarkup(createElement(Comp));
@@ -163,17 +162,13 @@ const renderOneIcon = async (pack, exportName) => {
163
162
  if (PRESENTATION_ATTRS.has(key)) attrs.push(`${key}="${v}"`);
164
163
  }
165
164
  const inner = html.replace(/^<svg[^>]*>/i, "").replace(/<\/svg>\s*$/i, "");
166
- const stylePart = attrs.length ? ` ${attrs.join(" ")}` : "";
167
- const symbol = `<symbol id="${id}" viewBox="${viewBox}"${stylePart}>${inner}</symbol>`;
168
165
  return {
169
166
  id,
170
- symbol
167
+ symbol: `<symbol id="${id}" viewBox="${viewBox}"${attrs.length ? ` ${attrs.join(" ")}` : ""}>${inner}</symbol>`
171
168
  };
172
169
  };
173
170
  const buildSprite = async (icons) => {
174
- const rendered = await Promise.all(Array.from(icons).map(({ pack, exportName }) => renderOneIcon(pack, exportName)));
175
- const symbols = rendered.map((r) => r.symbol).join("");
176
- return `<svg xmlns="http://www.w3.org/2000/svg"><defs>${symbols}</defs></svg>`;
171
+ return `<svg xmlns="http://www.w3.org/2000/svg"><defs>${(await Promise.all(Array.from(icons).map(({ pack, exportName }) => renderOneIcon(pack, exportName)))).map((r) => r.symbol).join("")}</defs></svg>`;
177
172
  };
178
173
  const createCollector = () => {
179
174
  const set = /* @__PURE__ */ new Map();
@@ -196,7 +191,7 @@ const createCollector = () => {
196
191
  //#endregion
197
192
  //#region src/vite/plugin.ts
198
193
  const reactIconsSprite = (options = {}) => {
199
- const { spriteUrlVersion } = options;
194
+ const { fileName } = options;
200
195
  const collector = createCollector();
201
196
  return {
202
197
  name: "vite-plugin-react-icons-sprite",
@@ -225,13 +220,15 @@ const reactIconsSprite = (options = {}) => {
225
220
  },
226
221
  async generateBundle(_options, bundle) {
227
222
  const spriteXml = await buildSprite(collector.toList());
228
- const assetId = this.emitFile({
223
+ const generatedHash = createHash("sha256").update(spriteXml).digest("hex").slice(0, 8);
224
+ const emitFileOptions = {
229
225
  type: "asset",
230
- name: "react-icons-sprite.svg",
231
226
  source: spriteXml
232
- });
233
- const fileName = this.getFileName(assetId);
234
- const finalUrl = spriteUrlVersion && spriteUrlVersion.length > 0 ? `/${fileName}?v=${encodeURIComponent(spriteUrlVersion)}` : `/${fileName}`;
227
+ };
228
+ if (fileName) emitFileOptions.fileName = fileName;
229
+ else emitFileOptions.name = "react-icons-sprite.svg";
230
+ const assetId = this.emitFile(emitFileOptions);
231
+ const finalUrl = `/${this.getFileName(assetId)}?v=${encodeURIComponent(generatedHash)}`;
235
232
  for (const [, item] of Object.entries(bundle)) if (item.type === "chunk" && typeof item.code === "string") {
236
233
  if (item.code.includes(PLACEHOLDER)) item.code = item.code.replaceAll(PLACEHOLDER, finalUrl);
237
234
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-icons-sprite",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "",
@@ -49,20 +49,21 @@
49
49
  "react-icons": ">= 5"
50
50
  },
51
51
  "devDependencies": {
52
- "@babel/generator": "7.28.3",
53
- "@babel/parser": "7.28.3",
54
- "@babel/traverse": "7.28.3",
55
- "@babel/types": "7.28.2",
56
- "@biomejs/biome": "2.2.2",
52
+ "@babel/generator": "7.28.5",
53
+ "@babel/parser": "7.28.5",
54
+ "@babel/traverse": "7.28.5",
55
+ "@babel/types": "7.28.5",
56
+ "@biomejs/biome": "2.3.0",
57
57
  "@types/babel__generator": "7.27.0",
58
58
  "@types/babel__traverse": "7.28.0",
59
- "@types/react-dom": "19.1.9",
60
- "react": "19.1.1",
61
- "react-dom": "19.1.1",
59
+ "@types/node": "^24.9.1",
60
+ "@types/react-dom": "19.2.2",
61
+ "react": "19.2.0",
62
+ "react-dom": "19.2.0",
62
63
  "react-icons": "5.5.0",
63
- "tsdown": "0.14.2",
64
- "typescript": "5.9.2",
65
- "vite": "7.1.3"
64
+ "tsdown": "0.15.10",
65
+ "typescript": "5.9.3",
66
+ "vite": "7.1.12"
66
67
  },
67
68
  "keywords": [
68
69
  "vite",