mirror of
https://github.com/lucide-icons/lucide.git
synced 2025-12-16 07:37:42 +01:00
chore(scripts): Refactor scripts to typescript (#3316)
* Adjust typescript types * adjust types * fix types in all helper files * Fix types * Migrate js files to ts files * Refactor to TS files * Rename extentions * Adjust imports * Fix builds * Update lockfile * Fix last typescript migration * Fix entry path @lucide/outline-svg * Fix types * add checkout step * format files * Format files
This commit is contained in:
@@ -26,38 +26,63 @@ async function getReleaseMetaData() {
|
||||
return releaseMetaData;
|
||||
}
|
||||
|
||||
function convertReleaseMetaData(releaseMetaData) {
|
||||
return Object.entries(releaseMetaData)
|
||||
.map(([key, value]) => [key, addAttribute(value, 'name', key)])
|
||||
.map(([, value]) => value)
|
||||
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) => addAttribute(value, 'index', index))
|
||||
.map((value, index) => addAttribute(value, 'unicode', index + startUnicode));
|
||||
.map((value, index) => ({ ...value, index }))
|
||||
.map((value, index) => ({
|
||||
...value,
|
||||
unicode: index + startUnicode,
|
||||
}));
|
||||
}
|
||||
|
||||
function addAttribute(obj, attribute, value) {
|
||||
obj[attribute] = value;
|
||||
return obj;
|
||||
}
|
||||
type CollatorFunction = (a: ReleaseMetaDataWithName, b: ReleaseMetaDataWithName) => number;
|
||||
|
||||
function sortMultiple(a, b, collators = []) {
|
||||
const comparison = collators.shift()(a, b);
|
||||
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, b) {
|
||||
const dates = [a, b].map((value) => new Date(value.createdRelease.date).valueOf());
|
||||
return (dates[0] > dates[1]) - (dates[0] < dates[1]);
|
||||
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, b) {
|
||||
function sortByName(a: ReleaseMetaDataWithName, b: ReleaseMetaDataWithName) {
|
||||
return new Intl.Collator('en-US').compare(a.name, b.name);
|
||||
}
|
||||
|
||||
function getIconUnicode(name) {
|
||||
const { unicode } = releaseMetaData.find(({ name: iconname }) => iconname === name);
|
||||
return String.fromCharCode(unicode);
|
||||
function getIconUnicode(name: string): [string, number] {
|
||||
const { unicode } = releaseMetaData.find(({ name: iconName }) => iconName === name) ?? {
|
||||
unicode: startUnicode,
|
||||
};
|
||||
return [String.fromCharCode(unicode), startUnicode];
|
||||
}
|
||||
|
||||
async function init() {
|
||||
@@ -83,7 +108,7 @@ async function init() {
|
||||
generateInfoData: true,
|
||||
website: {
|
||||
title: 'Lucide',
|
||||
logo: null,
|
||||
logo: undefined,
|
||||
meta: {
|
||||
description: 'Lucide icons as TTF/EOT/WOFF/WOFF2/SVG.',
|
||||
keywords: 'Lucide,TTF,EOT,WOFF,WOFF2,SVG',
|
||||
@@ -3,9 +3,10 @@
|
||||
"description": "A internal used package to build the font.",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"main": "main.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node ./main.mjs"
|
||||
"start": "node ./main.ts"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
@@ -14,5 +15,10 @@
|
||||
"fs-extra": "^11.2.0",
|
||||
"minimist": "^1.2.8",
|
||||
"svgtofont": "^6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/minimist": "^1.2.5",
|
||||
"@types/node": "^22"
|
||||
}
|
||||
}
|
||||
|
||||
18
tools/build-font/tsconfig.json
Normal file
18
tools/build-font/tsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"noEmitOnError": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"moduleResolution": "nodenext",
|
||||
"module": "nodenext",
|
||||
"target": "ESNext",
|
||||
"esModuleInterop": true,
|
||||
"lib": ["esnext"],
|
||||
"resolveJsonModule": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
},
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
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';
|
||||
20
tools/build-helpers/helpers.ts
Normal file
20
tools/build-helpers/helpers.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export * from './src/toCamelCase.ts';
|
||||
export * from './src/toPascalCase.ts';
|
||||
export * from './src/toKebabCase.ts';
|
||||
export * from './src/resetFile.ts';
|
||||
export * from './src/readFile.ts';
|
||||
export * from './src/appendFile.ts';
|
||||
export * from './src/writeFile.ts';
|
||||
export * from './src/writeFileIfNotExists.ts';
|
||||
export * from './src/readAllMetadata.ts';
|
||||
export * from './src/readMetadata.ts';
|
||||
export * from './src/readSvgDirectory.ts';
|
||||
export * from './src/readSvg.ts';
|
||||
export * from './src/writeSvgFile.ts';
|
||||
export * from './src/hash.ts';
|
||||
export * from './src/generateHashedKey.ts';
|
||||
export * from './src/hasDuplicatedChildren.ts';
|
||||
export * from './src/mergeArrays.ts';
|
||||
export * from './src/getCurrentDirPath.ts';
|
||||
export * from './src/minifySvg.ts';
|
||||
export * from './src/shuffleArray.ts';
|
||||
@@ -3,9 +3,12 @@
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"description": "A internal used package with helpers.",
|
||||
"main": "helpers.mjs",
|
||||
"types": "module",
|
||||
"main": "helpers.ts",
|
||||
"types": "helpers.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "module"
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/node": "^22"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,5 +9,8 @@ import path from 'path';
|
||||
* @param {string} fileName
|
||||
* @param {string} outputDirectory
|
||||
*/
|
||||
export const appendFile = (content, fileName, outputDirectory) =>
|
||||
fs.appendFile(path.join(outputDirectory, fileName), content, 'utf-8');
|
||||
export const appendFile = (
|
||||
content: string,
|
||||
fileName: string,
|
||||
outputDirectory: string,
|
||||
): Promise<void> => fs.appendFile(path.join(outputDirectory, fileName), content, 'utf-8');
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { hash } from './hash.mjs';
|
||||
import { hash } from './hash.ts';
|
||||
|
||||
/**
|
||||
* Generate Hashed string based on name and attributes
|
||||
@@ -9,4 +9,10 @@ import { hash } from './hash.mjs';
|
||||
* @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]));
|
||||
export const generateHashedKey = ({
|
||||
name,
|
||||
attributes,
|
||||
}: {
|
||||
name: string;
|
||||
attributes: Record<string, unknown>;
|
||||
}): string => hash(JSON.stringify([name, attributes]));
|
||||
@@ -8,4 +8,5 @@ import { fileURLToPath } from 'url';
|
||||
* @param {string} currentPath
|
||||
* @returns {string}
|
||||
*/
|
||||
export const getCurrentDirPath = (currentPath) => path.dirname(fileURLToPath(currentPath));
|
||||
export const getCurrentDirPath = (currentPath: string): string =>
|
||||
path.dirname(fileURLToPath(currentPath));
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { generateHashedKey } from './generateHashedKey.mjs';
|
||||
import { generateHashedKey } from './generateHashedKey.ts';
|
||||
|
||||
/**
|
||||
* Checks if array of items contains duplicated items
|
||||
@@ -7,7 +7,9 @@ import { generateHashedKey } from './generateHashedKey.mjs';
|
||||
* @param {array} children an array of items
|
||||
* @returns {Boolean} if items contains duplicated items.
|
||||
*/
|
||||
export const hasDuplicatedChildren = (children) => {
|
||||
export const hasDuplicatedChildren = (
|
||||
children: Array<{ name: string; attributes: Record<string, unknown> }>,
|
||||
): boolean => {
|
||||
const hashedKeys = children.map(generateHashedKey);
|
||||
|
||||
return !hashedKeys.every(
|
||||
@@ -6,7 +6,7 @@
|
||||
* @param {number} seed
|
||||
* @returns {string} A hashed string of 6 characters
|
||||
*/
|
||||
export const hash = (string, seed = 5381) => {
|
||||
export const hash = (string: string, seed: number = 5381): string => {
|
||||
let i = string.length;
|
||||
|
||||
while (i) {
|
||||
@@ -6,7 +6,7 @@
|
||||
* @param {array} b
|
||||
* @returns {array}
|
||||
*/
|
||||
export const mergeArrays = (a, b) => {
|
||||
export const mergeArrays = <T>(a: T[], b: T[]): T[] => {
|
||||
a = a.concat(b);
|
||||
a = a.filter((i, p) => a.indexOf(i) === p);
|
||||
return a;
|
||||
@@ -5,7 +5,7 @@
|
||||
* @param {string} string
|
||||
* @returns string
|
||||
*/
|
||||
export function minifySvg(string) {
|
||||
export function minifySvg(string: string): string {
|
||||
return string
|
||||
? string
|
||||
.replace(/>[\r\n ]+</g, '><')
|
||||
@@ -1,7 +1,6 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { readMetadata } from './readMetadata.mjs';
|
||||
import { readMetadata } from './readMetadata.ts';
|
||||
|
||||
/**
|
||||
* Reads metadata from the icons/categories directories
|
||||
@@ -9,7 +8,7 @@ import { readMetadata } from './readMetadata.mjs';
|
||||
* @param {string} directory
|
||||
* @returns {object} A map of icon or category metadata
|
||||
*/
|
||||
export const readAllMetadata = async (directory) => {
|
||||
export const readAllMetadata = async (directory: string): Promise<Record<string, unknown>> => {
|
||||
const directoryContent = await fs.readdir(directory);
|
||||
|
||||
const metaDataPromises = directoryContent
|
||||
@@ -8,4 +8,5 @@ import path from 'path';
|
||||
* @param {string} path
|
||||
* @returns {string} The contents of a file
|
||||
*/
|
||||
export const readFile = (path) => fs.readFile(path.resolve(__dirname, '../', path), 'utf-8');
|
||||
export const readFile = (filePath: string): Promise<string | Buffer> =>
|
||||
fs.readFile(path.resolve(__dirname, '../', filePath), 'utf-8');
|
||||
@@ -1,16 +0,0 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import fs from 'fs/promises';
|
||||
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 = async (fileName, directory) => {
|
||||
const metadataFileContent = await fs.readFile(path.join(directory, fileName), 'utf-8');
|
||||
|
||||
return JSON.parse(metadataFileContent);
|
||||
};
|
||||
23
tools/build-helpers/src/readMetadata.ts
Normal file
23
tools/build-helpers/src/readMetadata.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import fs from 'fs/promises';
|
||||
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 = async (fileName: string, directory: string): Promise<unknown> => {
|
||||
let metadataFileContent: string | Buffer = await fs.readFile(
|
||||
path.join(directory, fileName),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
if (Buffer.isBuffer(metadataFileContent)) {
|
||||
metadataFileContent = metadataFileContent.toString('utf-8');
|
||||
}
|
||||
|
||||
return JSON.parse(metadataFileContent);
|
||||
};
|
||||
@@ -8,5 +8,5 @@ import path from 'path';
|
||||
* @param {string} fileName
|
||||
* @param {string} directory
|
||||
*/
|
||||
export const readSvg = (fileName, directory) =>
|
||||
export const readSvg = (fileName: string, directory: string): Promise<string> =>
|
||||
fs.readFile(path.join(directory, fileName), 'utf-8');
|
||||
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { type PathLike } from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
@@ -9,7 +10,10 @@ import path from 'path';
|
||||
* @param {string} fileExtension
|
||||
* @returns {array} An array of file paths containing svgs
|
||||
*/
|
||||
export const readSvgDirectory = async (directory, fileExtension = '.svg') => {
|
||||
export const readSvgDirectory = async (
|
||||
directory: PathLike,
|
||||
fileExtension: string = '.svg',
|
||||
): Promise<string[]> => {
|
||||
const directoryContents = await fs.readdir(directory);
|
||||
|
||||
return directoryContents.filter((file) => path.extname(file) === fileExtension);
|
||||
@@ -8,5 +8,5 @@ import path from 'path';
|
||||
* @param {string} fileName
|
||||
* @param {string} outputDirectory
|
||||
*/
|
||||
export const resetFile = (fileName, outputDirectory) =>
|
||||
export const resetFile = (fileName: string, outputDirectory: string): Promise<void> =>
|
||||
fs.writeFile(path.join(outputDirectory, fileName), '', 'utf-8');
|
||||
@@ -3,7 +3,7 @@
|
||||
* @param {array} array
|
||||
* @returns {array}
|
||||
*/
|
||||
export const shuffleArray = (array) => {
|
||||
export const shuffleArray = <T>(array: T[]): T[] => {
|
||||
// eslint-disable-next-line no-plusplus
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
@@ -5,7 +5,7 @@
|
||||
* @param {string} string
|
||||
* @returns {string} A camelized string
|
||||
*/
|
||||
export const toCamelCase = (string) =>
|
||||
export const toCamelCase = (string: string): string =>
|
||||
string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) =>
|
||||
p2 ? p2.toUpperCase() : p1.toLowerCase(),
|
||||
);
|
||||
@@ -5,4 +5,5 @@
|
||||
* @param {string} string
|
||||
* @returns {string} A kebabized string
|
||||
*/
|
||||
export const toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
||||
export const toKebabCase = (string: string): string =>
|
||||
string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { toCamelCase } from './toCamelCase.mjs';
|
||||
import { toCamelCase } from './toCamelCase.ts';
|
||||
|
||||
/**
|
||||
* Converts string to PascalCase
|
||||
@@ -7,7 +7,7 @@ import { toCamelCase } from './toCamelCase.mjs';
|
||||
* @param {string} string
|
||||
* @returns {string} A pascalized string
|
||||
*/
|
||||
export const toPascalCase = (string) => {
|
||||
export const toPascalCase = (string: string): string => {
|
||||
const camelCase = toCamelCase(string);
|
||||
|
||||
return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
|
||||
@@ -5,9 +5,12 @@ import path from 'path';
|
||||
/**
|
||||
* writes content to a file
|
||||
*
|
||||
* @param {string} content
|
||||
* @param {string} fileName
|
||||
* @param {string} outputDirectory
|
||||
* @param {string} content
|
||||
*/
|
||||
export const writeSvgFile = (fileName, outputDirectory, content) =>
|
||||
fs.writeFile(path.join(outputDirectory, fileName), content, 'utf-8');
|
||||
export const writeFile = (
|
||||
content: string,
|
||||
fileName: string,
|
||||
outputDirectory: string,
|
||||
): Promise<void> => fs.writeFile(path.join(outputDirectory, fileName), content, 'utf-8');
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import fs from 'fs/promises';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { writeFile } from './writeFile.mjs';
|
||||
import { writeFile } from './writeFile.ts';
|
||||
|
||||
/**
|
||||
* writes content to a file if it does not exist
|
||||
@@ -10,7 +10,11 @@ import { writeFile } from './writeFile.mjs';
|
||||
* @param {string} fileName
|
||||
* @param {string} outputDirectory
|
||||
*/
|
||||
export const writeFileIfNotExists = (content, fileName, outputDirectory) => {
|
||||
export const writeFileIfNotExists = (
|
||||
content: string,
|
||||
fileName: string,
|
||||
outputDirectory: string,
|
||||
): void => {
|
||||
if (!fs.existsSync(path.join(outputDirectory, fileName))) {
|
||||
writeFile(content, fileName, outputDirectory);
|
||||
}
|
||||
@@ -5,9 +5,12 @@ import path from 'path';
|
||||
/**
|
||||
* writes content to a file
|
||||
*
|
||||
* @param {string} content
|
||||
* @param {string} fileName
|
||||
* @param {string} outputDirectory
|
||||
* @param {string} content
|
||||
*/
|
||||
export const writeFile = (content, fileName, outputDirectory) =>
|
||||
fs.writeFile(path.join(outputDirectory, fileName), content, 'utf-8');
|
||||
export const writeSvgFile = (
|
||||
fileName: string,
|
||||
outputDirectory: string,
|
||||
content: string,
|
||||
): Promise<void> => fs.writeFile(path.join(outputDirectory, fileName), content, 'utf-8');
|
||||
18
tools/build-helpers/tsconfig.json
Normal file
18
tools/build-helpers/tsconfig.json
Normal 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",
|
||||
},
|
||||
}
|
||||
@@ -2,8 +2,23 @@ import path from 'path';
|
||||
import fs from 'fs';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { toPascalCase, resetFile, appendFile } from '@lucide/helpers';
|
||||
import deprecationReasonTemplate from '../../utils/deprecationReasonTemplate.mjs';
|
||||
import getExportString from './getExportString.mjs';
|
||||
import deprecationReasonTemplate from '../../utils/deprecationReasonTemplate.ts';
|
||||
import getExportString from './getExportString.ts';
|
||||
import type { IconMetadata, IconNode } from '../../types.ts';
|
||||
import { type INode } from 'svgson';
|
||||
|
||||
interface GenerateAliasesFilesOptions {
|
||||
iconNodes: Record<string, INode>;
|
||||
outputDirectory: string;
|
||||
fileExtension: string;
|
||||
iconFileExtension?: string;
|
||||
iconMetaData: Record<string, IconMetadata>;
|
||||
aliasImportFileExtension: string;
|
||||
aliasNamesOnly?: boolean;
|
||||
separateAliasesFile?: boolean;
|
||||
separateAliasesFileExtension?: string;
|
||||
showLog?: boolean;
|
||||
}
|
||||
|
||||
export default async function generateAliasesFiles({
|
||||
iconNodes,
|
||||
@@ -16,7 +31,7 @@ export default async function generateAliasesFiles({
|
||||
separateAliasesFile = false,
|
||||
separateAliasesFileExtension,
|
||||
showLog = true,
|
||||
}) {
|
||||
}: GenerateAliasesFilesOptions) {
|
||||
const iconsDistDirectory = path.join(outputDirectory, `icons`);
|
||||
const icons = Object.keys(iconNodes);
|
||||
|
||||
@@ -47,7 +62,7 @@ export default async function generateAliasesFiles({
|
||||
return {
|
||||
name: alias,
|
||||
deprecated: false,
|
||||
};
|
||||
} as const;
|
||||
}
|
||||
return alias;
|
||||
});
|
||||
@@ -1,8 +1,8 @@
|
||||
const getExportString = (
|
||||
componentName,
|
||||
iconName,
|
||||
aliasImportFileExtension,
|
||||
deprecated,
|
||||
componentName: string,
|
||||
iconName: string,
|
||||
aliasImportFileExtension: string,
|
||||
deprecated?: boolean,
|
||||
deprecationReason = '',
|
||||
) =>
|
||||
deprecated
|
||||
@@ -1,5 +1,14 @@
|
||||
import path from 'path';
|
||||
import { resetFile, appendFile } from '@lucide/helpers';
|
||||
import type { IconMetadata, IconNode } from '../types.ts';
|
||||
|
||||
interface GenerateDynamicImports {
|
||||
iconNodes: Record<string, IconNode>;
|
||||
outputDirectory: string;
|
||||
fileExtension: string;
|
||||
iconMetaData: Record<string, IconMetadata>;
|
||||
showLog?: boolean;
|
||||
}
|
||||
|
||||
export default async function generateDynamicImports({
|
||||
iconNodes,
|
||||
@@ -7,7 +16,7 @@ export default async function generateDynamicImports({
|
||||
fileExtension,
|
||||
iconMetaData,
|
||||
showLog = true,
|
||||
}) {
|
||||
}: GenerateDynamicImports) {
|
||||
const fileName = path.basename(`dynamicIconImports${fileExtension}`);
|
||||
const icons = Object.keys(iconNodes);
|
||||
|
||||
@@ -2,12 +2,13 @@ import path from 'path';
|
||||
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { toPascalCase, toCamelCase, resetFile, appendFile } from '@lucide/helpers';
|
||||
import type { IconNode } from '../types.ts';
|
||||
|
||||
export default async function generateExportFile(
|
||||
inputEntry,
|
||||
outputDirectory,
|
||||
iconNodes,
|
||||
exportModuleNameCasing,
|
||||
inputEntry: string,
|
||||
outputDirectory: string,
|
||||
iconNodes: IconNode,
|
||||
exportModuleNameCasing: 'camel' | 'pascal',
|
||||
iconFileExtension = '',
|
||||
) {
|
||||
const fileName = path.basename(inputEntry);
|
||||
@@ -2,7 +2,22 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
import prettier from 'prettier';
|
||||
import { readSvg, toPascalCase } from '@lucide/helpers';
|
||||
import deprecationReasonTemplate from '../utils/deprecationReasonTemplate.mjs';
|
||||
import deprecationReasonTemplate from '../utils/deprecationReasonTemplate.ts';
|
||||
import type { IconMetadata, IconNode, Path, TemplateFunction } from '../types.ts';
|
||||
import { type INode } from 'svgson';
|
||||
|
||||
interface GenerateIconFiles {
|
||||
iconNodes: Record<string, INode>;
|
||||
outputDirectory: Path;
|
||||
template: TemplateFunction;
|
||||
showLog?: boolean;
|
||||
iconFileExtension?: string;
|
||||
separateIconFileExport?: boolean;
|
||||
separateIconFileExportExtension?: string;
|
||||
pretty?: boolean;
|
||||
iconsDir: string;
|
||||
iconMetaData: Record<string, IconMetadata>;
|
||||
}
|
||||
|
||||
function generateIconFiles({
|
||||
iconNodes,
|
||||
@@ -15,7 +30,7 @@ function generateIconFiles({
|
||||
pretty = true,
|
||||
iconsDir,
|
||||
iconMetaData,
|
||||
}) {
|
||||
}: GenerateIconFiles) {
|
||||
const icons = Object.keys(iconNodes);
|
||||
const iconsDistDirectory = path.join(outputDirectory, `icons`);
|
||||
|
||||
@@ -27,13 +42,15 @@ function generateIconFiles({
|
||||
const location = path.join(iconsDistDirectory, `${iconName}${iconFileExtension}`);
|
||||
const componentName = toPascalCase(iconName);
|
||||
|
||||
let { children } = iconNodes[iconName];
|
||||
children = children.map(({ name, attributes }) => [name, attributes]);
|
||||
const children: IconNode = iconNodes[iconName].children.map(({ name, attributes }) => [
|
||||
name,
|
||||
attributes,
|
||||
]);
|
||||
|
||||
const getSvg = () => readSvg(`${iconName}.svg`, iconsDir);
|
||||
const { deprecated = false, toBeRemovedInVersion = null } = iconMetaData[iconName];
|
||||
const { deprecated = false, toBeRemovedInVersion = undefined } = iconMetaData[iconName];
|
||||
const deprecationReason = deprecated
|
||||
? deprecationReasonTemplate(iconMetaData[iconName].deprecationReason, {
|
||||
? deprecationReasonTemplate(iconMetaData[iconName]?.deprecationReason ?? '', {
|
||||
componentName,
|
||||
iconName,
|
||||
toBeRemovedInVersion,
|
||||
@@ -50,7 +67,7 @@ function generateIconFiles({
|
||||
});
|
||||
|
||||
const output = pretty
|
||||
? prettier.format(elementTemplate, {
|
||||
? await prettier.format(elementTemplate, {
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
printWidth: 100,
|
||||
@@ -4,16 +4,37 @@ import path from 'path';
|
||||
import getArgumentOptions from 'minimist';
|
||||
|
||||
import { readSvgDirectory } from '@lucide/helpers';
|
||||
import renderIconsObject from './render/renderIconsObject.mjs';
|
||||
import generateIconFiles from './building/generateIconFiles.mjs';
|
||||
import generateExportsFile from './building/generateExportsFile.mjs';
|
||||
import renderIconsObject from './render/renderIconsObject.ts';
|
||||
import generateIconFiles from './building/generateIconFiles.ts';
|
||||
import generateExportsFile from './building/generateExportsFile.ts';
|
||||
|
||||
import generateAliasesFiles from './building/aliases/generateAliasesFiles.mjs';
|
||||
import generateAliasesFiles from './building/aliases/generateAliasesFiles.ts';
|
||||
// eslint-disable-next-line import/no-named-as-default, import/no-named-as-default-member
|
||||
import getIconMetaData from './utils/getIconMetaData.mjs';
|
||||
import generateDynamicImports from './building/generateDynamicImports.mjs';
|
||||
import getIconMetaData from './utils/getIconMetaData.ts';
|
||||
import generateDynamicImports from './building/generateDynamicImports.ts';
|
||||
|
||||
const cliArguments = getArgumentOptions(process.argv.slice(2));
|
||||
interface CliArguments {
|
||||
renderUniqueKey?: boolean;
|
||||
templateSrc?: string;
|
||||
silent?: boolean;
|
||||
iconFileExtension?: string;
|
||||
importImportFileExtension?: string;
|
||||
exportFileName?: string;
|
||||
exportModuleNameCasing?: 'camel' | 'pascal';
|
||||
withAliases?: boolean;
|
||||
aliasNamesOnly?: boolean;
|
||||
withDynamicImports?: boolean;
|
||||
separateAliasesFile?: boolean;
|
||||
separateAliasesFileExtension?: string;
|
||||
separateIconFileExport?: boolean;
|
||||
separateIconFileExportExtension?: string;
|
||||
aliasesFileExtension?: string;
|
||||
aliasImportFileExtension?: string;
|
||||
pretty?: boolean;
|
||||
output: string | undefined;
|
||||
}
|
||||
|
||||
const cliArguments = getArgumentOptions(process.argv.slice(2)) as unknown as CliArguments;
|
||||
|
||||
const ICONS_DIR = path.resolve(process.cwd(), '../../icons');
|
||||
const OUTPUT_DIR = path.resolve(process.cwd(), cliArguments.output || '../build');
|
||||
@@ -64,7 +85,7 @@ async function buildIcons() {
|
||||
iconFileExtension,
|
||||
separateIconFileExport,
|
||||
separateIconFileExportExtension,
|
||||
pretty: JSON.parse(pretty),
|
||||
pretty: JSON.parse(String(pretty)),
|
||||
iconsDir: ICONS_DIR,
|
||||
iconMetaData,
|
||||
});
|
||||
@@ -1,3 +1,3 @@
|
||||
export { default as getAliases } from './utils/getAliases.mjs';
|
||||
export { default as getIconMetaData } from './utils/getIconMetaData.mjs';
|
||||
export { default as renderIconsObject } from './render/renderIconsObject.mjs';
|
||||
export { default as getAliases } from './utils/getAliases';
|
||||
export { default as getIconMetaData } from './utils/getIconMetaData';
|
||||
export { default as renderIconsObject } from './render/renderIconsObject';
|
||||
@@ -2,22 +2,38 @@
|
||||
"name": "@lucide/build-icons",
|
||||
"description": "A internal used package to build icon code files for the lucide icon library packages.",
|
||||
"version": "1.1.0",
|
||||
"main": "index.mjs",
|
||||
"main": "index.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node ./cli.mjs"
|
||||
"start": "node ./cli.ts"
|
||||
},
|
||||
"bin": {
|
||||
"build-icons": "./cli.mjs"
|
||||
"build-icons": "./cli.ts"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=23.0.0"
|
||||
"node": ">= 23"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./index.ts",
|
||||
"require": "./index.ts"
|
||||
},
|
||||
"./cli": {
|
||||
"import": "./cli.ts",
|
||||
"require": "./cli.ts"
|
||||
},
|
||||
"./utils/*": {
|
||||
"import": "./utils/*.ts",
|
||||
"require": "./utils/*.ts"
|
||||
}
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@lucide/helpers": "workspace:*"
|
||||
"@lucide/helpers": "workspace:*",
|
||||
"@types/minimist": "^1.2.5",
|
||||
"@types/node": "^22"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.7",
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { basename } from 'path';
|
||||
import { parseSync } from 'svgson';
|
||||
import { type INode, parseSync } from 'svgson';
|
||||
import { generateHashedKey, readSvg, hasDuplicatedChildren } from '@lucide/helpers';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param {string} iconsDirectory - The directory where the icons are stored.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export default async function generateIconObject(
|
||||
svgFiles,
|
||||
iconsDirectory,
|
||||
svgFiles: string[],
|
||||
iconsDirectory: string,
|
||||
renderUniqueKey = false,
|
||||
) {
|
||||
const svgsContentPromises = svgFiles.map(async (svgFile) => {
|
||||
@@ -39,7 +39,7 @@ export default async function generateIconObject(
|
||||
|
||||
const svgsContents = await Promise.all(svgsContentPromises);
|
||||
|
||||
return svgsContents.reduce((icons, icon) => {
|
||||
return svgsContents.reduce<Record<string, INode>>((icons, icon) => {
|
||||
icons[icon.name] = icon.contents;
|
||||
return icons;
|
||||
}, {});
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": false,
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"noEmitOnError": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
@@ -8,10 +8,10 @@
|
||||
"module": "ESNext",
|
||||
"target": "ESNext",
|
||||
"esModuleInterop": true,
|
||||
"allowJs": true,
|
||||
"lib": ["esnext"],
|
||||
"resolveJsonModule": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
},
|
||||
}
|
||||
|
||||
45
tools/build-icons/types.ts
Normal file
45
tools/build-icons/types.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { type INode } from 'svgson';
|
||||
|
||||
export type SVGProps = Record<string, string | number>;
|
||||
|
||||
export type IconNode = [tag: string, attrs: SVGProps][];
|
||||
|
||||
export type IconNodeWithChildren = [tag: string, attrs: SVGProps, children: IconNode];
|
||||
|
||||
export type TemplateFunction = (params: {
|
||||
componentName: string;
|
||||
iconName: string;
|
||||
children: IconNode;
|
||||
getSvg: () => Promise<string>;
|
||||
deprecated?: boolean;
|
||||
deprecationReason?: string;
|
||||
}) => Promise<string>;
|
||||
|
||||
export type Path = string;
|
||||
|
||||
export type AliasDeprecationReason = 'alias.typo' | 'alias.name' | 'alias.duplicate';
|
||||
|
||||
export type AliasDeprecation = {
|
||||
name: string;
|
||||
deprecated: true;
|
||||
deprecationReason: AliasDeprecationReason;
|
||||
toBeRemovedInVersion: string;
|
||||
};
|
||||
|
||||
export type IconDeprecationReason = 'icon.brand' | '';
|
||||
|
||||
export type IconMetadataBase = {
|
||||
toBeRemovedInVersion?: string;
|
||||
categories?: string[];
|
||||
aliases?: (string | AliasDeprecation)[];
|
||||
tags?: string[];
|
||||
deprecationReason?: IconDeprecationReason;
|
||||
deprecated?: boolean;
|
||||
};
|
||||
|
||||
export type IconMetadataWithDeprecation = IconMetadataBase & {
|
||||
deprecated: true;
|
||||
deprecationReason?: IconDeprecationReason;
|
||||
};
|
||||
|
||||
export type IconMetadata = IconMetadataBase | IconMetadataWithDeprecation;
|
||||
@@ -1,4 +1,4 @@
|
||||
const base64SVG = (svgContents) =>
|
||||
const base64SVG = (svgContents: string) =>
|
||||
Buffer.from(
|
||||
svgContents
|
||||
.replace('\n', '')
|
||||
16
tools/build-icons/utils/defineExportTemplate.ts
Normal file
16
tools/build-icons/utils/defineExportTemplate.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { type IconNode } from '../types.ts';
|
||||
|
||||
export interface ExportTemplate {
|
||||
componentName: string;
|
||||
iconName: string;
|
||||
children: IconNode;
|
||||
getSvg: () => Promise<string>;
|
||||
deprecated: boolean;
|
||||
deprecationReason: string;
|
||||
}
|
||||
|
||||
export type TemplateFunction = (params: ExportTemplate) => Promise<string>;
|
||||
|
||||
const defineExportTemplate = (exportFunction: TemplateFunction) => exportFunction;
|
||||
|
||||
export default defineExportTemplate;
|
||||
@@ -1,6 +1,16 @@
|
||||
import type { AliasDeprecationReason, IconDeprecationReason } from '../types.ts';
|
||||
|
||||
export default function deprecationReasonTemplate(
|
||||
deprecationReason,
|
||||
{ componentName, iconName, toBeRemovedInVersion },
|
||||
deprecationReason: AliasDeprecationReason | IconDeprecationReason,
|
||||
{
|
||||
componentName,
|
||||
iconName,
|
||||
toBeRemovedInVersion,
|
||||
}: {
|
||||
componentName: string;
|
||||
iconName: string;
|
||||
toBeRemovedInVersion?: string;
|
||||
},
|
||||
) {
|
||||
const removalNotice = toBeRemovedInVersion
|
||||
? ` This ${
|
||||
@@ -13,7 +23,7 @@ export default function deprecationReasonTemplate(
|
||||
return `Renamed because of typo, use {@link ${componentName}} instead.${removalNotice}`;
|
||||
case 'alias.duplicate':
|
||||
return `The icon was combined with another icon that shares the same use case, use {@link ${componentName}} instead.${removalNotice}`;
|
||||
case 'alias.naming':
|
||||
case 'alias.name':
|
||||
return `The name of this icon was changed because it didn't meet our guidelines anymore, use {@link ${componentName}} instead.${removalNotice}`;
|
||||
case 'icon.brand':
|
||||
return `Brand icons have been deprecated and are due to be removed, please refer to https://github.com/lucide-icons/lucide/issues/670. We recommend using https://simpleicons.org/?q=${iconName} instead.${removalNotice}`;
|
||||
@@ -1,7 +1,8 @@
|
||||
import path from 'path';
|
||||
import { readSvgDirectory } from '@lucide/helpers';
|
||||
import { Path } from '../types';
|
||||
|
||||
async function getAliases(iconDirectory) {
|
||||
async function getAliases(iconDirectory: Path) {
|
||||
const iconJsons = await readSvgDirectory(iconDirectory, '.json');
|
||||
const aliasesEntries = await Promise.all(
|
||||
iconJsons.map(async (jsonFile) => {
|
||||
@@ -1,10 +1,11 @@
|
||||
import path from 'path';
|
||||
import { readSvgDirectory } from '@lucide/helpers';
|
||||
import { type IconMetadata } from '../types.ts';
|
||||
|
||||
async function getIconMetaData(iconDirectory) {
|
||||
async function getIconMetaData(iconDirectory: string): Promise<Record<string, IconMetadata>> {
|
||||
const iconJsons = await readSvgDirectory(iconDirectory, '.json');
|
||||
const aliasesEntries = await Promise.all(
|
||||
iconJsons.map(async (jsonFile) => {
|
||||
iconJsons.map(async (jsonFile: string) => {
|
||||
/** eslint-disable */
|
||||
const file = await import(path.join(iconDirectory, jsonFile), { with: { type: 'json' } });
|
||||
return [path.basename(jsonFile, '.json'), file.default];
|
||||
14
tools/outline-svg/oslllo-svg-fixer.d.ts
vendored
Normal file
14
tools/outline-svg/oslllo-svg-fixer.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
declare module 'oslllo-svg-fixer' {
|
||||
export interface SVGFixerOptions {
|
||||
showProgressBar?: boolean;
|
||||
traceResolution?: number;
|
||||
}
|
||||
|
||||
export default function SVGFixer(
|
||||
inputDir: string,
|
||||
outputDir: string,
|
||||
options?: SVGFixerOptions,
|
||||
): {
|
||||
fix: () => Promise<void>;
|
||||
};
|
||||
}
|
||||
@@ -3,9 +3,10 @@
|
||||
"description": "A internal used package to outline SVGs.",
|
||||
"private": true,
|
||||
"version": "2.0.0",
|
||||
"main": "index.js",
|
||||
"main": "main.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node ./main.mjs"
|
||||
"start": "node ./main.ts"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
||||
18
tools/outline-svg/tsconfig.json
Normal file
18
tools/outline-svg/tsconfig.json
Normal 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",
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user