Compare commits

..

1 Commits

Author SHA1 Message Date
Eric Fennis
1075461aab Add new lucide angular package 2025-12-13 20:12:09 +01:00
47 changed files with 4260 additions and 2637 deletions

View File

@@ -11,9 +11,6 @@ permissions:
id-token: write # Required for OIDC
contents: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
create-release:
if: github.repository == 'lucide-icons/lucide' && startsWith(github.event.head_commit.message, 'feat(icons)')

View File

@@ -22,9 +22,6 @@ permissions:
id-token: write # Required for OIDC
contents: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
pre-release:
if: github.repository == 'lucide-icons/lucide' && contains('["ericfennis", "karsa-mistmere", "jguddas"]', github.actor)
@@ -138,8 +135,11 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Outline svg Icons
run: pnpm build:outline-icons
- name: Create font in ./lucide-font
run: pnpm build:font --saveCodePoints
run: pnpm build:font
- name: 'Upload to Artifacts'
uses: actions/upload-artifact@v4

1
.gitignore vendored
View File

@@ -14,7 +14,6 @@ coverage
stats
*.log
outlined
lucide-font
packages/**/src/icons/*.js
packages/**/src/icons/*.ts
packages/**/src/icons/*.tsx

View File

@@ -70,7 +70,7 @@ const value = computed({
color: var(--vp-c-text-2);
padding: 3px 8px 3px 3px;
height: auto;
font-size: 13px;
font-size: 14px;
text-align: left;
border: 1px solid transparent;
cursor: text;
@@ -90,7 +90,7 @@ const value = computed({
border: none;
background: transparent;
color: var(--vp-c-text-1);
font-size: 13px;
font-size: 14px;
text-align: left;
border-radius: 8px;
cursor: text;

View File

@@ -102,16 +102,10 @@ The example below imports all ES Modules, so exercise caution when using it. Imp
### Icon Component Example
```tsx
import * as icons from 'lucide-react-native/icons';
```jsx
import { icons } from 'lucide-react-native';
interface IconProps {
name: keyof typeof icons;
color?: string;
size?: number;
}
const Icon = ({ name, color, size }: IconProps) => {
const Icon = ({ name, color, size }) => {
const LucideIcon = icons[name];
return <LucideIcon color={color} size={size} />;
@@ -122,11 +116,11 @@ export default Icon;
#### Using the Icon Component
```tsx
```jsx
import Icon from './Icon';
const App = () => {
return <Icon name="House" />;
return <Icon name="house" />;
};
export default App;

View File

@@ -1,37 +0,0 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"karsa-mistmere"
],
"tags": [
"toolkit",
"tools",
"trunk",
"chest",
"box",
"storage",
"utility",
"utilities",
"container",
"kit",
"set",
"repair",
"fix",
"service",
"maintenance",
"mechanic",
"workshop",
"construction",
"hardware",
"equipment",
"gear",
"handyman",
"engineering",
"craft",
"diy"
],
"categories": [
"tools",
"home"
]
}

View File

@@ -1,17 +0,0 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M16 12v4" />
<path d="M16 6a2 2 0 0 1 1.414.586l4 4A2 2 0 0 1 22 12v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 .586-1.414l4-4A2 2 0 0 1 8 6z" />
<path d="M16 6V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2" />
<path d="M2 14h20" />
<path d="M8 12v4" />
</svg>

Before

Width:  |  Height:  |  Size: 471 B

0
lucide-font/lucide.svg Normal file
View File

View File

@@ -16,7 +16,7 @@
"lucide-svelte": "pnpm --filter lucide-svelte",
"lucide-static": "pnpm --filter lucide-static",
"build:outline-icons": "pnpm --filter outline-svg start",
"build:font": "pnpm --filter build-font start",
"build:font": "pnpm --filter docs prebuild:releaseJson && pnpm --filter build-font start",
"optimize": "node ./scripts/optimizeSvgs.mts",
"addjsons": "node ./scripts/addMissingIconJsonFiles.mts",
"checkIcons": "node ./scripts/checkIconsAndCategories.mts",

View File

@@ -0,0 +1,4 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": ["angular.ng-template"]
}

20
packages/angular/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,20 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
},
{
"name": "ng test",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: test",
"url": "http://localhost:9876/debug.html"
}
]
}

42
packages/angular/.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,42 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
},
{
"type": "npm",
"script": "test",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
}
]
}

View File

@@ -0,0 +1 @@
# @lucide/angular

View File

@@ -0,0 +1,36 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"cli": {
"packageManager": "pnpm"
},
"newProjectRoot": ".",
"projects": {
"@lucide/angular": {
"projectType": "library",
"root": "./",
"sourceRoot": "./src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular/build:ng-packagr",
"configurations": {
"production": {
"tsConfig": "./tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "./tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular/build:unit-test",
"options": {
"tsConfig": "./tsconfig.spec.json"
}
}
}
}
}
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "./node_modules/ng-packagr/ng-package.schema.json",
"dest": "./dist",
"lib": {
"entryFile": "src/public-api.ts"
}
}

View File

@@ -0,0 +1,33 @@
{
"name": "@lucide/angular",
"version": "0.0.1",
"sideEffects": false,
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"peerDependencies": {
"@angular/common": "^21.0.0",
"@angular/core": "^21.0.0"
},
"devDependencies": {
"@angular/common": "^21.0.0",
"@angular/compiler": "^21.0.0",
"@angular/core": "^21.0.0",
"@angular/forms": "^21.0.0",
"@angular/platform-browser": "^21.0.0",
"@angular/router": "^21.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"@angular/build": "^21.0.3",
"@angular/cli": "^21.0.3",
"@angular/compiler-cli": "^21.0.0",
"jsdom": "^27.1.0",
"ng-packagr": "^21.0.0",
"typescript": "~5.9.2",
"vitest": "^4.0.8"
}
}

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MyLib } from './my-lib';
describe('MyLib', () => {
let component: MyLib;
let fixture: ComponentFixture<MyLib>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MyLib]
})
.compileComponents();
fixture = TestBed.createComponent(MyLib);
component = fixture.componentInstance;
await fixture.whenStable();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,15 @@
import { Component } from '@angular/core';
@Component({
selector: 'lib-my-lib',
imports: [],
template: `
<p>
my-lib works!
</p>
`,
styles: ``,
})
export class MyLib {
}

View File

@@ -0,0 +1,5 @@
/*
* Public API Surface of my-lib
*/
export * from './lib/my-lib';

