Compare commits

..
Author SHA1 Message Date
Eric Fennis 28adf2e0b9 Merge branch 'main' of https://github.com/lucide-icons/lucide into remove-nextjs-aliases 2026-07-23 13:42:36 +02:00
NielsLazaromsgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Eric FennisKarsa
75b6ac1969 feat(icons): added pending-cw icon (#4439)
* Added icons/pending-cw.svg

* Added icons/pending-cw.json

* Update icons/pending-cw.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Rename pending-cw.json to clock-arrow-cw.json

* Add clock-arrow-cw.svg icon file

* Update icons/clock-arrow-cw.json

* Update icons: replace and refine `history` and `clock-*` SVGs and metadata

* update file name

* updated icon name

* Refine `clock-arrow-ccw` metadata: add use-cases, tags, and aliases

* fix(icons): rename history/pending _yet again_. Sorry, Niels 🫣

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Karsa <contact@karsa.org>
2026-07-23 11:59:09 +02:00
KarsaandEric Fennis 138ce8e748 fix(packages/angular): add more supported attributes for SVG nodes (#4570)
* fix(packages/angular): add more supported attributes for SVG nodes

* fix(packages/angular): use original matcher syntax instead of toMatchInlineSnapshot matcher

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-07-23 11:29:24 +02:00
KarsaandEric Fennis 87ec1a0587 fix(icons): optimised square-scissors icons (#4581)
* Updated icons/square-bottom-dashed-scissors.svg

* Updated icons/square-bottom-dashed-scissors.json

* Update square-bottom-dashed-scissors.json

* Update square-scissors.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-07-23 10:43:44 +02:00
Eric Fennis 02eaf471a9 fix(docs): Fix site build (#4582)
* Fix site build

* Fix site build
2026-07-23 10:15:09 +02:00
Karsa 3035ed309e feat(icons): removed brand names from tags & revised brand stopwords (#4575) 2026-07-23 08:13:10 +02:00
Karsagithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Eric Fennis
4e50f0d759 feat(icons): added shield-keyhole icon (#4580)
* Added icons/shield-keyhole.svg

* Added icons/shield-keyhole.json

* Update shield-keyhole.json

* Update icons/shield-keyhole.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-23 08:10:33 +02:00
Karsa a924c01b8e fix(icons): changed book-open icons (#4565)
* Updated icons/book-open.svg

* Updated icons/book-open.json

* Updated icons/book-open-text.svg

* Updated icons/book-open-text.json

* Updated icons/book-open-check.svg
2026-07-23 08:10:07 +02:00
Karsa 37f32cb73a Update ad.json (#4579) 2026-07-23 07:47:39 +02:00
kamilaandKarsa 4e2efb9350 fix(icons): changed columns-3-cog icon (#4578)
* fix(icons)/columns-3-cog

* Update icons/columns-3-cog.svg

Co-authored-by: Karsa <contact@karsa.org>

* add EOF new line to columns-3-cog.svg

---------

Co-authored-by: Karsa <contact@karsa.org>
2026-07-22 16:13:09 +02:00
Eric Fennis 2f531012f5 Remove script 2026-07-17 18:25:40 +02:00
Karsa 658573b017 fix(docs): fix home page package links & accessible labels (#4540) 2026-07-17 13:33:45 +02:00
Karsagithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Eric Fennis
5136572c10 feat(icons): added scan-box icon (#4551)
* Added icons/scan-box.svg

* Added icons/scan-box.json

* Update scan-box.json

* Update scan-box.svg

* Update icons/scan-box.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update icons/scan-box.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-17 10:25:16 +02:00
Karsa 4f8088fd90 fix(icons): changed file-box icon (#4552)
* Updated icons/file-box.svg

* chore(icons/file-box): extend tags & use case metadata
2026-07-17 10:24:40 +02:00
Karsa ae40cfbc73 fix(icons): changed ethernet-port icon (#4555)
* Updated icons/ethernet-port.svg

* Updated icons/ethernet-port.json
2026-07-17 09:28:22 +02:00
Karsa cdd7db1ce6 fix(icons): changed hdmi-port icon (#4554)
* Updated icons/hdmi-port.svg

* Updated icons/hdmi-port.json
2026-07-17 09:28:17 +02:00
Jakob Guddas f930bbe07c feat(docs): add studio link (#4534)
* feat: add studio link

* fix: add aria label
2026-07-16 14:41:14 +02:00
Eric Fennis f229f83f0c chore(depedencies): Update dependencies (#4553)
* update deps

* Update even more

* Fix lint errors

* Apply feedback

* Update lockfile
2026-07-16 14:38:12 +02:00
Jakob Guddas b442632ee6 fix: broken link in brand logo comment (#4533) 2026-07-11 12:59:38 +02:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Eric Fennis
078a0b2339 chore(deps-dev): bump @babel/core from 7.28.5 to 7.29.6 (#4476)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.28.5 to 7.29.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.29.6/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-version: 7.29.6
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-07-10 11:53:34 +02:00
5d78605650 fix(site): empty search results on first load for tag-only matches (#4413)
* fix(site): fetch icon search metadata when typing

* fix(site): gate "No results" on metadata availability, not fetch state

* fix(site): also wait on categories metadata on the category page

Keeps the loading guard symmetric with the icons page. The narrower
tags-only check was technically more precise (this page's useSearch
doesn't index categories), but the failure case it guarded against —
brand search landing on a blank area if /api/categories lags behind
/api/tags — is too narrow to justify the asymmetry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-07-10 11:22:57 +02:00
84 changed files with 5479 additions and 9478 deletions
@@ -33,7 +33,7 @@ jobs:
Lucide **does not accept** brand logos, and we do not plan to add them in the future. This is due to a combination of **legal restrictions**, **design consistency concerns**, and **practical maintenance reasons**.
[Click here to read our official statement about brand logos in Lucide.](./BRAND_LOGOS_STATEMENT.md)
[Click here to read our official statement about brand logos in Lucide.](https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md)
You can [search for similar issues.](https://github.com/lucide-icons/lucide/issues?q=is%3Aissue+${VALUE})
+4 -5
View File
@@ -56,7 +56,6 @@
"kubernetes": "Kubernetes",
"leetcode": "LeetCode",
"leet-code": "LeetCode",
"line": "LINE",
"linkedin": "LinkedIn",
"lua": "Lua",
"mariadb": "MariaDB",
@@ -69,7 +68,7 @@
"nestjs": "NestJS",
"netflix": "Netflix",
"netlify": "Netlify",
"next": "Next.js",
"nextjs": "Next.js",
"nodejs": "Node.js",
"notion": "Notion",
"nostr": "Nostr",
@@ -105,7 +104,8 @@
"shopify": "Shopify",
"skype": "Skype",
"slack": "Slack",
"solid": "SolidJS",
"snapchat": "Snapchat",
"solidjs": "SolidJS",
"soundcloud": "SoundCloud",
"spotify": "Spotify",
"sqlite": "SQLite",
@@ -144,6 +144,5 @@
"xbox": "Xbox",
"yarn": "Yarn",
"youtube": "YouTube",
"zig": "Zig",
"zoom": "Zoom"
"zig": "Zig"
}
@@ -13,23 +13,21 @@ const { go } = useRouter()
<HomeSectionTitle>Available For:</HomeSectionTitle>
<div class="packages-list">
<a
v-for="{ name, logo, logoDark } in data.packages"
:href="`/guide/packages/${name}`"
v-for="{ name, label, path, logo, logoDark } in data.packages"
:href="path"
class="package-logo"
:aria-label="`Read more about: ${name} package`"
@click.prevent="go(`/guide/packages/${name}`)"
@click.prevent="go(path)"
>
<img
:src="logo"
:class="{ light: logoDark, 'image-logo': true }"
:alt="`${name} logo`"
:alt="label"
loading="lazy"
/>
<img
v-if="logoDark"
:src="logoDark"
:alt="`${name} logo`"
:alt="label"
class="image-logo dark"
loading="lazy"
/>
@@ -3,6 +3,7 @@ import type { IconEntity } from '../../types';
import { computed, ref } from 'vue';
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
import { useIconStyleContext } from '../../composables/useIconStyle';
import { squarePen } from '../../../data/iconNodes';
const props = defineProps<{
name: IconEntity['name'];
@@ -19,6 +20,31 @@ const iconComponent = computed(() => {
if (!props.name || !props.iconNode) return null;
return createLucideIcon(props.name, props.iconNode);
});
const SquarePenIcon = createLucideIcon('SquarePen', squarePen);
const innerSvg = computed(() => {
if (!props.iconNode) return '';
const string = props.iconNode
.map(([tag, attrs]) => {
const attributes = Object.entries(attrs)
.map(([key, value]) => `${key}="${value}"`)
.join(' ');
return `<${tag} ${attributes}/>`;
})
.join('');
return string
.replace(/>[\r\n ]+</g, '><')
.replace(/(<.*?>)|\s+/g, (m, $1) => $1 || ' ')
.trim();
});
const base64InnerSvg = computed(() => {
if (typeof window === 'undefined') return '';
return btoa(innerSvg.value);
});
</script>
<template>
@@ -57,6 +83,16 @@ const iconComponent = computed(() => {
/>
</g>
</svg>
<a
:href="`https://studio.lucide.dev/edit?value=${encodeURIComponent(base64InnerSvg)}&name=${name}`"
target="_blank"
rel="noopener noreferrer"
class="studio-button"
title="Open in Lucide Studio"
aria-label="Open in Lucide Studio"
>
<SquarePenIcon :size="14" />
</a>
</div>
</template>
@@ -75,6 +111,8 @@ const iconComponent = computed(() => {
position: relative;
background: var(--vp-c-bg-alt);
border-radius: 8px;
overflow: hidden;
flex-shrink: 0;
}
.icon-container > :deep(svg:not(.icon-grid)) {
width: 100%;
@@ -85,6 +123,34 @@ const iconComponent = computed(() => {
opacity: 0.8;
}
.studio-button {
position: absolute;
bottom: 8px;
right: 8px;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
padding: 5px;
display: flex;
align-items: center;
justify-content: center;
color: var(--vp-c-text-2);
opacity: 0;
transition: opacity 0.2s, color 0.2s, background-color 0.2s;
z-index: 2;
}
.icon-container:hover .studio-button,
.studio-button:focus-visible {
opacity: 1;
}
.studio-button:hover,
.studio-button:focus-visible {
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
}
.icon-component.customizable {
will-change: width, height, stroke-width, stroke;
/* color: var(--customize-color, currentColor);
@@ -44,8 +44,12 @@ function setActiveIconName(name: string) {
activeIconName.value = name;
}
const { execute: fetchTags, data: tags } = useFetchTags();
const { execute: fetchCategories, data: categoriesMap } = useFetchCategories();
const { execute: fetchTags, data: tags, isFetching: isFetchingTags } = useFetchTags();
const {
execute: fetchCategories,
data: categoriesMap,
isFetching: isFetchingCategories,
} = useFetchCategories();
const overviewEl = ref<HTMLElement | null>(null);
const { width: containerWidth } = useElementSize(overviewEl);
@@ -117,6 +121,9 @@ const categoriesList = computed(() => {
}, []);
});
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
const isSearchMetadataLoading = computed(
() => searchQuery.value.length > 0 && (tags.value == null || categoriesMap.value == null),
);
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(categoriesList, {
itemHeight: ICON_SIZE + ICON_GRID_GAP,
@@ -135,15 +142,21 @@ onMounted(() => {
useEventListener(window, 'scroll', containerProps.onScroll);
});
function onFocusSearchInput() {
if (tags.value == null) {
fetchTags();
function loadSearchMetadata() {
if (tags.value == null && !isFetchingTags.value) {
void fetchTags();
}
if (categoriesMap.value == null) {
fetchCategories();
if (categoriesMap.value == null && !isFetchingCategories.value) {
void fetchCategories();
}
}
watch(searchQuery, (searchString) => {
if (searchString !== '') {
loadSearchMetadata();
}
});
const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
@@ -178,11 +191,11 @@ function handleCloseDrawer() {
:shortcut="kbdSearchShortcut"
class="input-wrapper"
ref="searchInput"
@focus="onFocusSearchInput"
@focus="loadSearchMetadata"
/>
</StickyBar>
<NoResults
v-if="searchPlaceholder.isNoResults"
v-if="searchPlaceholder.isNoResults && !isSearchMetadataLoading"
:searchQuery="searchPlaceholder.query"
:isBrandSearch="searchPlaceholder.isBrand"
@clear="searchQuery = ''"
@@ -52,8 +52,12 @@ const props = defineProps<{
const activeIconName = ref(null);
const selectedSort = ref(SORTING[0])
const { execute: fetchTags, data: tags } = useFetchTags();
const { execute: fetchCategories, data: categories } = useFetchCategories();
const { execute: fetchTags, data: tags, isFetching: isFetchingTags } = useFetchTags();
const {
execute: fetchCategories,
data: categories,
isFetching: isFetchingCategories,
} = useFetchCategories();
const overviewEl = ref<HTMLElement | null>(null);
const { width: containerWidth } = useElementSize(overviewEl);
@@ -110,6 +114,9 @@ const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
]);
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
const isSearchMetadataLoading = computed(
() => searchQuery.value.length > 0 && (tags.value == null || categories.value == null),
);
const chunkedIcons = computed(() => {
return chunkArray(searchResults.value, columnSize.value);
@@ -135,15 +142,21 @@ function setActiveIconName(name: string) {
activeIconName.value = name;
}
function onFocusSearchInput() {
if (tags.value == null) {
fetchTags();
function loadSearchMetadata() {
if (tags.value == null && !isFetchingTags.value) {
void fetchTags();
}
if (categories.value == null) {
fetchCategories();
if (categories.value == null && !isFetchingCategories.value) {
void fetchCategories();
}
}
watch(searchQuery, (searchString) => {
if (searchString !== '') {
loadSearchMetadata();
}
});
const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
@@ -179,7 +192,7 @@ function handleCloseDrawer() {
ref="searchInput"
:shortcut="kbdSearchShortcut"
class="input-wrapper"
@focus="onFocusSearchInput"
@focus="loadSearchMetadata"
/>
<Select
@@ -197,7 +210,7 @@ function handleCloseDrawer() {
</Select>
</StickyBar>
<NoResults
v-if="searchPlaceholder.isNoResults"
v-if="searchPlaceholder.isNoResults && !isSearchMetadataLoading"
:searchQuery="searchPlaceholder.query"
:isBrandSearch="searchPlaceholder.isBrand"
@clear="searchQuery = ''"
@@ -12,6 +12,10 @@ import Sandpack from '~/.vitepress/theme/components/editors/SandpackAngular.vue'
While they aren't provided as standalone components, they can be still be passed to the `LucideIcon` component the same way as official icons:
::: info Limitation
Only custom icons that follow Lucide's [code conventions](../../../contribute/icon-design-guide.md#code-conventions) are supported.
:::
### Directly as LucideIconData
::: sandpack {template=angular showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="@lucide/angular,@lucide/lab"}
+21 -20
View File
@@ -8,7 +8,7 @@
"docs:dev": "pnpm run /^prebuild:.*/ && vitepress dev",
"docs:build": "pnpm run /^prebuild:.*/ && vitepress build",
"docs:preview": "vitepress preview",
"build:docs": "vitepress build",
"build:docs": "NODE_OPTIONS=--max-old-space-size=8192 vitepress build",
"prebuild:iconNodes": "node ./scripts/writeIconNodes.mts",
"prebuild:metaJson": "node ./scripts/writeIconMetaIndex.mts",
"prebuild:releaseJson": "node ./scripts/writeReleaseMetadata.mts",
@@ -18,9 +18,9 @@
"prebuild:iconPopularity": "node ./scripts/writeiconPopularity.mts",
"prebuild:brandStopwords": "node ./scripts/writeBrandStopwords.mts",
"postbuild:vercelJson": "node ./scripts/writeVercelOutput.mts",
"dev": "npx nitropack dev",
"prebuild:api": "npx nitropack prepare",
"build:api": "npx nitropack build",
"dev": "nitropack dev",
"prebuild:api": "nitropack prepare",
"build:api": "NODE_OPTIONS=--max-old-space-size=8192 nitropack build",
"build": "pnpm run /^prebuild:.*/ && pnpm run build:api && pnpm run build:docs && pnpm postbuild:vercelJson",
"preview": "node .output/server/index.mjs"
},
@@ -32,31 +32,32 @@
"@lucide/shared": "workspace:*",
"@rollup/plugin-replace": "^6.0.3",
"@types/semver": "^7.7.1",
"h3": "^1.15.9",
"h3": "^1.15.11",
"markdown-it": "^14.3.0",
"markdown-it-container": "^4.0.0",
"nitropack": "^2.13.4",
"rollup-plugin-copy": "^3.5.0",
"svg-path-commander": "^2.1.11",
"vitepress": "^1.6.4",
"vitepress-plugin-group-icons": "^1.6.5",
"vitepress-plugin-llms": "^1.11.1",
"markdown-it": "^14.2.0",
"markdown-it-container": "^4.0.0",
"semver": "^7.7.3",
"semver": "^7.8.5",
"sharp": "^0.34.5",
"shiki": "^3.15.0",
"shiki": "^3.23.0",
"simple-git": "^3.36.0",
"sitemap": "^7.1.2",
"sitemap": "^7.1.3",
"svg-path-commander": "^2.2.1",
"svg-pathdata": "^6.0.3",
"svgson": "^5.3.1"
"svgson": "^5.3.1",
"vitepress": "^1.6.4",
"vitepress-plugin-group-icons": "^1.7.5",
"vitepress-plugin-llms": "^1.13.3"
},
"dependencies": {
"@floating-ui/vue": "^1.1.9",
"@floating-ui/vue": "^1.1.11",
"@headlessui/vue": "^1.7.23",
"@lucide/vue": "workspace:*",
"@vueuse/components": "^14.0.0",
"@vueuse/core": "^14.0.0",
"@vueuse/components": "^14.3.0",
"@vueuse/core": "^14.3.0",
"@vueuse/integrations": "^14.3.0",
"element-to-path": "^1.2.1",
"fuse.js": "^7.1.0",
"fuse.js": "^7.5.0",
"jszip": "^3.10.1",
"lodash-es": "^4.18.1",
"lucide": "workspace:*",
@@ -65,6 +66,6 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"sandpack-vue3": "3.1.12",
"vue": "^3.5.24"
"vue": "^3.5.39"
}
}
+7 -7
View File
@@ -23,12 +23,12 @@ const writeIconFiles = icons.map(async (iconFileName) => {
const location = path.resolve(iconDetailsDirectory, `${iconName}.ts`);
const contents = `\
import iconNode from '../iconNodes/${iconName}.node.json'
import metaData from '../../../../icons/${iconName}.json'
import releaseData from '../releaseMetadata/${iconName}.json'
import popularity from '../iconPopularity/${iconName}.json'
import iconNode from '../iconNodes/${iconName}.node.json' with { type: 'json' };
import metaData from '../../../../icons/${iconName}.json' with { type: 'json' };
import releaseData from '../releaseMetadata/${iconName}.json' with { type: 'json' };
import popularity from '../iconPopularity/${iconName}.json' with { type: 'json' };
const { tags, categories, contributors, aliases, deprecated, deprecationReason, toBeRemovedInVersion } = metaData
const { tags, categories, contributors, aliases, deprecated, deprecationReason, toBeRemovedInVersion } = metaData;
const iconDetails = {
name: '${iconName}',
@@ -42,9 +42,9 @@ const iconDetails = {
deprecationReason,
toBeRemovedInVersion,
...releaseData,
}
};
export default iconDetails
export default iconDetails;
`;
await fs.promises.writeFile(location, contents, 'utf-8');
+6 -1
View File
@@ -21,10 +21,15 @@
"paid",
"partner",
"promo",
"sponsor"
"sponsor",
"audio description",
"video description",
"described video",
"visual description"
],
"categories": [
"multimedia",
"accessibility",
"notifications"
]
}
+2 -2
View File
@@ -9,7 +9,7 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 21V7" />
<path d="M12 5v16" />
<path d="m16 12 2 2 4-4" />
<path d="M22 6V4a1 1 0 0 0-1-1h-5a4 4 0 0 0-4 4 4 4 0 0 0-4-4H3a1 1 0 0 0-1 1v13a1 1 0 0 0 1 1h6a3 3 0 0 1 3 3 3 3 0 0 1 3-3h6a1 1 0 0 0 1-1v-1.3" />
<path d="M22 6V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2h4.001A2 2 0 0022 17v-1.344" />
</svg>

Before

Width:  |  Height:  |  Size: 414 B

After

Width:  |  Height:  |  Size: 424 B

+2 -1
View File
@@ -1,7 +1,8 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"danielbayley"
"danielbayley",
"karsa-mistmere"
],
"use-cases": [],
"tags": [
+6 -6
View File
@@ -9,10 +9,10 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 7v14" />
<path d="M16 12h2" />
<path d="M16 8h2" />
<path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z" />
<path d="M6 12h2" />
<path d="M6 8h2" />
<path d="M12 5v16" />
<path d="M16 13h2" />
<path d="M16 9h2" />
<path d="M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z" />
<path d="M6 13h2" />
<path d="M6 9h2" />
</svg>

Before

Width:  |  Height:  |  Size: 470 B

After

Width:  |  Height:  |  Size: 479 B

+2 -1
View File
@@ -3,7 +3,8 @@
"contributors": [
"colebemis",
"csandman",
"ericfennis"
"ericfennis",
"karsa-mistmere"
],
"use-cases": [],
"tags": [
+2 -2
View File
@@ -9,6 +9,6 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 7v14" />
<path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z" />
<path d="M12 5v16" />
<path d="M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z" />
</svg>

Before

Width:  |  Height:  |  Size: 378 B

After

Width:  |  Height:  |  Size: 387 B

+12 -3
View File
@@ -7,9 +7,18 @@
],
"use-cases": [],
"tags": [
"chromecast",
"airplay",
"screen"
"stream",
"streaming",
"screen",
"display",
"wireless",
"broadcast",
"transmit",
"media",
"receiver",
"remote display",
"screen sharing",
"media streaming"
],
"categories": [
"devices",
-4
View File
@@ -8,10 +8,6 @@
"tags": [
"stories",
"social media",
"instagram",
"facebook",
"meta",
"snapchat",
"sharing",
"content"
],
+2 -1
View File
@@ -3,7 +3,8 @@
"contributors": [
"irvineacosta",
"danielbayley",
"karsa-mistmere"
"karsa-mistmere",
"kamilasw"
],
"use-cases": [],
"tags": [
+11 -11
View File
@@ -9,16 +9,16 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10.5 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v5.5" />
<path d="m14.3 19.6 1-.4" />
<path d="M15 3v7.5" />
<path d="m15.2 16.9-.9-.3" />
<path d="m16.6 21.7.3-.9" />
<path d="m16.8 15.3-.4-1" />
<path d="m19.1 15.2.3-.9" />
<path d="m19.6 21.7-.4-1" />
<path d="m20.7 16.8 1-.4" />
<path d="m21.7 19.4-.9-.3" />
<path d="M10.6 21H5a2 2 0 01-2-2V5a2 2 0 012-2h14a2 2 0 012 2v5.6" />
<path d="m14.305 19.53.923-.382" />
<path d="M15 3v7.6" />
<path d="m15.229 16.852-.924-.383" />
<path d="m16.852 15.228-.383-.923" />
<path d="m16.852 20.772-.383.924" />
<path d="m19.148 15.228.383-.923" />
<path d="m19.53 21.696-.382-.924" />
<path d="m20.773 16.852.922-.383" />
<path d="m20.773 19.148.922.383" />
<path d="M9 3v18" />
<circle cx="18" cy="18" r="3" />
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 617 B

After

Width:  |  Height:  |  Size: 675 B

-1
View File
@@ -11,7 +11,6 @@
"east",
"south",
"west",
"safari",
"browser"
],
"categories": [
+1 -2
View File
@@ -7,8 +7,7 @@
"tags": [
"pc",
"chassis",
"codespaces",
"github"
"codespaces"
],
"categories": [
"devices",
-1
View File
@@ -9,7 +9,6 @@
"shipping",
"freight",
"supply chain",
"docker",
"environment",
"devops",
"code",
+2 -1
View File
@@ -1,7 +1,8 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"ericfennis"
"ericfennis",
"karsa-mistmere"
],
"use-cases": [],
"tags": [
+2 -2
View File
@@ -9,9 +9,9 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m15 20 3-3h2a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h2l3 3z" />
<path d="M6 8v1" />
<path d="M10 8v1" />
<path d="M14 8v1" />
<path d="M18 8v1" />
<path d="M19 17a2 2 0 00-1.765 1.059l-.47.882A2 2 0 0115 20H9a2 2 0 01-1.765-1.059l-.47-.882A2 2 0 005 17H4a2 2 0 01-2-2V6a2 2 0 012-2h16a2 2 0 012 2v9a2 2 0 01-2 2z" />
<path d="M6 8v1" />
</svg>

Before

Width:  |  Height:  |  Size: 396 B

After

Width:  |  Height:  |  Size: 471 B

+26 -3
View File
@@ -4,13 +4,36 @@
"karsa-mistmere",
"danielbayley"
],
"use-cases": [],
"use-cases": [
"representing 3D model files such as OBJ, STL, FBX, or GLTF",
"indicating CAD and engineering model documents",
"showing printable or manufacturable 3D assets",
"identifying files containing digital product models"
],
"tags": [
"document",
"page",
"sheet",
"cube",
"box",
"3d",
"model",
"asset",
"object",
"geometry",
"mesh",
"ar",
"augmented reality",
"cad",
"design",
"blueprint",
"draft",
"package",
"model"
"scene"
],
"categories": [
"files"
"files",
"design",
"development"
]
}
+5 -5
View File
@@ -9,9 +9,9 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M14.5 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v3.8" />
<path d="M14 2v5a1 1 0 0 0 1 1h5" />
<path d="M11.7 14.2 7 17l-4.7-2.8" />
<path d="M3 13.1a2 2 0 0 0-.999 1.76v3.24a2 2 0 0 0 .969 1.78L6 21.7a2 2 0 0 0 2.03.01L11 19.9a2 2 0 0 0 1-1.76V14.9a2 2 0 0 0-.97-1.78L8 11.3a2 2 0 0 0-2.03-.01z" />
<path d="M7 17v5" />
<path d="M14 2v5a1 1 0 001 1h5" />
<path d="M14.692 22H18a2 2 0 002-2V8a2.4 2.4 0 00-.706-1.706l-3.588-3.588A2.4 2.4 0 0014 2H6a2 2 0 00-2 2v3.804" />
<path d="M2.264 13.752 7 16.5l4.737-2.748" />
<path d="M2.995 13.014A2 2 0 002 14.744v3.516a2 2 0 00.996 1.73l3 1.74a2 2 0 002.008 0l3-1.74A2 2 0 0012 18.26v-3.517a2 2 0 00-.995-1.73l-3-1.742a2 2 0 00-1.892-.064z" />
<path d="M7 16.5V22" />
</svg>

Before

Width:  |  Height:  |  Size: 599 B

After

Width:  |  Height:  |  Size: 610 B

+2 -1
View File
@@ -1,7 +1,8 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"danielbayley"
"danielbayley",
"karsa-mistmere"
],
"use-cases": [],
"tags": [
+2 -2
View File
@@ -9,6 +9,6 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M22 9a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v4a1 1 0 0 0 1 1h1l2 2h12l2-2h1a1 1 0 0 0 1-1Z" />
<path d="M7.5 12h9" />
<path d="M22 9a1 1 0 00-1-1H3a1 1 0 00-1 1v4a1 1 0 001 1h.5a2 2 0 011.6.8l.3.4A2 2 0 007 16h10a2 2 0 001.6-.8l.3-.4a2 2 0 011.6-.8h.5a1 1 0 001-1z" />
<path d="M8 12h8" />
</svg>

Before

Width:  |  Height:  |  Size: 330 B

After

Width:  |  Height:  |  Size: 384 B

-26
View File
@@ -1,26 +0,0 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"ericfennis",
"danielbayley",
"jguddas",
"karsa-mistmere"
],
"use-cases": [],
"tags": [
"time",
"redo",
"undo",
"rewind",
"timeline",
"version",
"time machine",
"backup",
"rotate",
"ccw"
],
"categories": [
"arrows",
"time"
]
}
+38
View File
@@ -0,0 +1,38 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"ericfennis",
"danielbayley",
"jguddas",
"karsa-mistmere"
],
"use-cases": [
"To view the activity history of an account or document.",
"To browse past events in a log or audit trail.",
"To show recent actions, such as recently visited pages.",
"To restore an earlier version of a file."
],
"tags": [
"history",
"time",
"redo",
"undo",
"rewind",
"timeline",
"version",
"time machine",
"backup",
"ccw"
],
"categories": [
"arrows",
"time"
],
"aliases": [
{
"name": "history",
"deprecated": true,
"deprecationReason": "alias.name"
}
]
}

Before

Width:  |  Height:  |  Size: 324 B

After

Width:  |  Height:  |  Size: 324 B

+24
View File
@@ -0,0 +1,24 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"NielsLazaroms"
],
"use-cases": [
"To indicate when you need to process items.",
"To indicate that a process is happening"
],
"tags": [
"clock",
"waiting",
"schedule",
"hourglass",
"loading",
"pause",
"pending",
"time",
"watch"
],
"categories": [
"time"
]
}
+22
View File
@@ -0,0 +1,22 @@
<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"
>
<path d="M12 3a9.75 9.75 0 0 1 6.74 2.74" />
<path d="M18.74 5.74 21 8" />
<path d="M21 8V3" />
<path d="M7.5 19.794c-6-3.464-6-12.124 0-15.588" />
<path d="M7.5 4.206A9 9 0 0 1 12 3" />
<path d="M12 7v5l4 2" />
<path d="M14 20.775A9 9 0 0 1 12 21" />
<path d="M19 17.656a9 9 0 0 1-1.5 1.456" />
<path d="M21 12a9 9 0 0 1-.228 2" />
<path d="M21 8h-5" />
</svg>

After

Width:  |  Height:  |  Size: 583 B

+46
View File
@@ -0,0 +1,46 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"wojtekmaj",
"ericfennis",
"karsa-mistmere"
],
"use-cases": [
"starting an augmented reality scan",
"placing virtual 3D objects in a real-world scene",
"indicating spatial mapping or room scanning",
"previewing products with virtual try-on",
"recognizing the surrounding environment for AR experiences",
"scanning an object to create a 3D model",
"measuring room dimensions for interior planning",
"calibrating depth sensors or LiDAR",
"placing furniture or decor in a room using AR"
],
"tags": [
"ar",
"augmented reality",
"3d",
"object detection",
"object recognition",
"tracking",
"spatial computing",
"capture",
"cube",
"bounding box",
"camera",
"frame",
"shape",
"boundary",
"lidar",
"depth",
"scanning",
"mapping",
"placement"
],
"categories": [
"design",
"devices",
"shopping",
"gaming"
]
}
+19
View File
@@ -0,0 +1,19 @@
<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"
>
<path d="M12 12v5.5" />
<path d="M17 3h2a2 2 0 012 2v2" />
<path d="M21 17v2a2 2 0 01-2 2h-2" />
<path d="M3 7V5a2 2 0 012-2h2" />
<path d="M7 21H5a2 2 0 01-2-2v-2" />
<path d="M7.264 9.252 12 12l4.737-2.748" />
<path d="M7.995 8.514A2 2 0 007 10.244v3.516a2 2 0 00.996 1.73l3 1.74a2 2 0 002.008 0l3-1.74A2 2 0 0017 13.76v-3.517a2 2 0 00-.995-1.73l-3-1.742a2 2 0 00-1.892-.064z" />
</svg>

After

Width:  |  Height:  |  Size: 604 B

+54
View File
@@ -0,0 +1,54 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"karsa-mistmere"
],
"use-cases": [
"Indicating that a resource or area is protected and requires authorized access",
"Representing access control and permission management settings",
"Showing security options for accounts, applications, or protected systems",
"Indicating content or functionality restricted to authenticated users",
"Representing authentication and secure sign-in features",
"Showing protected credentials, secrets, or other sensitive information",
"Representing identity and account protection features",
"Indicating security policies or authorization requirements"
],
"tags": [
"cybersecurity",
"secure",
"safety",
"protection",
"defense",
"defence",
"defender",
"block",
"threat",
"prevention",
"antivirus",
"vigilance",
"vigilant",
"detection",
"scan",
"find",
"strength",
"strong",
"tough",
"invincible",
"invincibility",
"invulnerable",
"undamaged",
"audit",
"admin",
"verification",
"crest",
"bravery",
"trooper",
"pawn"
],
"categories": [
"account",
"security",
"development",
"files"
]
}
+15
View File
@@ -0,0 +1,15 @@
<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"
>
<path d="M12 13v3" />
<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 01-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 011-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 011.52 0C14.51 3.81 17 5 19 5a1 1 0 011 1z" />
<circle cx="12" cy="11" r="2" />
</svg>

After

Width:  |  Height:  |  Size: 438 B

+7 -13
View File
@@ -9,18 +9,12 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="5" y1="3" x2="19" y2="3" />
<line x1="3" y1="5" x2="3" y2="19" />
<line x1="21" y1="5" x2="21" y2="19" />
<line x1="9" y1="21" x2="10" y2="21" />
<line x1="14" y1="21" x2="15" y2="21" />
<path d="M 3 5 A2 2 0 0 1 5 3" />
<path d="M 19 3 A2 2 0 0 1 21 5" />
<path d="M 5 21 A2 2 0 0 1 3 19" />
<path d="M 21 19 A2 2 0 0 1 19 21" />
<circle cx="8.5" cy="8.5" r="1.5" />
<line x1="9.56066" y1="9.56066" x2="12" y2="12" />
<line x1="17" y1="17" x2="14.82" y2="14.82" />
<path d="M14 21h1" />
<path d="m17 17-2.18-2.18" />
<path d="M5 21a2 2 0 01-2-2V5a2 2 0 012-2h14a2 2 0 012 2v14a2 2 0 01-2 2" />
<path d="M9 21h1" />
<path d="M9.56 14.44 17 7" />
<path d="M9.56 9.56 12 12" />
<circle cx="8.5" cy="15.5" r="1.5" />
<line x1="9.56066" y1="14.43934" x2="17" y2="7" />
<circle cx="8.5" cy="8.5" r="1.5" />
</svg>

Before

Width:  |  Height:  |  Size: 801 B

After

Width:  |  Height:  |  Size: 509 B

+5 -5
View File
@@ -9,10 +9,10 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="18" height="18" x="3" y="3" rx="2" />
<circle cx="8.5" cy="8.5" r="1.5" />
<line x1="9.56066" y1="9.56066" x2="12" y2="12" />
<line x1="17" y1="17" x2="14.82" y2="14.82" />
<path d="m17 17-2.18-2.18" />
<path d="M9.56 14.44 17 7" />
<path d="M9.56 9.56 12 12" />
<circle cx="8.5" cy="15.5" r="1.5" />
<line x1="9.56066" y1="14.43934" x2="17" y2="7" />
<circle cx="8.5" cy="8.5" r="1.5" />
<rect x="3" y="3" width="18" height="18" rx="2" />
</svg>

Before

Width:  |  Height:  |  Size: 495 B

After

Width:  |  Height:  |  Size: 436 B

-1
View File
@@ -8,7 +8,6 @@ const filenamesToAjvOption = (filenames) => filenames.map((filename) => `-d ${fi
const config = {
'icons/*.svg': [
'node ./scripts/optimizeStagedSvgs.mts',
'node ./scripts/generateNextJSAliases.mts',
],
'icons/*.json': (filenames) => [
`ajv --spec=draft2020 -s icon.schema.json ${filenamesToAjvOption(filenames)}`,
+10 -11
View File
@@ -24,7 +24,6 @@
"generate:changelog": "node ./scripts/generateChangelog.mts",
"generate:sponsors": "node scripts/updateSponsors.mts",
"generate:contributors": "node ./scripts/updateContributors.mts icons/*.svg",
"generate:nextJSAliases": "node ./scripts/generateNextJSAliases.mts",
"suggest:metadata": "node ./scripts/suggestMetaData.mts",
"suggest:metadata:watch": "node --env-file .env --watch ./scripts/suggestMetaData.mts",
"postinstall": "husky install",
@@ -42,16 +41,16 @@
"renamePattern": "node ./scripts/rename/renamePattern.mts"
},
"devDependencies": {
"@actions/core": "^1.11.1",
"@actions/github": "^6.0.1",
"@actions/core": "^3.0.1",
"@actions/github": "^9.1.1",
"@html-eslint/eslint-plugin": "^0.19.1",
"@html-eslint/parser": "^0.19.1",
"@octokit/rest": "^19.0.13",
"@types/yargs": "^17.0.35",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"@vitest/coverage-v8": "4.0.12",
"@vitest/ui": "4.0.12",
"@vitest/coverage-v8": "4.1.10",
"@vitest/ui": "4.1.10",
"ajv-cli": "^5.0.0",
"dotenv": "^17.4.2",
"eslint": "^8.57.1",
@@ -64,22 +63,22 @@
"eslint-plugin-import": "^2.32.0",
"husky": "^8.0.3",
"jsdom": "^27.4.0",
"lint-staged": "^13.3.0",
"lint-staged": "^17.0.8",
"minimist": "^1.2.8",
"openai": "^5.23.2",
"p-memoize": "^7.1.1",
"prettier": "3.2.4",
"prettier-plugin-astro": "^0.14.1",
"semver": "^7.8.0",
"semver": "^7.8.5",
"simple-git": "^3.36.0",
"svgo": "^3.3.3",
"svgo": "^3.3.4",
"svgson": "^5.3.1",
"vitest": "4.1.0",
"yargs": "^17.7.2",
"vitest": "4.1.10",
"yargs": "^17.7.3",
"zod": "^3.25.76"
},
"engines": {
"node": ">=24.11.1"
},
"packageManager": "pnpm@11.6.0+sha512.9a36518224080c6fe5165afdcfe79bfa118c29be703f3f462b1e32efe1e98e47e8750b148e08286250aad4113cc7993ca413c4e2cd447752708c2ee5751bc95f"
"packageManager": "pnpm@11.16.0+sha512.b767e9a98fc87aec0f42daefcd0e84941c2cdb45d73c4e1e68860fb2bdfdbe032ab592105479e5e05c237f740c8a5ffdbbb52385797ddc2296745a1bbb883e8d"
}
+15 -15
View File
@@ -46,24 +46,24 @@
"@angular-eslint/eslint-plugin-template": "~21.1.0",
"@angular-eslint/schematics": "~21.1.0",
"@angular-eslint/template-parser": "~21.1.0",
"@angular/build": "^21.0.3",
"@angular/cli": "^21.0.3",
"@angular/common": "^21.2.17",
"@angular/compiler": "^21.2.17",
"@angular/compiler-cli": "^21.0.0",
"@angular/core": "^21.2.17",
"@angular/forms": "^21.0.0",
"@angular/platform-browser": "^21.0.0",
"@angular/platform-server": "^21.2.17",
"@angular/router": "^21.0.0",
"@angular/build": "^21.2.19",
"@angular/cli": "^21.2.19",
"@angular/common": "^21.2.18",
"@angular/compiler": "^21.2.18",
"@angular/compiler-cli": "^21.2.18",
"@angular/core": "^21.2.18",
"@angular/forms": "^21.2.18",
"@angular/platform-browser": "^21.2.18",
"@angular/platform-server": "^21.2.18",
"@angular/router": "^21.2.18",
"@lucide/build-icons": "workspace:*",
"@lucide/helpers": "workspace:*",
"@vitest/browser-playwright": "^4.0.12",
"@vitest/browser-playwright": "^4.1.10",
"angular-eslint": "21.1.0",
"ng-packagr": "^21.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"typescript": "~5.9.2"
"ng-packagr": "^21.2.5",
"rxjs": "~7.8.2",
"tslib": "^2.8.1",
"typescript": "~5.9.3"
},
"peerDependencies": {
"@angular/common": ">=17.0.0",
+125 -14
View File
@@ -43,13 +43,124 @@ describe('LucideDynamicIcon', () => {
const supportedShapesIcon: LucideIconData = {
name: 'supported-shapes',
node: [
['path', { d: 'm1 1 2 2', fill: 'currentColor', key: 'path-key' }],
['line', { x1: 1, x2: 2, y1: 3, y2: 4, key: 'line-key' }],
['polygon', { points: '1 1 2 2 3 1', key: 'polygon-key' }],
['polyline', { points: '1 1 2 2 3 1', key: 'polyline-key' }],
['circle', { cx: 12, cy: 12, r: 8, fill: 'currentColor', key: 'circle-key' }],
['ellipse', { cx: 12, cy: 12, rx: 8, ry: 4, key: 'ellipse-key' }],
['rect', { x: 1, y: 2, width: 3, height: 4, rx: 5, ry: 6, key: 'rect-key' }],
[
'path',
{
class: 'path-class',
d: 'm1 1 2 2',
id: 'path-id',
opacity: '0.7',
stroke: 'red',
'stroke-opacity': '0.5',
fill: 'blue',
'fill-opacity': '0.3',
'vector-effect': 'non-scaling-stroke',
key: 'path-key',
},
],
[
'line',
{
class: 'line-class',
id: 'line-id',
opacity: '0.7',
stroke: 'red',
'stroke-opacity': '0.5',
fill: 'blue',
'fill-opacity': '0.3',
x1: 1,
x2: 2,
y1: 3,
y2: 4,
'vector-effect': 'non-scaling-stroke',
key: 'line-key',
},
],
[
'polygon',
{
class: 'polygon-class',
id: 'polygon-id',
opacity: '0.7',
stroke: 'red',
'stroke-opacity': '0.5',
fill: 'blue',
'fill-opacity': '0.3',
points: '1 1 2 2 3 1',
'vector-effect': 'non-scaling-stroke',
key: 'polygon-key',
},
],
[
'polyline',
{
class: 'polyline-class',
id: 'polyline-id',
opacity: '0.7',
stroke: 'red',
'stroke-opacity': '0.5',
fill: 'blue',
'fill-opacity': '0.3',
points: '1 1 2 2 3 1',
'vector-effect': 'non-scaling-stroke',
key: 'polyline-key',
},
],
[
'circle',
{
class: 'circle-class',
cx: 12,
cy: 12,
id: 'circle-id',
opacity: '0.7',
r: 8,
stroke: 'red',
'stroke-opacity': '0.5',
fill: 'blue',
'fill-opacity': '0.3',
'vector-effect': 'non-scaling-stroke',
key: 'circle-key',
},
],
[
'ellipse',
{
class: 'ellipse-class',
cx: 12,
cy: 12,
id: 'ellipse-id',
opacity: '0.7',
rx: 8,
ry: 4,
stroke: 'red',
'stroke-opacity': '0.5',
fill: 'blue',
'fill-opacity': '0.3',
'vector-effect': 'non-scaling-stroke',
key: 'ellipse-key',
},
],
[
'rect',
{
class: 'rect-class',
x: 1,
y: 2,
id: 'rect-id',
opacity: '0.7',
width: 3,
height: 4,
rx: 5,
ry: 6,
stroke: 'red',
'stroke-opacity': '0.5',
fill: 'blue',
'fill-opacity': '0.3',
'vector-effect': 'non-scaling-stroke',
key: 'rect-key',
},
],
],
};
function createComponent() {
@@ -88,13 +199,13 @@ describe('LucideDynamicIcon', () => {
fixture.detectChanges();
const children = getRenderedChildren();
expect(children.map((child) => child.outerHTML)).toEqual([
'<path d="m1 1 2 2" fill="currentColor"></path>',
'<line x1="1" x2="2" y1="3" y2="4"></line>',
'<polygon points="1 1 2 2 3 1"></polygon>',
'<polyline points="1 1 2 2 3 1"></polyline>',
'<circle cx="12" cy="12" r="8" fill="currentColor"></circle>',
'<ellipse cx="12" cy="12" rx="8" ry="4"></ellipse>',
'<rect x="1" y="2" width="3" height="4" rx="5" ry="6"></rect>',
'<path class="path-class" d="m1 1 2 2" id="path-id" opacity="0.7" stroke="red" stroke-opacity="0.5" fill="blue" fill-opacity="0.3" vector-effect="non-scaling-stroke"></path>',
'<line class="line-class" id="line-id" opacity="0.7" stroke="red" stroke-opacity="0.5" fill="blue" fill-opacity="0.3" x1="1" x2="2" y1="3" y2="4" vector-effect="non-scaling-stroke"></line>',
'<polygon class="polygon-class" id="polygon-id" opacity="0.7" stroke="red" stroke-opacity="0.5" fill="blue" fill-opacity="0.3" points="1 1 2 2 3 1" vector-effect="non-scaling-stroke"></polygon>',
'<polyline class="polyline-class" id="polyline-id" opacity="0.7" stroke="red" stroke-opacity="0.5" fill="blue" fill-opacity="0.3" points="1 1 2 2 3 1" vector-effect="non-scaling-stroke"></polyline>',
'<circle class="circle-class" cx="12" cy="12" id="circle-id" opacity="0.7" r="8" stroke="red" stroke-opacity="0.5" fill="blue" fill-opacity="0.3" vector-effect="non-scaling-stroke"></circle>',
'<ellipse class="ellipse-class" cx="12" cy="12" id="ellipse-id" opacity="0.7" rx="8" ry="4" stroke="red" stroke-opacity="0.5" fill="blue" fill-opacity="0.3" vector-effect="non-scaling-stroke"></ellipse>',
'<rect class="rect-class" x="1" y="2" id="rect-id" opacity="0.7" width="3" height="4" rx="5" ry="6" stroke="red" stroke-opacity="0.5" fill="blue" fill-opacity="0.3" vector-effect="non-scaling-stroke"></rect>',
]);
});
@@ -11,13 +11,26 @@ export const lucideIconTemplate = `@if (title(); as titleValue) {
@switch (child[0]) {
@case ('path') {
<svg:path
[attr.class]="attrs['class']"
[attr.d]="attrs['d']"
[attr.id]="attrs['id']"
[attr.opacity]="attrs['opacity']"
[attr.stroke]="attrs['stroke']"
[attr.stroke-opacity]="attrs['stroke-opacity']"
[attr.fill]="attrs['fill']"
[attr.fill-opacity]="attrs['fill-opacity']"
[attr.vector-effect]="attrs['vector-effect']"
/>
}
@case ('line') {
<svg:line
[attr.class]="attrs['class']"
[attr.id]="attrs['id']"
[attr.opacity]="attrs['opacity']"
[attr.stroke]="attrs['stroke']"
[attr.stroke-opacity]="attrs['stroke-opacity']"
[attr.fill]="attrs['fill']"
[attr.fill-opacity]="attrs['fill-opacity']"
[attr.x1]="attrs['x1']"
[attr.x2]="attrs['x2']"
[attr.y1]="attrs['y1']"
@@ -27,42 +40,76 @@ export const lucideIconTemplate = `@if (title(); as titleValue) {
}
@case ('polygon') {
<svg:polygon
[attr.class]="attrs['class']"
[attr.id]="attrs['id']"
[attr.opacity]="attrs['opacity']"
[attr.stroke]="attrs['stroke']"
[attr.stroke-opacity]="attrs['stroke-opacity']"
[attr.fill]="attrs['fill']"
[attr.fill-opacity]="attrs['fill-opacity']"
[attr.points]="attrs['points']"
[attr.vector-effect]="attrs['vector-effect']"
/>
}
@case ('polyline') {
<svg:polyline
[attr.class]="attrs['class']"
[attr.id]="attrs['id']"
[attr.opacity]="attrs['opacity']"
[attr.stroke]="attrs['stroke']"
[attr.stroke-opacity]="attrs['stroke-opacity']"
[attr.fill]="attrs['fill']"
[attr.fill-opacity]="attrs['fill-opacity']"
[attr.points]="attrs['points']"
[attr.vector-effect]="attrs['vector-effect']"
/>
}
@case ('circle') {
<svg:circle
[attr.class]="attrs['class']"
[attr.cx]="attrs['cx']"
[attr.cy]="attrs['cy']"
[attr.id]="attrs['id']"
[attr.opacity]="attrs['opacity']"
[attr.r]="attrs['r']"
[attr.stroke]="attrs['stroke']"
[attr.stroke-opacity]="attrs['stroke-opacity']"
[attr.fill]="attrs['fill']"
[attr.fill-opacity]="attrs['fill-opacity']"
[attr.vector-effect]="attrs['vector-effect']"
/>
}
@case ('ellipse') {
<svg:ellipse
[attr.class]="attrs['class']"
[attr.cx]="attrs['cx']"
[attr.cy]="attrs['cy']"
[attr.id]="attrs['id']"
[attr.opacity]="attrs['opacity']"
[attr.rx]="attrs['rx']"
[attr.ry]="attrs['ry']"
[attr.stroke]="attrs['stroke']"
[attr.stroke-opacity]="attrs['stroke-opacity']"
[attr.fill]="attrs['fill']"
[attr.fill-opacity]="attrs['fill-opacity']"
[attr.vector-effect]="attrs['vector-effect']"
/>
}
@case ('rect') {
<svg:rect
[attr.class]="attrs['class']"
[attr.x]="attrs['x']"
[attr.y]="attrs['y']"
[attr.id]="attrs['id']"
[attr.opacity]="attrs['opacity']"
[attr.width]="attrs['width']"
[attr.height]="attrs['height']"
[attr.rx]="attrs['rx']"
[attr.ry]="attrs['ry']"
[attr.stroke]="attrs['stroke']"
[attr.stroke-opacity]="attrs['stroke-opacity']"
[attr.fill]="attrs['fill']"
[attr.fill-opacity]="attrs['fill-opacity']"
[attr.vector-effect]="attrs['vector-effect']"
/>
}
+9 -9
View File
@@ -48,17 +48,17 @@
"test:watch": "pnpm build:icons && vitest run --watch"
},
"devDependencies": {
"@astrojs/ts-plugin": "^1.10.4",
"@astrojs/ts-plugin": "^1.10.10",
"@lucide/build-icons": "workspace:*",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.9.1",
"astro": "^6.4.8",
"jest-serializer-html": "^7.1.0",
"linkedom": "^0.18.5",
"prettier": "^3.4.2",
"typescript": "^5.8.3",
"vite": "^7.3.2",
"vitest": "^4.1.0",
"astro": "^6.4.6"
"linkedom": "^0.18.13",
"prettier": "^3.9.5",
"typescript": "^5.9.3",
"vite": "^7.3.6",
"vitest": "4.1.10"
},
"peerDependencies": {
"astro": "^4 || ^5 || ^6 || ^7"
+7 -7
View File
@@ -63,14 +63,14 @@
"@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",
"@rollup/plugin-replace": "^6.0.3",
"@testing-library/jest-dom": "^6.9.1",
"jest-serializer-html": "^7.1.0",
"rollup": "^4.53.3",
"rollup-plugin-dts": "^6.2.3",
"rollup": "^4.62.2",
"rollup-plugin-dts": "^6.4.1",
"rollup-plugin-preserve-directives": "^0.4.0",
"typescript": "^5.8.3",
"vite": "^7.2.4",
"vitest": "^4.1.0"
"typescript": "^5.9.3",
"vite": "^7.3.6",
"vitest": "4.1.10"
}
}
+8 -8
View File
@@ -50,15 +50,15 @@
"@lucide/build-icons": "workspace:*",
"@lucide/rollup-plugins": "workspace:*",
"@lucide/shared": "workspace:*",
"@preact/preset-vite": "^2.10.2",
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/preact": "^3.2.3",
"@preact/preset-vite": "^2.10.5",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/preact": "^3.2.4",
"jest-serializer-html": "^7.1.0",
"preact": "^10.27.3",
"rollup": "^4.59.0",
"rollup-plugin-dts": "^6.2.3",
"typescript": "^5.8.3",
"vite": "^7.2.4"
"preact": "^10.29.7",
"rollup": "^4.62.2",
"rollup-plugin-dts": "^6.4.1",
"typescript": "^5.9.3",
"vite": "^7.3.6"
},
"peerDependencies": {
"preact": "^10.27.2"
+13 -13
View File
@@ -68,20 +68,20 @@
"@lucide/helpers": "workspace:*",
"@lucide/rollup-plugins": "workspace:*",
"@lucide/shared": "workspace:*",
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/react": "^14.1.2",
"@types/prop-types": "^15.7.5",
"@types/react": "^18.0.21",
"@vitejs/plugin-react": "^4.2.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^14.3.1",
"@types/prop-types": "^15.7.15",
"@types/react": "^18.3.31",
"@vitejs/plugin-react": "^4.7.0",
"jest-serializer-html": "^7.1.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-native": "^0.76.0",
"react-native-svg": "^15.8.0",
"rollup": "^4.59.0",
"rollup-plugin-dts": "^6.2.3",
"typescript": "^5.8.3",
"vite": "^7.2.4"
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-native": "^0.76.9",
"react-native-svg": "^15.15.5",
"rollup": "^4.62.2",
"rollup-plugin-dts": "^6.4.1",
"typescript": "^5.9.3",
"vite": "^7.3.6"
},
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+8 -8
View File
@@ -53,18 +53,18 @@
"@lucide/build-icons": "workspace:*",
"@lucide/rollup-plugins": "workspace:*",
"@lucide/shared": "workspace:*",
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/react": "^14.1.2",
"@types/react": "^18.2.37",
"@vitejs/plugin-react": "^4.6.0",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^14.3.1",
"@types/react": "^18.3.31",
"@vitejs/plugin-react": "^4.7.0",
"jest-serializer-html": "^7.1.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"rollup": "^4.59.0",
"rollup-plugin-dts": "^6.2.3",
"rollup": "^4.62.2",
"rollup-plugin-dts": "^6.4.1",
"rollup-plugin-preserve-directives": "^0.4.0",
"typescript": "^5.8.3",
"vite": "^7.2.4"
"typescript": "^5.9.3",
"vite": "^7.3.6"
},
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+12 -12
View File
@@ -68,23 +68,23 @@
"test:watch": "vitest watch"
},
"devDependencies": {
"@babel/core": "^7.23.9",
"@babel/preset-env": "^7.23.9",
"@babel/preset-typescript": "^7.23.3",
"@babel/core": "^7.29.7",
"@babel/preset-env": "^7.29.7",
"@babel/preset-typescript": "^7.29.7",
"@lucide/build-icons": "workspace:*",
"@lucide/rollup-plugins": "workspace:*",
"@lucide/shared": "workspace:*",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-babel": "^6.1.0",
"@solidjs/testing-library": "^0.8.10",
"@testing-library/jest-dom": "^6.8.0",
"babel-preset-solid": "^1.8.12",
"@testing-library/jest-dom": "^6.9.1",
"babel-preset-solid": "^1.9.12",
"esbuild": "^0.28.1",
"jest-serializer-html": "^7.1.0",
"rollup": "^4.59.0",
"solid-js": "^1.9.4",
"typescript": "^5.8.3",
"vite": "^7.2.4",
"vite-plugin-solid": "^2.11.6",
"esbuild": "^0.28.1"
"rollup": "^4.62.2",
"solid-js": "^1.9.14",
"typescript": "^5.9.3",
"vite": "^7.3.6",
"vite-plugin-solid": "^2.11.12"
},
"peerDependencies": {
"solid-js": "^1.4.7"
+5 -5
View File
@@ -40,10 +40,10 @@
"@lucide/build-icons": "workspace:*",
"@lucide/helpers": "workspace:*",
"@lucide/rollup-plugins": "workspace:*",
"@types/node": "^22.15.30",
"prettier": "^2.3.2",
"svgson": "^5.2.1",
"rollup": "^4.59.0",
"rollup-plugin-dts": "^6.2.1"
"@types/node": "^22.20.1",
"prettier": "^2.8.8",
"rollup": "^4.62.2",
"rollup-plugin-dts": "^6.4.1",
"svgson": "^5.3.1"
}
}
+7 -7
View File
@@ -43,14 +43,14 @@
},
"devDependencies": {
"@lucide/build-icons": "workspace:*",
"@lucide/shared": "workspace:*",
"@lucide/rollup-plugins": "workspace:*",
"@rollup/plugin-replace": "^6.0.2",
"@testing-library/jest-dom": "^6.8.0",
"@lucide/shared": "workspace:*",
"@rollup/plugin-replace": "^6.0.3",
"@testing-library/jest-dom": "^6.9.1",
"jest-serializer-html": "^7.1.0",
"rollup": "^4.59.0",
"rollup-plugin-dts": "^6.2.3",
"typescript": "^5.8.3",
"vite": "^7.2.4"
"rollup": "^4.62.2",
"rollup-plugin-dts": "^6.4.1",
"typescript": "^5.9.3",
"vite": "^7.3.6"
}
}
+1 -1
View File
@@ -13,6 +13,6 @@
"test:watch": "vitest watch"
},
"devDependencies": {
"vite": "^7.2.4"
"vite": "^7.3.6"
}
}
-87
View File
@@ -1,87 +0,0 @@
<p align="center">
<a href="https://github.com/lucide-icons/lucide">
<img src="https://lucide.dev/package-logos/lucide-solid.svg" alt="Lucide icon library for Solid applications." width="540">
</a>
</p>
<p align="center">
Lucide icon library for Solid v2 applications.
</p>
<div align="center">
[![npm](https://img.shields.io/npm/v/lucide-solid?color=blue)](https://www.npmjs.com/package/lucide-solid)
![NPM Downloads](https://img.shields.io/npm/dw/lucide-solid)
[![License](https://img.shields.io/badge/license-ISC-green)](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/solid">Documentation</a>
·
<a href="https://lucide.dev/license">License</a>
</p>
# Lucide Solid
Implementation of the Lucide icon library for solid applications.
## Installation
```sh
pnpm add @lucide/solid
```
```sh
npm install @lucide/solid
```
```sh
yarn add @lucide/solid
```
```sh
bun add @lucide/solid
```
## Documentation
For full documentation, visit [lucide.dev](https://lucide.dev/guide/packages/lucide-solid)
## 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)
## 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>
### Hero backers 🦸
<a href="https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
<a href="https://www.paxhistoria.co/"><img src="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia – An alternate history sandbox game" /></a>
### Backers ☕
<a href="https://www.fina.money/"><img src="https://lucide.dev/sponsors/fina-money.png" width="180" alt="Fina Money – Modular Finance Tracker" /></a>
### Other contributors 💸
You can find all our past and non-recurring financial contributors at [our Open Collective page](https://opencollective.com/lucide-icons).
-94
View File
@@ -1,94 +0,0 @@
{
"name": "@lucide/solid",
"description": "A Lucide icon library package for Solid v2 applications.",
"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/solid"
},
"keywords": [
"Lucide",
"Solid",
"SolidJS",
"Feather",
"Icons",
"Icon",
"SVG",
"Feather Icons",
"Fontawesome",
"Font Awesome"
],
"author": "Eric Fennis",
"source": "src/lucide-solid.ts",
"main": "dist/cjs/lucide-solid.js",
"module": "dist/esm/lucide-solid.mjs",
"types": "dist/types/lucide-solid.d.ts",
"files": [
"dist"
],
"exports": {
".": {
"types": "./dist/types/lucide-solid.d.ts",
"solid": "./dist/source/lucide-solid.jsx",
"import": "./dist/esm/lucide-solid.mjs",
"browser": "./dist/esm/lucide-solid.mjs",
"require": "./dist/cjs/lucide-solid.js"
},
"./icons": {
"types": "./dist/types/lucide-solid.d.ts",
"solid": "./dist/source/lucide-solid.jsx",
"import": "./dist/esm/lucide-solid.mjs",
"browser": "./dist/esm/lucide-solid.mjs",
"require": "./dist/cjs/lucide-solid.js"
},
"./icons/*": {
"types": "./dist/types/icons/*.d.ts",
"solid": "./dist/source/icons/*.jsx",
"import": "./dist/esm/icons/*.mjs",
"browser": "./dist/esm/icons/*.mjs",
"require": "./dist/cjs/icons/*.js"
}
},
"sideEffects": false,
"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:transpile": "tsc --jsx preserve -t es2020 --rootDir src --outDir dist --noEmit false",
"build:version": "node ./scripts/replaceVersion.mjs",
"build:bundle": "rollup -c rollup.config.mjs",
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --withAliases --separateAliasesFile --separateAliasesFileIgnore=fingerprint --aliasesFileExtension=.ts --iconFileExtension=.tsx --exportFileName=index.ts",
"typecheck": "tsc",
"typecheck:watch": "tsc -w",
"test": "pnpm build:icons && vitest run",
"test:watch": "vitest watch"
},
"devDependencies": {
"@babel/core": "^7.23.9",
"@babel/preset-env": "^7.23.9",
"@babel/preset-typescript": "^7.23.3",
"@lucide/build-icons": "workspace:*",
"@lucide/rollup-plugins": "workspace:*",
"@lucide/shared": "workspace:*",
"@rollup/plugin-babel": "^6.0.4",
"@solidjs/testing-library": "1.0.0-beta.2",
"@solidjs/web": "2.0.0-beta.16",
"@testing-library/jest-dom": "^6.8.0",
"babel-preset-solid": "2.0.0-beta.16",
"esbuild": "^0.28.1",
"jest-serializer-html": "^7.1.0",
"rollup": "^4.59.0",
"solid-js": "2.0.0-beta.16",
"typescript": "^5.8.3",
"vite": "^7.2.4",
"vite-plugin-solid": "^2.11.6"
},
"peerDependencies": {
"solid-js": "2.0.0-beta.16",
"@solidjs/web": "2.0.0-beta.16"
}
}
-135
View File
@@ -1,135 +0,0 @@
import path from 'path';
import { babel } from '@rollup/plugin-babel';
import esbuild from 'esbuild';
import plugins from '@lucide/rollup-plugins';
import ts from 'typescript';
import pkg from './package.json' with { type: 'json' };
const packageName = 'LucideSolid';
const outputFileName = 'lucide-solid';
const outputDir = 'dist';
const inputs = ['src/lucide-solid.ts'];
const bundles = [
{
format: 'cjs',
inputs,
outputDir,
preserveModules: true,
},
{
format: 'esm',
inputs,
outputDir,
preserveModules: true,
extension: 'mjs',
},
];
const configs = bundles
.map(({ inputs, outputDir, format, preserveModules, extension = 'js' }) =>
inputs.map((input) => ({
input,
plugins: [
babel({
extensions: ['.ts', '.tsx', '.js', '.jsx'],
babelHelpers: 'bundled',
presets: [
'babel-preset-solid',
'@babel/preset-typescript',
['@babel/preset-env', { bugfixes: true, targets: 'last 2 years' }],
],
}),
...plugins({
pkg,
withEsbuild: false,
}),
format === 'esm'
? {
name: 'ts',
buildEnd() {
// Transpile typescript to './dist/source'
esbuild.build({
entryPoints: ['./src/**/*.tsx', './src/**/*.ts'],
outdir: './dist/source',
outExtension: {
'.js': '.jsx',
},
loader: {
'.js': 'jsx',
},
jsx: 'preserve',
jsxImportSource: 'solid-js',
bundle: true,
format: 'esm',
sourcemap: true,
target: ['esnext'],
banner: {
js: `/**
* @license ${pkg.name} v${pkg.version} - ${pkg.license}
*
* This source code is licensed under the ${pkg.license} license.
* See the LICENSE file in the root directory of this source tree.
*/`,
},
plugins: [
{
name: 'externalize-everything-except-own-dependencies',
setup(build) {
build.onResolve({ filter: /(.*)/ }, (args) => {
const modulePath = path.join(args.resolveDir, args.path);
if (
args.kind === 'import-statement' &&
args.path !== '@lucide/shared' &&
!modulePath.includes('packages/shared')
) {
return { path: args.path, external: true };
}
});
},
},
],
external: ['solid-js'],
});
// Generate types
const program = ts.createProgram([pkg.source], {
target: ts.ScriptTarget.ESNext,
module: ts.ModuleKind.ESNext,
moduleResolution: ts.ModuleResolutionKind.NodeJs,
jsx: ts.JsxEmit.Preserve,
jsxImportSource: 'solid-js',
allowSyntheticDefaultImports: true,
esModuleInterop: true,
declarationDir: `dist/types`,
declaration: true,
emitDeclarationOnly: true,
});
program.emit();
},
}
: null,
],
external: ['solid-js', '@solidjs/web', 'solid-js/store'],
output: {
name: packageName,
...(preserveModules
? {
dir: `${outputDir}/${format}`,
exports: 'auto',
entryFileNames: `[name].${extension}`,
}
: {
file: `${outputDir}/${format}/${outputFileName}.${extension}`,
}),
format: format === 'source' ? 'esm' : format,
preserveModules,
preserveModulesRoot: 'src',
sourcemap: true,
},
})),
)
.flat();
export default configs;
-38
View File
@@ -1,38 +0,0 @@
import base64SVG from '@lucide/build-icons/utils/base64SVG';
import defineExportTemplate from '@lucide/build-icons/utils/defineExportTemplate';
export default defineExportTemplate(async ({
componentName,
iconName,
children,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `
import Icon from '../Icon';
import type { IconNode, LucideProps } from '../types';
const iconNode: IconNode = ${JSON.stringify(children)};
/**
* @component @name ${componentName}
* @description Lucide SVG icon component, renders SVG Element with children.
*
* @preview ![img](data:image/svg+xml;base64,${svgBase64}) - https://lucide.dev/icons/${iconName}
* @see https://lucide.dev/guide/packages/lucide-solid - Documentation
*
* @param {Object} props - Lucide icons props and any valid SVG attribute
* @returns {JSX.Element} JSX Element
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
const ${componentName} = (props: LucideProps) => (
<Icon {...props} iconNode={iconNode} name="${iconName}" />
)
export default ${componentName};
`;
});
-78
View File
@@ -1,78 +0,0 @@
import { For, omit, useContext } from 'solid-js';
import { Dynamic } from '@solidjs/web';
import defaultAttributes from './defaultAttributes';
import { IconNode, LucideProps } from './types';
import { LucideContext } from './context';
import { hasA11yProp, mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared';
import { JSX } from '@solidjs/web';
interface IconProps {
name?: string;
iconNode: IconNode;
}
const Icon = (props: LucideProps & IconProps) => {
const [localProps, rest] = omit(props, [
'color',
'size',
'strokeWidth',
'children',
'class',
'name',
'iconNode',
'absoluteStrokeWidth',
]);
const globalProps = useContext(LucideContext);
return (
<svg
{...defaultAttributes}
width={localProps.size ?? globalProps.size ?? defaultAttributes.width}
height={localProps.size ?? globalProps.size ?? defaultAttributes.height}
stroke={localProps.color ?? globalProps.color ?? defaultAttributes.stroke}
stroke-width={
(localProps.absoluteStrokeWidth ?? globalProps.absoluteStrokeWidth) === true
? (Number(
localProps.strokeWidth ??
globalProps.strokeWidth ??
defaultAttributes['stroke-width'],
) *
24) /
Number(localProps.size ?? globalProps.size)
: Number(
localProps.strokeWidth ??
globalProps.strokeWidth ??
defaultAttributes['stroke-width'],
)
}
class={mergeClasses(
'lucide',
'lucide-icon',
globalProps.class,
...(localProps.name != null
? [
`lucide-${toKebabCase(toPascalCase(localProps.name))}`,
`lucide-${toKebabCase(localProps.name)}`,
]
: []),
localProps.class,
)}
aria-hidden={!localProps.children && !hasA11yProp(rest) ? 'true' : undefined}
{...rest}
>
<For each={localProps.iconNode}>
{([elementName, attrs]) => {
return (
<Dynamic
component={elementName}
{...attrs}
/>
);
}}
</For>
</svg>
);
};
export default Icon;
-3
View File
@@ -1,3 +0,0 @@
export * from './aliases';
export * from './prefixed';
export * from './suffixed';
-37
View File
@@ -1,37 +0,0 @@
import { createContext, omit } from 'solid-js';
import { type JSX } from '@solidjs/web';
export const LucideContext = createContext<{
size?: number;
color?: string;
strokeWidth?: number;
absoluteStrokeWidth?: boolean;
class?: string;
}>({
size: 24,
color: 'currentColor',
strokeWidth: 2,
absoluteStrokeWidth: false,
class: '',
});
interface LucideProviderProps {
children: JSX.Element;
size?: number;
color?: string;
strokeWidth?: number;
absoluteStrokeWidth?: boolean;
class?: string;
}
export function LucideProvider(props: LucideProviderProps) {
const [value, rest] = omit(props, [
'size',
'color',
'strokeWidth',
'absoluteStrokeWidth',
'class',
]);
return <LucideContext.Provider value={value}>{rest.children}</LucideContext.Provider>;
}
-15
View File
@@ -1,15 +0,0 @@
import { type SVGAttributes } from './types';
const defaultAttributes: SVGAttributes = {
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',
};
export default defaultAttributes;
-1
View File
@@ -1 +0,0 @@
Folder for generated icons
-7
View File
@@ -1,7 +0,0 @@
export * from './icons';
export * as icons from './icons';
export * from './aliases';
export * from './types';
export * from './context';
export { default as Icon } from './Icon';
-15
View File
@@ -1,15 +0,0 @@
import { type JSX } from '@solidjs/web';
export type IconNode = [elementName: keyof JSX.IntrinsicElements, attrs: Record<string, string>][];
export type SVGAttributes = Partial<JSX.SvgSVGAttributes<SVGSVGElement>>;
export interface LucideProps extends SVGAttributes {
key?: string | number;
class?: string;
size?: string | number;
color?: string;
strokeWidth?: string | number;
absoluteStrokeWidth?: boolean;
}
export type LucideIcon = (props: LucideProps) => JSX.Element;
-106
View File
@@ -1,106 +0,0 @@
import { describe, it, expect } from 'vitest';
import { render } from '@solidjs/testing-library';
import { airVent } from './testIconNodes';
import { Icon } from '../src/lucide-solid';
describe('Using Icon Component', () => {
it('should render icon based on a iconNode', async () => {
const { container } = render(() => (
<Icon
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>
));
expect(container.firstChild).toBeDefined();
});
it('should render icon and match snapshot', async () => {
const { container } = render(() => (
<Icon
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>
));
expect(container.firstChild).toMatchSnapshot();
});
});
describe('Icon Component Accessibility', () => {
it('should have aria-hidden prop when no aria prop is present', async () => {
const { container } = render(() => (
<Icon
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>
));
expect(container.firstChild).toHaveAttribute('aria-hidden', 'true');
});
it('should not have aria-hidden prop when aria prop is present', async () => {
const { container } = render(() => (
<Icon
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
aria-label="Air conditioning"
/>
));
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
});
it('should not have aria-hidden prop when title prop is present', async () => {
const { container } = render(() => (
<Icon
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
// @ts-expect-error
title="Air conditioning"
/>
));
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
});
it('should not have aria-hidden prop when there are children that could be a <title> element', async () => {
const { container } = render(() => (
<Icon
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
>
<title>Some title</title>
</Icon>
));
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
});
it('should never override aria-hidden prop', async () => {
const { container } = render(() => (
<Icon
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
aria-hidden={false}
/>
));
expect(container.firstChild).toHaveAttribute('aria-hidden', 'false');
});
});
@@ -1,34 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
<svg
aria-hidden="true"
class="lucide lucide-icon"
fill="none"
height="48"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
key="larmp2"
/>
<path
d="M6 8h12"
key="6g4wlu"
/>
<path
d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
key="1bo8pg"
/>
<path
d="M6.6 15.6A2 2 0 1 0 10 17v-5"
key="t9h90c"
/>
</svg>
`;
@@ -1,26 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using LucideProvider > should render the icon with LucideProvider 1`] = `
<svg
aria-hidden="true"
class="lucide lucide-icon lucide-house"
fill="none"
height="48"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"
key="5wwlr5"
/>
<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"
/>
</svg>
`;
@@ -1,123 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using lucide icon components > should adjust the size, stroke color and stroke width 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
viewbox="0 0 24 24"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
width="48"
height="48"
stroke="red"
stroke-width="4"
class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3"
aria-hidden="true"
data-testid="grid-icon"
>
<rect width="18"
height="18"
x="3"
y="3"
rx="2"
key="afitv7"
>
</rect>
<path d="M3 9h18"
key="1pudct"
>
</path>
<path d="M3 15h18"
key="5xshup"
>
</path>
<path d="M9 3v18"
key="fh3hqa"
>
</path>
<path d="M15 3v18"
key="14nvp0"
>
</path>
</svg>
`;
exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
viewbox="0 0 24 24"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
width="48"
height="48"
stroke="red"
stroke-width="1"
class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3"
aria-hidden="true"
data-testid="grid-icon"
>
<rect width="18"
height="18"
x="3"
y="3"
rx="2"
key="afitv7"
>
</rect>
<path d="M3 9h18"
key="1pudct"
>
</path>
<path d="M3 15h18"
key="5xshup"
>
</path>
<path d="M9 3v18"
key="fh3hqa"
>
</path>
<path d="M15 3v18"
key="14nvp0"
>
</path>
</svg>
`;
exports[`Using lucide icon components > should render a component 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
viewbox="0 0 24 24"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
width="24"
height="24"
stroke="currentColor"
stroke-width="2"
class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3"
aria-hidden="true"
>
<rect width="18"
height="18"
x="3"
y="3"
rx="2"
key="afitv7"
>
</rect>
<path d="M3 9h18"
key="1pudct"
>
</path>
<path d="M3 15h18"
key="5xshup"
>
</path>
<path d="M9 3v18"
key="fh3hqa"
>
</path>
<path d="M15 3v18"
key="14nvp0"
>
</path>
</svg>
`;
-102
View File
@@ -1,102 +0,0 @@
import { render } from '@solidjs/testing-library';
import { describe, expect, it } from 'vitest';
import { House, LucideProvider } from '../src/lucide-solid';
describe('Using LucideProvider', () => {
it('should render the icon with LucideProvider', () => {
const { container } = render(() => (
<LucideProvider
size={48}
color="red"
>
<House />
</LucideProvider>
));
expect(container.firstChild).toMatchSnapshot();
});
it('should render the icon with default props when no provider is used', () => {
const { container } = render(() => <House />);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '24');
expect(IconComponent).toHaveAttribute('height', '24');
expect(IconComponent).toHaveAttribute('stroke', 'currentColor');
expect(IconComponent).toHaveAttribute('stroke-width', '2');
});
it('should render the icon with LucideProvider and custom strokeWidth', () => {
const { container } = render(() => (
<LucideProvider
size={48}
color="red"
strokeWidth={4}
>
<House />
</LucideProvider>
));
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '48');
expect(IconComponent).toHaveAttribute('height', '48');
expect(IconComponent).toHaveAttribute('stroke', 'red');
expect(IconComponent).toHaveAttribute('stroke-width', '4');
});
it('should render the icon with LucideProvider and custom absoluteStrokeWidth', () => {
const { container } = render(() => (
<LucideProvider
size={48}
color="red"
absoluteStrokeWidth
>
<House />
</LucideProvider>
));
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('stroke-width', '1');
});
it("should override the provider's global props when passing props to the icon", () => {
const { container } = render(() => (
<LucideProvider
size={48}
color="red"
strokeWidth={4}
>
<House
size={24}
color="blue"
strokeWidth={2}
/>
</LucideProvider>
));
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '24');
expect(IconComponent).toHaveAttribute('height', '24');
expect(IconComponent).toHaveAttribute('stroke', 'blue');
expect(IconComponent).toHaveAttribute('stroke-width', '2');
});
it('should merge className from provider and icon', () => {
const { container } = render(() => (
<LucideProvider class="provider-class">
<House class="icon-class" />
</LucideProvider>
));
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute(
'class',
'lucide lucide-icon provider-class lucide-house icon-class',
);
});
});
@@ -1,90 +0,0 @@
import { describe, it, expect } from 'vitest';
import { render, cleanup } from '@solidjs/testing-library';
import { Edit2, Grid, Pen, Droplet } from '../src/lucide-solid';
describe('Using lucide icon components', () => {
it('should render a component', () => {
const { container } = render(() => <Grid />);
expect(container.innerHTML).toMatchSnapshot();
});
it('should adjust the size, stroke color and stroke width', async () => {
const testId = 'grid-icon';
const { container, getByTestId } = render(() => (
<Grid
data-testid={testId}
size={48}
color="red"
strokeWidth={4}
/>
));
const { attributes } = (await getByTestId(testId)) as unknown as {
attributes: Record<string, { value: string }>;
};
expect(attributes.stroke.value).toBe('red');
expect(attributes.width.value).toBe('48');
expect(attributes.height.value).toBe('48');
expect(attributes['stroke-width'].value).toBe('4');
expect(container.innerHTML).toMatchSnapshot();
});
it('should render the alias icon', () => {
const testId = 'pen-icon';
const { container } = render(() => (
<Pen
data-testid={testId}
size={48}
stroke="red"
strokeWidth={4}
/>
));
const PenIconRenderedHTML = container.innerHTML;
cleanup();
const { container: Edit2Container } = render(() => (
<Edit2
data-testid={testId}
size={48}
stroke="red"
strokeWidth={4}
/>
));
expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML);
});
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
const testId = 'grid-icon';
const { container, getByTestId } = render(() => (
<Grid
data-testid={testId}
size={48}
color="red"
absoluteStrokeWidth
/>
));
const { attributes } = getByTestId(testId) as unknown as {
attributes: Record<string, { value: string }>;
};
expect(attributes.stroke.value).toBe('red');
expect(attributes.width.value).toBe('48');
expect(attributes.height.value).toBe('48');
expect(attributes['stroke-width'].value).toBe('1');
expect(container.innerHTML).toMatchSnapshot();
});
it('should add all classes to the element', () => {
const testClass = 'my-class';
const { container } = render(() => <Droplet class={testClass} />);
expect(container.firstChild).toHaveClass(testClass);
expect(container.firstChild).toHaveClass('lucide');
expect(container.firstChild).toHaveClass('lucide-droplet');
});
});
-10
View File
@@ -1,10 +0,0 @@
import { expect, afterEach } from 'vitest';
import { cleanup } from '@solidjs/testing-library';
import '@testing-library/jest-dom/vitest';
import htmlSerializer from 'jest-serializer-html';
expect.addSnapshotSerializer(htmlSerializer);
afterEach(() => {
cleanup();
});
-22
View File
@@ -1,22 +0,0 @@
import { IconNode } from '../src/types';
export const airVent: IconNode = [
[
'path',
{
d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2',
key: 'larmp2',
},
],
['path', { d: 'M6 8h12', key: '6g4wlu' }],
['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }],
['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }],
];
export const coffee: IconNode = [
['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }],
['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z', key: '1bxrl0' }],
['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }],
['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }],
['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }],
];
-18
View File
@@ -1,18 +0,0 @@
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "@solidjs/web",
"types": ["vite/client", "@testing-library/jest-dom"],
"noEmit": true,
"isolatedModules": true,
"outDir": "./dist",
"declarationDir": "./dist",
"declaration": true,
},
}
-13
View File
@@ -1,13 +0,0 @@
import { defineConfig } from 'vitest/config'
import solidPlugin from 'vite-plugin-solid';
export default defineConfig({
// TODO: Remove this when Solid testing library has support for Vitest 1.0, see: https://github.com/solidjs/solid-testing-library/issues/52
// @ts-ignore
plugins: [solidPlugin()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './tests/setupVitest.js',
},
});
+11 -11
View File
@@ -60,18 +60,18 @@
"devDependencies": {
"@lucide/build-icons": "workspace:*",
"@lucide/helpers": "workspace:*",
"@sveltejs/package": "^2.5.0",
"@sveltejs/vite-plugin-svelte": "^6.1.3",
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/svelte": "^5.2.8",
"@tsconfig/svelte": "^5.0.4",
"@sveltejs/package": "^2.5.8",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/svelte": "^5.4.2",
"@tsconfig/svelte": "^5.0.8",
"jest-serializer-html": "^7.1.0",
"jsdom": "^20.0.3",
"svelte": "^5.38.6",
"svelte-check": "^4.3.4",
"svelte-preprocess": "^6.0.3",
"typescript": "^5.8.3",
"vite": "^6.3.6"
"jsdom": "^27.4.0",
"svelte": "^5.56.5",
"svelte-check": "^4.7.2",
"svelte-preprocess": "^6.0.5",
"typescript": "^5.9.3",
"vite": "^7.3.6"
},
"peerDependencies": {
"svelte": "^5"
@@ -110,7 +110,6 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
<!---->
</svg>
</div>
`;
@@ -204,7 +203,6 @@ exports[`Using lucide icon components > should render an component 1`] = `
<!---->
</svg>
</div>
`;
@@ -265,6 +263,5 @@ exports[`Using lucide icon components > should render an icon slot 1`] = `
<!---->
</svg>
</div>
`;
+6 -7
View File
@@ -44,16 +44,15 @@
"@lucide/build-icons": "workspace:*",
"@lucide/rollup-plugins": "workspace:*",
"@lucide/shared": "workspace:*",
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/vue": "^8.1.0",
"@vitejs/plugin-vue": "^4.6.2",
"@vue/test-utils": "2.4.5",
"@vitejs/plugin-vue2": "2.2.0",
"rollup-plugin-dts": "^6.1.0",
"rollup": "^4.59.0",
"typescript": "^5.8.3",
"vite": "^7.2.4",
"vue": "^3.4.21"
"rollup": "^4.62.2",
"rollup-plugin-dts": "^6.4.1",
"typescript": "^5.9.3",
"vite": "^7.3.6",
"vue": "^3.5.39"
},
"peerDependencies": {
"vue": ">=3.0.1"
+4712 -8078
View File
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -4,13 +4,16 @@ packages:
- 'docs'
overrides:
axios: ^1.12.0
cross-spawn: 7.0.5
fast-json-patch: ^3.1.1
form-data: ^4.0.4
semver: ^7.7.3
vite-prerender-plugin: 0.5.12
webpack-dev-middleware: ^5.3.4
vitest: '4.1.10'
vite: '^7.3.6'
minimatch@>=9.0.0 <9.0.6: ^9.0.6
minimatch@>=9.0.0 <9.0.7: ^9.0.7
allowBuilds:
'@parcel/watcher': false
-49
View File
@@ -1,49 +0,0 @@
import path from 'path';
import { promises as fs } from 'fs';
import { getCurrentDirPath, readSvgDirectory } from '../tools/build-helpers/helpers.ts';
// This is a special case convertion NextJS uses for their modularize imports. We try to follow the same convention, to generate the same imports.
function pascalToKebabNextJSFlavour(str: string) {
return str
.replace(/([a-z])([A-Z])/g, '$1-$2')
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
.replace(/([a-z])-?([0-9]+|[A-Z])/g, '$1-$2')
.replace(/([0-9]+)-?([a-zA-Z])/g, '$1-$2')
.replace(/([0-9])-([0-9])/g, '$1$2')
.split('-')
.map((word) => word.toLowerCase())
.join('-');
}
const currentDir = getCurrentDirPath(import.meta.url);
const ICONS_DIR = path.resolve(currentDir, '../icons');
const svgFiles = await readSvgDirectory(ICONS_DIR);
const iconNames = svgFiles.map((icon) => icon.split('.')[0]).reverse();
console.log('Creating aliases for NextJS imports: ');
Promise.all(
iconNames.map(async (iconName) => {
const pascalCaseName = iconName.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
const iconNameKebabCaseNextjsFlavour = pascalToKebabNextJSFlavour(pascalCaseName);
if (iconName !== iconNameKebabCaseNextjsFlavour) {
console.log(iconName, '➡️', iconNameKebabCaseNextjsFlavour);
const metaJson = await fs.readFile(path.resolve(ICONS_DIR, `${iconName}.json`), 'utf-8');
const iconMetaData = JSON.parse(metaJson);
const aliases = iconMetaData.aliases ?? [];
if (!aliases.includes(iconNameKebabCaseNextjsFlavour)) {
aliases.push(iconNameKebabCaseNextjsFlavour);
}
let output = JSON.stringify({ ...iconMetaData, aliases }, null, 2);
output = `${output}\n`;
fs.writeFile(path.resolve(ICONS_DIR, `${iconName}.json`), output, 'utf-8');
}
}),
);
+4 -4
View File
@@ -13,16 +13,16 @@
"author": "",
"license": "ISC",
"dependencies": {
"@vercel/blob": "^2.3.0",
"fs-extra": "^11.2.0",
"@vercel/blob": "^2.6.1",
"fs-extra": "^11.3.6",
"minimist": "^1.2.8",
"oslllo-svg-fixer": "^5.0.0",
"svgtofont": "^6.5.2"
"svgtofont": "^6.5.3"
},
"devDependencies": {
"@lucide/helpers": "workspace:*",
"@types/fs-extra": "^11.0.4",
"@types/minimist": "^1.2.5",
"@types/node": "^22"
"@types/node": "^22.20.1"
}
}
+1 -1
View File
@@ -9,6 +9,6 @@
"license": "ISC",
"type": "module",
"devDependencies": {
"@types/node": "^22"
"@types/node": "^22.20.1"
}
}
+4 -4
View File
@@ -37,13 +37,13 @@
"devDependencies": {
"@lucide/helpers": "workspace:*",
"@types/minimist": "^1.2.5",
"@types/node": "^22"
"@types/node": "^22.20.1"
},
"dependencies": {
"minimist": "^1.2.7",
"minimist": "^1.2.8",
"prettier": "2.7.1",
"svgo": "^3.3.3",
"svgson": "^5.2.1"
"svgo": "^3.3.4",
"svgson": "^5.3.1"
},
"peerDependencies": {
"@lucide/helpers": "*"
+3 -3
View File
@@ -11,10 +11,10 @@
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-replace": "^6.0.3",
"esbuild": "^0.28.1",
"rollup": "^4.59.0",
"rollup": "^4.62.2",
"rollup-plugin-esbuild": "^6.2.1",
"rollup-plugin-license": "^3.6.0",
"rollup-plugin-license": "^3.7.1",
"rollup-plugin-summary": "^3.0.1",
"rollup-plugin-visualizer": "^6.0.5"
"rollup-plugin-visualizer": "^6.0.11"
}
}