refactor(scripts): Cleaning up scripts (#2092)

* cleanup scripts

* Move helpers to package

* Fixes scripts

* Fix scripts

* Formatting

* Fix helpers import paths

* Remove lucide-figma

* Rename helpers package

* Fix build

* formatting

* Adjust main build-icons file

* Add export casing

* Adds `exportModuleNameCasing` fro lab project

* format files

* Bump package version @lucide/build-icons

* Revert changes in icons

* Revert changes in PR yml

* Fix lint issues

* Fix site build

* fix lint errors

* Attempt fix linting

* Fix lint errors
This commit is contained in:
Eric Fennis
2024-06-28 11:24:37 +02:00
committed by GitHub
parent ce79418c66
commit 077242cfa0
113 changed files with 455 additions and 31739 deletions

View File

@@ -1,4 +1,4 @@
const DEFAULT_ATTRS = require('./scripts/render/default-attrs.json');
const DEFAULT_ATTRS = require('@lucide/build-icons/render/default-attrs.json');
module.exports = {
root: true,
@@ -15,7 +15,9 @@ module.exports = {
'no-use-before-define': 'off',
'import/no-extraneous-dependencies': [
'error',
{ devDependencies: ['**/*.test.js', '**/*.spec.js', './scripts/**'] },
{
devDependencies: ['**/*.test.js', '**/*.spec.js', '**/scripts/**'],
},
],
'import/extensions': [
'error',

View File

@@ -158,16 +158,5 @@
"href": "https://www.npmjs.com/package/lucide-static"
}
]
},
"lucide-flutter": {
"order": 9,
"icon": "flutter",
"shields": [
{
"alt": "flutter",
"src": "https://img.shields.io/pub/v/lucide_icons",
"href": "https://img.shields.io/pub/v/lucide_icons"
}
]
}
}

View File

@@ -1,6 +1,5 @@
import { promises as fs, constants } from 'fs';
import path from 'path';
import yaml from 'js-yaml';
import { PackageItem } from '../theme/types';
const fileExist = (filePath) =>
@@ -27,11 +26,6 @@ const fetchPackages = async (): Promise<PackageItem[]> => {
return JSON.parse(await fs.readFile(jsonFilePath, 'utf-8'));
}
const ymlFilePath = path.resolve(filePath, 'pubspec.yaml');
if (await fileExist(ymlFilePath)) {
return yaml.load(await fs.readFile(ymlFilePath, 'utf-8'));
}
return null;
}),
);

View File

