mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-11-10 13:18:25 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -57,6 +57,7 @@ jobs:
|
||||
'lucide-svelte',
|
||||
'@lucide/astro',
|
||||
'@lucide/svelte',
|
||||
'@lucide/icons',
|
||||
'@lucide/vue',
|
||||
]
|
||||
steps:
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
```
|
||||
@@ -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 |
@@ -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 |
@@ -0,0 +1,10 @@
|
||||
stats
|
||||
node_modules
|
||||
tests
|
||||
scripts
|
||||
build
|
||||
src
|
||||
babel.config.js
|
||||
jest.config.js
|
||||
rollup.config.js
|
||||
yarn.error.log
|
||||
@@ -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">
|
||||
[](https://www.npmjs.com/package/@lucide/icons)
|
||||

|
||||
[](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>
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
];
|
||||
@@ -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  - 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};
|
||||
`;
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './aliases';
|
||||
export * from './prefixed';
|
||||
export * from './suffixed';
|
||||
@@ -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';
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -0,0 +1,2 @@
|
||||
export { lucideIconNames, type LucideIconName } from './dynamicIcon';
|
||||
export { default as lucideDynamicIconImports } from './dynamicIconImports';
|
||||
@@ -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>;
|
||||
@@ -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';
|
||||
@@ -0,0 +1,4 @@
|
||||
export * as icons from './icons';
|
||||
export * from './icons';
|
||||
export * from './aliases/index';
|
||||
export * from './types';
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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, '');
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
import { expect } from 'vitest';
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
import htmlSerializer from 'jest-serializer-html';
|
||||
|
||||
expect.addSnapshotSerializer(htmlSerializer);
|
||||
@@ -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"],
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
setupFiles: './tests/setupVitest.js',
|
||||
}
|
||||
});
|
||||
Generated
+46
-26
@@ -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
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user