From 2189ae0f5585d362fc263f191afd5eba42d933ae Mon Sep 17 00:00:00 2001 From: Karsa Date: Mon, 6 Apr 2026 15:02:13 +0200 Subject: [PATCH 1/8] feat(packages): extract build logic to @lucide/shared, still wip --- .gitignore | 2 + packages/angular/package.json | 7 +- packages/angular/src/lucide-icon-base.ts | 22 +++-- packages/astro/scripts/exportTemplate.mts | 42 ++++----- packages/astro/src/Icon.astro | 44 ++++++--- packages/astro/src/createLucideIcon.ts | 15 ++-- packages/astro/src/types.ts | 8 ++ .../__snapshots__/lucide-astro.spec.ts.snap | 8 +- packages/astro/tests/createLucideIcon.spec.ts | 5 +- packages/icons/package.json | 1 + packages/icons/src/buildLucideDataUri.ts | 20 +---- packages/icons/src/buildLucideIconElement.ts | 21 +---- packages/icons/src/buildLucideIconNode.ts | 45 +--------- packages/icons/src/buildLucideSvg.ts | 11 +-- packages/icons/src/types.ts | 44 +-------- .../icons/tests/buildLucideIconNode.spec.ts | 26 ++++++ .../lucide-preact/scripts/exportTemplate.mts | 24 +++-- packages/lucide-preact/src/Icon.ts | 45 ++++++---- .../lucide-preact/src/createLucideIcon.ts | 54 +++++++---- packages/lucide-preact/src/types.ts | 16 +++- .../tests/__snapshots__/context.spec.tsx.snap | 2 +- .../createLucideIcon.spec.tsx.snap | 10 +-- .../__snapshots__/lucide-preact.spec.tsx.snap | 6 +- packages/lucide-preact/tests/context.spec.tsx | 5 +- .../tests/createLucideIcon.spec.tsx | 62 +++++++++---- .../scripts/exportTemplate.mts | 24 +++-- packages/lucide-react-native/src/Icon.ts | 61 ++++++++++--- .../src/createLucideIcon.ts | 45 +++++++--- packages/lucide-react-native/src/types.ts | 27 +++--- .../lucide-react/scripts/exportTemplate.mts | 25 +++--- packages/lucide-react/src/DynamicIcon.ts | 24 +++-- packages/lucide-react/src/Icon.ts | 76 +++++++++++++--- packages/lucide-react/src/createLucideIcon.ts | 53 +++++++---- packages/lucide-react/src/types.ts | 27 +++--- .../tests/__snapshots__/context.spec.tsx.snap | 2 +- .../createLucideIcon.spec.tsx.snap | 64 ++++++------- .../__snapshots__/lucide-react.spec.tsx.snap | 6 +- packages/lucide-react/tests/context.spec.tsx | 5 +- .../tests/createLucideIcon.spec.tsx | 62 +++++++++---- .../lucide-solid/scripts/exportTemplate.mts | 25 +++--- packages/lucide-solid/src/Icon.tsx | 86 ++++++++++-------- packages/lucide-solid/src/types.ts | 5 ++ .../tests/__snapshots__/context.spec.tsx.snap | 2 +- .../__snapshots__/lucide-solid.spec.tsx.snap | 6 +- packages/lucide-solid/tests/context.spec.tsx | 2 +- packages/lucide/src/createElement.ts | 47 +++------- packages/lucide/src/replaceElement.ts | 13 ++- .../shared/src/build/buildLucideDataUri.ts | 30 +++++++ .../src/build/buildLucideIconElement.ts | 36 ++++++++ .../shared/src/build/buildLucideIconNode.ts | 89 +++++++++++++++++++ packages/shared/src/build/buildLucideSvg.ts | 20 +++++ packages/shared/src/build/types.ts | 54 +++++++++++ packages/shared/src/index.ts | 6 ++ packages/shared/src/utils/toLucideIconData.ts | 22 +++++ .../shared/tests/toLucideIconData.spec.ts | 31 +++++++ packages/svelte/scripts/exportTemplate.mts | 11 +-- packages/svelte/src/Icon.svelte | 57 +++++++++--- packages/svelte/src/types.ts | 7 ++ .../tests/__snapshots__/Icon.spec.ts.snap | 2 +- .../__snapshots__/lucide-svelte.spec.ts.snap | 10 +-- packages/svelte/tests/setupVitest.ts | 3 +- packages/vue/scripts/exportTemplate.mts | 25 +++--- packages/vue/src/Icon.ts | 50 +++++++---- packages/vue/src/createLucideIcon.ts | 12 +-- packages/vue/src/types.ts | 5 ++ .../vue/tests/__snapshots__/Icon.spec.ts.snap | 2 +- .../tests/__snapshots__/context.spec.ts.snap | 2 +- .../__snapshots__/lucide-vue.spec.ts.snap | 10 +-- packages/vue/tests/context.spec.ts | 2 +- pnpm-lock.yaml | 3 + 70 files changed, 1125 insertions(+), 596 deletions(-) create mode 100644 packages/shared/src/build/buildLucideDataUri.ts create mode 100644 packages/shared/src/build/buildLucideIconElement.ts create mode 100644 packages/shared/src/build/buildLucideIconNode.ts create mode 100644 packages/shared/src/build/buildLucideSvg.ts create mode 100644 packages/shared/src/build/types.ts create mode 100644 packages/shared/src/utils/toLucideIconData.ts create mode 100644 packages/shared/tests/toLucideIconData.spec.ts diff --git a/.gitignore b/.gitignore index 691b0bc39..7aab235ad 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,8 @@ packages/**/src/aliases.ts !packages/**/src/aliases/index.ts packages/**/src/utils/*.ts !packages/shared/src/utils/*.ts +!packages/shared/src/build/ +!packages/shared/src/build/*.ts packages/**/src/dynamicIconImports.ts packages/**/DynamicIcon.d.ts packages/**/dynamicIconImports.js diff --git a/packages/angular/package.json b/packages/angular/package.json index 622181ed7..7ba81a27e 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -17,14 +17,15 @@ "scripts": { "ng": "ng", "watch": "ng build --watch --configuration development", - "prebuild": "pnpm clean && pnpm copy:license && pnpm build:icons", + "prebuild": "pnpm clean && pnpm copy:license && pnpm copy:utils && pnpm build:icons", "build": "pnpm prebuild && pnpm build:ng", "copy:license": "cp ../../LICENSE ./LICENSE", + "copy:utils": "mkdir -p ./src/utils && cp -f ../../packages/shared/src/build/buildLucideIconNode.ts ./src/utils/ && cp -f ../../packages/shared/src/build/types.ts ./src/utils/", "clean": "rm -rf dist && rm -rf ./src/icons/*.ts", "build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --iconFileExtension=.ts --exportFileName=lucide-angular.ts --useDefaultExports=0", "build:ng": "ng build --configuration production", - "test": "pnpm build:icons && ng test --no-watch", - "test:watch": "ng test", + "test": "pnpm copy:utils && pnpm build:icons && ng test --no-watch", + "test:watch": "pnpm copy:utils && ng test", "lint": "npx eslint 'src/**/*.{js,jsx,ts,tsx,html,css,scss}' --quiet --fix", "e2e": "ng e2e", "version": "pnpm version --git-tag-version=false" diff --git a/packages/angular/src/lucide-icon-base.ts b/packages/angular/src/lucide-icon-base.ts index 0775d0fbf..59fdb9667 100644 --- a/packages/angular/src/lucide-icon-base.ts +++ b/packages/angular/src/lucide-icon-base.ts @@ -12,6 +12,7 @@ import { LUCIDE_CONFIG } from './lucide-config'; import { LucideIconData, Nullable } from './types'; import defaultAttributes from './default-attributes'; import { lucideIconTemplate } from './lucide-icon-template'; +import buildLucideIconNode from './utils/buildLucideIconNode'; /** * @internal @@ -82,18 +83,27 @@ export abstract class LucideIconBase { const icon = this.icon(); if (icon) { const absoluteStrokeWidth = this.absoluteStrokeWidth(); - const { name, node, aliases = [] } = icon; - const classes = [name, ...aliases].map((item) => `lucide-${item}`); + const [, iconAttributes, iconNode] = buildLucideIconNode(icon as any, { + absoluteStrokeWidth, + hasA11yProp: this.title() != null, + }); + + const classes = + typeof iconAttributes['class'] === 'string' + ? iconAttributes['class'] + .split(' ') + .filter((cssClass) => cssClass && cssClass !== 'lucide') + : []; + for (const cssClass of classes) { this.renderer.addClass(this.elRef.nativeElement, cssClass); } + const contentRef = this.contentRef(); const refChild = contentRef.nativeElement; - const elements = node.map(([name, attrs]) => { + const elements = (iconNode ?? []).map(([name, attrs]) => { const element = this.renderer.createElement(name, 'http://www.w3.org/2000/svg'); - if (absoluteStrokeWidth) { - this.renderer.setAttribute(element, 'vector-effect', 'non-scaling-stroke'); - } + Object.entries(attrs).forEach(([name, value]) => this.renderer.setAttribute( element, diff --git a/packages/astro/scripts/exportTemplate.mts b/packages/astro/scripts/exportTemplate.mts index c8a41ed3a..1c43db3e4 100644 --- a/packages/astro/scripts/exportTemplate.mts +++ b/packages/astro/scripts/exportTemplate.mts @@ -1,28 +1,27 @@ 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); +export default defineExportTemplate( + async ({ componentName, iconName, iconData, getSvg, deprecated, deprecationReason }) => { + const svgContents = await getSvg(); + const svgBase64 = base64SVG(svgContents); - // Astro doesn't need keyed children in loops - const keylessChildren = children.map((c) => { - const [element, { key, ...otherAttrs }] = c; - return [element, otherAttrs]; - }); + // Astro doesn't need keyed children in loops + const keylessIconData = { + ...iconData, + node: iconData.node.map((child) => { + const [element, { key, ...otherAttrs }] = child; + return [element, otherAttrs]; + }), + }; - // TODO: build-icons' `pretty` is set to false as the prettier - // formatter uses babel which I'm not sure it supports typescript - return ` + // TODO: build-icons' `pretty` is set to false as the prettier + // formatter uses babel which I'm not sure it supports typescript + return ` import createLucideIcon from '../createLucideIcon'; -import type { AstroComponent } from '../types' +import type { AstroComponent, LucideIconData } from '../types' + +const iconData: LucideIconData = ${JSON.stringify(keylessIconData)}; /** * @component @name ${componentName} @@ -35,8 +34,9 @@ import type { AstroComponent } from '../types' * @returns {any} Astro Component * ${deprecated ? `@deprecated ${deprecationReason}` : ''} */ -const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(keylessChildren)}) as AstroComponent; +const ${componentName} = createLucideIcon(iconData) as AstroComponent; export default ${componentName}; `; -}); + }, +); diff --git a/packages/astro/src/Icon.astro b/packages/astro/src/Icon.astro index eb504dbad..df27db114 100644 --- a/packages/astro/src/Icon.astro +++ b/packages/astro/src/Icon.astro @@ -1,32 +1,50 @@ --- -import defaultAttributes from './defaultAttributes'; +import buildLucideIconNode from './utils/buildLucideIconNode'; import type { IconProps as Props } from './types'; import { hasA11yProp } from './utils/hasA11yProp'; - const { color = 'currentColor', size = 24, 'stroke-width': strokeWidth = 2, absoluteStrokeWidth = false, + name, + aliases, iconNode = [], class: className, ...rest } = Astro.props; + +const calculatedStrokeWidth = + absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth; + +const [, rawIconAttributes, builtIconNode = []] = buildLucideIconNode( + { + name, + aliases, + size: 24, + node: iconNode, + }, + { + color, + size, + strokeWidth: calculatedStrokeWidth, + className, + hasA11yProp: hasA11yProp(rest), + }, +); + +const { class: iconClassName, ...builtIconAttributes } = rawIconAttributes; +const iconAttributes = { + ...builtIconAttributes, + ...rest, +}; --- - {iconNode.map(([Tag, attrs]) => )} + {builtIconNode.map(([Tag, attrs]) => )} diff --git a/packages/astro/src/createLucideIcon.ts b/packages/astro/src/createLucideIcon.ts index 7ff99d5ec..22d12e942 100644 --- a/packages/astro/src/createLucideIcon.ts +++ b/packages/astro/src/createLucideIcon.ts @@ -1,11 +1,9 @@ import type { AstroComponentFactory } from 'astro/runtime/server/render/astro/factory.js'; -import type { IconNode } from './types'; +import type { LucideIconData } from './types'; import { render, renderSlot, createComponent, renderComponent } from 'astro/compiler-runtime'; import Icon from './Icon.astro'; -import { mergeClasses } from './utils/mergeClasses'; -import { toKebabCase } from './utils/toKebabCase'; -export default (iconName: string, iconNode: IconNode): AstroComponentFactory => { +export default (iconData: LucideIconData): AstroComponentFactory => { const Component = createComponent( ($$result, $$props: Record, $$slots) => { const { class: className, ...restProps } = $$props; @@ -14,11 +12,10 @@ export default (iconName: string, iconNode: IconNode): AstroComponentFactory => 'Icon', Icon, { - class: mergeClasses( - Boolean(iconName) && `lucide-${toKebabCase(iconName)}`, - Boolean(className) && className, - ), - iconNode, + class: className, + name: iconData.name, + aliases: iconData.aliases, + iconNode: iconData.node, ...restProps, }, { default: () => render`${renderSlot($$result, $$slots['default'])}` }, diff --git a/packages/astro/src/types.ts b/packages/astro/src/types.ts index 71f1d527f..d7ce5f411 100644 --- a/packages/astro/src/types.ts +++ b/packages/astro/src/types.ts @@ -9,6 +9,8 @@ export interface IconProps extends SVGAttributes { 'stroke-width'?: number | string; absoluteStrokeWidth?: boolean; class?: string; + name?: string; + aliases?: string[]; iconNode?: IconNode; title?: string; } @@ -17,6 +19,12 @@ export type SVGAttributes = HTMLAttributes<'svg'>; export type IconNode = IconNodeChild[]; +export interface LucideIconData { + name: string; + node: IconNode; + aliases?: string[]; +} + type IconNodeChild = [elementName: SVGElements, attrs: SVGAttributes]; // All possible svg elements according to the Astro definitions diff --git a/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap b/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap index a6ff7f2e4..6f9ff2921 100644 --- a/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap +++ b/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap @@ -47,7 +47,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" - class="lucide lucide-grid-3x3" + class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3" > should not scale the strokeWidth when ab stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" - class="lucide lucide-grid-3x3" + class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3" > should render a component 1`] = ` stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" - class="lucide lucide-grid-3x3" + class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3" > should render the icon with default attr stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" - class="lucide lucide-grid-3x3" + class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3" > { it('should create a component from an iconNode', async () => { - const AirVent = createLucideIcon('AirVent', airVent); + const AirVent = createLucideIcon({ + name: 'air-vent', + node: airVent, + }); const { container } = await render(AirVent); expect(container.innerHTML).toBeDefined(); diff --git a/packages/icons/package.json b/packages/icons/package.json index 165cbd181..7f2e917e7 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -64,6 +64,7 @@ "@lucide/build-icons": "workspace:*", "@lucide/helpers": "^1.0.0", "@lucide/rollup-plugins": "workspace:*", + "@lucide/shared": "workspace:*", "@rollup/plugin-replace": "^6.0.2", "@testing-library/jest-dom": "^6.6.3", "jest-serializer-html": "^7.1.0", diff --git a/packages/icons/src/buildLucideDataUri.ts b/packages/icons/src/buildLucideDataUri.ts index 64aa7526c..eaa3dae1d 100644 --- a/packages/icons/src/buildLucideDataUri.ts +++ b/packages/icons/src/buildLucideDataUri.ts @@ -1,5 +1,5 @@ +import { buildLucideDataUri as sharedBuildLucideDataUri } from '@lucide/shared'; import { LucideBuildParams, LucideIconData } from './types'; -import buildLucideSvg from './buildLucideSvg'; /** * Creates a base64 encoded data URI from a Lucide icon object. @@ -8,23 +8,7 @@ import buildLucideSvg from './buildLucideSvg'; * @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.'); + return sharedBuildLucideDataUri(icon as any, params as any); } export default buildLucideDataUri; diff --git a/packages/icons/src/buildLucideIconElement.ts b/packages/icons/src/buildLucideIconElement.ts index a819d0fde..c79f96ee2 100644 --- a/packages/icons/src/buildLucideIconElement.ts +++ b/packages/icons/src/buildLucideIconElement.ts @@ -1,20 +1,5 @@ -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; -}; +import { buildLucideIconElement as sharedBuildLucideIconElement } from '@lucide/shared'; +import { LucideBuildParams, LucideIconData } from './types'; /** * Creates an SvgElement from a Lucide icon object. @@ -28,7 +13,7 @@ function buildLucideIconElement( icon: LucideIconData, params: LucideBuildParams = {}, ) { - return buildDomElement(document, buildLucideIconNode(icon, params)); + return sharedBuildLucideIconElement(document, icon as any, params as any); } export default buildLucideIconElement; diff --git a/packages/icons/src/buildLucideIconNode.ts b/packages/icons/src/buildLucideIconNode.ts index a918ffbbd..9e29618dd 100644 --- a/packages/icons/src/buildLucideIconNode.ts +++ b/packages/icons/src/buildLucideIconNode.ts @@ -1,17 +1,6 @@ +import { buildLucideIconNode as sharedBuildLucideIconNode } from '@lucide/shared'; 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. * @@ -19,37 +8,7 @@ const defaultAttributes = { * @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, - ]), - ]; + return sharedBuildLucideIconNode(icon as any, params as any) as LucideIconNode; } export default buildLucideIconNode; diff --git a/packages/icons/src/buildLucideSvg.ts b/packages/icons/src/buildLucideSvg.ts index 27bca4a91..197e1b626 100644 --- a/packages/icons/src/buildLucideSvg.ts +++ b/packages/icons/src/buildLucideSvg.ts @@ -1,10 +1,5 @@ -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('')}`; +import { buildLucideSvg as sharedBuildLucideSvg } from '@lucide/shared'; +import { LucideBuildParams, LucideIconData } from './types'; /** * Creates an SVG string from a Lucide icon object. @@ -13,7 +8,7 @@ const buildDomNode = ([tagName, attributes, children = []]: LucideIconNode): str * @param params Additional build parameters. */ function buildLucideSvg(icon: LucideIconData, params: LucideBuildParams = {}) { - return buildDomNode(buildLucideIconNode(icon, params)); + return sharedBuildLucideSvg(icon as any, params as any); } export default buildLucideSvg; diff --git a/packages/icons/src/types.ts b/packages/icons/src/types.ts index 563c93fd7..08e15ffa1 100644 --- a/packages/icons/src/types.ts +++ b/packages/icons/src/types.ts @@ -1,43 +1 @@ -export type SVGProps = Record; - -/** - * 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 }); +export type { LucideBuildParams, LucideIconData, LucideIconNode, SVGProps } from '@lucide/shared'; diff --git a/packages/icons/tests/buildLucideIconNode.spec.ts b/packages/icons/tests/buildLucideIconNode.spec.ts index dc3345ac5..5af66acbf 100644 --- a/packages/icons/tests/buildLucideIconNode.spec.ts +++ b/packages/icons/tests/buildLucideIconNode.spec.ts @@ -89,4 +89,30 @@ describe('buildLucideIconNode', () => { expect(HouseSVG[1]['class']).toBe('lucide lucide-house lucide-home icon'); }); + + it('should support className with multiple classes', () => { + const HouseSVG = buildLucideIconNode(House, { + className: ' icon custom lucide-house ', + }); + + expect(HouseSVG[1]['class']).toBe('lucide lucide-house lucide-home icon custom'); + }); + + it('should not include default classes when includeDefaultClasses is false', () => { + const HouseSVG = buildLucideIconNode(House, { includeDefaultClasses: false }); + + expect(HouseSVG[1]['class']).toBeUndefined(); + }); + + it('should add aria-hidden when hasA11yProp is false', () => { + const HouseSVG = buildLucideIconNode(House, { hasA11yProp: false }); + + expect(HouseSVG[1]['aria-hidden']).toBe('true'); + }); + + it('should not add aria-hidden when hasA11yProp is true', () => { + const HouseSVG = buildLucideIconNode(House, { hasA11yProp: true }); + + expect(HouseSVG[1]['aria-hidden']).toBeUndefined(); + }); }); diff --git a/packages/lucide-preact/scripts/exportTemplate.mts b/packages/lucide-preact/scripts/exportTemplate.mts index 28cc154bc..8eaa0baa6 100644 --- a/packages/lucide-preact/scripts/exportTemplate.mts +++ b/packages/lucide-preact/scripts/exportTemplate.mts @@ -1,19 +1,16 @@ 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); +export default defineExportTemplate( + async ({ componentName, iconName, iconData, getSvg, deprecated, deprecationReason }) => { + const svgContents = await getSvg(); + const svgBase64 = base64SVG(svgContents); - return ` + return ` import createLucideIcon from '../createLucideIcon'; +import type { LucideIconData } from '../types'; + +const iconData: LucideIconData = ${JSON.stringify(iconData)}; /** * @component @name ${componentName} @@ -26,8 +23,9 @@ import createLucideIcon from '../createLucideIcon'; * @returns {JSX.Element} JSX Element * ${deprecated ? `@deprecated ${deprecationReason}` : ''} */ -const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(children)}); +const ${componentName} = createLucideIcon(iconData); export default ${componentName}; `; -}); + }, +); diff --git a/packages/lucide-preact/src/Icon.ts b/packages/lucide-preact/src/Icon.ts index 0fb278848..07583e0b6 100644 --- a/packages/lucide-preact/src/Icon.ts +++ b/packages/lucide-preact/src/Icon.ts @@ -1,12 +1,13 @@ import { h, toChildArray } from 'preact'; +import { buildLucideIconNode, hasA11yProp, mergeClasses } from '@lucide/shared'; import defaultAttributes from './defaultAttributes'; -import type { IconNode, LucideProps } from './types'; +import type { LucideIconNode, LucideProps } from './types'; import { useLucideContext } from './context'; -import { mergeClasses } from '@lucide/shared'; -import { hasA11yProp } from '@lucide/shared'; interface IconComponentProps extends LucideProps { - iconNode: IconNode; + name?: string; + aliases?: string[]; + iconNode: LucideIconNode; } /** @@ -19,8 +20,8 @@ interface IconComponentProps extends LucideProps { * @param {number} props.strokeWidth - The stroke width of the icon * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width * @param {string} props.class - The class name of the icon - * @param {IconNode} props.children - The children of the icon - * @param {IconNode} props.iconNode - The icon node of the icon + * @param {LucideIconNode} props.children - The children of the icon + * @param {LucideIconNode} props.iconNode - The icon node of the icon * * @returns {ForwardRefExoticComponent} LucideIcon */ @@ -31,6 +32,8 @@ const Icon = ({ absoluteStrokeWidth, children, iconNode, + name, + aliases, class: classes = '', ...rest }: IconComponentProps) => { @@ -47,20 +50,28 @@ const Icon = ({ ? (Number(strokeWidth ?? contextStrokeWidth) * 24) / Number(size ?? contextSize) : strokeWidth ?? contextStrokeWidth; - return h( - 'svg', + const hasAccessibleProp = Boolean(children) || hasA11yProp(rest); + + const [, svgAttributes, builtIconNode = []] = buildLucideIconNode( { - ...defaultAttributes, - width: size ?? contextSize ?? 24, - height: size ?? contextSize ?? 24, - stroke: color ?? contextColor, - ['stroke-width' as 'strokeWidth']: calculatedStrokeWidth, - class: mergeClasses('lucide', contextClass, classes), - ...(!children && !hasA11yProp(rest) && { 'aria-hidden': 'true' }), - ...rest, + name, + aliases, + size: 24, + node: iconNode, + }, + { + color: color ?? contextColor, + size: size ?? contextSize ?? defaultAttributes.width, + strokeWidth: calculatedStrokeWidth, + className: mergeClasses(contextClass, classes as string), + hasA11yProp: hasAccessibleProp, }, - [...iconNode.map(([tag, attrs]) => h(tag, attrs)), ...toChildArray(children)], ); + + return h('svg', { ...svgAttributes, ...rest }, [ + ...builtIconNode.map(([tag, attrs]) => h(tag, attrs)), + ...toChildArray(children), + ]); }; export default Icon; diff --git a/packages/lucide-preact/src/createLucideIcon.ts b/packages/lucide-preact/src/createLucideIcon.ts index c5c14d4a3..5620bdad5 100644 --- a/packages/lucide-preact/src/createLucideIcon.ts +++ b/packages/lucide-preact/src/createLucideIcon.ts @@ -1,34 +1,56 @@ import { h, type JSX } from 'preact'; -import { mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared'; +import { mergeClasses, toLucideIconData, toPascalCase } from '@lucide/shared'; import Icon from './Icon'; -import type { IconNode, LucideIcon, LucideProps } from './types'; +import type { LucideIcon, LucideIconData, LucideIconNode, LucideProps } from './types'; /** - * Create a Lucide icon component - * @param {string} iconName - * @param {array} iconNode - * @returns {FunctionComponent} LucideIcon + * Creates a Lucide icon component from icon data. + * + * @param iconData Icon data object. + * @returns Lucide icon component. */ -const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => { +function createLucideIcon(iconData: LucideIconData): LucideIcon; + +/** + * Creates a Lucide icon component from the legacy icon arguments. + * + * @param iconName Icon name. + * @param iconNode Icon node. + * @param aliases Optional icon aliases. + * @returns Lucide icon component. + */ +function createLucideIcon( + iconName: string, + iconNode: LucideIconNode, + aliases?: string[], +): LucideIcon; + +function createLucideIcon( + iconDataOrName: LucideIconData | string, + iconNode?: LucideIconNode, + aliases: string[] = [], +): LucideIcon { + const iconData = + typeof iconDataOrName === 'string' + ? toLucideIconData(iconDataOrName, iconNode as LucideIconNode, aliases) + : iconDataOrName; + const Component = ({ class: classes = '', className = '', children, ...props }: LucideProps) => h( Icon, { ...props, - iconNode, - class: mergeClasses>( - `lucide-${toKebabCase(toPascalCase(iconName))}`, - `lucide-${toKebabCase(iconName)}`, - classes, - className, - ), + name: iconData.name, + aliases: iconData.aliases, + iconNode: iconData.node, + class: mergeClasses>(classes, className), }, children, ); - Component.displayName = toPascalCase(iconName); + Component.displayName = toPascalCase(iconData.name); return Component; -}; +} export default createLucideIcon; diff --git a/packages/lucide-preact/src/types.ts b/packages/lucide-preact/src/types.ts index 03604e960..f9736e37e 100644 --- a/packages/lucide-preact/src/types.ts +++ b/packages/lucide-preact/src/types.ts @@ -1,6 +1,20 @@ import { type FunctionComponent, type JSX } from 'preact'; +import type { + LucideIconData as SharedLucideIconData, + LucideIconNode as SharedLucideIconNode, +} from '@lucide/shared'; -export type IconNode = [elementName: keyof JSX.IntrinsicElements, attrs: Record][]; +export type LucideIconNode = SharedLucideIconNode[]; + +export type LucideIconData = SharedLucideIconData & { + name: string; + node: LucideIconNode; +}; + +/** + * @deprecated Use LucideIconNode instead. + */ +export type IconNode = LucideIconNode; export interface LucideProps extends Partial> { color?: string; diff --git a/packages/lucide-preact/tests/__snapshots__/context.spec.tsx.snap b/packages/lucide-preact/tests/__snapshots__/context.spec.tsx.snap index b572910a9..85a5974f2 100644 --- a/packages/lucide-preact/tests/__snapshots__/context.spec.tsx.snap +++ b/packages/lucide-preact/tests/__snapshots__/context.spec.tsx.snap @@ -3,7 +3,7 @@ exports[`Using LucideProvider > should render the icon with LucideProvider 1`] = `
{ - const svgContents = await getSvg(); - const svgBase64 = base64SVG(svgContents); +export default defineExportTemplate( + async ({ componentName, iconName, iconData, getSvg, deprecated, deprecationReason }) => { + const svgContents = await getSvg(); + const svgBase64 = base64SVG(svgContents); - return ` + return ` import createLucideIcon from '../createLucideIcon'; -import { IconNode } from '../types'; +import type { LucideIconData } from '../types'; -export const __iconNode: IconNode = ${JSON.stringify(children)} +export const __iconData: LucideIconData = ${JSON.stringify(iconData)} /** * @component @name ${componentName} @@ -29,8 +23,9 @@ export const __iconNode: IconNode = ${JSON.stringify(children)} * @returns {FunctionalComponent} Vue component * ${deprecated ? `@deprecated ${deprecationReason}` : ''} */ -const ${componentName} = createLucideIcon('${iconName}', __iconNode); +const ${componentName} = createLucideIcon(__iconData); export default ${componentName}; `; -}); + }, +); diff --git a/packages/vue/src/Icon.ts b/packages/vue/src/Icon.ts index 75d85aee4..5bdecdb5f 100644 --- a/packages/vue/src/Icon.ts +++ b/packages/vue/src/Icon.ts @@ -1,5 +1,5 @@ import { computed, type FunctionalComponent, h } from 'vue'; -import { isEmptyString, mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared'; +import { buildLucideIconNode, isEmptyString, toKebabCase } from '@lucide/shared'; import defaultAttributes from './defaultAttributes'; import { IconNode, LucideProps } from './types'; import { useLucideProps } from './context'; @@ -7,11 +7,13 @@ import { useLucideProps } from './context'; interface IconProps { iconNode: IconNode; name: string; + aliases?: string[]; } const Icon: FunctionalComponent = ( { name, + aliases, iconNode, absoluteStrokeWidth, 'absolute-stroke-width': absoluteStrokeWidthKebabCase, @@ -54,25 +56,39 @@ const Icon: FunctionalComponent = ( return strokeWidthValue; }); - return h( - 'svg', + const normalizedName = computed(() => (name != null ? toKebabCase(name) : undefined)); + + const iconName = computed(() => (normalizedName.value ? `${normalizedName.value}-icon` : 'icon')); + + const [, svgAttributes, builtIconNode = []] = buildLucideIconNode( { - ...defaultAttributes, - ...props, - width: size ?? contextSize ?? defaultAttributes.width, - height: size ?? contextSize ?? defaultAttributes.height, - stroke: color ?? contextColor ?? defaultAttributes.stroke, - 'stroke-width': calculatedStrokeWidth.value, - class: mergeClasses( - 'lucide', - contextClass, - ...(name - ? [`lucide-${toKebabCase(toPascalCase(name))}-icon`, `lucide-${toKebabCase(name)}`] - : ['lucide-icon']), - ), + name: iconName.value, + aliases, + size: 24, + node: iconNode, + }, + { + size: size ?? contextSize ?? defaultAttributes.width, + color: color ?? contextColor ?? defaultAttributes.stroke, + strokeWidth: calculatedStrokeWidth.value, + className: contextClass, }, - [...iconNode.map((child) => h(...child)), ...(slots.default ? [slots.default()] : [])], ); + + const iconAttributes = { + ...svgAttributes, + ...props, + width: svgAttributes.width, + height: svgAttributes.height, + stroke: svgAttributes.stroke, + 'stroke-width': svgAttributes['stroke-width'], + class: svgAttributes.class, + }; + + return h('svg', iconAttributes, [ + ...builtIconNode.map((child) => h(...child)), + ...(slots.default ? [slots.default()] : []), + ]); }; export default Icon; diff --git a/packages/vue/src/createLucideIcon.ts b/packages/vue/src/createLucideIcon.ts index 85919b106..10d5247df 100644 --- a/packages/vue/src/createLucideIcon.ts +++ b/packages/vue/src/createLucideIcon.ts @@ -1,23 +1,23 @@ import { h } from 'vue'; -import { IconNode, LucideIcon } from './types'; +import { LucideIcon, LucideIconData } from './types'; import Icon from './Icon'; /** * Create a Lucide icon component - * @param {string} iconName - * @param {array} iconNode + * @param {object} iconData * @returns {FunctionalComponent} LucideIcon */ const createLucideIcon = - (iconName: string, iconNode: IconNode): LucideIcon => + (iconData: LucideIconData): LucideIcon => (props, { slots, attrs }) => h( Icon, { ...attrs, ...props, - iconNode, - name: iconName, + iconNode: iconData.node, + name: iconData.name, + aliases: iconData.aliases, }, slots.default ? { default: slots.default } : undefined, ); diff --git a/packages/vue/src/types.ts b/packages/vue/src/types.ts index a8b865db6..38d5f2f6d 100644 --- a/packages/vue/src/types.ts +++ b/packages/vue/src/types.ts @@ -8,4 +8,9 @@ export interface LucideProps extends Partial { } export type IconNode = [elementName: string, attrs: Record][]; +export interface LucideIconData { + name: string; + node: IconNode; + aliases?: string[]; +} export type LucideIcon = FunctionalComponent; diff --git a/packages/vue/tests/__snapshots__/Icon.spec.ts.snap b/packages/vue/tests/__snapshots__/Icon.spec.ts.snap index 4b742c143..9907eb584 100644 --- a/packages/vue/tests/__snapshots__/Icon.spec.ts.snap +++ b/packages/vue/tests/__snapshots__/Icon.spec.ts.snap @@ -2,7 +2,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = ` should render the icon with LucideProvider 1`] = ` should add a class to the element 1`] = `
should add a class to the element 1`] = exports[`Using lucide icon components > should add a style attribute to the element 1`] = `
should add a style attribute to the elem exports[`Using lucide icon components > should adjust the size, stroke color and stroke width 1`] = `
should adjust the size, stroke color and exports[`Using lucide icon components > should pass children to the icon slot 1`] = `
should pass children to the icon slot 1` exports[`Using lucide icon components > should render an component 1`] = `
{ expect(IconComponent).toHaveAttribute( 'class', - 'lucide provider-class lucide-house-icon lucide-house icon-class', + 'lucide lucide-house-icon lucide-home provider-class icon-class', ); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ae7209cf..39ee05ff8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -357,6 +357,9 @@ importers: '@lucide/rollup-plugins': specifier: workspace:* version: link:../../tools/rollup-plugins + '@lucide/shared': + specifier: workspace:* + version: link:../shared '@rollup/plugin-replace': specifier: ^6.0.2 version: 6.0.3(rollup@4.59.0) From a23c4b45cd56ae1a2d018682b7c8051e4049cbf8 Mon Sep 17 00:00:00 2001 From: Karsa Date: Fri, 22 May 2026 09:54:45 +0200 Subject: [PATCH 2/8] feat(): unify icon build logic some more, add nonScalingStroke option, marking absoluteStrokeWidth as deprecated --- .gitignore | 1 + package.json | 1 + packages/angular/package.json | 2 +- packages/angular/src/lucide-config.ts | 8 +- packages/angular/src/lucide-icon-base.spec.ts | 47 ++++- packages/angular/src/lucide-icon-base.ts | 140 ++++++++++--- packages/angular/src/types.ts | 18 +- packages/astro/package.json | 2 +- packages/astro/src/Icon.astro | 46 ++-- packages/astro/src/createLucideIcon.ts | 46 +++- packages/astro/src/defaultAttributes.ts | 15 -- packages/astro/src/lucide-astro.ts | 2 +- packages/astro/src/types.ts | 42 ++-- .../tests/__snapshots__/Icon.spec.ts.snap | 2 +- .../createLucideIcon.spec.ts.snap | 2 +- .../__snapshots__/lucide-astro.spec.ts.snap | 74 +++++-- packages/astro/tests/lucide-astro.spec.ts | 43 +++- packages/icons/rollup.config.mjs | 7 +- packages/icons/src/buildLucideDataUri.ts | 15 +- packages/icons/src/buildLucideIconElement.ts | 20 +- packages/icons/src/buildLucideIconNode.ts | 15 +- packages/icons/src/buildLucideSvg.ts | 15 +- packages/icons/src/types.ts | 11 +- .../buildLucideIconNode.spec.ts.snap | 2 +- .../icons/tests/buildLucideIconNode.spec.ts | 38 +++- packages/icons/tsconfig.json | 5 + packages/lucide-preact/rollup.config.mjs | 11 +- packages/lucide-preact/src/Icon.ts | 71 ++++--- packages/lucide-preact/src/context.ts | 13 +- .../lucide-preact/src/createLucideIcon.ts | 20 +- .../lucide-preact/src/defaultAttributes.ts | 14 +- packages/lucide-preact/src/types.ts | 15 +- .../__snapshots__/lucide-preact.spec.tsx.snap | 54 ++++- packages/lucide-preact/tests/context.spec.tsx | 16 ++ .../tests/lucide-preact.spec.tsx | 42 +++- packages/lucide-preact/tsconfig.json | 5 + .../lucide-react-native/rollup.config.mjs | 13 +- packages/lucide-react-native/src/Icon.ts | 59 +++--- packages/lucide-react-native/src/context.ts | 13 +- .../src/createLucideIcon.ts | 6 +- .../src/defaultAttributes.ts | 25 +-- packages/lucide-react-native/src/types.ts | 17 +- .../lucide-react-native/tests/Icon.spec.tsx | 28 +++ .../tests/__snapshots__/Icon.spec.tsx.snap | 2 + .../tests/__snapshots__/context.spec.tsx.snap | 2 + .../lucide-react-native.spec.tsx.snap | 197 ++++++++++++------ .../tests/lucide-react-native.spec.tsx | 22 +- packages/lucide-react-native/tsconfig.json | 5 + packages/lucide-react/package.json | 1 + packages/lucide-react/rollup.config.mjs | 15 +- .../lucide-react/scripts/exportTemplate.mts | 7 +- packages/lucide-react/src/DynamicIcon.ts | 26 +-- packages/lucide-react/src/Icon.ts | 98 ++++----- packages/lucide-react/src/context.ts | 8 +- packages/lucide-react/src/createLucideIcon.ts | 18 +- .../lucide-react/src/defaultAttributes.ts | 12 +- packages/lucide-react/src/types.ts | 18 +- .../tests/__snapshots__/context.spec.tsx.snap | 48 +++++ .../__snapshots__/lucide-react.spec.tsx.snap | 54 ++++- packages/lucide-react/tests/context.spec.tsx | 18 ++ .../lucide-react/tests/lucide-react.spec.tsx | 42 +++- packages/lucide-react/tsconfig.json | 7 +- .../lucide-solid/scripts/exportTemplate.mts | 2 +- packages/lucide-solid/src/Icon.tsx | 81 +++---- packages/lucide-solid/src/context.tsx | 10 + .../lucide-solid/src/defaultAttributes.ts | 17 +- packages/lucide-solid/src/types.ts | 25 ++- packages/lucide-solid/tests/Icon.spec.tsx | 28 +++ .../__snapshots__/lucide-solid.spec.tsx.snap | 94 ++++----- .../lucide-solid/tests/lucide-solid.spec.tsx | 2 +- packages/lucide-solid/tsconfig.json | 5 + packages/lucide/src/createElement.ts | 47 +++-- packages/lucide/src/replaceElement.ts | 11 +- packages/shared/package.json | 7 + .../src/build/buildLucideIconForReact.ts | 27 +++ .../shared/src/build/buildLucideIconNode.ts | 82 +++----- .../src/build}/defaultAttributes.ts | 6 +- .../src/build/defaultReactAttributes.ts | 13 ++ packages/shared/src/build/types.ts | 43 +++- packages/shared/src/index.ts | 3 + packages/svelte/package.json | 2 +- packages/svelte/scripts/exportTemplate.mts | 8 +- packages/svelte/src/Icon.svelte | 56 ++--- packages/svelte/src/context.ts | 4 + packages/svelte/src/lucide-svelte.ts | 2 +- packages/svelte/src/types.ts | 40 ++-- .../__snapshots__/lucide-svelte.spec.ts.snap | 70 +++---- packages/svelte/tests/lucide-svelte.spec.ts | 2 +- packages/svelte/tsconfig.json | 5 + packages/vue/rollup.config.mjs | 29 +-- packages/vue/src/Icon.ts | 119 +++++------ packages/vue/src/context.ts | 4 + packages/vue/src/createLucideIcon.ts | 41 +++- packages/vue/src/defaultAttributes.ts | 12 +- packages/vue/src/types.ts | 27 ++- packages/vue/tests/Icon.spec.ts | 54 +++-- .../vue/tests/__snapshots__/Icon.spec.ts.snap | 58 +++++- .../tests/__snapshots__/context.spec.ts.snap | 2 +- .../__snapshots__/lucide-vue.spec.ts.snap | 10 +- packages/vue/tests/context.spec.ts | 2 +- packages/vue/tests/lucide-vue.spec.ts | 60 +++++- packages/vue/tsconfig.json | 5 + 102 files changed, 1809 insertions(+), 947 deletions(-) delete mode 100644 packages/astro/src/defaultAttributes.ts create mode 100644 packages/shared/src/build/buildLucideIconForReact.ts rename packages/{svelte/src => shared/src/build}/defaultAttributes.ts (75%) create mode 100644 packages/shared/src/build/defaultReactAttributes.ts diff --git a/.gitignore b/.gitignore index 7aab235ad..fc8681b54 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ packages/**/src/utils/*.ts !packages/shared/src/utils/*.ts !packages/shared/src/build/ !packages/shared/src/build/*.ts +packages/shared/src/build/types.d.ts packages/**/src/dynamicIconImports.ts packages/**/DynamicIcon.d.ts packages/**/dynamicIconImports.js diff --git a/package.json b/package.json index 7bb11484b..adf630030 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "@vitest/coverage-v8": "4.0.12", "@vitest/ui": "4.0.12", "ajv-cli": "^5.0.0", + "cpy-cli": "^7.0.0", "dotenv": "^17.4.2", "eslint": "^8.57.1", "eslint-config-airbnb-base": "^15.0.0", diff --git a/packages/angular/package.json b/packages/angular/package.json index 7ba81a27e..91438770a 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -20,7 +20,7 @@ "prebuild": "pnpm clean && pnpm copy:license && pnpm copy:utils && pnpm build:icons", "build": "pnpm prebuild && pnpm build:ng", "copy:license": "cp ../../LICENSE ./LICENSE", - "copy:utils": "mkdir -p ./src/utils && cp -f ../../packages/shared/src/build/buildLucideIconNode.ts ./src/utils/ && cp -f ../../packages/shared/src/build/types.ts ./src/utils/", + "copy:utils": "cpy ../../packages/shared/src/build/{buildLucideIconNode.ts,defaultAttributes.ts,types.ts} ../../packages/shared/src/utils/mergeClasses.ts ./src/utils/", "clean": "rm -rf dist && rm -rf ./src/icons/*.ts", "build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --iconFileExtension=.ts --exportFileName=lucide-angular.ts --useDefaultExports=0", "build:ng": "ng build --configuration production", diff --git a/packages/angular/src/lucide-config.ts b/packages/angular/src/lucide-config.ts index 8a4953127..3944638f1 100644 --- a/packages/angular/src/lucide-config.ts +++ b/packages/angular/src/lucide-config.ts @@ -19,6 +19,11 @@ export interface LucideConfig { * @default 2 */ strokeWidth: number; + /** + * Whether to use absolute stroke width. + * @deprecated Use `nonScalingStroke` instead. + */ + absoluteStrokeWidth: boolean; /** * Whether stroke width should be scaled to appear uniform regardless of icon size. * @default false @@ -31,7 +36,7 @@ export interface LucideConfig { * } * ``` */ - absoluteStrokeWidth: boolean; + nonScalingStroke: boolean; } /** @@ -42,6 +47,7 @@ export const lucideDefaultConfig: LucideConfig = { size: 24, strokeWidth: 2, absoluteStrokeWidth: false, + nonScalingStroke: false, }; /** diff --git a/packages/angular/src/lucide-icon-base.spec.ts b/packages/angular/src/lucide-icon-base.spec.ts index ff0154357..736847c44 100644 --- a/packages/angular/src/lucide-icon-base.spec.ts +++ b/packages/angular/src/lucide-icon-base.spec.ts @@ -41,6 +41,7 @@ describe('LucideIconBase', () => { let size: WritableSignal; let strokeWidth: WritableSignal; let absoluteStrokeWidth: WritableSignal; + let nonScalingStroke: WritableSignal; const getSvgAttribute = (attr: string) => fixture.nativeElement.getAttribute(attr); function createComponent() { return TestBed.createComponent(LucideCircleCheck, { @@ -51,6 +52,7 @@ describe('LucideIconBase', () => { inputBinding('size', size), inputBinding('strokeWidth', strokeWidth), inputBinding('absoluteStrokeWidth', absoluteStrokeWidth), + inputBinding('nonScalingStroke', nonScalingStroke), ], }); } @@ -60,6 +62,7 @@ describe('LucideIconBase', () => { size = signal(undefined); strokeWidth = signal(undefined); absoluteStrokeWidth = signal(undefined); + nonScalingStroke = signal(undefined); fixture = createComponent(); component = fixture.componentInstance; }); @@ -113,6 +116,11 @@ describe('LucideIconBase', () => { expect(getSvgAttribute('width')).toBe('18'); expect(getSvgAttribute('height')).toBe('18'); }); + it('should set viewBox', () => { + size.set('18'); + fixture.detectChanges(); + expect(getSvgAttribute('viewBox')).toBe('0 0 24 24'); + }); }); describe('strokeWidth', () => { @@ -133,21 +141,37 @@ describe('LucideIconBase', () => { }); describe('absoluteStrokeWidth', () => { + it('should adjust stroke width', () => { + strokeWidth.set(2); + size.set(32); + absoluteStrokeWidth.set(true); + fixture.detectChanges(); + expect(getSvgAttribute('stroke-width')).toBe('1.5'); + }); + it('should not set vector-effect on children', () => { + absoluteStrokeWidth.set(true); + for (const child of fixture.nativeElement.children) { + expect(child.getAttribute('vector-effect')).toBeNull(); + } + }); + }); + + describe('nonScalingStroke', () => { it('should not adjust stroke width', () => { strokeWidth.set(2); size.set(12); - absoluteStrokeWidth.set(true); + nonScalingStroke.set(false); fixture.detectChanges(); expect(getSvgAttribute('stroke-width')).toBe('2'); }); it('should not set vector-effect on children', () => { - absoluteStrokeWidth.set(false); + nonScalingStroke.set(false); for (const child of fixture.nativeElement.children) { expect(child.getAttribute('vector-effect')).toBeNull(); } }); it('should set vector-effect on children', () => { - absoluteStrokeWidth.set(true); + nonScalingStroke.set(true); for (const child of fixture.nativeElement.children) { expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke'); } @@ -205,12 +229,12 @@ describe('LucideIconBase', () => { describe('strokeWidth', () => { it('should use stroke width from config', () => { fixture.detectChanges(); - expect(getSvgAttribute('stroke-width')).toBe('1'); + expect(getSvgAttribute('stroke-width')).toBe('2'); }); it('should use override stroke width from config', () => { strokeWidth.set(3); fixture.detectChanges(); - expect(getSvgAttribute('stroke-width')).toBe('3'); + expect(getSvgAttribute('stroke-width')).toBe('6'); }); }); describe('size', () => { @@ -239,5 +263,18 @@ describe('LucideIconBase', () => { } }); }); + describe('nonScalingStroke', () => { + it('should use nonScalingStroke from config', () => { + for (const child of fixture.nativeElement.children) { + expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke'); + } + }); + it('should override nonScalingStroke', () => { + nonScalingStroke.set(false); + for (const child of fixture.nativeElement.children) { + expect(child.getAttribute('vector-effect')).toBeNull(); + } + }); + }); }); }); diff --git a/packages/angular/src/lucide-icon-base.ts b/packages/angular/src/lucide-icon-base.ts index 59fdb9667..156091cb0 100644 --- a/packages/angular/src/lucide-icon-base.ts +++ b/packages/angular/src/lucide-icon-base.ts @@ -1,5 +1,6 @@ import { Component, + computed, effect, ElementRef, inject, @@ -9,7 +10,7 @@ import { viewChild, } from '@angular/core'; import { LUCIDE_CONFIG } from './lucide-config'; -import { LucideIconData, Nullable } from './types'; +import { LucideIconData, LucideIconNode, Nullable } from './types'; import defaultAttributes from './default-attributes'; import { lucideIconTemplate } from './lucide-icon-template'; import buildLucideIconNode from './utils/buildLucideIconNode'; @@ -23,12 +24,13 @@ import buildLucideIconNode from './utils/buildLucideIconNode'; template: lucideIconTemplate, host: { ...defaultAttributes, - class: 'lucide', - '[attr.width]': 'size()', - '[attr.height]': 'size()', - '[attr.stroke]': 'color()', - '[attr.stroke-width]': 'strokeWidth()', - '[attr.aria-hidden]': '!title()', + '[attr.class]': 'computedClass()', + '[attr.width]': 'computedWidth()', + '[attr.height]': 'computedHeight()', + '[attr.viewBox]': 'computedViewbox()', + '[attr.stroke]': 'computedStroke()', + '[attr.stroke-width]': 'computedStrokeWidth()', + '[attr.aria-hidden]': 'computedAriaHidden()', }, }) export abstract class LucideIconBase { @@ -57,6 +59,16 @@ export abstract class LucideIconBase { readonly size = input(this.iconConfig.size, { transform: (value: Nullable) => value ?? this.iconConfig.size, }); + /** + * Icon width. + * @default 24 + */ + readonly width = input(null); + /** + * Icon height. + * @default 24 + */ + readonly height = input(null); /** * Stroke color. * @default currentColor @@ -72,36 +84,105 @@ export abstract class LucideIconBase { transform: (value: Nullable) => value ?? this.iconConfig.strokeWidth, }); /** - * If set to true, it adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements. + * Whether to use absolute stroke width. */ readonly absoluteStrokeWidth = input(this.iconConfig.absoluteStrokeWidth, { transform: (value: Nullable) => value ?? this.iconConfig.absoluteStrokeWidth, }); + /** + * If set to true, it adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements. + */ + readonly nonScalingStroke = input(this.iconConfig.nonScalingStroke, { + transform: (value: Nullable) => value ?? this.iconConfig.nonScalingStroke, + }); + + /** + * @internal + */ + protected readonly computedIcon = computed(() => { + const icon = this.icon(); + return icon + ? buildLucideIconNode(icon, { + width: this.width() ?? this.size(), + height: this.height() ?? this.size(), + color: this.color(), + strokeWidth: this.strokeWidth(), + absoluteStrokeWidth: this.absoluteStrokeWidth(), + nonScalingStroke: this.nonScalingStroke(), + hasA11yProp: this.title() != null, + }) + : null; + }); + + /** + * @internal + */ + protected readonly computedAttributes = computed( + () => this.computedIcon()?.[1], + ); + + /** + * @internal + */ + protected readonly computedIconNode = computed(() => this.computedIcon()?.[2]); + + /** + * @internal + */ + protected readonly computedWidth = computed( + () => this.computedAttributes()?.['width'] ?? this.iconConfig.size, + ); + + /** + * @internal + */ + protected readonly computedHeight = computed( + () => this.computedAttributes()?.['height'] ?? this.iconConfig.size, + ); + + /** + * @internal + */ + protected readonly computedStroke = computed( + () => this.computedAttributes()?.['stroke'] ?? this.iconConfig.color, + ); + + /** + * @internal + */ + protected readonly computedStrokeWidth = computed( + () => this.computedAttributes()?.['stroke-width'] ?? this.iconConfig.strokeWidth, + ); + + /** + * @internal + */ + protected readonly computedAriaHidden = computed( + () => this.computedAttributes()?.['aria-hidden'] === 'true', + ); + + /** + * @internal + */ + protected readonly computedViewbox = computed( + () => this.computedAttributes()?.['viewBox'] ?? '0 0 24 24', + ); + + /** + * @internal + */ + protected readonly computedClass = computed( + () => this.computedAttributes()?.['class'] ?? 'lucide', + ); constructor() { effect((onCleanup) => { - const icon = this.icon(); - if (icon) { - const absoluteStrokeWidth = this.absoluteStrokeWidth(); - const [, iconAttributes, iconNode] = buildLucideIconNode(icon as any, { - absoluteStrokeWidth, - hasA11yProp: this.title() != null, - }); - - const classes = - typeof iconAttributes['class'] === 'string' - ? iconAttributes['class'] - .split(' ') - .filter((cssClass) => cssClass && cssClass !== 'lucide') - : []; - - for (const cssClass of classes) { - this.renderer.addClass(this.elRef.nativeElement, cssClass); - } - + const iconNode = this.computedIconNode(); + const iconAttributes = this.computedIconNode(); + if (iconNode && iconAttributes) { const contentRef = this.contentRef(); const refChild = contentRef.nativeElement; - const elements = (iconNode ?? []).map(([name, attrs]) => { + const elements = iconNode.map(([name, attrs]) => { const element = this.renderer.createElement(name, 'http://www.w3.org/2000/svg'); Object.entries(attrs).forEach(([name, value]) => @@ -118,9 +199,6 @@ export abstract class LucideIconBase { elements.forEach((element) => this.renderer.removeChild(this.elRef.nativeElement, element), ); - for (const cssClass of classes) { - this.renderer.removeClass(this.elRef.nativeElement, cssClass); - } }); } }); diff --git a/packages/angular/src/types.ts b/packages/angular/src/types.ts index bc75de19e..c234ab405 100644 --- a/packages/angular/src/types.ts +++ b/packages/angular/src/types.ts @@ -1,18 +1,16 @@ import { Signal, Type } from '@angular/core'; +import { + LucideIconNode as SharedLucideIconNode, + LucideIconData as SharedLucideIconData, +} from './utils/types'; -type HtmlAttributes = { [key: string]: string | number }; -export type LucideIconNode = readonly [string, HtmlAttributes]; +export type LucideIconNode = SharedLucideIconNode; export type LucideIcons = { [key: string]: LucideIconData }; /** * A Lucide icon object that fully describes an icon to be displayed. */ -export type LucideIconData = { - name: string; - size?: number; - node: LucideIconNode[]; - aliases?: string[]; -}; +export type LucideIconData = SharedLucideIconData & { name: string }; /** * Input signal map of Lucide icon components. @@ -22,7 +20,11 @@ interface LucideIconProps { size: Signal>; color: Signal>; strokeWidth: Signal>; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth: Signal>; + nonScalingStroke: Signal>; } /** diff --git a/packages/astro/package.json b/packages/astro/package.json index 6f44dede1..76f1aa944 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -43,7 +43,7 @@ "build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --withAliases --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts --pretty=false", "clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.ts", "copy:license": "cp ../../LICENSE ./LICENSE", - "copy:utils": "mkdir -p ./src/utils && for f in hasA11yProp toKebabCase mergeClasses; do cp -f ../../packages/shared/src/utils/$f.ts ./src/utils/; done", + "copy:utils": "cpy ../../packages/shared/src/build/{buildLucideIconNode.ts,defaultAttributes.ts,types.ts} ../../packages/shared/src/utils/{hasA11yProp.ts,toKebabCase.ts,mergeClasses.ts} ./src/utils/", "test": "pnpm copy:utils && pnpm build:icons && vitest run", "test:watch": "pnpm build:icons && vitest run --watch", "version": "pnpm version --git-tag-version=false" diff --git a/packages/astro/src/Icon.astro b/packages/astro/src/Icon.astro index df27db114..b20d3b835 100644 --- a/packages/astro/src/Icon.astro +++ b/packages/astro/src/Icon.astro @@ -6,45 +6,39 @@ import { hasA11yProp } from './utils/hasA11yProp'; const { color = 'currentColor', size = 24, + width, + height, 'stroke-width': strokeWidth = 2, absoluteStrokeWidth = false, - name, - aliases, - iconNode = [], + nonScalingStroke = false, + iconNode, + icon = { + node: iconNode, + aliases: [], + size: 24, + }, class: className, ...rest } = Astro.props; -const calculatedStrokeWidth = - absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth; +const [, builtIconAttributes, builtIconNode] = buildLucideIconNode(icon, { + color, + width: width ?? size, + height: height ?? size, + strokeWidth, + absoluteStrokeWidth, + nonScalingStroke, + className, + hasA11yProp: hasA11yProp(rest), +}); -const [, rawIconAttributes, builtIconNode = []] = buildLucideIconNode( - { - name, - aliases, - size: 24, - node: iconNode, - }, - { - color, - size, - strokeWidth: calculatedStrokeWidth, - className, - hasA11yProp: hasA11yProp(rest), - }, -); - -const { class: iconClassName, ...builtIconAttributes } = rawIconAttributes; const iconAttributes = { ...builtIconAttributes, ...rest, }; --- - + {builtIconNode.map(([Tag, attrs]) => )} diff --git a/packages/astro/src/createLucideIcon.ts b/packages/astro/src/createLucideIcon.ts index 22d12e942..e693daf7e 100644 --- a/packages/astro/src/createLucideIcon.ts +++ b/packages/astro/src/createLucideIcon.ts @@ -1,9 +1,43 @@ import type { AstroComponentFactory } from 'astro/runtime/server/render/astro/factory.js'; -import type { LucideIconData } from './types'; +import type { LucideIconData, LucideIconNode } from './types'; import { render, renderSlot, createComponent, renderComponent } from 'astro/compiler-runtime'; import Icon from './Icon.astro'; -export default (iconData: LucideIconData): AstroComponentFactory => { +/** + * Creates a Lucide icon component from icon data. + * + * @param icon Icon data object. + * @returns Lucide icon component. + */ +function createLucideIcon(icon: LucideIconData): AstroComponentFactory; + +/** + * Creates a Lucide icon component from the legacy icon arguments. + * + * @param iconName Icon name. + * @param iconNode Icon node. + * @param aliases Optional icon aliases. + * @returns Lucide icon component. + */ +function createLucideIcon( + iconName: string, + iconNode: LucideIconNode[], + aliases?: string[], +): AstroComponentFactory; + +function createLucideIcon( + iconOrIconName: string | LucideIconData, + iconNode: LucideIconNode[] = [], + aliases: string[] = [], +): AstroComponentFactory { + const icon = + typeof iconOrIconName === 'string' + ? { + name: iconOrIconName, + node: iconNode, + aliases, + } + : iconOrIconName; const Component = createComponent( ($$result, $$props: Record, $$slots) => { const { class: className, ...restProps } = $$props; @@ -13,9 +47,7 @@ export default (iconData: LucideIconData): AstroComponentFactory => { Icon, { class: className, - name: iconData.name, - aliases: iconData.aliases, - iconNode: iconData.node, + icon, ...restProps, }, { default: () => render`${renderSlot($$result, $$slots['default'])}` }, @@ -25,4 +57,6 @@ export default (iconData: LucideIconData): AstroComponentFactory => { 'none', ); return Component; -}; +} + +export default createLucideIcon; diff --git a/packages/astro/src/defaultAttributes.ts b/packages/astro/src/defaultAttributes.ts deleted file mode 100644 index 5b1fddd06..000000000 --- a/packages/astro/src/defaultAttributes.ts +++ /dev/null @@ -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; diff --git a/packages/astro/src/lucide-astro.ts b/packages/astro/src/lucide-astro.ts index adc0c1003..19d041177 100644 --- a/packages/astro/src/lucide-astro.ts +++ b/packages/astro/src/lucide-astro.ts @@ -3,6 +3,6 @@ export * as icons from './icons/index'; export * from './aliases'; export * from './types'; -export { default as defaultAttributes } from './defaultAttributes'; +export { default as defaultAttributes } from './utils/defaultAttributes'; export { default as createLucideIcon } from './createLucideIcon'; export { default as Icon } from './Icon.astro'; diff --git a/packages/astro/src/types.ts b/packages/astro/src/types.ts index d7ce5f411..f8ad56c01 100644 --- a/packages/astro/src/types.ts +++ b/packages/astro/src/types.ts @@ -1,31 +1,47 @@ import type { HTMLAttributes } from 'astro/types'; +import type { + LucideIconData as SharedLucideIconData, + LucideIconNode as SharedLucideIconNode, +} from './utils/types'; // Type that the Astro language server needs to infer component props in Astro files -export type AstroComponent = (_props: IconProps) => any; +export type AstroComponent = (_props: LucideProps) => any; -export interface IconProps extends SVGAttributes { +export type LucideProps = SVGAttributes & { color?: string; size?: number | string; 'stroke-width'?: number | string; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; class?: string; - name?: string; - aliases?: string[]; - iconNode?: IconNode; title?: string; -} +}; + +export type IconProps = LucideProps & + ( + | { + icon: LucideIconData; + iconNode?: never; + } + | { + icon?: never; + iconNode: LucideIconNode; + } + ); export type SVGAttributes = HTMLAttributes<'svg'>; -export type IconNode = IconNodeChild[]; +export type LucideIconNode = SharedLucideIconNode; -export interface LucideIconData { - name: string; - node: IconNode; - aliases?: string[]; -} +export type LucideIconData = SharedLucideIconData; -type IconNodeChild = [elementName: SVGElements, attrs: SVGAttributes]; +/** + * @deprecated Use LucideIconNode instead. + */ +export type IconNode = LucideIconNode[]; // All possible svg elements according to the Astro definitions type SVGElements = diff --git a/packages/astro/tests/__snapshots__/Icon.spec.ts.snap b/packages/astro/tests/__snapshots__/Icon.spec.ts.snap index a550ccb86..f3bc1ec4d 100644 --- a/packages/astro/tests/__snapshots__/Icon.spec.ts.snap +++ b/packages/astro/tests/__snapshots__/Icon.spec.ts.snap @@ -10,8 +10,8 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = ` stroke-width="1" stroke-linecap="round" stroke-linejoin="round" - aria-hidden="true" class="lucide" + aria-hidden="true" > diff --git a/packages/astro/tests/__snapshots__/createLucideIcon.spec.ts.snap b/packages/astro/tests/__snapshots__/createLucideIcon.spec.ts.snap index 13a285420..e53acc2bb 100644 --- a/packages/astro/tests/__snapshots__/createLucideIcon.spec.ts.snap +++ b/packages/astro/tests/__snapshots__/createLucideIcon.spec.ts.snap @@ -10,8 +10,8 @@ exports[`Using createLucideIcon > should create a component from an iconNode 1`] stroke-width="2" stroke-linecap="round" stroke-linejoin="round" - aria-hidden="true" class="lucide lucide-air-vent" + aria-hidden="true" > diff --git a/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap b/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap index 6f9ff2921..218463aea 100644 --- a/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap +++ b/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap @@ -10,9 +10,9 @@ exports[`Using lucide icon components > should add a non-default attribute to th stroke-width="2" stroke-linecap="round" stroke-linejoin="round" + class="lucide lucide-smile" aria-hidden="true" style="position: absolute" - class="lucide lucide-smile" > should adjust the size, stroke color and stroke-width="4" stroke-linecap="round" stroke-linejoin="round" - aria-hidden="true" class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3" + aria-hidden="true" > should apply all classes to the element stroke-width="2" stroke-linecap="round" stroke-linejoin="round" + class="lucide lucide-droplet lucide-drop my-icon" aria-hidden="true" - class="lucide lucide-droplet my-icon" > `; -exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` +exports[`Using lucide icon components > should apply vector-effect when nonScalingStroke is set 1`] = ` `; @@ -126,8 +135,8 @@ exports[`Using lucide icon components > should pass children to the icon slot 1` stroke-width="2" stroke-linecap="round" stroke-linejoin="round" - aria-hidden="true" class="lucide lucide-smile" + aria-hidden="true" > should render a component 1`] = ` stroke-width="2" stroke-linecap="round" stroke-linejoin="round" - aria-hidden="true" class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3" + aria-hidden="true" > should render the icon with default attr stroke-width="2" stroke-linecap="round" stroke-linejoin="round" - aria-hidden="true" class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3" + aria-hidden="true" +> + + + + + + + + + + + +`; + +exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` + `; -exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` +exports[`Using lucide icon components > should apply vector-effect when nonScalingStroke is set 1`] = ` `; @@ -92,3 +101,34 @@ exports[`Using lucide icon components > should render an component 1`] = ` `; + +exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` + +`; diff --git a/packages/lucide-preact/tests/context.spec.tsx b/packages/lucide-preact/tests/context.spec.tsx index 856b148fd..c2afcc312 100644 --- a/packages/lucide-preact/tests/context.spec.tsx +++ b/packages/lucide-preact/tests/context.spec.tsx @@ -51,6 +51,22 @@ describe('Using LucideProvider', () => { expect(IconComponent).toHaveAttribute('stroke-width', '1'); }); + it('should render the icon with LucideProvider and custom nonScalingStroke', () => { + const { container } = render( + + + , + ); + + const IconComponent = container.firstElementChild; + + expect(IconComponent?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + }); + it("should override the provider's global props when passing props to the icon", () => { const { container } = render( }; @@ -70,7 +70,7 @@ describe('Using lucide icon components', () => { expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML); }); - it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { + it('should scale the strokeWidth when absoluteStrokeWidth is set', () => { const { container } = render( { expect(container.innerHTML).toMatchSnapshot(); }); + it('should apply vector-effect when nonScalingStroke is set', () => { + const { container } = render( + , + ); + + const SVGElement = container.firstElementChild; + + expect(SVGElement).toHaveAttribute('stroke', 'red'); + expect(SVGElement).toHaveAttribute('width', '48'); + expect(SVGElement).toHaveAttribute('height', '48'); + expect(SVGElement).toHaveAttribute('stroke-width', '2'); + expect(SVGElement?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + + expect(container.innerHTML).toMatchSnapshot(); + }); + it('should apply all classes to the element', () => { + const iconData: LucideIconData = { + name: 'droplet', + size: 24, + node: [ + [ + 'path', + { + d: 'M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z', + key: 'abc123', + }, + ], + ], + aliases: ['drop'], + }; const testClass = 'my-class'; - const { container } = render(); + const Icon = createLucideIcon(iconData); + const { container } = render(); expect(container.firstChild).toHaveClass(testClass); expect(container.firstChild).toHaveClass('lucide'); expect(container.firstChild).toHaveClass('lucide-droplet'); + expect(container.firstChild).toHaveClass('lucide-drop'); }); }); diff --git a/packages/lucide-preact/tsconfig.json b/packages/lucide-preact/tsconfig.json index 61ae11e69..ff8a883fe 100644 --- a/packages/lucide-preact/tsconfig.json +++ b/packages/lucide-preact/tsconfig.json @@ -8,6 +8,11 @@ "moduleResolution": "node", "module": "ESNext", "target": "ESNext", + "baseUrl": ".", + "paths": { + "@lucide/shared": ["../shared/src/index.ts"], + "@lucide/shared/types": ["../shared/src/build/types.ts"], + }, "esModuleInterop": true, "isolatedModules": true, "lib": ["esnext", "dom"], diff --git a/packages/lucide-react-native/rollup.config.mjs b/packages/lucide-react-native/rollup.config.mjs index cc692ecab..8eba95708 100644 --- a/packages/lucide-react-native/rollup.config.mjs +++ b/packages/lucide-react-native/rollup.config.mjs @@ -2,6 +2,11 @@ import plugins from '@lucide/rollup-plugins'; import dts from 'rollup-plugin-dts'; import pkg from './package.json' with { type: 'json' }; +const dtsOptions = { + includeExternal: ['@lucide/shared/types'], + tsconfig: './tsconfig.json', +}; + const packageName = 'LucideReact'; const outputFileName = 'lucide-react-native'; const outputDir = 'dist'; @@ -61,7 +66,7 @@ export default [ format: 'es', }, ], - plugins: [dts()], + plugins: [dts(dtsOptions)], }, { input: inputs[1], @@ -71,7 +76,7 @@ export default [ format: 'es', }, ], - plugins: [dts()], + plugins: [dts(dtsOptions)], }, { input: `src/${outputFileName}.suffixed.ts`, @@ -81,7 +86,7 @@ export default [ format: 'es', }, ], - plugins: [dts()], + plugins: [dts(dtsOptions)], }, { input: `src/${outputFileName}.prefixed.ts`, @@ -91,7 +96,7 @@ export default [ format: 'es', }, ], - plugins: [dts()], + plugins: [dts(dtsOptions)], }, ...configs, ]; diff --git a/packages/lucide-react-native/src/Icon.ts b/packages/lucide-react-native/src/Icon.ts index d00627b44..0d49ccba9 100644 --- a/packages/lucide-react-native/src/Icon.ts +++ b/packages/lucide-react-native/src/Icon.ts @@ -1,15 +1,14 @@ import { createElement, forwardRef, type FunctionComponent } from 'react'; import * as NativeSvg from 'react-native-svg'; -import { buildLucideIconNode } from '@lucide/shared'; -import defaultAttributes, { childDefaultAttributes } from './defaultAttributes'; -import { LucideIconNode, LucideProps } from './types'; +import { buildLucideIconForReact, hasA11yProp, mergeClasses } from '@lucide/shared'; +import { childDefaultAttributes } from './defaultAttributes'; +import { LucideIconData, LucideIconNode, LucideProps } from './types'; import { useLucideContext } from './context'; -interface IconComponentProps extends LucideProps { - iconNode: LucideIconNode; +type IconComponentProps = LucideProps & { testID?: string; className?: string; -} +} & ({ icon: LucideIconData; iconNode?: never } | { icon?: never; iconNode: LucideIconNode[] }); const toNativeSvgAttrName = (attributeName: string) => { if (attributeName === 'class') { @@ -44,8 +43,8 @@ const toNativeSvgAttributes = (attributes: Record) => { * @param {number} props.strokeWidth - The stroke width of the icon * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width * @param {string} props.className - The class name of the icon - * @param {LucideIconNode} props.children - The children of the icon - * @param {LucideIconNode} props.iconNode - The icon node of the icon + * @param {LucideIconNode[]} props.children - The children of the icon + * @param {LucideIconNode[]} props.iconNode - The icon node of the icon * * @returns {ForwardRefExoticComponent} LucideIcon */ @@ -54,10 +53,17 @@ const Icon = forwardRef( { color, size, + width = size, + height = size, strokeWidth, absoluteStrokeWidth, + nonScalingStroke, children, - iconNode, + iconNode = [], + icon = { + node: iconNode, + size: 24, + }, className, testID, ...rest @@ -68,31 +74,28 @@ const Icon = forwardRef( size: contextSize = 24, strokeWidth: contextStrokeWidth = 2, absoluteStrokeWidth: contextAbsoluteStrokeWidth = false, + nonScalingStroke: contextNonScalingStroke = false, color: contextColor = 'currentColor', + className: contextClass = '', } = useLucideContext() ?? {}; - const calculatedStrokeWidth = - absoluteStrokeWidth ?? contextAbsoluteStrokeWidth - ? (Number(strokeWidth ?? contextStrokeWidth) * 24) / Number(size ?? contextSize) - : strokeWidth ?? contextStrokeWidth; + const hasAccessibleProp = Boolean(children) || hasA11yProp(rest); - const [, svgAttributes, builtIconNode = []] = buildLucideIconNode( - { - size: 24, - node: iconNode, - }, - { - color: color ?? contextColor ?? defaultAttributes.stroke, - size: size ?? contextSize ?? defaultAttributes.width, - strokeWidth: calculatedStrokeWidth, - className, - includeDefaultClasses: false, - }, - ); + const [, svgAttributes, builtIconNode] = buildLucideIconForReact(icon, { + color: color ?? contextColor, + width: width ?? size ?? contextSize, + height: height ?? size ?? contextSize, + strokeWidth: strokeWidth ?? contextStrokeWidth, + absoluteStrokeWidth: absoluteStrokeWidth ?? contextAbsoluteStrokeWidth, + nonScalingStroke: nonScalingStroke ?? contextNonScalingStroke, + className: mergeClasses(contextClass, className), + hasA11yProp: hasAccessibleProp, + attributes: rest, + }); const customAttrs = { - stroke: color ?? contextColor ?? defaultAttributes.stroke, - strokeWidth: calculatedStrokeWidth, + stroke: svgAttributes['stroke'], + strokeWidth: svgAttributes['strokeWidth'], ...rest, }; diff --git a/packages/lucide-react-native/src/context.ts b/packages/lucide-react-native/src/context.ts index c63d8a412..3dd87faa8 100644 --- a/packages/lucide-react-native/src/context.ts +++ b/packages/lucide-react-native/src/context.ts @@ -4,12 +4,17 @@ const LucideContext = createContext<{ size?: number; color?: string; strokeWidth?: number; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; }>({ size: 24, color: 'currentColor', strokeWidth: 2, absoluteStrokeWidth: false, + nonScalingStroke: false, }); interface LucideProviderProps { @@ -17,7 +22,11 @@ interface LucideProviderProps { size?: number; color?: string; strokeWidth?: number; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; } export function LucideProvider({ @@ -26,6 +35,7 @@ export function LucideProvider({ color, strokeWidth, absoluteStrokeWidth, + nonScalingStroke, }: LucideProviderProps) { const value = useMemo( () => ({ @@ -33,8 +43,9 @@ export function LucideProvider({ color, strokeWidth, absoluteStrokeWidth, + nonScalingStroke, }), - [size, color, strokeWidth, absoluteStrokeWidth], + [size, color, strokeWidth, absoluteStrokeWidth, nonScalingStroke], ); return createElement(LucideContext.Provider, { value }, children); diff --git a/packages/lucide-react-native/src/createLucideIcon.ts b/packages/lucide-react-native/src/createLucideIcon.ts index 31f98f974..058deba06 100644 --- a/packages/lucide-react-native/src/createLucideIcon.ts +++ b/packages/lucide-react-native/src/createLucideIcon.ts @@ -6,10 +6,10 @@ import Icon from './Icon'; /** * Creates a Lucide icon component from icon data. * - * @param iconData Icon data object. + * @param icon Icon data object. * @returns Lucide icon component. */ -function createLucideIcon(iconData: LucideIconData): LucideIcon; +function createLucideIcon(icon: LucideIconData): LucideIcon; /** * Creates a Lucide icon component from the legacy icon arguments. @@ -38,7 +38,7 @@ function createLucideIcon( const Component = forwardRef((props, ref) => createElement(Icon, { ref, - iconNode: iconData.node, + icon: iconData, ...props, }), ); diff --git a/packages/lucide-react-native/src/defaultAttributes.ts b/packages/lucide-react-native/src/defaultAttributes.ts index 3fae7d973..6f55b9edc 100644 --- a/packages/lucide-react-native/src/defaultAttributes.ts +++ b/packages/lucide-react-native/src/defaultAttributes.ts @@ -1,21 +1,10 @@ -const defaultAttributes = { - xmlns: 'http://www.w3.org/2000/svg', - width: 24, - height: 24, - viewBox: '0 0 24 24', - fill: 'none', - stroke: 'currentColor', - strokeWidth: 2, - strokeLinecap: 'round', - strokeLinejoin: 'round', -}; +import { defaultReactAttributes } from '@lucide/shared'; export const childDefaultAttributes = { - fill: defaultAttributes.fill, - stroke: defaultAttributes.stroke, - strokeWidth: defaultAttributes.strokeWidth, - strokeLinecap: defaultAttributes.strokeLinecap, - strokeLinejoin: defaultAttributes.strokeLinejoin, + fill: defaultReactAttributes.fill, + stroke: defaultReactAttributes.stroke, + strokeWidth: defaultReactAttributes.strokeWidth, + strokeLinecap: defaultReactAttributes.strokeLinecap, + strokeLinejoin: defaultReactAttributes.strokeLinejoin, }; - -export default defaultAttributes; +export default defaultReactAttributes; diff --git a/packages/lucide-react-native/src/types.ts b/packages/lucide-react-native/src/types.ts index 993c3b720..fc3dfe772 100644 --- a/packages/lucide-react-native/src/types.ts +++ b/packages/lucide-react-native/src/types.ts @@ -3,23 +3,26 @@ import type { SvgProps } from 'react-native-svg'; import type { LucideIconData as SharedLucideIconData, LucideIconNode as SharedLucideIconNode, -} from '@lucide/shared'; +} from '@lucide/shared/types'; -export type LucideIconNode = SharedLucideIconNode[]; +export type LucideIconNode = SharedLucideIconNode; -export type LucideIconData = SharedLucideIconData & { - name: string; - node: LucideIconNode; -}; +export type LucideIconData = SharedLucideIconData; /** * @deprecated Use LucideIconNode instead. */ -export type IconNode = LucideIconNode; +export type IconNode = LucideIconNode[]; export interface LucideProps extends SvgProps { size?: string | number; + width?: string | number; + height?: string | number; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; 'data-testid'?: string; } diff --git a/packages/lucide-react-native/tests/Icon.spec.tsx b/packages/lucide-react-native/tests/Icon.spec.tsx index 0bf4e886a..5c55e0b1c 100644 --- a/packages/lucide-react-native/tests/Icon.spec.tsx +++ b/packages/lucide-react-native/tests/Icon.spec.tsx @@ -7,6 +7,8 @@ import { Icon } from '../src/lucide-react-native'; vi.mock('react-native-svg'); describe('Using Icon Component', () => { + const airVentIcon = { name: 'air-vent', node: airVent }; + it('should render icon based on a iconNode', async () => { const { container } = render( { expect(container.firstChild).toMatchSnapshot(); }); + + it('should render icon based on icon data', async () => { + const { container } = render( + , + ); + + expect(container.firstChild).toBeDefined(); + }); + + it('should support nonScalingStroke', async () => { + const { container } = render( + , + ); + + expect(container.firstChild?.firstChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + }); }); diff --git a/packages/lucide-react-native/tests/__snapshots__/Icon.spec.tsx.snap b/packages/lucide-react-native/tests/__snapshots__/Icon.spec.tsx.snap index fd704741d..819ed9daf 100644 --- a/packages/lucide-react-native/tests/__snapshots__/Icon.spec.tsx.snap +++ b/packages/lucide-react-native/tests/__snapshots__/Icon.spec.tsx.snap @@ -2,6 +2,8 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = ` `; + +exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` + +`; diff --git a/packages/lucide-react/tests/context.spec.tsx b/packages/lucide-react/tests/context.spec.tsx index e6cd56e28..5b8e6cd76 100644 --- a/packages/lucide-react/tests/context.spec.tsx +++ b/packages/lucide-react/tests/context.spec.tsx @@ -60,6 +60,24 @@ describe('Using LucideProvider', () => { const IconComponent = container.firstElementChild; expect(IconComponent).toHaveAttribute('stroke-width', '1'); + expect(container.firstChild).toMatchSnapshot(); + }); + + it('should render the icon with LucideProvider and custom nonScalingStroke', () => { + const { container } = render( + + + , + ); + + const IconComponent = container.firstElementChild; + + expect(IconComponent?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + expect(container.firstChild).toMatchSnapshot(); }); it("should override the provider's global props when passing props to the icon", () => { diff --git a/packages/lucide-react/tests/lucide-react.spec.tsx b/packages/lucide-react/tests/lucide-react.spec.tsx index 8365d96f4..9b7480e03 100644 --- a/packages/lucide-react/tests/lucide-react.spec.tsx +++ b/packages/lucide-react/tests/lucide-react.spec.tsx @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; import { render, cleanup } from '@testing-library/react'; -import { Pen, Edit2, Grid, Droplet } from '../src/lucide-react'; +import { Pen, Edit2, Grid, createLucideIcon, LucideIconData } from '../src/lucide-react'; import defaultAttributes from '../src/defaultAttributes'; describe('Using lucide icon components', () => { @@ -69,7 +69,7 @@ describe('Using lucide icon components', () => { expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML); }); - it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { + it('should scale the strokeWidth when absoluteStrokeWidth is set', () => { const { container, getByTestId } = render( { expect(container.innerHTML).toMatchSnapshot(); }); + it('should apply vector-effect when nonScalingStroke is set', () => { + const { container, getByTestId } = render( + , + ); + + const SVGElement = container.firstElementChild; + + expect(SVGElement).toHaveAttribute('stroke', 'red'); + expect(SVGElement).toHaveAttribute('width', '48'); + expect(SVGElement).toHaveAttribute('height', '48'); + expect(SVGElement).toHaveAttribute('stroke-width', '2'); + expect(SVGElement?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + + expect(container.innerHTML).toMatchSnapshot(); + }); + it('should apply all classNames to the element', () => { + const iconData: LucideIconData = { + name: 'droplet', + size: 24, + node: [ + [ + 'path', + { + d: 'M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z', + key: 'abc123', + }, + ], + ], + aliases: ['drop'], + }; const testClass = 'my-class'; - const { container } = render(); + const Icon = createLucideIcon(iconData); + const { container } = render(); expect(container.firstChild).toHaveClass(testClass); expect(container.firstChild).toHaveClass('lucide'); expect(container.firstChild).toHaveClass('lucide-droplet'); + expect(container.firstChild).toHaveClass('lucide-drop'); }); }); diff --git a/packages/lucide-react/tsconfig.json b/packages/lucide-react/tsconfig.json index 43f70ec12..bd959bbec 100644 --- a/packages/lucide-react/tsconfig.json +++ b/packages/lucide-react/tsconfig.json @@ -8,6 +8,11 @@ "moduleResolution": "node", "module": "ESNext", "target": "ESNext", + "baseUrl": ".", + "paths": { + "@lucide/shared": ["../shared/src/index.ts"], + "@lucide/shared/types": ["../shared/src/build/types.ts"], + }, "esModuleInterop": true, "isolatedModules": true, "lib": ["esnext", "dom"], @@ -19,7 +24,7 @@ "sourceMap": true, "outDir": "./dist", "jsx": "react-jsx", - "types": ["@testing-library/jest-dom"], + "types": ["@testing-library/jest-dom", "node"], }, "exclude": ["**/node_modules"], "include": ["src", "tests"], diff --git a/packages/lucide-solid/scripts/exportTemplate.mts b/packages/lucide-solid/scripts/exportTemplate.mts index 8bf2bb3c1..6a2b6d376 100644 --- a/packages/lucide-solid/scripts/exportTemplate.mts +++ b/packages/lucide-solid/scripts/exportTemplate.mts @@ -24,7 +24,7 @@ const iconData: LucideIconData = ${JSON.stringify(iconData)}; * ${deprecated ? `@deprecated ${deprecationReason}` : ''} */ const ${componentName} = (props: LucideProps) => ( - + ) export default ${componentName}; diff --git a/packages/lucide-solid/src/Icon.tsx b/packages/lucide-solid/src/Icon.tsx index 51df3fe23..2183f4053 100644 --- a/packages/lucide-solid/src/Icon.tsx +++ b/packages/lucide-solid/src/Icon.tsx @@ -1,77 +1,58 @@ import { For, splitProps, useContext } from 'solid-js'; import { Dynamic } from 'solid-js/web'; import { buildLucideIconNode, hasA11yProp, mergeClasses } from '@lucide/shared'; -import defaultAttributes from './defaultAttributes'; -import { IconNode, LucideProps } from './types'; +import { LucideIconData, LucideIconNode, LucideProps } from './types'; import { LucideContext } from './context'; -interface IconProps { - name?: string; - aliases?: string[]; - iconNode: IconNode; -} +type IconProps = + | { + icon: LucideIconData; + iconNode?: never; + } + | { + icon?: never; + iconNode?: LucideIconNode[]; + }; const Icon = (props: LucideProps & IconProps) => { const [localProps, rest] = splitProps(props, [ 'color', 'size', + 'width', + 'height', 'strokeWidth', 'children', 'class', - 'name', - 'aliases', + 'icon', 'iconNode', 'absoluteStrokeWidth', + 'nonScalingStroke', ]); const globalProps = useContext(LucideContext); - const calculatedStrokeWidth = - (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'], - ); - const hasAccessibleProp = Boolean(localProps.children) || hasA11yProp(rest); - const [, svgAttributes, builtIconNode = []] = buildLucideIconNode( - { - name: localProps.name, - aliases: localProps.aliases, - size: 24, - node: localProps.iconNode, - }, - { - color: localProps.color ?? globalProps.color ?? defaultAttributes.stroke, - size: localProps.size ?? globalProps.size ?? defaultAttributes.width, - strokeWidth: calculatedStrokeWidth, - className: mergeClasses('lucide-icon', globalProps.class, localProps.class), - hasA11yProp: hasAccessibleProp, - }, - ); - - const svgElementAttributes = { - xmlns: svgAttributes.xmlns, - viewBox: svgAttributes.viewBox, - fill: svgAttributes.fill, - 'stroke-linecap': svgAttributes['stroke-linecap'], - 'stroke-linejoin': svgAttributes['stroke-linejoin'], - width: svgAttributes.width, - height: svgAttributes.height, - stroke: svgAttributes.stroke, - 'stroke-width': svgAttributes['stroke-width'], - class: svgAttributes.class, - 'aria-hidden': svgAttributes['aria-hidden'], - ...rest, + const icon: LucideIconData = localProps.icon ?? { + node: localProps.iconNode ?? ([] as LucideIconNode[]), + size: 24, + aliases: [], }; + const [, svgAttributes, builtIconNode] = buildLucideIconNode(icon, { + color: localProps.color ?? globalProps.color, + width: localProps.width ?? localProps.size ?? globalProps.size, + height: localProps.height ?? localProps.size ?? globalProps.size, + strokeWidth: localProps.strokeWidth ?? globalProps.strokeWidth, + absoluteStrokeWidth: localProps.absoluteStrokeWidth ?? globalProps.absoluteStrokeWidth, + nonScalingStroke: localProps.nonScalingStroke ?? globalProps.nonScalingStroke, + className: mergeClasses('lucide-icon', globalProps.class, localProps.class), + hasA11yProp: hasAccessibleProp, + attributes: rest, + }); + return ( - + {([elementName, attrs]) => { return ( diff --git a/packages/lucide-solid/src/context.tsx b/packages/lucide-solid/src/context.tsx index 709de0cc9..c12f31109 100644 --- a/packages/lucide-solid/src/context.tsx +++ b/packages/lucide-solid/src/context.tsx @@ -4,13 +4,18 @@ export const LucideContext = createContext<{ size?: number; color?: string; strokeWidth?: number; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; class?: string; }>({ size: 24, color: 'currentColor', strokeWidth: 2, absoluteStrokeWidth: false, + nonScalingStroke: false, class: '', }); @@ -19,7 +24,11 @@ interface LucideProviderProps { size?: number; color?: string; strokeWidth?: number; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; class?: string; } @@ -29,6 +38,7 @@ export function LucideProvider(props: LucideProviderProps) { 'color', 'strokeWidth', 'absoluteStrokeWidth', + 'nonScalingStroke', 'class', ]); diff --git a/packages/lucide-solid/src/defaultAttributes.ts b/packages/lucide-solid/src/defaultAttributes.ts index 092994586..cc4036fbd 100644 --- a/packages/lucide-solid/src/defaultAttributes.ts +++ b/packages/lucide-solid/src/defaultAttributes.ts @@ -1,16 +1 @@ -import { JSX } from 'solid-js/jsx-runtime'; -import { 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; +export { defaultAttributes as default } from '@lucide/shared'; diff --git a/packages/lucide-solid/src/types.ts b/packages/lucide-solid/src/types.ts index 343e5b5c0..209acad63 100644 --- a/packages/lucide-solid/src/types.ts +++ b/packages/lucide-solid/src/types.ts @@ -1,20 +1,33 @@ import { JSX } from 'solid-js/jsx-runtime'; +import type { + LucideIconData as SharedLucideIconData, + LucideIconNode as SharedLucideIconNode, +} from '@lucide/shared/types'; -export type IconNode = [elementName: keyof JSX.IntrinsicElements, attrs: Record][]; -export interface LucideIconData { - name: string; - node: IconNode; - aliases?: string[]; -} export type SVGAttributes = Partial>; +export type LucideIconNode = SharedLucideIconNode; + +export type LucideIconData = SharedLucideIconData; + +/** + * @deprecated Use LucideIconNode instead. + */ +export type IconNode = LucideIconNode[]; + export interface LucideProps extends SVGAttributes { key?: string | number; class?: string; size?: string | number; + width?: string | number; + height?: string | number; color?: string; strokeWidth?: string | number; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; } export type LucideIcon = (props: LucideProps) => JSX.Element; diff --git a/packages/lucide-solid/tests/Icon.spec.tsx b/packages/lucide-solid/tests/Icon.spec.tsx index b10dd6211..42d274018 100644 --- a/packages/lucide-solid/tests/Icon.spec.tsx +++ b/packages/lucide-solid/tests/Icon.spec.tsx @@ -5,6 +5,8 @@ import { airVent } from './testIconNodes'; import { Icon } from '../src/lucide-solid'; describe('Using Icon Component', () => { + const airVentIcon = { name: 'air-vent', node: airVent }; + it('should render icon based on a iconNode', async () => { const { container } = render(() => ( { expect(container.firstChild).toMatchSnapshot(); }); + + it('should render icon based on icon data', async () => { + const { container } = render(() => ( + + )); + + expect(container.firstChild).toBeDefined(); + }); + + it('should support nonScalingStroke', async () => { + const { container } = render(() => ( + + )); + + expect(container.firstChild?.firstChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + }); }); describe('Icon Component Accessibility', () => { diff --git a/packages/lucide-solid/tests/__snapshots__/lucide-solid.spec.tsx.snap b/packages/lucide-solid/tests/__snapshots__/lucide-solid.spec.tsx.snap index 230c23a4a..22743b33f 100644 --- a/packages/lucide-solid/tests/__snapshots__/lucide-solid.spec.tsx.snap +++ b/packages/lucide-solid/tests/__snapshots__/lucide-solid.spec.tsx.snap @@ -2,55 +2,14 @@ exports[`Using lucide icon components > should adjust the size, stroke color and stroke width 1`] = ` -`; - -exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` -
`; -exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` - -`; - exports[`Using lucide icon components > should render an component 1`] = `
should render an icon slot 1`] = `
`; + +exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` + +`; diff --git a/packages/svelte/tests/lucide-svelte.spec.ts b/packages/svelte/tests/lucide-svelte.spec.ts index ec0cb6604..5e8dda33a 100644 --- a/packages/svelte/tests/lucide-svelte.spec.ts +++ b/packages/svelte/tests/lucide-svelte.spec.ts @@ -66,7 +66,7 @@ describe('Using lucide icon components', () => { expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML); }); - it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { + it('should scale the strokeWidth when absoluteStrokeWidth is set', () => { const { container } = render(Smile, { color: 'red', size: 48, diff --git a/packages/svelte/tsconfig.json b/packages/svelte/tsconfig.json index 3ef7dda35..58280e442 100644 --- a/packages/svelte/tsconfig.json +++ b/packages/svelte/tsconfig.json @@ -6,6 +6,11 @@ "module": "NodeNext", "moduleResolution": "NodeNext", "resolveJsonModule": true, + "baseUrl": ".", + "paths": { + "@lucide/shared": ["../shared/src/index.ts"], + "@lucide/shared/types": ["../shared/src/build/types.ts"], + }, "allowJs": true, "checkJs": true, "isolatedModules": true, diff --git a/packages/vue/rollup.config.mjs b/packages/vue/rollup.config.mjs index 94bba42f8..b69ab424a 100644 --- a/packages/vue/rollup.config.mjs +++ b/packages/vue/rollup.config.mjs @@ -2,6 +2,11 @@ import plugins from '@lucide/rollup-plugins'; import pkg from './package.json' with { type: 'json' }; import dts from 'rollup-plugin-dts'; +const dtsOptions = { + includeExternal: ['@lucide/shared/types'], + tsconfig: './tsconfig.json', +}; + const packageName = '@lucide/vue'; const outputFileName = 'lucide-vue'; const outputDir = 'dist'; @@ -58,13 +63,7 @@ export default [ format: 'es', }, ], - plugins: [ - dts({ - compilerOptions: { - preserveSymlinks: false, - }, - }), - ], + plugins: [dts(dtsOptions)], }, { input: `src/${outputFileName}.suffixed.ts`, @@ -74,13 +73,7 @@ export default [ format: 'es', }, ], - plugins: [ - dts({ - compilerOptions: { - preserveSymlinks: false, - }, - }), - ], + plugins: [dts(dtsOptions)], }, { input: `src/${outputFileName}.prefixed.ts`, @@ -90,13 +83,7 @@ export default [ format: 'es', }, ], - plugins: [ - dts({ - compilerOptions: { - preserveSymlinks: false, - }, - }), - ], + plugins: [dts(dtsOptions)], }, ...configs, ]; diff --git a/packages/vue/src/Icon.ts b/packages/vue/src/Icon.ts index 5bdecdb5f..d9f826371 100644 --- a/packages/vue/src/Icon.ts +++ b/packages/vue/src/Icon.ts @@ -1,26 +1,48 @@ -import { computed, type FunctionalComponent, h } from 'vue'; -import { buildLucideIconNode, isEmptyString, toKebabCase } from '@lucide/shared'; +import { type FunctionalComponent, h } from 'vue'; +import { + buildLucideIconNode, + hasA11yProp, + isEmptyString, + mergeClasses, + toKebabCase, +} from '@lucide/shared'; import defaultAttributes from './defaultAttributes'; -import { IconNode, LucideProps } from './types'; +import { LucideIconNode, LucideIconData, LucideProps } from './types'; import { useLucideProps } from './context'; -interface IconProps { - iconNode: IconNode; - name: string; - aliases?: string[]; -} +type IconProps = + | { + icon: LucideIconData; + iconNode?: never; + name?: never; + } + | { + icon?: never; + iconNode: LucideIconNode[]; + name: string; + }; const Icon: FunctionalComponent = ( { name, - aliases, - iconNode, + iconNode = [], + icon = { + name: toKebabCase(name), + node: iconNode, + size: 24, + aliases: [], + }, absoluteStrokeWidth, 'absolute-stroke-width': absoluteStrokeWidthKebabCase, + nonScalingStroke, + 'non-scaling-stroke': nonScalingStrokeKebabCase, strokeWidth, 'stroke-width': strokeWidthKebabCase, size, + width = size, + height = size, color, + class: classes = '', ...props }, { slots }, @@ -30,62 +52,43 @@ const Icon: FunctionalComponent = ( color: contextColor, strokeWidth: contextStrokeWidth = 2, absoluteStrokeWidth: contextAbsoluteStrokeWidth = false, + nonScalingStroke: contextNonScalingStroke = false, class: contextClass = '', } = useLucideProps(); - const calculatedStrokeWidth = computed(() => { - const isAbsoluteStrokeWidth = - isEmptyString(absoluteStrokeWidth) || - isEmptyString(absoluteStrokeWidthKebabCase) || - absoluteStrokeWidth === true || - absoluteStrokeWidthKebabCase === true || - contextAbsoluteStrokeWidth === true; + const isAbsoluteStrokeWidth = + isEmptyString(absoluteStrokeWidth) || + isEmptyString(absoluteStrokeWidthKebabCase) || + absoluteStrokeWidth === true || + absoluteStrokeWidthKebabCase === true || + contextAbsoluteStrokeWidth === true; - const strokeWidthValue = - strokeWidth || - strokeWidthKebabCase || - contextStrokeWidth || - defaultAttributes['stroke-width']; + const isNonScalingStroke = + isEmptyString(nonScalingStroke) || + isEmptyString(nonScalingStrokeKebabCase) || + nonScalingStroke === true || + nonScalingStrokeKebabCase === true || + contextNonScalingStroke === true; - if (isAbsoluteStrokeWidth) { - return ( - (Number(strokeWidthValue) * 24) / Number(size ?? contextSize ?? defaultAttributes.width) - ); - } + const { class: propsClass, ...restProps } = props; - return strokeWidthValue; + const [, svgAttributes, builtIconNode] = buildLucideIconNode(icon, { + color: color ?? contextColor, + width: width ?? size ?? contextSize, + height: height ?? size ?? contextSize, + strokeWidth: + strokeWidth ?? + strokeWidthKebabCase ?? + contextStrokeWidth ?? + defaultAttributes['stroke-width'], + absoluteStrokeWidth: isAbsoluteStrokeWidth, + nonScalingStroke: isNonScalingStroke, + className: mergeClasses(contextClass, propsClass), + hasA11yProp: !!slots || hasA11yProp(restProps), + attributes: restProps, }); - const normalizedName = computed(() => (name != null ? toKebabCase(name) : undefined)); - - const iconName = computed(() => (normalizedName.value ? `${normalizedName.value}-icon` : 'icon')); - - const [, svgAttributes, builtIconNode = []] = buildLucideIconNode( - { - name: iconName.value, - aliases, - size: 24, - node: iconNode, - }, - { - size: size ?? contextSize ?? defaultAttributes.width, - color: color ?? contextColor ?? defaultAttributes.stroke, - strokeWidth: calculatedStrokeWidth.value, - className: contextClass, - }, - ); - - const iconAttributes = { - ...svgAttributes, - ...props, - width: svgAttributes.width, - height: svgAttributes.height, - stroke: svgAttributes.stroke, - 'stroke-width': svgAttributes['stroke-width'], - class: svgAttributes.class, - }; - - return h('svg', iconAttributes, [ + return h('svg', svgAttributes, [ ...builtIconNode.map((child) => h(...child)), ...(slots.default ? [slots.default()] : []), ]); diff --git a/packages/vue/src/context.ts b/packages/vue/src/context.ts index 7af325ec7..0435ed55c 100644 --- a/packages/vue/src/context.ts +++ b/packages/vue/src/context.ts @@ -6,7 +6,11 @@ interface LucideIconsContext { size?: number; color?: string; strokeWidth?: number; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; class?: string; } diff --git a/packages/vue/src/createLucideIcon.ts b/packages/vue/src/createLucideIcon.ts index 10d5247df..5981b8153 100644 --- a/packages/vue/src/createLucideIcon.ts +++ b/packages/vue/src/createLucideIcon.ts @@ -1,25 +1,44 @@ import { h } from 'vue'; -import { LucideIcon, LucideIconData } from './types'; +import { LucideIcon, LucideIconData, LucideIconNode } from './types'; import Icon from './Icon'; /** - * Create a Lucide icon component - * @param {object} iconData - * @returns {FunctionalComponent} LucideIcon + * Creates a Lucide icon component from icon data. + * + * @param icon Icon data object. + * @returns Lucide icon component. */ -const createLucideIcon = - (iconData: LucideIconData): LucideIcon => - (props, { slots, attrs }) => +function createLucideIcon(icon: LucideIconData): LucideIcon; + +/** + * Creates a Lucide icon component from the legacy icon arguments. + * + * @param iconName Icon name. + * @param iconNode Icon node. + * @returns Lucide icon component. + */ +function createLucideIcon(iconName: string, iconNode: LucideIconNode[]): LucideIcon; + +function createLucideIcon( + iconDataOrName: LucideIconData | string, + iconNode: LucideIconNode[] = [], +): LucideIcon { + const icon: LucideIconData = + typeof iconDataOrName === 'string' + ? { + name: iconDataOrName, + node: iconNode, + } + : iconDataOrName; + return (props, { slots }) => h( Icon, { - ...attrs, ...props, - iconNode: iconData.node, - name: iconData.name, - aliases: iconData.aliases, + icon, }, slots.default ? { default: slots.default } : undefined, ); +} export default createLucideIcon; diff --git a/packages/vue/src/defaultAttributes.ts b/packages/vue/src/defaultAttributes.ts index 8ae7d46ea..cc4036fbd 100644 --- a/packages/vue/src/defaultAttributes.ts +++ b/packages/vue/src/defaultAttributes.ts @@ -1,11 +1 @@ -export default { - 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 { defaultAttributes as default } from '@lucide/shared'; diff --git a/packages/vue/src/types.ts b/packages/vue/src/types.ts index 38d5f2f6d..f292be26a 100644 --- a/packages/vue/src/types.ts +++ b/packages/vue/src/types.ts @@ -1,16 +1,31 @@ import type { FunctionalComponent, SVGAttributes } from 'vue'; +import type { + LucideIconData as SharedLucideIconData, + LucideIconNode as SharedLucideIconNode, +} from '@lucide/shared/types'; export interface LucideProps extends Partial { size?: 24 | number; strokeWidth?: number | string; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + /** + * @deprecated Use `nonScalingStroke` instead. + */ 'absolute-stroke-width'?: boolean; + nonScalingStroke?: boolean; + 'non-scaling-stroke'?: boolean; } -export type IconNode = [elementName: string, attrs: Record][]; -export interface LucideIconData { - name: string; - node: IconNode; - aliases?: string[]; -} +export type LucideIconNode = SharedLucideIconNode; + +export type LucideIconData = SharedLucideIconData; + +/** + * @deprecated Use LucideIconNode instead. + */ +export type IconNode = LucideIconNode[]; + export type LucideIcon = FunctionalComponent; diff --git a/packages/vue/tests/Icon.spec.ts b/packages/vue/tests/Icon.spec.ts index 968a36d05..54c3c394e 100644 --- a/packages/vue/tests/Icon.spec.ts +++ b/packages/vue/tests/Icon.spec.ts @@ -5,21 +5,9 @@ import { airVent } from './testIconNodes'; import { Icon } from '../src/lucide-vue'; describe('Using Icon Component', () => { - it('should render icon based on a iconNode', async () => { - const { container } = render(Icon, { - props: { - iconNode: airVent, - name: 'AirVent', - size: 48, - color: 'red', - absoluteStrokeWidth: true, - }, - }); + const airVentIcon = { name: 'air-vent', node: airVent, size: 24 }; - expect(container.firstChild).toBeDefined(); - }); - - it('should render icon and match snapshot', async () => { + it('should render icon based on an icon node', async () => { const { container } = render(Icon, { props: { iconNode: airVent, @@ -32,4 +20,42 @@ describe('Using Icon Component', () => { expect(container.firstChild).toMatchSnapshot(); }); + + it('should render icon based on icon data', async () => { + const { container } = render(Icon, { + props: { + icon: airVentIcon, + size: 48, + color: 'red', + absoluteStrokeWidth: true, + }, + }); + + expect(container.firstChild).toMatchSnapshot(); + }); + + it('should render icon and match snapshot', async () => { + const { container } = render(Icon, { + props: { + icon: airVentIcon, + size: 48, + color: 'red', + absoluteStrokeWidth: true, + }, + }); + + expect(container.firstChild).toMatchSnapshot(); + }); + + it('should render icon based on icon data', async () => { + const { container } = render(Icon, { + props: { + icon: airVentIcon, + size: 48, + color: 'red', + }, + }); + + expect(container.firstChild).toBeDefined(); + }); }); diff --git a/packages/vue/tests/__snapshots__/Icon.spec.ts.snap b/packages/vue/tests/__snapshots__/Icon.spec.ts.snap index 9907eb584..a765d4e6e 100644 --- a/packages/vue/tests/__snapshots__/Icon.spec.ts.snap +++ b/packages/vue/tests/__snapshots__/Icon.spec.ts.snap @@ -2,7 +2,63 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = ` + + + + + +`; + +exports[`Using Icon Component > should render icon based on an icon node 1`] = ` + + + + + + +`; + +exports[`Using Icon Component > should render icon based on icon data 1`] = ` + should render the icon with LucideProvider 1`] = ` should add a class to the element 1`] = `
should add a class to the element 1`] = exports[`Using lucide icon components > should add a style attribute to the element 1`] = `
should add a style attribute to the elem exports[`Using lucide icon components > should adjust the size, stroke color and stroke width 1`] = `
should adjust the size, stroke color and exports[`Using lucide icon components > should pass children to the icon slot 1`] = `
should pass children to the icon slot 1` exports[`Using lucide icon components > should render an component 1`] = `
{ expect(IconComponent).toHaveAttribute( 'class', - 'lucide lucide-house-icon lucide-home provider-class icon-class', + 'lucide lucide-house lucide-home provider-class icon-class', ); }); }); diff --git a/packages/vue/tests/lucide-vue.spec.ts b/packages/vue/tests/lucide-vue.spec.ts index bde2e0fd5..cf2b536fa 100644 --- a/packages/vue/tests/lucide-vue.spec.ts +++ b/packages/vue/tests/lucide-vue.spec.ts @@ -59,7 +59,7 @@ describe('Using lucide icon components', () => { expect(icon).toHaveClass('my-icon'); expect(icon).toHaveClass('lucide'); - expect(icon).toHaveClass('lucide-smile-icon'); + expect(icon).toHaveClass('lucide-smile'); }); it('should add a style attribute to the element', () => { @@ -152,7 +152,7 @@ describe('Using lucide icon components', () => { expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML); }); - it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { + it('should scale the strokeWidth when absoluteStrokeWidth is set', () => { const { container } = render(Pen, { props: { size: 48, @@ -168,7 +168,7 @@ describe('Using lucide icon components', () => { expect(icon).toHaveAttribute('stroke-width', '1'); }); - it('should not scale the strokeWidth when absoluteStrokeWidth is as empty value attribute', () => { + it('should scale the strokeWidth when absoluteStrokeWidth is as empty value attribute', () => { const { container } = render(Pen, { props: { size: 48, @@ -184,7 +184,7 @@ describe('Using lucide icon components', () => { expect(icon).toHaveAttribute('stroke-width', '1'); }); - it('should not scale the strokeWidth when absoluteStrokeWidth is written in kebabCase', () => { + it('should scale the strokeWidth when absoluteStrokeWidth is written in kebabCase', () => { const { container } = render(Pen, { props: { size: 48, @@ -200,4 +200,56 @@ describe('Using lucide icon components', () => { expect(icon).toHaveAttribute('stroke', 'red'); expect(icon).toHaveAttribute('stroke-width', '1'); }); + + it('should apply vector-effect when nonScalingStroke is set', () => { + const { container } = render(Pen, { + props: { + size: 48, + color: 'red', + nonScalingStroke: true, + }, + }); + + const icon = container.firstElementChild; + + expect(icon).toHaveAttribute('width', '48'); + expect(icon).toHaveAttribute('stroke', 'red'); + expect(icon).toHaveAttribute('stroke-width', '2'); + expect(icon?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + }); + + it('should apply vector-effect when nonScalingStroke is as empty value attribute', () => { + const { container } = render(Pen, { + props: { + size: 48, + color: 'red', + nonScalingStroke: '' as unknown as boolean, // Vue treats empty string as true for boolean props + }, + }); + + const icon = container.firstElementChild; + + expect(icon).toHaveAttribute('width', '48'); + expect(icon).toHaveAttribute('stroke', 'red'); + expect(icon).toHaveAttribute('stroke-width', '2'); + expect(icon?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + }); + + it('should apply vector-effect when nonScalingStroke is written in kebabCase', () => { + const { container } = render(Pen, { + props: { + size: 48, + color: 'red', + 'stroke-width': '2', + 'non-scaling-stroke': '' as unknown as boolean, // Vue treats empty string as true for boolean props + }, + }); + + const icon = container.firstElementChild; + + expect(icon).toHaveAttribute('width', '48'); + expect(icon).toHaveAttribute('stroke', 'red'); + expect(icon).toHaveAttribute('stroke-width', '2'); + expect(icon?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + }); }); diff --git a/packages/vue/tsconfig.json b/packages/vue/tsconfig.json index c38e018fa..dba73c989 100644 --- a/packages/vue/tsconfig.json +++ b/packages/vue/tsconfig.json @@ -4,6 +4,11 @@ "useDefineForClassFields": true, "module": "ESNext", "moduleResolution": "Node", + "baseUrl": ".", + "paths": { + "@lucide/shared": ["../shared/src/index.ts"], + "@lucide/shared/types": ["../shared/src/build/types.ts"], + }, "strict": true, "jsx": "preserve", "resolveJsonModule": true, From 06e962e44b3e74d781e4395d73274dac3dbc1e34 Mon Sep 17 00:00:00 2001 From: Karsa Date: Fri, 22 May 2026 21:28:11 +0200 Subject: [PATCH 3/8] feat(): unify icon build logic some more, add nonScalingStroke option, marking absoluteStrokeWidth as deprecated --- packages/astro/src/Icon.astro | 2 +- packages/lucide-preact/src/Icon.ts | 11 +- packages/lucide-preact/src/types.ts | 2 +- packages/lucide-react-native/src/Icon.ts | 2 +- packages/lucide-react/src/Icon.ts | 2 +- packages/lucide-solid/src/Icon.tsx | 2 +- .../shared/src/build/buildLucideIconNode.ts | 16 +- packages/shared/src/build/types.ts | 7 +- packages/shared/src/utils/toCamelCase.ts | 25 +- packages/svelte/package.json | 2 +- packages/svelte/scripts/exportTemplate.mts | 4 +- packages/svelte/scripts/patchCopiedUtils.mts | 14 + packages/svelte/src/Icon.svelte | 8 +- packages/svelte/src/types.ts | 2 +- .../__snapshots__/lucide-svelte.spec.ts.snap | 40 ++ packages/svelte/tests/lucide-svelte.spec.ts | 18 + packages/vue/src/Icon.ts | 9 +- packages/vue/tests/Icon.spec.ts | 25 - .../vue/tests/__snapshots__/Icon.spec.ts.snap | 28 - pnpm-lock.yaml | 527 ++++++++++-------- 20 files changed, 426 insertions(+), 320 deletions(-) create mode 100644 packages/svelte/scripts/patchCopiedUtils.mts diff --git a/packages/astro/src/Icon.astro b/packages/astro/src/Icon.astro index b20d3b835..7d54bedfa 100644 --- a/packages/astro/src/Icon.astro +++ b/packages/astro/src/Icon.astro @@ -21,7 +21,7 @@ const { ...rest } = Astro.props; -const [, builtIconAttributes, builtIconNode] = buildLucideIconNode(icon, { +const [, builtIconAttributes, builtIconNode = []] = buildLucideIconNode(icon, { color, width: width ?? size, height: height ?? size, diff --git a/packages/lucide-preact/src/Icon.ts b/packages/lucide-preact/src/Icon.ts index 3bb74276d..c9c65dcfe 100644 --- a/packages/lucide-preact/src/Icon.ts +++ b/packages/lucide-preact/src/Icon.ts @@ -1,19 +1,18 @@ import { h, toChildArray } from 'preact'; import { buildLucideIconNode, hasA11yProp, mergeClasses } from '@lucide/shared'; -import defaultAttributes from './defaultAttributes'; import type { LucideIconData, LucideIconNode, LucideProps } from './types'; import { useLucideContext } from './context'; type IconComponentProps = LucideProps & ( - | { - iconNode: LucideIconNode; - icon?: never; - } | { icon: LucideIconData; iconNode?: never; } + | { + iconNode: LucideIconNode[]; + icon?: never; + } ); /** @@ -59,7 +58,7 @@ const Icon = ({ class: contextClass = '', } = useLucideContext() ?? {}; - const [name, svgAttributes, builtIconNode] = buildLucideIconNode(icon, { + const [name, svgAttributes, builtIconNode = []] = buildLucideIconNode(icon, { color: color ?? contextColor, width: width ?? size ?? contextSize, height: height ?? size ?? contextSize, diff --git a/packages/lucide-preact/src/types.ts b/packages/lucide-preact/src/types.ts index 6ccc4ad25..15204ae36 100644 --- a/packages/lucide-preact/src/types.ts +++ b/packages/lucide-preact/src/types.ts @@ -2,7 +2,7 @@ import { type FunctionComponent, type JSX } from 'preact'; import type { LucideIconData as SharedLucideIconData, LucideIconNode as SharedLucideIconNode, -} from '@lucide/shared'; +} from '@lucide/shared/types'; export type LucideIconNode = SharedLucideIconNode; diff --git a/packages/lucide-react-native/src/Icon.ts b/packages/lucide-react-native/src/Icon.ts index 0d49ccba9..2ada9234d 100644 --- a/packages/lucide-react-native/src/Icon.ts +++ b/packages/lucide-react-native/src/Icon.ts @@ -81,7 +81,7 @@ const Icon = forwardRef( const hasAccessibleProp = Boolean(children) || hasA11yProp(rest); - const [, svgAttributes, builtIconNode] = buildLucideIconForReact(icon, { + const [, svgAttributes, builtIconNode = []] = buildLucideIconForReact(icon, { color: color ?? contextColor, width: width ?? size ?? contextSize, height: height ?? size ?? contextSize, diff --git a/packages/lucide-react/src/Icon.ts b/packages/lucide-react/src/Icon.ts index ed2de90d6..cf52b9688 100644 --- a/packages/lucide-react/src/Icon.ts +++ b/packages/lucide-react/src/Icon.ts @@ -66,7 +66,7 @@ const Icon = forwardRef( const hasAccessibleProp = Boolean(children) || hasA11yProp(rest); - const [name, svgAttributes, builtIconNode] = buildLucideIconForReact(icon, { + const [name, svgAttributes, builtIconNode = []] = buildLucideIconForReact(icon, { color: color ?? contextColor, width: width ?? size ?? contextSize, height: height ?? size ?? contextSize, diff --git a/packages/lucide-solid/src/Icon.tsx b/packages/lucide-solid/src/Icon.tsx index 2183f4053..d53b00e23 100644 --- a/packages/lucide-solid/src/Icon.tsx +++ b/packages/lucide-solid/src/Icon.tsx @@ -39,7 +39,7 @@ const Icon = (props: LucideProps & IconProps) => { aliases: [], }; - const [, svgAttributes, builtIconNode] = buildLucideIconNode(icon, { + const [, svgAttributes, builtIconNode = []] = buildLucideIconNode(icon, { color: localProps.color ?? globalProps.color, width: localProps.width ?? localProps.size ?? globalProps.size, height: localProps.height ?? localProps.size ?? globalProps.size, diff --git a/packages/shared/src/build/buildLucideIconNode.ts b/packages/shared/src/build/buildLucideIconNode.ts index 6cdb2c940..3ab903c41 100644 --- a/packages/shared/src/build/buildLucideIconNode.ts +++ b/packages/shared/src/build/buildLucideIconNode.ts @@ -17,8 +17,8 @@ function buildLucideIconNode(icon: LucideIconData, params: LucideBuildParams = { const viewBoxHeight = icon.size ?? icon.height ?? defaultAttributes['height']; const aliasClassNames = icon.aliases - ?.filter((alias): alias is string => typeof alias === 'string' && alias.trim() !== '') - .map((alias) => `lucide-${alias}`) ?? []; + ?.filter((alias: string): alias is string => typeof alias === 'string' && alias.trim() !== '') + .map((alias: string) => `lucide-${alias}`) ?? []; const iconClassNames = [...(icon.name ? [`lucide-${icon.name}`] : []), ...aliasClassNames]; const classNamesFromClassName = params.className?.split(' ').filter(Boolean) ?? []; const className = @@ -55,12 +55,14 @@ function buildLucideIconNode(icon: LucideIconData, params: LucideBuildParams = { return [ 'svg', attributes, - icon.node.map(([name, attrs]) => [ - name, - params.nonScalingStroke + icon.node.map((child): LucideIconNode => { + const [name, attrs, children] = child; + const nextAttrs = params.nonScalingStroke ? { [getAttributeName('vector-effect')]: 'non-scaling-stroke', ...attrs } - : attrs, - ]), + : attrs; + + return children ? [name, nextAttrs, children] : [name, nextAttrs]; + }), ]; } diff --git a/packages/shared/src/build/types.ts b/packages/shared/src/build/types.ts index 5844972ba..39a99662f 100644 --- a/packages/shared/src/build/types.ts +++ b/packages/shared/src/build/types.ts @@ -1,4 +1,5 @@ -export type SVGProps = Record; +/* eslint-disable @typescript-eslint/no-explicit-any */ +export type SVGProps = Record; /** * A Lucide icon node (an svgson-like internal format) @@ -70,6 +71,6 @@ export type LucideBuildParams = SVGProps> */ attributeNames?: Record; } & ( - | { size?: string | number; width?: never; height?: never } - | { size?: never; width?: string | number; height?: string | number } + | { size?: string | number | null; width?: never; height?: never } + | { size?: never; width?: string | number | null; height?: string | number | null } ); diff --git a/packages/shared/src/utils/toCamelCase.ts b/packages/shared/src/utils/toCamelCase.ts index 52ade7dbd..6faf1194b 100644 --- a/packages/shared/src/utils/toCamelCase.ts +++ b/packages/shared/src/utils/toCamelCase.ts @@ -4,7 +4,24 @@ * @param {string} string * @returns {string} A camelized string */ -export const toCamelCase = (string: T) => - string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => - p2 ? p2.toUpperCase() : p1.toLowerCase(), - ); +export const toCamelCase = (string: T) => { + let out = ''; + let upperNext = false; + + for (const ch of string) { + if (ch === '-' || ch === '_' || ch <= ' ') { + upperNext = out.length > 0; + continue; + } + + if (out.length === 0) { + out += ch.toLowerCase(); + } else { + out += upperNext ? ch.toUpperCase() : ch; + } + + upperNext = false; + } + + return out as string; +}; diff --git a/packages/svelte/package.json b/packages/svelte/package.json index de071b825..900f5a4ac 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -48,7 +48,7 @@ "scripts": { "build": "pnpm clean && pnpm copy:license && pnpm copy:utils && pnpm build:icons && pnpm build:package && pnpm build:license", "copy:license": "cp ../../LICENSE ./LICENSE", - "copy:utils": "cpy ../../packages/shared/src/build/{buildLucideIconNode.ts,defaultAttributes.ts,types.ts} ../../packages/shared/src/utils/{hasA11yProp.ts,mergeClasses.ts} ./src/utils/", + "copy:utils": "cpy ../../packages/shared/src/build/{buildLucideIconNode.ts,defaultAttributes.ts,types.ts} ../../packages/shared/src/utils/{hasA11yProp.ts,mergeClasses.ts} ./src/utils/ && node ./scripts/patchCopiedUtils.mts", "clean": "rm -rf dist stats ./src/icons/*.{ts,svelte} ./src/aliases/{aliases,prefixed,suffixed}.ts", "build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --exportFileName=index.ts --iconFileExtension=.svelte --importImportFileExtension=.svelte --separateIconFileExport --separateIconFileExportExtension=.ts --withAliases --aliasesFileExtension=.ts --separateAliasesFile --separateAliasesFileExtension=.ts --aliasImportFileExtension=.js --pretty=false", "build:package": "svelte-package --input ./src", diff --git a/packages/svelte/scripts/exportTemplate.mts b/packages/svelte/scripts/exportTemplate.mts index e65a57fc1..ef046a584 100644 --- a/packages/svelte/scripts/exportTemplate.mts +++ b/packages/svelte/scripts/exportTemplate.mts @@ -8,8 +8,8 @@ export default defineExportTemplate( const svgBase64 = base64SVG(svgContents); return `\ -
`; +exports[`Using lucide icon components > should apply vector-effect when nonScalingStroke is set 1`] = ` + +`; + exports[`Using lucide icon components > should render an component 1`] = `
{ expect(container.innerHTML).toMatchSnapshot(); }); + it('should apply vector-effect when nonScalingStroke is set', () => { + const { container } = render(Smile, { + color: 'red', + size: 48, + nonScalingStroke: true, + }); + + const IconComponent = container.firstElementChild; + + expect(IconComponent).toHaveAttribute('width', '48'); + expect(IconComponent).toHaveAttribute('height', '48'); + expect(IconComponent).toHaveAttribute('stroke', 'red'); + expect(IconComponent).toHaveAttribute('stroke-width', '2'); + expect(IconComponent?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + + expect(container.innerHTML).toMatchSnapshot(); + }); + it('should use context values from the global set properties', () => { const { container } = render(ContextWrapper); diff --git a/packages/vue/src/Icon.ts b/packages/vue/src/Icon.ts index d9f826371..54aa8c474 100644 --- a/packages/vue/src/Icon.ts +++ b/packages/vue/src/Icon.ts @@ -6,7 +6,6 @@ import { mergeClasses, toKebabCase, } from '@lucide/shared'; -import defaultAttributes from './defaultAttributes'; import { LucideIconNode, LucideIconData, LucideProps } from './types'; import { useLucideProps } from './context'; @@ -72,15 +71,11 @@ const Icon: FunctionalComponent = ( const { class: propsClass, ...restProps } = props; - const [, svgAttributes, builtIconNode] = buildLucideIconNode(icon, { + const [, svgAttributes, builtIconNode = []] = buildLucideIconNode(icon, { color: color ?? contextColor, width: width ?? size ?? contextSize, height: height ?? size ?? contextSize, - strokeWidth: - strokeWidth ?? - strokeWidthKebabCase ?? - contextStrokeWidth ?? - defaultAttributes['stroke-width'], + strokeWidth: strokeWidth ?? strokeWidthKebabCase ?? contextStrokeWidth, absoluteStrokeWidth: isAbsoluteStrokeWidth, nonScalingStroke: isNonScalingStroke, className: mergeClasses(contextClass, propsClass), diff --git a/packages/vue/tests/Icon.spec.ts b/packages/vue/tests/Icon.spec.ts index 54c3c394e..68ceca4f2 100644 --- a/packages/vue/tests/Icon.spec.ts +++ b/packages/vue/tests/Icon.spec.ts @@ -21,19 +21,6 @@ describe('Using Icon Component', () => { expect(container.firstChild).toMatchSnapshot(); }); - it('should render icon based on icon data', async () => { - const { container } = render(Icon, { - props: { - icon: airVentIcon, - size: 48, - color: 'red', - absoluteStrokeWidth: true, - }, - }); - - expect(container.firstChild).toMatchSnapshot(); - }); - it('should render icon and match snapshot', async () => { const { container } = render(Icon, { props: { @@ -46,16 +33,4 @@ describe('Using Icon Component', () => { expect(container.firstChild).toMatchSnapshot(); }); - - it('should render icon based on icon data', async () => { - const { container } = render(Icon, { - props: { - icon: airVentIcon, - size: 48, - color: 'red', - }, - }); - - expect(container.firstChild).toBeDefined(); - }); }); diff --git a/packages/vue/tests/__snapshots__/Icon.spec.ts.snap b/packages/vue/tests/__snapshots__/Icon.spec.ts.snap index a765d4e6e..e7b15be0c 100644 --- a/packages/vue/tests/__snapshots__/Icon.spec.ts.snap +++ b/packages/vue/tests/__snapshots__/Icon.spec.ts.snap @@ -55,31 +55,3 @@ exports[`Using Icon Component > should render icon based on an icon node 1`] = ` /> `; - -exports[`Using Icon Component > should render icon based on icon data 1`] = ` - - - - - - -`; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39ee05ff8..14a5d56ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -51,7 +51,10 @@ importers: version: 4.0.12(vitest@4.0.12) ajv-cli: specifier: ^5.0.0 - version: 5.0.0(ts-node@10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@24.10.1)(typescript@5.9.3)) + version: 5.0.0(ts-node@10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@25.9.1)(typescript@5.9.3)) + cpy-cli: + specifier: ^7.0.0 + version: 7.0.0 dotenv: specifier: ^17.4.2 version: 17.4.2 @@ -117,7 +120,7 @@ importers: version: 5.3.1 vitest: specifier: 4.0.12 - version: 4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 4.0.12(@types/debug@4.1.12)(@types/node@25.9.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) yargs: specifier: ^17.7.2 version: 17.7.2 @@ -232,10 +235,10 @@ importers: version: 5.3.1 vitepress: specifier: ^1.6.4 - version: 1.6.4(@algolia/client-search@5.48.1)(@types/node@24.10.1)(@types/react@18.3.27)(axios@1.13.2)(fuse.js@7.1.0)(less@4.2.0)(postcss@8.5.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.98.0)(search-insights@2.8.2)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3) + version: 1.6.4(@algolia/client-search@5.48.1)(@types/node@25.9.1)(@types/react@18.3.27)(axios@1.13.2)(fuse.js@7.1.0)(less@4.2.0)(postcss@8.5.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.98.0)(search-insights@2.8.2)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3) vitepress-plugin-group-icons: specifier: ^1.6.5 - version: 1.7.1(vite@5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)) + version: 1.7.1(vite@5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)) vitepress-plugin-llms: specifier: ^1.11.1 version: 1.12.0 @@ -244,7 +247,7 @@ importers: devDependencies: '@angular-eslint/builder': specifier: ~21.1.0 - version: 21.1.0(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) '@angular-eslint/eslint-plugin': specifier: ~21.1.0 version: 21.1.0(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) @@ -253,16 +256,16 @@ importers: version: 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) '@angular-eslint/schematics': specifier: ~21.1.0 - version: 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) + version: 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) '@angular-eslint/template-parser': specifier: ~21.1.0 version: 21.1.0(eslint@8.57.1)(typescript@5.9.3) '@angular/build': specifier: ^21.0.3 - version: 21.2.3(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(@angular/compiler@21.2.5)(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(@angular/platform-browser@21.2.5(@angular/common@21.2.5(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2)))(@types/node@24.10.1)(chokidar@5.0.0)(jiti@2.7.0)(less@4.2.0)(ng-packagr@21.2.1(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3))(postcss@8.5.8)(stylus@0.56.0)(terser@5.47.1)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.1)(yaml@2.8.0) + version: 21.2.3(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(@angular/compiler@21.2.5)(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(@angular/platform-browser@21.2.5(@angular/common@21.2.5(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2)))(@types/node@25.9.1)(chokidar@5.0.0)(jiti@2.7.0)(less@4.2.0)(ng-packagr@21.2.1(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3))(postcss@8.5.8)(stylus@0.56.0)(terser@5.47.1)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.1)(yaml@2.8.0) '@angular/cli': specifier: ^21.0.3 - version: 21.2.3(@types/node@24.10.1)(chokidar@5.0.0) + version: 21.2.3(@types/node@25.9.1)(chokidar@5.0.0) '@angular/common': specifier: ^21.0.0 version: 21.2.5(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(rxjs@7.8.2) @@ -292,10 +295,10 @@ importers: version: link:../../tools/build-helpers '@vitest/browser-playwright': specifier: ^4.0.12 - version: 4.1.1(playwright@1.58.2)(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + version: 4.1.1(playwright@1.58.2)(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) angular-eslint: specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript-eslint@8.57.1(eslint@8.57.1)(typescript@5.9.3))(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript-eslint@8.57.1(eslint@8.57.1)(typescript@5.9.3))(typescript@5.9.3) ng-packagr: specifier: ^21.0.0 version: 21.2.1(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3) @@ -326,7 +329,7 @@ importers: version: 6.9.1 astro: specifier: ^6.1.10 - version: 6.1.10(@types/node@24.10.1)(@vercel/blob@2.3.0)(db0@0.3.4)(ioredis@5.10.1)(jiti@2.7.0)(less@4.2.0)(rollup@4.60.3)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3)(yaml@2.8.0) + version: 6.1.10(@types/node@25.9.1)(@vercel/blob@2.3.0)(db0@0.3.4)(ioredis@5.10.1)(jiti@2.7.0)(less@4.2.0)(rollup@4.60.3)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3)(yaml@2.8.0) jest-serializer-html: specifier: ^7.1.0 version: 7.1.0 @@ -341,10 +344,10 @@ importers: version: 5.9.3 vite: specifier: ^7.3.2 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) vitest: specifier: ^4.1.1 - version: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + version: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) packages/icons: devDependencies: @@ -383,10 +386,10 @@ importers: version: 5.9.3 vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) vitest: specifier: ^4.0.12 - version: 4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 4.0.12(@types/debug@4.1.12)(@types/node@25.9.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) packages/lucide: devDependencies: @@ -419,7 +422,7 @@ importers: version: 5.9.3 vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) packages/lucide-preact: devDependencies: @@ -434,7 +437,7 @@ importers: version: link:../shared '@preact/preset-vite': specifier: ^2.10.2 - version: 2.10.2(@babel/core@7.29.0)(preact@10.28.2)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + version: 2.10.2(@babel/core@7.29.0)(preact@10.28.2)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@testing-library/jest-dom': specifier: ^6.8.0 version: 6.9.1 @@ -458,7 +461,7 @@ importers: version: 5.9.3 vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) packages/lucide-react: devDependencies: @@ -477,12 +480,15 @@ importers: '@testing-library/react': specifier: ^14.1.2 version: 14.3.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@types/node': + specifier: ^25.9.1 + version: 25.9.1 '@types/react': specifier: ^18.2.37 version: 18.3.27 '@vitejs/plugin-react': specifier: ^4.6.0 - version: 4.7.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + version: 4.7.0(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) jest-serializer-html: specifier: ^7.1.0 version: 7.1.0 @@ -506,7 +512,7 @@ importers: version: 5.9.3 vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) packages/lucide-react-native: devDependencies: @@ -533,7 +539,7 @@ importers: version: 18.3.27 '@vitejs/plugin-react': specifier: ^4.2.1 - version: 4.7.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + version: 4.7.0(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) jest-serializer-html: specifier: ^7.1.0 version: 7.1.0 @@ -560,7 +566,7 @@ importers: version: 5.9.3 vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) packages/lucide-solid: devDependencies: @@ -611,10 +617,10 @@ importers: version: 5.9.3 vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) vite-plugin-solid: specifier: ^2.11.6 - version: 2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + version: 2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) packages/lucide-static: devDependencies: @@ -647,7 +653,7 @@ importers: devDependencies: vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) packages/svelte: devDependencies: @@ -662,13 +668,13 @@ importers: version: 2.5.6(svelte@5.43.14)(typescript@5.9.3) '@sveltejs/vite-plugin-svelte': specifier: ^6.1.3 - version: 6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + version: 6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@testing-library/jest-dom': specifier: ^6.8.0 version: 6.9.1 '@testing-library/svelte': specifier: ^5.2.8 - version: 5.2.9(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + version: 5.2.9(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) '@tsconfig/svelte': specifier: ^5.0.4 version: 5.0.6 @@ -692,7 +698,7 @@ importers: version: 5.9.3 vite: specifier: ^6.3.6 - version: 6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) packages/vue: devDependencies: @@ -713,10 +719,10 @@ importers: version: 8.1.0(@vue/compiler-sfc@3.5.24)(vue@3.5.24(typescript@5.9.3)) '@vitejs/plugin-vue': specifier: ^4.6.2 - version: 4.6.2(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3)) + version: 4.6.2(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3)) '@vitejs/plugin-vue2': specifier: 2.2.0 - version: 2.2.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3)) + version: 2.2.0(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3)) '@vue/test-utils': specifier: 2.4.5 version: 2.4.5 @@ -731,7 +737,7 @@ importers: version: 5.9.3 vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) vue: specifier: ^3.4.21 version: 3.5.24(typescript@5.9.3) @@ -5541,8 +5547,8 @@ packages: '@types/node@22.19.1': resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==} - '@types/node@24.10.1': - resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + '@types/node@25.9.1': + resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==} '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} @@ -6948,6 +6954,10 @@ packages: resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} engines: {node: '>=18'} + copy-file@11.1.0: + resolution: {integrity: sha512-X8XDzyvYaA6msMyAM575CUoygY5b44QzLcGRKsK3MFmXcOvQa518dNPLsKYwkYsn72g3EiW+LE0ytd/FlqWmyw==} + engines: {node: '>=18'} + core-js-compat@3.45.0: resolution: {integrity: sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==} @@ -6962,6 +6972,15 @@ packages: resolution: {integrity: sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==} engines: {node: '>=4'} + cpy-cli@7.0.0: + resolution: {integrity: sha512-uGCdhIxGfZcPXidCuT8w1jBknVXFx0un7NLjzqBZcdnkIWtLUnWMPk5TC37ceoVjwASLSNsRtTXXNTuFIyE2ng==} + engines: {node: '>=20'} + hasBin: true + + cpy@13.2.2: + resolution: {integrity: sha512-FEc7gmNSj4mDQCWNt3mByVn3qZX3EjMvggiqgCQPF1yJAFfyX6Ai/emN89w18TRp+QcFmdTLgX3hfNnIVgYHcw==} + engines: {node: '>=20'} + crc-32@1.2.2: resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} engines: {node: '>=0.8'} @@ -8929,6 +8948,10 @@ packages: jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + junk@4.0.1: + resolution: {integrity: sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ==} + engines: {node: '>=12.20'} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -9226,6 +9249,10 @@ packages: memoize-one@5.2.1: resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + meow@14.1.0: + resolution: {integrity: sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==} + engines: {node: '>=20'} + merge-anything@5.1.7: resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} engines: {node: '>=12.13'} @@ -9961,6 +9988,14 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + p-event@6.0.1: + resolution: {integrity: sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==} + engines: {node: '>=16.17'} + + p-filter@4.1.0: + resolution: {integrity: sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==} + engines: {node: '>=18'} + p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -9997,6 +10032,10 @@ packages: resolution: {integrity: sha512-dWgLE8AH0HjQ9fe74pUkKkvzzYT18Inp4zra3lKHnnwqGvcfcUBrvF2EAVX+envufDNBOzpPq/IBUONDbI7+3g==} engines: {node: '>=20'} + p-timeout@6.1.4: + resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} + engines: {node: '>=14.16'} + p-timeout@7.0.1: resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} engines: {node: '>=20'} @@ -11717,8 +11756,8 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@7.16.0: - resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} undici@5.28.5: resolution: {integrity: sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==} @@ -12906,11 +12945,11 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-eslint/builder@21.1.0(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3)': + '@angular-eslint/builder@21.1.0(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@angular-devkit/architect': 0.2102.3(chokidar@5.0.0) '@angular-devkit/core': 21.2.3(chokidar@5.0.0) - '@angular/cli': 21.2.3(@types/node@24.10.1)(chokidar@5.0.0) + '@angular/cli': 21.2.3(@types/node@25.9.1)(chokidar@5.0.0) eslint: 8.57.1 typescript: 5.9.3 transitivePeerDependencies: @@ -12939,13 +12978,13 @@ snapshots: ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 - '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3)': + '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@angular-devkit/core': 21.2.3(chokidar@5.0.0) '@angular-devkit/schematics': 21.2.3(chokidar@5.0.0) '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) - '@angular/cli': 21.2.3(@types/node@24.10.1)(chokidar@5.0.0) + '@angular/cli': 21.2.3(@types/node@25.9.1)(chokidar@5.0.0) ignore: 7.0.5 semver: 7.7.3 strip-json-comments: 3.1.1 @@ -12971,7 +13010,7 @@ snapshots: eslint: 8.57.1 typescript: 5.9.3 - '@angular/build@21.2.3(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(@angular/compiler@21.2.5)(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(@angular/platform-browser@21.2.5(@angular/common@21.2.5(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2)))(@types/node@24.10.1)(chokidar@5.0.0)(jiti@2.7.0)(less@4.2.0)(ng-packagr@21.2.1(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3))(postcss@8.5.8)(stylus@0.56.0)(terser@5.47.1)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.1)(yaml@2.8.0)': + '@angular/build@21.2.3(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(@angular/compiler@21.2.5)(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(@angular/platform-browser@21.2.5(@angular/common@21.2.5(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2)))(@types/node@25.9.1)(chokidar@5.0.0)(jiti@2.7.0)(less@4.2.0)(ng-packagr@21.2.1(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3))(postcss@8.5.8)(stylus@0.56.0)(terser@5.47.1)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.1)(yaml@2.8.0)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2102.3(chokidar@5.0.0) @@ -12980,8 +13019,8 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@24.10.1) - '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@inquirer/confirm': 5.1.21(@types/node@25.9.1) + '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) beasties: 0.4.1 browserslist: 4.28.1 esbuild: 0.27.3 @@ -13002,7 +13041,7 @@ snapshots: tslib: 2.8.1 typescript: 5.9.3 undici: 7.22.0 - vite: 7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) watchpack: 2.5.1 optionalDependencies: '@angular/core': 21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2) @@ -13011,7 +13050,7 @@ snapshots: lmdb: 3.5.1 ng-packagr: 21.2.1(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3) postcss: 8.5.8 - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) transitivePeerDependencies: - '@types/node' - chokidar @@ -13025,13 +13064,13 @@ snapshots: - tsx - yaml - '@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0)': + '@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0)': dependencies: '@angular-devkit/architect': 0.2102.3(chokidar@5.0.0) '@angular-devkit/core': 21.2.3(chokidar@5.0.0) '@angular-devkit/schematics': 21.2.3(chokidar@5.0.0) - '@inquirer/prompts': 7.10.1(@types/node@24.10.1) - '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@24.10.1))(@types/node@24.10.1)(listr2@9.0.5) + '@inquirer/prompts': 7.10.1(@types/node@25.9.1) + '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.1))(@types/node@25.9.1)(listr2@9.0.5) '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) '@schematics/angular': 21.2.3(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 @@ -15838,128 +15877,128 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.2(@types/node@24.10.1)': + '@inquirer/checkbox@4.3.2(@types/node@25.9.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/confirm@5.1.21(@types/node@24.10.1)': + '@inquirer/confirm@5.1.21(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/core@10.3.2(@types/node@24.10.1)': + '@inquirer/core@10.3.2(@types/node@25.9.1)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/editor@4.2.23(@types/node@24.10.1)': + '@inquirer/editor@4.2.23(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/external-editor': 1.0.3(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/external-editor': 1.0.3(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/expand@4.0.23(@types/node@24.10.1)': + '@inquirer/expand@4.0.23(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/external-editor@1.0.3(@types/node@24.10.1)': + '@inquirer/external-editor@1.0.3(@types/node@25.9.1)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 '@inquirer/figures@1.0.15': {} - '@inquirer/input@4.3.1(@types/node@24.10.1)': + '@inquirer/input@4.3.1(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/number@3.0.23(@types/node@24.10.1)': + '@inquirer/number@3.0.23(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/password@4.0.23(@types/node@24.10.1)': + '@inquirer/password@4.0.23(@types/node@25.9.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/prompts@7.10.1(@types/node@24.10.1)': + '@inquirer/prompts@7.10.1(@types/node@25.9.1)': dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@24.10.1) - '@inquirer/confirm': 5.1.21(@types/node@24.10.1) - '@inquirer/editor': 4.2.23(@types/node@24.10.1) - '@inquirer/expand': 4.0.23(@types/node@24.10.1) - '@inquirer/input': 4.3.1(@types/node@24.10.1) - '@inquirer/number': 3.0.23(@types/node@24.10.1) - '@inquirer/password': 4.0.23(@types/node@24.10.1) - '@inquirer/rawlist': 4.1.11(@types/node@24.10.1) - '@inquirer/search': 3.2.2(@types/node@24.10.1) - '@inquirer/select': 4.4.2(@types/node@24.10.1) + '@inquirer/checkbox': 4.3.2(@types/node@25.9.1) + '@inquirer/confirm': 5.1.21(@types/node@25.9.1) + '@inquirer/editor': 4.2.23(@types/node@25.9.1) + '@inquirer/expand': 4.0.23(@types/node@25.9.1) + '@inquirer/input': 4.3.1(@types/node@25.9.1) + '@inquirer/number': 3.0.23(@types/node@25.9.1) + '@inquirer/password': 4.0.23(@types/node@25.9.1) + '@inquirer/rawlist': 4.1.11(@types/node@25.9.1) + '@inquirer/search': 3.2.2(@types/node@25.9.1) + '@inquirer/select': 4.4.2(@types/node@25.9.1) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/rawlist@4.1.11(@types/node@24.10.1)': + '@inquirer/rawlist@4.1.11(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/search@3.2.2(@types/node@24.10.1)': + '@inquirer/search@3.2.2(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/select@4.4.2(@types/node@24.10.1)': + '@inquirer/select@4.4.2(@types/node@25.9.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/type@3.0.10(@types/node@24.10.1)': + '@inquirer/type@3.0.10(@types/node@25.9.1)': optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 '@ioredis/commands@1.5.1': {} @@ -16321,10 +16360,10 @@ snapshots: dependencies: '@lezer/common': 1.3.0 - '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@24.10.1))(@types/node@24.10.1)(listr2@9.0.5)': + '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.1))(@types/node@25.9.1)(listr2@9.0.5)': dependencies: - '@inquirer/prompts': 7.10.1(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/prompts': 7.10.1(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) listr2: 9.0.5 transitivePeerDependencies: - '@types/node' @@ -16869,18 +16908,18 @@ snapshots: '@poppinss/exception@1.2.3': {} - '@preact/preset-vite@2.10.2(@babel/core@7.29.0)(preact@10.28.2)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@preact/preset-vite@2.10.2(@babel/core@7.29.0)(preact@10.28.2)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.29.0) - '@prefresh/vite': 2.4.1(preact@10.28.2)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@prefresh/vite': 2.4.1(preact@10.28.2)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@rollup/pluginutils': 4.2.1 babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.0) debug: 4.4.3 picocolors: 1.1.1 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vite-prerender-plugin: 0.5.12(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite-prerender-plugin: 0.5.12(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) transitivePeerDependencies: - preact - supports-color @@ -16893,7 +16932,7 @@ snapshots: '@prefresh/utils@1.2.0': {} - '@prefresh/vite@2.4.1(preact@10.28.2)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@prefresh/vite@2.4.1(preact@10.28.2)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: '@babel/core': 7.28.5 '@prefresh/babel-plugin': 0.5.0 @@ -16901,7 +16940,7 @@ snapshots: '@prefresh/utils': 1.2.0 '@rollup/pluginutils': 4.2.1 preact: 10.28.2 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) transitivePeerDependencies: - supports-color @@ -17703,22 +17742,22 @@ snapshots: transitivePeerDependencies: - typescript - '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)))(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)))(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) obug: 2.1.1 svelte: 5.43.14 - vite: 6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)))(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)))(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) deepmerge: 4.3.1 magic-string: 0.30.21 obug: 2.1.1 svelte: 5.43.14 - vite: 6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vitefu: 1.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vite: 6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vitefu: 1.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@swc/core-darwin-arm64@1.7.23': optional: true @@ -17851,13 +17890,13 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@testing-library/svelte@5.2.9(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': + '@testing-library/svelte@5.2.9(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': dependencies: '@testing-library/dom': 10.4.1 svelte: 5.43.14 optionalDependencies: - vite: 6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vite: 6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.5.24)(vue@3.5.24(typescript@5.9.3))': dependencies: @@ -17951,7 +17990,7 @@ snapshots: '@types/fs-extra@8.1.5': dependencies: - '@types/node': 24.10.1 + '@types/node': 22.19.1 '@types/glob@7.2.0': dependencies: @@ -18021,9 +18060,9 @@ snapshots: dependencies: undici-types: 6.21.0 - '@types/node@24.10.1': + '@types/node@25.9.1': dependencies: - undici-types: 7.16.0 + undici-types: 7.24.6 '@types/prop-types@15.7.15': {} @@ -18042,7 +18081,7 @@ snapshots: '@types/sax@1.2.7': dependencies: - '@types/node': 17.0.45 + '@types/node': 22.19.1 '@types/semver@7.7.1': {} @@ -18321,11 +18360,11 @@ snapshots: - rollup - supports-color - '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: - vite: 7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - '@vitejs/plugin-react@4.7.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@vitejs/plugin-react@4.7.0(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: '@babel/core': 7.28.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.0) @@ -18333,32 +18372,32 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue2@2.2.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3))': + '@vitejs/plugin-vue2@2.2.0(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3))': dependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) vue: 3.5.24(typescript@5.9.3) - '@vitejs/plugin-vue@4.6.2(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3))': + '@vitejs/plugin-vue@4.6.2(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3))': dependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) vue: 3.5.24(typescript@5.9.3) - '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1))(vue@3.5.24(typescript@5.9.3))': + '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1))(vue@3.5.24(typescript@5.9.3))': dependencies: - vite: 5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1) + vite: 5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1) vue: 3.5.24(typescript@5.9.3) - '@vitest/browser-playwright@4.1.1(playwright@1.58.2)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': + '@vitest/browser-playwright@4.1.1(playwright@1.58.2)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': dependencies: - '@vitest/browser': 4.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) - '@vitest/mocker': 4.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/browser': 4.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + '@vitest/mocker': 4.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) playwright: 1.58.2 tinyrainbow: 3.0.3 - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) transitivePeerDependencies: - bufferutil - msw @@ -18366,26 +18405,26 @@ snapshots: - vite optional: true - '@vitest/browser-playwright@4.1.1(playwright@1.58.2)(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': + '@vitest/browser-playwright@4.1.1(playwright@1.58.2)(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': dependencies: - '@vitest/browser': 4.1.1(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) - '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/browser': 4.1.1(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) playwright: 1.58.2 tinyrainbow: 3.0.3 - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser-playwright@4.1.1(playwright@1.58.2)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': + '@vitest/browser-playwright@4.1.1(playwright@1.58.2)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': dependencies: - '@vitest/browser': 4.1.1(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) - '@vitest/mocker': 4.1.1(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/browser': 4.1.1(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + '@vitest/mocker': 4.1.1(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) playwright: 1.58.2 tinyrainbow: 3.0.3 - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) transitivePeerDependencies: - bufferutil - msw @@ -18393,16 +18432,16 @@ snapshots: - vite optional: true - '@vitest/browser@4.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': + '@vitest/browser@4.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/mocker': 4.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@vitest/utils': 4.1.1 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) ws: 8.20.1 transitivePeerDependencies: - bufferutil @@ -18411,16 +18450,16 @@ snapshots: - vite optional: true - '@vitest/browser@4.1.1(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': + '@vitest/browser@4.1.1(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@vitest/utils': 4.1.1 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) ws: 8.20.1 transitivePeerDependencies: - bufferutil @@ -18428,16 +18467,16 @@ snapshots: - utf-8-validate - vite - '@vitest/browser@4.1.1(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': + '@vitest/browser@4.1.1(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.1(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/mocker': 4.1.1(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@vitest/utils': 4.1.1 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) ws: 8.20.1 transitivePeerDependencies: - bufferutil @@ -18459,7 +18498,7 @@ snapshots: magicast: 0.5.2 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vitest: 4.0.12(@types/debug@4.1.12)(@types/node@25.9.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) transitivePeerDependencies: - supports-color @@ -18481,38 +18520,38 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.12(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@vitest/mocker@4.0.12(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: '@vitest/spy': 4.0.12 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - '@vitest/mocker@4.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@vitest/mocker@4.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: '@vitest/spy': 4.1.1 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) optional: true - '@vitest/mocker@4.1.1(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@vitest/mocker@4.1.1(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: '@vitest/spy': 4.1.1 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - '@vitest/mocker@4.1.1(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@vitest/mocker@4.1.1(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: '@vitest/spy': 4.1.1 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) '@vitest/pretty-format@4.0.12': dependencies: @@ -18558,7 +18597,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vitest: 4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vitest: 4.0.12(@types/debug@4.1.12)(@types/node@25.9.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) '@vitest/utils@4.0.12': dependencies: @@ -18783,7 +18822,7 @@ snapshots: agent-base@7.1.4: {} - ajv-cli@5.0.0(ts-node@10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@24.10.1)(typescript@5.9.3)): + ajv-cli@5.0.0(ts-node@10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@25.9.1)(typescript@5.9.3)): dependencies: ajv: 8.17.1 fast-json-patch: 3.1.1 @@ -18793,7 +18832,7 @@ snapshots: json5: 2.2.3 minimist: 1.2.8 optionalDependencies: - ts-node: 10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@24.10.1)(typescript@5.9.3) + ts-node: 10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@25.9.1)(typescript@5.9.3) ajv-formats@3.0.1(ajv@8.17.1): optionalDependencies: @@ -18858,16 +18897,16 @@ snapshots: '@algolia/requester-fetch': 5.48.1 '@algolia/requester-node-http': 5.48.1 - angular-eslint@21.1.0(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript-eslint@8.57.1(eslint@8.57.1)(typescript@5.9.3))(typescript@5.9.3): + angular-eslint@21.1.0(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript-eslint@8.57.1(eslint@8.57.1)(typescript@5.9.3))(typescript@5.9.3): dependencies: '@angular-devkit/core': 21.2.3(chokidar@5.0.0) '@angular-devkit/schematics': 21.2.3(chokidar@5.0.0) - '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) + '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) - '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) + '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) '@angular-eslint/template-parser': 21.1.0(eslint@8.57.1)(typescript@5.9.3) - '@angular/cli': 21.2.3(@types/node@24.10.1)(chokidar@5.0.0) + '@angular/cli': 21.2.3(@types/node@25.9.1)(chokidar@5.0.0) '@typescript-eslint/types': 8.57.1 '@typescript-eslint/utils': 8.57.1(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 @@ -19036,7 +19075,7 @@ snapshots: estree-walker: 3.0.3 js-tokens: 10.0.0 - astro@6.1.10(@types/node@24.10.1)(@vercel/blob@2.3.0)(db0@0.3.4)(ioredis@5.10.1)(jiti@2.7.0)(less@4.2.0)(rollup@4.60.3)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3)(yaml@2.8.0): + astro@6.1.10(@types/node@25.9.1)(@vercel/blob@2.3.0)(db0@0.3.4)(ioredis@5.10.1)(jiti@2.7.0)(less@4.2.0)(rollup@4.60.3)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3)(yaml@2.8.0): dependencies: '@astrojs/compiler': 3.0.1 '@astrojs/internal-helpers': 0.9.0 @@ -19087,8 +19126,8 @@ snapshots: unist-util-visit: 5.1.0 unstorage: 1.17.5(@vercel/blob@2.3.0)(db0@0.3.4)(ioredis@5.10.1) vfile: 6.0.3 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vitefu: 1.1.3(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vitefu: 1.1.3(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) xxhash-wasm: 1.1.0 yargs-parser: 22.0.0 zod: 4.4.3 @@ -19851,6 +19890,11 @@ snapshots: dependencies: is-what: 5.5.0 + copy-file@11.1.0: + dependencies: + graceful-fs: 4.2.11 + p-event: 6.0.1 + core-js-compat@3.45.0: dependencies: browserslist: 4.25.1 @@ -19869,6 +19913,21 @@ snapshots: js-yaml: 3.14.2 parse-json: 4.0.0 + cpy-cli@7.0.0: + dependencies: + cpy: 13.2.2 + globby: 16.2.0 + meow: 14.1.0 + + cpy@13.2.2: + dependencies: + copy-file: 11.1.0 + globby: 16.2.0 + junk: 4.0.1 + micromatch: 4.0.8 + p-filter: 4.1.0 + p-map: 7.0.4 + crc-32@1.2.2: {} crc32-stream@6.0.0: @@ -21940,7 +21999,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.10.1 + '@types/node': 22.19.1 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -22207,6 +22266,8 @@ snapshots: readable-stream: 2.3.8 setimmediate: 1.0.5 + junk@4.0.1: {} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -22703,6 +22764,8 @@ snapshots: memoize-one@5.2.1: {} + meow@14.1.0: {} + merge-anything@5.1.7: dependencies: is-what: 4.1.15 @@ -23845,6 +23908,14 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + p-event@6.0.1: + dependencies: + p-timeout: 6.1.4 + + p-filter@4.1.0: + dependencies: + p-map: 7.0.4 + p-limit@2.3.0: dependencies: p-try: 2.2.0 @@ -23881,6 +23952,8 @@ snapshots: eventemitter3: 5.0.4 p-timeout: 7.0.1 + p-timeout@6.1.4: {} + p-timeout@7.0.1: {} p-try@2.2.0: {} @@ -25826,14 +25899,14 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-node@10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@24.10.1)(typescript@5.9.3): + ts-node@10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@25.9.1)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.12 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 24.10.1 + '@types/node': 25.9.1 acorn: 8.16.0 acorn-walk: 8.3.5 arg: 4.1.3 @@ -25988,7 +26061,7 @@ snapshots: undici-types@6.21.0: {} - undici-types@7.16.0: {} + undici-types@7.24.6: {} undici@5.28.5: dependencies: @@ -26263,7 +26336,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): dependencies: '@babel/core': 7.28.5 '@types/babel__core': 7.20.5 @@ -26271,14 +26344,14 @@ snapshots: merge-anything: 5.1.7 solid-js: 1.9.10 solid-refresh: 0.6.3(solid-js@1.9.10) - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vitefu: 1.0.5(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vitefu: 1.0.5(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) optionalDependencies: '@testing-library/jest-dom': 6.9.1 transitivePeerDependencies: - supports-color - vite-prerender-plugin@0.5.12(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vite-prerender-plugin@0.5.12(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): dependencies: kolorist: 1.8.0 magic-string: 0.30.21 @@ -26286,22 +26359,22 @@ snapshots: simple-code-frame: 1.3.0 source-map: 0.7.6 stack-trace: 1.0.0-pre2 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vite@5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1): + vite@5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1): dependencies: esbuild: 0.21.5 postcss: 8.5.6 rollup: 4.60.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 fsevents: 2.3.3 less: 4.2.0 sass: 1.98.0 stylus: 0.56.0 terser: 5.47.1 - vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): + vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.4) @@ -26310,7 +26383,7 @@ snapshots: rollup: 4.60.1 tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 fsevents: 2.3.3 jiti: 2.7.0 less: 4.2.0 @@ -26319,7 +26392,7 @@ snapshots: terser: 5.47.1 yaml: 2.8.0 - vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): + vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.3) @@ -26328,7 +26401,7 @@ snapshots: rollup: 4.60.3 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 fsevents: 2.3.3 jiti: 2.7.0 less: 4.2.0 @@ -26337,7 +26410,7 @@ snapshots: terser: 5.47.1 yaml: 2.8.0 - vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): + vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.4) @@ -26346,7 +26419,7 @@ snapshots: rollup: 4.60.1 tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 fsevents: 2.3.3 jiti: 2.7.0 less: 4.2.0 @@ -26355,25 +26428,25 @@ snapshots: terser: 5.47.1 yaml: 2.8.0 - vitefu@1.0.5(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vitefu@1.0.5(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): optionalDependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vitefu@1.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vitefu@1.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): optionalDependencies: - vite: 6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vitefu@1.1.3(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vitefu@1.1.3(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): optionalDependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vitepress-plugin-group-icons@1.7.1(vite@5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)): + vitepress-plugin-group-icons@1.7.1(vite@5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)): dependencies: '@iconify-json/logos': 1.2.10 '@iconify-json/vscode-icons': 1.2.45 '@iconify/utils': 3.1.0 optionalDependencies: - vite: 5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1) + vite: 5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1) vitepress-plugin-llms@1.12.0: dependencies: @@ -26394,7 +26467,7 @@ snapshots: transitivePeerDependencies: - supports-color - vitepress@1.6.4(@algolia/client-search@5.48.1)(@types/node@24.10.1)(@types/react@18.3.27)(axios@1.13.2)(fuse.js@7.1.0)(less@4.2.0)(postcss@8.5.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.98.0)(search-insights@2.8.2)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3): + vitepress@1.6.4(@algolia/client-search@5.48.1)(@types/node@25.9.1)(@types/react@18.3.27)(axios@1.13.2)(fuse.js@7.1.0)(less@4.2.0)(postcss@8.5.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.98.0)(search-insights@2.8.2)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3): dependencies: '@docsearch/css': 3.8.2 '@docsearch/js': 3.8.2(@algolia/client-search@5.48.1)(@types/react@18.3.27)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(search-insights@2.8.2) @@ -26403,7 +26476,7 @@ snapshots: '@shikijs/transformers': 2.5.0 '@shikijs/types': 2.5.0 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1))(vue@3.5.24(typescript@5.9.3)) + '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1))(vue@3.5.24(typescript@5.9.3)) '@vue/devtools-api': 7.7.9 '@vue/shared': 3.5.24 '@vueuse/core': 12.8.2(typescript@5.9.3) @@ -26412,7 +26485,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.2.0 shiki: 2.5.0 - vite: 5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1) + vite: 5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1) vue: 3.5.24(typescript@5.9.3) optionalDependencies: postcss: 8.5.9 @@ -26443,10 +26516,10 @@ snapshots: - typescript - universal-cookie - vitest@4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): + vitest@4.0.12(@types/debug@4.1.12)(@types/node@25.9.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): dependencies: '@vitest/expect': 4.0.12 - '@vitest/mocker': 4.0.12(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/mocker': 4.0.12(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@vitest/pretty-format': 4.0.12 '@vitest/runner': 4.0.12 '@vitest/snapshot': 4.0.12 @@ -26463,11 +26536,11 @@ snapshots: tinyexec: 0.3.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 24.10.1 + '@types/node': 25.9.1 '@vitest/ui': 4.0.12(vitest@4.0.12) jsdom: 27.4.0 transitivePeerDependencies: @@ -26484,10 +26557,10 @@ snapshots: - tsx - yaml - vitest@4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vitest@4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): dependencies: '@vitest/expect': 4.1.1 - '@vitest/mocker': 4.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/mocker': 4.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@vitest/pretty-format': 4.1.1 '@vitest/runner': 4.1.1 '@vitest/snapshot': 4.1.1 @@ -26504,21 +26577,21 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.10.1 - '@vitest/browser-playwright': 4.1.1(playwright@1.58.2)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + '@types/node': 25.9.1 + '@vitest/browser-playwright': 4.1.1(playwright@1.58.2)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) '@vitest/ui': 4.0.12(vitest@4.0.12) jsdom: 20.0.3 transitivePeerDependencies: - msw optional: true - vitest@4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vitest@4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): dependencies: '@vitest/expect': 4.1.1 - '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@vitest/pretty-format': 4.1.1 '@vitest/runner': 4.1.1 '@vitest/snapshot': 4.1.1 @@ -26535,20 +26608,20 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.10.1 - '@vitest/browser-playwright': 4.1.1(playwright@1.58.2)(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + '@types/node': 25.9.1 + '@vitest/browser-playwright': 4.1.1(playwright@1.58.2)(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) '@vitest/ui': 4.0.12(vitest@4.0.12) jsdom: 27.4.0 transitivePeerDependencies: - msw - vitest@4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vitest@4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): dependencies: '@vitest/expect': 4.1.1 - '@vitest/mocker': 4.1.1(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/mocker': 4.1.1(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@vitest/pretty-format': 4.1.1 '@vitest/runner': 4.1.1 '@vitest/snapshot': 4.1.1 @@ -26565,11 +26638,11 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.10.1 - '@vitest/browser-playwright': 4.1.1(playwright@1.58.2)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + '@types/node': 25.9.1 + '@vitest/browser-playwright': 4.1.1(playwright@1.58.2)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) '@vitest/ui': 4.0.12(vitest@4.0.12) jsdom: 27.4.0 transitivePeerDependencies: From f5437f0a4c978e3e40e0f701f89a88e4422b53e7 Mon Sep 17 00:00:00 2001 From: Karsa Date: Mon, 6 Apr 2026 15:02:13 +0200 Subject: [PATCH 4/8] feat(packages): extract build logic to @lucide/shared, still wip --- .gitignore | 2 + packages/angular/package.json | 7 +- packages/angular/src/lucide-icon-base.ts | 22 +++-- packages/astro/scripts/exportTemplate.mts | 42 ++++----- packages/astro/src/Icon.astro | 44 ++++++--- packages/astro/src/createLucideIcon.ts | 15 ++-- packages/astro/src/types.ts | 8 ++ .../__snapshots__/lucide-astro.spec.ts.snap | 8 +- packages/astro/tests/createLucideIcon.spec.ts | 5 +- packages/icons/package.json | 1 + packages/icons/src/buildLucideDataUri.ts | 20 +---- packages/icons/src/buildLucideIconElement.ts | 21 +---- packages/icons/src/buildLucideIconNode.ts | 45 +--------- packages/icons/src/buildLucideSvg.ts | 11 +-- packages/icons/src/types.ts | 44 +-------- .../icons/tests/buildLucideIconNode.spec.ts | 26 ++++++ .../lucide-preact/scripts/exportTemplate.mts | 24 +++-- packages/lucide-preact/src/Icon.ts | 45 ++++++---- .../lucide-preact/src/createLucideIcon.ts | 54 +++++++---- packages/lucide-preact/src/types.ts | 16 +++- .../tests/__snapshots__/context.spec.tsx.snap | 2 +- .../createLucideIcon.spec.tsx.snap | 10 +-- .../__snapshots__/lucide-preact.spec.tsx.snap | 6 +- packages/lucide-preact/tests/context.spec.tsx | 5 +- .../tests/createLucideIcon.spec.tsx | 62 +++++++++---- .../scripts/exportTemplate.mts | 24 +++-- packages/lucide-react-native/src/Icon.ts | 61 ++++++++++--- .../src/createLucideIcon.ts | 45 +++++++--- packages/lucide-react-native/src/types.ts | 27 +++--- .../lucide-react/scripts/exportTemplate.mts | 25 +++--- packages/lucide-react/src/DynamicIcon.ts | 24 +++-- packages/lucide-react/src/Icon.ts | 76 +++++++++++++--- packages/lucide-react/src/createLucideIcon.ts | 53 +++++++---- packages/lucide-react/src/types.ts | 27 +++--- .../tests/__snapshots__/context.spec.tsx.snap | 2 +- .../createLucideIcon.spec.tsx.snap | 64 ++++++------- .../__snapshots__/lucide-react.spec.tsx.snap | 6 +- packages/lucide-react/tests/context.spec.tsx | 5 +- .../tests/createLucideIcon.spec.tsx | 62 +++++++++---- .../lucide-solid/scripts/exportTemplate.mts | 25 +++--- packages/lucide-solid/src/Icon.tsx | 86 ++++++++++-------- packages/lucide-solid/src/types.ts | 5 ++ .../tests/__snapshots__/context.spec.tsx.snap | 2 +- .../__snapshots__/lucide-solid.spec.tsx.snap | 6 +- packages/lucide-solid/tests/context.spec.tsx | 2 +- packages/lucide/src/createElement.ts | 47 +++------- packages/lucide/src/replaceElement.ts | 13 ++- .../shared/src/build/buildLucideDataUri.ts | 30 +++++++ .../src/build/buildLucideIconElement.ts | 36 ++++++++ .../shared/src/build/buildLucideIconNode.ts | 89 +++++++++++++++++++ packages/shared/src/build/buildLucideSvg.ts | 20 +++++ packages/shared/src/build/types.ts | 54 +++++++++++ packages/shared/src/index.ts | 6 ++ packages/shared/src/utils/toLucideIconData.ts | 22 +++++ .../shared/tests/toLucideIconData.spec.ts | 31 +++++++ packages/svelte/scripts/exportTemplate.mts | 11 +-- packages/svelte/src/Icon.svelte | 57 +++++++++--- packages/svelte/src/types.ts | 7 ++ .../tests/__snapshots__/Icon.spec.ts.snap | 2 +- .../__snapshots__/lucide-svelte.spec.ts.snap | 10 +-- packages/svelte/tests/setupVitest.ts | 3 +- packages/vue/scripts/exportTemplate.mts | 25 +++--- packages/vue/src/Icon.ts | 50 +++++++---- packages/vue/src/createLucideIcon.ts | 12 +-- packages/vue/src/types.ts | 5 ++ .../vue/tests/__snapshots__/Icon.spec.ts.snap | 2 +- .../tests/__snapshots__/context.spec.ts.snap | 2 +- .../__snapshots__/lucide-vue.spec.ts.snap | 10 +-- packages/vue/tests/context.spec.ts | 2 +- pnpm-lock.yaml | 3 + 70 files changed, 1125 insertions(+), 596 deletions(-) create mode 100644 packages/shared/src/build/buildLucideDataUri.ts create mode 100644 packages/shared/src/build/buildLucideIconElement.ts create mode 100644 packages/shared/src/build/buildLucideIconNode.ts create mode 100644 packages/shared/src/build/buildLucideSvg.ts create mode 100644 packages/shared/src/build/types.ts create mode 100644 packages/shared/src/utils/toLucideIconData.ts create mode 100644 packages/shared/tests/toLucideIconData.spec.ts diff --git a/.gitignore b/.gitignore index 691b0bc39..7aab235ad 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,8 @@ packages/**/src/aliases.ts !packages/**/src/aliases/index.ts packages/**/src/utils/*.ts !packages/shared/src/utils/*.ts +!packages/shared/src/build/ +!packages/shared/src/build/*.ts packages/**/src/dynamicIconImports.ts packages/**/DynamicIcon.d.ts packages/**/dynamicIconImports.js diff --git a/packages/angular/package.json b/packages/angular/package.json index 622181ed7..7ba81a27e 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -17,14 +17,15 @@ "scripts": { "ng": "ng", "watch": "ng build --watch --configuration development", - "prebuild": "pnpm clean && pnpm copy:license && pnpm build:icons", + "prebuild": "pnpm clean && pnpm copy:license && pnpm copy:utils && pnpm build:icons", "build": "pnpm prebuild && pnpm build:ng", "copy:license": "cp ../../LICENSE ./LICENSE", + "copy:utils": "mkdir -p ./src/utils && cp -f ../../packages/shared/src/build/buildLucideIconNode.ts ./src/utils/ && cp -f ../../packages/shared/src/build/types.ts ./src/utils/", "clean": "rm -rf dist && rm -rf ./src/icons/*.ts", "build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --iconFileExtension=.ts --exportFileName=lucide-angular.ts --useDefaultExports=0", "build:ng": "ng build --configuration production", - "test": "pnpm build:icons && ng test --no-watch", - "test:watch": "ng test", + "test": "pnpm copy:utils && pnpm build:icons && ng test --no-watch", + "test:watch": "pnpm copy:utils && ng test", "lint": "npx eslint 'src/**/*.{js,jsx,ts,tsx,html,css,scss}' --quiet --fix", "e2e": "ng e2e", "version": "pnpm version --git-tag-version=false" diff --git a/packages/angular/src/lucide-icon-base.ts b/packages/angular/src/lucide-icon-base.ts index 0775d0fbf..59fdb9667 100644 --- a/packages/angular/src/lucide-icon-base.ts +++ b/packages/angular/src/lucide-icon-base.ts @@ -12,6 +12,7 @@ import { LUCIDE_CONFIG } from './lucide-config'; import { LucideIconData, Nullable } from './types'; import defaultAttributes from './default-attributes'; import { lucideIconTemplate } from './lucide-icon-template'; +import buildLucideIconNode from './utils/buildLucideIconNode'; /** * @internal @@ -82,18 +83,27 @@ export abstract class LucideIconBase { const icon = this.icon(); if (icon) { const absoluteStrokeWidth = this.absoluteStrokeWidth(); - const { name, node, aliases = [] } = icon; - const classes = [name, ...aliases].map((item) => `lucide-${item}`); + const [, iconAttributes, iconNode] = buildLucideIconNode(icon as any, { + absoluteStrokeWidth, + hasA11yProp: this.title() != null, + }); + + const classes = + typeof iconAttributes['class'] === 'string' + ? iconAttributes['class'] + .split(' ') + .filter((cssClass) => cssClass && cssClass !== 'lucide') + : []; + for (const cssClass of classes) { this.renderer.addClass(this.elRef.nativeElement, cssClass); } + const contentRef = this.contentRef(); const refChild = contentRef.nativeElement; - const elements = node.map(([name, attrs]) => { + const elements = (iconNode ?? []).map(([name, attrs]) => { const element = this.renderer.createElement(name, 'http://www.w3.org/2000/svg'); - if (absoluteStrokeWidth) { - this.renderer.setAttribute(element, 'vector-effect', 'non-scaling-stroke'); - } + Object.entries(attrs).forEach(([name, value]) => this.renderer.setAttribute( element, diff --git a/packages/astro/scripts/exportTemplate.mts b/packages/astro/scripts/exportTemplate.mts index c8a41ed3a..1c43db3e4 100644 --- a/packages/astro/scripts/exportTemplate.mts +++ b/packages/astro/scripts/exportTemplate.mts @@ -1,28 +1,27 @@ 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); +export default defineExportTemplate( + async ({ componentName, iconName, iconData, getSvg, deprecated, deprecationReason }) => { + const svgContents = await getSvg(); + const svgBase64 = base64SVG(svgContents); - // Astro doesn't need keyed children in loops - const keylessChildren = children.map((c) => { - const [element, { key, ...otherAttrs }] = c; - return [element, otherAttrs]; - }); + // Astro doesn't need keyed children in loops + const keylessIconData = { + ...iconData, + node: iconData.node.map((child) => { + const [element, { key, ...otherAttrs }] = child; + return [element, otherAttrs]; + }), + }; - // TODO: build-icons' `pretty` is set to false as the prettier - // formatter uses babel which I'm not sure it supports typescript - return ` + // TODO: build-icons' `pretty` is set to false as the prettier + // formatter uses babel which I'm not sure it supports typescript + return ` import createLucideIcon from '../createLucideIcon'; -import type { AstroComponent } from '../types' +import type { AstroComponent, LucideIconData } from '../types' + +const iconData: LucideIconData = ${JSON.stringify(keylessIconData)}; /** * @component @name ${componentName} @@ -35,8 +34,9 @@ import type { AstroComponent } from '../types' * @returns {any} Astro Component * ${deprecated ? `@deprecated ${deprecationReason}` : ''} */ -const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(keylessChildren)}) as AstroComponent; +const ${componentName} = createLucideIcon(iconData) as AstroComponent; export default ${componentName}; `; -}); + }, +); diff --git a/packages/astro/src/Icon.astro b/packages/astro/src/Icon.astro index eb504dbad..df27db114 100644 --- a/packages/astro/src/Icon.astro +++ b/packages/astro/src/Icon.astro @@ -1,32 +1,50 @@ --- -import defaultAttributes from './defaultAttributes'; +import buildLucideIconNode from './utils/buildLucideIconNode'; import type { IconProps as Props } from './types'; import { hasA11yProp } from './utils/hasA11yProp'; - const { color = 'currentColor', size = 24, 'stroke-width': strokeWidth = 2, absoluteStrokeWidth = false, + name, + aliases, iconNode = [], class: className, ...rest } = Astro.props; + +const calculatedStrokeWidth = + absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth; + +const [, rawIconAttributes, builtIconNode = []] = buildLucideIconNode( + { + name, + aliases, + size: 24, + node: iconNode, + }, + { + color, + size, + strokeWidth: calculatedStrokeWidth, + className, + hasA11yProp: hasA11yProp(rest), + }, +); + +const { class: iconClassName, ...builtIconAttributes } = rawIconAttributes; +const iconAttributes = { + ...builtIconAttributes, + ...rest, +}; --- - {iconNode.map(([Tag, attrs]) => )} + {builtIconNode.map(([Tag, attrs]) => )} diff --git a/packages/astro/src/createLucideIcon.ts b/packages/astro/src/createLucideIcon.ts index 7ff99d5ec..22d12e942 100644 --- a/packages/astro/src/createLucideIcon.ts +++ b/packages/astro/src/createLucideIcon.ts @@ -1,11 +1,9 @@ import type { AstroComponentFactory } from 'astro/runtime/server/render/astro/factory.js'; -import type { IconNode } from './types'; +import type { LucideIconData } from './types'; import { render, renderSlot, createComponent, renderComponent } from 'astro/compiler-runtime'; import Icon from './Icon.astro'; -import { mergeClasses } from './utils/mergeClasses'; -import { toKebabCase } from './utils/toKebabCase'; -export default (iconName: string, iconNode: IconNode): AstroComponentFactory => { +export default (iconData: LucideIconData): AstroComponentFactory => { const Component = createComponent( ($$result, $$props: Record, $$slots) => { const { class: className, ...restProps } = $$props; @@ -14,11 +12,10 @@ export default (iconName: string, iconNode: IconNode): AstroComponentFactory => 'Icon', Icon, { - class: mergeClasses( - Boolean(iconName) && `lucide-${toKebabCase(iconName)}`, - Boolean(className) && className, - ), - iconNode, + class: className, + name: iconData.name, + aliases: iconData.aliases, + iconNode: iconData.node, ...restProps, }, { default: () => render`${renderSlot($$result, $$slots['default'])}` }, diff --git a/packages/astro/src/types.ts b/packages/astro/src/types.ts index 71f1d527f..d7ce5f411 100644 --- a/packages/astro/src/types.ts +++ b/packages/astro/src/types.ts @@ -9,6 +9,8 @@ export interface IconProps extends SVGAttributes { 'stroke-width'?: number | string; absoluteStrokeWidth?: boolean; class?: string; + name?: string; + aliases?: string[]; iconNode?: IconNode; title?: string; } @@ -17,6 +19,12 @@ export type SVGAttributes = HTMLAttributes<'svg'>; export type IconNode = IconNodeChild[]; +export interface LucideIconData { + name: string; + node: IconNode; + aliases?: string[]; +} + type IconNodeChild = [elementName: SVGElements, attrs: SVGAttributes]; // All possible svg elements according to the Astro definitions diff --git a/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap b/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap index a6ff7f2e4..6f9ff2921 100644 --- a/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap +++ b/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap @@ -47,7 +47,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" - class="lucide lucide-grid-3x3" + class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3" > should not scale the strokeWidth when ab stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" - class="lucide lucide-grid-3x3" + class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3" > should render a component 1`] = ` stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" - class="lucide lucide-grid-3x3" + class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3" > should render the icon with default attr stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" - class="lucide lucide-grid-3x3" + class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3" > { it('should create a component from an iconNode', async () => { - const AirVent = createLucideIcon('AirVent', airVent); + const AirVent = createLucideIcon({ + name: 'air-vent', + node: airVent, + }); const { container } = await render(AirVent); expect(container.innerHTML).toBeDefined(); diff --git a/packages/icons/package.json b/packages/icons/package.json index 165cbd181..7f2e917e7 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -64,6 +64,7 @@ "@lucide/build-icons": "workspace:*", "@lucide/helpers": "^1.0.0", "@lucide/rollup-plugins": "workspace:*", + "@lucide/shared": "workspace:*", "@rollup/plugin-replace": "^6.0.2", "@testing-library/jest-dom": "^6.6.3", "jest-serializer-html": "^7.1.0", diff --git a/packages/icons/src/buildLucideDataUri.ts b/packages/icons/src/buildLucideDataUri.ts index 64aa7526c..eaa3dae1d 100644 --- a/packages/icons/src/buildLucideDataUri.ts +++ b/packages/icons/src/buildLucideDataUri.ts @@ -1,5 +1,5 @@ +import { buildLucideDataUri as sharedBuildLucideDataUri } from '@lucide/shared'; import { LucideBuildParams, LucideIconData } from './types'; -import buildLucideSvg from './buildLucideSvg'; /** * Creates a base64 encoded data URI from a Lucide icon object. @@ -8,23 +8,7 @@ import buildLucideSvg from './buildLucideSvg'; * @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.'); + return sharedBuildLucideDataUri(icon as any, params as any); } export default buildLucideDataUri; diff --git a/packages/icons/src/buildLucideIconElement.ts b/packages/icons/src/buildLucideIconElement.ts index a819d0fde..c79f96ee2 100644 --- a/packages/icons/src/buildLucideIconElement.ts +++ b/packages/icons/src/buildLucideIconElement.ts @@ -1,20 +1,5 @@ -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; -}; +import { buildLucideIconElement as sharedBuildLucideIconElement } from '@lucide/shared'; +import { LucideBuildParams, LucideIconData } from './types'; /** * Creates an SvgElement from a Lucide icon object. @@ -28,7 +13,7 @@ function buildLucideIconElement( icon: LucideIconData, params: LucideBuildParams = {}, ) { - return buildDomElement(document, buildLucideIconNode(icon, params)); + return sharedBuildLucideIconElement(document, icon as any, params as any); } export default buildLucideIconElement; diff --git a/packages/icons/src/buildLucideIconNode.ts b/packages/icons/src/buildLucideIconNode.ts index a918ffbbd..9e29618dd 100644 --- a/packages/icons/src/buildLucideIconNode.ts +++ b/packages/icons/src/buildLucideIconNode.ts @@ -1,17 +1,6 @@ +import { buildLucideIconNode as sharedBuildLucideIconNode } from '@lucide/shared'; 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. * @@ -19,37 +8,7 @@ const defaultAttributes = { * @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, - ]), - ]; + return sharedBuildLucideIconNode(icon as any, params as any) as LucideIconNode; } export default buildLucideIconNode; diff --git a/packages/icons/src/buildLucideSvg.ts b/packages/icons/src/buildLucideSvg.ts index 27bca4a91..197e1b626 100644 --- a/packages/icons/src/buildLucideSvg.ts +++ b/packages/icons/src/buildLucideSvg.ts @@ -1,10 +1,5 @@ -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('')}`; +import { buildLucideSvg as sharedBuildLucideSvg } from '@lucide/shared'; +import { LucideBuildParams, LucideIconData } from './types'; /** * Creates an SVG string from a Lucide icon object. @@ -13,7 +8,7 @@ const buildDomNode = ([tagName, attributes, children = []]: LucideIconNode): str * @param params Additional build parameters. */ function buildLucideSvg(icon: LucideIconData, params: LucideBuildParams = {}) { - return buildDomNode(buildLucideIconNode(icon, params)); + return sharedBuildLucideSvg(icon as any, params as any); } export default buildLucideSvg; diff --git a/packages/icons/src/types.ts b/packages/icons/src/types.ts index 563c93fd7..08e15ffa1 100644 --- a/packages/icons/src/types.ts +++ b/packages/icons/src/types.ts @@ -1,43 +1 @@ -export type SVGProps = Record; - -/** - * 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 }); +export type { LucideBuildParams, LucideIconData, LucideIconNode, SVGProps } from '@lucide/shared'; diff --git a/packages/icons/tests/buildLucideIconNode.spec.ts b/packages/icons/tests/buildLucideIconNode.spec.ts index dc3345ac5..5af66acbf 100644 --- a/packages/icons/tests/buildLucideIconNode.spec.ts +++ b/packages/icons/tests/buildLucideIconNode.spec.ts @@ -89,4 +89,30 @@ describe('buildLucideIconNode', () => { expect(HouseSVG[1]['class']).toBe('lucide lucide-house lucide-home icon'); }); + + it('should support className with multiple classes', () => { + const HouseSVG = buildLucideIconNode(House, { + className: ' icon custom lucide-house ', + }); + + expect(HouseSVG[1]['class']).toBe('lucide lucide-house lucide-home icon custom'); + }); + + it('should not include default classes when includeDefaultClasses is false', () => { + const HouseSVG = buildLucideIconNode(House, { includeDefaultClasses: false }); + + expect(HouseSVG[1]['class']).toBeUndefined(); + }); + + it('should add aria-hidden when hasA11yProp is false', () => { + const HouseSVG = buildLucideIconNode(House, { hasA11yProp: false }); + + expect(HouseSVG[1]['aria-hidden']).toBe('true'); + }); + + it('should not add aria-hidden when hasA11yProp is true', () => { + const HouseSVG = buildLucideIconNode(House, { hasA11yProp: true }); + + expect(HouseSVG[1]['aria-hidden']).toBeUndefined(); + }); }); diff --git a/packages/lucide-preact/scripts/exportTemplate.mts b/packages/lucide-preact/scripts/exportTemplate.mts index 28cc154bc..8eaa0baa6 100644 --- a/packages/lucide-preact/scripts/exportTemplate.mts +++ b/packages/lucide-preact/scripts/exportTemplate.mts @@ -1,19 +1,16 @@ 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); +export default defineExportTemplate( + async ({ componentName, iconName, iconData, getSvg, deprecated, deprecationReason }) => { + const svgContents = await getSvg(); + const svgBase64 = base64SVG(svgContents); - return ` + return ` import createLucideIcon from '../createLucideIcon'; +import type { LucideIconData } from '../types'; + +const iconData: LucideIconData = ${JSON.stringify(iconData)}; /** * @component @name ${componentName} @@ -26,8 +23,9 @@ import createLucideIcon from '../createLucideIcon'; * @returns {JSX.Element} JSX Element * ${deprecated ? `@deprecated ${deprecationReason}` : ''} */ -const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(children)}); +const ${componentName} = createLucideIcon(iconData); export default ${componentName}; `; -}); + }, +); diff --git a/packages/lucide-preact/src/Icon.ts b/packages/lucide-preact/src/Icon.ts index 0fb278848..07583e0b6 100644 --- a/packages/lucide-preact/src/Icon.ts +++ b/packages/lucide-preact/src/Icon.ts @@ -1,12 +1,13 @@ import { h, toChildArray } from 'preact'; +import { buildLucideIconNode, hasA11yProp, mergeClasses } from '@lucide/shared'; import defaultAttributes from './defaultAttributes'; -import type { IconNode, LucideProps } from './types'; +import type { LucideIconNode, LucideProps } from './types'; import { useLucideContext } from './context'; -import { mergeClasses } from '@lucide/shared'; -import { hasA11yProp } from '@lucide/shared'; interface IconComponentProps extends LucideProps { - iconNode: IconNode; + name?: string; + aliases?: string[]; + iconNode: LucideIconNode; } /** @@ -19,8 +20,8 @@ interface IconComponentProps extends LucideProps { * @param {number} props.strokeWidth - The stroke width of the icon * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width * @param {string} props.class - The class name of the icon - * @param {IconNode} props.children - The children of the icon - * @param {IconNode} props.iconNode - The icon node of the icon + * @param {LucideIconNode} props.children - The children of the icon + * @param {LucideIconNode} props.iconNode - The icon node of the icon * * @returns {ForwardRefExoticComponent} LucideIcon */ @@ -31,6 +32,8 @@ const Icon = ({ absoluteStrokeWidth, children, iconNode, + name, + aliases, class: classes = '', ...rest }: IconComponentProps) => { @@ -47,20 +50,28 @@ const Icon = ({ ? (Number(strokeWidth ?? contextStrokeWidth) * 24) / Number(size ?? contextSize) : strokeWidth ?? contextStrokeWidth; - return h( - 'svg', + const hasAccessibleProp = Boolean(children) || hasA11yProp(rest); + + const [, svgAttributes, builtIconNode = []] = buildLucideIconNode( { - ...defaultAttributes, - width: size ?? contextSize ?? 24, - height: size ?? contextSize ?? 24, - stroke: color ?? contextColor, - ['stroke-width' as 'strokeWidth']: calculatedStrokeWidth, - class: mergeClasses('lucide', contextClass, classes), - ...(!children && !hasA11yProp(rest) && { 'aria-hidden': 'true' }), - ...rest, + name, + aliases, + size: 24, + node: iconNode, + }, + { + color: color ?? contextColor, + size: size ?? contextSize ?? defaultAttributes.width, + strokeWidth: calculatedStrokeWidth, + className: mergeClasses(contextClass, classes as string), + hasA11yProp: hasAccessibleProp, }, - [...iconNode.map(([tag, attrs]) => h(tag, attrs)), ...toChildArray(children)], ); + + return h('svg', { ...svgAttributes, ...rest }, [ + ...builtIconNode.map(([tag, attrs]) => h(tag, attrs)), + ...toChildArray(children), + ]); }; export default Icon; diff --git a/packages/lucide-preact/src/createLucideIcon.ts b/packages/lucide-preact/src/createLucideIcon.ts index c5c14d4a3..5620bdad5 100644 --- a/packages/lucide-preact/src/createLucideIcon.ts +++ b/packages/lucide-preact/src/createLucideIcon.ts @@ -1,34 +1,56 @@ import { h, type JSX } from 'preact'; -import { mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared'; +import { mergeClasses, toLucideIconData, toPascalCase } from '@lucide/shared'; import Icon from './Icon'; -import type { IconNode, LucideIcon, LucideProps } from './types'; +import type { LucideIcon, LucideIconData, LucideIconNode, LucideProps } from './types'; /** - * Create a Lucide icon component - * @param {string} iconName - * @param {array} iconNode - * @returns {FunctionComponent} LucideIcon + * Creates a Lucide icon component from icon data. + * + * @param iconData Icon data object. + * @returns Lucide icon component. */ -const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => { +function createLucideIcon(iconData: LucideIconData): LucideIcon; + +/** + * Creates a Lucide icon component from the legacy icon arguments. + * + * @param iconName Icon name. + * @param iconNode Icon node. + * @param aliases Optional icon aliases. + * @returns Lucide icon component. + */ +function createLucideIcon( + iconName: string, + iconNode: LucideIconNode, + aliases?: string[], +): LucideIcon; + +function createLucideIcon( + iconDataOrName: LucideIconData | string, + iconNode?: LucideIconNode, + aliases: string[] = [], +): LucideIcon { + const iconData = + typeof iconDataOrName === 'string' + ? toLucideIconData(iconDataOrName, iconNode as LucideIconNode, aliases) + : iconDataOrName; + const Component = ({ class: classes = '', className = '', children, ...props }: LucideProps) => h( Icon, { ...props, - iconNode, - class: mergeClasses>( - `lucide-${toKebabCase(toPascalCase(iconName))}`, - `lucide-${toKebabCase(iconName)}`, - classes, - className, - ), + name: iconData.name, + aliases: iconData.aliases, + iconNode: iconData.node, + class: mergeClasses>(classes, className), }, children, ); - Component.displayName = toPascalCase(iconName); + Component.displayName = toPascalCase(iconData.name); return Component; -}; +} export default createLucideIcon; diff --git a/packages/lucide-preact/src/types.ts b/packages/lucide-preact/src/types.ts index 03604e960..f9736e37e 100644 --- a/packages/lucide-preact/src/types.ts +++ b/packages/lucide-preact/src/types.ts @@ -1,6 +1,20 @@ import { type FunctionComponent, type JSX } from 'preact'; +import type { + LucideIconData as SharedLucideIconData, + LucideIconNode as SharedLucideIconNode, +} from '@lucide/shared'; -export type IconNode = [elementName: keyof JSX.IntrinsicElements, attrs: Record][]; +export type LucideIconNode = SharedLucideIconNode[]; + +export type LucideIconData = SharedLucideIconData & { + name: string; + node: LucideIconNode; +}; + +/** + * @deprecated Use LucideIconNode instead. + */ +export type IconNode = LucideIconNode; export interface LucideProps extends Partial> { color?: string; diff --git a/packages/lucide-preact/tests/__snapshots__/context.spec.tsx.snap b/packages/lucide-preact/tests/__snapshots__/context.spec.tsx.snap index b572910a9..85a5974f2 100644 --- a/packages/lucide-preact/tests/__snapshots__/context.spec.tsx.snap +++ b/packages/lucide-preact/tests/__snapshots__/context.spec.tsx.snap @@ -3,7 +3,7 @@ exports[`Using LucideProvider > should render the icon with LucideProvider 1`] = `
{ - const svgContents = await getSvg(); - const svgBase64 = base64SVG(svgContents); +export default defineExportTemplate( + async ({ componentName, iconName, iconData, getSvg, deprecated, deprecationReason }) => { + const svgContents = await getSvg(); + const svgBase64 = base64SVG(svgContents); - return ` + return ` import createLucideIcon from '../createLucideIcon'; -import { IconNode } from '../types'; +import type { LucideIconData } from '../types'; -export const __iconNode: IconNode = ${JSON.stringify(children)} +export const __iconData: LucideIconData = ${JSON.stringify(iconData)} /** * @component @name ${componentName} @@ -29,8 +23,9 @@ export const __iconNode: IconNode = ${JSON.stringify(children)} * @returns {FunctionalComponent} Vue component * ${deprecated ? `@deprecated ${deprecationReason}` : ''} */ -const ${componentName} = createLucideIcon('${iconName}', __iconNode); +const ${componentName} = createLucideIcon(__iconData); export default ${componentName}; `; -}); + }, +); diff --git a/packages/vue/src/Icon.ts b/packages/vue/src/Icon.ts index 75d85aee4..5bdecdb5f 100644 --- a/packages/vue/src/Icon.ts +++ b/packages/vue/src/Icon.ts @@ -1,5 +1,5 @@ import { computed, type FunctionalComponent, h } from 'vue'; -import { isEmptyString, mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared'; +import { buildLucideIconNode, isEmptyString, toKebabCase } from '@lucide/shared'; import defaultAttributes from './defaultAttributes'; import { IconNode, LucideProps } from './types'; import { useLucideProps } from './context'; @@ -7,11 +7,13 @@ import { useLucideProps } from './context'; interface IconProps { iconNode: IconNode; name: string; + aliases?: string[]; } const Icon: FunctionalComponent = ( { name, + aliases, iconNode, absoluteStrokeWidth, 'absolute-stroke-width': absoluteStrokeWidthKebabCase, @@ -54,25 +56,39 @@ const Icon: FunctionalComponent = ( return strokeWidthValue; }); - return h( - 'svg', + const normalizedName = computed(() => (name != null ? toKebabCase(name) : undefined)); + + const iconName = computed(() => (normalizedName.value ? `${normalizedName.value}-icon` : 'icon')); + + const [, svgAttributes, builtIconNode = []] = buildLucideIconNode( { - ...defaultAttributes, - ...props, - width: size ?? contextSize ?? defaultAttributes.width, - height: size ?? contextSize ?? defaultAttributes.height, - stroke: color ?? contextColor ?? defaultAttributes.stroke, - 'stroke-width': calculatedStrokeWidth.value, - class: mergeClasses( - 'lucide', - contextClass, - ...(name - ? [`lucide-${toKebabCase(toPascalCase(name))}-icon`, `lucide-${toKebabCase(name)}`] - : ['lucide-icon']), - ), + name: iconName.value, + aliases, + size: 24, + node: iconNode, + }, + { + size: size ?? contextSize ?? defaultAttributes.width, + color: color ?? contextColor ?? defaultAttributes.stroke, + strokeWidth: calculatedStrokeWidth.value, + className: contextClass, }, - [...iconNode.map((child) => h(...child)), ...(slots.default ? [slots.default()] : [])], ); + + const iconAttributes = { + ...svgAttributes, + ...props, + width: svgAttributes.width, + height: svgAttributes.height, + stroke: svgAttributes.stroke, + 'stroke-width': svgAttributes['stroke-width'], + class: svgAttributes.class, + }; + + return h('svg', iconAttributes, [ + ...builtIconNode.map((child) => h(...child)), + ...(slots.default ? [slots.default()] : []), + ]); }; export default Icon; diff --git a/packages/vue/src/createLucideIcon.ts b/packages/vue/src/createLucideIcon.ts index 85919b106..10d5247df 100644 --- a/packages/vue/src/createLucideIcon.ts +++ b/packages/vue/src/createLucideIcon.ts @@ -1,23 +1,23 @@ import { h } from 'vue'; -import { IconNode, LucideIcon } from './types'; +import { LucideIcon, LucideIconData } from './types'; import Icon from './Icon'; /** * Create a Lucide icon component - * @param {string} iconName - * @param {array} iconNode + * @param {object} iconData * @returns {FunctionalComponent} LucideIcon */ const createLucideIcon = - (iconName: string, iconNode: IconNode): LucideIcon => + (iconData: LucideIconData): LucideIcon => (props, { slots, attrs }) => h( Icon, { ...attrs, ...props, - iconNode, - name: iconName, + iconNode: iconData.node, + name: iconData.name, + aliases: iconData.aliases, }, slots.default ? { default: slots.default } : undefined, ); diff --git a/packages/vue/src/types.ts b/packages/vue/src/types.ts index a8b865db6..38d5f2f6d 100644 --- a/packages/vue/src/types.ts +++ b/packages/vue/src/types.ts @@ -8,4 +8,9 @@ export interface LucideProps extends Partial { } export type IconNode = [elementName: string, attrs: Record][]; +export interface LucideIconData { + name: string; + node: IconNode; + aliases?: string[]; +} export type LucideIcon = FunctionalComponent; diff --git a/packages/vue/tests/__snapshots__/Icon.spec.ts.snap b/packages/vue/tests/__snapshots__/Icon.spec.ts.snap index 4b742c143..9907eb584 100644 --- a/packages/vue/tests/__snapshots__/Icon.spec.ts.snap +++ b/packages/vue/tests/__snapshots__/Icon.spec.ts.snap @@ -2,7 +2,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = ` should render the icon with LucideProvider 1`] = ` should add a class to the element 1`] = `
should add a class to the element 1`] = exports[`Using lucide icon components > should add a style attribute to the element 1`] = `
should add a style attribute to the elem exports[`Using lucide icon components > should adjust the size, stroke color and stroke width 1`] = `
should adjust the size, stroke color and exports[`Using lucide icon components > should pass children to the icon slot 1`] = `
should pass children to the icon slot 1` exports[`Using lucide icon components > should render an component 1`] = `
{ expect(IconComponent).toHaveAttribute( 'class', - 'lucide provider-class lucide-house-icon lucide-house icon-class', + 'lucide lucide-house-icon lucide-home provider-class icon-class', ); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ae7209cf..39ee05ff8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -357,6 +357,9 @@ importers: '@lucide/rollup-plugins': specifier: workspace:* version: link:../../tools/rollup-plugins + '@lucide/shared': + specifier: workspace:* + version: link:../shared '@rollup/plugin-replace': specifier: ^6.0.2 version: 6.0.3(rollup@4.59.0) From 8d0fa2e218c3ee65e1bdb297a5b13775a2f49bb7 Mon Sep 17 00:00:00 2001 From: Karsa Date: Fri, 22 May 2026 09:54:45 +0200 Subject: [PATCH 5/8] feat(): unify icon build logic some more, add nonScalingStroke option, marking absoluteStrokeWidth as deprecated --- .gitignore | 1 + package.json | 1 + packages/angular/package.json | 2 +- packages/angular/src/lucide-config.ts | 8 +- packages/angular/src/lucide-icon-base.spec.ts | 47 ++++- packages/angular/src/lucide-icon-base.ts | 140 ++++++++++--- packages/angular/src/types.ts | 18 +- packages/astro/package.json | 2 +- packages/astro/src/Icon.astro | 46 ++-- packages/astro/src/createLucideIcon.ts | 46 +++- packages/astro/src/defaultAttributes.ts | 15 -- packages/astro/src/lucide-astro.ts | 2 +- packages/astro/src/types.ts | 42 ++-- .../tests/__snapshots__/Icon.spec.ts.snap | 2 +- .../createLucideIcon.spec.ts.snap | 2 +- .../__snapshots__/lucide-astro.spec.ts.snap | 74 +++++-- packages/astro/tests/lucide-astro.spec.ts | 43 +++- packages/icons/rollup.config.mjs | 7 +- packages/icons/src/buildLucideDataUri.ts | 15 +- packages/icons/src/buildLucideIconElement.ts | 20 +- packages/icons/src/buildLucideIconNode.ts | 15 +- packages/icons/src/buildLucideSvg.ts | 15 +- packages/icons/src/types.ts | 11 +- .../buildLucideIconNode.spec.ts.snap | 2 +- .../icons/tests/buildLucideIconNode.spec.ts | 38 +++- packages/icons/tsconfig.json | 5 + packages/lucide-preact/rollup.config.mjs | 11 +- packages/lucide-preact/src/Icon.ts | 71 ++++--- packages/lucide-preact/src/context.ts | 13 +- .../lucide-preact/src/createLucideIcon.ts | 20 +- .../lucide-preact/src/defaultAttributes.ts | 14 +- packages/lucide-preact/src/types.ts | 15 +- .../__snapshots__/lucide-preact.spec.tsx.snap | 54 ++++- packages/lucide-preact/tests/context.spec.tsx | 16 ++ .../tests/lucide-preact.spec.tsx | 42 +++- packages/lucide-preact/tsconfig.json | 5 + .../lucide-react-native/rollup.config.mjs | 13 +- packages/lucide-react-native/src/Icon.ts | 59 +++--- packages/lucide-react-native/src/context.ts | 13 +- .../src/createLucideIcon.ts | 6 +- .../src/defaultAttributes.ts | 25 +-- packages/lucide-react-native/src/types.ts | 17 +- .../lucide-react-native/tests/Icon.spec.tsx | 28 +++ .../tests/__snapshots__/Icon.spec.tsx.snap | 2 + .../tests/__snapshots__/context.spec.tsx.snap | 2 + .../lucide-react-native.spec.tsx.snap | 197 ++++++++++++------ .../tests/lucide-react-native.spec.tsx | 22 +- packages/lucide-react-native/tsconfig.json | 5 + packages/lucide-react/package.json | 1 + packages/lucide-react/rollup.config.mjs | 15 +- .../lucide-react/scripts/exportTemplate.mts | 7 +- packages/lucide-react/src/DynamicIcon.ts | 26 +-- packages/lucide-react/src/Icon.ts | 98 ++++----- packages/lucide-react/src/context.ts | 8 +- packages/lucide-react/src/createLucideIcon.ts | 18 +- .../lucide-react/src/defaultAttributes.ts | 12 +- packages/lucide-react/src/types.ts | 18 +- .../tests/__snapshots__/context.spec.tsx.snap | 48 +++++ .../__snapshots__/lucide-react.spec.tsx.snap | 54 ++++- packages/lucide-react/tests/context.spec.tsx | 18 ++ .../lucide-react/tests/lucide-react.spec.tsx | 42 +++- packages/lucide-react/tsconfig.json | 7 +- .../lucide-solid/scripts/exportTemplate.mts | 2 +- packages/lucide-solid/src/Icon.tsx | 81 +++---- packages/lucide-solid/src/context.tsx | 10 + .../lucide-solid/src/defaultAttributes.ts | 17 +- packages/lucide-solid/src/types.ts | 25 ++- packages/lucide-solid/tests/Icon.spec.tsx | 28 +++ .../__snapshots__/lucide-solid.spec.tsx.snap | 94 ++++----- .../lucide-solid/tests/lucide-solid.spec.tsx | 2 +- packages/lucide-solid/tsconfig.json | 5 + packages/lucide/src/createElement.ts | 47 +++-- packages/lucide/src/replaceElement.ts | 11 +- packages/shared/package.json | 7 + .../src/build/buildLucideIconForReact.ts | 27 +++ .../shared/src/build/buildLucideIconNode.ts | 82 +++----- .../src/build}/defaultAttributes.ts | 6 +- .../src/build/defaultReactAttributes.ts | 13 ++ packages/shared/src/build/types.ts | 43 +++- packages/shared/src/index.ts | 3 + packages/svelte/package.json | 2 +- packages/svelte/scripts/exportTemplate.mts | 8 +- packages/svelte/src/Icon.svelte | 56 ++--- packages/svelte/src/context.ts | 4 + packages/svelte/src/lucide-svelte.ts | 2 +- packages/svelte/src/types.ts | 40 ++-- .../__snapshots__/lucide-svelte.spec.ts.snap | 70 +++---- packages/svelte/tests/lucide-svelte.spec.ts | 2 +- packages/svelte/tsconfig.json | 5 + packages/vue/rollup.config.mjs | 29 +-- packages/vue/src/Icon.ts | 119 +++++------ packages/vue/src/context.ts | 4 + packages/vue/src/createLucideIcon.ts | 41 +++- packages/vue/src/defaultAttributes.ts | 12 +- packages/vue/src/types.ts | 27 ++- packages/vue/tests/Icon.spec.ts | 54 +++-- .../vue/tests/__snapshots__/Icon.spec.ts.snap | 58 +++++- .../tests/__snapshots__/context.spec.ts.snap | 2 +- .../__snapshots__/lucide-vue.spec.ts.snap | 10 +- packages/vue/tests/context.spec.ts | 2 +- packages/vue/tests/lucide-vue.spec.ts | 60 +++++- packages/vue/tsconfig.json | 5 + 102 files changed, 1809 insertions(+), 947 deletions(-) delete mode 100644 packages/astro/src/defaultAttributes.ts create mode 100644 packages/shared/src/build/buildLucideIconForReact.ts rename packages/{svelte/src => shared/src/build}/defaultAttributes.ts (75%) create mode 100644 packages/shared/src/build/defaultReactAttributes.ts diff --git a/.gitignore b/.gitignore index 7aab235ad..fc8681b54 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ packages/**/src/utils/*.ts !packages/shared/src/utils/*.ts !packages/shared/src/build/ !packages/shared/src/build/*.ts +packages/shared/src/build/types.d.ts packages/**/src/dynamicIconImports.ts packages/**/DynamicIcon.d.ts packages/**/dynamicIconImports.js diff --git a/package.json b/package.json index 7bb11484b..adf630030 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "@vitest/coverage-v8": "4.0.12", "@vitest/ui": "4.0.12", "ajv-cli": "^5.0.0", + "cpy-cli": "^7.0.0", "dotenv": "^17.4.2", "eslint": "^8.57.1", "eslint-config-airbnb-base": "^15.0.0", diff --git a/packages/angular/package.json b/packages/angular/package.json index 7ba81a27e..91438770a 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -20,7 +20,7 @@ "prebuild": "pnpm clean && pnpm copy:license && pnpm copy:utils && pnpm build:icons", "build": "pnpm prebuild && pnpm build:ng", "copy:license": "cp ../../LICENSE ./LICENSE", - "copy:utils": "mkdir -p ./src/utils && cp -f ../../packages/shared/src/build/buildLucideIconNode.ts ./src/utils/ && cp -f ../../packages/shared/src/build/types.ts ./src/utils/", + "copy:utils": "cpy ../../packages/shared/src/build/{buildLucideIconNode.ts,defaultAttributes.ts,types.ts} ../../packages/shared/src/utils/mergeClasses.ts ./src/utils/", "clean": "rm -rf dist && rm -rf ./src/icons/*.ts", "build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --iconFileExtension=.ts --exportFileName=lucide-angular.ts --useDefaultExports=0", "build:ng": "ng build --configuration production", diff --git a/packages/angular/src/lucide-config.ts b/packages/angular/src/lucide-config.ts index 8a4953127..3944638f1 100644 --- a/packages/angular/src/lucide-config.ts +++ b/packages/angular/src/lucide-config.ts @@ -19,6 +19,11 @@ export interface LucideConfig { * @default 2 */ strokeWidth: number; + /** + * Whether to use absolute stroke width. + * @deprecated Use `nonScalingStroke` instead. + */ + absoluteStrokeWidth: boolean; /** * Whether stroke width should be scaled to appear uniform regardless of icon size. * @default false @@ -31,7 +36,7 @@ export interface LucideConfig { * } * ``` */ - absoluteStrokeWidth: boolean; + nonScalingStroke: boolean; } /** @@ -42,6 +47,7 @@ export const lucideDefaultConfig: LucideConfig = { size: 24, strokeWidth: 2, absoluteStrokeWidth: false, + nonScalingStroke: false, }; /** diff --git a/packages/angular/src/lucide-icon-base.spec.ts b/packages/angular/src/lucide-icon-base.spec.ts index ff0154357..736847c44 100644 --- a/packages/angular/src/lucide-icon-base.spec.ts +++ b/packages/angular/src/lucide-icon-base.spec.ts @@ -41,6 +41,7 @@ describe('LucideIconBase', () => { let size: WritableSignal; let strokeWidth: WritableSignal; let absoluteStrokeWidth: WritableSignal; + let nonScalingStroke: WritableSignal; const getSvgAttribute = (attr: string) => fixture.nativeElement.getAttribute(attr); function createComponent() { return TestBed.createComponent(LucideCircleCheck, { @@ -51,6 +52,7 @@ describe('LucideIconBase', () => { inputBinding('size', size), inputBinding('strokeWidth', strokeWidth), inputBinding('absoluteStrokeWidth', absoluteStrokeWidth), + inputBinding('nonScalingStroke', nonScalingStroke), ], }); } @@ -60,6 +62,7 @@ describe('LucideIconBase', () => { size = signal(undefined); strokeWidth = signal(undefined); absoluteStrokeWidth = signal(undefined); + nonScalingStroke = signal(undefined); fixture = createComponent(); component = fixture.componentInstance; }); @@ -113,6 +116,11 @@ describe('LucideIconBase', () => { expect(getSvgAttribute('width')).toBe('18'); expect(getSvgAttribute('height')).toBe('18'); }); + it('should set viewBox', () => { + size.set('18'); + fixture.detectChanges(); + expect(getSvgAttribute('viewBox')).toBe('0 0 24 24'); + }); }); describe('strokeWidth', () => { @@ -133,21 +141,37 @@ describe('LucideIconBase', () => { }); describe('absoluteStrokeWidth', () => { + it('should adjust stroke width', () => { + strokeWidth.set(2); + size.set(32); + absoluteStrokeWidth.set(true); + fixture.detectChanges(); + expect(getSvgAttribute('stroke-width')).toBe('1.5'); + }); + it('should not set vector-effect on children', () => { + absoluteStrokeWidth.set(true); + for (const child of fixture.nativeElement.children) { + expect(child.getAttribute('vector-effect')).toBeNull(); + } + }); + }); + + describe('nonScalingStroke', () => { it('should not adjust stroke width', () => { strokeWidth.set(2); size.set(12); - absoluteStrokeWidth.set(true); + nonScalingStroke.set(false); fixture.detectChanges(); expect(getSvgAttribute('stroke-width')).toBe('2'); }); it('should not set vector-effect on children', () => { - absoluteStrokeWidth.set(false); + nonScalingStroke.set(false); for (const child of fixture.nativeElement.children) { expect(child.getAttribute('vector-effect')).toBeNull(); } }); it('should set vector-effect on children', () => { - absoluteStrokeWidth.set(true); + nonScalingStroke.set(true); for (const child of fixture.nativeElement.children) { expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke'); } @@ -205,12 +229,12 @@ describe('LucideIconBase', () => { describe('strokeWidth', () => { it('should use stroke width from config', () => { fixture.detectChanges(); - expect(getSvgAttribute('stroke-width')).toBe('1'); + expect(getSvgAttribute('stroke-width')).toBe('2'); }); it('should use override stroke width from config', () => { strokeWidth.set(3); fixture.detectChanges(); - expect(getSvgAttribute('stroke-width')).toBe('3'); + expect(getSvgAttribute('stroke-width')).toBe('6'); }); }); describe('size', () => { @@ -239,5 +263,18 @@ describe('LucideIconBase', () => { } }); }); + describe('nonScalingStroke', () => { + it('should use nonScalingStroke from config', () => { + for (const child of fixture.nativeElement.children) { + expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke'); + } + }); + it('should override nonScalingStroke', () => { + nonScalingStroke.set(false); + for (const child of fixture.nativeElement.children) { + expect(child.getAttribute('vector-effect')).toBeNull(); + } + }); + }); }); }); diff --git a/packages/angular/src/lucide-icon-base.ts b/packages/angular/src/lucide-icon-base.ts index 59fdb9667..156091cb0 100644 --- a/packages/angular/src/lucide-icon-base.ts +++ b/packages/angular/src/lucide-icon-base.ts @@ -1,5 +1,6 @@ import { Component, + computed, effect, ElementRef, inject, @@ -9,7 +10,7 @@ import { viewChild, } from '@angular/core'; import { LUCIDE_CONFIG } from './lucide-config'; -import { LucideIconData, Nullable } from './types'; +import { LucideIconData, LucideIconNode, Nullable } from './types'; import defaultAttributes from './default-attributes'; import { lucideIconTemplate } from './lucide-icon-template'; import buildLucideIconNode from './utils/buildLucideIconNode'; @@ -23,12 +24,13 @@ import buildLucideIconNode from './utils/buildLucideIconNode'; template: lucideIconTemplate, host: { ...defaultAttributes, - class: 'lucide', - '[attr.width]': 'size()', - '[attr.height]': 'size()', - '[attr.stroke]': 'color()', - '[attr.stroke-width]': 'strokeWidth()', - '[attr.aria-hidden]': '!title()', + '[attr.class]': 'computedClass()', + '[attr.width]': 'computedWidth()', + '[attr.height]': 'computedHeight()', + '[attr.viewBox]': 'computedViewbox()', + '[attr.stroke]': 'computedStroke()', + '[attr.stroke-width]': 'computedStrokeWidth()', + '[attr.aria-hidden]': 'computedAriaHidden()', }, }) export abstract class LucideIconBase { @@ -57,6 +59,16 @@ export abstract class LucideIconBase { readonly size = input(this.iconConfig.size, { transform: (value: Nullable) => value ?? this.iconConfig.size, }); + /** + * Icon width. + * @default 24 + */ + readonly width = input(null); + /** + * Icon height. + * @default 24 + */ + readonly height = input(null); /** * Stroke color. * @default currentColor @@ -72,36 +84,105 @@ export abstract class LucideIconBase { transform: (value: Nullable) => value ?? this.iconConfig.strokeWidth, }); /** - * If set to true, it adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements. + * Whether to use absolute stroke width. */ readonly absoluteStrokeWidth = input(this.iconConfig.absoluteStrokeWidth, { transform: (value: Nullable) => value ?? this.iconConfig.absoluteStrokeWidth, }); + /** + * If set to true, it adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements. + */ + readonly nonScalingStroke = input(this.iconConfig.nonScalingStroke, { + transform: (value: Nullable) => value ?? this.iconConfig.nonScalingStroke, + }); + + /** + * @internal + */ + protected readonly computedIcon = computed(() => { + const icon = this.icon(); + return icon + ? buildLucideIconNode(icon, { + width: this.width() ?? this.size(), + height: this.height() ?? this.size(), + color: this.color(), + strokeWidth: this.strokeWidth(), + absoluteStrokeWidth: this.absoluteStrokeWidth(), + nonScalingStroke: this.nonScalingStroke(), + hasA11yProp: this.title() != null, + }) + : null; + }); + + /** + * @internal + */ + protected readonly computedAttributes = computed( + () => this.computedIcon()?.[1], + ); + + /** + * @internal + */ + protected readonly computedIconNode = computed(() => this.computedIcon()?.[2]); + + /** + * @internal + */ + protected readonly computedWidth = computed( + () => this.computedAttributes()?.['width'] ?? this.iconConfig.size, + ); + + /** + * @internal + */ + protected readonly computedHeight = computed( + () => this.computedAttributes()?.['height'] ?? this.iconConfig.size, + ); + + /** + * @internal + */ + protected readonly computedStroke = computed( + () => this.computedAttributes()?.['stroke'] ?? this.iconConfig.color, + ); + + /** + * @internal + */ + protected readonly computedStrokeWidth = computed( + () => this.computedAttributes()?.['stroke-width'] ?? this.iconConfig.strokeWidth, + ); + + /** + * @internal + */ + protected readonly computedAriaHidden = computed( + () => this.computedAttributes()?.['aria-hidden'] === 'true', + ); + + /** + * @internal + */ + protected readonly computedViewbox = computed( + () => this.computedAttributes()?.['viewBox'] ?? '0 0 24 24', + ); + + /** + * @internal + */ + protected readonly computedClass = computed( + () => this.computedAttributes()?.['class'] ?? 'lucide', + ); constructor() { effect((onCleanup) => { - const icon = this.icon(); - if (icon) { - const absoluteStrokeWidth = this.absoluteStrokeWidth(); - const [, iconAttributes, iconNode] = buildLucideIconNode(icon as any, { - absoluteStrokeWidth, - hasA11yProp: this.title() != null, - }); - - const classes = - typeof iconAttributes['class'] === 'string' - ? iconAttributes['class'] - .split(' ') - .filter((cssClass) => cssClass && cssClass !== 'lucide') - : []; - - for (const cssClass of classes) { - this.renderer.addClass(this.elRef.nativeElement, cssClass); - } - + const iconNode = this.computedIconNode(); + const iconAttributes = this.computedIconNode(); + if (iconNode && iconAttributes) { const contentRef = this.contentRef(); const refChild = contentRef.nativeElement; - const elements = (iconNode ?? []).map(([name, attrs]) => { + const elements = iconNode.map(([name, attrs]) => { const element = this.renderer.createElement(name, 'http://www.w3.org/2000/svg'); Object.entries(attrs).forEach(([name, value]) => @@ -118,9 +199,6 @@ export abstract class LucideIconBase { elements.forEach((element) => this.renderer.removeChild(this.elRef.nativeElement, element), ); - for (const cssClass of classes) { - this.renderer.removeClass(this.elRef.nativeElement, cssClass); - } }); } }); diff --git a/packages/angular/src/types.ts b/packages/angular/src/types.ts index bc75de19e..c234ab405 100644 --- a/packages/angular/src/types.ts +++ b/packages/angular/src/types.ts @@ -1,18 +1,16 @@ import { Signal, Type } from '@angular/core'; +import { + LucideIconNode as SharedLucideIconNode, + LucideIconData as SharedLucideIconData, +} from './utils/types'; -type HtmlAttributes = { [key: string]: string | number }; -export type LucideIconNode = readonly [string, HtmlAttributes]; +export type LucideIconNode = SharedLucideIconNode; export type LucideIcons = { [key: string]: LucideIconData }; /** * A Lucide icon object that fully describes an icon to be displayed. */ -export type LucideIconData = { - name: string; - size?: number; - node: LucideIconNode[]; - aliases?: string[]; -}; +export type LucideIconData = SharedLucideIconData & { name: string }; /** * Input signal map of Lucide icon components. @@ -22,7 +20,11 @@ interface LucideIconProps { size: Signal>; color: Signal>; strokeWidth: Signal>; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth: Signal>; + nonScalingStroke: Signal>; } /** diff --git a/packages/astro/package.json b/packages/astro/package.json index 6f44dede1..76f1aa944 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -43,7 +43,7 @@ "build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --withAliases --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts --pretty=false", "clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.ts", "copy:license": "cp ../../LICENSE ./LICENSE", - "copy:utils": "mkdir -p ./src/utils && for f in hasA11yProp toKebabCase mergeClasses; do cp -f ../../packages/shared/src/utils/$f.ts ./src/utils/; done", + "copy:utils": "cpy ../../packages/shared/src/build/{buildLucideIconNode.ts,defaultAttributes.ts,types.ts} ../../packages/shared/src/utils/{hasA11yProp.ts,toKebabCase.ts,mergeClasses.ts} ./src/utils/", "test": "pnpm copy:utils && pnpm build:icons && vitest run", "test:watch": "pnpm build:icons && vitest run --watch", "version": "pnpm version --git-tag-version=false" diff --git a/packages/astro/src/Icon.astro b/packages/astro/src/Icon.astro index df27db114..b20d3b835 100644 --- a/packages/astro/src/Icon.astro +++ b/packages/astro/src/Icon.astro @@ -6,45 +6,39 @@ import { hasA11yProp } from './utils/hasA11yProp'; const { color = 'currentColor', size = 24, + width, + height, 'stroke-width': strokeWidth = 2, absoluteStrokeWidth = false, - name, - aliases, - iconNode = [], + nonScalingStroke = false, + iconNode, + icon = { + node: iconNode, + aliases: [], + size: 24, + }, class: className, ...rest } = Astro.props; -const calculatedStrokeWidth = - absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth; +const [, builtIconAttributes, builtIconNode] = buildLucideIconNode(icon, { + color, + width: width ?? size, + height: height ?? size, + strokeWidth, + absoluteStrokeWidth, + nonScalingStroke, + className, + hasA11yProp: hasA11yProp(rest), +}); -const [, rawIconAttributes, builtIconNode = []] = buildLucideIconNode( - { - name, - aliases, - size: 24, - node: iconNode, - }, - { - color, - size, - strokeWidth: calculatedStrokeWidth, - className, - hasA11yProp: hasA11yProp(rest), - }, -); - -const { class: iconClassName, ...builtIconAttributes } = rawIconAttributes; const iconAttributes = { ...builtIconAttributes, ...rest, }; --- - + {builtIconNode.map(([Tag, attrs]) => )} diff --git a/packages/astro/src/createLucideIcon.ts b/packages/astro/src/createLucideIcon.ts index 22d12e942..e693daf7e 100644 --- a/packages/astro/src/createLucideIcon.ts +++ b/packages/astro/src/createLucideIcon.ts @@ -1,9 +1,43 @@ import type { AstroComponentFactory } from 'astro/runtime/server/render/astro/factory.js'; -import type { LucideIconData } from './types'; +import type { LucideIconData, LucideIconNode } from './types'; import { render, renderSlot, createComponent, renderComponent } from 'astro/compiler-runtime'; import Icon from './Icon.astro'; -export default (iconData: LucideIconData): AstroComponentFactory => { +/** + * Creates a Lucide icon component from icon data. + * + * @param icon Icon data object. + * @returns Lucide icon component. + */ +function createLucideIcon(icon: LucideIconData): AstroComponentFactory; + +/** + * Creates a Lucide icon component from the legacy icon arguments. + * + * @param iconName Icon name. + * @param iconNode Icon node. + * @param aliases Optional icon aliases. + * @returns Lucide icon component. + */ +function createLucideIcon( + iconName: string, + iconNode: LucideIconNode[], + aliases?: string[], +): AstroComponentFactory; + +function createLucideIcon( + iconOrIconName: string | LucideIconData, + iconNode: LucideIconNode[] = [], + aliases: string[] = [], +): AstroComponentFactory { + const icon = + typeof iconOrIconName === 'string' + ? { + name: iconOrIconName, + node: iconNode, + aliases, + } + : iconOrIconName; const Component = createComponent( ($$result, $$props: Record, $$slots) => { const { class: className, ...restProps } = $$props; @@ -13,9 +47,7 @@ export default (iconData: LucideIconData): AstroComponentFactory => { Icon, { class: className, - name: iconData.name, - aliases: iconData.aliases, - iconNode: iconData.node, + icon, ...restProps, }, { default: () => render`${renderSlot($$result, $$slots['default'])}` }, @@ -25,4 +57,6 @@ export default (iconData: LucideIconData): AstroComponentFactory => { 'none', ); return Component; -}; +} + +export default createLucideIcon; diff --git a/packages/astro/src/defaultAttributes.ts b/packages/astro/src/defaultAttributes.ts deleted file mode 100644 index 5b1fddd06..000000000 --- a/packages/astro/src/defaultAttributes.ts +++ /dev/null @@ -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; diff --git a/packages/astro/src/lucide-astro.ts b/packages/astro/src/lucide-astro.ts index adc0c1003..19d041177 100644 --- a/packages/astro/src/lucide-astro.ts +++ b/packages/astro/src/lucide-astro.ts @@ -3,6 +3,6 @@ export * as icons from './icons/index'; export * from './aliases'; export * from './types'; -export { default as defaultAttributes } from './defaultAttributes'; +export { default as defaultAttributes } from './utils/defaultAttributes'; export { default as createLucideIcon } from './createLucideIcon'; export { default as Icon } from './Icon.astro'; diff --git a/packages/astro/src/types.ts b/packages/astro/src/types.ts index d7ce5f411..f8ad56c01 100644 --- a/packages/astro/src/types.ts +++ b/packages/astro/src/types.ts @@ -1,31 +1,47 @@ import type { HTMLAttributes } from 'astro/types'; +import type { + LucideIconData as SharedLucideIconData, + LucideIconNode as SharedLucideIconNode, +} from './utils/types'; // Type that the Astro language server needs to infer component props in Astro files -export type AstroComponent = (_props: IconProps) => any; +export type AstroComponent = (_props: LucideProps) => any; -export interface IconProps extends SVGAttributes { +export type LucideProps = SVGAttributes & { color?: string; size?: number | string; 'stroke-width'?: number | string; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; class?: string; - name?: string; - aliases?: string[]; - iconNode?: IconNode; title?: string; -} +}; + +export type IconProps = LucideProps & + ( + | { + icon: LucideIconData; + iconNode?: never; + } + | { + icon?: never; + iconNode: LucideIconNode; + } + ); export type SVGAttributes = HTMLAttributes<'svg'>; -export type IconNode = IconNodeChild[]; +export type LucideIconNode = SharedLucideIconNode; -export interface LucideIconData { - name: string; - node: IconNode; - aliases?: string[]; -} +export type LucideIconData = SharedLucideIconData; -type IconNodeChild = [elementName: SVGElements, attrs: SVGAttributes]; +/** + * @deprecated Use LucideIconNode instead. + */ +export type IconNode = LucideIconNode[]; // All possible svg elements according to the Astro definitions type SVGElements = diff --git a/packages/astro/tests/__snapshots__/Icon.spec.ts.snap b/packages/astro/tests/__snapshots__/Icon.spec.ts.snap index a550ccb86..f3bc1ec4d 100644 --- a/packages/astro/tests/__snapshots__/Icon.spec.ts.snap +++ b/packages/astro/tests/__snapshots__/Icon.spec.ts.snap @@ -10,8 +10,8 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = ` stroke-width="1" stroke-linecap="round" stroke-linejoin="round" - aria-hidden="true" class="lucide" + aria-hidden="true" > diff --git a/packages/astro/tests/__snapshots__/createLucideIcon.spec.ts.snap b/packages/astro/tests/__snapshots__/createLucideIcon.spec.ts.snap index 13a285420..e53acc2bb 100644 --- a/packages/astro/tests/__snapshots__/createLucideIcon.spec.ts.snap +++ b/packages/astro/tests/__snapshots__/createLucideIcon.spec.ts.snap @@ -10,8 +10,8 @@ exports[`Using createLucideIcon > should create a component from an iconNode 1`] stroke-width="2" stroke-linecap="round" stroke-linejoin="round" - aria-hidden="true" class="lucide lucide-air-vent" + aria-hidden="true" > diff --git a/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap b/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap index 6f9ff2921..218463aea 100644 --- a/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap +++ b/packages/astro/tests/__snapshots__/lucide-astro.spec.ts.snap @@ -10,9 +10,9 @@ exports[`Using lucide icon components > should add a non-default attribute to th stroke-width="2" stroke-linecap="round" stroke-linejoin="round" + class="lucide lucide-smile" aria-hidden="true" style="position: absolute" - class="lucide lucide-smile" > should adjust the size, stroke color and stroke-width="4" stroke-linecap="round" stroke-linejoin="round" - aria-hidden="true" class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3" + aria-hidden="true" > should apply all classes to the element stroke-width="2" stroke-linecap="round" stroke-linejoin="round" + class="lucide lucide-droplet lucide-drop my-icon" aria-hidden="true" - class="lucide lucide-droplet my-icon" > `; -exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` +exports[`Using lucide icon components > should apply vector-effect when nonScalingStroke is set 1`] = ` `; @@ -126,8 +135,8 @@ exports[`Using lucide icon components > should pass children to the icon slot 1` stroke-width="2" stroke-linecap="round" stroke-linejoin="round" - aria-hidden="true" class="lucide lucide-smile" + aria-hidden="true" > should render a component 1`] = ` stroke-width="2" stroke-linecap="round" stroke-linejoin="round" - aria-hidden="true" class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3" + aria-hidden="true" > should render the icon with default attr stroke-width="2" stroke-linecap="round" stroke-linejoin="round" - aria-hidden="true" class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3" + aria-hidden="true" +> + + + + + + + + + + + +`; + +exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` + `; -exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` +exports[`Using lucide icon components > should apply vector-effect when nonScalingStroke is set 1`] = ` `; @@ -92,3 +101,34 @@ exports[`Using lucide icon components > should render an component 1`] = ` `; + +exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` + +`; diff --git a/packages/lucide-preact/tests/context.spec.tsx b/packages/lucide-preact/tests/context.spec.tsx index 856b148fd..c2afcc312 100644 --- a/packages/lucide-preact/tests/context.spec.tsx +++ b/packages/lucide-preact/tests/context.spec.tsx @@ -51,6 +51,22 @@ describe('Using LucideProvider', () => { expect(IconComponent).toHaveAttribute('stroke-width', '1'); }); + it('should render the icon with LucideProvider and custom nonScalingStroke', () => { + const { container } = render( + + + , + ); + + const IconComponent = container.firstElementChild; + + expect(IconComponent?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + }); + it("should override the provider's global props when passing props to the icon", () => { const { container } = render( }; @@ -70,7 +70,7 @@ describe('Using lucide icon components', () => { expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML); }); - it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { + it('should scale the strokeWidth when absoluteStrokeWidth is set', () => { const { container } = render( { expect(container.innerHTML).toMatchSnapshot(); }); + it('should apply vector-effect when nonScalingStroke is set', () => { + const { container } = render( + , + ); + + const SVGElement = container.firstElementChild; + + expect(SVGElement).toHaveAttribute('stroke', 'red'); + expect(SVGElement).toHaveAttribute('width', '48'); + expect(SVGElement).toHaveAttribute('height', '48'); + expect(SVGElement).toHaveAttribute('stroke-width', '2'); + expect(SVGElement?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + + expect(container.innerHTML).toMatchSnapshot(); + }); + it('should apply all classes to the element', () => { + const iconData: LucideIconData = { + name: 'droplet', + size: 24, + node: [ + [ + 'path', + { + d: 'M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z', + key: 'abc123', + }, + ], + ], + aliases: ['drop'], + }; const testClass = 'my-class'; - const { container } = render(); + const Icon = createLucideIcon(iconData); + const { container } = render(); expect(container.firstChild).toHaveClass(testClass); expect(container.firstChild).toHaveClass('lucide'); expect(container.firstChild).toHaveClass('lucide-droplet'); + expect(container.firstChild).toHaveClass('lucide-drop'); }); }); diff --git a/packages/lucide-preact/tsconfig.json b/packages/lucide-preact/tsconfig.json index 61ae11e69..ff8a883fe 100644 --- a/packages/lucide-preact/tsconfig.json +++ b/packages/lucide-preact/tsconfig.json @@ -8,6 +8,11 @@ "moduleResolution": "node", "module": "ESNext", "target": "ESNext", + "baseUrl": ".", + "paths": { + "@lucide/shared": ["../shared/src/index.ts"], + "@lucide/shared/types": ["../shared/src/build/types.ts"], + }, "esModuleInterop": true, "isolatedModules": true, "lib": ["esnext", "dom"], diff --git a/packages/lucide-react-native/rollup.config.mjs b/packages/lucide-react-native/rollup.config.mjs index cc692ecab..8eba95708 100644 --- a/packages/lucide-react-native/rollup.config.mjs +++ b/packages/lucide-react-native/rollup.config.mjs @@ -2,6 +2,11 @@ import plugins from '@lucide/rollup-plugins'; import dts from 'rollup-plugin-dts'; import pkg from './package.json' with { type: 'json' }; +const dtsOptions = { + includeExternal: ['@lucide/shared/types'], + tsconfig: './tsconfig.json', +}; + const packageName = 'LucideReact'; const outputFileName = 'lucide-react-native'; const outputDir = 'dist'; @@ -61,7 +66,7 @@ export default [ format: 'es', }, ], - plugins: [dts()], + plugins: [dts(dtsOptions)], }, { input: inputs[1], @@ -71,7 +76,7 @@ export default [ format: 'es', }, ], - plugins: [dts()], + plugins: [dts(dtsOptions)], }, { input: `src/${outputFileName}.suffixed.ts`, @@ -81,7 +86,7 @@ export default [ format: 'es', }, ], - plugins: [dts()], + plugins: [dts(dtsOptions)], }, { input: `src/${outputFileName}.prefixed.ts`, @@ -91,7 +96,7 @@ export default [ format: 'es', }, ], - plugins: [dts()], + plugins: [dts(dtsOptions)], }, ...configs, ]; diff --git a/packages/lucide-react-native/src/Icon.ts b/packages/lucide-react-native/src/Icon.ts index d00627b44..0d49ccba9 100644 --- a/packages/lucide-react-native/src/Icon.ts +++ b/packages/lucide-react-native/src/Icon.ts @@ -1,15 +1,14 @@ import { createElement, forwardRef, type FunctionComponent } from 'react'; import * as NativeSvg from 'react-native-svg'; -import { buildLucideIconNode } from '@lucide/shared'; -import defaultAttributes, { childDefaultAttributes } from './defaultAttributes'; -import { LucideIconNode, LucideProps } from './types'; +import { buildLucideIconForReact, hasA11yProp, mergeClasses } from '@lucide/shared'; +import { childDefaultAttributes } from './defaultAttributes'; +import { LucideIconData, LucideIconNode, LucideProps } from './types'; import { useLucideContext } from './context'; -interface IconComponentProps extends LucideProps { - iconNode: LucideIconNode; +type IconComponentProps = LucideProps & { testID?: string; className?: string; -} +} & ({ icon: LucideIconData; iconNode?: never } | { icon?: never; iconNode: LucideIconNode[] }); const toNativeSvgAttrName = (attributeName: string) => { if (attributeName === 'class') { @@ -44,8 +43,8 @@ const toNativeSvgAttributes = (attributes: Record) => { * @param {number} props.strokeWidth - The stroke width of the icon * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width * @param {string} props.className - The class name of the icon - * @param {LucideIconNode} props.children - The children of the icon - * @param {LucideIconNode} props.iconNode - The icon node of the icon + * @param {LucideIconNode[]} props.children - The children of the icon + * @param {LucideIconNode[]} props.iconNode - The icon node of the icon * * @returns {ForwardRefExoticComponent} LucideIcon */ @@ -54,10 +53,17 @@ const Icon = forwardRef( { color, size, + width = size, + height = size, strokeWidth, absoluteStrokeWidth, + nonScalingStroke, children, - iconNode, + iconNode = [], + icon = { + node: iconNode, + size: 24, + }, className, testID, ...rest @@ -68,31 +74,28 @@ const Icon = forwardRef( size: contextSize = 24, strokeWidth: contextStrokeWidth = 2, absoluteStrokeWidth: contextAbsoluteStrokeWidth = false, + nonScalingStroke: contextNonScalingStroke = false, color: contextColor = 'currentColor', + className: contextClass = '', } = useLucideContext() ?? {}; - const calculatedStrokeWidth = - absoluteStrokeWidth ?? contextAbsoluteStrokeWidth - ? (Number(strokeWidth ?? contextStrokeWidth) * 24) / Number(size ?? contextSize) - : strokeWidth ?? contextStrokeWidth; + const hasAccessibleProp = Boolean(children) || hasA11yProp(rest); - const [, svgAttributes, builtIconNode = []] = buildLucideIconNode( - { - size: 24, - node: iconNode, - }, - { - color: color ?? contextColor ?? defaultAttributes.stroke, - size: size ?? contextSize ?? defaultAttributes.width, - strokeWidth: calculatedStrokeWidth, - className, - includeDefaultClasses: false, - }, - ); + const [, svgAttributes, builtIconNode] = buildLucideIconForReact(icon, { + color: color ?? contextColor, + width: width ?? size ?? contextSize, + height: height ?? size ?? contextSize, + strokeWidth: strokeWidth ?? contextStrokeWidth, + absoluteStrokeWidth: absoluteStrokeWidth ?? contextAbsoluteStrokeWidth, + nonScalingStroke: nonScalingStroke ?? contextNonScalingStroke, + className: mergeClasses(contextClass, className), + hasA11yProp: hasAccessibleProp, + attributes: rest, + }); const customAttrs = { - stroke: color ?? contextColor ?? defaultAttributes.stroke, - strokeWidth: calculatedStrokeWidth, + stroke: svgAttributes['stroke'], + strokeWidth: svgAttributes['strokeWidth'], ...rest, }; diff --git a/packages/lucide-react-native/src/context.ts b/packages/lucide-react-native/src/context.ts index c63d8a412..3dd87faa8 100644 --- a/packages/lucide-react-native/src/context.ts +++ b/packages/lucide-react-native/src/context.ts @@ -4,12 +4,17 @@ const LucideContext = createContext<{ size?: number; color?: string; strokeWidth?: number; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; }>({ size: 24, color: 'currentColor', strokeWidth: 2, absoluteStrokeWidth: false, + nonScalingStroke: false, }); interface LucideProviderProps { @@ -17,7 +22,11 @@ interface LucideProviderProps { size?: number; color?: string; strokeWidth?: number; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; } export function LucideProvider({ @@ -26,6 +35,7 @@ export function LucideProvider({ color, strokeWidth, absoluteStrokeWidth, + nonScalingStroke, }: LucideProviderProps) { const value = useMemo( () => ({ @@ -33,8 +43,9 @@ export function LucideProvider({ color, strokeWidth, absoluteStrokeWidth, + nonScalingStroke, }), - [size, color, strokeWidth, absoluteStrokeWidth], + [size, color, strokeWidth, absoluteStrokeWidth, nonScalingStroke], ); return createElement(LucideContext.Provider, { value }, children); diff --git a/packages/lucide-react-native/src/createLucideIcon.ts b/packages/lucide-react-native/src/createLucideIcon.ts index 31f98f974..058deba06 100644 --- a/packages/lucide-react-native/src/createLucideIcon.ts +++ b/packages/lucide-react-native/src/createLucideIcon.ts @@ -6,10 +6,10 @@ import Icon from './Icon'; /** * Creates a Lucide icon component from icon data. * - * @param iconData Icon data object. + * @param icon Icon data object. * @returns Lucide icon component. */ -function createLucideIcon(iconData: LucideIconData): LucideIcon; +function createLucideIcon(icon: LucideIconData): LucideIcon; /** * Creates a Lucide icon component from the legacy icon arguments. @@ -38,7 +38,7 @@ function createLucideIcon( const Component = forwardRef((props, ref) => createElement(Icon, { ref, - iconNode: iconData.node, + icon: iconData, ...props, }), ); diff --git a/packages/lucide-react-native/src/defaultAttributes.ts b/packages/lucide-react-native/src/defaultAttributes.ts index 3fae7d973..6f55b9edc 100644 --- a/packages/lucide-react-native/src/defaultAttributes.ts +++ b/packages/lucide-react-native/src/defaultAttributes.ts @@ -1,21 +1,10 @@ -const defaultAttributes = { - xmlns: 'http://www.w3.org/2000/svg', - width: 24, - height: 24, - viewBox: '0 0 24 24', - fill: 'none', - stroke: 'currentColor', - strokeWidth: 2, - strokeLinecap: 'round', - strokeLinejoin: 'round', -}; +import { defaultReactAttributes } from '@lucide/shared'; export const childDefaultAttributes = { - fill: defaultAttributes.fill, - stroke: defaultAttributes.stroke, - strokeWidth: defaultAttributes.strokeWidth, - strokeLinecap: defaultAttributes.strokeLinecap, - strokeLinejoin: defaultAttributes.strokeLinejoin, + fill: defaultReactAttributes.fill, + stroke: defaultReactAttributes.stroke, + strokeWidth: defaultReactAttributes.strokeWidth, + strokeLinecap: defaultReactAttributes.strokeLinecap, + strokeLinejoin: defaultReactAttributes.strokeLinejoin, }; - -export default defaultAttributes; +export default defaultReactAttributes; diff --git a/packages/lucide-react-native/src/types.ts b/packages/lucide-react-native/src/types.ts index 993c3b720..fc3dfe772 100644 --- a/packages/lucide-react-native/src/types.ts +++ b/packages/lucide-react-native/src/types.ts @@ -3,23 +3,26 @@ import type { SvgProps } from 'react-native-svg'; import type { LucideIconData as SharedLucideIconData, LucideIconNode as SharedLucideIconNode, -} from '@lucide/shared'; +} from '@lucide/shared/types'; -export type LucideIconNode = SharedLucideIconNode[]; +export type LucideIconNode = SharedLucideIconNode; -export type LucideIconData = SharedLucideIconData & { - name: string; - node: LucideIconNode; -}; +export type LucideIconData = SharedLucideIconData; /** * @deprecated Use LucideIconNode instead. */ -export type IconNode = LucideIconNode; +export type IconNode = LucideIconNode[]; export interface LucideProps extends SvgProps { size?: string | number; + width?: string | number; + height?: string | number; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; 'data-testid'?: string; } diff --git a/packages/lucide-react-native/tests/Icon.spec.tsx b/packages/lucide-react-native/tests/Icon.spec.tsx index 0bf4e886a..5c55e0b1c 100644 --- a/packages/lucide-react-native/tests/Icon.spec.tsx +++ b/packages/lucide-react-native/tests/Icon.spec.tsx @@ -7,6 +7,8 @@ import { Icon } from '../src/lucide-react-native'; vi.mock('react-native-svg'); describe('Using Icon Component', () => { + const airVentIcon = { name: 'air-vent', node: airVent }; + it('should render icon based on a iconNode', async () => { const { container } = render( { expect(container.firstChild).toMatchSnapshot(); }); + + it('should render icon based on icon data', async () => { + const { container } = render( + , + ); + + expect(container.firstChild).toBeDefined(); + }); + + it('should support nonScalingStroke', async () => { + const { container } = render( + , + ); + + expect(container.firstChild?.firstChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + }); }); diff --git a/packages/lucide-react-native/tests/__snapshots__/Icon.spec.tsx.snap b/packages/lucide-react-native/tests/__snapshots__/Icon.spec.tsx.snap index fd704741d..819ed9daf 100644 --- a/packages/lucide-react-native/tests/__snapshots__/Icon.spec.tsx.snap +++ b/packages/lucide-react-native/tests/__snapshots__/Icon.spec.tsx.snap @@ -2,6 +2,8 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = ` `; + +exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` + +`; diff --git a/packages/lucide-react/tests/context.spec.tsx b/packages/lucide-react/tests/context.spec.tsx index e6cd56e28..5b8e6cd76 100644 --- a/packages/lucide-react/tests/context.spec.tsx +++ b/packages/lucide-react/tests/context.spec.tsx @@ -60,6 +60,24 @@ describe('Using LucideProvider', () => { const IconComponent = container.firstElementChild; expect(IconComponent).toHaveAttribute('stroke-width', '1'); + expect(container.firstChild).toMatchSnapshot(); + }); + + it('should render the icon with LucideProvider and custom nonScalingStroke', () => { + const { container } = render( + + + , + ); + + const IconComponent = container.firstElementChild; + + expect(IconComponent?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + expect(container.firstChild).toMatchSnapshot(); }); it("should override the provider's global props when passing props to the icon", () => { diff --git a/packages/lucide-react/tests/lucide-react.spec.tsx b/packages/lucide-react/tests/lucide-react.spec.tsx index 8365d96f4..9b7480e03 100644 --- a/packages/lucide-react/tests/lucide-react.spec.tsx +++ b/packages/lucide-react/tests/lucide-react.spec.tsx @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; import { render, cleanup } from '@testing-library/react'; -import { Pen, Edit2, Grid, Droplet } from '../src/lucide-react'; +import { Pen, Edit2, Grid, createLucideIcon, LucideIconData } from '../src/lucide-react'; import defaultAttributes from '../src/defaultAttributes'; describe('Using lucide icon components', () => { @@ -69,7 +69,7 @@ describe('Using lucide icon components', () => { expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML); }); - it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { + it('should scale the strokeWidth when absoluteStrokeWidth is set', () => { const { container, getByTestId } = render( { expect(container.innerHTML).toMatchSnapshot(); }); + it('should apply vector-effect when nonScalingStroke is set', () => { + const { container, getByTestId } = render( + , + ); + + const SVGElement = container.firstElementChild; + + expect(SVGElement).toHaveAttribute('stroke', 'red'); + expect(SVGElement).toHaveAttribute('width', '48'); + expect(SVGElement).toHaveAttribute('height', '48'); + expect(SVGElement).toHaveAttribute('stroke-width', '2'); + expect(SVGElement?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + + expect(container.innerHTML).toMatchSnapshot(); + }); + it('should apply all classNames to the element', () => { + const iconData: LucideIconData = { + name: 'droplet', + size: 24, + node: [ + [ + 'path', + { + d: 'M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z', + key: 'abc123', + }, + ], + ], + aliases: ['drop'], + }; const testClass = 'my-class'; - const { container } = render(); + const Icon = createLucideIcon(iconData); + const { container } = render(); expect(container.firstChild).toHaveClass(testClass); expect(container.firstChild).toHaveClass('lucide'); expect(container.firstChild).toHaveClass('lucide-droplet'); + expect(container.firstChild).toHaveClass('lucide-drop'); }); }); diff --git a/packages/lucide-react/tsconfig.json b/packages/lucide-react/tsconfig.json index 43f70ec12..bd959bbec 100644 --- a/packages/lucide-react/tsconfig.json +++ b/packages/lucide-react/tsconfig.json @@ -8,6 +8,11 @@ "moduleResolution": "node", "module": "ESNext", "target": "ESNext", + "baseUrl": ".", + "paths": { + "@lucide/shared": ["../shared/src/index.ts"], + "@lucide/shared/types": ["../shared/src/build/types.ts"], + }, "esModuleInterop": true, "isolatedModules": true, "lib": ["esnext", "dom"], @@ -19,7 +24,7 @@ "sourceMap": true, "outDir": "./dist", "jsx": "react-jsx", - "types": ["@testing-library/jest-dom"], + "types": ["@testing-library/jest-dom", "node"], }, "exclude": ["**/node_modules"], "include": ["src", "tests"], diff --git a/packages/lucide-solid/scripts/exportTemplate.mts b/packages/lucide-solid/scripts/exportTemplate.mts index 8bf2bb3c1..6a2b6d376 100644 --- a/packages/lucide-solid/scripts/exportTemplate.mts +++ b/packages/lucide-solid/scripts/exportTemplate.mts @@ -24,7 +24,7 @@ const iconData: LucideIconData = ${JSON.stringify(iconData)}; * ${deprecated ? `@deprecated ${deprecationReason}` : ''} */ const ${componentName} = (props: LucideProps) => ( - + ) export default ${componentName}; diff --git a/packages/lucide-solid/src/Icon.tsx b/packages/lucide-solid/src/Icon.tsx index 51df3fe23..2183f4053 100644 --- a/packages/lucide-solid/src/Icon.tsx +++ b/packages/lucide-solid/src/Icon.tsx @@ -1,77 +1,58 @@ import { For, splitProps, useContext } from 'solid-js'; import { Dynamic } from 'solid-js/web'; import { buildLucideIconNode, hasA11yProp, mergeClasses } from '@lucide/shared'; -import defaultAttributes from './defaultAttributes'; -import { IconNode, LucideProps } from './types'; +import { LucideIconData, LucideIconNode, LucideProps } from './types'; import { LucideContext } from './context'; -interface IconProps { - name?: string; - aliases?: string[]; - iconNode: IconNode; -} +type IconProps = + | { + icon: LucideIconData; + iconNode?: never; + } + | { + icon?: never; + iconNode?: LucideIconNode[]; + }; const Icon = (props: LucideProps & IconProps) => { const [localProps, rest] = splitProps(props, [ 'color', 'size', + 'width', + 'height', 'strokeWidth', 'children', 'class', - 'name', - 'aliases', + 'icon', 'iconNode', 'absoluteStrokeWidth', + 'nonScalingStroke', ]); const globalProps = useContext(LucideContext); - const calculatedStrokeWidth = - (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'], - ); - const hasAccessibleProp = Boolean(localProps.children) || hasA11yProp(rest); - const [, svgAttributes, builtIconNode = []] = buildLucideIconNode( - { - name: localProps.name, - aliases: localProps.aliases, - size: 24, - node: localProps.iconNode, - }, - { - color: localProps.color ?? globalProps.color ?? defaultAttributes.stroke, - size: localProps.size ?? globalProps.size ?? defaultAttributes.width, - strokeWidth: calculatedStrokeWidth, - className: mergeClasses('lucide-icon', globalProps.class, localProps.class), - hasA11yProp: hasAccessibleProp, - }, - ); - - const svgElementAttributes = { - xmlns: svgAttributes.xmlns, - viewBox: svgAttributes.viewBox, - fill: svgAttributes.fill, - 'stroke-linecap': svgAttributes['stroke-linecap'], - 'stroke-linejoin': svgAttributes['stroke-linejoin'], - width: svgAttributes.width, - height: svgAttributes.height, - stroke: svgAttributes.stroke, - 'stroke-width': svgAttributes['stroke-width'], - class: svgAttributes.class, - 'aria-hidden': svgAttributes['aria-hidden'], - ...rest, + const icon: LucideIconData = localProps.icon ?? { + node: localProps.iconNode ?? ([] as LucideIconNode[]), + size: 24, + aliases: [], }; + const [, svgAttributes, builtIconNode] = buildLucideIconNode(icon, { + color: localProps.color ?? globalProps.color, + width: localProps.width ?? localProps.size ?? globalProps.size, + height: localProps.height ?? localProps.size ?? globalProps.size, + strokeWidth: localProps.strokeWidth ?? globalProps.strokeWidth, + absoluteStrokeWidth: localProps.absoluteStrokeWidth ?? globalProps.absoluteStrokeWidth, + nonScalingStroke: localProps.nonScalingStroke ?? globalProps.nonScalingStroke, + className: mergeClasses('lucide-icon', globalProps.class, localProps.class), + hasA11yProp: hasAccessibleProp, + attributes: rest, + }); + return ( - + {([elementName, attrs]) => { return ( diff --git a/packages/lucide-solid/src/context.tsx b/packages/lucide-solid/src/context.tsx index 709de0cc9..c12f31109 100644 --- a/packages/lucide-solid/src/context.tsx +++ b/packages/lucide-solid/src/context.tsx @@ -4,13 +4,18 @@ export const LucideContext = createContext<{ size?: number; color?: string; strokeWidth?: number; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; class?: string; }>({ size: 24, color: 'currentColor', strokeWidth: 2, absoluteStrokeWidth: false, + nonScalingStroke: false, class: '', }); @@ -19,7 +24,11 @@ interface LucideProviderProps { size?: number; color?: string; strokeWidth?: number; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; class?: string; } @@ -29,6 +38,7 @@ export function LucideProvider(props: LucideProviderProps) { 'color', 'strokeWidth', 'absoluteStrokeWidth', + 'nonScalingStroke', 'class', ]); diff --git a/packages/lucide-solid/src/defaultAttributes.ts b/packages/lucide-solid/src/defaultAttributes.ts index 092994586..cc4036fbd 100644 --- a/packages/lucide-solid/src/defaultAttributes.ts +++ b/packages/lucide-solid/src/defaultAttributes.ts @@ -1,16 +1 @@ -import { JSX } from 'solid-js/jsx-runtime'; -import { 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; +export { defaultAttributes as default } from '@lucide/shared'; diff --git a/packages/lucide-solid/src/types.ts b/packages/lucide-solid/src/types.ts index 343e5b5c0..209acad63 100644 --- a/packages/lucide-solid/src/types.ts +++ b/packages/lucide-solid/src/types.ts @@ -1,20 +1,33 @@ import { JSX } from 'solid-js/jsx-runtime'; +import type { + LucideIconData as SharedLucideIconData, + LucideIconNode as SharedLucideIconNode, +} from '@lucide/shared/types'; -export type IconNode = [elementName: keyof JSX.IntrinsicElements, attrs: Record][]; -export interface LucideIconData { - name: string; - node: IconNode; - aliases?: string[]; -} export type SVGAttributes = Partial>; +export type LucideIconNode = SharedLucideIconNode; + +export type LucideIconData = SharedLucideIconData; + +/** + * @deprecated Use LucideIconNode instead. + */ +export type IconNode = LucideIconNode[]; + export interface LucideProps extends SVGAttributes { key?: string | number; class?: string; size?: string | number; + width?: string | number; + height?: string | number; color?: string; strokeWidth?: string | number; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; } export type LucideIcon = (props: LucideProps) => JSX.Element; diff --git a/packages/lucide-solid/tests/Icon.spec.tsx b/packages/lucide-solid/tests/Icon.spec.tsx index b10dd6211..42d274018 100644 --- a/packages/lucide-solid/tests/Icon.spec.tsx +++ b/packages/lucide-solid/tests/Icon.spec.tsx @@ -5,6 +5,8 @@ import { airVent } from './testIconNodes'; import { Icon } from '../src/lucide-solid'; describe('Using Icon Component', () => { + const airVentIcon = { name: 'air-vent', node: airVent }; + it('should render icon based on a iconNode', async () => { const { container } = render(() => ( { expect(container.firstChild).toMatchSnapshot(); }); + + it('should render icon based on icon data', async () => { + const { container } = render(() => ( + + )); + + expect(container.firstChild).toBeDefined(); + }); + + it('should support nonScalingStroke', async () => { + const { container } = render(() => ( + + )); + + expect(container.firstChild?.firstChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + }); }); describe('Icon Component Accessibility', () => { diff --git a/packages/lucide-solid/tests/__snapshots__/lucide-solid.spec.tsx.snap b/packages/lucide-solid/tests/__snapshots__/lucide-solid.spec.tsx.snap index 230c23a4a..22743b33f 100644 --- a/packages/lucide-solid/tests/__snapshots__/lucide-solid.spec.tsx.snap +++ b/packages/lucide-solid/tests/__snapshots__/lucide-solid.spec.tsx.snap @@ -2,55 +2,14 @@ exports[`Using lucide icon components > should adjust the size, stroke color and stroke width 1`] = ` -`; - -exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` -
`; -exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` - -`; - exports[`Using lucide icon components > should render an component 1`] = `
should render an icon slot 1`] = `
`; + +exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = ` + +`; diff --git a/packages/svelte/tests/lucide-svelte.spec.ts b/packages/svelte/tests/lucide-svelte.spec.ts index ec0cb6604..5e8dda33a 100644 --- a/packages/svelte/tests/lucide-svelte.spec.ts +++ b/packages/svelte/tests/lucide-svelte.spec.ts @@ -66,7 +66,7 @@ describe('Using lucide icon components', () => { expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML); }); - it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { + it('should scale the strokeWidth when absoluteStrokeWidth is set', () => { const { container } = render(Smile, { color: 'red', size: 48, diff --git a/packages/svelte/tsconfig.json b/packages/svelte/tsconfig.json index 3ef7dda35..58280e442 100644 --- a/packages/svelte/tsconfig.json +++ b/packages/svelte/tsconfig.json @@ -6,6 +6,11 @@ "module": "NodeNext", "moduleResolution": "NodeNext", "resolveJsonModule": true, + "baseUrl": ".", + "paths": { + "@lucide/shared": ["../shared/src/index.ts"], + "@lucide/shared/types": ["../shared/src/build/types.ts"], + }, "allowJs": true, "checkJs": true, "isolatedModules": true, diff --git a/packages/vue/rollup.config.mjs b/packages/vue/rollup.config.mjs index 94bba42f8..b69ab424a 100644 --- a/packages/vue/rollup.config.mjs +++ b/packages/vue/rollup.config.mjs @@ -2,6 +2,11 @@ import plugins from '@lucide/rollup-plugins'; import pkg from './package.json' with { type: 'json' }; import dts from 'rollup-plugin-dts'; +const dtsOptions = { + includeExternal: ['@lucide/shared/types'], + tsconfig: './tsconfig.json', +}; + const packageName = '@lucide/vue'; const outputFileName = 'lucide-vue'; const outputDir = 'dist'; @@ -58,13 +63,7 @@ export default [ format: 'es', }, ], - plugins: [ - dts({ - compilerOptions: { - preserveSymlinks: false, - }, - }), - ], + plugins: [dts(dtsOptions)], }, { input: `src/${outputFileName}.suffixed.ts`, @@ -74,13 +73,7 @@ export default [ format: 'es', }, ], - plugins: [ - dts({ - compilerOptions: { - preserveSymlinks: false, - }, - }), - ], + plugins: [dts(dtsOptions)], }, { input: `src/${outputFileName}.prefixed.ts`, @@ -90,13 +83,7 @@ export default [ format: 'es', }, ], - plugins: [ - dts({ - compilerOptions: { - preserveSymlinks: false, - }, - }), - ], + plugins: [dts(dtsOptions)], }, ...configs, ]; diff --git a/packages/vue/src/Icon.ts b/packages/vue/src/Icon.ts index 5bdecdb5f..d9f826371 100644 --- a/packages/vue/src/Icon.ts +++ b/packages/vue/src/Icon.ts @@ -1,26 +1,48 @@ -import { computed, type FunctionalComponent, h } from 'vue'; -import { buildLucideIconNode, isEmptyString, toKebabCase } from '@lucide/shared'; +import { type FunctionalComponent, h } from 'vue'; +import { + buildLucideIconNode, + hasA11yProp, + isEmptyString, + mergeClasses, + toKebabCase, +} from '@lucide/shared'; import defaultAttributes from './defaultAttributes'; -import { IconNode, LucideProps } from './types'; +import { LucideIconNode, LucideIconData, LucideProps } from './types'; import { useLucideProps } from './context'; -interface IconProps { - iconNode: IconNode; - name: string; - aliases?: string[]; -} +type IconProps = + | { + icon: LucideIconData; + iconNode?: never; + name?: never; + } + | { + icon?: never; + iconNode: LucideIconNode[]; + name: string; + }; const Icon: FunctionalComponent = ( { name, - aliases, - iconNode, + iconNode = [], + icon = { + name: toKebabCase(name), + node: iconNode, + size: 24, + aliases: [], + }, absoluteStrokeWidth, 'absolute-stroke-width': absoluteStrokeWidthKebabCase, + nonScalingStroke, + 'non-scaling-stroke': nonScalingStrokeKebabCase, strokeWidth, 'stroke-width': strokeWidthKebabCase, size, + width = size, + height = size, color, + class: classes = '', ...props }, { slots }, @@ -30,62 +52,43 @@ const Icon: FunctionalComponent = ( color: contextColor, strokeWidth: contextStrokeWidth = 2, absoluteStrokeWidth: contextAbsoluteStrokeWidth = false, + nonScalingStroke: contextNonScalingStroke = false, class: contextClass = '', } = useLucideProps(); - const calculatedStrokeWidth = computed(() => { - const isAbsoluteStrokeWidth = - isEmptyString(absoluteStrokeWidth) || - isEmptyString(absoluteStrokeWidthKebabCase) || - absoluteStrokeWidth === true || - absoluteStrokeWidthKebabCase === true || - contextAbsoluteStrokeWidth === true; + const isAbsoluteStrokeWidth = + isEmptyString(absoluteStrokeWidth) || + isEmptyString(absoluteStrokeWidthKebabCase) || + absoluteStrokeWidth === true || + absoluteStrokeWidthKebabCase === true || + contextAbsoluteStrokeWidth === true; - const strokeWidthValue = - strokeWidth || - strokeWidthKebabCase || - contextStrokeWidth || - defaultAttributes['stroke-width']; + const isNonScalingStroke = + isEmptyString(nonScalingStroke) || + isEmptyString(nonScalingStrokeKebabCase) || + nonScalingStroke === true || + nonScalingStrokeKebabCase === true || + contextNonScalingStroke === true; - if (isAbsoluteStrokeWidth) { - return ( - (Number(strokeWidthValue) * 24) / Number(size ?? contextSize ?? defaultAttributes.width) - ); - } + const { class: propsClass, ...restProps } = props; - return strokeWidthValue; + const [, svgAttributes, builtIconNode] = buildLucideIconNode(icon, { + color: color ?? contextColor, + width: width ?? size ?? contextSize, + height: height ?? size ?? contextSize, + strokeWidth: + strokeWidth ?? + strokeWidthKebabCase ?? + contextStrokeWidth ?? + defaultAttributes['stroke-width'], + absoluteStrokeWidth: isAbsoluteStrokeWidth, + nonScalingStroke: isNonScalingStroke, + className: mergeClasses(contextClass, propsClass), + hasA11yProp: !!slots || hasA11yProp(restProps), + attributes: restProps, }); - const normalizedName = computed(() => (name != null ? toKebabCase(name) : undefined)); - - const iconName = computed(() => (normalizedName.value ? `${normalizedName.value}-icon` : 'icon')); - - const [, svgAttributes, builtIconNode = []] = buildLucideIconNode( - { - name: iconName.value, - aliases, - size: 24, - node: iconNode, - }, - { - size: size ?? contextSize ?? defaultAttributes.width, - color: color ?? contextColor ?? defaultAttributes.stroke, - strokeWidth: calculatedStrokeWidth.value, - className: contextClass, - }, - ); - - const iconAttributes = { - ...svgAttributes, - ...props, - width: svgAttributes.width, - height: svgAttributes.height, - stroke: svgAttributes.stroke, - 'stroke-width': svgAttributes['stroke-width'], - class: svgAttributes.class, - }; - - return h('svg', iconAttributes, [ + return h('svg', svgAttributes, [ ...builtIconNode.map((child) => h(...child)), ...(slots.default ? [slots.default()] : []), ]); diff --git a/packages/vue/src/context.ts b/packages/vue/src/context.ts index 7af325ec7..0435ed55c 100644 --- a/packages/vue/src/context.ts +++ b/packages/vue/src/context.ts @@ -6,7 +6,11 @@ interface LucideIconsContext { size?: number; color?: string; strokeWidth?: number; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + nonScalingStroke?: boolean; class?: string; } diff --git a/packages/vue/src/createLucideIcon.ts b/packages/vue/src/createLucideIcon.ts index 10d5247df..5981b8153 100644 --- a/packages/vue/src/createLucideIcon.ts +++ b/packages/vue/src/createLucideIcon.ts @@ -1,25 +1,44 @@ import { h } from 'vue'; -import { LucideIcon, LucideIconData } from './types'; +import { LucideIcon, LucideIconData, LucideIconNode } from './types'; import Icon from './Icon'; /** - * Create a Lucide icon component - * @param {object} iconData - * @returns {FunctionalComponent} LucideIcon + * Creates a Lucide icon component from icon data. + * + * @param icon Icon data object. + * @returns Lucide icon component. */ -const createLucideIcon = - (iconData: LucideIconData): LucideIcon => - (props, { slots, attrs }) => +function createLucideIcon(icon: LucideIconData): LucideIcon; + +/** + * Creates a Lucide icon component from the legacy icon arguments. + * + * @param iconName Icon name. + * @param iconNode Icon node. + * @returns Lucide icon component. + */ +function createLucideIcon(iconName: string, iconNode: LucideIconNode[]): LucideIcon; + +function createLucideIcon( + iconDataOrName: LucideIconData | string, + iconNode: LucideIconNode[] = [], +): LucideIcon { + const icon: LucideIconData = + typeof iconDataOrName === 'string' + ? { + name: iconDataOrName, + node: iconNode, + } + : iconDataOrName; + return (props, { slots }) => h( Icon, { - ...attrs, ...props, - iconNode: iconData.node, - name: iconData.name, - aliases: iconData.aliases, + icon, }, slots.default ? { default: slots.default } : undefined, ); +} export default createLucideIcon; diff --git a/packages/vue/src/defaultAttributes.ts b/packages/vue/src/defaultAttributes.ts index 8ae7d46ea..cc4036fbd 100644 --- a/packages/vue/src/defaultAttributes.ts +++ b/packages/vue/src/defaultAttributes.ts @@ -1,11 +1 @@ -export default { - 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 { defaultAttributes as default } from '@lucide/shared'; diff --git a/packages/vue/src/types.ts b/packages/vue/src/types.ts index 38d5f2f6d..f292be26a 100644 --- a/packages/vue/src/types.ts +++ b/packages/vue/src/types.ts @@ -1,16 +1,31 @@ import type { FunctionalComponent, SVGAttributes } from 'vue'; +import type { + LucideIconData as SharedLucideIconData, + LucideIconNode as SharedLucideIconNode, +} from '@lucide/shared/types'; export interface LucideProps extends Partial { size?: 24 | number; strokeWidth?: number | string; + /** + * @deprecated Use `nonScalingStroke` instead. + */ absoluteStrokeWidth?: boolean; + /** + * @deprecated Use `nonScalingStroke` instead. + */ 'absolute-stroke-width'?: boolean; + nonScalingStroke?: boolean; + 'non-scaling-stroke'?: boolean; } -export type IconNode = [elementName: string, attrs: Record][]; -export interface LucideIconData { - name: string; - node: IconNode; - aliases?: string[]; -} +export type LucideIconNode = SharedLucideIconNode; + +export type LucideIconData = SharedLucideIconData; + +/** + * @deprecated Use LucideIconNode instead. + */ +export type IconNode = LucideIconNode[]; + export type LucideIcon = FunctionalComponent; diff --git a/packages/vue/tests/Icon.spec.ts b/packages/vue/tests/Icon.spec.ts index 968a36d05..54c3c394e 100644 --- a/packages/vue/tests/Icon.spec.ts +++ b/packages/vue/tests/Icon.spec.ts @@ -5,21 +5,9 @@ import { airVent } from './testIconNodes'; import { Icon } from '../src/lucide-vue'; describe('Using Icon Component', () => { - it('should render icon based on a iconNode', async () => { - const { container } = render(Icon, { - props: { - iconNode: airVent, - name: 'AirVent', - size: 48, - color: 'red', - absoluteStrokeWidth: true, - }, - }); + const airVentIcon = { name: 'air-vent', node: airVent, size: 24 }; - expect(container.firstChild).toBeDefined(); - }); - - it('should render icon and match snapshot', async () => { + it('should render icon based on an icon node', async () => { const { container } = render(Icon, { props: { iconNode: airVent, @@ -32,4 +20,42 @@ describe('Using Icon Component', () => { expect(container.firstChild).toMatchSnapshot(); }); + + it('should render icon based on icon data', async () => { + const { container } = render(Icon, { + props: { + icon: airVentIcon, + size: 48, + color: 'red', + absoluteStrokeWidth: true, + }, + }); + + expect(container.firstChild).toMatchSnapshot(); + }); + + it('should render icon and match snapshot', async () => { + const { container } = render(Icon, { + props: { + icon: airVentIcon, + size: 48, + color: 'red', + absoluteStrokeWidth: true, + }, + }); + + expect(container.firstChild).toMatchSnapshot(); + }); + + it('should render icon based on icon data', async () => { + const { container } = render(Icon, { + props: { + icon: airVentIcon, + size: 48, + color: 'red', + }, + }); + + expect(container.firstChild).toBeDefined(); + }); }); diff --git a/packages/vue/tests/__snapshots__/Icon.spec.ts.snap b/packages/vue/tests/__snapshots__/Icon.spec.ts.snap index 9907eb584..a765d4e6e 100644 --- a/packages/vue/tests/__snapshots__/Icon.spec.ts.snap +++ b/packages/vue/tests/__snapshots__/Icon.spec.ts.snap @@ -2,7 +2,63 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = ` + + + + + +`; + +exports[`Using Icon Component > should render icon based on an icon node 1`] = ` + + + + + + +`; + +exports[`Using Icon Component > should render icon based on icon data 1`] = ` + should render the icon with LucideProvider 1`] = ` should add a class to the element 1`] = `
should add a class to the element 1`] = exports[`Using lucide icon components > should add a style attribute to the element 1`] = `
should add a style attribute to the elem exports[`Using lucide icon components > should adjust the size, stroke color and stroke width 1`] = `
should adjust the size, stroke color and exports[`Using lucide icon components > should pass children to the icon slot 1`] = `
should pass children to the icon slot 1` exports[`Using lucide icon components > should render an component 1`] = `
{ expect(IconComponent).toHaveAttribute( 'class', - 'lucide lucide-house-icon lucide-home provider-class icon-class', + 'lucide lucide-house lucide-home provider-class icon-class', ); }); }); diff --git a/packages/vue/tests/lucide-vue.spec.ts b/packages/vue/tests/lucide-vue.spec.ts index bde2e0fd5..cf2b536fa 100644 --- a/packages/vue/tests/lucide-vue.spec.ts +++ b/packages/vue/tests/lucide-vue.spec.ts @@ -59,7 +59,7 @@ describe('Using lucide icon components', () => { expect(icon).toHaveClass('my-icon'); expect(icon).toHaveClass('lucide'); - expect(icon).toHaveClass('lucide-smile-icon'); + expect(icon).toHaveClass('lucide-smile'); }); it('should add a style attribute to the element', () => { @@ -152,7 +152,7 @@ describe('Using lucide icon components', () => { expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML); }); - it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { + it('should scale the strokeWidth when absoluteStrokeWidth is set', () => { const { container } = render(Pen, { props: { size: 48, @@ -168,7 +168,7 @@ describe('Using lucide icon components', () => { expect(icon).toHaveAttribute('stroke-width', '1'); }); - it('should not scale the strokeWidth when absoluteStrokeWidth is as empty value attribute', () => { + it('should scale the strokeWidth when absoluteStrokeWidth is as empty value attribute', () => { const { container } = render(Pen, { props: { size: 48, @@ -184,7 +184,7 @@ describe('Using lucide icon components', () => { expect(icon).toHaveAttribute('stroke-width', '1'); }); - it('should not scale the strokeWidth when absoluteStrokeWidth is written in kebabCase', () => { + it('should scale the strokeWidth when absoluteStrokeWidth is written in kebabCase', () => { const { container } = render(Pen, { props: { size: 48, @@ -200,4 +200,56 @@ describe('Using lucide icon components', () => { expect(icon).toHaveAttribute('stroke', 'red'); expect(icon).toHaveAttribute('stroke-width', '1'); }); + + it('should apply vector-effect when nonScalingStroke is set', () => { + const { container } = render(Pen, { + props: { + size: 48, + color: 'red', + nonScalingStroke: true, + }, + }); + + const icon = container.firstElementChild; + + expect(icon).toHaveAttribute('width', '48'); + expect(icon).toHaveAttribute('stroke', 'red'); + expect(icon).toHaveAttribute('stroke-width', '2'); + expect(icon?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + }); + + it('should apply vector-effect when nonScalingStroke is as empty value attribute', () => { + const { container } = render(Pen, { + props: { + size: 48, + color: 'red', + nonScalingStroke: '' as unknown as boolean, // Vue treats empty string as true for boolean props + }, + }); + + const icon = container.firstElementChild; + + expect(icon).toHaveAttribute('width', '48'); + expect(icon).toHaveAttribute('stroke', 'red'); + expect(icon).toHaveAttribute('stroke-width', '2'); + expect(icon?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + }); + + it('should apply vector-effect when nonScalingStroke is written in kebabCase', () => { + const { container } = render(Pen, { + props: { + size: 48, + color: 'red', + 'stroke-width': '2', + 'non-scaling-stroke': '' as unknown as boolean, // Vue treats empty string as true for boolean props + }, + }); + + const icon = container.firstElementChild; + + expect(icon).toHaveAttribute('width', '48'); + expect(icon).toHaveAttribute('stroke', 'red'); + expect(icon).toHaveAttribute('stroke-width', '2'); + expect(icon?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + }); }); diff --git a/packages/vue/tsconfig.json b/packages/vue/tsconfig.json index c38e018fa..dba73c989 100644 --- a/packages/vue/tsconfig.json +++ b/packages/vue/tsconfig.json @@ -4,6 +4,11 @@ "useDefineForClassFields": true, "module": "ESNext", "moduleResolution": "Node", + "baseUrl": ".", + "paths": { + "@lucide/shared": ["../shared/src/index.ts"], + "@lucide/shared/types": ["../shared/src/build/types.ts"], + }, "strict": true, "jsx": "preserve", "resolveJsonModule": true, From 3d4b0e0878d2babe2bc5f893baf27b089ea93db7 Mon Sep 17 00:00:00 2001 From: Karsa Date: Fri, 22 May 2026 21:28:11 +0200 Subject: [PATCH 6/8] feat(): unify icon build logic some more, add nonScalingStroke option, marking absoluteStrokeWidth as deprecated --- packages/astro/src/Icon.astro | 2 +- packages/lucide-preact/src/Icon.ts | 11 +- packages/lucide-preact/src/types.ts | 2 +- packages/lucide-react-native/src/Icon.ts | 2 +- packages/lucide-react/src/Icon.ts | 2 +- packages/lucide-solid/src/Icon.tsx | 2 +- .../shared/src/build/buildLucideIconNode.ts | 16 +- packages/shared/src/build/types.ts | 7 +- packages/shared/src/utils/toCamelCase.ts | 25 +- packages/svelte/package.json | 2 +- packages/svelte/scripts/exportTemplate.mts | 4 +- packages/svelte/scripts/patchCopiedUtils.mts | 14 + packages/svelte/src/Icon.svelte | 8 +- packages/svelte/src/types.ts | 2 +- .../__snapshots__/lucide-svelte.spec.ts.snap | 40 ++ packages/svelte/tests/lucide-svelte.spec.ts | 18 + packages/vue/src/Icon.ts | 9 +- packages/vue/tests/Icon.spec.ts | 25 - .../vue/tests/__snapshots__/Icon.spec.ts.snap | 28 - pnpm-lock.yaml | 527 ++++++++++-------- 20 files changed, 426 insertions(+), 320 deletions(-) create mode 100644 packages/svelte/scripts/patchCopiedUtils.mts diff --git a/packages/astro/src/Icon.astro b/packages/astro/src/Icon.astro index b20d3b835..7d54bedfa 100644 --- a/packages/astro/src/Icon.astro +++ b/packages/astro/src/Icon.astro @@ -21,7 +21,7 @@ const { ...rest } = Astro.props; -const [, builtIconAttributes, builtIconNode] = buildLucideIconNode(icon, { +const [, builtIconAttributes, builtIconNode = []] = buildLucideIconNode(icon, { color, width: width ?? size, height: height ?? size, diff --git a/packages/lucide-preact/src/Icon.ts b/packages/lucide-preact/src/Icon.ts index 3bb74276d..c9c65dcfe 100644 --- a/packages/lucide-preact/src/Icon.ts +++ b/packages/lucide-preact/src/Icon.ts @@ -1,19 +1,18 @@ import { h, toChildArray } from 'preact'; import { buildLucideIconNode, hasA11yProp, mergeClasses } from '@lucide/shared'; -import defaultAttributes from './defaultAttributes'; import type { LucideIconData, LucideIconNode, LucideProps } from './types'; import { useLucideContext } from './context'; type IconComponentProps = LucideProps & ( - | { - iconNode: LucideIconNode; - icon?: never; - } | { icon: LucideIconData; iconNode?: never; } + | { + iconNode: LucideIconNode[]; + icon?: never; + } ); /** @@ -59,7 +58,7 @@ const Icon = ({ class: contextClass = '', } = useLucideContext() ?? {}; - const [name, svgAttributes, builtIconNode] = buildLucideIconNode(icon, { + const [name, svgAttributes, builtIconNode = []] = buildLucideIconNode(icon, { color: color ?? contextColor, width: width ?? size ?? contextSize, height: height ?? size ?? contextSize, diff --git a/packages/lucide-preact/src/types.ts b/packages/lucide-preact/src/types.ts index 6ccc4ad25..15204ae36 100644 --- a/packages/lucide-preact/src/types.ts +++ b/packages/lucide-preact/src/types.ts @@ -2,7 +2,7 @@ import { type FunctionComponent, type JSX } from 'preact'; import type { LucideIconData as SharedLucideIconData, LucideIconNode as SharedLucideIconNode, -} from '@lucide/shared'; +} from '@lucide/shared/types'; export type LucideIconNode = SharedLucideIconNode; diff --git a/packages/lucide-react-native/src/Icon.ts b/packages/lucide-react-native/src/Icon.ts index 0d49ccba9..2ada9234d 100644 --- a/packages/lucide-react-native/src/Icon.ts +++ b/packages/lucide-react-native/src/Icon.ts @@ -81,7 +81,7 @@ const Icon = forwardRef( const hasAccessibleProp = Boolean(children) || hasA11yProp(rest); - const [, svgAttributes, builtIconNode] = buildLucideIconForReact(icon, { + const [, svgAttributes, builtIconNode = []] = buildLucideIconForReact(icon, { color: color ?? contextColor, width: width ?? size ?? contextSize, height: height ?? size ?? contextSize, diff --git a/packages/lucide-react/src/Icon.ts b/packages/lucide-react/src/Icon.ts index ed2de90d6..cf52b9688 100644 --- a/packages/lucide-react/src/Icon.ts +++ b/packages/lucide-react/src/Icon.ts @@ -66,7 +66,7 @@ const Icon = forwardRef( const hasAccessibleProp = Boolean(children) || hasA11yProp(rest); - const [name, svgAttributes, builtIconNode] = buildLucideIconForReact(icon, { + const [name, svgAttributes, builtIconNode = []] = buildLucideIconForReact(icon, { color: color ?? contextColor, width: width ?? size ?? contextSize, height: height ?? size ?? contextSize, diff --git a/packages/lucide-solid/src/Icon.tsx b/packages/lucide-solid/src/Icon.tsx index 2183f4053..d53b00e23 100644 --- a/packages/lucide-solid/src/Icon.tsx +++ b/packages/lucide-solid/src/Icon.tsx @@ -39,7 +39,7 @@ const Icon = (props: LucideProps & IconProps) => { aliases: [], }; - const [, svgAttributes, builtIconNode] = buildLucideIconNode(icon, { + const [, svgAttributes, builtIconNode = []] = buildLucideIconNode(icon, { color: localProps.color ?? globalProps.color, width: localProps.width ?? localProps.size ?? globalProps.size, height: localProps.height ?? localProps.size ?? globalProps.size, diff --git a/packages/shared/src/build/buildLucideIconNode.ts b/packages/shared/src/build/buildLucideIconNode.ts index 6cdb2c940..3ab903c41 100644 --- a/packages/shared/src/build/buildLucideIconNode.ts +++ b/packages/shared/src/build/buildLucideIconNode.ts @@ -17,8 +17,8 @@ function buildLucideIconNode(icon: LucideIconData, params: LucideBuildParams = { const viewBoxHeight = icon.size ?? icon.height ?? defaultAttributes['height']; const aliasClassNames = icon.aliases - ?.filter((alias): alias is string => typeof alias === 'string' && alias.trim() !== '') - .map((alias) => `lucide-${alias}`) ?? []; + ?.filter((alias: string): alias is string => typeof alias === 'string' && alias.trim() !== '') + .map((alias: string) => `lucide-${alias}`) ?? []; const iconClassNames = [...(icon.name ? [`lucide-${icon.name}`] : []), ...aliasClassNames]; const classNamesFromClassName = params.className?.split(' ').filter(Boolean) ?? []; const className = @@ -55,12 +55,14 @@ function buildLucideIconNode(icon: LucideIconData, params: LucideBuildParams = { return [ 'svg', attributes, - icon.node.map(([name, attrs]) => [ - name, - params.nonScalingStroke + icon.node.map((child): LucideIconNode => { + const [name, attrs, children] = child; + const nextAttrs = params.nonScalingStroke ? { [getAttributeName('vector-effect')]: 'non-scaling-stroke', ...attrs } - : attrs, - ]), + : attrs; + + return children ? [name, nextAttrs, children] : [name, nextAttrs]; + }), ]; } diff --git a/packages/shared/src/build/types.ts b/packages/shared/src/build/types.ts index 5844972ba..39a99662f 100644 --- a/packages/shared/src/build/types.ts +++ b/packages/shared/src/build/types.ts @@ -1,4 +1,5 @@ -export type SVGProps = Record; +/* eslint-disable @typescript-eslint/no-explicit-any */ +export type SVGProps = Record; /** * A Lucide icon node (an svgson-like internal format) @@ -70,6 +71,6 @@ export type LucideBuildParams = SVGProps> */ attributeNames?: Record; } & ( - | { size?: string | number; width?: never; height?: never } - | { size?: never; width?: string | number; height?: string | number } + | { size?: string | number | null; width?: never; height?: never } + | { size?: never; width?: string | number | null; height?: string | number | null } ); diff --git a/packages/shared/src/utils/toCamelCase.ts b/packages/shared/src/utils/toCamelCase.ts index 52ade7dbd..6faf1194b 100644 --- a/packages/shared/src/utils/toCamelCase.ts +++ b/packages/shared/src/utils/toCamelCase.ts @@ -4,7 +4,24 @@ * @param {string} string * @returns {string} A camelized string */ -export const toCamelCase = (string: T) => - string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => - p2 ? p2.toUpperCase() : p1.toLowerCase(), - ); +export const toCamelCase = (string: T) => { + let out = ''; + let upperNext = false; + + for (const ch of string) { + if (ch === '-' || ch === '_' || ch <= ' ') { + upperNext = out.length > 0; + continue; + } + + if (out.length === 0) { + out += ch.toLowerCase(); + } else { + out += upperNext ? ch.toUpperCase() : ch; + } + + upperNext = false; + } + + return out as string; +}; diff --git a/packages/svelte/package.json b/packages/svelte/package.json index de071b825..900f5a4ac 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -48,7 +48,7 @@ "scripts": { "build": "pnpm clean && pnpm copy:license && pnpm copy:utils && pnpm build:icons && pnpm build:package && pnpm build:license", "copy:license": "cp ../../LICENSE ./LICENSE", - "copy:utils": "cpy ../../packages/shared/src/build/{buildLucideIconNode.ts,defaultAttributes.ts,types.ts} ../../packages/shared/src/utils/{hasA11yProp.ts,mergeClasses.ts} ./src/utils/", + "copy:utils": "cpy ../../packages/shared/src/build/{buildLucideIconNode.ts,defaultAttributes.ts,types.ts} ../../packages/shared/src/utils/{hasA11yProp.ts,mergeClasses.ts} ./src/utils/ && node ./scripts/patchCopiedUtils.mts", "clean": "rm -rf dist stats ./src/icons/*.{ts,svelte} ./src/aliases/{aliases,prefixed,suffixed}.ts", "build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --exportFileName=index.ts --iconFileExtension=.svelte --importImportFileExtension=.svelte --separateIconFileExport --separateIconFileExportExtension=.ts --withAliases --aliasesFileExtension=.ts --separateAliasesFile --separateAliasesFileExtension=.ts --aliasImportFileExtension=.js --pretty=false", "build:package": "svelte-package --input ./src", diff --git a/packages/svelte/scripts/exportTemplate.mts b/packages/svelte/scripts/exportTemplate.mts index e65a57fc1..ef046a584 100644 --- a/packages/svelte/scripts/exportTemplate.mts +++ b/packages/svelte/scripts/exportTemplate.mts @@ -8,8 +8,8 @@ export default defineExportTemplate( const svgBase64 = base64SVG(svgContents); return `\ -
`; +exports[`Using lucide icon components > should apply vector-effect when nonScalingStroke is set 1`] = ` + +`; + exports[`Using lucide icon components > should render an component 1`] = `
{ expect(container.innerHTML).toMatchSnapshot(); }); + it('should apply vector-effect when nonScalingStroke is set', () => { + const { container } = render(Smile, { + color: 'red', + size: 48, + nonScalingStroke: true, + }); + + const IconComponent = container.firstElementChild; + + expect(IconComponent).toHaveAttribute('width', '48'); + expect(IconComponent).toHaveAttribute('height', '48'); + expect(IconComponent).toHaveAttribute('stroke', 'red'); + expect(IconComponent).toHaveAttribute('stroke-width', '2'); + expect(IconComponent?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke'); + + expect(container.innerHTML).toMatchSnapshot(); + }); + it('should use context values from the global set properties', () => { const { container } = render(ContextWrapper); diff --git a/packages/vue/src/Icon.ts b/packages/vue/src/Icon.ts index d9f826371..54aa8c474 100644 --- a/packages/vue/src/Icon.ts +++ b/packages/vue/src/Icon.ts @@ -6,7 +6,6 @@ import { mergeClasses, toKebabCase, } from '@lucide/shared'; -import defaultAttributes from './defaultAttributes'; import { LucideIconNode, LucideIconData, LucideProps } from './types'; import { useLucideProps } from './context'; @@ -72,15 +71,11 @@ const Icon: FunctionalComponent = ( const { class: propsClass, ...restProps } = props; - const [, svgAttributes, builtIconNode] = buildLucideIconNode(icon, { + const [, svgAttributes, builtIconNode = []] = buildLucideIconNode(icon, { color: color ?? contextColor, width: width ?? size ?? contextSize, height: height ?? size ?? contextSize, - strokeWidth: - strokeWidth ?? - strokeWidthKebabCase ?? - contextStrokeWidth ?? - defaultAttributes['stroke-width'], + strokeWidth: strokeWidth ?? strokeWidthKebabCase ?? contextStrokeWidth, absoluteStrokeWidth: isAbsoluteStrokeWidth, nonScalingStroke: isNonScalingStroke, className: mergeClasses(contextClass, propsClass), diff --git a/packages/vue/tests/Icon.spec.ts b/packages/vue/tests/Icon.spec.ts index 54c3c394e..68ceca4f2 100644 --- a/packages/vue/tests/Icon.spec.ts +++ b/packages/vue/tests/Icon.spec.ts @@ -21,19 +21,6 @@ describe('Using Icon Component', () => { expect(container.firstChild).toMatchSnapshot(); }); - it('should render icon based on icon data', async () => { - const { container } = render(Icon, { - props: { - icon: airVentIcon, - size: 48, - color: 'red', - absoluteStrokeWidth: true, - }, - }); - - expect(container.firstChild).toMatchSnapshot(); - }); - it('should render icon and match snapshot', async () => { const { container } = render(Icon, { props: { @@ -46,16 +33,4 @@ describe('Using Icon Component', () => { expect(container.firstChild).toMatchSnapshot(); }); - - it('should render icon based on icon data', async () => { - const { container } = render(Icon, { - props: { - icon: airVentIcon, - size: 48, - color: 'red', - }, - }); - - expect(container.firstChild).toBeDefined(); - }); }); diff --git a/packages/vue/tests/__snapshots__/Icon.spec.ts.snap b/packages/vue/tests/__snapshots__/Icon.spec.ts.snap index a765d4e6e..e7b15be0c 100644 --- a/packages/vue/tests/__snapshots__/Icon.spec.ts.snap +++ b/packages/vue/tests/__snapshots__/Icon.spec.ts.snap @@ -55,31 +55,3 @@ exports[`Using Icon Component > should render icon based on an icon node 1`] = ` /> `; - -exports[`Using Icon Component > should render icon based on icon data 1`] = ` - - - - - - -`; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39ee05ff8..14a5d56ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -51,7 +51,10 @@ importers: version: 4.0.12(vitest@4.0.12) ajv-cli: specifier: ^5.0.0 - version: 5.0.0(ts-node@10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@24.10.1)(typescript@5.9.3)) + version: 5.0.0(ts-node@10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@25.9.1)(typescript@5.9.3)) + cpy-cli: + specifier: ^7.0.0 + version: 7.0.0 dotenv: specifier: ^17.4.2 version: 17.4.2 @@ -117,7 +120,7 @@ importers: version: 5.3.1 vitest: specifier: 4.0.12 - version: 4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 4.0.12(@types/debug@4.1.12)(@types/node@25.9.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) yargs: specifier: ^17.7.2 version: 17.7.2 @@ -232,10 +235,10 @@ importers: version: 5.3.1 vitepress: specifier: ^1.6.4 - version: 1.6.4(@algolia/client-search@5.48.1)(@types/node@24.10.1)(@types/react@18.3.27)(axios@1.13.2)(fuse.js@7.1.0)(less@4.2.0)(postcss@8.5.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.98.0)(search-insights@2.8.2)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3) + version: 1.6.4(@algolia/client-search@5.48.1)(@types/node@25.9.1)(@types/react@18.3.27)(axios@1.13.2)(fuse.js@7.1.0)(less@4.2.0)(postcss@8.5.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.98.0)(search-insights@2.8.2)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3) vitepress-plugin-group-icons: specifier: ^1.6.5 - version: 1.7.1(vite@5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)) + version: 1.7.1(vite@5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)) vitepress-plugin-llms: specifier: ^1.11.1 version: 1.12.0 @@ -244,7 +247,7 @@ importers: devDependencies: '@angular-eslint/builder': specifier: ~21.1.0 - version: 21.1.0(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) '@angular-eslint/eslint-plugin': specifier: ~21.1.0 version: 21.1.0(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) @@ -253,16 +256,16 @@ importers: version: 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) '@angular-eslint/schematics': specifier: ~21.1.0 - version: 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) + version: 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) '@angular-eslint/template-parser': specifier: ~21.1.0 version: 21.1.0(eslint@8.57.1)(typescript@5.9.3) '@angular/build': specifier: ^21.0.3 - version: 21.2.3(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(@angular/compiler@21.2.5)(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(@angular/platform-browser@21.2.5(@angular/common@21.2.5(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2)))(@types/node@24.10.1)(chokidar@5.0.0)(jiti@2.7.0)(less@4.2.0)(ng-packagr@21.2.1(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3))(postcss@8.5.8)(stylus@0.56.0)(terser@5.47.1)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.1)(yaml@2.8.0) + version: 21.2.3(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(@angular/compiler@21.2.5)(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(@angular/platform-browser@21.2.5(@angular/common@21.2.5(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2)))(@types/node@25.9.1)(chokidar@5.0.0)(jiti@2.7.0)(less@4.2.0)(ng-packagr@21.2.1(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3))(postcss@8.5.8)(stylus@0.56.0)(terser@5.47.1)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.1)(yaml@2.8.0) '@angular/cli': specifier: ^21.0.3 - version: 21.2.3(@types/node@24.10.1)(chokidar@5.0.0) + version: 21.2.3(@types/node@25.9.1)(chokidar@5.0.0) '@angular/common': specifier: ^21.0.0 version: 21.2.5(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(rxjs@7.8.2) @@ -292,10 +295,10 @@ importers: version: link:../../tools/build-helpers '@vitest/browser-playwright': specifier: ^4.0.12 - version: 4.1.1(playwright@1.58.2)(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + version: 4.1.1(playwright@1.58.2)(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) angular-eslint: specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript-eslint@8.57.1(eslint@8.57.1)(typescript@5.9.3))(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript-eslint@8.57.1(eslint@8.57.1)(typescript@5.9.3))(typescript@5.9.3) ng-packagr: specifier: ^21.0.0 version: 21.2.1(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3) @@ -326,7 +329,7 @@ importers: version: 6.9.1 astro: specifier: ^6.1.10 - version: 6.1.10(@types/node@24.10.1)(@vercel/blob@2.3.0)(db0@0.3.4)(ioredis@5.10.1)(jiti@2.7.0)(less@4.2.0)(rollup@4.60.3)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3)(yaml@2.8.0) + version: 6.1.10(@types/node@25.9.1)(@vercel/blob@2.3.0)(db0@0.3.4)(ioredis@5.10.1)(jiti@2.7.0)(less@4.2.0)(rollup@4.60.3)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3)(yaml@2.8.0) jest-serializer-html: specifier: ^7.1.0 version: 7.1.0 @@ -341,10 +344,10 @@ importers: version: 5.9.3 vite: specifier: ^7.3.2 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) vitest: specifier: ^4.1.1 - version: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + version: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) packages/icons: devDependencies: @@ -383,10 +386,10 @@ importers: version: 5.9.3 vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) vitest: specifier: ^4.0.12 - version: 4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 4.0.12(@types/debug@4.1.12)(@types/node@25.9.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) packages/lucide: devDependencies: @@ -419,7 +422,7 @@ importers: version: 5.9.3 vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) packages/lucide-preact: devDependencies: @@ -434,7 +437,7 @@ importers: version: link:../shared '@preact/preset-vite': specifier: ^2.10.2 - version: 2.10.2(@babel/core@7.29.0)(preact@10.28.2)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + version: 2.10.2(@babel/core@7.29.0)(preact@10.28.2)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@testing-library/jest-dom': specifier: ^6.8.0 version: 6.9.1 @@ -458,7 +461,7 @@ importers: version: 5.9.3 vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) packages/lucide-react: devDependencies: @@ -477,12 +480,15 @@ importers: '@testing-library/react': specifier: ^14.1.2 version: 14.3.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@types/node': + specifier: ^25.9.1 + version: 25.9.1 '@types/react': specifier: ^18.2.37 version: 18.3.27 '@vitejs/plugin-react': specifier: ^4.6.0 - version: 4.7.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + version: 4.7.0(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) jest-serializer-html: specifier: ^7.1.0 version: 7.1.0 @@ -506,7 +512,7 @@ importers: version: 5.9.3 vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) packages/lucide-react-native: devDependencies: @@ -533,7 +539,7 @@ importers: version: 18.3.27 '@vitejs/plugin-react': specifier: ^4.2.1 - version: 4.7.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + version: 4.7.0(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) jest-serializer-html: specifier: ^7.1.0 version: 7.1.0 @@ -560,7 +566,7 @@ importers: version: 5.9.3 vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) packages/lucide-solid: devDependencies: @@ -611,10 +617,10 @@ importers: version: 5.9.3 vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) vite-plugin-solid: specifier: ^2.11.6 - version: 2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + version: 2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) packages/lucide-static: devDependencies: @@ -647,7 +653,7 @@ importers: devDependencies: vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) packages/svelte: devDependencies: @@ -662,13 +668,13 @@ importers: version: 2.5.6(svelte@5.43.14)(typescript@5.9.3) '@sveltejs/vite-plugin-svelte': specifier: ^6.1.3 - version: 6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + version: 6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@testing-library/jest-dom': specifier: ^6.8.0 version: 6.9.1 '@testing-library/svelte': specifier: ^5.2.8 - version: 5.2.9(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + version: 5.2.9(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) '@tsconfig/svelte': specifier: ^5.0.4 version: 5.0.6 @@ -692,7 +698,7 @@ importers: version: 5.9.3 vite: specifier: ^6.3.6 - version: 6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) packages/vue: devDependencies: @@ -713,10 +719,10 @@ importers: version: 8.1.0(@vue/compiler-sfc@3.5.24)(vue@3.5.24(typescript@5.9.3)) '@vitejs/plugin-vue': specifier: ^4.6.2 - version: 4.6.2(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3)) + version: 4.6.2(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3)) '@vitejs/plugin-vue2': specifier: 2.2.0 - version: 2.2.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3)) + version: 2.2.0(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3)) '@vue/test-utils': specifier: 2.4.5 version: 2.4.5 @@ -731,7 +737,7 @@ importers: version: 5.9.3 vite: specifier: ^7.2.4 - version: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + version: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) vue: specifier: ^3.4.21 version: 3.5.24(typescript@5.9.3) @@ -5541,8 +5547,8 @@ packages: '@types/node@22.19.1': resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==} - '@types/node@24.10.1': - resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + '@types/node@25.9.1': + resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==} '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} @@ -6948,6 +6954,10 @@ packages: resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} engines: {node: '>=18'} + copy-file@11.1.0: + resolution: {integrity: sha512-X8XDzyvYaA6msMyAM575CUoygY5b44QzLcGRKsK3MFmXcOvQa518dNPLsKYwkYsn72g3EiW+LE0ytd/FlqWmyw==} + engines: {node: '>=18'} + core-js-compat@3.45.0: resolution: {integrity: sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==} @@ -6962,6 +6972,15 @@ packages: resolution: {integrity: sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==} engines: {node: '>=4'} + cpy-cli@7.0.0: + resolution: {integrity: sha512-uGCdhIxGfZcPXidCuT8w1jBknVXFx0un7NLjzqBZcdnkIWtLUnWMPk5TC37ceoVjwASLSNsRtTXXNTuFIyE2ng==} + engines: {node: '>=20'} + hasBin: true + + cpy@13.2.2: + resolution: {integrity: sha512-FEc7gmNSj4mDQCWNt3mByVn3qZX3EjMvggiqgCQPF1yJAFfyX6Ai/emN89w18TRp+QcFmdTLgX3hfNnIVgYHcw==} + engines: {node: '>=20'} + crc-32@1.2.2: resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} engines: {node: '>=0.8'} @@ -8929,6 +8948,10 @@ packages: jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + junk@4.0.1: + resolution: {integrity: sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ==} + engines: {node: '>=12.20'} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -9226,6 +9249,10 @@ packages: memoize-one@5.2.1: resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + meow@14.1.0: + resolution: {integrity: sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==} + engines: {node: '>=20'} + merge-anything@5.1.7: resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} engines: {node: '>=12.13'} @@ -9961,6 +9988,14 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + p-event@6.0.1: + resolution: {integrity: sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==} + engines: {node: '>=16.17'} + + p-filter@4.1.0: + resolution: {integrity: sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==} + engines: {node: '>=18'} + p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -9997,6 +10032,10 @@ packages: resolution: {integrity: sha512-dWgLE8AH0HjQ9fe74pUkKkvzzYT18Inp4zra3lKHnnwqGvcfcUBrvF2EAVX+envufDNBOzpPq/IBUONDbI7+3g==} engines: {node: '>=20'} + p-timeout@6.1.4: + resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} + engines: {node: '>=14.16'} + p-timeout@7.0.1: resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} engines: {node: '>=20'} @@ -11717,8 +11756,8 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@7.16.0: - resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} undici@5.28.5: resolution: {integrity: sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==} @@ -12906,11 +12945,11 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-eslint/builder@21.1.0(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3)': + '@angular-eslint/builder@21.1.0(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@angular-devkit/architect': 0.2102.3(chokidar@5.0.0) '@angular-devkit/core': 21.2.3(chokidar@5.0.0) - '@angular/cli': 21.2.3(@types/node@24.10.1)(chokidar@5.0.0) + '@angular/cli': 21.2.3(@types/node@25.9.1)(chokidar@5.0.0) eslint: 8.57.1 typescript: 5.9.3 transitivePeerDependencies: @@ -12939,13 +12978,13 @@ snapshots: ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 - '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3)': + '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@angular-devkit/core': 21.2.3(chokidar@5.0.0) '@angular-devkit/schematics': 21.2.3(chokidar@5.0.0) '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) - '@angular/cli': 21.2.3(@types/node@24.10.1)(chokidar@5.0.0) + '@angular/cli': 21.2.3(@types/node@25.9.1)(chokidar@5.0.0) ignore: 7.0.5 semver: 7.7.3 strip-json-comments: 3.1.1 @@ -12971,7 +13010,7 @@ snapshots: eslint: 8.57.1 typescript: 5.9.3 - '@angular/build@21.2.3(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(@angular/compiler@21.2.5)(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(@angular/platform-browser@21.2.5(@angular/common@21.2.5(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2)))(@types/node@24.10.1)(chokidar@5.0.0)(jiti@2.7.0)(less@4.2.0)(ng-packagr@21.2.1(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3))(postcss@8.5.8)(stylus@0.56.0)(terser@5.47.1)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.1)(yaml@2.8.0)': + '@angular/build@21.2.3(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(@angular/compiler@21.2.5)(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(@angular/platform-browser@21.2.5(@angular/common@21.2.5(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2)))(@types/node@25.9.1)(chokidar@5.0.0)(jiti@2.7.0)(less@4.2.0)(ng-packagr@21.2.1(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3))(postcss@8.5.8)(stylus@0.56.0)(terser@5.47.1)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.1)(yaml@2.8.0)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2102.3(chokidar@5.0.0) @@ -12980,8 +13019,8 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@24.10.1) - '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@inquirer/confirm': 5.1.21(@types/node@25.9.1) + '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) beasties: 0.4.1 browserslist: 4.28.1 esbuild: 0.27.3 @@ -13002,7 +13041,7 @@ snapshots: tslib: 2.8.1 typescript: 5.9.3 undici: 7.22.0 - vite: 7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) watchpack: 2.5.1 optionalDependencies: '@angular/core': 21.2.5(@angular/compiler@21.2.5)(rxjs@7.8.2) @@ -13011,7 +13050,7 @@ snapshots: lmdb: 3.5.1 ng-packagr: 21.2.1(@angular/compiler-cli@21.2.5(@angular/compiler@21.2.5)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3) postcss: 8.5.8 - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) transitivePeerDependencies: - '@types/node' - chokidar @@ -13025,13 +13064,13 @@ snapshots: - tsx - yaml - '@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0)': + '@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0)': dependencies: '@angular-devkit/architect': 0.2102.3(chokidar@5.0.0) '@angular-devkit/core': 21.2.3(chokidar@5.0.0) '@angular-devkit/schematics': 21.2.3(chokidar@5.0.0) - '@inquirer/prompts': 7.10.1(@types/node@24.10.1) - '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@24.10.1))(@types/node@24.10.1)(listr2@9.0.5) + '@inquirer/prompts': 7.10.1(@types/node@25.9.1) + '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.1))(@types/node@25.9.1)(listr2@9.0.5) '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) '@schematics/angular': 21.2.3(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 @@ -15838,128 +15877,128 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.2(@types/node@24.10.1)': + '@inquirer/checkbox@4.3.2(@types/node@25.9.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/confirm@5.1.21(@types/node@24.10.1)': + '@inquirer/confirm@5.1.21(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/core@10.3.2(@types/node@24.10.1)': + '@inquirer/core@10.3.2(@types/node@25.9.1)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/editor@4.2.23(@types/node@24.10.1)': + '@inquirer/editor@4.2.23(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/external-editor': 1.0.3(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/external-editor': 1.0.3(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/expand@4.0.23(@types/node@24.10.1)': + '@inquirer/expand@4.0.23(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/external-editor@1.0.3(@types/node@24.10.1)': + '@inquirer/external-editor@1.0.3(@types/node@25.9.1)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 '@inquirer/figures@1.0.15': {} - '@inquirer/input@4.3.1(@types/node@24.10.1)': + '@inquirer/input@4.3.1(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/number@3.0.23(@types/node@24.10.1)': + '@inquirer/number@3.0.23(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/password@4.0.23(@types/node@24.10.1)': + '@inquirer/password@4.0.23(@types/node@25.9.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/prompts@7.10.1(@types/node@24.10.1)': + '@inquirer/prompts@7.10.1(@types/node@25.9.1)': dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@24.10.1) - '@inquirer/confirm': 5.1.21(@types/node@24.10.1) - '@inquirer/editor': 4.2.23(@types/node@24.10.1) - '@inquirer/expand': 4.0.23(@types/node@24.10.1) - '@inquirer/input': 4.3.1(@types/node@24.10.1) - '@inquirer/number': 3.0.23(@types/node@24.10.1) - '@inquirer/password': 4.0.23(@types/node@24.10.1) - '@inquirer/rawlist': 4.1.11(@types/node@24.10.1) - '@inquirer/search': 3.2.2(@types/node@24.10.1) - '@inquirer/select': 4.4.2(@types/node@24.10.1) + '@inquirer/checkbox': 4.3.2(@types/node@25.9.1) + '@inquirer/confirm': 5.1.21(@types/node@25.9.1) + '@inquirer/editor': 4.2.23(@types/node@25.9.1) + '@inquirer/expand': 4.0.23(@types/node@25.9.1) + '@inquirer/input': 4.3.1(@types/node@25.9.1) + '@inquirer/number': 3.0.23(@types/node@25.9.1) + '@inquirer/password': 4.0.23(@types/node@25.9.1) + '@inquirer/rawlist': 4.1.11(@types/node@25.9.1) + '@inquirer/search': 3.2.2(@types/node@25.9.1) + '@inquirer/select': 4.4.2(@types/node@25.9.1) optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/rawlist@4.1.11(@types/node@24.10.1)': + '@inquirer/rawlist@4.1.11(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/search@3.2.2(@types/node@24.10.1)': + '@inquirer/search@3.2.2(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/select@4.4.2(@types/node@24.10.1)': + '@inquirer/select@4.4.2(@types/node@25.9.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/core': 10.3.2(@types/node@25.9.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 - '@inquirer/type@3.0.10(@types/node@24.10.1)': + '@inquirer/type@3.0.10(@types/node@25.9.1)': optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 '@ioredis/commands@1.5.1': {} @@ -16321,10 +16360,10 @@ snapshots: dependencies: '@lezer/common': 1.3.0 - '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@24.10.1))(@types/node@24.10.1)(listr2@9.0.5)': + '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.1))(@types/node@25.9.1)(listr2@9.0.5)': dependencies: - '@inquirer/prompts': 7.10.1(@types/node@24.10.1) - '@inquirer/type': 3.0.10(@types/node@24.10.1) + '@inquirer/prompts': 7.10.1(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) listr2: 9.0.5 transitivePeerDependencies: - '@types/node' @@ -16869,18 +16908,18 @@ snapshots: '@poppinss/exception@1.2.3': {} - '@preact/preset-vite@2.10.2(@babel/core@7.29.0)(preact@10.28.2)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@preact/preset-vite@2.10.2(@babel/core@7.29.0)(preact@10.28.2)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.29.0) - '@prefresh/vite': 2.4.1(preact@10.28.2)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@prefresh/vite': 2.4.1(preact@10.28.2)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@rollup/pluginutils': 4.2.1 babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.0) debug: 4.4.3 picocolors: 1.1.1 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vite-prerender-plugin: 0.5.12(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite-prerender-plugin: 0.5.12(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) transitivePeerDependencies: - preact - supports-color @@ -16893,7 +16932,7 @@ snapshots: '@prefresh/utils@1.2.0': {} - '@prefresh/vite@2.4.1(preact@10.28.2)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@prefresh/vite@2.4.1(preact@10.28.2)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: '@babel/core': 7.28.5 '@prefresh/babel-plugin': 0.5.0 @@ -16901,7 +16940,7 @@ snapshots: '@prefresh/utils': 1.2.0 '@rollup/pluginutils': 4.2.1 preact: 10.28.2 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) transitivePeerDependencies: - supports-color @@ -17703,22 +17742,22 @@ snapshots: transitivePeerDependencies: - typescript - '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)))(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)))(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) obug: 2.1.1 svelte: 5.43.14 - vite: 6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)))(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)))(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) deepmerge: 4.3.1 magic-string: 0.30.21 obug: 2.1.1 svelte: 5.43.14 - vite: 6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vitefu: 1.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vite: 6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vitefu: 1.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@swc/core-darwin-arm64@1.7.23': optional: true @@ -17851,13 +17890,13 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@testing-library/svelte@5.2.9(svelte@5.43.14)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': + '@testing-library/svelte@5.2.9(svelte@5.43.14)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': dependencies: '@testing-library/dom': 10.4.1 svelte: 5.43.14 optionalDependencies: - vite: 6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vite: 6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.5.24)(vue@3.5.24(typescript@5.9.3))': dependencies: @@ -17951,7 +17990,7 @@ snapshots: '@types/fs-extra@8.1.5': dependencies: - '@types/node': 24.10.1 + '@types/node': 22.19.1 '@types/glob@7.2.0': dependencies: @@ -18021,9 +18060,9 @@ snapshots: dependencies: undici-types: 6.21.0 - '@types/node@24.10.1': + '@types/node@25.9.1': dependencies: - undici-types: 7.16.0 + undici-types: 7.24.6 '@types/prop-types@15.7.15': {} @@ -18042,7 +18081,7 @@ snapshots: '@types/sax@1.2.7': dependencies: - '@types/node': 17.0.45 + '@types/node': 22.19.1 '@types/semver@7.7.1': {} @@ -18321,11 +18360,11 @@ snapshots: - rollup - supports-color - '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: - vite: 7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - '@vitejs/plugin-react@4.7.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@vitejs/plugin-react@4.7.0(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: '@babel/core': 7.28.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.0) @@ -18333,32 +18372,32 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue2@2.2.0(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3))': + '@vitejs/plugin-vue2@2.2.0(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3))': dependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) vue: 3.5.24(typescript@5.9.3) - '@vitejs/plugin-vue@4.6.2(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3))': + '@vitejs/plugin-vue@4.6.2(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vue@3.5.24(typescript@5.9.3))': dependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) vue: 3.5.24(typescript@5.9.3) - '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1))(vue@3.5.24(typescript@5.9.3))': + '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1))(vue@3.5.24(typescript@5.9.3))': dependencies: - vite: 5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1) + vite: 5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1) vue: 3.5.24(typescript@5.9.3) - '@vitest/browser-playwright@4.1.1(playwright@1.58.2)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': + '@vitest/browser-playwright@4.1.1(playwright@1.58.2)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': dependencies: - '@vitest/browser': 4.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) - '@vitest/mocker': 4.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/browser': 4.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + '@vitest/mocker': 4.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) playwright: 1.58.2 tinyrainbow: 3.0.3 - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) transitivePeerDependencies: - bufferutil - msw @@ -18366,26 +18405,26 @@ snapshots: - vite optional: true - '@vitest/browser-playwright@4.1.1(playwright@1.58.2)(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': + '@vitest/browser-playwright@4.1.1(playwright@1.58.2)(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': dependencies: - '@vitest/browser': 4.1.1(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) - '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/browser': 4.1.1(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) playwright: 1.58.2 tinyrainbow: 3.0.3 - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser-playwright@4.1.1(playwright@1.58.2)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': + '@vitest/browser-playwright@4.1.1(playwright@1.58.2)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': dependencies: - '@vitest/browser': 4.1.1(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) - '@vitest/mocker': 4.1.1(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/browser': 4.1.1(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + '@vitest/mocker': 4.1.1(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) playwright: 1.58.2 tinyrainbow: 3.0.3 - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) transitivePeerDependencies: - bufferutil - msw @@ -18393,16 +18432,16 @@ snapshots: - vite optional: true - '@vitest/browser@4.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': + '@vitest/browser@4.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/mocker': 4.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@vitest/utils': 4.1.1 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) ws: 8.20.1 transitivePeerDependencies: - bufferutil @@ -18411,16 +18450,16 @@ snapshots: - vite optional: true - '@vitest/browser@4.1.1(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': + '@vitest/browser@4.1.1(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@vitest/utils': 4.1.1 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) ws: 8.20.1 transitivePeerDependencies: - bufferutil @@ -18428,16 +18467,16 @@ snapshots: - utf-8-validate - vite - '@vitest/browser@4.1.1(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': + '@vitest/browser@4.1.1(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.1(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/mocker': 4.1.1(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@vitest/utils': 4.1.1 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vitest: 4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) ws: 8.20.1 transitivePeerDependencies: - bufferutil @@ -18459,7 +18498,7 @@ snapshots: magicast: 0.5.2 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vitest: 4.0.12(@types/debug@4.1.12)(@types/node@25.9.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) transitivePeerDependencies: - supports-color @@ -18481,38 +18520,38 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.12(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@vitest/mocker@4.0.12(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: '@vitest/spy': 4.0.12 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - '@vitest/mocker@4.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@vitest/mocker@4.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: '@vitest/spy': 4.1.1 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) optional: true - '@vitest/mocker@4.1.1(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@vitest/mocker@4.1.1(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: '@vitest/spy': 4.1.1 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - '@vitest/mocker@4.1.1(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': + '@vitest/mocker@4.1.1(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))': dependencies: '@vitest/spy': 4.1.1 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) '@vitest/pretty-format@4.0.12': dependencies: @@ -18558,7 +18597,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vitest: 4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vitest: 4.0.12(@types/debug@4.1.12)(@types/node@25.9.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) '@vitest/utils@4.0.12': dependencies: @@ -18783,7 +18822,7 @@ snapshots: agent-base@7.1.4: {} - ajv-cli@5.0.0(ts-node@10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@24.10.1)(typescript@5.9.3)): + ajv-cli@5.0.0(ts-node@10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@25.9.1)(typescript@5.9.3)): dependencies: ajv: 8.17.1 fast-json-patch: 3.1.1 @@ -18793,7 +18832,7 @@ snapshots: json5: 2.2.3 minimist: 1.2.8 optionalDependencies: - ts-node: 10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@24.10.1)(typescript@5.9.3) + ts-node: 10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@25.9.1)(typescript@5.9.3) ajv-formats@3.0.1(ajv@8.17.1): optionalDependencies: @@ -18858,16 +18897,16 @@ snapshots: '@algolia/requester-fetch': 5.48.1 '@algolia/requester-node-http': 5.48.1 - angular-eslint@21.1.0(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript-eslint@8.57.1(eslint@8.57.1)(typescript@5.9.3))(typescript@5.9.3): + angular-eslint@21.1.0(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript-eslint@8.57.1(eslint@8.57.1)(typescript@5.9.3))(typescript@5.9.3): dependencies: '@angular-devkit/core': 21.2.3(chokidar@5.0.0) '@angular-devkit/schematics': 21.2.3(chokidar@5.0.0) - '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) + '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) - '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@angular/cli@21.2.3(@types/node@24.10.1)(chokidar@5.0.0))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) + '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@8.57.1)(typescript@5.9.3))(@angular/cli@21.2.3(@types/node@25.9.1)(chokidar@5.0.0))(@typescript-eslint/types@8.57.1)(@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3))(chokidar@5.0.0)(eslint@8.57.1)(typescript@5.9.3) '@angular-eslint/template-parser': 21.1.0(eslint@8.57.1)(typescript@5.9.3) - '@angular/cli': 21.2.3(@types/node@24.10.1)(chokidar@5.0.0) + '@angular/cli': 21.2.3(@types/node@25.9.1)(chokidar@5.0.0) '@typescript-eslint/types': 8.57.1 '@typescript-eslint/utils': 8.57.1(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 @@ -19036,7 +19075,7 @@ snapshots: estree-walker: 3.0.3 js-tokens: 10.0.0 - astro@6.1.10(@types/node@24.10.1)(@vercel/blob@2.3.0)(db0@0.3.4)(ioredis@5.10.1)(jiti@2.7.0)(less@4.2.0)(rollup@4.60.3)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3)(yaml@2.8.0): + astro@6.1.10(@types/node@25.9.1)(@vercel/blob@2.3.0)(db0@0.3.4)(ioredis@5.10.1)(jiti@2.7.0)(less@4.2.0)(rollup@4.60.3)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3)(yaml@2.8.0): dependencies: '@astrojs/compiler': 3.0.1 '@astrojs/internal-helpers': 0.9.0 @@ -19087,8 +19126,8 @@ snapshots: unist-util-visit: 5.1.0 unstorage: 1.17.5(@vercel/blob@2.3.0)(db0@0.3.4)(ioredis@5.10.1) vfile: 6.0.3 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vitefu: 1.1.3(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vitefu: 1.1.3(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) xxhash-wasm: 1.1.0 yargs-parser: 22.0.0 zod: 4.4.3 @@ -19851,6 +19890,11 @@ snapshots: dependencies: is-what: 5.5.0 + copy-file@11.1.0: + dependencies: + graceful-fs: 4.2.11 + p-event: 6.0.1 + core-js-compat@3.45.0: dependencies: browserslist: 4.25.1 @@ -19869,6 +19913,21 @@ snapshots: js-yaml: 3.14.2 parse-json: 4.0.0 + cpy-cli@7.0.0: + dependencies: + cpy: 13.2.2 + globby: 16.2.0 + meow: 14.1.0 + + cpy@13.2.2: + dependencies: + copy-file: 11.1.0 + globby: 16.2.0 + junk: 4.0.1 + micromatch: 4.0.8 + p-filter: 4.1.0 + p-map: 7.0.4 + crc-32@1.2.2: {} crc32-stream@6.0.0: @@ -21940,7 +21999,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.10.1 + '@types/node': 22.19.1 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -22207,6 +22266,8 @@ snapshots: readable-stream: 2.3.8 setimmediate: 1.0.5 + junk@4.0.1: {} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -22703,6 +22764,8 @@ snapshots: memoize-one@5.2.1: {} + meow@14.1.0: {} + merge-anything@5.1.7: dependencies: is-what: 4.1.15 @@ -23845,6 +23908,14 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + p-event@6.0.1: + dependencies: + p-timeout: 6.1.4 + + p-filter@4.1.0: + dependencies: + p-map: 7.0.4 + p-limit@2.3.0: dependencies: p-try: 2.2.0 @@ -23881,6 +23952,8 @@ snapshots: eventemitter3: 5.0.4 p-timeout: 7.0.1 + p-timeout@6.1.4: {} + p-timeout@7.0.1: {} p-try@2.2.0: {} @@ -25826,14 +25899,14 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-node@10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@24.10.1)(typescript@5.9.3): + ts-node@10.9.2(@swc/core@1.7.23(@swc/helpers@0.5.17))(@types/node@25.9.1)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.12 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 24.10.1 + '@types/node': 25.9.1 acorn: 8.16.0 acorn-walk: 8.3.5 arg: 4.1.3 @@ -25988,7 +26061,7 @@ snapshots: undici-types@6.21.0: {} - undici-types@7.16.0: {} + undici-types@7.24.6: {} undici@5.28.5: dependencies: @@ -26263,7 +26336,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): dependencies: '@babel/core': 7.28.5 '@types/babel__core': 7.20.5 @@ -26271,14 +26344,14 @@ snapshots: merge-anything: 5.1.7 solid-js: 1.9.10 solid-refresh: 0.6.3(solid-js@1.9.10) - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vitefu: 1.0.5(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vitefu: 1.0.5(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) optionalDependencies: '@testing-library/jest-dom': 6.9.1 transitivePeerDependencies: - supports-color - vite-prerender-plugin@0.5.12(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vite-prerender-plugin@0.5.12(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): dependencies: kolorist: 1.8.0 magic-string: 0.30.21 @@ -26286,22 +26359,22 @@ snapshots: simple-code-frame: 1.3.0 source-map: 0.7.6 stack-trace: 1.0.0-pre2 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vite@5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1): + vite@5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1): dependencies: esbuild: 0.21.5 postcss: 8.5.6 rollup: 4.60.3 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 fsevents: 2.3.3 less: 4.2.0 sass: 1.98.0 stylus: 0.56.0 terser: 5.47.1 - vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): + vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.4) @@ -26310,7 +26383,7 @@ snapshots: rollup: 4.60.1 tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 fsevents: 2.3.3 jiti: 2.7.0 less: 4.2.0 @@ -26319,7 +26392,7 @@ snapshots: terser: 5.47.1 yaml: 2.8.0 - vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): + vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.3) @@ -26328,7 +26401,7 @@ snapshots: rollup: 4.60.3 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 fsevents: 2.3.3 jiti: 2.7.0 less: 4.2.0 @@ -26337,7 +26410,7 @@ snapshots: terser: 5.47.1 yaml: 2.8.0 - vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): + vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.4) @@ -26346,7 +26419,7 @@ snapshots: rollup: 4.60.1 tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.9.1 fsevents: 2.3.3 jiti: 2.7.0 less: 4.2.0 @@ -26355,25 +26428,25 @@ snapshots: terser: 5.47.1 yaml: 2.8.0 - vitefu@1.0.5(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vitefu@1.0.5(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): optionalDependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vitefu@1.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vitefu@1.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): optionalDependencies: - vite: 6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vitefu@1.1.3(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vitefu@1.1.3(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): optionalDependencies: - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) - vitepress-plugin-group-icons@1.7.1(vite@5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)): + vitepress-plugin-group-icons@1.7.1(vite@5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)): dependencies: '@iconify-json/logos': 1.2.10 '@iconify-json/vscode-icons': 1.2.45 '@iconify/utils': 3.1.0 optionalDependencies: - vite: 5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1) + vite: 5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1) vitepress-plugin-llms@1.12.0: dependencies: @@ -26394,7 +26467,7 @@ snapshots: transitivePeerDependencies: - supports-color - vitepress@1.6.4(@algolia/client-search@5.48.1)(@types/node@24.10.1)(@types/react@18.3.27)(axios@1.13.2)(fuse.js@7.1.0)(less@4.2.0)(postcss@8.5.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.98.0)(search-insights@2.8.2)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3): + vitepress@1.6.4(@algolia/client-search@5.48.1)(@types/node@25.9.1)(@types/react@18.3.27)(axios@1.13.2)(fuse.js@7.1.0)(less@4.2.0)(postcss@8.5.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.98.0)(search-insights@2.8.2)(stylus@0.56.0)(terser@5.47.1)(typescript@5.9.3): dependencies: '@docsearch/css': 3.8.2 '@docsearch/js': 3.8.2(@algolia/client-search@5.48.1)(@types/react@18.3.27)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(search-insights@2.8.2) @@ -26403,7 +26476,7 @@ snapshots: '@shikijs/transformers': 2.5.0 '@shikijs/types': 2.5.0 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1))(vue@3.5.24(typescript@5.9.3)) + '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1))(vue@3.5.24(typescript@5.9.3)) '@vue/devtools-api': 7.7.9 '@vue/shared': 3.5.24 '@vueuse/core': 12.8.2(typescript@5.9.3) @@ -26412,7 +26485,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.2.0 shiki: 2.5.0 - vite: 5.4.21(@types/node@24.10.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1) + vite: 5.4.21(@types/node@25.9.1)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1) vue: 3.5.24(typescript@5.9.3) optionalDependencies: postcss: 8.5.9 @@ -26443,10 +26516,10 @@ snapshots: - typescript - universal-cookie - vitest@4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): + vitest@4.0.12(@types/debug@4.1.12)(@types/node@25.9.1)(@vitest/ui@4.0.12)(jiti@2.7.0)(jsdom@27.4.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0): dependencies: '@vitest/expect': 4.0.12 - '@vitest/mocker': 4.0.12(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/mocker': 4.0.12(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@vitest/pretty-format': 4.0.12 '@vitest/runner': 4.0.12 '@vitest/snapshot': 4.0.12 @@ -26463,11 +26536,11 @@ snapshots: tinyexec: 0.3.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 24.10.1 + '@types/node': 25.9.1 '@vitest/ui': 4.0.12(vitest@4.0.12) jsdom: 27.4.0 transitivePeerDependencies: @@ -26484,10 +26557,10 @@ snapshots: - tsx - yaml - vitest@4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vitest@4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@20.0.3)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): dependencies: '@vitest/expect': 4.1.1 - '@vitest/mocker': 4.1.1(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/mocker': 4.1.1(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@vitest/pretty-format': 4.1.1 '@vitest/runner': 4.1.1 '@vitest/snapshot': 4.1.1 @@ -26504,21 +26577,21 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.10.1 - '@vitest/browser-playwright': 4.1.1(playwright@1.58.2)(vite@6.4.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + '@types/node': 25.9.1 + '@vitest/browser-playwright': 4.1.1(playwright@1.58.2)(vite@6.4.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) '@vitest/ui': 4.0.12(vitest@4.0.12) jsdom: 20.0.3 transitivePeerDependencies: - msw optional: true - vitest@4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vitest@4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): dependencies: '@vitest/expect': 4.1.1 - '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@vitest/pretty-format': 4.1.1 '@vitest/runner': 4.1.1 '@vitest/snapshot': 4.1.1 @@ -26535,20 +26608,20 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.10.1 - '@vitest/browser-playwright': 4.1.1(playwright@1.58.2)(vite@7.3.1(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + '@types/node': 25.9.1 + '@vitest/browser-playwright': 4.1.1(playwright@1.58.2)(vite@7.3.1(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.97.3)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) '@vitest/ui': 4.0.12(vitest@4.0.12) jsdom: 27.4.0 transitivePeerDependencies: - msw - vitest@4.1.1(@types/node@24.10.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): + vitest@4.1.1(@types/node@25.9.1)(@vitest/browser-playwright@4.1.1)(@vitest/ui@4.0.12(vitest@4.0.12))(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)): dependencies: '@vitest/expect': 4.1.1 - '@vitest/mocker': 4.1.1(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) + '@vitest/mocker': 4.1.1(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0)) '@vitest/pretty-format': 4.1.1 '@vitest/runner': 4.1.1 '@vitest/snapshot': 4.1.1 @@ -26565,11 +26638,11 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) + vite: 7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.10.1 - '@vitest/browser-playwright': 4.1.1(playwright@1.58.2)(vite@7.3.2(@types/node@24.10.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) + '@types/node': 25.9.1 + '@vitest/browser-playwright': 4.1.1(playwright@1.58.2)(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(less@4.2.0)(sass@1.98.0)(stylus@0.56.0)(terser@5.47.1)(yaml@2.8.0))(vitest@4.1.1) '@vitest/ui': 4.0.12(vitest@4.0.12) jsdom: 27.4.0 transitivePeerDependencies: From 3041de00a1715b3f942ba05837cae6312a51ff4e Mon Sep 17 00:00:00 2001 From: Karsa Date: Tue, 14 Jul 2026 17:05:38 +0200 Subject: [PATCH 7/8] chore(icons): revert git hook changes made to icons --- icons/ad.svg | 2 +- icons/arrow-down-0-1.json | 3 +-- icons/arrow-down-1-0.json | 3 +-- icons/arrow-down-a-z.json | 3 +-- icons/arrow-down-z-a.json | 3 +-- icons/arrow-up-0-1.json | 3 +-- icons/arrow-up-1-0.json | 3 +-- icons/arrow-up-a-z.json | 3 +-- icons/arrow-up-z-a.json | 3 +-- icons/axis-3d.json | 3 +-- icons/bone-fracture.svg | 2 +- icons/database-arrow-down.svg | 4 ++-- icons/database-arrow-up.svg | 4 ++-- icons/database-check.svg | 4 ++-- icons/database-minus.svg | 4 ++-- icons/database-plus.svg | 4 ++-- icons/database-x.svg | 4 ++-- icons/file-axis-3d.json | 3 +-- icons/grid-2x2-check.json | 3 +-- icons/grid-2x2-plus.json | 3 +-- icons/grid-2x2-x.json | 3 +-- icons/grid-2x2.json | 3 +-- icons/grid-3x3.json | 3 +-- icons/move-3d.json | 3 +-- icons/paper-bag.svg | 4 ++-- icons/rotate-3d.json | 3 +-- icons/save-pen.svg | 8 ++++---- icons/scale-3d.json | 3 +-- icons/square-arrow-down-left.svg | 2 +- icons/square-arrow-down-right.svg | 2 +- icons/square-arrow-up-left.svg | 2 +- icons/square-arrow-up-right.svg | 2 +- icons/tag-plus.svg | 2 +- icons/tag-x.svg | 2 +- icons/ungroup.svg | 4 ++-- icons/wallet-cards.svg | 2 +- 36 files changed, 47 insertions(+), 65 deletions(-) diff --git a/icons/ad.svg b/icons/ad.svg index b3dac7c3a..6b56aa9c0 100644 --- a/icons/ad.svg +++ b/icons/ad.svg @@ -12,5 +12,5 @@ - + diff --git a/icons/arrow-down-0-1.json b/icons/arrow-down-0-1.json index 54e722845..a2f90b046 100644 --- a/icons/arrow-down-0-1.json +++ b/icons/arrow-down-0-1.json @@ -26,7 +26,6 @@ "name": "arrow-down-01", "deprecationReason": "alias.name", "deprecated": true - }, - "arrow-down-01" + } ] } diff --git a/icons/arrow-down-1-0.json b/icons/arrow-down-1-0.json index 58b32af01..9930a1091 100644 --- a/icons/arrow-down-1-0.json +++ b/icons/arrow-down-1-0.json @@ -26,7 +26,6 @@ "name": "arrow-down-10", "deprecationReason": "alias.name", "deprecated": true - }, - "arrow-down-10" + } ] } diff --git a/icons/arrow-down-a-z.json b/icons/arrow-down-a-z.json index 00c9ec8ad..d29a21d5a 100644 --- a/icons/arrow-down-a-z.json +++ b/icons/arrow-down-a-z.json @@ -26,7 +26,6 @@ "name": "arrow-down-az", "deprecationReason": "alias.name", "deprecated": true - }, - "arrow-down-az" + } ] } diff --git a/icons/arrow-down-z-a.json b/icons/arrow-down-z-a.json index d0dfcb1d2..e34e3634d 100644 --- a/icons/arrow-down-z-a.json +++ b/icons/arrow-down-z-a.json @@ -27,7 +27,6 @@ "name": "arrow-down-za", "deprecationReason": "alias.name", "deprecated": true - }, - "arrow-down-za" + } ] } diff --git a/icons/arrow-up-0-1.json b/icons/arrow-up-0-1.json index 5510e288a..786bf4e70 100644 --- a/icons/arrow-up-0-1.json +++ b/icons/arrow-up-0-1.json @@ -26,7 +26,6 @@ "name": "arrow-up-01", "deprecationReason": "alias.name", "deprecated": true - }, - "arrow-up-01" + } ] } diff --git a/icons/arrow-up-1-0.json b/icons/arrow-up-1-0.json index 15f754e12..328c2c5bd 100644 --- a/icons/arrow-up-1-0.json +++ b/icons/arrow-up-1-0.json @@ -26,7 +26,6 @@ "name": "arrow-up-10", "deprecationReason": "alias.name", "deprecated": true - }, - "arrow-up-10" + } ] } diff --git a/icons/arrow-up-a-z.json b/icons/arrow-up-a-z.json index 340081226..1dcff0f8e 100644 --- a/icons/arrow-up-a-z.json +++ b/icons/arrow-up-a-z.json @@ -26,7 +26,6 @@ "name": "arrow-up-az", "deprecationReason": "alias.name", "deprecated": true - }, - "arrow-up-az" + } ] } diff --git a/icons/arrow-up-z-a.json b/icons/arrow-up-z-a.json index 3277961fc..53799ce63 100644 --- a/icons/arrow-up-z-a.json +++ b/icons/arrow-up-z-a.json @@ -27,7 +27,6 @@ "name": "arrow-up-za", "deprecationReason": "alias.name", "deprecated": true - }, - "arrow-up-za" + } ] } diff --git a/icons/axis-3d.json b/icons/axis-3d.json index 7a115d009..1d95cd342 100644 --- a/icons/axis-3d.json +++ b/icons/axis-3d.json @@ -17,7 +17,6 @@ "name": "axis-3-d", "deprecationReason": "alias.name", "deprecated": true - }, - "axis-3-d" + } ] } diff --git a/icons/bone-fracture.svg b/icons/bone-fracture.svg index cc7f42ee8..fc839a13a 100644 --- a/icons/bone-fracture.svg +++ b/icons/bone-fracture.svg @@ -15,4 +15,4 @@ - + \ No newline at end of file diff --git a/icons/database-arrow-down.svg b/icons/database-arrow-down.svg index 1ef27e3b1..271726fd9 100644 --- a/icons/database-arrow-down.svg +++ b/icons/database-arrow-down.svg @@ -12,7 +12,7 @@ - - + + diff --git a/icons/database-arrow-up.svg b/icons/database-arrow-up.svg index e9eb5f1af..210103d33 100644 --- a/icons/database-arrow-up.svg +++ b/icons/database-arrow-up.svg @@ -12,7 +12,7 @@ - - + + diff --git a/icons/database-check.svg b/icons/database-check.svg index 0aeed5eee..7284b3413 100644 --- a/icons/database-check.svg +++ b/icons/database-check.svg @@ -11,7 +11,7 @@ > - - + + diff --git a/icons/database-minus.svg b/icons/database-minus.svg index 9ff060cff..f56d2beb6 100644 --- a/icons/database-minus.svg +++ b/icons/database-minus.svg @@ -11,7 +11,7 @@ > - - + + diff --git a/icons/database-plus.svg b/icons/database-plus.svg index 35c0e7c67..0c377e07f 100644 --- a/icons/database-plus.svg +++ b/icons/database-plus.svg @@ -12,7 +12,7 @@ - - + + diff --git a/icons/database-x.svg b/icons/database-x.svg index 07c1d5757..ab46918b2 100644 --- a/icons/database-x.svg +++ b/icons/database-x.svg @@ -13,7 +13,7 @@ - - + + diff --git a/icons/file-axis-3d.json b/icons/file-axis-3d.json index 7ef73fa2d..1f512a43c 100644 --- a/icons/file-axis-3d.json +++ b/icons/file-axis-3d.json @@ -20,7 +20,6 @@ "name": "file-axis-3-d", "deprecationReason": "alias.name", "deprecated": true - }, - "file-axis-3-d" + } ] } diff --git a/icons/grid-2x2-check.json b/icons/grid-2x2-check.json index 71b509e58..a89d0e213 100644 --- a/icons/grid-2x2-check.json +++ b/icons/grid-2x2-check.json @@ -33,7 +33,6 @@ "name": "grid-2-x-2-check", "deprecationReason": "alias.name", "deprecated": true - }, - "grid-2-x-2-check" + } ] } diff --git a/icons/grid-2x2-plus.json b/icons/grid-2x2-plus.json index 8bdc4e8a0..32835a7dd 100644 --- a/icons/grid-2x2-plus.json +++ b/icons/grid-2x2-plus.json @@ -34,7 +34,6 @@ "name": "grid-2-x-2-plus", "deprecationReason": "alias.name", "deprecated": true - }, - "grid-2-x-2-plus" + } ] } diff --git a/icons/grid-2x2-x.json b/icons/grid-2x2-x.json index b5f68e433..c84d666cb 100644 --- a/icons/grid-2x2-x.json +++ b/icons/grid-2x2-x.json @@ -33,7 +33,6 @@ "name": "grid-2-x-2-x", "deprecationReason": "alias.name", "deprecated": true - }, - "grid-2-x-2-x" + } ] } diff --git a/icons/grid-2x2.json b/icons/grid-2x2.json index f9d737ab5..2500f7924 100644 --- a/icons/grid-2x2.json +++ b/icons/grid-2x2.json @@ -34,7 +34,6 @@ "name": "grid-2-x-2", "deprecationReason": "alias.name", "deprecated": true - }, - "grid-2-x-2" + } ] } diff --git a/icons/grid-3x3.json b/icons/grid-3x3.json index e632819c2..76b11cc7b 100644 --- a/icons/grid-3x3.json +++ b/icons/grid-3x3.json @@ -28,7 +28,6 @@ "name": "grid-3-x-3", "deprecationReason": "alias.name", "deprecated": true - }, - "grid-3-x-3" + } ] } diff --git a/icons/move-3d.json b/icons/move-3d.json index c4f3b7dad..a969e0e70 100644 --- a/icons/move-3d.json +++ b/icons/move-3d.json @@ -21,7 +21,6 @@ "name": "move-3-d", "deprecationReason": "alias.name", "deprecated": true - }, - "move-3-d" + } ] } diff --git a/icons/paper-bag.svg b/icons/paper-bag.svg index 3bb264eb6..42fa1d6d4 100644 --- a/icons/paper-bag.svg +++ b/icons/paper-bag.svg @@ -9,6 +9,6 @@ stroke-linecap="round" stroke-linejoin="round" > - - + + diff --git a/icons/rotate-3d.json b/icons/rotate-3d.json index df388f804..9a2ca069d 100644 --- a/icons/rotate-3d.json +++ b/icons/rotate-3d.json @@ -19,7 +19,6 @@ "name": "rotate-3-d", "deprecationReason": "alias.name", "deprecated": true - }, - "rotate-3-d" + } ] } diff --git a/icons/save-pen.svg b/icons/save-pen.svg index 6c00d4339..08d1b114c 100644 --- a/icons/save-pen.svg +++ b/icons/save-pen.svg @@ -9,8 +9,8 @@ stroke-linecap="round" stroke-linejoin="round" > - - - - + + + + diff --git a/icons/scale-3d.json b/icons/scale-3d.json index a74b38005..4906533f2 100644 --- a/icons/scale-3d.json +++ b/icons/scale-3d.json @@ -20,7 +20,6 @@ "name": "scale-3-d", "deprecationReason": "alias.name", "deprecated": true - }, - "scale-3-d" + } ] } diff --git a/icons/square-arrow-down-left.svg b/icons/square-arrow-down-left.svg index 7d7693ed9..31eeb9bad 100644 --- a/icons/square-arrow-down-left.svg +++ b/icons/square-arrow-down-left.svg @@ -11,5 +11,5 @@ > - + diff --git a/icons/square-arrow-down-right.svg b/icons/square-arrow-down-right.svg index 936413ecf..ac131302b 100644 --- a/icons/square-arrow-down-right.svg +++ b/icons/square-arrow-down-right.svg @@ -11,5 +11,5 @@ > - + diff --git a/icons/square-arrow-up-left.svg b/icons/square-arrow-up-left.svg index 27bc4ed00..f93c447cf 100644 --- a/icons/square-arrow-up-left.svg +++ b/icons/square-arrow-up-left.svg @@ -11,5 +11,5 @@ > - + diff --git a/icons/square-arrow-up-right.svg b/icons/square-arrow-up-right.svg index cf352fa99..d68d22524 100644 --- a/icons/square-arrow-up-right.svg +++ b/icons/square-arrow-up-right.svg @@ -11,5 +11,5 @@ > - + diff --git a/icons/tag-plus.svg b/icons/tag-plus.svg index d4f73e847..ce3cb0b53 100644 --- a/icons/tag-plus.svg +++ b/icons/tag-plus.svg @@ -12,5 +12,5 @@ - + diff --git a/icons/tag-x.svg b/icons/tag-x.svg index 37cf6d706..3c5e5df43 100644 --- a/icons/tag-x.svg +++ b/icons/tag-x.svg @@ -12,5 +12,5 @@ - + diff --git a/icons/ungroup.svg b/icons/ungroup.svg index d7f2780a3..cb11f0b3b 100644 --- a/icons/ungroup.svg +++ b/icons/ungroup.svg @@ -9,6 +9,6 @@ stroke-linecap="round" stroke-linejoin="round" > - - + + diff --git a/icons/wallet-cards.svg b/icons/wallet-cards.svg index 9e89da51c..5cc2f0005 100644 --- a/icons/wallet-cards.svg +++ b/icons/wallet-cards.svg @@ -11,5 +11,5 @@ > - + From 78ef931b509e02c6c6db56a92ae6a4bf48d999ad Mon Sep 17 00:00:00 2001 From: Karsa Date: Tue, 14 Jul 2026 17:40:34 +0200 Subject: [PATCH 8/8] fix(packages/astro,react-native,vue): fix iconNode type fix(packages/vue): fix hasA11yProp --- packages/astro/src/types.ts | 2 +- packages/lucide-react-native/src/createLucideIcon.ts | 10 ++++++---- packages/shared/src/index.ts | 4 ++-- packages/vue/src/Icon.ts | 12 +++++++----- packages/vue/tests/__snapshots__/Icon.spec.ts.snap | 2 ++ .../vue/tests/__snapshots__/context.spec.ts.snap | 1 + .../vue/tests/__snapshots__/lucide-vue.spec.ts.snap | 6 ++++-- 7 files changed, 23 insertions(+), 14 deletions(-) diff --git a/packages/astro/src/types.ts b/packages/astro/src/types.ts index f8ad56c01..d23968290 100644 --- a/packages/astro/src/types.ts +++ b/packages/astro/src/types.ts @@ -28,7 +28,7 @@ export type IconProps = LucideProps & } | { icon?: never; - iconNode: LucideIconNode; + iconNode: LucideIconNode[]; } ); diff --git a/packages/lucide-react-native/src/createLucideIcon.ts b/packages/lucide-react-native/src/createLucideIcon.ts index 058deba06..0136b6a6b 100644 --- a/packages/lucide-react-native/src/createLucideIcon.ts +++ b/packages/lucide-react-native/src/createLucideIcon.ts @@ -21,18 +21,18 @@ function createLucideIcon(icon: LucideIconData): LucideIcon; */ function createLucideIcon( iconName: string, - iconNode: LucideIconNode, + iconNode: LucideIconNode[], aliases?: string[], ): LucideIcon; function createLucideIcon( iconDataOrName: LucideIconData | string, - iconNode?: LucideIconNode, + iconNode?: LucideIconNode[], aliases: string[] = [], ): LucideIcon { const iconData = typeof iconDataOrName === 'string' - ? toLucideIconData(iconDataOrName, iconNode as LucideIconNode, aliases) + ? toLucideIconData(iconDataOrName, iconNode as LucideIconNode[], aliases) : iconDataOrName; const Component = forwardRef((props, ref) => @@ -43,7 +43,9 @@ function createLucideIcon( }), ); - Component.displayName = toPascalCase(iconData.name); + if (iconData.name) { + Component.displayName = toPascalCase(iconData.name); + } return Component; } diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 26cb8770b..e0bd3a2f1 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,10 +1,10 @@ export { default as buildLucideDataUri } from './build/buildLucideDataUri'; export { default as buildLucideIconElement } from './build/buildLucideIconElement'; export { default as buildLucideIconNode } from './build/buildLucideIconNode'; -export { default as buildLucideIconForReact } from './build/buildLucideIconForReact.ts'; +export { default as buildLucideIconForReact } from './build/buildLucideIconForReact'; export { default as buildLucideSvg } from './build/buildLucideSvg'; export { default as defaultAttributes } from './build/defaultAttributes'; -export { default as defaultReactAttributes } from './build/defaultReactAttributes.ts'; +export { default as defaultReactAttributes } from './build/defaultReactAttributes'; export type { LucideBuildParams, LucideIconData, LucideIconNode, SVGProps } from './build/types'; export * from './utils/hasA11yProp'; export * from './utils/isEmptyString'; diff --git a/packages/vue/src/Icon.ts b/packages/vue/src/Icon.ts index 9841b884d..99c85a93a 100644 --- a/packages/vue/src/Icon.ts +++ b/packages/vue/src/Icon.ts @@ -13,18 +13,18 @@ type IconProps = | { icon: LucideIconData; iconNode?: never; - 'icon-node': never; + 'icon-node'?: never; name?: never; } | { icon?: never; iconNode: LucideIconNode[]; - 'icon-node': never; + 'icon-node'?: never; name: string; } | { icon?: never; - iconNode: never; + iconNode?: never; 'icon-node': LucideIconNode[]; name: string; }; @@ -80,6 +80,8 @@ const Icon: FunctionalComponent = ( const { class: propsClass, ...restProps } = props; + const defaultSlot = slots.default?.(); + const [, svgAttributes, builtIconNode = []] = buildLucideIconNode(icon, { color: color ?? contextColor, width: width ?? size ?? contextSize, @@ -88,13 +90,13 @@ const Icon: FunctionalComponent = ( absoluteStrokeWidth: isAbsoluteStrokeWidth, nonScalingStroke: isNonScalingStroke, className: mergeClasses(contextClass, propsClass), - hasA11yProp: !!slots || hasA11yProp(restProps), + hasA11yProp: (defaultSlot != null && defaultSlot.length > 0) || hasA11yProp(restProps), attributes: restProps, }); return h('svg', svgAttributes, [ ...builtIconNode.map((child) => h(...child)), - ...(slots.default ? [slots.default()] : []), + ...(defaultSlot ?? []), ]); }; diff --git a/packages/vue/tests/__snapshots__/Icon.spec.ts.snap b/packages/vue/tests/__snapshots__/Icon.spec.ts.snap index e7b15be0c..9257657d8 100644 --- a/packages/vue/tests/__snapshots__/Icon.spec.ts.snap +++ b/packages/vue/tests/__snapshots__/Icon.spec.ts.snap @@ -2,6 +2,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = `
`; @@ -161,6 +162,7 @@ exports[`Using lucide icon components > should pass children to the icon slot 1` exports[`Using lucide icon components > should render an component 1`] = `