react-icons-sprite 0.7.0 → 0.8.0-rc.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/collector-D3-6hdBe.mjs +5 -0
- package/dist/{core-TIimc90M.mjs → core-02H9qSgn.mjs} +80 -16
- package/dist/icon.mjs +15 -3
- package/dist/index.mjs +2 -4
- package/dist/vite/plugin.mjs +3 -5
- package/dist/webpack/loader.mjs +5 -7
- package/dist/webpack/plugin.d.mts +2 -2
- package/dist/webpack/plugin.mjs +5 -6
- package/package.json +31 -27
- package/LICENSE.md +0 -21
- package/README.md +0 -208
- package/dist/collector-BbjpnJS-.mjs +0 -7
- package/dist/icon-DB53krat.mjs +0 -17
|
@@ -4,7 +4,6 @@ import * as t from "@babel/types";
|
|
|
4
4
|
import { parse } from "@babel/parser";
|
|
5
5
|
import _traverse from "@babel/traverse";
|
|
6
6
|
import _generate from "@babel/generator";
|
|
7
|
-
|
|
8
7
|
//#region src/core.ts
|
|
9
8
|
const traverse = _traverse.default ?? _traverse;
|
|
10
9
|
const generate = _generate.default ?? _generate;
|
|
@@ -21,13 +20,11 @@ const DEFAULT_ICON_SOURCES = [
|
|
|
21
20
|
/^react-feather$/,
|
|
22
21
|
/^react-bootstrap-icons$/,
|
|
23
22
|
/^grommet-icons$/,
|
|
24
|
-
/^remixicon-react$/,
|
|
25
23
|
/^@remixicon\/react$/,
|
|
26
24
|
/^devicons-react$/,
|
|
27
25
|
/^@fortawesome\/react-fontawesome$/,
|
|
28
26
|
/^@fortawesome\/[\w-]+-svg-icons$/,
|
|
29
|
-
/^@mui\/icons-material
|
|
30
|
-
/^@iconscout\/react-unicons$/
|
|
27
|
+
/^@mui\/icons-material(?:\/.*)?$/
|
|
31
28
|
];
|
|
32
29
|
const sourceMatchesSupported = (source, sources = DEFAULT_ICON_SOURCES) => sources.some((re) => re.test(source));
|
|
33
30
|
const normalizeAlias = (pack) => {
|
|
@@ -56,6 +53,15 @@ const collectIconImports = (ast, sources = DEFAULT_ICON_SOURCES) => {
|
|
|
56
53
|
decl: node,
|
|
57
54
|
spec
|
|
58
55
|
});
|
|
56
|
+
} else if (t.isImportDefaultSpecifier(spec) && t.isIdentifier(spec.local)) {
|
|
57
|
+
const exportName = "default";
|
|
58
|
+
const localName = spec.local.name;
|
|
59
|
+
map.set(localName, {
|
|
60
|
+
pack,
|
|
61
|
+
exportName,
|
|
62
|
+
decl: node,
|
|
63
|
+
spec
|
|
64
|
+
});
|
|
59
65
|
}
|
|
60
66
|
}
|
|
61
67
|
return map;
|
|
@@ -63,8 +69,8 @@ const collectIconImports = (ast, sources = DEFAULT_ICON_SOURCES) => {
|
|
|
63
69
|
const findExistingIconImport = (ast) => {
|
|
64
70
|
let iconLocalName = ICON_COMPONENT_NAME;
|
|
65
71
|
let hasIconImport = false;
|
|
66
|
-
for (const n of ast.program.body) if (t.isImportDeclaration(n) && n.source.value ===
|
|
67
|
-
for (const s of n.specifiers) if (t.isImportSpecifier(s) && t.isIdentifier(s.imported, { name:
|
|
72
|
+
for (const n of ast.program.body) if (t.isImportDeclaration(n) && n.source.value === "react-icons-sprite") {
|
|
73
|
+
for (const s of n.specifiers) if (t.isImportSpecifier(s) && t.isIdentifier(s.imported, { name: "ReactIconsSpriteIcon" })) {
|
|
68
74
|
hasIconImport = true;
|
|
69
75
|
iconLocalName = t.isIdentifier(s.local) ? s.local.name : ICON_COMPONENT_NAME;
|
|
70
76
|
break;
|
|
@@ -88,14 +94,31 @@ const replaceJsxWithSprite = (ast, localNameToImport, iconLocalName, register) =
|
|
|
88
94
|
const meta = localNameToImport.get(local);
|
|
89
95
|
if (!meta) return;
|
|
90
96
|
if (isAlreadyIcon(name)) return;
|
|
97
|
+
let iconPack = meta.pack;
|
|
98
|
+
let iconExport = meta.exportName;
|
|
99
|
+
let usedLocal = local;
|
|
100
|
+
if (meta.pack === "@fortawesome/react-fontawesome" && meta.exportName === "FontAwesomeIcon") {
|
|
101
|
+
const iconAttr = path.node.attributes.find((a) => t.isJSXAttribute(a) && t.isJSXIdentifier(a.name, { name: "icon" }));
|
|
102
|
+
if (iconAttr && t.isJSXExpressionContainer(iconAttr.value) && t.isIdentifier(iconAttr.value.expression)) {
|
|
103
|
+
const iconLocalName = iconAttr.value.expression.name;
|
|
104
|
+
const iconMeta = localNameToImport.get(iconLocalName);
|
|
105
|
+
if (iconMeta) {
|
|
106
|
+
iconPack = iconMeta.pack;
|
|
107
|
+
iconExport = iconMeta.exportName;
|
|
108
|
+
usedLocal = iconLocalName;
|
|
109
|
+
path.node.attributes = path.node.attributes.filter((a) => a !== iconAttr);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
91
113
|
path.node.name = t.jSXIdentifier(iconLocalName);
|
|
92
114
|
if (!path.node.attributes.some((a) => t.isJSXAttribute(a) && t.isJSXIdentifier(a.name, { name: "iconId" }))) {
|
|
93
|
-
const idValue = computeIconId(
|
|
115
|
+
const idValue = computeIconId(iconPack, iconExport);
|
|
94
116
|
path.node.attributes.unshift(t.jSXAttribute(t.jSXIdentifier("iconId"), t.stringLiteral(idValue)));
|
|
95
117
|
}
|
|
96
118
|
usedLocalNames.add(local);
|
|
119
|
+
if (usedLocal !== local) usedLocalNames.add(usedLocal);
|
|
97
120
|
anyReplacements = true;
|
|
98
|
-
register(
|
|
121
|
+
register(iconPack, iconExport);
|
|
99
122
|
},
|
|
100
123
|
JSXClosingElement(path) {
|
|
101
124
|
const name = path.node.name;
|
|
@@ -118,8 +141,8 @@ const insertIconImport = (ast, iconLocalName = ICON_COMPONENT_NAME) => {
|
|
|
118
141
|
};
|
|
119
142
|
const pruneUsedSpecifiers = (ast, localNameToImport, usedLocalNames) => {
|
|
120
143
|
for (const { decl } of new Set([...localNameToImport.values()])) decl.specifiers = decl.specifiers.filter((s) => {
|
|
121
|
-
if (
|
|
122
|
-
return
|
|
144
|
+
if ((t.isImportSpecifier(s) || t.isImportDefaultSpecifier(s)) && t.isIdentifier(s.local)) return !usedLocalNames.has(s.local.name);
|
|
145
|
+
return true;
|
|
123
146
|
});
|
|
124
147
|
ast.program.body = ast.program.body.filter((n) => !t.isImportDeclaration(n) || n.specifiers.length > 0);
|
|
125
148
|
};
|
|
@@ -173,14 +196,56 @@ const PRESENTATION_ATTRS = new Set([
|
|
|
173
196
|
"vector-effect"
|
|
174
197
|
]);
|
|
175
198
|
const ATTR_RE = /([a-zA-Z_:.-]+)\s*=\s*"([^"]*)"/g;
|
|
199
|
+
const toKebab = (s) => s.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
|
|
200
|
+
const resolveSpecificImportPath = (pack, exportName) => {
|
|
201
|
+
if (/^@mui\/icons-material(?:\/.*)?$/.test(pack)) {
|
|
202
|
+
if (pack.split("/").length > 2) return pack;
|
|
203
|
+
return `${pack}/${exportName}`;
|
|
204
|
+
}
|
|
205
|
+
if (/^@radix-ui\/react-icons$/.test(pack)) return `${pack}/${exportName}`;
|
|
206
|
+
if (/^@heroicons\/react\/(?:\d{2})\/(?:outline|solid)$/.test(pack)) return `${pack}/${exportName}`;
|
|
207
|
+
if (/^@fortawesome\/[\w-]+-svg-icons$/.test(pack)) return `${pack}/${exportName}`;
|
|
208
|
+
if (/^lucide-react$/.test(pack)) return `${pack}/icons/${toKebab(exportName)}`;
|
|
209
|
+
if (/^@phosphor-icons\/react$/.test(pack)) return `${pack}/dist/ssr/${exportName}.es.js`;
|
|
210
|
+
if (/^phosphor-react$/.test(pack)) return `${pack}/dist/icons/${exportName}.esm.js`;
|
|
211
|
+
if (/^@tabler\/icons-react$/.test(pack)) return `${pack}/dist/esm/icons/${exportName}.mjs`;
|
|
212
|
+
if (/^react-feather$/.test(pack)) return `${pack}/dist/icons/${toKebab(exportName)}`;
|
|
213
|
+
if (/^react-bootstrap-icons$/.test(pack)) return `${pack}/dist/icons/${toKebab(exportName)}`;
|
|
214
|
+
return null;
|
|
215
|
+
};
|
|
176
216
|
const renderOneIcon = async (pack, exportName) => {
|
|
177
|
-
|
|
217
|
+
let mod;
|
|
218
|
+
const specificPath = resolveSpecificImportPath(pack, exportName);
|
|
219
|
+
if (specificPath) try {
|
|
220
|
+
mod = await import(
|
|
221
|
+
/* @vite-ignore */
|
|
222
|
+
specificPath
|
|
223
|
+
);
|
|
224
|
+
if (mod && "default" in mod && Object.keys(mod).length === 1) mod[exportName] = mod.default;
|
|
225
|
+
} catch {
|
|
226
|
+
mod = await import(
|
|
227
|
+
/* @vite-ignore */
|
|
228
|
+
pack
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
else mod = await import(
|
|
178
232
|
/* @vite-ignore */
|
|
179
233
|
pack
|
|
180
|
-
)
|
|
234
|
+
);
|
|
235
|
+
let Comp = mod[exportName] ?? mod.default;
|
|
236
|
+
if (pack.includes("fortawesome") && Comp && typeof Comp === "object" && "icon" in Comp && Array.isArray(Comp.icon)) {
|
|
237
|
+
const [width, height, , , pathData] = Comp.icon;
|
|
238
|
+
const viewBox = `0 0 ${width} ${height}`;
|
|
239
|
+
const id = computeIconId(pack, exportName);
|
|
240
|
+
return {
|
|
241
|
+
id,
|
|
242
|
+
symbol: `<symbol id="${id}" viewBox="${viewBox}">${(Array.isArray(pathData) ? pathData : [pathData]).map((d) => `<path d="${d}" />`).join("")}</symbol>`
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
if (Comp && typeof Comp === "object" && "default" in Comp && !("$$typeof" in Comp)) Comp = Comp.default;
|
|
181
246
|
if (!Comp) throw new Error(`Icon export not found: ${pack} -> ${exportName}`);
|
|
182
247
|
const id = computeIconId(pack, exportName);
|
|
183
|
-
const html = renderToStaticMarkup(createElement(Comp));
|
|
248
|
+
const html = renderToStaticMarkup(createElement(Comp, {}));
|
|
184
249
|
const viewBox = html.match(/viewBox="([^"]+)"/i)?.[1] ?? "0 0 24 24";
|
|
185
250
|
const svgAttrsRaw = html.match(/^<svg\b([^>]*)>/i)?.[1] ?? "";
|
|
186
251
|
const attrs = [];
|
|
@@ -188,7 +253,7 @@ const renderOneIcon = async (pack, exportName) => {
|
|
|
188
253
|
const key = k.toLowerCase();
|
|
189
254
|
if (PRESENTATION_ATTRS.has(key)) attrs.push(`${key}="${v}"`);
|
|
190
255
|
}
|
|
191
|
-
const inner = html.replace(/^<svg[^>]*>/i, "").replace(/<\/svg>\s*$/i, "");
|
|
256
|
+
const inner = html.replace(/^<svg[^>]*>/i, "").replace(/<\/svg>\s*$/i, "").replace(/<svg[^>]*>/gi, "").replace(/<\/svg>/gi, "");
|
|
192
257
|
return {
|
|
193
258
|
id,
|
|
194
259
|
symbol: `<symbol id="${id}" viewBox="${viewBox}"${attrs.length ? ` ${attrs.join(" ")}` : ""}>${inner}</symbol>`
|
|
@@ -214,6 +279,5 @@ const createCollector = () => {
|
|
|
214
279
|
}
|
|
215
280
|
};
|
|
216
281
|
};
|
|
217
|
-
|
|
218
282
|
//#endregion
|
|
219
|
-
export { transformModule as i, buildSprite as n, createCollector as r, DEFAULT_ICON_SOURCES as t };
|
|
283
|
+
export { transformModule as i, buildSprite as n, createCollector as r, DEFAULT_ICON_SOURCES as t };
|
package/dist/icon.mjs
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
//#region src/icon.tsx
|
|
3
|
+
const ReactIconsSpriteIcon = ({ iconId, ...rest }) => {
|
|
4
|
+
const iconHref = `__SPRITE_URL_PLACEHOLDER__#${iconId}`;
|
|
5
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
6
|
+
height: "1em",
|
|
7
|
+
width: "1em",
|
|
8
|
+
preserveAspectRatio: "xMidYMid meet",
|
|
9
|
+
viewBox: "0 0 24 24",
|
|
10
|
+
...rest,
|
|
11
|
+
children: /* @__PURE__ */ jsx("use", { href: iconHref })
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { ReactIconsSpriteIcon };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { ReactIconsSpriteIcon } from "./icon.mjs";
|
|
3
2
|
//#region src/index.ts
|
|
4
3
|
const REACT_ICONS_SPRITE_URL_PLACEHOLDER = "__SPRITE_URL_PLACEHOLDER__";
|
|
5
|
-
|
|
6
4
|
//#endregion
|
|
7
|
-
export { REACT_ICONS_SPRITE_URL_PLACEHOLDER, ReactIconsSpriteIcon };
|
|
5
|
+
export { REACT_ICONS_SPRITE_URL_PLACEHOLDER, ReactIconsSpriteIcon };
|
package/dist/vite/plugin.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { REACT_ICONS_SPRITE_URL_PLACEHOLDER } from "../index.mjs";
|
|
2
|
-
import { i as transformModule, n as buildSprite, r as createCollector, t as DEFAULT_ICON_SOURCES } from "../core-
|
|
2
|
+
import { i as transformModule, n as buildSprite, r as createCollector, t as DEFAULT_ICON_SOURCES } from "../core-02H9qSgn.mjs";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
|
-
|
|
5
4
|
//#region src/vite/plugin.ts
|
|
6
5
|
const reactIconsSprite = (options = {}) => {
|
|
7
6
|
const { fileName } = options;
|
|
@@ -41,11 +40,10 @@ const reactIconsSprite = (options = {}) => {
|
|
|
41
40
|
const assetId = this.emitFile(emitFileOptions);
|
|
42
41
|
const finalUrl = `/${this.getFileName(assetId)}`;
|
|
43
42
|
for (const [, item] of Object.entries(bundle)) if (item.type === "chunk" && typeof item.code === "string") {
|
|
44
|
-
if (item.code.includes(
|
|
43
|
+
if (item.code.includes("__SPRITE_URL_PLACEHOLDER__")) item.code = item.code.replaceAll(REACT_ICONS_SPRITE_URL_PLACEHOLDER, finalUrl);
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
46
|
};
|
|
48
47
|
};
|
|
49
|
-
|
|
50
48
|
//#endregion
|
|
51
|
-
export { reactIconsSprite };
|
|
49
|
+
export { reactIconsSprite };
|
package/dist/webpack/loader.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { i as transformModule } from "../core-
|
|
2
|
-
import { t as collector } from "../collector-
|
|
3
|
-
|
|
1
|
+
import { i as transformModule } from "../core-02H9qSgn.mjs";
|
|
2
|
+
import { t as collector } from "../collector-D3-6hdBe.mjs";
|
|
4
3
|
//#region src/webpack/loader.ts
|
|
5
4
|
const reactIconsSpriteLoader = async function(source) {
|
|
5
|
+
if (this.mode === "development") return source;
|
|
6
6
|
const id = this.resourcePath;
|
|
7
7
|
try {
|
|
8
|
-
if (!/\.(mjs|cjs|js|jsx|ts|tsx)$/i.test(id)
|
|
8
|
+
if (!/\.(mjs|cjs|js|jsx|ts|tsx)$/i.test(id)) return source;
|
|
9
9
|
const { code, anyReplacements } = transformModule(String(source), id, (pack, exportName) => {
|
|
10
10
|
collector.add(pack, exportName);
|
|
11
11
|
});
|
|
@@ -16,7 +16,5 @@ const reactIconsSpriteLoader = async function(source) {
|
|
|
16
16
|
return source;
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
|
-
var loader_default = reactIconsSpriteLoader;
|
|
20
|
-
|
|
21
19
|
//#endregion
|
|
22
|
-
export {
|
|
20
|
+
export { reactIconsSpriteLoader as default };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Compiler
|
|
1
|
+
import { Compiler } from "webpack";
|
|
2
2
|
|
|
3
3
|
//#region src/webpack/plugin.d.ts
|
|
4
4
|
type ReactIconsSpriteWebpackPluginOptions = {
|
|
@@ -9,7 +9,7 @@ type ReactIconsSpriteWebpackPluginOptions = {
|
|
|
9
9
|
*/
|
|
10
10
|
fileName?: string;
|
|
11
11
|
};
|
|
12
|
-
declare class ReactIconsSpriteWebpackPlugin
|
|
12
|
+
declare class ReactIconsSpriteWebpackPlugin {
|
|
13
13
|
private readonly fileName?;
|
|
14
14
|
constructor(options?: ReactIconsSpriteWebpackPluginOptions);
|
|
15
15
|
apply(compiler: Compiler): void;
|
package/dist/webpack/plugin.mjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { REACT_ICONS_SPRITE_URL_PLACEHOLDER } from "../index.mjs";
|
|
2
|
-
import { n as buildSprite } from "../core-
|
|
3
|
-
import { t as collector } from "../collector-
|
|
2
|
+
import { n as buildSprite } from "../core-02H9qSgn.mjs";
|
|
3
|
+
import { t as collector } from "../collector-D3-6hdBe.mjs";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
|
-
|
|
6
5
|
//#region src/webpack/plugin.ts
|
|
7
6
|
var ReactIconsSpriteWebpackPlugin = class {
|
|
8
7
|
fileName;
|
|
@@ -10,6 +9,7 @@ var ReactIconsSpriteWebpackPlugin = class {
|
|
|
10
9
|
this.fileName = options.fileName;
|
|
11
10
|
}
|
|
12
11
|
apply(compiler) {
|
|
12
|
+
if (compiler.options.mode === "development") return;
|
|
13
13
|
const pluginName = "react-icons-sprite-webpack-plugin";
|
|
14
14
|
compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
|
|
15
15
|
collector.clear();
|
|
@@ -34,7 +34,7 @@ var ReactIconsSpriteWebpackPlugin = class {
|
|
|
34
34
|
if (!/\.(js|mjs|cjs)$/i.test(filename)) continue;
|
|
35
35
|
const src = asset.source.source();
|
|
36
36
|
if (typeof src !== "string") continue;
|
|
37
|
-
if (!src.includes(
|
|
37
|
+
if (!src.includes("__SPRITE_URL_PLACEHOLDER__")) continue;
|
|
38
38
|
const next = src.replaceAll(REACT_ICONS_SPRITE_URL_PLACEHOLDER, finalUrl);
|
|
39
39
|
compilation.updateAsset(filename, new RawSource(next));
|
|
40
40
|
}
|
|
@@ -42,6 +42,5 @@ var ReactIconsSpriteWebpackPlugin = class {
|
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
|
-
|
|
46
45
|
//#endregion
|
|
47
|
-
export { ReactIconsSpriteWebpackPlugin };
|
|
46
|
+
export { ReactIconsSpriteWebpackPlugin };
|
package/package.json
CHANGED
|
@@ -1,29 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-icons-sprite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0-rc.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "",
|
|
7
7
|
"author": "Jure Rotar <hello@jurerotar.com>",
|
|
8
8
|
"homepage": "https://github.com/jurerotar/react-icons-sprite#README",
|
|
9
|
-
"main": "dist/
|
|
10
|
-
"
|
|
9
|
+
"main": "dist/index.mjs",
|
|
10
|
+
"module": "dist/index.mjs",
|
|
11
|
+
"types": "dist/index.d.mts",
|
|
11
12
|
"sideEffects": false,
|
|
12
13
|
"exports": {
|
|
13
14
|
".": {
|
|
14
15
|
"types": "./dist/index.d.mts",
|
|
15
|
-
"import": "./dist/index.mjs"
|
|
16
|
-
|
|
16
|
+
"import": "./dist/index.mjs"
|
|
17
|
+
},
|
|
18
|
+
"./icon": {
|
|
19
|
+
"types": "./dist/icon.d.mts",
|
|
20
|
+
"import": "./dist/icon.mjs"
|
|
17
21
|
},
|
|
18
22
|
"./vite": {
|
|
19
23
|
"types": "./dist/vite/plugin.d.mts",
|
|
20
|
-
"import": "./dist/vite/plugin.mjs"
|
|
21
|
-
"default": "./dist/vite/plugin.mjs"
|
|
24
|
+
"import": "./dist/vite/plugin.mjs"
|
|
22
25
|
},
|
|
23
26
|
"./webpack": {
|
|
24
27
|
"types": "./dist/webpack/plugin.d.mts",
|
|
25
|
-
"import": "./dist/webpack/plugin.mjs"
|
|
26
|
-
"default": "./dist/webpack/plugin.mjs"
|
|
28
|
+
"import": "./dist/webpack/plugin.mjs"
|
|
27
29
|
},
|
|
28
30
|
"./webpack/loader": {
|
|
29
31
|
"types": "./dist/webpack/loader.d.mts",
|
|
@@ -47,10 +49,10 @@
|
|
|
47
49
|
"scripts": {
|
|
48
50
|
"build": "tsdown",
|
|
49
51
|
"dev": "tsdown --watch",
|
|
50
|
-
"format": "biome format --no-errors-on-unmatched",
|
|
51
|
-
"format:
|
|
52
|
-
"lint": "biome lint --no-errors-on-unmatched",
|
|
53
|
-
"lint:
|
|
52
|
+
"format": "biome format --write --no-errors-on-unmatched",
|
|
53
|
+
"format:check": "biome format --no-errors-on-unmatched",
|
|
54
|
+
"lint": "biome lint --write --no-errors-on-unmatched",
|
|
55
|
+
"lint:check": "biome lint --no-errors-on-unmatched",
|
|
54
56
|
"type-check": "tsgo",
|
|
55
57
|
"test": "vitest run",
|
|
56
58
|
"prepublishOnly": "npm run build",
|
|
@@ -58,34 +60,36 @@
|
|
|
58
60
|
},
|
|
59
61
|
"peerDependencies": {
|
|
60
62
|
"react": ">= 16",
|
|
61
|
-
"react-dom": ">= 16"
|
|
62
|
-
|
|
63
|
+
"react-dom": ">= 16"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@babel/generator": "7.29.1",
|
|
67
|
+
"@babel/parser": "7.29.0",
|
|
68
|
+
"@babel/traverse": "7.29.0",
|
|
69
|
+
"@babel/types": "7.29.0"
|
|
63
70
|
},
|
|
64
71
|
"devDependencies": {
|
|
65
|
-
"@babel/generator": "7.28.6",
|
|
66
|
-
"@babel/parser": "7.28.6",
|
|
67
|
-
"@babel/traverse": "7.28.6",
|
|
68
|
-
"@babel/types": "7.28.6",
|
|
69
|
-
"@biomejs/biome": "2.3.13",
|
|
70
72
|
"@types/babel__generator": "7.27.0",
|
|
71
73
|
"@types/babel__traverse": "7.28.0",
|
|
72
|
-
"@types/node": "25.
|
|
74
|
+
"@types/node": "25.3.5",
|
|
73
75
|
"@types/react-dom": "19.2.3",
|
|
74
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
75
|
-
"react": "19.2.
|
|
76
|
-
"react-dom": "19.2.
|
|
77
|
-
"
|
|
78
|
-
"tsdown": "0.20.1",
|
|
76
|
+
"@typescript/native-preview": "7.0.0-dev.20260309.1",
|
|
77
|
+
"react": "19.2.4",
|
|
78
|
+
"react-dom": "19.2.4",
|
|
79
|
+
"tsdown": "0.21.1",
|
|
79
80
|
"typescript": "5.9.3",
|
|
80
81
|
"vite": "7.3.1",
|
|
81
82
|
"vitest": "4.0.18",
|
|
82
|
-
"webpack": "5.
|
|
83
|
+
"webpack": "5.105.4"
|
|
83
84
|
},
|
|
84
85
|
"keywords": [
|
|
85
86
|
"vite",
|
|
86
87
|
"vite-plugin",
|
|
87
88
|
"webpack",
|
|
88
89
|
"webpack-plugin",
|
|
90
|
+
"rsbuild",
|
|
91
|
+
"rsbuild-plugin",
|
|
92
|
+
"esbuild",
|
|
89
93
|
"rollup",
|
|
90
94
|
"rolldown",
|
|
91
95
|
"react-icons",
|
package/LICENSE.md
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
The MIT License (MIT)
|
|
2
|
-
|
|
3
|
-
Copyright © 2025 Jure Rotar
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE
|
package/README.md
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
# react-icons-sprite
|
|
2
|
-
|
|
3
|
-
`react-icons-sprite` is a lightweight plugin for Vite and Webpack that turns React icon components into a single SVG spritesheet and rewrites your code to reference those symbols via `<use>`.
|
|
4
|
-
|
|
5
|
-
It supports multiple React icon packages that export icons as individual React components. This approach both shrinks your bundle (no more inlined React components for every icon) and reduces runtime overhead, since React no longer has to reconcile large, nested SVG trees.
|
|
6
|
-
|
|
7
|
-
## Supported icon libraries
|
|
8
|
-
|
|
9
|
-
Out of the box, imports from the following libraries are detected and transformed:
|
|
10
|
-
|
|
11
|
-
- `react-icons/*` packs (e.g. `react-icons/bi`, `react-icons/fa`, ...)
|
|
12
|
-
- `lucide-react`
|
|
13
|
-
- `@radix-ui/react-icons`
|
|
14
|
-
- `@heroicons/react` (v1 and v2 subpaths)
|
|
15
|
-
- `@tabler/icons-react`
|
|
16
|
-
- `phosphor-react`
|
|
17
|
-
- `@phosphor-icons/react`
|
|
18
|
-
- `react-feather`
|
|
19
|
-
- `react-bootstrap-icons`
|
|
20
|
-
- `grommet-icons`
|
|
21
|
-
- `remixicon-react`
|
|
22
|
-
- `@remixicon/react`
|
|
23
|
-
- `devicons-react`
|
|
24
|
-
- `@fortawesome/free-solid-svg-icons` (and other Font Awesome icon packs)
|
|
25
|
-
- `@fortawesome/react-fontawesome`
|
|
26
|
-
- `@mui/icons-material`
|
|
27
|
-
- `@iconscout/react-unicons`
|
|
28
|
-
|
|
29
|
-
## Motivation
|
|
30
|
-
|
|
31
|
-
By default, when you use an icon library like `react-icons`, each icon is a React component. For example:
|
|
32
|
-
|
|
33
|
-
```tsx
|
|
34
|
-
import { LuWheat } from "react-icons/lu";
|
|
35
|
-
|
|
36
|
-
export function Example() {
|
|
37
|
-
return <LuWheat />;
|
|
38
|
-
}
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
looks harmless, but at build time this compiles to something like:
|
|
42
|
-
|
|
43
|
-
```javascript
|
|
44
|
-
import { b as e } from './iconBase-BU3rGdXB.js'
|
|
45
|
-
|
|
46
|
-
function t(t) {
|
|
47
|
-
return e({
|
|
48
|
-
tag: `svg`, attr: { viewBox: `0 0 640 512` }, child: [{
|
|
49
|
-
tag: `path`, attr: {
|
|
50
|
-
d: `M524.531,69.836a1.5,1.5,0,0,0-.764-.7A485.065,485.065,0,0,0,404.081,32.03a1.816,1.816,0,0,0-1.923.91,337.461,337.461,0,0,0-14.9,30.6,447.848,447.848,0,0,0-134.426,0,309.541,309.541,0,0,0-15.135-30.6,1.89,1.89,0,0,0-1.924-.91A483.689,483.689,0,0,0,116.085,69.137a1.712,1.712,0,0,0-.788.676C39.068,183.651,18.186,294.69,28.43,404.354a2.016,2.016,0,0,0,.765,1.375A487.666,487.666,0,0,0,176.02,479.918a1.9,1.9,0,0,0,2.063-.676A348.2,348.2,0,0,0,208.12,430.4a1.86,1.86,0,0,0-1.019-2.588,321.173,321.173,0,0,1-45.868-21.853,1.885,1.885,0,0,1-.185-3.126c3.082-2.309,6.166-4.711,9.109-7.137a1.819,1.819,0,0,1,1.9-.256c96.229,43.917,200.41,43.917,295.5,0a1.812,1.812,0,0,1,1.924.233c2.944,2.426,6.027,4.851,9.132,7.16a1.884,1.884,0,0,1-.162,3.126,301.407,301.407,0,0,1-45.89,21.83,1.875,1.875,0,0,0-1,2.611,391.055,391.055,0,0,0,30.014,48.815,1.864,1.864,0,0,0,2.063.7A486.048,486.048,0,0,0,610.7,405.729a1.882,1.882,0,0,0,.765-1.352C623.729,277.594,590.933,167.465,524.531,69.836ZM222.491,337.58c-28.972,0-52.844-26.587-52.844-59.239S193.056,219.1,222.491,219.1c29.665,0,53.306,26.82,52.843,59.239C275.334,310.993,251.924,337.58,222.491,337.58Zm195.38,0c-28.971,0-52.843-26.587-52.843-59.239S388.437,219.1,417.871,219.1c29.667,0,53.307,26.82,52.844,59.239C470.715,310.993,447.538,337.58,417.871,337.58Z`
|
|
51
|
-
}, child: []
|
|
52
|
-
}]
|
|
53
|
-
})(t)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function n(t) {
|
|
57
|
-
return e({
|
|
58
|
-
tag: `svg`, attr: { viewBox: `0 0 496 512` }, child: [{
|
|
59
|
-
tag: `path`, attr: {
|
|
60
|
-
d: `M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z`
|
|
61
|
-
}, child: []
|
|
62
|
-
}]
|
|
63
|
-
})(t)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ... potentially dozens more child components.
|
|
67
|
-
|
|
68
|
-
export { t, n };
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
That means:
|
|
72
|
-
|
|
73
|
-
- Every icon adds hundreds of characters of JSX to your bundle.
|
|
74
|
-
- At runtime, React must create and diff dozens of DOM nodes for every icon render.
|
|
75
|
-
- If you render 100 icons, React is reconciling potentially thousands of <path> elements.
|
|
76
|
-
|
|
77
|
-
Now compare that to output when using a spritesheet with `<use>`:
|
|
78
|
-
|
|
79
|
-
```tsx
|
|
80
|
-
import { LuWheat } from "react-icons/lu";
|
|
81
|
-
|
|
82
|
-
export function Example() {
|
|
83
|
-
return <LuWheat />;
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
```javascript
|
|
88
|
-
import { b as n } from './icon-FONPSuqX.js';
|
|
89
|
-
|
|
90
|
-
var r = e(t());
|
|
91
|
-
const i = () => (0, r.jsx)(n, { iconId: `ri-react-icons-lu-LuWheat` });
|
|
92
|
-
export { i as IconWheat };
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
`icon-FONPSuqX.js` in this case is our simple icon wrapper, which looks like this:
|
|
96
|
-
|
|
97
|
-
```javascript
|
|
98
|
-
var n = e(t());
|
|
99
|
-
const r = ({ iconId: e, ...t }) => {
|
|
100
|
-
let r = `assets/react-icons-sprite-C-JClopV.svg#${e}`;
|
|
101
|
-
return (0, n.jsxs)(`svg`, {
|
|
102
|
-
height: `1em`,
|
|
103
|
-
width: `1em`,
|
|
104
|
-
preserveAspectRatio: `xMidYMid meet`,
|
|
105
|
-
viewBox: `0 0 24 24`, ...t,
|
|
106
|
-
children: [(0, n.jsx)(`title`, { children: e }), (0, n.jsx)(`use`, { href: r })]
|
|
107
|
-
})
|
|
108
|
-
};
|
|
109
|
-
export { r as b };
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
Runtime difference:
|
|
113
|
-
|
|
114
|
-
- React only reconciles two DOM nodes (`<svg>` + `<use>`).
|
|
115
|
-
- All heavy path data lives in the static spritesheet once, not duplicated across components.
|
|
116
|
-
- Updating 100 icons is as cheap as updating 100 `<use>` tags.
|
|
117
|
-
|
|
118
|
-
This is a big win when you’re rendering icons in lists, tables, or maps where dozens or hundreds of them appear at once.
|
|
119
|
-
|
|
120
|
-
### Performance comparison
|
|
121
|
-
|
|
122
|
-
| icon (pack) | react-icons icon render mean time | react-icons-sprite icon render mean time | Relative difference |
|
|
123
|
-
|--------------------|----------------------------------:|---------------------------------------------:|------------------------:|
|
|
124
|
-
| **FiCpu** (fi) | 0.188 ms | 0.048 ms | 74.6% reduction |
|
|
125
|
-
| **MdBuild** (md) | 0.198 ms | 0.048 ms | 76.0% reduction |
|
|
126
|
-
| **FaCamera** (fa) | 0.162 ms | 0.015 ms | 90.7% reduction |
|
|
127
|
-
| **IoAperture** (io5)| 0.029 ms | 0.015 ms | 49.7% reduction |
|
|
128
|
-
| **BiBell** (bi) | 0.023 ms | 0.014 ms | 38.5% reduction |
|
|
129
|
-
| **AiOutlineAlert** (ai) | 0.023 ms | 0.014 ms | 38.3% reduction |
|
|
130
|
-
| **BsAlarm** (bs) | 0.027 ms | 0.014 ms | 47.4% reduction |
|
|
131
|
-
| **RiAnchor** (ri) | 0.023 ms | 0.014 ms | 38.6% reduction |
|
|
132
|
-
| **CgArrows** (cg) | 0.029 ms | 0.014 ms | 52.0% reduction |
|
|
133
|
-
| **HiAcademicCap** (hi) | 0.023 ms | 0.014 ms | 38.6% reduction |
|
|
134
|
-
| **SiTypescript** (si) | 0.023 ms | 0.014 ms | 39.7% reduction |
|
|
135
|
-
| **TiThLarge** (ti) | 0.023 ms | 0.014 ms | 40.0% reduction |
|
|
136
|
-
|
|
137
|
-
* **Test details / machine:** **Lenovo Legion 5 Pro 16ACH6H** (Ryzen 7 5800H — 8 cores / 16 threads, base ≈ 3.2 GHz, turbo ≈ 4.4 GHz, DDR4-3200 memory); Node.js v24.10.0.
|
|
138
|
-
* Differences will vary based on icons used in your application, but they will generally be the range of 50-75% reduction in render time. Larger icons will generate a larger difference.
|
|
139
|
-
|
|
140
|
-
## Installation
|
|
141
|
-
|
|
142
|
-
Install the plugin via npm or yarn:
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
npm install --save-dev react-icons-sprite
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### Vite
|
|
149
|
-
Add the plugin to the `plugins` array in your Vite config.
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
// vite.config.ts
|
|
153
|
-
import { defineConfig } from 'vite';
|
|
154
|
-
import { reactIconsSprite } from 'react-icons-sprite/vite';
|
|
155
|
-
|
|
156
|
-
export default defineConfig({
|
|
157
|
-
plugins: [reactIconsSprite()],
|
|
158
|
-
});
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### Webpack
|
|
162
|
-
Add the loader to transform modules that import icons and install the plugin to emit the sprite and rewrite the placeholder URL.
|
|
163
|
-
|
|
164
|
-
```js
|
|
165
|
-
// webpack.config.js (v5)
|
|
166
|
-
const path = require('path');
|
|
167
|
-
const { reactIconsSprite } = require('react-icons-sprite/webpack');
|
|
168
|
-
|
|
169
|
-
module.exports = {
|
|
170
|
-
mode: 'production',
|
|
171
|
-
// ... your existing config
|
|
172
|
-
module: {
|
|
173
|
-
rules: [
|
|
174
|
-
{
|
|
175
|
-
test: /\.(mjs|cjs|js|jsx|ts|tsx)$/,
|
|
176
|
-
exclude: /node_modules/,
|
|
177
|
-
use: [
|
|
178
|
-
{
|
|
179
|
-
loader: require.resolve('react-icons-sprite/webpack/loader'),
|
|
180
|
-
},
|
|
181
|
-
// put your ts/tsx loader after ours (e.g. babel-loader or ts-loader)
|
|
182
|
-
],
|
|
183
|
-
},
|
|
184
|
-
],
|
|
185
|
-
},
|
|
186
|
-
plugins: [
|
|
187
|
-
reactIconsSprite({
|
|
188
|
-
// optional: fileName: 'icons.svg'
|
|
189
|
-
}),
|
|
190
|
-
],
|
|
191
|
-
};
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
## How it works
|
|
195
|
-
|
|
196
|
-
In **development mode**, the plugin does nothing special. Icons are rendered as they normally would from your icon library. This keeps hot module replacement (HMR) snappy — there’s no extra parsing of the codebase or regenerating of the sprite on every save. If the plugin were to build the sprite during dev, it would need to constantly scan for icon imports and rebuild the sheet, which is expensive and slows down iteration. So, in dev, you get the normal component behavior.
|
|
197
|
-
|
|
198
|
-
In **build mode**, the plugin transforms your code. It parses each module, looks for imports from supported React icon packages, and rewrites the JSX. Instead of rendering full inline `<svg>` trees, it replaces them with `<ReactIconsSpriteIcon iconId="..." />`. While doing this, it collects every unique icon used across the project. After the bundling step, the plugin renders all those icons once to static markup and generates a single SVG file containing `<symbol>` definitions for each one. Finally, it rewrites your bundle to point every `<ReactIconsSpriteIcon>` at that spritesheet using a `<use>` tag.
|
|
199
|
-
|
|
200
|
-
The result: during development you keep fast feedback loops, and in production you ship a single optimized sprite file with lightweight `<use>` references.
|
|
201
|
-
|
|
202
|
-
## Contributing
|
|
203
|
-
|
|
204
|
-
Contributions are welcome! Feel free to open an issue or submit a pull request.
|
|
205
|
-
|
|
206
|
-
## License
|
|
207
|
-
|
|
208
|
-
This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for details.
|
package/dist/icon-DB53krat.mjs
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
//#region src/icon.tsx
|
|
4
|
-
const ReactIconsSpriteIcon = ({ iconId, ...rest }) => {
|
|
5
|
-
const iconHref = `__SPRITE_URL_PLACEHOLDER__#${iconId}`;
|
|
6
|
-
return /* @__PURE__ */ jsx("svg", {
|
|
7
|
-
height: "1em",
|
|
8
|
-
width: "1em",
|
|
9
|
-
preserveAspectRatio: "xMidYMid meet",
|
|
10
|
-
viewBox: "0 0 24 24",
|
|
11
|
-
...rest,
|
|
12
|
-
children: /* @__PURE__ */ jsx("use", { href: iconHref })
|
|
13
|
-
});
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
//#endregion
|
|
17
|
-
export { ReactIconsSpriteIcon as t };
|