react-icons-sprite 0.4.0 → 0.6.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/README.md +46 -10
- package/dist/collector-DrPR8kXO.mjs +7 -0
- package/dist/{core-CcX_8jJU.mjs → core-BoovxbBC.mjs} +32 -8
- package/dist/icon.mjs +1 -1
- package/dist/index.d.mts +4 -0
- package/dist/index.mjs +3 -0
- package/dist/src-Bf4uZBwp.mjs +5 -0
- package/dist/vite/plugin.mjs +7 -8
- package/dist/webpack/loader.mjs +2 -2
- package/dist/webpack/plugin.d.mts +1 -1
- package/dist/webpack/plugin.mjs +7 -6
- package/package.json +19 -19
- package/dist/collector-CbA7qCnf.mjs +0 -7
package/README.md
CHANGED
|
@@ -1,10 +1,30 @@
|
|
|
1
1
|
# react-icons-sprite
|
|
2
2
|
|
|
3
|
-
`react-icons-sprite` is a lightweight plugin for Vite and Webpack
|
|
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`
|
|
4
24
|
|
|
5
25
|
## Motivation
|
|
6
26
|
|
|
7
|
-
By default, when you use `react-icons`, each icon is a React component. For example:
|
|
27
|
+
By default, when you use an icon library like `react-icons`, each icon is a React component. For example:
|
|
8
28
|
|
|
9
29
|
```tsx
|
|
10
30
|
import { LuWheat } from "react-icons/lu";
|
|
@@ -64,7 +84,7 @@ export function Example() {
|
|
|
64
84
|
import { b as n } from './icon-FONPSuqX.js';
|
|
65
85
|
|
|
66
86
|
var r = e(t());
|
|
67
|
-
const i = () => (0, r.jsx)(n, { iconId: `ri-LuWheat` });
|
|
87
|
+
const i = () => (0, r.jsx)(n, { iconId: `ri-react-icons-lu-LuWheat` });
|
|
68
88
|
export { i as IconWheat };
|
|
69
89
|
```
|
|
70
90
|
|
|
@@ -93,6 +113,26 @@ Runtime difference:
|
|
|
93
113
|
|
|
94
114
|
This is a big win when you’re rendering icons in lists, tables, or maps where dozens or hundreds of them appear at once.
|
|
95
115
|
|
|
116
|
+
### Performance comparison
|
|
117
|
+
|
|
118
|
+
| icon (pack) | react-icons icon render mean time | react-icons-sprite icon render mean time | Relative difference |
|
|
119
|
+
|--------------------|----------------------------------:|---------------------------------------------:|------------------------:|
|
|
120
|
+
| **FiCpu** (fi) | 0.188 ms | 0.048 ms | 74.6% reduction |
|
|
121
|
+
| **MdBuild** (md) | 0.198 ms | 0.048 ms | 76.0% reduction |
|
|
122
|
+
| **FaCamera** (fa) | 0.162 ms | 0.015 ms | 90.7% reduction |
|
|
123
|
+
| **IoAperture** (io5)| 0.029 ms | 0.015 ms | 49.7% reduction |
|
|
124
|
+
| **BiBell** (bi) | 0.023 ms | 0.014 ms | 38.5% reduction |
|
|
125
|
+
| **AiOutlineAlert** (ai) | 0.023 ms | 0.014 ms | 38.3% reduction |
|
|
126
|
+
| **BsAlarm** (bs) | 0.027 ms | 0.014 ms | 47.4% reduction |
|
|
127
|
+
| **RiAnchor** (ri) | 0.023 ms | 0.014 ms | 38.6% reduction |
|
|
128
|
+
| **CgArrows** (cg) | 0.029 ms | 0.014 ms | 52.0% reduction |
|
|
129
|
+
| **HiAcademicCap** (hi) | 0.023 ms | 0.014 ms | 38.6% reduction |
|
|
130
|
+
| **SiTypescript** (si) | 0.023 ms | 0.014 ms | 39.7% reduction |
|
|
131
|
+
| **TiThLarge** (ti) | 0.023 ms | 0.014 ms | 40.0% reduction |
|
|
132
|
+
|
|
133
|
+
* **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.
|
|
134
|
+
* 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.
|
|
135
|
+
|
|
96
136
|
## Installation
|
|
97
137
|
|
|
98
138
|
Install the plugin via npm or yarn:
|
|
@@ -115,7 +155,7 @@ export default defineConfig({
|
|
|
115
155
|
```
|
|
116
156
|
|
|
117
157
|
### Webpack
|
|
118
|
-
Add the loader to transform modules that import
|
|
158
|
+
Add the loader to transform modules that import icons and install the plugin to emit the sprite and rewrite the placeholder URL.
|
|
119
159
|
|
|
120
160
|
```js
|
|
121
161
|
// webpack.config.js (v5)
|
|
@@ -144,18 +184,14 @@ module.exports = {
|
|
|
144
184
|
// optional: fileName: 'icons.svg'
|
|
145
185
|
}),
|
|
146
186
|
],
|
|
147
|
-
output: {
|
|
148
|
-
// ensure your publicPath is set correctly if you deploy under a sub-path
|
|
149
|
-
// publicPath: '/',
|
|
150
|
-
},
|
|
151
187
|
};
|
|
152
188
|
```
|
|
153
189
|
|
|
154
190
|
## How it works
|
|
155
191
|
|
|
156
|
-
In **development mode**, the plugin does nothing special. Icons are rendered as they normally would from
|
|
192
|
+
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.
|
|
157
193
|
|
|
158
|
-
In **build mode**, the plugin transforms your code. It parses each module, looks for imports from
|
|
194
|
+
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.
|
|
159
195
|
|
|
160
196
|
The result: during development you keep fast feedback loops, and in production you ship a single optimized sprite file with lightweight `<use>` references.
|
|
161
197
|
|
|
@@ -8,9 +8,30 @@ import _generate from "@babel/generator";
|
|
|
8
8
|
//#region src/core.ts
|
|
9
9
|
const traverse = _traverse.default ?? _traverse;
|
|
10
10
|
const generate = _generate.default ?? _generate;
|
|
11
|
-
const PLACEHOLDER = "__SPRITE_URL_PLACEHOLDER__";
|
|
12
11
|
const ICON_SOURCE = "react-icons-sprite";
|
|
13
12
|
const ICON_COMPONENT_NAME = "ReactIconsSpriteIcon";
|
|
13
|
+
const DEFAULT_ICON_SOURCES = [
|
|
14
|
+
/^react-icons\/[\w-]+$/,
|
|
15
|
+
/^lucide-react$/,
|
|
16
|
+
/^@radix-ui\/react-icons$/,
|
|
17
|
+
/^@heroicons\/react(?:\/.*)?$/,
|
|
18
|
+
/^@tabler\/icons-react$/,
|
|
19
|
+
/^phosphor-react$/,
|
|
20
|
+
/^@phosphor-icons\/react$/,
|
|
21
|
+
/^react-feather$/,
|
|
22
|
+
/^react-bootstrap-icons$/,
|
|
23
|
+
/^grommet-icons$/,
|
|
24
|
+
/^remixicon-react$/,
|
|
25
|
+
/^@remixicon\/react$/,
|
|
26
|
+
/^devicons-react$/
|
|
27
|
+
];
|
|
28
|
+
const sourceMatchesSupported = (source, sources = DEFAULT_ICON_SOURCES) => sources.some((re) => re.test(source));
|
|
29
|
+
const normalizeAlias = (pack) => {
|
|
30
|
+
return pack.replace(/^@/, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
31
|
+
};
|
|
32
|
+
const computeIconId = (pack, exportName) => {
|
|
33
|
+
return `ri-${normalizeAlias(pack)}-${exportName}`;
|
|
34
|
+
};
|
|
14
35
|
const parseAst = (code, filename = "module.tsx") => {
|
|
15
36
|
return parse(code, {
|
|
16
37
|
sourceType: "module",
|
|
@@ -18,9 +39,9 @@ const parseAst = (code, filename = "module.tsx") => {
|
|
|
18
39
|
sourceFilename: filename
|
|
19
40
|
});
|
|
20
41
|
};
|
|
21
|
-
const
|
|
42
|
+
const collectIconImports = (ast, sources = DEFAULT_ICON_SOURCES) => {
|
|
22
43
|
const map = /* @__PURE__ */ new Map();
|
|
23
|
-
for (const node of ast.program.body) if (t.isImportDeclaration(node) &&
|
|
44
|
+
for (const node of ast.program.body) if (t.isImportDeclaration(node) && sourceMatchesSupported(node.source.value, sources) && node.importKind !== "type") {
|
|
24
45
|
const pack = node.source.value;
|
|
25
46
|
for (const spec of node.specifiers) if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported) && t.isIdentifier(spec.local) && spec.importKind !== "type") {
|
|
26
47
|
const exportName = spec.imported.name;
|
|
@@ -64,7 +85,10 @@ const replaceJsxWithSprite = (ast, localNameToImport, iconLocalName, register) =
|
|
|
64
85
|
if (!meta) return;
|
|
65
86
|
if (isAlreadyIcon(name)) return;
|
|
66
87
|
path.node.name = t.jSXIdentifier(iconLocalName);
|
|
67
|
-
if (!path.node.attributes.some((a) => t.isJSXAttribute(a) && t.isJSXIdentifier(a.name, { name: "iconId" })))
|
|
88
|
+
if (!path.node.attributes.some((a) => t.isJSXAttribute(a) && t.isJSXIdentifier(a.name, { name: "iconId" }))) {
|
|
89
|
+
const idValue = computeIconId(meta.pack, meta.exportName);
|
|
90
|
+
path.node.attributes.unshift(t.jSXAttribute(t.jSXIdentifier("iconId"), t.stringLiteral(idValue)));
|
|
91
|
+
}
|
|
68
92
|
usedLocalNames.add(local);
|
|
69
93
|
anyReplacements = true;
|
|
70
94
|
register(meta.pack, meta.exportName);
|
|
@@ -105,9 +129,9 @@ const generateCode = (ast, origCode, id) => {
|
|
|
105
129
|
map
|
|
106
130
|
};
|
|
107
131
|
};
|
|
108
|
-
const transformModule = (code, id, register) => {
|
|
132
|
+
const transformModule = (code, id, register, sources = DEFAULT_ICON_SOURCES) => {
|
|
109
133
|
const ast = parseAst(code, id);
|
|
110
|
-
const localNameToImport =
|
|
134
|
+
const localNameToImport = collectIconImports(ast, sources);
|
|
111
135
|
if (localNameToImport.size === 0) return {
|
|
112
136
|
code,
|
|
113
137
|
map: null,
|
|
@@ -151,7 +175,7 @@ const renderOneIcon = async (pack, exportName) => {
|
|
|
151
175
|
pack
|
|
152
176
|
))[exportName];
|
|
153
177
|
if (!Comp) throw new Error(`Icon export not found: ${pack} -> ${exportName}`);
|
|
154
|
-
const id =
|
|
178
|
+
const id = computeIconId(pack, exportName);
|
|
155
179
|
const html = renderToStaticMarkup(createElement(Comp));
|
|
156
180
|
const viewBox = html.match(/viewBox="([^"]+)"/i)?.[1] ?? "0 0 24 24";
|
|
157
181
|
const svgAttrsRaw = html.match(/^<svg\b([^>]*)>/i)?.[1] ?? "";
|
|
@@ -188,4 +212,4 @@ const createCollector = () => {
|
|
|
188
212
|
};
|
|
189
213
|
|
|
190
214
|
//#endregion
|
|
191
|
-
export { transformModule as i, buildSprite as n, createCollector as r,
|
|
215
|
+
export { transformModule as i, buildSprite as n, createCollector as r, DEFAULT_ICON_SOURCES as t };
|
package/dist/icon.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
|
|
3
3
|
//#region src/icon.tsx
|
|
4
|
-
const ReactIconsSpriteIcon = ({ iconId
|
|
4
|
+
const ReactIconsSpriteIcon = ({ iconId, ...rest }) => {
|
|
5
5
|
const iconHref = `__SPRITE_URL_PLACEHOLDER__#${iconId}`;
|
|
6
6
|
return /* @__PURE__ */ jsx("svg", {
|
|
7
7
|
height: "1em",
|
package/dist/index.d.mts
ADDED
package/dist/index.mjs
ADDED
package/dist/vite/plugin.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as REACT_ICONS_SPRITE_URL_PLACEHOLDER } from "../src-Bf4uZBwp.mjs";
|
|
2
|
+
import { i as transformModule, n as buildSprite, r as createCollector, t as DEFAULT_ICON_SOURCES } from "../core-BoovxbBC.mjs";
|
|
2
3
|
import { createHash } from "node:crypto";
|
|
3
4
|
|
|
4
5
|
//#region src/vite/plugin.ts
|
|
@@ -15,11 +16,10 @@ const reactIconsSprite = (options = {}) => {
|
|
|
15
16
|
transform(code, id) {
|
|
16
17
|
const cleanId = id.split("?", 1)[0];
|
|
17
18
|
if (!/\.(mjs|cjs|js|jsx|ts|tsx)$/.test(cleanId)) return null;
|
|
18
|
-
if (!/from\s+['"]react-icons\//.test(code)) return null;
|
|
19
19
|
try {
|
|
20
20
|
const { code: next, map, anyReplacements } = transformModule(code, id, (pack, exportName) => {
|
|
21
21
|
collector.add(pack, exportName);
|
|
22
|
-
});
|
|
22
|
+
}, DEFAULT_ICON_SOURCES);
|
|
23
23
|
if (!anyReplacements) return null;
|
|
24
24
|
return {
|
|
25
25
|
code: next,
|
|
@@ -35,14 +35,13 @@ const reactIconsSprite = (options = {}) => {
|
|
|
35
35
|
const generatedHash = createHash("sha256").update(spriteXml).digest("hex").slice(0, 8);
|
|
36
36
|
const emitFileOptions = {
|
|
37
37
|
type: "asset",
|
|
38
|
-
source: spriteXml
|
|
38
|
+
source: spriteXml,
|
|
39
|
+
fileName: fileName ? fileName : `react-icons-sprite-${generatedHash}.svg`
|
|
39
40
|
};
|
|
40
|
-
if (fileName) emitFileOptions.fileName = fileName;
|
|
41
|
-
else emitFileOptions.name = "react-icons-sprite.svg";
|
|
42
41
|
const assetId = this.emitFile(emitFileOptions);
|
|
43
|
-
const finalUrl = `/${this.getFileName(assetId)}
|
|
42
|
+
const finalUrl = `/${this.getFileName(assetId)}`;
|
|
44
43
|
for (const [, item] of Object.entries(bundle)) if (item.type === "chunk" && typeof item.code === "string") {
|
|
45
|
-
if (item.code.includes(
|
|
44
|
+
if (item.code.includes(REACT_ICONS_SPRITE_URL_PLACEHOLDER)) item.code = item.code.replaceAll(REACT_ICONS_SPRITE_URL_PLACEHOLDER, finalUrl);
|
|
46
45
|
}
|
|
47
46
|
}
|
|
48
47
|
};
|
package/dist/webpack/loader.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as transformModule } from "../core-
|
|
2
|
-
import { t as collector } from "../collector-
|
|
1
|
+
import { i as transformModule } from "../core-BoovxbBC.mjs";
|
|
2
|
+
import { t as collector } from "../collector-DrPR8kXO.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/webpack/loader.ts
|
|
5
5
|
const reactIconsSpriteLoader = async function(source) {
|
|
@@ -4,7 +4,7 @@ import { Compiler, WebpackPluginInstance } from "webpack";
|
|
|
4
4
|
type ReactIconsSpriteWebpackPluginOptions = {
|
|
5
5
|
/**
|
|
6
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.svg`.
|
|
7
|
+
* If fileName is omitted, name will be generated as `react-icons-sprite-[hash].svg`.
|
|
8
8
|
* This is useful when, for example, multiple sprite sheets are generated during client and server builds.
|
|
9
9
|
*/
|
|
10
10
|
fileName?: string;
|
package/dist/webpack/plugin.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { t as REACT_ICONS_SPRITE_URL_PLACEHOLDER } from "../src-Bf4uZBwp.mjs";
|
|
2
|
+
import { n as buildSprite } from "../core-BoovxbBC.mjs";
|
|
3
|
+
import { t as collector } from "../collector-DrPR8kXO.mjs";
|
|
3
4
|
import { createHash } from "node:crypto";
|
|
4
5
|
|
|
5
6
|
//#region src/webpack/plugin.ts
|
|
@@ -19,7 +20,7 @@ var ReactIconsSpriteWebpackPlugin = class {
|
|
|
19
20
|
}, async () => {
|
|
20
21
|
const spriteXml = await buildSprite(collector.toList());
|
|
21
22
|
const generatedHash = createHash("sha256").update(spriteXml).digest("hex").slice(0, 8);
|
|
22
|
-
const name = this.fileName ??
|
|
23
|
+
const name = this.fileName ?? `react-icons-sprite-${generatedHash}.svg`;
|
|
23
24
|
const RawSource = compiler.webpack?.sources?.RawSource;
|
|
24
25
|
if (!RawSource) throw new Error("[react-icons-sprite] Unable to access webpack RawSource");
|
|
25
26
|
compilation.emitAsset(name, new RawSource(spriteXml));
|
|
@@ -27,14 +28,14 @@ var ReactIconsSpriteWebpackPlugin = class {
|
|
|
27
28
|
let base = "";
|
|
28
29
|
if (typeof outputPublicPath === "string" && outputPublicPath !== "auto") base = outputPublicPath.endsWith("/") ? outputPublicPath : `${outputPublicPath}/`;
|
|
29
30
|
else base = "/";
|
|
30
|
-
const finalUrl = `${base}${name}
|
|
31
|
+
const finalUrl = `${base}${name}`;
|
|
31
32
|
for (const asset of compilation.getAssets()) {
|
|
32
33
|
const filename = asset.name;
|
|
33
34
|
if (!/\.(js|mjs|cjs)$/i.test(filename)) continue;
|
|
34
35
|
const src = asset.source.source();
|
|
35
36
|
if (typeof src !== "string") continue;
|
|
36
|
-
if (!src.includes(
|
|
37
|
-
const next = src.replaceAll(
|
|
37
|
+
if (!src.includes(REACT_ICONS_SPRITE_URL_PLACEHOLDER)) continue;
|
|
38
|
+
const next = src.replaceAll(REACT_ICONS_SPRITE_URL_PLACEHOLDER, finalUrl);
|
|
38
39
|
compilation.updateAsset(filename, new RawSource(next));
|
|
39
40
|
}
|
|
40
41
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-icons-sprite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "",
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
"sideEffects": false,
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|
|
14
|
-
"types": "./dist/
|
|
15
|
-
"import": "./dist/
|
|
16
|
-
"default": "./dist/
|
|
14
|
+
"types": "./dist/index.d.mts",
|
|
15
|
+
"import": "./dist/index.mjs",
|
|
16
|
+
"default": "./dist/index.mjs"
|
|
17
17
|
},
|
|
18
18
|
"./vite": {
|
|
19
19
|
"types": "./dist/vite/plugin.d.mts",
|
|
@@ -29,11 +29,6 @@
|
|
|
29
29
|
"types": "./dist/webpack/loader.d.mts",
|
|
30
30
|
"import": "./dist/webpack/loader.mjs",
|
|
31
31
|
"default": "./dist/webpack/loader.mjs"
|
|
32
|
-
},
|
|
33
|
-
"./turbopack": {
|
|
34
|
-
"types": "./dist/turbopack/plugin.d.mts",
|
|
35
|
-
"import": "./dist/turbopack/plugin.mjs",
|
|
36
|
-
"default": "./dist/turbopack/plugin.mjs"
|
|
37
32
|
}
|
|
38
33
|
},
|
|
39
34
|
"publishConfig": {
|
|
@@ -52,9 +47,12 @@
|
|
|
52
47
|
"scripts": {
|
|
53
48
|
"build": "tsdown",
|
|
54
49
|
"dev": "tsdown --watch",
|
|
55
|
-
"format": "biome format
|
|
56
|
-
"
|
|
57
|
-
"lint
|
|
50
|
+
"format": "biome format --no-errors-on-unmatched",
|
|
51
|
+
"format:fix": "biome format --write --no-errors-on-unmatched",
|
|
52
|
+
"lint": "biome lint --no-errors-on-unmatched",
|
|
53
|
+
"lint:fix": "biome lint --write --no-errors-on-unmatched",
|
|
54
|
+
"type-check": "tsgo",
|
|
55
|
+
"test": "vitest run",
|
|
58
56
|
"prepublishOnly": "npm run build",
|
|
59
57
|
"release": "npm publish --access public"
|
|
60
58
|
},
|
|
@@ -68,18 +66,20 @@
|
|
|
68
66
|
"@babel/parser": "7.28.5",
|
|
69
67
|
"@babel/traverse": "7.28.5",
|
|
70
68
|
"@babel/types": "7.28.5",
|
|
71
|
-
"@biomejs/biome": "2.3.
|
|
69
|
+
"@biomejs/biome": "2.3.8",
|
|
72
70
|
"@types/babel__generator": "7.27.0",
|
|
73
71
|
"@types/babel__traverse": "7.28.0",
|
|
74
|
-
"@types/node": "24.
|
|
72
|
+
"@types/node": "24.10.2",
|
|
75
73
|
"@types/react-dom": "19.2.3",
|
|
76
|
-
"
|
|
77
|
-
"react
|
|
74
|
+
"@typescript/native-preview": "7.0.0-dev.20251210.1",
|
|
75
|
+
"react": "19.2.1",
|
|
76
|
+
"react-dom": "19.2.1",
|
|
78
77
|
"react-icons": "5.5.0",
|
|
79
|
-
"tsdown": "0.
|
|
78
|
+
"tsdown": "0.17.2",
|
|
80
79
|
"typescript": "5.9.3",
|
|
81
|
-
"vite": "7.2.
|
|
82
|
-
"
|
|
80
|
+
"vite": "7.2.7",
|
|
81
|
+
"vitest": "4.0.15",
|
|
82
|
+
"webpack": "5.103.0"
|
|
83
83
|
},
|
|
84
84
|
"keywords": [
|
|
85
85
|
"vite",
|