mirror of
https://github.com/lucide-icons/lucide.git
synced 2025-12-16 07:47:41 +01:00
Add support for aliases (#899)
* extract workflow * Add aliases build * Setup types building for aliases * Add types generation for aliases * Finish React Aliases * Finish aliases for lucide-react * setup aliases preact * Fix aliases in preact * Add aliases preact * Add aliases lucide-react-native * Fix solid js build * update lock file * Improve solid for solid start * update import * update import * lucide solid fix types generation * Fix lucide sold * Fix svelte aliases * update lockfile * Fix imports * Fix solid js issues * Add aliases to the vue packages * Fix lucide react native * Test alpha versions lucide-vue, lucide-vue-next, lucide-svelte
This commit is contained in:
48
tools/build-icons/building/generateAliasesFile.mjs
Normal file
48
tools/build-icons/building/generateAliasesFile.mjs
Normal file
@@ -0,0 +1,48 @@
|
||||
import path from 'path';
|
||||
import { toPascalCase, resetFile, appendFile } from '../../../scripts/helpers.mjs';
|
||||
|
||||
const getImportString = (componentName, iconName, aliasImportFileExtension = '') =>
|
||||
`export { default as ${componentName} } from './icons/${iconName}${aliasImportFileExtension}';\n`;
|
||||
|
||||
export default function generateAliasesFile({
|
||||
iconNodes,
|
||||
outputDirectory,
|
||||
fileExtension,
|
||||
aliases,
|
||||
aliasImportFileExtension,
|
||||
showLog = true,
|
||||
}) {
|
||||
const fileName = path.basename(`aliases${fileExtension}`);
|
||||
const icons = Object.keys(iconNodes);
|
||||
|
||||
// Reset file
|
||||
resetFile(fileName, outputDirectory);
|
||||
|
||||
// Generate Import for Icon VNodes
|
||||
icons.forEach((iconName) => {
|
||||
const componentName = toPascalCase(iconName);
|
||||
const iconAliases = aliases[iconName]?.aliases;
|
||||
|
||||
let importString = `// ${componentName} aliases\n`;
|
||||
|
||||
importString += getImportString(`${componentName}Icon`, iconName, aliasImportFileExtension);
|
||||
importString += getImportString(`Lucide${componentName}`, iconName, aliasImportFileExtension);
|
||||
|
||||
if (iconAliases != null && Array.isArray(iconAliases)) {
|
||||
iconAliases.forEach((alias) => {
|
||||
const componentNameAlias = toPascalCase(alias);
|
||||
importString += getImportString(componentNameAlias, iconName, aliasImportFileExtension);
|
||||
});
|
||||
}
|
||||
|
||||
importString += '\n';
|
||||
|
||||
appendFile(importString, fileName, outputDirectory);
|
||||
});
|
||||
|
||||
appendFile('\n', fileName, outputDirectory);
|
||||
|
||||
if (showLog) {
|
||||
console.log(`Successfully generated ${fileName} file`);
|
||||
}
|
||||
}
|
||||
23
tools/build-icons/building/generateExportsFile.mjs
Normal file
23
tools/build-icons/building/generateExportsFile.mjs
Normal file
@@ -0,0 +1,23 @@
|
||||
import path from 'path';
|
||||
|
||||
import { toPascalCase, resetFile, appendFile } from '../../../scripts/helpers.mjs';
|
||||
|
||||
export default (inputEntry, outputDirectory, iconNodes, iconFileExtension = '') => {
|
||||
const fileName = path.basename(inputEntry);
|
||||
|
||||
// Reset file
|
||||
resetFile(fileName, outputDirectory);
|
||||
|
||||
const icons = Object.keys(iconNodes);
|
||||
|
||||
// Generate Import for Icon VNodes
|
||||
icons.forEach((iconName) => {
|
||||
const componentName = toPascalCase(iconName);
|
||||
const importString = `export { default as ${componentName} } from './${iconName}${iconFileExtension}';\n`;
|
||||
appendFile(importString, fileName, outputDirectory);
|
||||
});
|
||||
|
||||
appendFile('\n', fileName, outputDirectory);
|
||||
|
||||
console.log(`Successfully generated ${fileName} file`);
|
||||
};
|
||||
49
tools/build-icons/building/generateIconFiles.mjs
Normal file
49
tools/build-icons/building/generateIconFiles.mjs
Normal file
@@ -0,0 +1,49 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import prettier from 'prettier';
|
||||
import { toPascalCase } from '../../../scripts/helpers.mjs';
|
||||
|
||||
export default ({
|
||||
iconNodes,
|
||||
outputDirectory,
|
||||
template,
|
||||
showLog = true,
|
||||
iconFileExtension = '.js',
|
||||
pretty = true,
|
||||
}) => {
|
||||
const icons = Object.keys(iconNodes);
|
||||
const iconsDistDirectory = path.join(outputDirectory, `icons`);
|
||||
|
||||
if (!fs.existsSync(iconsDistDirectory)) {
|
||||
fs.mkdirSync(iconsDistDirectory);
|
||||
}
|
||||
|
||||
const writeIconFiles = icons.map(async (iconName) => {
|
||||
const location = path.join(iconsDistDirectory, `${iconName}${iconFileExtension}`);
|
||||
const componentName = toPascalCase(iconName);
|
||||
|
||||
let { children } = iconNodes[iconName];
|
||||
children = children.map(({ name, attributes }) => [name, attributes]);
|
||||
|
||||
const elementTemplate = template({ componentName, iconName, children });
|
||||
const output = pretty
|
||||
? prettier.format(elementTemplate, {
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
parser: 'babel',
|
||||
})
|
||||
: elementTemplate;
|
||||
|
||||
await fs.promises.writeFile(location, output, 'utf-8');
|
||||
});
|
||||
|
||||
Promise.all(writeIconFiles)
|
||||
.then(() => {
|
||||
if (showLog) {
|
||||
console.log('Successfully built', icons.length, 'icons.');
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
throw new Error(`Something went wrong generating icon files,\n ${error}`);
|
||||
});
|
||||
};
|
||||
2
tools/build-icons/index.mjs
Normal file
2
tools/build-icons/index.mjs
Normal file
@@ -0,0 +1,2 @@
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export { default as getAliases } from './utils/getAliases.mjs';
|
||||
85
tools/build-icons/main.mjs
Executable file
85
tools/build-icons/main.mjs
Executable file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env node
|
||||
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 '../../scripts/helpers.mjs';
|
||||
import generateAliasesFile from './building/generateAliasesFile.mjs';
|
||||
import getAliases from './utils/getAliases.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',
|
||||
importImportFileExtension = '',
|
||||
exportFileName = 'index.js',
|
||||
withAliases = false,
|
||||
aliasesFileExtension = '.js',
|
||||
aliasImportFileExtension = '',
|
||||
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),
|
||||
});
|
||||
|
||||
if (withAliases) {
|
||||
const aliases = await getAliases(ICONS_DIR);
|
||||
|
||||
generateAliasesFile({
|
||||
iconNodes: icons,
|
||||
aliases,
|
||||
outputDirectory: OUTPUT_DIR,
|
||||
fileExtension: aliasesFileExtension,
|
||||
aliasImportFileExtension,
|
||||
showLog: !silent,
|
||||
});
|
||||
}
|
||||
|
||||
// Generates entry files for the compiler filled with icons exports
|
||||
generateExportsFile(
|
||||
path.join(OUTPUT_DIR, 'icons', exportFileName),
|
||||
path.join(OUTPUT_DIR, 'icons'),
|
||||
icons,
|
||||
importImportFileExtension,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
buildIcons();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
27
tools/build-icons/package.json
Normal file
27
tools/build-icons/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "@lucide/build-icons",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.mjs",
|
||||
"type":"module",
|
||||
"scripts": {
|
||||
"start": "node ./main.mjs"
|
||||
},
|
||||
"bin": {
|
||||
"build-icons": "./main.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.7",
|
||||
"node-fetch": "^3.2.10",
|
||||
"prettier": "2.7.1",
|
||||
"svgo": "^3.0.0",
|
||||
"svgson": "^5.2.1"
|
||||
}
|
||||
}
|
||||
11
tools/build-icons/render/default-attrs.json
Normal file
11
tools/build-icons/render/default-attrs.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
39
tools/build-icons/render/renderIconsObject.mjs
Normal file
39
tools/build-icons/render/renderIconsObject.mjs
Normal file
@@ -0,0 +1,39 @@
|
||||
import { basename } from 'path';
|
||||
import { parseSync } from 'svgson';
|
||||
import { generateHashedKey, readSvg, hasDuplicatedChildren } from '../../../scripts/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;
|
||||
}, {});
|
||||
16
tools/build-icons/utils/getAliases.mjs
Normal file
16
tools/build-icons/utils/getAliases.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import path from 'path';
|
||||
import { readSvgDirectory } from '../../../scripts/helpers.mjs';
|
||||
|
||||
async function getAliases(iconDirectory) {
|
||||
const iconJsons = readSvgDirectory(iconDirectory, '.json');
|
||||
const aliasesEntries = await Promise.all(
|
||||
iconJsons.map(async (jsonFile) => {
|
||||
const file = await import( path.join(iconDirectory, jsonFile), { assert: { type: 'json' } });
|
||||
return [path.basename(jsonFile, '.json'), file.default]
|
||||
})
|
||||
)
|
||||
|
||||
return Object.fromEntries(aliasesEntries);
|
||||
}
|
||||
|
||||
export default getAliases
|
||||
@@ -10,6 +10,7 @@
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@atomico/rollup-plugin-sizes": "^1.1.4",
|
||||
"@rollup/plugin-replace": "^5.0.1",
|
||||
"esbuild": "^0.15.16",
|
||||
"rollup": "^3.5.1",
|
||||
"rollup-plugin-esbuild": "^4.10.2",
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import bundleSize from '@atomico/rollup-plugin-sizes';
|
||||
import { visualizer } from 'rollup-plugin-visualizer';
|
||||
import bundleSize from '@atomico/rollup-plugin-sizes';
|
||||
import replace from '@rollup/plugin-replace';
|
||||
import license from 'rollup-plugin-license';
|
||||
import esbuild from 'rollup-plugin-esbuild';
|
||||
|
||||
const plugins = (pkg, minify) =>
|
||||
const plugins = (pkg, minify, esbuildOptions = {}) =>
|
||||
[
|
||||
esbuild({
|
||||
minify,
|
||||
...esbuildOptions,
|
||||
}),
|
||||
license({
|
||||
banner: `${pkg.name} v${pkg.version} - ${pkg.license}`,
|
||||
@@ -19,4 +21,6 @@ const plugins = (pkg, minify) =>
|
||||
}),
|
||||
].filter(Boolean);
|
||||
|
||||
export { bundleSize, license, visualizer, replace };
|
||||
|
||||
export default plugins;
|
||||
|
||||
Reference in New Issue
Block a user