View File

@@ -0,0 +1,38 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"compileOnSave": false,
"compilerOptions": {
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"paths": {
"my-lib": [
"./dist/my-lib"
]
},
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"isolatedModules": true,
"experimentalDecorators": true,
"importHelpers": true,
"target": "ES2022",
"module": "preserve"
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
},
"files": [],
"references": [
{
"path": "./projects/my-lib/tsconfig.lib.json"
},
{
"path": "./projects/my-lib/tsconfig.spec.json"
}
]
}

View File

@@ -0,0 +1,18 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": []
},
"include": [
"src/**/*.ts"
],
"exclude": [
"**/*.spec.ts"
]
}

View File

@@ -0,0 +1,11 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {
"declarationMap": false
},
"angularCompilerOptions": {
"compilationMode": "partial"
}
}

View File

@@ -0,0 +1,15 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"vitest/globals"
]
},
"include": [
"src/**/*.d.ts",
"src/**/*.spec.ts"
]
}

View File

@@ -24,23 +24,11 @@
"author": "Eric Fennis",
"amdName": "lucide-react-native",
"main": "dist/cjs/lucide-react-native.js",
"main:umd": "dist/umd/lucide-react-native.js",
"module": "dist/esm/lucide-react-native.js",
"unpkg": "dist/umd/lucide-react-native.min.js",
"typings": "dist/lucide-react-native.d.ts",
"react-native": "dist/esm/lucide-react-native.js",
"exports": {
".": {
"types": "./dist/lucide-react-native.d.ts",
"import": "./dist/esm/lucide-react-native.js",
"browser": "./dist/esm/lucide-react-native.js",
"require": "./dist/cjs/lucide-react-native.js"
},
"./icons": {
"types": "./dist/icons.d.ts",
"import": "./dist/esm/icons/index.js",
"browser": "./dist/esm/icons/index.js",
"require": "./dist/cjs/icons/index.js"
}
},
"sideEffects": false,
"files": [
"dist"

View File

@@ -5,7 +5,7 @@ import pkg from './package.json' with { type: 'json' };
const packageName = 'LucideReact';
const outputFileName = 'lucide-react-native';
const outputDir = 'dist';
const inputs = ['src/lucide-react-native.ts', 'src/icons/index.ts'];
const inputs = ['src/lucide-react-native.ts'];
const bundles = [
{
format: 'cjs',
@@ -60,16 +60,6 @@ export default [
],
plugins: [dts()],
},
{
input: inputs[1],
output: [
{
file: `dist/icons.d.ts`,
format: 'es',
},
],
plugins: [dts()],
},
{
input: `src/${outputFileName}.suffixed.ts`,
output: [

View File

@@ -31,7 +31,6 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
absoluteStrokeWidth,
children,
iconNode,
className,
...rest
},
ref,
@@ -47,7 +46,6 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
{
ref,
...defaultAttributes,
className,
width: size,
height: size,
...customAttrs,

View File

@@ -1,4 +1,5 @@
export * from './icons';
export * as icons from './icons';
export * from './aliases/prefixed';
export * from './types';

View File

@@ -1,4 +1,5 @@
export * from './icons';
export * as icons from './icons';
export * from './aliases/suffixed';
export * from './types';

View File

@@ -1,4 +1,5 @@
export * from './icons';
export * as icons from './icons';
export * from './aliases';
export * from './types';

4086
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

149
tools/build-font/main.ts Normal file
View File

@@ -0,0 +1,149 @@
import { readJson } from 'fs-extra/esm';
import svgtofont from 'svgtofont';
import getArgumentOptions from 'minimist';
import path from 'path';
const fontName = 'lucide';
const classNamePrefix = 'icon';
const startUnicode = 57400;
const inputDir = path.join(process.cwd(), '../../', 'outlined');
const cliArguments = getArgumentOptions(process.argv.slice(2));
const { outputDir = 'lucide-font' } = cliArguments;
const targetDir = path.join(process.cwd(), '../../', outputDir);
const releaseMetaDataDir = path.join(process.cwd(), '../../', 'docs/.vitepress/data');
const releaseMetaDataPath = path.resolve(releaseMetaDataDir, 'releaseMetaData.json');
const releaseMetaData = convertReleaseMetaData(await getReleaseMetaData());
async function getReleaseMetaData() {
let releaseMetaData = {};
try {
releaseMetaData = await readJson(releaseMetaDataPath);
} catch (err) {
throw new Error('Execution stopped because no release information was found.');
}
return releaseMetaData;
}
type Releases = Record<string, ReleaseMetaData>;
type ReleaseMetaData = {
createdRelease: {
version: string;
date: string;
};
changedRelease: {
version: string;
date: string;
};
};
type ReleaseMetaDataWithName = ReleaseMetaData & {
name: string;
};
function convertReleaseMetaData(releases: Releases) {
return Object.entries(releases)
.map(([key, data]) => ({
...data,
name: key,
}))
.sort((a, b) => sortMultiple(a, b, [sortByCreatedReleaseDate, sortByName]))
.map((value, index) => ({ ...value, index }))
.map((value, index) => ({
...value,
unicode: index + startUnicode,
}));
}
type CollatorFunction = (a: ReleaseMetaDataWithName, b: ReleaseMetaDataWithName) => number;
function sortMultiple(
a: ReleaseMetaDataWithName,
b: ReleaseMetaDataWithName,
collators: CollatorFunction[] = [],
) {
const comparison = collators?.shift?.()?.(a, b) ?? 0;
if (comparison === 0 && collators.length > 0) return sortMultiple(a, b, collators);
return comparison;
}
function sortByCreatedReleaseDate(a: ReleaseMetaDataWithName, b: ReleaseMetaDataWithName) {
const [dateA, dateB] = [a, b].map((value) => new Date(value.createdRelease.date).valueOf());
return Number(dateA > dateB) - Number(dateA < dateB);
}
function sortByName(a: ReleaseMetaDataWithName, b: ReleaseMetaDataWithName) {
return new Intl.Collator('en-US').compare(a.name, b.name);
}
function getIconUnicode(name: string): [string, number] {
const { unicode } = releaseMetaData.find(({ name: iconName }) => iconName === name) ?? {
unicode: startUnicode,
};
return [String.fromCharCode(unicode), startUnicode];
}
async function init() {
console.time('Font generation');
try {
await svgtofont({
src: path.resolve(process.cwd(), inputDir),
dist: path.resolve(process.cwd(), targetDir),
// styleTemplates: path.resolve(process.cwd(), 'styles'), // Add different templates if needed
fontName,
classNamePrefix,
css: {
fontSize: 'inherit',
},
emptyDist: true,
useCSSVars: false,
outSVGReact: false,
outSVGPath: false,
svgicons2svgfont: {
fontHeight: 1000, // At least 1000 is recommended
normalize: false,
},
generateInfoData: true,
website: {
title: 'Lucide',
logo: undefined,
meta: {
description: 'Lucide icons as TTF/EOT/WOFF/WOFF2/SVG.',
keywords: 'Lucide,TTF,EOT,WOFF,WOFF2,SVG',
},
corners: {
url: 'https://github.com/lucide-icons/lucide',
width: 62, // default: 60
height: 62, // default: 60
bgColor: '#dc3545', // default: '#151513'
},
links: [
{
title: 'GitHub',
url: 'https://github.com/lucide-icons/lucide',
},
{
title: 'Feedback',
url: 'https://github.com/lucide-icons/lucide/issues',
},
{
title: 'Font Class',
url: 'index.html',
},
{
title: 'Unicode',
url: 'unicode.html',
},
],
},
getIconUnicode,
});
} catch (err) {
console.log(err);
}
console.timeEnd('Font generation');
}
init();

View File

@@ -6,7 +6,7 @@
"main": "main.ts",
"type": "module",
"scripts": {
"start": "node ./src/main.ts"
"start": "node ./main.ts"
},
"keywords": [],
"author": "",
@@ -14,11 +14,9 @@
"dependencies": {
"fs-extra": "^11.2.0",
"minimist": "^1.2.8",
"oslllo-svg-fixer": "^5.0.0",
"svgtofont": "^6.5.0"
},
"devDependencies": {
"@lucide/helpers": "workspace:*",
"@types/fs-extra": "^11.0.4",
"@types/minimist": "^1.2.5",
"@types/node": "^22"

View File

@@ -1,60 +0,0 @@
import { type IconAliases } from "@lucide/helpers";
import path from "path";
import { promises as fs } from 'fs';
import { cwd } from "process";
export type CodePoints = Record<string, number>;
async function getLatestCodePoints(): Promise<CodePoints> {
// This is for the first release where no codepoints.json exists yet
const codepointsContents = await fs.readFile(path.join(cwd(), 'codepoints.json'), 'utf-8')
return JSON.parse(codepointsContents) as CodePoints
// Next releases will use the codepoints.json from latest release in lucide-static.
// const codepointsContents = await fetch('https://unpkg.com/lucide-static@latest/font/codepoints.json')
// return codepointsContents.json() as Promise<CodePoints>
}
interface AllocateCodePointsOptions {
saveCodePoints?: boolean;
iconsWithAliases: IconAliases
}
export async function allocateCodePoints({
saveCodePoints = false,
iconsWithAliases
}: AllocateCodePointsOptions): Promise<CodePoints> {
const baseCodePoints = await getLatestCodePoints()
const endCodePoint = Math.max(...Object.values(baseCodePoints))
await Promise.all(
iconsWithAliases.map(async ([iconName, aliases]) => {
if(!baseCodePoints[iconName]) {
console.log('Code point not found creating new one for', iconName);
baseCodePoints[iconName] = endCodePoint + 1;
}
aliases.forEach((alias, index) => {
if (baseCodePoints[alias]) {
return;
}
console.log('Code point not found creating new one for');
baseCodePoints[alias] = endCodePoint + index + 1;
});
})
)
if (saveCodePoints) {
await fs.writeFile(
path.join(cwd(), 'codepoints.json'),
JSON.stringify(baseCodePoints, null, 2),
'utf-8'
);
}
return baseCodePoints;
}

View File

@@ -1,86 +0,0 @@
import svgtofont from 'svgtofont';
import { type CodePoints } from './allocateCodepoints.ts';
interface BuildFontOptions {
inputDir: string;
targetDir: string;
fontName: string;
classNamePrefix: string;
codePoints: CodePoints
startUnicode: number;
}
export async function buildFont({
inputDir,
targetDir,
fontName,
classNamePrefix,
codePoints,
startUnicode
}: BuildFontOptions) {
console.time('Font generation');
try {
await svgtofont({
src: inputDir,
dist: targetDir,
fontName,
classNamePrefix,
css: {
fontSize: 'inherit',
},
emptyDist: true,
useCSSVars: false,
outSVGReact: false,
outSVGPath: false,
addLigatures: true,
svgicons2svgfont: {
fontHeight: 1000, // At least 1000 is recommended
normalize: false,
},
generateInfoData: true,
website: {
title: 'Lucide',
logo: undefined,
meta: {
description: 'Lucide icons as TTF/EOT/WOFF/WOFF2/SVG.',
keywords: 'Lucide,TTF,EOT,WOFF,WOFF2,SVG',
},
corners: {
url: 'https://github.com/lucide-icons/lucide',
width: 62, // default: 60
height: 62, // default: 60
bgColor: '#dc3545', // default: '#151513'
},
links: [
{
title: 'GitHub',
url: 'https://github.com/lucide-icons/lucide',
},
{
title: 'Feedback',
url: 'https://github.com/lucide-icons/lucide/issues',
},
{
title: 'Font Class',
url: 'index.html',
},
{
title: 'Unicode',
url: 'unicode.html',
},
],
},
getIconUnicode: (name: string) => {
if (!codePoints[name]) {
throw new Error(`No codepoint found for icon: ${name}`);
}
const unicode = codePoints[name];
return [String.fromCharCode(unicode), startUnicode];
},
});
} catch (err) {
console.log(err);
}
console.timeEnd('Font generation');
}

View File

@@ -1,15 +0,0 @@
import { type IconAliases } from "@lucide/helpers";
import { type CodePoints } from "./allocateCodepoints.ts";
export function hasMissingCodePoints(iconsWithAliases: IconAliases, codePoints: CodePoints): boolean {
return iconsWithAliases.map(([iconName, aliases]) => ([iconName, ...aliases]))
.flat()
.some(name => {
if (!codePoints?.[name]) {
console.log(`Missing code point for icon/alias: ${name}`);
return true;
}
return false;
});
}

View File

@@ -1,52 +0,0 @@
import getArgumentOptions from 'minimist';
import path from 'path';
import { promises as fs } from 'fs';
import { getAllIconAliases } from '@lucide/helpers';
import { outlineSVG } from './outlineSVGs.ts';
import { allocateCodePoints } from './allocateCodepoints.ts';
import { buildFont } from './buildFont.ts';
import { hasMissingCodePoints } from './helpers.ts';
const fontName = 'lucide';
const classNamePrefix = 'icon';
const startUnicode = 57400;
const outputDir = 'lucide-font';
const {
saveCodePoints = false,
} = getArgumentOptions(process.argv.slice(2)) ?? {}
const repoRoot = path.join(process.cwd(), '../../')
const iconsDir = path.join(repoRoot, 'icons');
const outlinedDir = path.join(repoRoot, 'outlined');
const targetDir = path.join(repoRoot, outputDir);
const iconsWithAliases = await getAllIconAliases(iconsDir)
await outlineSVG({
iconsDir,
outlinedDir,
iconsWithAliases
});
const codePoints = await allocateCodePoints({
saveCodePoints,
iconsWithAliases
});
if (hasMissingCodePoints(iconsWithAliases, codePoints)) {
throw new Error('Some icons or aliases are missing code points. See log for details.');
}
await buildFont({
inputDir: outlinedDir,
targetDir,
fontName,
classNamePrefix,
codePoints,
startUnicode,
});
await fs.copyFile(path.join(process.cwd(), 'codepoints.json'), path.join(targetDir, 'codepoints.json'));

View File

@@ -1,49 +0,0 @@
import { promises as fs } from 'fs';
import SVGFixer from 'oslllo-svg-fixer';
import { getAllIconAliases, type IconAliases } from '@lucide/helpers';
import path from 'path';
interface OutlineSVGOptions {
iconsDir: string;
outlinedDir: string;
iconsWithAliases: IconAliases
}
export async function outlineSVG({
iconsDir,
outlinedDir,
iconsWithAliases
}: OutlineSVGOptions) {
console.time('icon outliner');
try {
try {
await fs.mkdir(outlinedDir);
} catch (error) { } // eslint-disable-line no-empty
await SVGFixer(iconsDir, outlinedDir, {
showProgressBar: true,
traceResolution: 800,
}).fix();
console.log('Duplicate icons with aliases..');
await Promise.all(iconsWithAliases.map(async ([iconName, aliases]) => {
const sourcePath = path.join(outlinedDir, `${iconName}.svg`);
await Promise.all(aliases.map(async (aliasName) => {
const destinationPath = path.join(outlinedDir, `${aliasName}.svg`);
try {
await fs.copyFile(sourcePath, destinationPath);
console.log(`Copied ${iconName}.svg to ${aliasName}.svg`);
} catch (err) {
console.log(`Failed to copy ${sourcePath} to ${destinationPath}:`, err);
}
}));
}));
console.timeEnd('icon outliner');
} catch (err) {
console.log(err);
}
}

View File

View File

@@ -7,7 +7,6 @@ export * from './src/appendFile.ts';
export * from './src/writeFile.ts';
export * from './src/writeFileIfNotExists.ts';
export * from './src/readAllMetadata.ts';
export * from './src/getAllIconAliases.ts';
export * from './src/readMetadata.ts';
export * from './src/readSvgDirectory.ts';
export * from './src/readSvg.ts';

View File

@@ -1,20 +0,0 @@
import { readAllMetadata } from "./readAllMetadata.ts";
export type IconAliases = [iconName: string, aliases: string[]][];
export const getAllIconAliases = async (iconsDir: string): Promise<IconAliases> => {
const metaDataFiles = await readAllMetadata(iconsDir)
return Object.entries(metaDataFiles).map(([iconName, metadata]) => {
const { aliases } = metadata;
if (!aliases?.length) return [iconName, []];
const aliasesNames = aliases.map(alias =>
typeof alias === 'string' ? alias : alias.name,
);
return [iconName, aliasesNames]
})
}

View File

@@ -1,7 +1,6 @@
import fs from 'fs/promises';
import path from 'path';
import { readMetadata } from './readMetadata.ts';
import { type IconMetadata } from '../../build-icons/types.ts';
/**
* Reads metadata from the icons/categories directories
@@ -9,7 +8,7 @@ import { type IconMetadata } from '../../build-icons/types.ts';
* @param {string} directory
* @returns {object} A map of icon or category metadata
*/
export const readAllMetadata = async (directory: string): Promise<Record<string, IconMetadata>> => {
export const readAllMetadata = async (directory: string): Promise<Record<string, unknown>> => {
const directoryContent = await fs.readdir(directory);
const metaDataPromises = directoryContent
@@ -17,7 +16,6 @@ export const readAllMetadata = async (directory: string): Promise<Record<string,
.map(async (file) => [path.basename(file, '.json'), await readMetadata(file, directory)]);
const metadata = await Promise.all(metaDataPromises);
if (metadata.length === 0) {
throw new Error(`No metadata files found in directory: ${directory}`);
}

View File

@@ -0,0 +1,3 @@
# @lucide/outline-svg
A internal used package to outline SVGs.

29
tools/outline-svg/main.ts Normal file
View File

@@ -0,0 +1,29 @@
import { promises as fs } from 'fs';
import SVGFixer from 'oslllo-svg-fixer';
import getArgumentOptions from 'minimist';
import path from 'path';
const inputDir = path.join(process.cwd(), '../../icons');
const cliArguments = getArgumentOptions(process.argv.slice(2));
const { outputDir = 'outlined' } = cliArguments;
const targetDir = path.join(process.cwd(), '../../', outputDir);
async function init() {
console.time('icon outliner');
try {
try {
await fs.mkdir(targetDir);
} catch (error) {} // eslint-disable-line no-empty
await SVGFixer(inputDir, targetDir, {
showProgressBar: true,
traceResolution: 800,
}).fix();
console.timeEnd('icon outliner');
} catch (err) {
console.log(err);
}
}
init();

View File

@@ -0,0 +1,18 @@
{
"name": "@lucide/outline-svg",
"description": "A internal used package to outline SVGs.",
"private": true,
"version": "2.0.0",
"main": "main.ts",
"type": "module",
"scripts": {
"start": "node ./main.ts"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"minimist": "^1.2.8",
"oslllo-svg-fixer": "^5.0.0"
}
}

View File

@@ -0,0 +1,18 @@
{
"compilerOptions": {
"strict": true,
"declaration": true,
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"module": "ESNext",
"target": "ESNext",
"esModuleInterop": true,
"lib": ["esnext"],
"resolveJsonModule": true,
"allowImportingTsExtensions": true,
"noEmit": true,
"sourceMap": true,
"outDir": "./dist",
},
}