Improve types export for lucide-react (#1424)

* Add types plugin

* Add js doc comment

* Only enable dynamic imports for CJS and ESM builds

* Add documentation

* Adjust docs

* Add test for dynamic import

* Adjust note

* Adjustment in docs
This commit is contained in:
Eric Fennis
2023-07-13 16:39:02 +02:00
committed by GitHub
parent e9d69c6948
commit c97c6ed9e4
15 changed files with 271 additions and 112 deletions

1
.gitignore vendored
View File

@@ -17,6 +17,7 @@ packages/**/src/icons/*.js
packages/**/src/icons/*.ts packages/**/src/icons/*.ts
packages/**/src/icons/*.tsx packages/**/src/icons/*.tsx
packages/**/src/aliases.ts packages/**/src/aliases.ts
packages/**/src/dynamicIconImports.ts
packages/**/LICENSE packages/**/LICENSE
categories.json categories.json
tags.json tags.json

View File

@@ -67,6 +67,8 @@ It is possible to create one generic icon component to load icons. It's not reco
::: danger ::: danger
Example below importing all ES Modules, caution using this example. All icons will be imported. When using bundlers like: `Webpack`, `Rollup` or `Vite` the application build size will grow strongly and harming the performance the application. Example below importing all ES Modules, caution using this example. All icons will be imported. When using bundlers like: `Webpack`, `Rollup` or `Vite` the application build size will grow strongly and harming the performance the application.
This is not the case for the latest NextJS, because it uses server side rendering. The icons will be streamed to the client when needed. For NextJS with Dynamic Imports, see [dynamic imports](#nextjs-example) section for more information.
::: :::
### Icon Component Example ### Icon Component Example
@@ -94,3 +96,59 @@ const App = () => {
export default App; export default App;
``` ```
#### With Dynamic Imports
> :warning: This is experimental and only works with bundlers that support dynamic imports.
Lucide react exports a dynamic import map `dynamicIconImports`. Useful for applications that want to show icons dynamically by icon name. For example when using a content management system with where icon names are stored in a database.
When using client side rendering, it will fetch the icon component when it's needed. This will reduce the initial bundle size.
The keys of the dynamic import map are the lucide original icon names (kebab case).
Example with React suspense:
```tsx
import React, { lazy, Suspense } from 'react';
import { dynamicIconImports, LucideProps } from 'lucide-react';
const fallback = <div style={{ background: '#ddd', width: 24, height: 24 }}/>
interface IconProps extends Omit<LucideProps, 'ref'> {
name: keyof typeof dynamicIconImports;
}
const Icon = ({ name, ...props }: IconProps) => {
const LucideIcon = lazy(dynamicIconImports[name]);
return (
<Suspense fallback={fallback}>
<LucideIcon {...props} />
</Suspense>
);
}
export default Icon
```
##### NextJS Example
In NextJS [the dynamic function](https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#nextdynamic) can be used to load the icon component dynamically.
```tsx
import dynamic from 'next/dynamic'
import { dynamicIconImports, LucideProps } from 'lucide-react';
interface IconProps extends LucideProps {
name: keyof typeof dynamicIconImports;
}
const Icon = ({ name, ...props }: IconProps) => {
const LucideIcon = dynamic(dynamicIconImports[name])
return <LucideIcon {...props} />;
};
export default Icon;
```

View File

@@ -60,7 +60,7 @@ It is possible to create a generic icon component to load icons.
> :warning: The example below is importing all ES modules. This is **not** recommended when you using a bundler since your application build size will grow substantially. > :warning: The example below is importing all ES modules. This is **not** recommended when you using a bundler since your application build size will grow substantially.
```js ```js
import * as icons from 'lucide-react'; import { icons } from 'lucide-react';
const Icon = ({ name, color, size }) => { const Icon = ({ name, color, size }) => {
const LucideIcon = icons[name]; const LucideIcon = icons[name];
@@ -70,3 +70,59 @@ const Icon = ({ name, color, size }) => {
export default Icon; export default Icon;
``` ```
#### With Dynamic Imports
> :warning: This is experimental and only works with bundlers that support dynamic imports.
Lucide react exports a dynamic import map `dynamicIconImports`. Useful for applications that want to show icons dynamically by icon name. For example when using a content management system with where icon names are stored in a database.
When using client side rendering, it will fetch the icon component when it's needed. This will reduce the initial bundle size.
The keys of the dynamic import map are the lucide original icon names.
Example with React suspense:
```tsx
import React, { lazy, Suspense } from 'react';
import { dynamicIconImports, LucideProps } from 'lucide-react';
const fallback = <div style={{ background: '#ddd', width: 24, height: 24 }}/>
interface IconProps extends Omit<LucideProps, 'ref'> {
name: keyof typeof dynamicIconImports;
}
const Icon = ({ name, ...props }: IconProps) => {
const LucideIcon = lazy(dynamicIconImports[name]);
return (
<Suspense fallback={fallback}>
<LucideIcon {...props} />
</Suspense>
);
}
export default Icon
```
##### NextJS Example
In NextJS [the dynamic function](https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#nextdynamic) can be used to load the icon component dynamically.
```tsx
import dynamic from 'next/dynamic'
import { dynamicIconImports, LucideProps } from 'lucide-react';
interface IconProps extends LucideProps {
name: keyof typeof dynamicIconImports;
}
const Icon = ({ name, ...props }: IconProps) => {
const LucideIcon = dynamic(dynamicIconImports[name])
return <LucideIcon {...props} />;
};
export default Icon;
```

View File

@@ -22,10 +22,10 @@
"dist" "dist"
], ],
"scripts": { "scripts": {
"build": "pnpm clean && pnpm copy:license && pnpm build:icons && pnpm typecheck && pnpm build:bundles && pnpm build:types", "build": "pnpm clean && pnpm copy:license && pnpm build:icons && pnpm typecheck && pnpm build:bundles",
"copy:license": "cp ../../LICENSE ./LICENSE", "copy:license": "cp ../../LICENSE ./LICENSE",
"clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.ts", "clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.ts",
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mjs --renderUniqueKey --withAliases --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts", "build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mjs --renderUniqueKey --withAliases --withDynamicImports --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts",
"build:types": "node ./scripts/buildTypes.mjs", "build:types": "node ./scripts/buildTypes.mjs",
"build:bundles": "rollup -c ./rollup.config.mjs", "build:bundles": "rollup -c ./rollup.config.mjs",
"typecheck": "tsc", "typecheck": "tsc",
@@ -44,6 +44,7 @@
"react": "17.0.2", "react": "17.0.2",
"react-dom": "17.0.2", "react-dom": "17.0.2",
"rollup": "^3.5.1", "rollup": "^3.5.1",
"rollup-plugin-dts": "^5.0.0",
"typescript": "^4.8.4", "typescript": "^4.8.4",
"vite": "^4.3.9", "vite": "^4.3.9",
"vitest": "^0.32.2" "vitest": "^0.32.2"

View File

@@ -1,5 +1,6 @@
import plugins, { replace } from '@lucide/rollup-plugins'; import plugins, { replace } from '@lucide/rollup-plugins';
import pkg from './package.json' assert { type: 'json' }; import pkg from './package.json' assert { type: 'json' };
import dts from "rollup-plugin-dts";
const packageName = 'LucideReact'; const packageName = 'LucideReact';
const outputFileName = 'lucide-react'; const outputFileName = 'lucide-react';
@@ -21,19 +22,21 @@ const bundles = [
format: 'cjs', format: 'cjs',
inputs, inputs,
outputDir, outputDir,
aliasesSupport: true aliasesSupport: true,
withDynamicImports: true,
}, },
{ {
format: 'esm', format: 'esm',
inputs, inputs,
outputDir, outputDir,
preserveModules: true, preserveModules: true,
aliasesSupport: true aliasesSupport: true,
withDynamicImports: true,
}, },
]; ];
const configs = bundles const configs = bundles
.map(({ inputs, outputDir, format, minify, preserveModules, aliasesSupport }) => .map(({ inputs, outputDir, format, minify, preserveModules, aliasesSupport, withDynamicImports }) =>
inputs.map(input => ({ inputs.map(input => ({
input, input,
plugins: [ plugins: [
@@ -47,6 +50,15 @@ const configs = bundles
}), }),
] : [] ] : []
), ),
...(
!withDynamicImports ? [
replace({
"export { default as dynamicIconImports } from './dynamicIconImports';": '',
delimiters: ['', ''],
preventAssignment: false,
}),
] : []
),
...plugins(pkg, minify) ...plugins(pkg, minify)
], ],
external: ['react', 'prop-types'], external: ['react', 'prop-types'],
@@ -71,4 +83,13 @@ const configs = bundles
) )
.flat(); .flat();
export default configs; export default [
{
input: inputs[0],
output: [{
file: `dist/${outputFileName}.d.ts`, format: "es"
}],
plugins: [dts()],
},
...configs
];

View File

@@ -1,92 +0,0 @@
import path from 'path';
// eslint-disable-next-line import/no-extraneous-dependencies
import { getAliases } from '@lucide/build-icons';
import {
readSvgDirectory,
resetFile,
toPascalCase,
writeFile,
getCurrentDirPath,
} from '../../../scripts/helpers.mjs';
const currentDir = getCurrentDirPath(import.meta.url);
const srcDirectory = path.join(currentDir, '../dist');
const writeDeclarationFile = (typesFile, directory, content) => {
resetFile(typesFile, directory);
writeFile(content, typesFile, directory);
};
const ICONS_DIR = path.resolve(currentDir, '../../../icons');
const TYPES_FILE = 'lucide-react.d.ts';
let declarationFileContent = `\
/// <reference types="react" />
import { SVGAttributes, FC, SVGProps, ReactSVG } from 'react'
declare module 'lucide-react'
// Create interface extending SVGProps
export interface LucideProps extends Partial<SVGProps<SVGSVGElement>> {
size?: string | number
absoluteStrokeWidth?: boolean
}
export type LucideIcon = (props: LucideProps) => JSX.Element;
export type IconNode = [elementName: keyof ReactSVG, attrs: Record<string, string>][]
export declare const createLucideIcon: (iconName: string, iconNode: IconNode) => LucideIcon;
export type Icon = FC<LucideProps>;
// Generated icon
`;
const svgFiles = readSvgDirectory(ICONS_DIR);
svgFiles.forEach((svgFile) => {
const iconName = path.basename(svgFile, '.svg');
const componentName = toPascalCase(iconName);
declarationFileContent += `export declare const ${componentName}: LucideIcon;\n`;
});
const aliases = await getAliases(ICONS_DIR);
declarationFileContent += `\n
// Generated icon aliases
`;
let aliasesCount = 0;
svgFiles.forEach((svgFile) => {
const iconName = path.basename(svgFile, '.svg');
const componentName = toPascalCase(iconName);
const iconAliases = aliases[iconName]?.aliases;
declarationFileContent += `// ${componentName} aliases\n`;
declarationFileContent += `export declare const ${componentName}Icon: LucideIcon;\n`;
declarationFileContent += `export declare const Lucide${componentName}: LucideIcon;\n`;
aliasesCount += 1;
if (iconAliases != null && Array.isArray(iconAliases)) {
iconAliases.forEach((alias) => {
const componentNameAlias = toPascalCase(alias);
declarationFileContent += `export declare const ${componentNameAlias}: LucideIcon;\n`;
aliasesCount += 1;
});
}
declarationFileContent += '\n';
});
writeDeclarationFile(TYPES_FILE, srcDirectory, declarationFileContent);
console.log(
`Generated ${TYPES_FILE} file with`,
svgFiles.length,
'icons and with',
aliasesCount,
'aliases',
);

View File

@@ -1,7 +1,33 @@
export default ({ componentName, children }) => ` export default ({ componentName, iconName, children, getSvg }) => {
const svgContents = getSvg();
const svgBase64 = Buffer.from(
svgContents
.replace('\n', '')
.replace(
'stroke="currentColor"',
'stroke="#000" style="background-color: #fff; border-radius: 2px"',
),
).toString('base64');
// declarationFileContent += `\
return `
import createLucideIcon from '../createLucideIcon'; import createLucideIcon from '../createLucideIcon';
/**
* @component @name ${componentName}
* @description Lucide SVG icon component, renders SVG Element with children.
*
* @preview ![img](data:image/svg+xml;base64,${svgBase64}) - https://lucide.dev/icons/${iconName}
* @see https://lucide.dev/guide/packages/lucide-react - Documentation
*
* @param {Object} props - Lucide icons props and any valid SVG attribute
* @returns {JSX.Element} JSX Element
*
*/
const ${componentName} = createLucideIcon('${componentName}', ${JSON.stringify(children)}); const ${componentName} = createLucideIcon('${componentName}', ${JSON.stringify(children)});
export default ${componentName}; export default ${componentName};
`; `;
};

View File

@@ -1,4 +1,4 @@
import { forwardRef, createElement, ReactSVG, SVGProps } from 'react'; import { forwardRef, createElement, ReactSVG, SVGProps, ForwardRefExoticComponent, RefAttributes } from 'react';
import defaultAttributes from './defaultAttributes'; import defaultAttributes from './defaultAttributes';
export type IconNode = [elementName: keyof ReactSVG, attrs: Record<string, string>][] export type IconNode = [elementName: keyof ReactSVG, attrs: Record<string, string>][]
@@ -9,6 +9,8 @@ export interface LucideProps extends SVGAttributes {
size?: string | number size?: string | number
absoluteStrokeWidth?: boolean absoluteStrokeWidth?: boolean
} }
export type LucideIcon = ForwardRefExoticComponent<LucideProps & RefAttributes<SVGSVGElement>>
/** /**
* Converts string to KebabCase * Converts string to KebabCase
* Copied from scripts/helper. If anyone knows how to properly import it here * Copied from scripts/helper. If anyone knows how to properly import it here
@@ -19,7 +21,7 @@ export interface LucideProps extends SVGAttributes {
*/ */
export const toKebabCase = (string: string) => string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); export const toKebabCase = (string: string) => string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
const createLucideIcon = (iconName: string, iconNode: IconNode) => { const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => {
const Component = forwardRef<SVGSVGElement, LucideProps>( const Component = forwardRef<SVGSVGElement, LucideProps>(
({ color = 'currentColor', size = 24, strokeWidth = 2, absoluteStrokeWidth, children, ...rest }, ref) => ({ color = 'currentColor', size = 24, strokeWidth = 2, absoluteStrokeWidth, children, ...rest }, ref) =>
createElement( createElement(

View File

@@ -1,4 +1,10 @@
export * from './icons'; export * from './icons';
export * as icons from './icons'; export * as icons from './icons';
export * from './aliases'; export * from './aliases';
export { default as createLucideIcon } from './createLucideIcon'; export { default as dynamicIconImports } from './dynamicIconImports';
export {
default as createLucideIcon,
type IconNode,
type LucideProps,
type LucideIcon,
} from './createLucideIcon';

View File

@@ -5,3 +5,5 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = `"<svg xmlns=\\"http://www.w3.org/2000/svg\\" width=\\"48\\" height=\\"48\\" viewBox=\\"0 0 24 24\\" fill=\\"none\\" stroke=\\"red\\" stroke-width=\\"1\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" class=\\"lucide lucide-grid\\" data-testid=\\"grid-icon\\"><rect width=\\"18\\" height=\\"18\\" x=\\"3\\" y=\\"3\\" rx=\\"2\\" ry=\\"2\\"></rect><line x1=\\"3\\" x2=\\"21\\" y1=\\"9\\" y2=\\"9\\"></line><line x1=\\"3\\" x2=\\"21\\" y1=\\"15\\" y2=\\"15\\"></line><line x1=\\"9\\" x2=\\"9\\" y1=\\"3\\" y2=\\"21\\"></line><line x1=\\"15\\" x2=\\"15\\" y1=\\"3\\" y2=\\"21\\"></line></svg>"`; exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = `"<svg xmlns=\\"http://www.w3.org/2000/svg\\" width=\\"48\\" height=\\"48\\" viewBox=\\"0 0 24 24\\" fill=\\"none\\" stroke=\\"red\\" stroke-width=\\"1\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" class=\\"lucide lucide-grid\\" data-testid=\\"grid-icon\\"><rect width=\\"18\\" height=\\"18\\" x=\\"3\\" y=\\"3\\" rx=\\"2\\" ry=\\"2\\"></rect><line x1=\\"3\\" x2=\\"21\\" y1=\\"9\\" y2=\\"9\\"></line><line x1=\\"3\\" x2=\\"21\\" y1=\\"15\\" y2=\\"15\\"></line><line x1=\\"9\\" x2=\\"9\\" y1=\\"3\\" y2=\\"21\\"></line><line x1=\\"15\\" x2=\\"15\\" y1=\\"3\\" y2=\\"21\\"></line></svg>"`;
exports[`Using lucide icon components > should render an component 1`] = `"<svg xmlns=\\"http://www.w3.org/2000/svg\\" width=\\"24\\" height=\\"24\\" viewBox=\\"0 0 24 24\\" fill=\\"none\\" stroke=\\"currentColor\\" stroke-width=\\"2\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" class=\\"lucide lucide-grid\\"><rect width=\\"18\\" height=\\"18\\" x=\\"3\\" y=\\"3\\" rx=\\"2\\" ry=\\"2\\"></rect><line x1=\\"3\\" x2=\\"21\\" y1=\\"9\\" y2=\\"9\\"></line><line x1=\\"3\\" x2=\\"21\\" y1=\\"15\\" y2=\\"15\\"></line><line x1=\\"9\\" x2=\\"9\\" y1=\\"3\\" y2=\\"21\\"></line><line x1=\\"15\\" x2=\\"15\\" y1=\\"3\\" y2=\\"21\\"></line></svg>"`; exports[`Using lucide icon components > should render an component 1`] = `"<svg xmlns=\\"http://www.w3.org/2000/svg\\" width=\\"24\\" height=\\"24\\" viewBox=\\"0 0 24 24\\" fill=\\"none\\" stroke=\\"currentColor\\" stroke-width=\\"2\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" class=\\"lucide lucide-grid\\"><rect width=\\"18\\" height=\\"18\\" x=\\"3\\" y=\\"3\\" rx=\\"2\\" ry=\\"2\\"></rect><line x1=\\"3\\" x2=\\"21\\" y1=\\"9\\" y2=\\"9\\"></line><line x1=\\"3\\" x2=\\"21\\" y1=\\"15\\" y2=\\"15\\"></line><line x1=\\"9\\" x2=\\"9\\" y1=\\"3\\" y2=\\"21\\"></line><line x1=\\"15\\" x2=\\"15\\" y1=\\"3\\" y2=\\"21\\"></line></svg>"`;
exports[`Using lucide icon components > should render icons dynamically by using the dynamicIconImports module 1`] = `"<svg xmlns=\\"http://www.w3.org/2000/svg\\" width=\\"48\\" height=\\"48\\" viewBox=\\"0 0 24 24\\" fill=\\"none\\" stroke=\\"red\\" stroke-width=\\"1\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" class=\\"lucide lucide-smile\\" aria-label=\\"smile\\"><circle cx=\\"12\\" cy=\\"12\\" r=\\"10\\"></circle><path d=\\"M8 14s1.5 2 4 2 4-2 4-2\\"></path><line x1=\\"9\\" x2=\\"9.01\\" y1=\\"9\\" y2=\\"9\\"></line><line x1=\\"15\\" x2=\\"15.01\\" y1=\\"9\\" y2=\\"9\\"></line></svg>"`;

View File

@@ -1,6 +1,7 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { render, cleanup } from '@testing-library/react' import { render, cleanup, waitFor } from '@testing-library/react'
import { Pen, Edit2, Grid } from '../src/lucide-react'; import { Pen, Edit2, Grid, dynamicIconImports, LucideProps } from '../src/lucide-react';
import { Suspense, lazy } from 'react';
describe('Using lucide icon components', () => { describe('Using lucide icon components', () => {
it('should render an component', () => { it('should render an component', () => {
@@ -73,4 +74,37 @@ describe('Using lucide icon components', () => {
expect( container.innerHTML ).toMatchSnapshot(); expect( container.innerHTML ).toMatchSnapshot();
}); });
it('should render icons dynamically by using the dynamicIconImports module', async () => {
interface IconProps extends Omit<LucideProps, 'ref'> {
name: keyof typeof dynamicIconImports;
}
const Icon = ({ name, ...props }: IconProps) => {
const LucideIcon = lazy(dynamicIconImports[name]);
return (
<Suspense fallback={null}>
<LucideIcon {...props} />
</Suspense>
);
}
const { container, getByLabelText } = render(
<Icon
aria-label="smile"
name="smile"
size={48}
stroke="red"
absoluteStrokeWidth
/>,
);
await waitFor(() => getByLabelText('smile'))
expect( container.innerHTML ).toMatchSnapshot();
});
}) })

9
pnpm-lock.yaml generated
View File

@@ -384,6 +384,9 @@ importers:
rollup: rollup:
specifier: ^3.5.1 specifier: ^3.5.1
version: 3.23.0 version: 3.23.0
rollup-plugin-dts:
specifier: ^5.0.0
version: 5.0.0(rollup@3.23.0)(typescript@4.9.5)
typescript: typescript:
specifier: ^4.8.4 specifier: ^4.8.4
version: 4.9.5 version: 4.9.5
@@ -1828,10 +1831,6 @@ packages:
resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
/@babel/helper-validator-identifier@7.19.1:
resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
engines: {node: '>=6.9.0'}
/@babel/helper-validator-identifier@7.22.5: /@babel/helper-validator-identifier@7.22.5:
resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
@@ -4872,7 +4871,7 @@ packages:
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/helper-string-parser': 7.21.5 '@babel/helper-string-parser': 7.21.5
'@babel/helper-validator-identifier': 7.19.1 '@babel/helper-validator-identifier': 7.22.5
to-fast-properties: 2.0.0 to-fast-properties: 2.0.0
/@babel/types@7.22.5: /@babel/types@7.22.5:

View File

@@ -0,0 +1,30 @@
import path from 'path';
import { resetFile, appendFile } from '../../../scripts/helpers.mjs';
export default function generateDynamicImports({
iconNodes,
outputDirectory,
fileExtension,
showLog = true,
}) {
const fileName = path.basename(`dynamicIconImports${fileExtension}`);
const icons = Object.keys(iconNodes);
// Reset file
resetFile(fileName, outputDirectory);
let importString = `const dynamicIconImports = {\n`;
// Generate Import for Icon VNodes
icons.forEach((iconName) => {
importString += ` '${iconName}': () => import('./icons/${iconName}'),\n`;
});
importString += '};\nexport default dynamicIconImports;\n';
appendFile(importString, fileName, outputDirectory);
if (showLog) {
console.log(`Successfully generated ${fileName} file`);
}
}

View File

@@ -1,7 +1,7 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import prettier from 'prettier'; import prettier from 'prettier';
import { toPascalCase } from '../../../scripts/helpers.mjs'; import { readSvg, toPascalCase } from '../../../scripts/helpers.mjs';
export default ({ export default ({
iconNodes, iconNodes,
@@ -10,6 +10,7 @@ export default ({
showLog = true, showLog = true,
iconFileExtension = '.js', iconFileExtension = '.js',
pretty = true, pretty = true,
iconsDir,
}) => { }) => {
const icons = Object.keys(iconNodes); const icons = Object.keys(iconNodes);
const iconsDistDirectory = path.join(outputDirectory, `icons`); const iconsDistDirectory = path.join(outputDirectory, `icons`);
@@ -25,7 +26,9 @@ export default ({
let { children } = iconNodes[iconName]; let { children } = iconNodes[iconName];
children = children.map(({ name, attributes }) => [name, attributes]); children = children.map(({ name, attributes }) => [name, attributes]);
const elementTemplate = template({ componentName, iconName, children }); const getSvg = () => readSvg(`${iconName}.svg`, iconsDir);
const elementTemplate = template({ componentName, iconName, children, getSvg });
const output = pretty const output = pretty
? prettier.format(elementTemplate, { ? prettier.format(elementTemplate, {
singleQuote: true, singleQuote: true,

View File

@@ -10,6 +10,7 @@ import generateExportsFile from './building/generateExportsFile.mjs';
import { readSvgDirectory, getCurrentDirPath } from '../../scripts/helpers.mjs'; import { readSvgDirectory, getCurrentDirPath } from '../../scripts/helpers.mjs';
import generateAliasesFile from './building/generateAliasesFile.mjs'; import generateAliasesFile from './building/generateAliasesFile.mjs';
import getAliases from './utils/getAliases.mjs'; import getAliases from './utils/getAliases.mjs';
import generateDynamicImports from './building/generateDynamicImports.mjs';
const cliArguments = getArgumentOptions(process.argv.slice(2)); const cliArguments = getArgumentOptions(process.argv.slice(2));
@@ -30,6 +31,7 @@ const {
importImportFileExtension = '', importImportFileExtension = '',
exportFileName = 'index.js', exportFileName = 'index.js',
withAliases = false, withAliases = false,
withDynamicImports = false,
aliasesFileExtension = '.js', aliasesFileExtension = '.js',
aliasImportFileExtension = '', aliasImportFileExtension = '',
pretty = true, pretty = true,
@@ -54,6 +56,7 @@ async function buildIcons() {
showLog: !silent, showLog: !silent,
iconFileExtension, iconFileExtension,
pretty: JSON.parse(pretty), pretty: JSON.parse(pretty),
iconsDir: ICONS_DIR,
}); });
if (withAliases) { if (withAliases) {
@@ -69,6 +72,15 @@ async function buildIcons() {
}); });
} }
if (withDynamicImports) {
generateDynamicImports({
iconNodes: icons,
outputDirectory: OUTPUT_DIR,
fileExtension: aliasesFileExtension,
showLog: !silent,
});
}
// Generates entry files for the compiler filled with icons exports // Generates entry files for the compiler filled with icons exports
generateExportsFile( generateExportsFile(
path.join(OUTPUT_DIR, 'icons', exportFileName), path.join(OUTPUT_DIR, 'icons', exportFileName),