feat(packages): added new @lucide/icons package (#3881)

* feat(packages): added lucide icons package skeleton

* feat(packages): added initial commit of @lucide/icons

* fix: fixed linting issues introduced in c4e5730bc4 (#3858)

* fix(packages/icons): update package, remove UMD export

* fix(packages/icons): update github actions

* docs(packages/icons): add documentation for various helpers and types

* fix(.github/ISSUE_TEMPLATE): fix linting issue (@ is a reserved character)

* fix(packages/icons): fix linting issues in configs

* feat(packages/icons): rename from packages/lucide-icons => packages/icons

* fix(pnpm): update lockfile

* fix(packages/icons): update readme and documentation

* fix(packages/icons): update absoluteStrokeWidth implementation

* test(packages/icons): update absoluteStrokeWidth unit test

* feat(packages/icons): add backwards compatible alias support in buildLucideIconNode

* feat(packages/icons): fix linting issues in build tools

* fix(packages/icons): rename LucideIcon => LucideIconData

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
This commit is contained in:
Karsa
2026-03-13 10:18:53 +01:00
committed by GitHub
co-authored by Eric Fennis
parent e4f1de7635
commit 8fe835c105
45 changed files with 1136 additions and 47 deletions
+1
View File
@@ -23,6 +23,7 @@ body:
- label: lucide-vue
- label: lucide-vue-next
- label: lucide-astro
- label: '@lucide/icons'
- label: Figma plugin
- label: source/main
- label: other/not relevant
@@ -23,6 +23,7 @@ body:
- label: lucide-vue
- label: lucide-vue-next
- label: lucide-astro
- label: '@lucide/icons'
- label: Figma plugin
- label: all JS packages
- label: site
+6
View File
@@ -85,6 +85,12 @@
- any-glob-to-any-file:
- 'packages/astro/*'
# For changes in the @lucide/icons package
❇️ lucide-icons:
- changed-files:
- any-glob-to-any-file:
- 'packages/lucide-icons/*'
# For changes in the lucide static package
🪨 static package:
- changed-files:
+43
View File
@@ -0,0 +1,43 @@
name: Lucide Icons Checks
on:
pull_request:
paths:
- packages/icons/**
- packages/shared/**
- tools/build-icons/**
- tools/rollup-plugins/**
- pnpm-lock.yaml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm --filter @lucide/icons build
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Test
run: pnpm --filter @lucide/icons test
+1
View File
@@ -57,6 +57,7 @@ jobs:
'lucide-svelte',
'@lucide/astro',
'@lucide/svelte',
'@lucide/icons',
'@lucide/vue',
]
steps:
+18
View File
@@ -153,5 +153,23 @@
"href": "https://www.npmjs.com/package/lucide-static"
}
]
},
"@lucide/icons": {
"docsAlias": "icons",
"packageDirname": "icons",
"order": 10,
"icon": "ts",
"shields": [
{
"alt": "npm",
"src": "https://img.shields.io/npm/v/@lucide/icons",
"href": "https://www.npmjs.com/package/@lucide/icons"
},
{
"alt": "npm",
"src": "https://img.shields.io/npm/dw/@lucide/icons",
"href": "https://www.npmjs.com/package/@lucide/icons"
}
]
}
}
+4
View File
@@ -104,6 +104,10 @@ const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
text: 'Lucide Static',
link: '/guide/packages/lucide-static',
},
{
text: 'Icon data & helpers',
link: '/guide/packages/icons',
},
],
},
{
+10
View File
@@ -79,6 +79,16 @@ declare module "lucide-react-native" {
}
```
```ts [@lucide-icons]
declare module "@lucide/icons" {
// Prefixed import names
export * from "@lucide/icons/dist/lucide-icons.prefixed";
// or
// Suffixed import names
export * from "@lucide/icons/dist/lucide-icons.suffixed";
}
```
:::
Place this in your project root or in a folder where your tsconfig.json is located, or locate it in your defined type directory.
+172
View File
@@ -0,0 +1,172 @@
# @lucide/icons
`@lucide/icons` is a helper library that exports Lucide **icon data** in a tree-shakable format, also providing utilities for dynamic importing icons.
It intentionally ships **no real rendering logic or components** — other packages (for example [`@lucide/angular`](http://npmjs.com/package/@lucide/angular)) can consume this data to render icons in their respective
frameworks. You can also use this package to build third-party integrations for frameworks we don't (yet) support.
## Installation
::: code-group
```sh [pnpm]
pnpm install @lucide/icons
```
```sh [yarn]
yarn add @lucide/icons
```
```sh [npm]
npm install @lucide/icons
```
```sh [bun]
bun add @lucide/icons
```
:::
## Icon data format
Each icon is described by the following interface:
```typescript
export type LucideIconData = {
name: string;
node: LucideIconNode[];
} & (
| { size: number }
| { width: number; height: number; }
);
```
| name | type | description |
|------------------------------|--------------------|--------------------------------------------------------------------|
| `name` | `string` | The name of the icon. |
| `node` | `LucideIconNode[]` | SVG child nodes as `[tagName, attributes]` tuples. |
| `size` or `width` & `height` | `number` | The dimensions of the icon (`size` is shorthand for square icons). |
## How to use
Icons can be imported individually. Only the icons you import end up referenced by your application code — the rest will be eliminated by tree-shaking.
```ts
import { House } from '@lucide/icons';
// House is icon data (not a rendered component).
```
## Building icons
`@lucide/icons` ships small helpers that convert Lucide icon data into different render-ready outputs.
All builders accept the same `params` object (`LucideBuildParams`) to customize the generated SVG.
### Build parameters
The following parameters are supported (names reflect the current implementation):
| param | type | effect |
|-----------------------|--------------------------|------------------------------------------------------------------------------------|
| `color` | `string` | Sets `stroke` (defaults to `currentColor`). |
| `size` | `number` | Sets both `width` and `height` (defaults to 24). |
| `width` | `number` | Sets `width` only. |
| `height` | `number` | Sets `height` only. |
| `strokeWidth` | `number` | Sets `stroke-width` (defaults to 2). |
| `absoluteStrokeWidth` | `boolean` | Adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements. |
| `className` | `string` | Appended to the generated `class` attribute. |
| `attributes` | `Record<string, string>` | Add or override any generated SVG attributes (including `class`, `viewBox`, etc.). |
::: info
SVG attributes generated by the builders include a default Lucide setup (`xmlns`, `viewBox`, `fill="none"`, `stroke="currentColor"`, `stroke-width="2"`, `stroke-linecap="round"`, `stroke-linejoin="round"`), plus a class string of the form: `lucide lucide-{iconName}`.
:::
### `buildLucideIconNode`
Creates a root SVG node in an svgson-like format:
```ts
import { buildLucideIconNode } from '@lucide/icons/builders';
import { House } from '@lucide/icons';
const node = buildLucideIconNode(House, {
size: 32,
strokeWidth: 1.5,
className: 'my-icon',
});
// -> ['svg', attributes, children]
```
This is useful if you want to plug Lucide icons into your own renderer, templating system, or framework integration.
### `buildLucideSvg`
Creates an SVG string:
```ts
import { buildLucideSvg } from '@lucide/icons/builders';
import { House } from '@lucide/icons';
const svg = buildLucideSvg(House, { size: 24, color: '#111' });
```
### `buildLucideIconElement`
Creates an actual DOM element (SVG) within the provided document:
```ts
import { buildLucideIconElement } from '@lucide/icons/builders';
import { House } from '@lucide/icons';
const el = buildLucideIconElement(document, House, { size: 24 });
document.body.appendChild(el);
```
### `buildLucideDataUri`
Creates a base64-encoded SVG data URI from a Lucide icon object.
This helper works in both browsers and Node.js:
- In browsers it uses `btoa` (with proper UTF-8 handling)
- In Node.js it falls back to `Buffer`
```ts
import { buildLucideDataUri } from '@lucide/icons/builders';
import { House } from '@lucide/icons';
const uri = buildLucideDataUri(House, { size: 24 });
```
The returned value can be used directly in places such as:
- `<img src="...">`
- CSS `background-image`
- Canvas drawing
- Inline data URLs in HTML or SVG
::: tip Environment notes
- The SVG is encoded as UTF-8 before base64 conversion to ensure correct handling of non-ASCII characters.
- No runtime configuration is required — the function automatically selects the appropriate encoding strategy.
- If neither `btoa` nor `Buffer` is available, an error is thrown.
:::
## Dynamic imports
Dynamic imports are useful when you only know the icon name at runtime (for example, icon names stored in a database or a CMS). For purely static use cases, prefer direct imports for the best tree-shaking results.
::: tip
Validate `iconName` before indexing the map (and provide a fallback icon) to avoid runtime errors.
:::
## Dynamic imports
Dynamic imports are useful when the icon name is only known at runtime (for example, icon names stored in a CMS or database). For purely static usage, prefer direct imports for maximum tree-shaking.
```ts
import { lucideDynamicIconImports } from '@lucide/icons/dynamic';
const name = 'house';
const icon = await lucideDynamicIconImports[name]?.();
if (!icon) {
// handle unknown icon name (fallback)
}
```
+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path fill="#3178C6" d="M24 0H0v24h24V0Z"/>
<path fill="#fff" fill-rule="evenodd" d="M14.11 18.797v2.588c.421.215.918.375 1.49.487a9.45 9.45 0 0 0 1.819.16 8.35 8.35 0 0 0 1.772-.179 4.407 4.407 0 0 0 1.472-.59c.421-.272.75-.628 1.003-1.07.243-.44.365-.983.365-1.63 0-.47-.065-.882-.206-1.238a2.98 2.98 0 0 0-.61-.947 4.48 4.48 0 0 0-.946-.74 11.05 11.05 0 0 0-1.247-.62c-.338-.14-.647-.27-.919-.412a4.124 4.124 0 0 1-.684-.403 1.784 1.784 0 0 1-.44-.44.928.928 0 0 1-.15-.525c0-.178.046-.338.14-.478.093-.141.225-.263.384-.366.169-.103.375-.178.619-.234.244-.057.515-.085.806-.085.216 0 .44.019.684.047.244.028.479.084.722.15.244.066.479.15.713.253.225.103.44.225.637.356v-2.418a6.524 6.524 0 0 0-1.293-.338 10.228 10.228 0 0 0-1.604-.112c-.618 0-1.2.065-1.753.197a4.42 4.42 0 0 0-1.453.618c-.422.282-.75.638-.993 1.07-.244.43-.366.955-.366 1.555 0 .77.225 1.425.666 1.978.45.544 1.125 1.004 2.024 1.388.357.15.685.29.994.431.31.14.572.281.797.44.225.16.403.32.534.498.132.178.197.384.197.61a.921.921 0 0 1-.121.468c-.085.14-.207.262-.366.375-.16.112-.366.187-.619.244a3.957 3.957 0 0 1-.862.084 4.938 4.938 0 0 1-1.67-.3 4.752 4.752 0 0 1-1.537-.872Zm-4.313-6.45h3.328V10.22H3.844v2.128h3.31v9.497h2.633v-9.497h.01Z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="32" fill="none" viewBox="0 0 128 32">
<rect width="128" height="32" rx="8" fill="#fff"/>
<path fill="#1b1b1f" d="M78.2 22.17a4.5 4.5 0 0 1-2.27-.55 3.7 3.7 0 0 1-1.46-1.55 5.2 5.2 0 0 1-.5-2.38c0-.9.16-1.69.5-2.37a3.87 3.87 0 0 1 3.62-2.16c.54 0 1.05.09 1.53.26s.9.44 1.27.8.66.81.87 1.37c.22.55.32 1.21.32 1.98v.63h-7.15v-1.4h5.18c0-.4-.09-.74-.25-1.05-.17-.3-.4-.55-.7-.73s-.64-.27-1.04-.27a2 2 0 0 0-1.1.3c-.32.2-.57.47-.75.8q-.255.495-.27 1.08v1.2c0 .52.1.95.28 1.31.19.36.45.64.78.83.33.2.72.29 1.17.29.3 0 .57-.04.81-.13.25-.08.46-.21.63-.38.18-.16.32-.37.4-.62l1.93.22a3 3 0 0 1-.7 1.33c-.33.37-.76.67-1.3.87-.52.2-1.12.31-1.8.31zM67.5 22.15a3.3 3.3 0 0 1-3.13-2.06 5.7 5.7 0 0 1-.47-2.44q0-1.455.48-2.46a3.34 3.34 0 0 1 4.39-1.77c.33.17.59.38.78.62.2.24.34.46.45.68h.08v-4.36h2.07V22h-2.03v-1.38H70c-.1.22-.26.44-.46.68a2.53 2.53 0 0 1-2.04.85m.57-1.68c.44 0 .8-.12 1.12-.36.3-.23.54-.57.7-1 .15-.41.23-.9.23-1.47s-.08-1.06-.24-1.48a2.1 2.1 0 0 0-.69-.97 1.8 1.8 0 0 0-1.12-.34c-.46 0-.84.12-1.15.35-.3.24-.53.57-.69 1-.15.41-.23.9-.23 1.44s.08 1.03.23 1.46c.16.42.4.76.7 1 .31.24.7.37 1.14.37M60.09 22v-8.73h2.05V22zm1.03-9.97a1.2 1.2 0 0 1-1.101-.677 1 1 0 0 1-.09-.433c0-.31.12-.57.35-.79q.36-.33.84-.33.495 0 .84.33a1.045 1.045 0 0 1 0 1.58c-.23.22-.5.32-.84.32M54.66 22.17a3.83 3.83 0 0 1-3.68-2.16 5.24 5.24 0 0 1-.5-2.34c0-.89.17-1.67.51-2.35s.82-1.2 1.44-1.59a4.16 4.16 0 0 1 2.22-.57c.7 0 1.33.13 1.88.4a3.21 3.21 0 0 1 1.87 2.74h-1.97a1.88 1.88 0 0 0-.56-1.06q-.45-.42-1.2-.42c-.41 0-.78.11-1.1.34-.32.22-.57.54-.74.96-.18.43-.27.93-.27 1.52 0 .6.1 1.1.27 1.53.17.42.41.75.73.98s.69.34 1.12.34c.3 0 .57-.05.81-.17.24-.11.45-.28.6-.5.17-.22.28-.49.34-.8h1.97a3.23 3.23 0 0 1-1.83 2.76 4.3 4.3 0 0 1-1.9.39zM46.68 18.33v-5.06h2.06V22h-2v-1.55h-.09c-.2.49-.52.89-.97 1.2-.45.3-1 .46-1.65.46a2.64 2.64 0 0 1-2.54-1.5 3.95 3.95 0 0 1-.37-1.78v-5.56h2.06v5.24c0 .55.15 1 .45 1.32.3.33.7.49 1.2.49a1.89 1.89 0 0 0 1.57-.88c.19-.3.28-.67.28-1.11M32.15 22V10.36h2.1v9.87h5.13V22z"/>
<path stroke="#1b1b1f" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 16a4 4 0 1 0-8 0 8 8 0 0 0 16 0c0-3.55-1.55-6.75-4-8.94"/>
<path stroke="#f56565" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 16a4 4 0 1 0 8 0 8 8 0 0 0-16 0c0 3.58 1.57 6.8 4.06 9"/>
<path fill="#ddd" d="M92 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4"/>
<path fill="#3178c6" d="M122 6h-20v20h20z"/>
<path fill="#fff" fill-rule="evenodd" d="M113.758 21.664v2.157q.526.267 1.242.406.751.14 1.516.133c.496.003.991-.047 1.476-.15a3.7 3.7 0 0 0 1.227-.49 2.54 2.54 0 0 0 .836-.893q.303-.55.304-1.358c0-.392-.054-.735-.172-1.032a2.5 2.5 0 0 0-.508-.789 3.7 3.7 0 0 0-.788-.616 9 9 0 0 0-1.04-.517 10 10 0 0 1-.765-.343 3.4 3.4 0 0 1-.57-.336 1.5 1.5 0 0 1-.367-.367.77.77 0 0 1-.125-.437q0-.223.117-.399c.077-.117.187-.219.32-.305.14-.086.312-.148.515-.195q.308-.07.672-.07c.18 0 .367.015.57.039.203.023.399.07.602.125q.303.082.594.21c.187.086.367.188.531.297V14.72a5.5 5.5 0 0 0-1.078-.282 8.5 8.5 0 0 0-1.336-.093q-.771 0-1.461.164a3.7 3.7 0 0 0-1.211.515 2.6 2.6 0 0 0-.828.892c-.203.358-.305.796-.305 1.296 0 .641.188 1.187.555 1.648q.563.678 1.687 1.157c.298.125.571.241.828.359.259.117.477.234.665.367.187.133.335.266.445.415s.164.32.164.508a.76.76 0 0 1-.101.39 1.1 1.1 0 0 1-.305.312 1.4 1.4 0 0 1-.516.204 3.3 3.3 0 0 1-.718.07 4.1 4.1 0 0 1-1.392-.25 4 4 0 0 1-1.28-.727m-3.594-5.375h2.773v-1.772h-7.734v1.773h2.758v7.914h2.195V16.29z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

+10
View File
@@ -0,0 +1,10 @@
stats
node_modules
tests
scripts
build
src
babel.config.js
jest.config.js
rollup.config.js
yarn.error.log
+82
View File
@@ -0,0 +1,82 @@
<p align="center">
<a href="https://github.com/lucide-icons/lucide">
<img src="https://lucide.dev/package-logos/icons.svg" alt="" width="540">
</a>
</p>
<p align="center">
Lucide helper library that exports icon data.
</p>
<div align="center">
[![npm](https://img.shields.io/npm/v/@lucide/icons?color=blue)](https://www.npmjs.com/package/@lucide/icons)
![NPM Downloads](https://img.shields.io/npm/dw/@lucide/icons)
[![GitHub](https://img.shields.io/github/license/lucide-icons/lucide)](https://lucide.dev/license)
</div>
<p align="center">
<a href="https://lucide.dev/guide/">About</a>
·
<a href="https://lucide.dev/icons/">Icons</a>
·
<a href="https://lucide.dev/guide/packages/lucide">Documentation</a>
·
<a href="https://lucide.dev/license">License</a>
</p>
# @lucide/icons
A helper library that exports Lucide icon data in a tree-shakable format, also providing utilities for dynamic importing icons.
## Installation
```sh
pnpm add @lucide/icons
```
```sh
npm install @lucide/icons
```
```sh
yarn add @lucide/icons
```
```sh
bun add @lucide/icons
```
### CDN
```html
<!-- Development version -->
<script src="https://unpkg.com/@lucide/icons@latest/dist/umd/lucide.js"></script>
<!-- Production version -->
<script src="https://unpkg.com/@lucide/icons@latest"></script>
```
## Documentation
For full documentation, visit [lucide.dev](https://lucide.dev/guide/packages/icons)
## Community
Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintainers and other users.
## License
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
<img src="https://lucide.dev/vercel.svg" alt="Powered by Vercel" width="200" />
</a>
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
### Awesome backers 🍺
<a href="https://www.scipress.io?utm_source=lucide"><img src="https://lucide.dev/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
<a href="https://github.com/pdfme/pdfme"><img src="../../docs/public/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
+77
View File
@@ -0,0 +1,77 @@
{
"name": "@lucide/icons",
"description": "A Lucide icon library that contains icon data in a standard Lucide format.",
"version": "0.0.1",
"license": "ISC",
"homepage": "https://lucide.dev",
"bugs": "https://github.com/lucide-icons/lucide/issues",
"repository": {
"type": "git",
"url": "https://github.com/lucide-icons/lucide.git",
"directory": "packages/lucide"
},
"keywords": [
"Lucide",
"HTML",
"Feather",
"Icons",
"Icon",
"SVG",
"Feather Icons"
],
"amdName": "lucide-icons",
"source": "src/lucide-icons.ts",
"main": "dist/cjs/lucide-icons.cjs",
"module": "dist/esm/lucide-icons.mjs",
"types": "dist/esm/lucide-icons.d.ts",
"sideEffects": false,
"exports": {
".": {
"types": "./dist/esm/lucide-icons.d.ts",
"import": "./dist/esm/lucide-icons.mjs",
"require": "./dist/cjs/lucide-icons.cjs",
"default": "./dist/esm/lucide-icons.mjs"
},
"./icons/*": {
"import": "./dist/esm/icons/*.mjs",
"default": "./dist/esm/icons/*.mjs"
},
"./dynamic": {
"types": "./dist/esm/dynamic.d.ts",
"import": "./dist/esm/dynamic.mjs",
"require": "./dist/cjs/dynamic.cjs",
"default": "./dist/esm/dynamic.mjs"
},
"./build": {
"types": "./dist/esm/build.d.ts",
"import": "./dist/esm/build.mjs",
"require": "./dist/cjs/build.cjs",
"default": "./dist/esm/build.mjs"
}
},
"scripts": {
"build": "pnpm clean && pnpm copy:license && pnpm build:icons && pnpm build:bundle",
"copy:license": "cp ../../LICENSE ./LICENSE",
"clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.ts",
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mjs --renderUniqueKey --withAliases --withDynamicImports --separateAliasesFile --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts",
"build:bundle": "rollup -c rollup.config.mjs",
"test": "pnpm build:icons && vitest run",
"test:watch": "vitest watch",
"test:update": "vitest -u",
"version": "pnpm version --git-tag-version=false"
},
"devDependencies": {
"@lucide/build-icons": "workspace:*",
"@lucide/helpers": "^1.0.0",
"@lucide/rollup-plugins": "workspace:*",
"@rollup/plugin-replace": "^6.0.2",
"@testing-library/jest-dom": "^6.6.3",
"jest-serializer-html": "^7.1.0",
"rollup": "^4.53.3",
"rollup-plugin-dts": "^6.2.3",
"rollup-plugin-preserve-directives": "^0.4.0",
"typescript": "^5.8.3",
"vite": "^7.2.4",
"vitest": "^4.0.12"
}
}
+87
View File
@@ -0,0 +1,87 @@
import plugins from '@lucide/rollup-plugins';
import pkg from './package.json' with { type: 'json' };
import dts from 'rollup-plugin-dts';
const packageName = '@lucide/icons';
const outputFileName = 'lucide-icons';
const inputs = [`src/lucide-icons.ts`];
const bundles = [
{
format: 'cjs',
inputs,
extension: 'cjs',
},
{
format: 'esm',
inputs: [...inputs, './src/dynamic.ts', './src/build.ts'],
preserveModules: true,
extension: 'mjs',
},
];
const configs = bundles
.map(
({
inputs,
outputDir = 'dist',
outputFile,
format,
minify,
preserveModules,
entryFileNames,
external = [],
paths,
extension = 'js',
}) =>
inputs.map((input) => ({
input,
plugins: [...plugins({ pkg, minify })],
external,
output: {
name: packageName,
entryFileNames,
...(preserveModules
? {
dir: `${outputDir}/${format}`,
entryFileNames: `[name].${extension}`,
}
: {
file:
outputFile ??
`${outputDir}/${format}/${outputFileName}${minify ? '.min' : ''}.${extension}`,
}),
paths,
format,
sourcemap: true,
preserveModules,
exports: 'named',
globals: {},
preserveModulesRoot: 'src',
},
})),
)
.flat();
export default [
...[
outputFileName,
`${outputFileName}.prefixed`,
`${outputFileName}.suffixed`,
'dynamic',
'build',
].map((filename) => ({
input: `./src/${filename}.ts`,
output: [
{
file: `dist/esm/${filename}.d.ts`,
format: 'esm',
},
{
file: `dist/cjs/${filename}.d.cts`,
format: 'cjs',
},
],
plugins: [dts()],
})),
...configs,
];
+32
View File
@@ -0,0 +1,32 @@
/* eslint-disable import/no-extraneous-dependencies */
import base64SVG from '@lucide/build-icons/utils/base64SVG';
export default async ({
componentName,
iconName,
getSvg,
deprecated,
deprecationReason,
iconData,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `
import type { LucideIconData } from '../types';
/**
* @name ${iconName}
* @description Lucide SVG icon node.
*
* @preview ![img](data:image/svg+xml;base64,${svgBase64}) - https://lucide.dev/icons/${iconName}
* @see https://lucide.dev/guide/packages/lucide - Documentation
*
* @returns {Array}
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
const ${componentName}: LucideIconData = ${JSON.stringify(iconData)}
export default ${componentName};
`;
};
+3
View File
@@ -0,0 +1,3 @@
export * from './aliases';
export * from './prefixed';
export * from './suffixed';
+4
View File
@@ -0,0 +1,4 @@
export { default as buildLucideIconNode } from './buildLucideIconNode';
export { default as buildLucideDataUri } from './buildLucideDataUri';
export { default as buildLucideIconElement } from './buildLucideIconElement';
export { default as buildLucideSvg } from './buildLucideSvg';
+30
View File
@@ -0,0 +1,30 @@
import { LucideBuildParams, LucideIconData } from './types';
import buildLucideSvg from './buildLucideSvg';
/**
* Creates a base64 encoded data URI from a Lucide icon object.
*
* @param icon The icon to build.
* @param params Additional build parameters.
*/
function buildLucideDataUri(icon: LucideIconData, params: LucideBuildParams = {}) {
const svg = buildLucideSvg(icon, params);
// Browser
if (typeof btoa === 'function') {
// Ensure proper UTF-8 handling before base64
const utf8Bytes = new TextEncoder().encode(svg);
let binary = '';
for (const element of utf8Bytes) binary += String.fromCodePoint(element);
return `data:image/svg+xml;base64,${btoa(binary)}`;
}
// Node.js (and other JS runtimes with Buffer)
if (typeof Buffer !== 'undefined') {
return `data:image/svg+xml;base64,${Buffer.from(svg, 'utf8').toString('base64')}`;
}
throw new Error('No base64 encoder available in this environment.');
}
export default buildLucideDataUri;
@@ -0,0 +1,34 @@
import { LucideBuildParams, LucideIconData, LucideIconNode } from './types';
import buildLucideIconNode from './buildLucideIconNode';
const buildDomElement = (
document: Document,
[tagName, attributes, children = []]: LucideIconNode,
): Element => {
const element = document.createElementNS('http://www.w3.org/2000/svg', tagName);
for (const [attrName, value] of Object.entries(attributes)) {
element.setAttribute(attrName, value);
}
for (const node of children) {
const childNode = buildDomElement(document, node);
element.appendChild(childNode);
}
return element;
};
/**
* Creates an SvgElement from a Lucide icon object.
*
* @param document The document to create the Element in.
* @param icon The icon to build.
* @param params Additional build parameters.
*/
function buildLucideIconElement(
document: Document,
icon: LucideIconData,
params: LucideBuildParams = {},
) {
return buildDomElement(document, buildLucideIconNode(icon, params));
}
export default buildLucideIconElement;
+55
View File
@@ -0,0 +1,55 @@
import { LucideBuildParams, LucideIconData, LucideIconNode } from './types';
const defaultAttributes = {
xmlns: 'http://www.w3.org/2000/svg',
width: '24',
height: '24',
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
};
/**
* Creates a Lucide icon node (an svgson-like format) from a Lucide icon object.
*
* @param icon The icon to build.
* @param params Additional build parameters.
*/
function buildLucideIconNode(icon: LucideIconData, params: LucideBuildParams = {}): LucideIconNode {
const viewBoxWidth = ('size' in icon ? icon.size : icon.width) ?? defaultAttributes.width;
const viewBoxHeight = ('size' in icon ? icon.size : icon.height) ?? defaultAttributes.height;
const aliasClassNames = icon.aliases?.map((alias) => `lucide-${alias}`) ?? [];
const classList = ['lucide', `lucide-${icon.name}`, ...aliasClassNames];
if (params['className']) {
classList.push(params['className']);
}
const attributes = {
...defaultAttributes,
...('color' in params && { stroke: params['color'] }),
...('size' in params &&
params['size'] && {
width: params['size'].toString(10),
height: params['size'].toString(10),
}),
...('width' in params && params['width'] && { width: params['width'].toString(10) }),
...('height' in params && params['height'] && { height: params['height'].toString(10) }),
...('strokeWidth' in params &&
params['strokeWidth'] && { 'stroke-width': params['strokeWidth'].toString(10) }),
class: classList.join(' '),
viewBox: `0 0 ${viewBoxWidth} ${viewBoxHeight}`,
...('attributes' in params && params.attributes),
};
return [
'svg',
attributes,
icon.node.map(([name, attrs]) => [
name,
params['absoluteStrokeWidth'] ? { 'vector-effect': 'non-scaling-stroke', ...attrs } : attrs,
]),
];
}
export default buildLucideIconNode;
+19
View File
@@ -0,0 +1,19 @@
import { LucideBuildParams, LucideIconData, LucideIconNode } from './types';
import buildLucideIconNode from './buildLucideIconNode';
const buildDomNode = ([tagName, attributes, children = []]: LucideIconNode): string =>
`<${tagName} ${Object.entries(attributes)
.map(([attrName, value]) => `${attrName}="${value}"`)
.join(' ')}>${children?.map((child) => buildDomNode(child)).join('')}</${tagName}>`;
/**
* Creates an SVG string from a Lucide icon object.
*
* @param icon The icon to build.
* @param params Additional build parameters.
*/
function buildLucideSvg(icon: LucideIconData, params: LucideBuildParams = {}) {
return buildDomNode(buildLucideIconNode(icon, params));
}
export default buildLucideSvg;
+2
View File
@@ -0,0 +1,2 @@
export { lucideIconNames, type LucideIconName } from './dynamicIcon';
export { default as lucideDynamicIconImports } from './dynamicIconImports';
+8
View File
@@ -0,0 +1,8 @@
import dynamicIconImports from './dynamicIconImports';
export type LucideIconName = keyof typeof dynamicIconImports;
/**
* The list of available Lucide icon names.
*/
export const lucideIconNames = Object.keys(dynamicIconImports) as Array<LucideIconName>;
+1
View File
@@ -0,0 +1 @@
Folder for generated icons
@@ -0,0 +1,5 @@
export * as icons from './icons';
export * from './aliases/prefixed';
export * from './types';
export * from './build';
@@ -0,0 +1,3 @@
export * as icons from './icons';
export * from './aliases/suffixed';
export * from './types';
+4
View File
@@ -0,0 +1,4 @@
export * as icons from './icons';
export * from './icons';
export * from './aliases/index';
export * from './types';
+43
View File
@@ -0,0 +1,43 @@
export type SVGProps = Record<string, string>;
/**
* A Lucide icon node (an svgson-like internal format)
*/
export type LucideIconNode =
| [attrName: string, attributes: SVGProps]
| [attrName: string, attributes: SVGProps, children: LucideIconNode[]];
/**
* A Lucide icon object that fully describes an icon to be displayed.
*/
export type LucideIconData = {
name: string;
node: LucideIconNode[];
aliases?: string[];
} & ({ size: number } | { width: number; height: number });
/**
* Build parameters for creating a Lucide icon instance for display.
*/
export type LucideBuildParams = {
/**
* The color of the icon.
*/
color?: string;
/**
* The stroke width.
*/
strokeWidth?: number;
/**
* Adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements.
*/
absoluteStrokeWidth?: boolean;
/**
* Extra CSS class names to pass to the SVG element.
*/
className?: string;
/**
* Any extra attributes to pass to the SVG element.
*/
attributes?: SVGProps;
} & ({ size?: number } | { width?: number; height?: number });
@@ -0,0 +1,24 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`buildLucideIconElement > should match the snapshot 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewbox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-house lucide-home"
>
<path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"
key="5wwlr5"
>
</path>
<path d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
key="r6nss1"
>
</path>
</svg>
`;
@@ -0,0 +1,3 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`buildLucideIconNode > should match the snapshot 1`] = `"{"xmlns":"http://www.w3.org/2000/svg","width":"24","height":"24","viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round","class":"lucide lucide-house lucide-home"}"`;
@@ -0,0 +1,26 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`buildLucideSvg > should match the snapshot 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewbox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-house lucide-home"
>
<path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"
key="5wwlr5"
>
</path>
<path d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
key="r6nss1"
>
</path>
</svg>
`;
exports[`buildLucideSvgDataUri > should match the snapshot 1`] = `"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWhvdXNlIGx1Y2lkZS1ob21lIj48cGF0aCBkPSJNMTUgMjF2LThhMSAxIDAgMCAwLTEtMWgtNGExIDEgMCAwIDAtMSAxdjgiIGtleT0iNXd3bHI1Ij48L3BhdGg+PHBhdGggZD0iTTMgMTBhMiAyIDAgMCAxIC43MDktMS41MjhsNy02YTIgMiAwIDAgMSAyLjU4MiAwbDcgNkEyIDIgMCAwIDEgMjEgMTB2OWEyIDIgMCAwIDEtMiAySDVhMiAyIDAgMCAxLTItMnoiIGtleT0icjZuc3MxIj48L3BhdGg+PC9zdmc+"`;
@@ -0,0 +1,50 @@
import { describe, expect, it } from 'vitest';
import { House } from '../src/lucide-icons';
import { getOriginalSvg, removeKeys } from './helpers';
import buildLucideIconElement from '../src/buildLucideIconElement';
describe('buildLucideIconElement', () => {
it('should create SVG Element', () => {
const HomeSVG = buildLucideIconElement(document, House);
expect(HomeSVG.tagName).toBe('svg');
});
it('should match the snapshot', () => {
const HomeSVG = buildLucideIconElement(document, House);
expect(HomeSVG.outerHTML).toMatchSnapshot();
});
it('should create SVG Element with attributes', () => {
const HomeSVG = buildLucideIconElement(document, House, { attributes: { fill: 'red' } });
expect(HomeSVG.getAttribute('fill')).toBe('red');
});
it('should create SVG Element with class name', () => {
const HomeSVG = buildLucideIconElement(document, House, { attributes: { class: 'icon' } });
expect(HomeSVG.getAttribute('class')).toBe('icon');
});
it('should add backwards-compatible classes', () => {
const HomeSVG = buildLucideIconElement(document, House);
expect(HomeSVG.getAttribute('class')).toBe('lucide lucide-house lucide-home');
});
it('should merge classes', () => {
const HomeSVG = buildLucideIconElement(document, House, { className: 'icon' });
expect(HomeSVG.getAttribute('class')).toBe('lucide lucide-house lucide-home icon');
});
it('should create the correct svg element', () => {
const HomeSVG = buildLucideIconElement(document, House);
const svg = getOriginalSvg('house', ['home']);
expect(removeKeys(HomeSVG.outerHTML)).toBe(svg);
});
});
@@ -0,0 +1,92 @@
import { describe, expect, it } from 'vitest';
import { House } from '../src/lucide-icons';
import buildLucideIconNode from '../src/buildLucideIconNode';
describe('buildLucideIconNode', () => {
it('should create icon node', () => {
const HouseSVG = buildLucideIconNode(House);
expect(HouseSVG.at(0)).toBe('svg');
});
it('should match the snapshot', () => {
const HouseSVG = buildLucideIconNode(House);
expect(JSON.stringify(HouseSVG.at(1))).toMatchSnapshot();
});
it('should override dimensions, but not viewBox', () => {
const HouseSVG = buildLucideIconNode(House, { size: 12 });
expect(HouseSVG[1]['width']).toBe('12');
expect(HouseSVG[1]['height']).toBe('12');
expect(HouseSVG[1]['viewBox']).toBe('0 0 24 24');
});
it('should override width, but not height', () => {
const HouseSVG = buildLucideIconNode(House, { width: 12 });
expect(HouseSVG[1]['width']).toBe('12');
expect(HouseSVG[1]['height']).toBe('24');
expect(HouseSVG[1]['viewBox']).toBe('0 0 24 24');
});
it('should override height, but not width', () => {
const HouseSVG = buildLucideIconNode(House, { height: 12 });
expect(HouseSVG[1]['width']).toBe('24');
expect(HouseSVG[1]['height']).toBe('12');
expect(HouseSVG[1]['viewBox']).toBe('0 0 24 24');
});
it('should override color', () => {
const HouseSVG = buildLucideIconNode(House, { color: 'pink' });
expect(HouseSVG[1]['stroke']).toBe('pink');
expect(HouseSVG[1]['fill']).toBe('none');
});
it('should override stroke width', () => {
const HouseSVG = buildLucideIconNode(House, { strokeWidth: 12 });
expect(HouseSVG[1]['stroke-width']).toBe('12');
});
it('should set non-scaling-stroke to child nodes', () => {
const HouseSVG = buildLucideIconNode(House, { absoluteStrokeWidth: true });
for (const node of HouseSVG[2]!) {
expect(node[1]['vector-effect']).toBe('non-scaling-stroke');
}
});
it('should not set non-scaling-stroke', () => {
const HouseSVG = buildLucideIconNode(House, { absoluteStrokeWidth: false });
expect(HouseSVG[1]['vector-effect']).toBeUndefined();
});
it('should create icon node with attributes', () => {
const HouseSVG = buildLucideIconNode(House, { attributes: { fill: 'red' } });
expect(HouseSVG[1]['fill']).toBe('red');
});
it('should create icon node with class name', () => {
const HouseSVG = buildLucideIconNode(House, { attributes: { class: 'icon' } });
expect(HouseSVG[1]['class']).toBe('icon');
});
it('should add backwards-compatible classes', () => {
const HouseSVG = buildLucideIconNode(House);
expect(HouseSVG[1]['class']).toBe('lucide lucide-house lucide-home');
});
it('should merge classes', () => {
const HouseSVG = buildLucideIconNode(House, { className: 'icon' });
expect(HouseSVG[1]['class']).toBe('lucide lucide-house lucide-home icon');
});
});
@@ -0,0 +1,29 @@
import { describe, expect, it } from 'vitest';
import { House } from '../src/lucide-icons';
import { getOriginalSvg, removeKeys } from './helpers';
import buildLucideSvg from '../src/buildLucideSvg';
import buildLucideDataUri from '../src/buildLucideDataUri';
describe('buildLucideSvg', () => {
it('should match the snapshot', () => {
const HouseSVG = buildLucideSvg(House);
expect(HouseSVG).toMatchSnapshot();
});
it('should create the correct svg element', () => {
const HouseSVG = buildLucideSvg(House);
const svg = getOriginalSvg('house', ['home']);
expect(removeKeys(HouseSVG)).toBe(svg);
});
});
describe('buildLucideSvgDataUri', () => {
it('should match the snapshot', () => {
const HouseDataUri = buildLucideDataUri(House);
expect(HouseDataUri).toMatchSnapshot();
});
});
+19
View File
@@ -0,0 +1,19 @@
import fs from 'fs';
import path from 'path';
import { parseSync, stringify } from 'svgson';
const ICONS_DIR = path.resolve(__dirname, '../../../icons');
export const getOriginalSvg = (iconName: string, aliasNames?: string[]) => {
const svgContent = fs.readFileSync(path.join(ICONS_DIR, `${iconName}.svg`), 'utf8');
const svgParsed = parseSync(svgContent);
if (aliasNames) {
svgParsed.attributes['class'] =
`lucide lucide-${iconName} ${aliasNames.map((alias) => `lucide-${alias}`).join(' ')}`;
}
return stringify(svgParsed, { selfClose: false });
};
export const removeKeys = (svg: string) => svg.replaceAll(/ key="[^"]+"/g, '');
+10
View File
@@ -0,0 +1,10 @@
import { describe, expect, it } from 'vitest';
import { House } from '../src/lucide-icons';
describe('lucide-icons', () => {
it('should init', () => {
const HouseSVG = House;
expect(House).toMatchObject(House);
});
});
+5
View File
@@ -0,0 +1,5 @@
import { expect } from 'vitest';
import '@testing-library/jest-dom/vitest';
import htmlSerializer from 'jest-serializer-html';
expect.addSnapshotSerializer(htmlSerializer);
+22
View File
@@ -0,0 +1,22 @@
{
"compilerOptions": {
"strict": true,
"declaration": false,
"noEmitOnError": true,
"noEmit": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"module": "ESNext",
"target": "ESNext",
"esModuleInterop": true,
"isolatedModules": true,
"lib": ["esnext", "dom"],
"skipLibCheck": true,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"downlevelIteration": true,
"sourceMap": true,
"outDir": "./dist",
},
"exclude": ["**/node_modules"],
}
+9
View File
@@ -0,0 +1,9 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
setupFiles: './tests/setupVitest.js',
}
});
+46 -26
View File
@@ -246,6 +246,45 @@ importers:
specifier: ^4.0.12
version: 4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(jiti@2.6.1)(jsdom@20.0.3)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0)
packages/icons:
devDependencies:
'@lucide/build-icons':
specifier: workspace:*
version: link:../../tools/build-icons
'@lucide/helpers':
specifier: ^1.0.0
version: 1.0.0
'@lucide/rollup-plugins':
specifier: workspace:*
version: link:../../tools/rollup-plugins
'@rollup/plugin-replace':
specifier: ^6.0.2
version: 6.0.3(rollup@4.59.0)
'@testing-library/jest-dom':
specifier: ^6.6.3
version: 6.9.1
jest-serializer-html:
specifier: ^7.1.0
version: 7.1.0
rollup:
specifier: ^4.53.3
version: 4.59.0
rollup-plugin-dts:
specifier: ^6.2.3
version: 6.2.3(rollup@4.59.0)(typescript@5.9.3)
rollup-plugin-preserve-directives:
specifier: ^0.4.0
version: 0.4.0(rollup@4.59.0)
typescript:
specifier: ^5.8.3
version: 5.9.3
vite:
specifier: ^7.2.4
version: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0)
vitest:
specifier: ^4.0.12
version: 4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(jiti@2.6.1)(jsdom@20.0.3)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0)
packages/lucide:
devDependencies:
'@lucide/build-icons':
@@ -3849,6 +3888,9 @@ packages:
'@lezer/lr@1.4.3':
resolution: {integrity: sha512-yenN5SqAxAPv/qMnpWW0AT7l+SxVrgG+u0tNsRQWqbrz66HIl8DnEbBObvy21J5K7+I1v7gsAnlE2VQ5yYVSeA==}
'@lucide/helpers@1.0.0':
resolution: {integrity: sha512-9dfxrgLLaoCfr3R/eh6wwlUcY+ZPdEv6SDwFMUhYoO6HhGL8yN8hb5ZwI/OfzbK9mdJpa+jYfwP4nF4ZlZwZLA==}
'@mapbox/node-pre-gyp@2.0.3':
resolution: {integrity: sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==}
engines: {node: '>=18'}
@@ -4547,15 +4589,6 @@ packages:
rollup:
optional: true
'@rollup/pluginutils@5.1.0':
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/pluginutils@5.3.0':
resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==}
engines: {node: '>=14.0.0'}
@@ -9588,9 +9621,6 @@ packages:
resolution: {integrity: sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==}
engines: {node: '>=12'}
magic-string@0.30.11:
resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==}
magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
@@ -17134,6 +17164,8 @@ snapshots:
dependencies:
'@lezer/common': 1.3.0
'@lucide/helpers@1.0.0': {}
'@mapbox/node-pre-gyp@2.0.3(encoding@0.1.13)':
dependencies:
consola: 3.4.2
@@ -17921,14 +17953,6 @@ snapshots:
optionalDependencies:
rollup: 4.59.0
'@rollup/pluginutils@5.1.0(rollup@4.59.0)':
dependencies:
'@types/estree': 1.0.8
estree-walker: 2.0.2
picomatch: 2.3.1
optionalDependencies:
rollup: 4.59.0
'@rollup/pluginutils@5.3.0(rollup@4.59.0)':
dependencies:
'@types/estree': 1.0.8
@@ -23770,10 +23794,6 @@ snapshots:
dependencies:
sourcemap-codec: 1.4.8
magic-string@0.30.11:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
magic-string@0.30.17:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -26509,8 +26529,8 @@ snapshots:
rollup-plugin-preserve-directives@0.4.0(rollup@4.59.0):
dependencies:
'@rollup/pluginutils': 5.1.0(rollup@4.59.0)
magic-string: 0.30.11
'@rollup/pluginutils': 5.3.0(rollup@4.59.0)
magic-string: 0.30.21
rollup: 4.59.0
rollup-plugin-sourcemaps@0.6.3(@types/node@12.20.55)(rollup@2.79.2):
@@ -48,15 +48,27 @@ function generateIconFiles({
]);
const getSvg = () => readSvg(`${iconName}.svg`, iconsDir);
const { deprecated = false, toBeRemovedInVersion = undefined } = iconMetaData[iconName];
const {
deprecated = false,
toBeRemovedInVersion = undefined,
aliases = [],
} = iconMetaData[iconName];
const deprecationReason = deprecated
? deprecationReasonTemplate(iconMetaData[iconName]?.deprecationReason ?? '', {
? deprecationReasonTemplate(iconMetaData[iconName].deprecationReason ?? '', {
componentName,
iconName,
toBeRemovedInVersion,
})
: '';
const iconData = {
name: iconName,
size: 24,
node: children,
...((aliases?.length ?? 0) > 0 && {
aliases: aliases.map((alias) => (typeof alias === 'string' ? alias : alias.name)),
}),
};
const elementTemplate = await template({
componentName,
iconName,
@@ -64,6 +76,7 @@ function generateIconFiles({
getSvg,
deprecated,
deprecationReason,
iconData,
});
const output = pretty
+14 -4
View File
@@ -6,14 +6,24 @@ export type IconNode = [tag: string, attrs: SVGProps][];
export type IconNodeWithChildren = [tag: string, attrs: SVGProps, children: IconNode];
export type TemplateFunction = (params: {
export type IconData = {
name: string;
node: IconNode;
aliases?: string[];
} & ({ size: number } | { width: number; height: number });
export interface ExportTemplate {
componentName: string;
iconName: string;
children: IconNode;
getSvg: () => Promise<string>;
deprecated?: boolean;
deprecationReason?: string;
}) => Promise<string>;
deprecated: boolean;
deprecationReason: string;
aliases?: (string | AliasDeprecation)[];
iconData: IconData;
}
export type TemplateFunction = (params: ExportTemplate) => Promise<string>;
export type Path = string;
@@ -1,15 +1,4 @@
import { type IconNode } from '../types.ts';
export interface ExportTemplate {
componentName: string;
iconName: string;
children: IconNode;
getSvg: () => Promise<string>;
deprecated: boolean;
deprecationReason: string;
}
export type TemplateFunction = (params: ExportTemplate) => Promise<string>;
import type { TemplateFunction } from '../types.ts';
const defineExportTemplate = (exportFunction: TemplateFunction) => exportFunction;
@@ -11,10 +11,9 @@ export default function deprecationReasonTemplate(
toBeRemovedInVersion?: string;
},
) {
const resourceName = deprecationReason.startsWith('icon') ? 'icon' : 'alias';
const removalNotice = toBeRemovedInVersion
? ` This ${
deprecationReason.startsWith('icon') ? 'icon' : 'alias'
} will be removed in ${toBeRemovedInVersion}`
? ` This ${resourceName} will be removed in ${toBeRemovedInVersion}`
: '';
switch (deprecationReason) {
@@ -24,6 +23,7 @@ export default function deprecationReasonTemplate(
return `The icon was combined with another icon that shares the same use case, use {@link ${componentName}} instead.${removalNotice}`;
case 'alias.name':
return `The name of this icon was changed because it didn't meet our guidelines anymore, use {@link ${componentName}} instead.${removalNotice}`;
default:
throw new Error(`Unknown deprecation reason: ${deprecationReason}`);
}
}