@@ -5,9 +5,10 @@ import fetchPackages from '../../../lib/fetchPackages';
export default {
async load() {
const packages = await fetchPackages();
return {
packages: packages
.filter((p) => p.name in packageData)
.filter((p) => p?.name != null && p.name in packageData)
.map((pData) => ({
...pData,
...packageData[pData.name],

View File

@@ -1,7 +1,8 @@
<script setup lang="ts">
import { data } from './PackageList.data'
import GridSection from '../base/GridSection.vue'
import PackageListItem from "./PackageListItem.vue";</script>
import PackageListItem from "./PackageListItem.vue";
</script>
<template>
<GridSection

View File

@@ -19,7 +19,7 @@ const props = defineProps<{
<img v-if="packageData.iconDark" :src="packageData.iconDark" alt="" class="package-icon dark" :class="packageData.iconClass" />
</div>
<div class="package-title">
<h2 class="title">{{ props.packageData.name }}</h2>
<h2 class="title">{{ props.packageData?.name }}</h2>
<a v-for="shield in props.packageData.shields" :href="shield.href" class="package-shield" rel="noreferrer noopener">
<img :src="shield.src" :alt="shield.href" />
</a>

View File

@@ -9,13 +9,13 @@
"docs:build": "pnpm run /^prebuild:.*/ && vitepress build",
"docs:preview": "vitepress preview",
"build:docs": "vitepress build",
"prebuild:iconNodes": "node ../scripts/writeIconNodes.mjs",
"prebuild:metaJson": "node ../scripts/writeIconMetaIndex.mjs",
"prebuild:releaseJson": "node ../scripts/writeReleaseMetadata.mjs",
"prebuild:iconNodes": "node ./scripts/writeIconNodes.mjs",
"prebuild:metaJson": "node ./scripts/writeIconMetaIndex.mjs",
"prebuild:releaseJson": "node ./scripts/writeReleaseMetadata.mjs",
"prebuild:categoriesJson": "node ./scripts/writeCategoriesMetadata.mjs",
"prebuild:relatedIcons": "node ../scripts/writeIconRelatedIcons.mjs",
"prebuild:iconDetails": "node ../scripts/writeIconDetails.mjs",
"postbuild:vercelJson": "node ../scripts/writeVercelOutput.mjs",
"prebuild:relatedIcons": "node ./scripts/writeIconRelatedIcons.mjs",
"prebuild:iconDetails": "node ./scripts/writeIconDetails.mjs",
"postbuild:vercelJson": "node ./scripts/writeVercelOutput.mjs",
"dev": "npx nitropack dev",
"prebuild:api": "npx nitropack prepare",
"build:api": "npx nitropack build",
@@ -25,6 +25,9 @@
"author": "Eric Fennis",
"license": "ISC",
"devDependencies": {
"@lucide/build-icons": "workspace:*",
"@lucide/helpers": "workspace:*",
"@lucide/shared": "workspace:*",
"@rollup/plugin-replace": "^5.0.2",
"@types/semver": "^7.5.3",
"h3": "^1.8.0",
@@ -41,7 +44,6 @@
"@vueuse/core": "^10.7.2",
"element-to-path": "^1.2.1",
"fuse.js": "^6.5.3",
"js-yaml": "^4.1.0",
"jszip": "^3.7.0",
"lodash": "^4.17.20",
"lodash-es": "^4.17.21",

View File

@@ -1,7 +1,6 @@
import fs from 'fs';
import path from 'path';
import renderIconsObject from './render/renderIconsObject.mjs';
import { readSvgDirectory, toCamelCase } from './helpers.mjs';
import { readSvgDirectory, toCamelCase } from '@lucide/helpers';
const currentDir = process.cwd();
const ICONS_DIR = path.resolve(currentDir, '../icons');

View File

@@ -1,6 +1,6 @@
import fs from 'fs';
import path from 'path';
import { readSvgDirectory, toCamelCase } from './helpers.mjs';
import { readSvgDirectory, toCamelCase } from '@lucide/helpers';
const currentDir = process.cwd();
const ICONS_DIR = path.resolve(currentDir, '../icons');

View File

@@ -1,7 +1,7 @@
import fs from 'fs';
import path from 'path';
import renderIconsObject from './render/renderIconsObject.mjs';
import { readSvgDirectory, toCamelCase } from './helpers.mjs';
import { renderIconsObject } from '@lucide/build-icons';
import { readSvgDirectory, toCamelCase } from '@lucide/helpers';
const currentDir = process.cwd();
const ICONS_DIR = path.resolve(currentDir, '../icons');

View File

@@ -1,6 +1,6 @@
import fs from 'fs';
import path from 'path';
import { readSvgDirectory } from './helpers.mjs';
import { readSvgDirectory } from '@lucide/helpers';
const currentDir = process.cwd();
const ICONS_DIR = path.resolve(currentDir, '../icons');
@@ -19,13 +19,6 @@ const categoryWeight = 3;
const MAX_RELATED_ICONS = 4 * 17; // grid of 4x17 icons, = 68 icons
const arrayMatches = (a, b) => {
// let matches = 0;
// for (let i = 0; i < a.length; ++i) {
// if (b.indexOf(a[i]) != -1) {
// matches++;
// }
// }
// return matches;
return a.filter((item) => b.includes(item)).length;
};
@@ -53,7 +46,6 @@ const getRelatedIcons = (currentIcon, icons) => {
};
const iconsMetaDataPromises = svgFiles.map(async (iconName) => {
// eslint-disable-next-line import/no-dynamic-require, global-require
const metaData = JSON.parse(fs.readFileSync(`../icons/${iconName}`));
const name = iconName.replace('.json', '');

View File

@@ -3,7 +3,7 @@ import fs from 'fs';
import path from 'path';
import { simpleGit } from 'simple-git';
import semver from 'semver';
import { readSvgDirectory } from './helpers.mjs';
import { readSvgDirectory } from '@lucide/helpers';
const DATE_OF_FORK = '2020-06-08T16:39:52+0100';

View File

@@ -1,8 +1,7 @@
import path from 'path';
import fs from 'fs';
// eslint-disable-next-line import/no-named-as-default, import/no-named-as-default-member
import getIconMetaData from '../tools/build-icons/utils/getIconMetaData.mjs';
import { getCurrentDirPath } from './helpers.mjs';
import { getIconMetaData } from '@lucide/build-icons';
import { getCurrentDirPath } from '@lucide/helpers';
const currentDir = process.cwd();
const scriptDir = getCurrentDirPath(import.meta.url);

View File

@@ -14,17 +14,15 @@
"lucide-vue-next": "pnpm --filter lucide-vue-next",
"lucide-svelte": "pnpm --filter lucide-svelte",
"lucide-static": "pnpm --filter lucide-static",
"build:icons": "node ./scripts/buildIcons.mjs --templateSrc ./packages/lucide/scripts/exportTemplate.mjs",
"build:outline-icons": "pnpm --filter outline-svg start",
"build:font": "pnpm --filter docs prebuild:releaseJson && pnpm --filter build-font start",
"generate:supersprite": "node ./scripts/generateSuperSVG.mjs",
"optimize": "node ./scripts/optimizeSvgs.mjs",
"addjsons": "node scripts/addMissingIconJsonFiles.mjs",
"checkIcons": "node scripts/checkIconsAndCategories.mjs --presets @babel/env",
"tags2icons": "node scripts/migrateTagsToIcons.mjs --presets @babel/env",
"icons2tags": "node scripts/migrateIconsToTags.mjs --presets @babel/env",
"icons2categories": "node scripts/migrateIconsToCategories.mjs --presets @babel/env",
"categories2icons": "node scripts/migrateCategoriesToIcons.mjs --presets @babel/env",
"checkIcons": "node scripts/checkIconsAndCategories.mjs",
"tags2icons": "node scripts/migrateTagsToIcons.mjs",
"icons2tags": "node scripts/migrateIconsToTags.mjs",
"icons2categories": "node scripts/migrateIconsToCategories.mjs",
"categories2icons": "node scripts/migrateCategoriesToIcons.mjs",
"generate:changelog": "node ./scripts/generateChangelog.mjs",
"generate:contributors": "node ./scripts/updateContributors.mjs icons/*.svg",
"generate:nextJSAliases": "node ./scripts/generateNextJSAliases.mjs",
@@ -42,6 +40,8 @@
"renamePattern": "node scripts/rename/renamePattern.mjs"
},
"devDependencies": {
"@lucide/build-icons": "workspace:*",
"@lucide/helpers": "workspace:*",
"@html-eslint/eslint-plugin": "^0.19.1",
"@html-eslint/parser": "^0.19.1",
"@octokit/rest": "^19.0.13",
@@ -60,7 +60,6 @@
"husky": "^8.0.3",
"lint-staged": "^13.3.0",
"minimist": "^1.2.8",
"node-fetch": "^3.3.2",
"p-memoize": "^7.1.1",
"prettier": "3.2.4",
"semver": "^7.5.4",

View File

@@ -1,25 +0,0 @@
# Lucide Figma plugin
A Figma plugin for using Lucide Icons
## Local development
1. Install the dependencies
```sh
yarn
```
2. Build the plugin
```sh
yarn watch
```
3. Open the [Figma desktop app](https://www.figma.com/downloads/)
4. Go to `Menu > Plugins > Development > New Plugin...`
5. Choose `lucide/packages/lucide-figma/manifest.json`
6. Run the plugin by going to `Menu > Plugins > Development > Lucide Icons`

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -1,26 +0,0 @@
{
"name": "Lucide Icons",
"id": "939567362549682242",
"api": "1.0.0",
"editorType": ["figma"],
"main": "dist/assets/main.js",
"ui": {
"interface": "dist/src/interface/interface.html",
"worker": "dist/src/worker/worker.html"
},
"parameterOnly": false,
"parameters": [
{
"name": "Icon",
"key": "icon-name",
"description": "Enter the name of the icon you want to insert."
},
{
"name": "Size",
"key": "size",
"description": "Enter the size of the icon.",
"allowFreeform": true,
"optional": true
}
]
}

View File

@@ -1,30 +0,0 @@
{
"name": "lucide-figma",
"version": "1.0.0",
"private": true,
"license": "ISC",
"main": "src/main.js",
"scripts": {
"dev": "vite",
"build": "tsc && rm -rf dist && pnpm build:main && pnpm build:worker && pnpm build:interface",
"build:main": "INPUT=main vite build",
"build:worker": "INPUT=worker vite build",
"build:interface": "INPUT=interface vite build",
"serve": "vite preview"
},
"dependencies": {
"minimist": "^1.2.6",
"react": "^17.0.0",
"react-dom": "^17.0.0"
},
"devDependencies": {
"@figma/plugin-typings": "^1.36.0",
"@lucide/shared": "workspace:*",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@vitejs/plugin-react": "^1.0.0",
"typescript": "^4.3.2",
"vite": "5.0.13",
"vite-plugin-singlefile": "^0.5.1"
}
}

View File

@@ -1,95 +0,0 @@
import iconNodeToSvg from '../helpers/iconNodeToSvg';
export type IconNode = any[];
export type IconName = string;
export type Tag = string[];
export interface Tags {
[key: string]: Tag;
}
export interface LucideIcons {
version: string;
iconNodes: { [key: IconName]: IconNode };
tags: Tags;
svgs: { [key: IconName]: string };
}
export const fetchIcons = async (cachedIcons?: LucideIcons): Promise<LucideIcons> => {
const response = await fetch('https://unpkg.com/lucide-static@latest/package.json');
const packageJson = await response.json();
if (cachedIcons && cachedIcons?.version === packageJson.version) {
return cachedIcons;
}
const [iconNodesResponse, tagsResponse] = await Promise.all([
fetch('https://lucide.dev/api/icon-nodes'),
fetch('https://lucide.dev/api/tags'),
]);
const iconNodes = await iconNodesResponse.json();
const tags = await tagsResponse.json();
const svgs = Object.keys(iconNodes).reduce((acc: { [key: string]: string }, iconName) => {
acc[iconName] = iconNodeToSvg(iconName, iconNodes[iconName]);
return acc;
}, {});
const lucideIcons: LucideIcons = {
version: packageJson.version,
tags,
iconNodes,
svgs,
};
parent.postMessage(
{
pluginMessage: {
type: 'setCachedIcons',
lucideIcons,
},
},
'*',
);
return lucideIcons;
};
export const getIcons = () =>
new Promise<LucideIcons>(async (resolve, reject) => {
parent.postMessage(
{
pluginMessage: {
type: 'getCachedIcons',
},
},
'*',
);
window.onmessage = async (event) => {
if (event.type === 'message' && event?.data?.pluginMessage.type === 'cachedIcons') {
const lucideIcons = await fetchIcons(event?.data?.pluginMessage?.cachedIcons);
resolve(lucideIcons);
}
};
});
type EventCallback = (lucideIcons: LucideIcons) => void;
export const iconFetchListener = (callback: EventCallback) => {
fetchIcons();
const handleEvent = (event: MessageEvent) => {
if (event.type === 'message' && event?.data?.pluginMessage.type === 'cachedIcons') {
const lucideIcons = event?.data?.pluginMessage?.cachedIcons;
callback(lucideIcons);
}
};
window.addEventListener('message', handleEvent);
const removeListener = () => {
window.removeEventListener('message', handleEvent);
};
return removeListener;
};

View File

@@ -1,3 +0,0 @@
const EditBar = () => {};
export default EditBar;

View File

@@ -1,18 +0,0 @@
.icon-button {
padding: 8px;
color: var(--color-black);
background: transparent;
border: 0;
border-radius: 2px;
appearance: none;
outline: 0;
&:hover {
background: rgba(0, 0, 0, 0.06);
}
&:focus,
&:active {
box-shadow: inset 0 0 0 2px var(--color-blue);
}
}

View File

@@ -1,37 +0,0 @@
import { renderToString } from 'react-dom/server';
import { FC } from 'react';
import './IconButton.scss';
interface IconButtonProps {
name: string;
component: FC;
}
function IconButton({ name, component: IconComponent }: IconButtonProps) {
const onIconClick = () => {
const svg = renderToString(<IconComponent />);
parent.postMessage(
{
pluginMessage: {
type: 'drawIcon',
icon: { name, svg },
},
},
'*',
);
};
return (
<button
key={name}
aria-label={name}
onClick={onIconClick}
className="icon-button"
>
<IconComponent />
</button>
);
}
export default IconButton;

View File

@@ -1 +0,0 @@
export { default } from './IconButton';

View File

@@ -1,30 +0,0 @@
.menu {
display: flex;
align-items: center;
flex-shrink: 0;
height: 40px;
line-height: 40px;
border-bottom: 1px solid var(--color-border, #e5e5e5);
justify-content: flex-start;
}
.menu-item {
font-weight: 500;
font-size: 11px;
color: var(--color-text-tertiary, #b3b3b3);
padding: 0 8px;
text-transform: capitalize;
cursor: pointer;
&.active {
color: var(--color-text, #333333);
}
&:first-child {
padding-left: 16px;
}
&:last-child {
padding-right: 16px;
}
}

View File

@@ -1,26 +0,0 @@
import { useState } from 'react';
import './Menu.scss';
interface MenuProps {
page: string;
setPage: (page: string) => void;
}
const menuItems = ['icons', 'info'];
const Menu = ({ page, setPage = (page) => {} }: MenuProps) => {
return (
<nav className="menu">
{menuItems.map((menuItem) => (
<div
className={`menu-item ${page === menuItem ? 'active' : null}`}
onClick={() => setPage(menuItem)}
>
{menuItem}
</div>
))}
</nav>
);
};
export default Menu;

View File

@@ -1 +0,0 @@
export { default } from './Menu';

View File

@@ -1,19 +0,0 @@
.search-input {
position: relative;
display: flex;
align-items: center;
.icon {
position: absolute;
left: 16px;
}
input {
width: 100%;
height: 40px;
padding: 0 16px 0 36px;
font-family: inherit;
font-size: 11px;
border: 0;
outline: 0;
}
}

View File

@@ -1,31 +0,0 @@
import './SearchInput.scss';
import { ChangeEvent } from 'react';
import SearchIcon from '../icons/SearchIcon';
interface SearchInputProps extends React.HTMLProps<HTMLDivElement> {
value: string;
iconCount: number;
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
placeholder: string;
}
function SearchInput({ value, onChange, placeholder, className, ...props }: SearchInputProps) {
return (
<div
className="search-input"
{...props}
>
<SearchIcon className="icon" />
<input
autoFocus
type="search"
value={value}
onChange={onChange}
placeholder={placeholder}
className="input__field"
/>
</div>
);
}
export default SearchInput;

View File

@@ -1 +0,0 @@
export { default } from './SearchInput';

View File

@@ -1,21 +0,0 @@
@keyframes load {
to {
background-position: 200% 0;
}
}
.skeleton {
--block: rgba(0, 0, 0, 0.06);
--loader: hsl(0 0% 89%);
background:
linear-gradient(-75deg, transparent 40%, var(--loader), transparent 60%) 0 0 / 200% 100%,
var(--block);
border-radius: calc(var(--padding) * 0.5);
animation: load 2s infinite linear;
background-attachment: fixed;
width: 40px;
height: 40px;
border-radius: 2px;
}

View File

@@ -1,13 +0,0 @@
import './Skeleton.scss';
const Skeleton = () => {
return (
<>
{Array.from({ length: 48 }, () => (
<div className="skeleton" />
))}
</>
);
};
export default Skeleton;

View File

@@ -1,21 +0,0 @@
import { createElement, forwardRef } from 'react';
const SearchIcon = (props: any) => (
<svg
width="11"
height="11"
viewBox="0 0 11 11"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
{...props}
>
<path
d="M6.453 7.16C5.776 7.687 4.924 8 4 8 1.79 8 0 6.21 0 4c0-2.21 1.79-4 4-4 2.21 0 4 1.79 4 4 0 .924-.314 1.776-.84 2.453l3.194 3.193-.708.707L6.453 7.16zM7 4c0 1.657-1.343 3-3 3-1.657 0-3-1.343-3-3 0-1.657 1.343-3 3-3 1.657 0 3 1.343 3 3z"
fillRule="evenodd"
fillOpacity="1"
stroke="none"
/>
</svg>
);
export default SearchIcon;

View File

@@ -1,50 +0,0 @@
import { forwardRef, createElement, SVGProps } from 'react';
import { IconNode } from '../api/fetchIcons';
import { toKebabCase } from '@lucide/shared';
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',
};
export interface LucideProps extends Partial<SVGProps<SVGSVGElement>> {
size?: string | number;
}
const createIconComponent = (iconName: string, iconNode: IconNode) => {
const Component = forwardRef<SVGSVGElement, LucideProps>(
({ color = 'currentColor', size = 24, strokeWidth = 2, children, ...rest }, ref) =>
createElement(
'svg',
{
ref,
...defaultAttributes,
width: size,
height: size,
stroke: color,
strokeWidth,
className: `lucide lucide-${toKebabCase(iconName)}`,
...rest,
},
[
...iconNode.map(([tag, attrs]: [tag: string, attrs: SVGProps<SVGSVGElement>]) =>
createElement(tag, attrs),
),
...([children] || []),
],
),
);
Component.displayName = `${iconName}`;
return Component;
};
export default createIconComponent;

View File

@@ -1,9 +0,0 @@
import { Tags } from '../api/fetchIcons';
import { Icon } from '../hooks/useSearch';
export default (icons: Icon[], tags: Tags, query: string) =>
icons.filter(([name]: Icon) => {
const iconTags = tags && tags[name] ? tags[name] : [];
return [name, ...iconTags].some((item: string) => item.toLowerCase().includes(query));
});

View File

@@ -1,11 +0,0 @@
import { createElement } from 'react';
import { renderToString } from 'react-dom/server';
import { IconNode } from '../api/fetchIcons';
import createIconComponent from './createIconComponent';
const iconNodeToSvg = (iconName: string, iconNode: IconNode) => {
const IconComponent = createIconComponent(iconName, iconNode);
return renderToString(createElement(IconComponent));
};
export default iconNodeToSvg;

View File

@@ -1,20 +0,0 @@
/**
* Converts string to camelcase
*
* @param {string} string
*/
export const toCamelCase = (string: string) =>
string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) =>
p2 ? p2.toUpperCase() : p1.toLowerCase(),
);
/**
* Converts string to PascalCase
*
* @param {string} string
*/
export const toPascalCase = (string: string) => {
const camelCase = toCamelCase(string);
return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
};

View File

@@ -1,14 +0,0 @@
import { IconName, IconNode, Tags } from '../api/fetchIcons';
import filterIcons from '../helpers/filterIcons';
export type Icon = [name: IconName, iconNode: IconNode];
function useSearch(icons: Icon[], tags: Tags, query: string) {
if (!query) return icons;
const searchString = query.toLowerCase();
return filterIcons(icons, tags, searchString);
}
export default useSearch;

View File

@@ -1 +0,0 @@
<svg fill="none" height="32" viewBox="0 0 32 32" width="32" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="m18.8744 19.5815c-1.0453.8849-2.3975 1.4185-3.8744 1.4185-3.3137 0-6-2.6863-6-6s2.6863-6 6-6 6 2.6863 6 6c0 1.4769-.5336 2.8291-1.4185 3.8744l4.2721 4.272-.7072.7072zm1.1256-4.5815c0 2.7614-2.2386 5-5 5s-5-2.2386-5-5 2.2386-5 5-5 5 2.2386 5 5z" fill="#000" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 408 B

View File

@@ -1 +0,0 @@
<svg fill="none" height="32" viewBox="0 0 32 32" width="32" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="m18.3972 18.6046c-.7793.625-1.7687.9988-2.8455.9988-2.5138 0-4.5517-2.0378-4.5517-4.5517 0-2.5138 2.0379-4.5517 4.5517-4.5517 2.5139 0 4.5517 2.0379 4.5517 4.5517 0 1.0769-.3739 2.0664-.999 2.8458l3.2491 3.2492-.7071.7071zm.7062-3.5529c0 1.9616-1.5901 3.5517-3.5517 3.5517-1.9615 0-3.5517-1.5901-3.5517-3.5517 0-1.9615 1.5902-3.5517 3.5517-3.5517 1.9616 0 3.5517 1.5902 3.5517 3.5517z" fill="#000" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 549 B

View File

@@ -1,6 +0,0 @@
<script
type="module"
src="./interface.tsx"
></script>
<div id="root"></div>

View File

@@ -1,103 +0,0 @@
@font-face {
font-family: Inter;
font-style: normal;
font-weight: 400;
font-display: swap;
src:
url('https://rsms.me/inter/font-files/Inter-Regular.woff2?v=3.9') format('woff2'),
url('https://rsms.me/inter/font-files/Inter-Regular.woff?v=3.9') format('woff');
}
@font-face {
font-family: Inter;
font-style: normal;
font-weight: 500;
font-display: swap;
src:
url('https://rsms.me/inter/font-files/Inter-Medium.woff2?v=3.9') format('woff2'),
url('https://rsms.me/inter/font-files/Inter-Medium.woff?v=3.9') format('woff');
}
:root {
--color-blue: #18a0fb;
--color-black: #333;
}
body {
font-family: Inter, sans-serif;
font-size: 11px;
margin: 0;
}
.search-input {
position: sticky;
top: 0;
border-bottom: 1px solid #e5e5e5;
backface-visibility: hidden;
}
main {
padding-bottom: 8px;
}
.icon-grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-gap: 8px;
}
footer {
margin-top: 8px;
padding: 8px;
font-size: 12px;
color: rgba(0, 0, 0, 0.5);
.footer-link {
color: inherit;
}
}
.floating {
position: absolute;
bottom: -10px;
left: 0;
padding: 10px;
background: blue;
}
.info-page {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 24px;
}
.lucide-logo {
width: 160px;
}
.version {
margin-top: 8px;
padding: 8px;
font-weight: bold;
font-size: 18px;
color: rgba(0, 0, 0, 0.5);
}
.link-list {
width: 160px;
margin: 0 auto;
padding-top: 24px;
}
.info-link {
color: var(--color-blue);
display: block;
padding: 2px 0;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}

View File

@@ -1,57 +0,0 @@
import { useEffect, useMemo, useState } from 'react';
import ReactDOM from 'react-dom';
import * as views from '../views';
type Views = typeof views;
import useSearch, { Icon } from '../hooks/useSearch';
import { getIcons, iconFetchListener, LucideIcons } from '../api/fetchIcons';
import './interface.scss';
import Menu from '../components/Menu';
function App() {
const [page, setPage] = useState('icons');
const [query, setQuery] = useState('');
const [icons, setIcons] = useState<Icon[]>([]);
const [tags, setTags] = useState({});
const [version, setVersion] = useState('');
const searchResults = useMemo(() => useSearch(icons, tags, query), [icons, query]);
const handleFetchResponse = async (lucideIcons: LucideIcons) => {
const icons = Object.entries(lucideIcons.iconNodes);
setIcons(icons);
setTags(lucideIcons.tags);
setVersion(lucideIcons.version);
};
useEffect(() => {
const removeListener = iconFetchListener(handleFetchResponse);
return removeListener;
}, []);
const View = views?.[page as keyof Views] ?? views.icons;
return (
<div>
<Menu
page={page}
setPage={setPage}
/>
<View
{...{
query,
setQuery,
searchResults,
icons,
version,
}}
/>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));

View File

@@ -1,149 +0,0 @@
import type { LucideIcons } from './api/fetchIcons';
import filterIcons from './helpers/filterIcons';
figma.showUI(__uiFiles__.worker, { visible: false });
let cachedIcons: LucideIcons;
type InsertableNodes = FrameNode | GroupNode;
function isInsertableNode(node: SceneNode): node is InsertableNodes {
return ['FRAME', 'GROUP'].includes(node.type);
}
const setResults = ({
result,
query,
lucideIcons,
}: {
result: SuggestionResults;
query: string;
lucideIcons: LucideIcons;
}) => {
const icons = Object.entries(lucideIcons.iconNodes);
const suggestions = filterIcons(icons, lucideIcons.tags, query.toLowerCase()).map(([name]) => ({
name,
icon: lucideIcons.svgs[name],
}));
result.setSuggestions(suggestions);
};
// const styles = figma.getLocalPaintStyles();
// const styleNames = styles.map((style) => style.name);
// console.log(styleNames);
figma.parameters.on('input', async ({ parameters, key, query, result }) => {
if (key === 'icon-name') {
cachedIcons = await figma.clientStorage.getAsync(`lucide-icons`);
if (cachedIcons && cachedIcons.iconNodes && cachedIcons.tags) {
setResults({ result, query, lucideIcons: cachedIcons });
}
}
if (key === 'size') {
const iconSizes = [24, 36, 48, 72];
result.setSuggestions(
iconSizes.map((size) => ({
name: size.toString(),
data: size,
})),
);
}
});
const drawIcon = ({ icon: { name, svg, size } }: any) => {
const min = 0;
const max = 100;
const randomPosition = () => Math.floor(Math.random() * (max - min + 1) + min);
const icon = figma.createNodeFromSvg(svg);
icon.setPluginData('isLucideIcon', 'true');
icon.setPluginData('iconName', name);
const pluginData = icon.getPluginData('isLucideIcon');
icon.name = name;
icon.x = Math.round(figma.viewport.center.x + randomPosition());
icon.y = Math.round(figma.viewport.center.y + randomPosition());
if (figma.currentPage.selection.length) {
let currentSelection = figma.currentPage.selection[0];
const isLucideIcon = currentSelection.getPluginData('isLucideIcon');
// if(isLucideIcon && currentSelection?.parent) {
// return
// // currentSelection = currentSelection.parent as SceneNode
// }
if (!isLucideIcon && isInsertableNode(currentSelection)) {
icon.x = currentSelection.type === 'GROUP' ? currentSelection.x : 0;
icon.y = currentSelection.type === 'GROUP' ? currentSelection.y : 0;
currentSelection.appendChild(icon);
}
}
figma.currentPage.selection = [icon];
// lock children
// icon.children.forEach((vectorNode, key) => {
// icon.children[key].locked = true
// });
};
const setCachedIcons = async (pluginMessage: any) => {
if (pluginMessage.lucideIcons) {
await figma.clientStorage.setAsync(`lucide-icons`, pluginMessage.lucideIcons);
}
};
const getCachedIcons = async () => {
cachedIcons = await figma.clientStorage.getAsync(`lucide-icons`);
const response = { type: 'cachedIcons' };
if (cachedIcons) {
Object.assign(response, { cachedIcons });
}
figma.ui.postMessage(response);
};
getCachedIcons();
figma.ui.onmessage = (event) => {
switch (event.type) {
case 'drawIcon':
drawIcon(event);
break;
case 'getCachedIcons':
getCachedIcons();
break;
case 'setCachedIcons':
setCachedIcons(event);
break;
case 'close':
figma.closePlugin();
break;
default:
break;
}
};
figma.on('run', (event) => {
if (event.parameters) {
figma.ui.postMessage({
type: 'getSvg',
iconName: event.parameters['icon-name'],
size: event.parameters['size'],
cachedIcons,
});
} else {
figma.showUI(__uiFiles__.interface, { width: 300, height: 400 });
}
});

View File

@@ -1,8 +0,0 @@
export default {
space: [0, 4, 8, 12, 16],
fontSizes: [12, 14, 16],
colors: {
blue: '#18a0fb',
},
radii: [0, 2],
};

View File

@@ -1,52 +0,0 @@
import IconButton from '../components/IconButton';
import SearchInput from '../components/SearchInput';
import createIconComponent from '../helpers/createIconComponent';
import { Icon } from '../hooks/useSearch';
import Skeleton from '../components/Skeleton/Skeleton';
interface PageProps {
query: string;
setQuery: (query: string) => void;
searchResults: Icon[];
icons: Icon[];
version: string;
}
const Icons = ({ query, setQuery, searchResults, icons, version }: PageProps) => {
return (
<>
<SearchInput
value={query}
iconCount={icons.length}
onChange={(event) => setQuery(event.target.value)}
placeholder={icons.length ? `Search ${icons.length} icons` : 'Loading icons ..'}
/>
<main>
<div className="icon-grid">
{icons.length ? (
searchResults.map(([name, iconNode]: any) => (
<IconButton
name={name}
key={name}
component={createIconComponent(name, iconNode)}
/>
))
) : (
<Skeleton />
)}
</div>
<footer>
<a
href="https://lucide.dev"
target="_blank"
className="footer-link"
>
Lucide v{version}
</a>
</footer>
</main>
</>
);
};
export default Icons;

View File

@@ -1,70 +0,0 @@
import { SyntheticEvent } from 'react';
interface PageProps {
version: string;
}
const Info = ({ version }: PageProps) => {
const menuItems = [
{
name: 'Report a bug',
url: 'https://github.com/lucide-icons/lucide/issues',
},
{
name: 'Contribute an icon',
url: 'https://github.com/lucide-icons/lucide/blob/main/CONTRIBUTING.md',
},
{
name: 'Website',
url: 'https://lucide.dev',
},
{
name: 'Repository',
url: 'https://github.com/lucide-icons/lucide',
},
{
name: 'License',
url: 'https://lucide.dev/license',
},
{
name: 'Community Page',
url: 'https://www.figma.com/community/plugin/939567362549682242/Lucide-Icons',
},
{
name: 'Supported Frameworks',
url: 'https://lucide.dev/packages',
},
];
const onClick = (url: string) => (event: SyntheticEvent) => {
event.preventDefault();
window.open(url, '_blank');
};
return (
<div className="info-page">
<img
src="https://lucide.dev/logo-text.svg"
alt="Lucide Logo"
className="lucide-logo"
/>
<p className="version">v{version}</p>
<section className="link-list">
{menuItems.map(({ name, url }) => (
<a
href={url}
key={name}
aria-label={name}
className="info-link"
onClick={onClick(url)}
>
{name}
</a>
))}
</section>
</div>
);
};
export default Info;

View File

@@ -1,2 +0,0 @@
export { default as icons } from './Icons';
export { default as info } from './Info';

View File

@@ -1,4 +0,0 @@
<script
type="module"
src="./worker.ts"
></script>

View File

@@ -1,85 +0,0 @@
import { fetchIcons, LucideIcons } from '../api/fetchIcons';
import createIconComponent from '../helpers/createIconComponent';
import { renderToString } from 'react-dom/server';
import { createElement } from 'react';
const getLatestIcons = async ({ cachedIcons }: any) => {
const lucideIcons = await fetchIcons(cachedIcons);
parent.postMessage(
{
pluginMessage: {
type: 'latestIcons',
lucideIcons,
},
},
'*',
);
};
const getSvg = async ({
cachedIcons,
iconName,
size = 24,
}: {
cachedIcons: LucideIcons;
iconName: string;
size: number;
}) => {
if (!cachedIcons) {
return;
}
console.log(iconName, size);
const iconNode = cachedIcons.iconNodes[iconName];
if (iconNode) {
const IconComponent = createIconComponent(iconName, iconNode);
const svg = renderToString(createElement(IconComponent, { size }));
parent.postMessage(
{
pluginMessage: {
type: 'drawIcon',
icon: {
name: iconName,
svg,
size,
},
},
},
'*',
);
parent.postMessage(
{
pluginMessage: {
type: 'close',
},
},
'*',
);
}
};
window.onmessage = async (event) => {
if (!event?.data?.pluginMessage) {
return;
}
const { pluginMessage } = event.data;
switch (pluginMessage.type) {
case 'getLatestIcons':
getLatestIcons(pluginMessage);
break;
case 'getSvg':
getSvg(pluginMessage);
break;
default:
break;
}
};

View File

@@ -1,21 +0,0 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": false,
"noEmit": true,
"jsx": "react-jsx",
"typeRoots": ["./node_modules/@types", "./node_modules/@figma"],
},
"include": ["src"],
}

View File

@@ -1,40 +0,0 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { viteSingleFile } from 'vite-plugin-singlefile';
import { resolve } from 'path';
const entries = {
main: resolve(__dirname, 'src/main.ts'),
interface: resolve(__dirname, './src/interface/interface.html'),
worker: resolve(__dirname, './src/worker/worker.html'),
};
const input = {};
if (process.env['INPUT']) {
const entry = process.env['INPUT'];
input[entry] = entries[entry];
}
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), viteSingleFile()],
build: {
target: 'esnext',
assetsInlineLimit: 100000000,
chunkSizeWarningLimit: 100000000,
cssCodeSplit: false,
brotliSize: false,
emptyOutDir: false,
rollupOptions: {
input,
inlineDynamicImports: true,
output: {
manualChunks: (chunk) => 'all.js',
entryFileNames: `assets/[name].js`,
chunkFileNames: `assets/[name].js`,
assetFileNames: `assets/[name].[ext]`,
},
},
},
});

View File

@@ -1,16 +1,15 @@
/* eslint-disable import/no-extraneous-dependencies */
import fs from 'fs';
import path from 'path';
import getArgumentOptions from 'minimist';
import { parseSync } from 'svgson';
import { readSvgDirectory, getCurrentDirPath } from '../../../scripts/helpers.mjs';
import { readSvgDirectory, getCurrentDirPath } from '@lucide/helpers';
import readSvgs from './readSvgs.mjs';
import generateSprite from './generateSprite.mjs';
import generateIconNodes from './generateIconNodes.mjs';
import copyIcons from './copyIcons.mjs';
import pkg from '../package.json' assert { type: 'json' };
import pkg from '../package.json' with { type: 'json' };
const cliArguments = getArgumentOptions(process.argv.slice(2));
const createDirectory = (dir) => {

View File

@@ -1,4 +1,4 @@
import { writeFile } from '../../../scripts/helpers.mjs';
import { writeFile } from '@lucide/helpers';
export default function generateIconNodes(parsedSvgs, packageDir) {
const iconNodes = parsedSvgs.reduce((acc, { name, parsedSvg }) => {

View File

@@ -1,7 +1,7 @@
/* eslint-disable import/no-extraneous-dependencies */
import { stringify } from 'svgson';
import { format } from 'prettier';
import { appendFile } from '../../../scripts/helpers.mjs';
import { appendFile } from '@lucide/helpers';
export default function generateSprite(svgs, packageDir, license) {
const symbols = svgs.map(({ name, parsedSvg }) => ({

View File

@@ -1,6 +1,6 @@
/* eslint-disable import/no-extraneous-dependencies */
import { basename } from 'path';
import { readSvg } from '../../../scripts/helpers.mjs';
import { readSvg } from '@lucide/helpers';
/**
* Build an object in the format: `{ <name>: <contents> }`.

View File

@@ -1,7 +1,7 @@
import { lstatSync } from 'fs';
import { readdir, readFile, writeFile } from 'fs/promises';
import path from 'path';
import { getCurrentDirPath } from '../../../scripts/helpers.mjs';
import { getCurrentDirPath } from '@lucide/helpers';
import { getJSBanner } from './license.mjs';
const currentDir = getCurrentDirPath(import.meta.url);
@@ -12,12 +12,15 @@ const files = await readdir(targetDirectory, {
encoding: 'utf-8',
});
// eslint-disable-next-line no-restricted-syntax
for (const file of files) {
const filepath = path.join(targetDirectory, file);
const filestat = lstatSync(filepath);
// eslint-disable-next-line no-continue
if (filestat.isFile() === false || filestat.isDirectory()) continue;
// eslint-disable-next-line no-await-in-loop
const contents = await readFile(filepath, { encoding: 'utf-8' });
let newContents = contents;
const ext = path.extname(filepath);
@@ -34,7 +37,8 @@ for (const file of files) {
// Places icon block comment at the top of the Svelte component class
if (/icons\/(.*?)\.svelte\.d\.ts/.test(filepath)) {
const svelteFilepath = filepath.replace('.d.ts', '');
let svelteFileContents = await readFile(svelteFilepath, { encoding: 'utf-8' });
// eslint-disable-next-line no-await-in-loop
const svelteFileContents = await readFile(svelteFilepath, { encoding: 'utf-8' });
const blockCommentRegex = /\/\*\*\n\s\*\s(@component\s@name)[\s\S]*?\*\//;
const blockCommentMatch = blockCommentRegex.exec(svelteFileContents);
@@ -54,6 +58,7 @@ for (const file of files) {
}
if (newContents !== contents) {
// eslint-disable-next-line no-await-in-loop
await writeFile(filepath, newContents, { encoding: 'utf-8' });
}
}

278
pnpm-lock.yaml generated
View File

@@ -16,6 +16,12 @@ importers:
'@html-eslint/parser':
specifier: ^0.19.1
version: 0.19.1
'@lucide/build-icons':
specifier: workspace:*
version: link:tools/build-icons
'@lucide/helpers':
specifier: workspace:*
version: link:tools/build-helpers
'@octokit/rest':
specifier: ^19.0.13
version: 19.0.13
@@ -64,9 +70,6 @@ importers:
minimist:
specifier: ^1.2.8
version: 1.2.8
node-fetch:
specifier: ^3.3.2
version: 3.3.2
p-memoize:
specifier: ^7.1.1
version: 7.1.1
@@ -112,9 +115,6 @@ importers:
fuse.js:
specifier: ^6.5.3
version: 6.6.2
js-yaml:
specifier: ^4.1.0
version: 4.1.0
jszip:
specifier: ^3.7.0
version: 3.10.1
@@ -161,6 +161,15 @@ importers:
specifier: ^3.4.13
version: 3.4.18(typescript@4.9.5)
devDependencies:
'@lucide/build-icons':
specifier: workspace:*
version: link:../tools/build-icons
'@lucide/helpers':
specifier: workspace:*
version: link:../tools/build-helpers
'@lucide/shared':
specifier: workspace:*
version: link:../packages/shared
'@rollup/plugin-replace':
specifier: ^5.0.2
version: 5.0.2
@@ -322,43 +331,6 @@ importers:
version: 0.11.8
publishDirectory: dist
packages/lucide-figma:
dependencies:
minimist:
specifier: ^1.2.6
version: 1.2.8
react:
specifier: ^17.0.0
version: 17.0.2
react-dom:
specifier: ^17.0.0
version: 17.0.2(react@17.0.2)
devDependencies:
'@figma/plugin-typings':
specifier: ^1.36.0
version: 1.72.0
'@lucide/shared':
specifier: workspace:*
version: link:../shared
'@types/react':
specifier: ^17.0.0
version: 17.0.62
'@types/react-dom':
specifier: ^17.0.0
version: 17.0.20
'@vitejs/plugin-react':
specifier: ^1.0.0
version: 1.3.2
typescript:
specifier: ^4.3.2
version: 4.9.5
vite:
specifier: 5.0.13
version: 5.0.13
vite-plugin-singlefile:
specifier: ^0.5.1
version: 0.5.1(vite@5.0.13)
packages/lucide-preact:
devDependencies:
'@lucide/build-icons':
@@ -724,8 +696,13 @@ importers:
specifier: ^3.25.4
version: 3.25.4
tools/build-helpers: {}
tools/build-icons:
dependencies:
'@lucide/helpers':
specifier: ^1.0.0
version: link:../build-helpers
minimist:
specifier: ^1.2.7
version: 1.2.8
@@ -2441,16 +2418,6 @@ packages:
'@babel/helper-plugin-utils': 7.22.5
dev: true
/@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.22.9):
resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.22.9
'@babel/helper-plugin-utils': 7.22.5
dev: true
/@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.9):
resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==}
engines: {node: '>=6.9.0'}
@@ -3482,16 +3449,6 @@ packages:
'@babel/helper-plugin-utils': 7.22.5
dev: true
/@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.22.9):
resolution: {integrity: sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.22.9
'@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.22.9)
dev: true
/@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.23.9):
resolution: {integrity: sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==}
engines: {node: '>=6.9.0'}
@@ -3502,16 +3459,6 @@ packages:
'@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.23.9)
dev: true
/@babel/plugin-transform-react-jsx-self@7.22.5(@babel/core@7.22.9):
resolution: {integrity: sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.22.9
'@babel/helper-plugin-utils': 7.22.5
dev: true
/@babel/plugin-transform-react-jsx-self@7.22.5(@babel/core@7.23.9):
resolution: {integrity: sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==}
engines: {node: '>=6.9.0'}
@@ -3532,16 +3479,6 @@ packages:
'@babel/helper-plugin-utils': 7.22.5
dev: true
/@babel/plugin-transform-react-jsx-source@7.22.5(@babel/core@7.22.9):
resolution: {integrity: sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.22.9
'@babel/helper-plugin-utils': 7.22.5
dev: true
/@babel/plugin-transform-react-jsx-source@7.22.5(@babel/core@7.23.9):
resolution: {integrity: sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==}
engines: {node: '>=6.9.0'}
@@ -3562,20 +3499,6 @@ packages:
'@babel/helper-plugin-utils': 7.22.5
dev: true
/@babel/plugin-transform-react-jsx@7.22.5(@babel/core@7.22.9):
resolution: {integrity: sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.22.9
'@babel/helper-annotate-as-pure': 7.22.5
'@babel/helper-module-imports': 7.22.15
'@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.22.9)
'@babel/types': 7.23.9
dev: true
/@babel/plugin-transform-react-jsx@7.22.5(@babel/core@7.23.9):
resolution: {integrity: sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==}
engines: {node: '>=6.9.0'}
@@ -5069,10 +4992,6 @@ packages:
engines: {node: '>=14'}
dev: true
/@figma/plugin-typings@1.72.0:
resolution: {integrity: sha512-SRW8qQOLf/QOtgP7OsUfw4oD6Z+tbenLR2PgNXGSh2SJohcnucIEEc7An5Wd5qTl35xfC4TmZE0YpTGMknVpgw==}
dev: true
/@floating-ui/core@1.6.0:
resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==}
dependencies:
@@ -6955,21 +6874,6 @@ packages:
rollup: 4.9.6
dev: true
/@rollup/plugin-node-resolve@11.2.1(rollup@2.79.1):
resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==}
engines: {node: '>= 10.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0
dependencies:
'@rollup/pluginutils': 3.1.0(rollup@2.79.1)
'@types/resolve': 1.17.1
builtin-modules: 3.3.0
deepmerge: 4.3.1
is-module: 1.0.0
resolve: 1.22.8
rollup: 2.79.1
dev: true
/@rollup/plugin-node-resolve@13.3.0(rollup@2.79.1):
resolution: {integrity: sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==}
engines: {node: '>= 10.0.0'}
@@ -7872,26 +7776,12 @@ packages:
resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==}
dev: true
/@types/react-dom@17.0.20:
resolution: {integrity: sha512-4pzIjSxDueZZ90F52mU3aPoogkHIoSIDG+oQ+wQK7Cy2B9S+MvOqY0uEA/qawKz381qrEDkvpwyt8Bm31I8sbA==}
dependencies:
'@types/react': 17.0.62
dev: true
/@types/react-dom@18.2.7:
resolution: {integrity: sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==}
dependencies:
'@types/react': 18.2.55
dev: true
/@types/react@17.0.62:
resolution: {integrity: sha512-eANCyz9DG8p/Vdhr0ZKST8JV12PhH2ACCDYlFw6DIO+D+ca+uP4jtEDEpVqXZrh/uZdXQGwk7whJa3ah5DtyLw==}
dependencies:
'@types/prop-types': 15.7.5
'@types/scheduler': 0.16.3
csstype: 3.1.2
dev: true
/@types/react@18.2.17:
resolution: {integrity: sha512-u+e7OlgPPh+aryjOm5UJMX32OvB2E3QASOAqVMY6Ahs90djagxwv2ya0IctglNbNTexC12qCSMZG47KPfy1hAA==}
dependencies:
@@ -8368,22 +8258,6 @@ packages:
- supports-color
dev: true
/@vitejs/plugin-react@1.3.2:
resolution: {integrity: sha512-aurBNmMo0kz1O4qRoY+FM4epSA39y3ShWGuqfLRA/3z0oEJAdtoSfgA3aO98/PCCHAqMaduLxIxErWrVKIFzXA==}
engines: {node: '>=12.0.0'}
dependencies:
'@babel/core': 7.22.9
'@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.22.9)
'@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.22.9)
'@babel/plugin-transform-react-jsx-self': 7.22.5(@babel/core@7.22.9)
'@babel/plugin-transform-react-jsx-source': 7.22.5(@babel/core@7.22.9)
'@rollup/pluginutils': 4.2.1
react-refresh: 0.13.0
resolve: 1.22.3
transitivePeerDependencies:
- supports-color
dev: true
/@vitejs/plugin-react@4.2.1(vite@5.0.13):
resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -11039,6 +10913,7 @@ packages:
/data-uri-to-buffer@4.0.1:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
dev: false
/data-urls@3.0.2:
resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==}
@@ -11311,8 +11186,8 @@ packages:
resolution: {integrity: sha512-M1Ob1zPSIvlARiJUkKqvAZ3VAqQY6Jcuth/pBKQ2b1dX/Qx0OnJ8Vux6J2H5PTMQeRzWrrbTu70VxBfv/OPDJA==}
dev: true
/destr@2.0.2:
resolution: {integrity: sha512-65AlobnZMiCET00KaFFjUefxDX0khFA/E4myqZ7a6Sq1yZtR8+FVIvilVX66vF2uobSumxooYZChiRPCKNqhmg==}
/destr@2.0.3:
resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==}
dev: true
/destroy@1.2.0:
@@ -12334,12 +12209,6 @@ packages:
'@esbuild/win32-ia32': 0.19.12
'@esbuild/win32-x64': 0.19.12
/esbuild@0.9.7:
resolution: {integrity: sha512-VtUf6aQ89VTmMLKrWHYG50uByMF4JQlVysb8dmg6cOgW8JnFCipmz7p+HNBl+RR3LLCuBxFGVauAe2wfnF9bLg==}
hasBin: true
requiresBuild: true
dev: true
/escalade@3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
engines: {node: '>=6'}
@@ -13005,6 +12874,7 @@ packages:
dependencies:
node-domexception: 1.0.0
web-streams-polyfill: 3.2.1
dev: false
/figures@3.2.0:
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
@@ -13199,6 +13069,7 @@ packages:
engines: {node: '>=12.20.0'}
dependencies:
fetch-blob: 3.2.0
dev: false
/forwarded@0.2.0:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
@@ -13619,10 +13490,10 @@ packages:
dependencies:
cookie-es: 1.0.0
defu: 6.1.4
destr: 2.0.2
iron-webcrypto: 1.0.0
destr: 2.0.3
iron-webcrypto: 1.1.0
ohash: 1.1.3
radix3: 1.1.0
radix3: 1.1.2
ufo: 1.4.0
uncrypto: 0.1.3
unenv: 1.9.0
@@ -14261,8 +14132,8 @@ packages:
resolution: {integrity: sha512-gScdcWHjTGclCU15CIv2r069NoQrys1UeUFFfaO1hL++ytLHkVw7N5nXJmFf3J2LEDMz1PkrvC0m62JEeu1axQ==}
dev: true
/iron-webcrypto@1.0.0:
resolution: {integrity: sha512-anOK1Mktt8U1Xi7fCM3RELTuYbnFikQY5VtrDj7kPgpejV7d43tWKhzgioO0zpkazLEL/j/iayRqnJhrGfqUsg==}
/iron-webcrypto@1.1.0:
resolution: {integrity: sha512-5vgYsCakNlaQub1orZK5QmNYhwYtcllTkZBp5sfIaCqY93Cf6l+v2rtE+E4TMbcfjxDMCdrO8wmp7+ZvhDECLA==}
dev: true
/is-accessor-descriptor@0.1.6:
@@ -14863,11 +14734,6 @@ packages:
'@sideway/pinpoint': 2.0.0
dev: true
/joycon@3.1.1:
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
engines: {node: '>=10'}
dev: true
/jpeg-js@0.4.4:
resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==}
dev: false
@@ -14903,6 +14769,7 @@ packages:
hasBin: true
dependencies:
argparse: 2.0.1
dev: true
/jsc-android@250231.0.0:
resolution: {integrity: sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw==}
@@ -16527,7 +16394,7 @@ packages:
consola: 3.2.3
cookie-es: 1.0.0
defu: 6.1.4
destr: 2.0.2
destr: 2.0.3
dot-prop: 8.0.2
esbuild: 0.19.12
escape-string-regexp: 5.0.0
@@ -16556,7 +16423,7 @@ packages:
perfect-debounce: 1.0.0
pkg-types: 1.0.3
pretty-bytes: 6.1.1
radix3: 1.1.0
radix3: 1.1.2
rollup: 4.9.6
rollup-plugin-visualizer: 5.12.0(rollup@4.9.6)
scule: 1.3.0
@@ -16620,6 +16487,7 @@ packages:
/node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
dev: false
/node-fetch-native@1.4.0:
resolution: {integrity: sha512-F5kfEj95kX8tkDhUCYdV8dg3/8Olx/94zB8+ZNthFs6Bz31UpUi8Xh40TN3thLwXgrwXry1pEg9lJ++tLWTcqA==}
@@ -16660,6 +16528,7 @@ packages:
data-uri-to-buffer: 4.0.1
fetch-blob: 3.2.0
formdata-polyfill: 4.0.10
dev: false
/node-forge@1.3.1:
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
@@ -17056,7 +16925,7 @@ packages:
/ofetch@1.3.3:
resolution: {integrity: sha512-s1ZCMmQWXy4b5K/TW9i/DtiN8Ku+xCiHcjQ6/J/nDdssirrQNOoB165Zu8EqLMA2lln1JUth9a0aW9Ap2ctrUg==}
dependencies:
destr: 2.0.2
destr: 2.0.3
node-fetch-native: 1.6.2
ufo: 1.4.0
dev: true
@@ -18548,8 +18417,8 @@ packages:
resolution: {integrity: sha512-y+AcwZ3HcUIGc9zGsNVf5+BY/LxL+z+4h4J3/pp8jxSmy1STaCocPS3qrj4tA5ehUSzqtqK+0Aygvz/r/8vy4g==}
dev: true
/radix3@1.1.0:
resolution: {integrity: sha512-pNsHDxbGORSvuSScqNJ+3Km6QAVqk8CfsCBIEoDgpqLrkD2f3QM4I7d1ozJJ172OmIcoUcerZaNWqtLkRXTV3A==}
/radix3@1.1.2:
resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
dev: true
/randombytes@2.1.0:
@@ -18587,7 +18456,7 @@ packages:
resolution: {integrity: sha512-lNeOl38Ws0eNxpO3+wD1I9rkHGQyj1NU1jlzv4go2CtEnEQEUfqnIvZG7W+bC/aXdJ27n5x/yUjb6RoT9tko+Q==}
dependencies:
defu: 6.1.4
destr: 2.0.2
destr: 2.0.3
flat: 5.0.2
dev: true
@@ -18601,17 +18470,6 @@ packages:
- utf-8-validate
dev: true
/react-dom@17.0.2(react@17.0.2):
resolution: {integrity: sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==}
peerDependencies:
react: 17.0.2
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
react: 17.0.2
scheduler: 0.20.2
dev: false
/react-dom@18.2.0(react@18.2.0):
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
peerDependencies:
@@ -18700,11 +18558,6 @@ packages:
- utf-8-validate
dev: true
/react-refresh@0.13.0:
resolution: {integrity: sha512-XP8A9BT0CpRBD+NYLLeIhld/RqG9+gktUjW1FkE+Vm7OCinbG1SshcK5tb9ls4kzvjZr9mOQc7HYgBngEyPAXg==}
engines: {node: '>=0.10.0'}
dev: true
/react-refresh@0.14.0:
resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==}
engines: {node: '>=0.10.0'}
@@ -18720,14 +18573,6 @@ packages:
react-is: 18.2.0
dev: true
/react@17.0.2:
resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==}
engines: {node: '>=0.10.0'}
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
dev: false
/react@18.2.0:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
engines: {node: '>=0.10.0'}
@@ -18988,15 +18833,6 @@ packages:
supports-preserve-symlinks-flag: 1.0.0
dev: true
/resolve@1.22.3:
resolution: {integrity: sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==}
hasBin: true
dependencies:
is-core-module: 2.13.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
dev: true
/resolve@1.22.4:
resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==}
hasBin: true
@@ -19111,20 +18947,6 @@ packages:
'@babel/code-frame': 7.23.5
dev: true
/rollup-plugin-esbuild@3.0.4(esbuild@0.9.7)(rollup@2.79.1):
resolution: {integrity: sha512-Txe/qWTx4NykWLwHjQ8vxXB1Mh6nTWnk7nl9lTYBN0DKnbvnSz4u2qiLqceLMZXTk//iYP5jnxlBNciNBp57LQ==}
engines: {node: '>=12'}
peerDependencies:
esbuild: '>=0.9.0'
rollup: ^1.20.0 || ^2.0.0
dependencies:
'@rollup/pluginutils': 4.2.1
esbuild: 0.9.7
joycon: 3.1.1
jsonc-parser: 3.2.1
rollup: 2.79.1
dev: true
/rollup-plugin-esbuild@6.1.1(esbuild@0.19.12)(rollup@4.9.6):
resolution: {integrity: sha512-CehMY9FAqJD5OUaE/Mi1r5z0kNeYxItmRO2zG4Qnv2qWKF09J2lTy5GUzjJR354ZPrLkCj4fiBN41lo8PzBUhw==}
engines: {node: '>=14.18.0'}
@@ -19415,13 +19237,6 @@ packages:
xmlchars: 2.2.0
dev: true
/scheduler@0.20.2:
resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==}
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
dev: false
/scheduler@0.23.0:
resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
dependencies:
@@ -21506,7 +21321,7 @@ packages:
dependencies:
anymatch: 3.1.3
chokidar: 3.5.3
destr: 2.0.2
destr: 2.0.3
h3: 1.10.1
ioredis: 5.3.2
listhen: 1.5.5
@@ -21714,18 +21529,6 @@ packages:
- terser
dev: true
/vite-plugin-singlefile@0.5.1(vite@5.0.13):
resolution: {integrity: sha512-yA9lWd6bSet0Br4/s34YPNnVBlDl2MbxlHDRrLrBCncD7q+HO5GGsw29Ymp+ydZ3eb4UU2GECgX2MJZW+qnoeQ==}
peerDependencies:
vite: ^2.1.2
dependencies:
'@rollup/plugin-node-resolve': 11.2.1(rollup@2.79.1)
esbuild: 0.9.7
rollup: 2.79.1
rollup-plugin-esbuild: 3.0.4(esbuild@0.9.7)(rollup@2.79.1)
vite: 5.0.13
dev: true
/vite-plugin-solid@2.10.1(@testing-library/jest-dom@6.4.2)(solid-js@1.8.14)(vite@5.0.13):
resolution: {integrity: sha512-kfVdNLWaJqaJVL52U6iCCKNW/nXE7bS1VVGOWPGllOkJfcNILymVSY0LCBLSnyy0iYnRtrXpiHm14rMuzeC7CA==}
peerDependencies:
@@ -22243,6 +22046,7 @@ packages:
/web-streams-polyfill@3.2.1:
resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
engines: {node: '>= 8'}
dev: false
/webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}

View File

@@ -1,5 +1,5 @@
import path from 'path';
import { getCurrentDirPath, readAllMetadata, readSvgDirectory, writeFile } from './helpers.mjs';
import { getCurrentDirPath, readAllMetadata, readSvgDirectory, writeFile } from '@lucide/helpers';
const currentDir = getCurrentDirPath(import.meta.url);
const ICONS_DIR = path.resolve(currentDir, '../icons');

View File

@@ -1,65 +0,0 @@
import fs from 'fs';
import path from 'path';
import getArgumentOptions from 'minimist';
import renderIconsObject from './render/renderIconsObject.mjs';
import generateIconFiles from './building/generateIconFiles.mjs';
import generateExportsFile from './building/generateExportsFile.mjs';
import { readSvgDirectory, getCurrentDirPath } from './helpers.mjs';
const cliArguments = getArgumentOptions(process.argv.slice(2));
const currentDir = getCurrentDirPath(import.meta.url);
const ICONS_DIR = path.resolve(currentDir, '../icons');
const OUTPUT_DIR = path.resolve(process.cwd(), cliArguments.output || '../build');
if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR);
}
const {
renderUniqueKey = false,
templateSrc,
silent = false,
iconFileExtension = '.js',
exportFileName = 'index.js',
pretty = true,
} = cliArguments;
async function buildIcons() {
if (templateSrc == null) {
throw new Error('No `templateSrc` argument given.');
}
const svgFiles = readSvgDirectory(ICONS_DIR);
const icons = renderIconsObject(svgFiles, ICONS_DIR, renderUniqueKey);
const { default: iconFileTemplate } = await import(path.resolve(process.cwd(), templateSrc));
// Generates iconsNodes files for each icon
generateIconFiles({
iconNodes: icons,
outputDirectory: OUTPUT_DIR,
template: iconFileTemplate,
showLog: !silent,
iconFileExtension,
pretty: JSON.parse(pretty),
});
// Generates entry files for the compiler filled with icons exports
generateExportsFile(
path.join(OUTPUT_DIR, 'icons', exportFileName),
path.join(OUTPUT_DIR, 'icons'),
icons,
iconFileExtension,
);
}
try {
buildIcons();
} catch (error) {
console.error(error);
}

View File

@@ -1,5 +1,5 @@
import path from 'path';
import { readSvgDirectory, getCurrentDirPath, readAllMetadata } from './helpers.mjs';
import { readSvgDirectory, getCurrentDirPath, readAllMetadata } from '@lucide/helpers';
const currentDir = getCurrentDirPath(import.meta.url);
const ICONS_DIR = path.resolve(currentDir, '../icons');

View File

@@ -1,5 +1,5 @@
import path from 'path';
import { getCurrentDirPath, writeFileIfNotExists } from '../helpers.mjs';
import { getCurrentDirPath, writeFileIfNotExists } from '@lucide/helpers';
const currentDir = getCurrentDirPath(import.meta.url);
const ICONS_DIR = path.resolve(currentDir, '../../icons');

View File

@@ -2,12 +2,12 @@ import fs from 'fs';
import path from 'path';
import { parseSync } from 'svgson';
import {
shuffle,
shuffleArray,
readSvgDirectory,
getCurrentDirPath,
minifySvg,
toPascalCase,
} from './helpers.mjs';
} from '@lucide/helpers';
const currentDir = getCurrentDirPath(import.meta.url);
const ICONS_DIR = path.resolve(currentDir, '../icons');
@@ -38,12 +38,12 @@ const svgFiles = readSvgDirectory(ICONS_DIR).map((file) => `icons/${file}`);
const iconsFilteredByName = (search) => svgFiles.filter((file) => file.includes(search));
const cohesionRandomImageTags = getImageTagsByFiles(
shuffle(svgFiles).slice(0, changedFiles.length),
shuffleArray(svgFiles).slice(0, changedFiles.length),
() => `${BASE_URL}/stroke-width/2`,
).join('');
const cohesionSquaresImageTags = getImageTagsByFiles(
shuffle(iconsFilteredByName('square')).slice(0, changedFiles.length),
shuffleArray(iconsFilteredByName('square')).slice(0, changedFiles.length),
() => `${BASE_URL}/stroke-width/2`,
).join('');

View File

@@ -1,6 +1,6 @@
import path from 'path';
import { promises as fs } from 'fs';
import { getCurrentDirPath, readSvgDirectory } from './helpers.mjs';
import { getCurrentDirPath, readSvgDirectory } from '@lucide/helpers';
// This is a special case convertion NextJS uses for their modularize imports. We try to follow the same convention, to generate the same imports.
function pascalToKebabNextJSFlavour(str) {

View File

@@ -1,60 +0,0 @@
import path from 'path';
import { stringify, parseSync } from 'svgson';
import * as prettier from 'prettier';
import { appendFile, readSvgDirectory, getCurrentDirPath } from './helpers.mjs';
import readSvgs from '../packages/lucide-static/scripts/readSvgs.mjs';
const currentDir = getCurrentDirPath(import.meta.url);
const ICONS_DIR = path.resolve('icons');
const PACKAGE_DIR = path.resolve(currentDir);
async function generateSprite(svgs, packageDir) {
const symbols = svgs.map(({ parsedSvg }, index) => {
const itemsPerRow = 10;
const numInRow = index % itemsPerRow;
const column = numInRow + 1;
const row = (index - numInRow) / itemsPerRow + 1;
return {
name: 'g',
type: 'element',
attributes: {
transform: `translate(${column * 24 - 24},${row * 24 - 24})`,
},
children: [parsedSvg],
};
});
const spriteSvgObject = {
name: 'svg',
type: 'element',
attributes: {
xmlns: 'http://www.w3.org/2000/svg',
version: '1.1',
},
children: symbols,
};
const spriteSvg = stringify(spriteSvgObject);
const prettifiedSprite = (await prettier.format(spriteSvg, { parser: 'babel' })).replace(
/;/g,
'',
);
const xmlMeta = `<?xml version="1.0" encoding="utf-8"?>\n`;
appendFile(xmlMeta, `super-sprite.svg`, packageDir);
appendFile(prettifiedSprite, `super-sprite.svg`, packageDir);
}
const svgFiles = readSvgDirectory(ICONS_DIR);
const svgs = readSvgs(svgFiles, ICONS_DIR);
const parsedSvgs = svgs.map(({ name, contents }) => ({
name,
contents,
parsedSvg: parseSync(contents),
}));
await generateSprite(parsedSvgs, PACKAGE_DIR);

View File

@@ -1,5 +1,3 @@
import fetch, { Headers } from 'node-fetch';
const githubApi = async (endpoint) => {
const headers = new Headers();
const username = 'ericfennis';

View File

@@ -1,226 +0,0 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
/**
* Converts string to CamelCase
*
* @param {string} string
* @returns {string} A camelized string
*/
export const toCamelCase = (string) =>
string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) =>
p2 ? p2.toUpperCase() : p1.toLowerCase(),
);
/**
* Converts string to PascalCase
*
* @param {string} string
* @returns {string} A pascalized string
*/
export const toPascalCase = (string) => {
const camelCase = toCamelCase(string);
return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
};
/**
* Converts string to KebabCase
*
* @param {string} string
* @returns {string} A kebabized string
*/
export const toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
/**
* Resets the file contents.
*
* @param {string} fileName
* @param {string} outputDirectory
*/
export const resetFile = (fileName, outputDirectory) =>
fs.writeFileSync(path.join(outputDirectory, fileName), '', 'utf-8');
/**
* Reads the file contents.
*
* @param {string} path
* @returns {string} The contents of a file
*/
export const readFile = (path) => fs.readFileSync(path.resolve(__dirname, '../', path), 'utf-8');
/**
* append content to a file
*
* @param {string} content
* @param {string} fileName
* @param {string} outputDirectory
*/
export const appendFile = (content, fileName, outputDirectory) =>
fs.appendFileSync(path.join(outputDirectory, fileName), content, 'utf-8');
/**
* writes content to a file
*
* @param {string} content
* @param {string} fileName
* @param {string} outputDirectory
*/
export const writeFile = (content, fileName, outputDirectory) =>
fs.writeFileSync(path.join(outputDirectory, fileName), content, 'utf-8');
/**
* writes content to a file if it does not exist
*
* @param {string} content
* @param {string} fileName
* @param {string} outputDirectory
*/
export const writeFileIfNotExists = (content, fileName, outputDirectory) => {
if (!fs.existsSync(path.join(outputDirectory, fileName))) {
writeFile(content, fileName, outputDirectory);
}
};
/**
* Reads metadata from the icons/categories directories
*
* @param {string} directory
* @returns {object} A map of icon or category metadata
*/
export const readAllMetadata = (directory) =>
fs
.readdirSync(directory)
.filter((file) => path.extname(file) === '.json')
.reduce((acc, fileName) => {
acc[path.basename(fileName, '.json')] = readMetadata(fileName, directory);
return acc;
}, {});
/**
* Reads metadata for an icon or category
*
* @param {string} fileName
* @param {string} directory
* @returns {object} The metadata for the icon or category
*/
export const readMetadata = (fileName, directory) =>
JSON.parse(fs.readFileSync(path.join(directory, fileName), 'utf-8'));
/**
* reads the icon directory
*
* @param {string} directory
* @param {string} fileExtension
* @returns {array} An array of file paths containing svgs
*/
export const readSvgDirectory = (directory, fileExtension = '.svg') =>
fs.readdirSync(directory).filter((file) => path.extname(file) === fileExtension);
/**
* Read svg from directory
*
* @param {string} fileName
* @param {string} directory
*/
export const readSvg = (fileName, directory) =>
fs.readFileSync(path.join(directory, fileName), 'utf-8');
/**
* writes content to a file
*
* @param {string} fileName
* @param {string} outputDirectory
* @param {string} content
*/
export const writeSvgFile = (fileName, outputDirectory, content) =>
fs.writeFileSync(path.join(outputDirectory, fileName), content, 'utf-8');
/**
* djb2 hashing function
*
* @param {string} string
* @param {number} seed
* @returns {string} A hashed string of 6 characters
*/
export const hash = (string, seed = 5381) => {
let i = string.length;
while (i) {
// eslint-disable-next-line no-bitwise, no-plusplus
seed = (seed * 33) ^ string.charCodeAt(--i);
}
// eslint-disable-next-line no-bitwise
return (seed >>> 0).toString(36).substr(0, 6);
};
/**
* Generate Hashed string based on name and attributes
*
* @param {object} seed
* @param {string} seed.name A name, for example an icon name
* @param {object} seed.attributes An object of SVGElement Attrbutes
* @returns {string} A hashed string of 6 characters
*/
export const generateHashedKey = ({ name, attributes }) => hash(JSON.stringify([name, attributes]));
/**
* Checks if array of items contains duplicated items
*
* @param {array} children an array of items
* @returns {Boolean} if items contains duplicated items.
*/
export const hasDuplicatedChildren = (children) => {
const hashedKeys = children.map(generateHashedKey);
return !hashedKeys.every(
(key, index) => index === hashedKeys.findIndex((childKey) => childKey === key),
);
};
/**
* @param {array} a
* @param {array} b
* @returns {array}
*/
export const mergeArrays = (a, b) => {
a = a.concat(b);
a = a.filter((i, p) => a.indexOf(i) === p);
return a;
};
/**
* @param {string} currentPath
* @returns {string}
*/
export const getCurrentDirPath = (currentPath) => path.dirname(fileURLToPath(currentPath));
/**
* @param {array} array
* @returns {array}
*/
export const shuffle = (array) => {
// eslint-disable-next-line no-plusplus
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
};
/**
* Minifies SVG
*
* @param {string} string
* @returns string
*/
export function minifySvg(string) {
return string
? string
.replace(/>[\r\n ]+</g, '><')
.replace(/(<.*?>)|\s+/g, (m, $1) => $1 || ' ')
.trim()
: '';
}

View File

@@ -1,6 +1,6 @@
import path from 'path';
import categories from '../categories.json' assert { type: 'json' };
import { mergeArrays, writeFile, readAllMetadata, getCurrentDirPath } from './helpers.mjs';
import { mergeArrays, writeFile, readAllMetadata, getCurrentDirPath } from '@lucide/helpers';
const currentDir = getCurrentDirPath(import.meta.url);
const ICONS_DIR = path.resolve(currentDir, '../icons');

View File

@@ -1,5 +1,5 @@
import path from 'path';
import { writeFile, getCurrentDirPath, readAllMetadata } from './helpers.mjs';
import { writeFile, getCurrentDirPath, readAllMetadata } from '@lucide/helpers';
const currentDir = getCurrentDirPath(import.meta.url);
const ICONS_DIR = path.resolve(currentDir, '../icons');

View File

@@ -1,5 +1,5 @@
import path from 'path';
import { writeFile, getCurrentDirPath, readAllMetadata } from './helpers.mjs';
import { writeFile, getCurrentDirPath, readAllMetadata } from '@lucide/helpers';
const currentDir = getCurrentDirPath(import.meta.url);
const ICONS_DIR = path.resolve(currentDir, '../icons');

View File

@@ -6,7 +6,7 @@ import {
writeFile,
mergeArrays,
getCurrentDirPath,
} from './helpers.mjs';
} from '@lucide/helpers';
const currentDir = getCurrentDirPath(import.meta.url);
const ICONS_DIR = path.resolve(currentDir, '../icons');

View File

@@ -1,7 +1,7 @@
import fs from 'fs';
import path from 'path';
import { readSvgDirectory, writeSvgFile } from '@lucide/helpers';
import processSvg from './render/processSvg.mjs';
import { readSvgDirectory, writeSvgFile } from './helpers.mjs';
const ICONS_DIR = path.resolve(process.cwd(), 'icons');

View File

@@ -1,5 +1,5 @@
import path from 'path';
import { getCurrentDirPath } from '../helpers.mjs';
import { getCurrentDirPath } from '@lucide/helpers';
import { renameIcon } from './renameIcon.function.mjs';
async function main() {

View File

@@ -1,8 +1,8 @@
import path from 'path';
import { getCurrentDirPath, readSvgDirectory } from '../helpers.mjs';
import { renameIcon } from './renameIcon.function.mjs';
import { getCurrentDirPath, readSvgDirectory } from '@lucide/helpers';
import yargs from 'yargs/yargs';
import { hideBin } from 'yargs/helpers';
import { renameIcon } from './renameIcon.function.mjs';
async function main() {
const currentDir = getCurrentDirPath(import.meta.url);
@@ -38,7 +38,7 @@ async function main() {
process.exit(1);
}
for (let oldName of iconNames.filter((name) => pattern.test(name))) {
for (const oldName of iconNames.filter((name) => pattern.test(name))) {
const newName = oldName.replaceAll(pattern, replacement);
console.log(`Renaming ${oldName} => ${newName}`);

View File

@@ -1,11 +0,0 @@
{
"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"
}

View File

@@ -1,7 +1,7 @@
import { optimize } from 'svgo';
import * as prettier from 'prettier';
import { parseSync, stringify } from 'svgson';
import DEFAULT_ATTRS from './default-attrs.json' assert { type: 'json' };
import DEFAULT_ATTRS from '@lucide/build-icons/render/default-attrs.json' assert { type: 'json' };
/**
* Optimize SVG with `svgo`.

View File

@@ -1,39 +0,0 @@
import { basename } from 'path';
import { parseSync } from 'svgson';
import { generateHashedKey, readSvg, hasDuplicatedChildren } from '../helpers.mjs';
/**
* Build an object in the format: `{ <name>: <contents> }`.
* @param {string[]} svgFiles - A list of filenames.
* @param {Function} getSvg - A function that returns the contents of an SVG file given a filename.
* @returns {Object}
*/
export default (svgFiles, iconsDirectory, renderUniqueKey = false) =>
svgFiles
.map((svgFile) => {
const name = basename(svgFile, '.svg');
const svg = readSvg(svgFile, iconsDirectory);
const contents = parseSync(svg);
if (!(contents.children && contents.children.length)) {
throw new Error(`${name}.svg has no children!`);
}
if (hasDuplicatedChildren(contents.children)) {
throw new Error(`Duplicated children in ${name}.svg`);
}
if (renderUniqueKey) {
contents.children = contents.children.map((child) => {
child.attributes.key = generateHashedKey(child);
return child;
});
}
return { name, contents };
})
.reduce((icons, icon) => {
icons[icon.name] = icon.contents;
return icons;
}, {});

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 807 KiB

View File

@@ -1,11 +0,0 @@
export default ({ componentName, children }) => `
import defaultAttributes from '../defaultAttributes';
const ${componentName} = [
'svg',
defaultAttributes,
${JSON.stringify(children)}
];
export default ${componentName};
`;

View File

@@ -0,0 +1,20 @@
export * from './src/toCamelCase.mjs';
export * from './src/toPascalCase.mjs';
export * from './src/toKebabCase.mjs';
export * from './src/resetFile.mjs';
export * from './src/readFile.mjs';
export * from './src/appendFile.mjs';
export * from './src/writeFile.mjs';
export * from './src/writeFileIfNotExists.mjs';
export * from './src/readAllMetadata.mjs';
export * from './src/readMetadata.mjs';
export * from './src/readSvgDirectory.mjs';
export * from './src/readSvg.mjs';
export * from './src/writeSvgFile.mjs';
export * from './src/hash.mjs';
export * from './src/generateHashedKey.mjs';
export * from './src/hasDuplicatedChildren.mjs';
export * from './src/mergeArrays.mjs';
export * from './src/getCurrentDirPath.mjs';
export * from './src/minifySvg.mjs';
export * from './src/shuffleArray.mjs';

View File

@@ -0,0 +1,11 @@
{
"name": "@lucide/helpers",
"private": true,
"version": "1.0.0",
"description": "A internal used package with helpers.",
"main": "main.mjs",
"types": "module",
"author": "",
"license": "ISC",
"type": "module"
}

View File

@@ -0,0 +1,13 @@
/* eslint-disable import/prefer-default-export */
import fs from 'fs';
import path from 'path';
/**
* append content to a file
*
* @param {string} content
* @param {string} fileName
* @param {string} outputDirectory
*/
export const appendFile = (content, fileName, outputDirectory) =>
fs.appendFileSync(path.join(outputDirectory, fileName), content, 'utf-8');

View File

@@ -0,0 +1,12 @@
/* eslint-disable import/prefer-default-export */
import { hash } from './hash.mjs';
/**
* Generate Hashed string based on name and attributes
*
* @param {object} seed
* @param {string} seed.name A name, for example an icon name
* @param {object} seed.attributes An object of SVGElement Attrbutes
* @returns {string} A hashed string of 6 characters
*/
export const generateHashedKey = ({ name, attributes }) => hash(JSON.stringify([name, attributes]));

View File

@@ -0,0 +1,11 @@
/* eslint-disable import/prefer-default-export */
import path from 'path';
import { fileURLToPath } from 'url';
/**
* Get the current directory path.
*
* @param {string} currentPath
* @returns {string}
*/
export const getCurrentDirPath = (currentPath) => path.dirname(fileURLToPath(currentPath));

View File

@@ -0,0 +1,16 @@
/* eslint-disable import/prefer-default-export */
import { generateHashedKey } from './generateHashedKey.mjs';
/**
* Checks if array of items contains duplicated items
*
* @param {array} children an array of items
* @returns {Boolean} if items contains duplicated items.
*/
export const hasDuplicatedChildren = (children) => {
const hashedKeys = children.map(generateHashedKey);
return !hashedKeys.every(
(key, index) => index === hashedKeys.findIndex((childKey) => childKey === key),
);
};

View File

@@ -0,0 +1,19 @@
/* eslint-disable import/prefer-default-export */
/**
* djb2 hashing function
*
* @param {string} string
* @param {number} seed
* @returns {string} A hashed string of 6 characters
*/
export const hash = (string, seed = 5381) => {
let i = string.length;
while (i) {
// eslint-disable-next-line no-bitwise, no-plusplus
seed = (seed * 33) ^ string.charCodeAt(--i);
}
// eslint-disable-next-line no-bitwise
return (seed >>> 0).toString(36).substr(0, 6);
};

View File

@@ -0,0 +1,13 @@
/* eslint-disable import/prefer-default-export */
/**
* Merge two arrays and remove duplicates
*
* @param {array} a
* @param {array} b
* @returns {array}
*/
export const mergeArrays = (a, b) => {
a = a.concat(b);
a = a.filter((i, p) => a.indexOf(i) === p);
return a;
};

View File

@@ -0,0 +1,15 @@
/* eslint-disable import/prefer-default-export */
/**
* Minifies SVG
*
* @param {string} string
* @returns string
*/
export function minifySvg(string) {
return string
? string
.replace(/>[\r\n ]+</g, '><')
.replace(/(<.*?>)|\s+/g, (m, $1) => $1 || ' ')
.trim()
: '';
}

View File

@@ -0,0 +1,19 @@
/* eslint-disable import/prefer-default-export */
import fs from 'fs';
import path from 'path';
import { readMetadata } from './readMetadata.mjs';
/**
* Reads metadata from the icons/categories directories
*
* @param {string} directory
* @returns {object} A map of icon or category metadata
*/
export const readAllMetadata = (directory) =>
fs
.readdirSync(directory)
.filter((file) => path.extname(file) === '.json')
.reduce((acc, fileName) => {
acc[path.basename(fileName, '.json')] = readMetadata(fileName, directory);
return acc;
}, {});

View File

@@ -0,0 +1,11 @@
/* eslint-disable import/prefer-default-export */
import fs from 'fs';
import path from 'path';
/**
* Reads the file contents.
*
* @param {string} path
* @returns {string} The contents of a file
*/
export const readFile = (path) => fs.readFileSync(path.resolve(__dirname, '../', path), 'utf-8');

View File

@@ -0,0 +1,13 @@
/* eslint-disable import/prefer-default-export */
import fs from 'fs';
import path from 'path';
/**
* Reads metadata for an icon or category
*
* @param {string} fileName
* @param {string} directory
* @returns {object} The metadata for the icon or category
*/
export const readMetadata = (fileName, directory) =>
JSON.parse(fs.readFileSync(path.join(directory, fileName), 'utf-8'));

View File

@@ -0,0 +1,12 @@
/* eslint-disable import/prefer-default-export */
import fs from 'fs';
import path from 'path';
/**
* Read svg from directory
*
* @param {string} fileName
* @param {string} directory
*/
export const readSvg = (fileName, directory) =>
fs.readFileSync(path.join(directory, fileName), 'utf-8');

View File

@@ -0,0 +1,13 @@
/* eslint-disable import/prefer-default-export */
import fs from 'fs';
import path from 'path';
/**
* reads the icon directory
*
* @param {string} directory
* @param {string} fileExtension
* @returns {array} An array of file paths containing svgs
*/
export const readSvgDirectory = (directory, fileExtension = '.svg') =>
fs.readdirSync(directory).filter((file) => path.extname(file) === fileExtension);

View File

@@ -0,0 +1,12 @@
/* eslint-disable import/prefer-default-export */
import fs from 'fs';
import path from 'path';
/**
* Resets the file contents.
*
* @param {string} fileName
* @param {string} outputDirectory
*/
export const resetFile = (fileName, outputDirectory) =>
fs.writeFileSync(path.join(outputDirectory, fileName), '', 'utf-8');

View File

@@ -0,0 +1,13 @@
/* eslint-disable import/prefer-default-export */
/**
* @param {array} array
* @returns {array}
*/
export const shuffle = (array) => {
// eslint-disable-next-line no-plusplus
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
};

View File

@@ -0,0 +1,11 @@
/* eslint-disable import/prefer-default-export */
/**
* Converts string to CamelCase
*
* @param {string} string
* @returns {string} A camelized string
*/
export const toCamelCase = (string) =>
string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) =>
p2 ? p2.toUpperCase() : p1.toLowerCase(),
);

View File

@@ -0,0 +1,8 @@
/* eslint-disable import/prefer-default-export */
/**
* Converts string to KebabCase
*
* @param {string} string
* @returns {string} A kebabized string
*/
export const toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();

View File

@@ -0,0 +1,14 @@
/* eslint-disable import/prefer-default-export */
import { toCamelCase } from './toCamelCase.mjs';
/**
* Converts string to PascalCase
*
* @param {string} string
* @returns {string} A pascalized string
*/
export const toPascalCase = (string) => {
const camelCase = toCamelCase(string);
return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
};

Some files were not shown because too many files have changed in this diff Show More