1
0
mirror of https://github.com/kremalicious/blog.git synced 2024-12-22 17:23:50 +01:00

generate react icon components

This commit is contained in:
Matthias Kretschmann 2023-10-26 18:59:24 +01:00
parent 8a2606185b
commit 0aafe28f85
Signed by: m
GPG Key ID: 606EEEF3C479A91F
4 changed files with 100 additions and 41 deletions

View File

@ -6,7 +6,9 @@ import fs from 'node:fs/promises'
import ps from 'node:path/posix'
import ora from 'ora'
import chalk from 'chalk'
import { toAstroComponent, toInnerSvg } from './svg.ts'
import { toInnerSvg } from './svg.ts'
import { toAstroComponent } from './toAstroComponent.ts'
import { toReactComponent } from './toReactComponent.ts'
// Current directory.
const currentDir = ps.resolve('.')
@ -31,15 +33,21 @@ export async function generateIcons(distDir: string) {
// clean the distribution directory
await fs.rm(distDir, { force: true, recursive: true })
await fs.mkdir(distDir, { recursive: true })
await fs.mkdir(`${distDir}/react`, { recursive: true })
// copy the attribute typings file
await fs.copyFile(
ps.resolve(currentDir, 'scripts/create-icons/Props.d.ts'),
ps.resolve(distDir, 'Props.d.ts')
)
await fs.copyFile(
ps.resolve(currentDir, 'scripts/create-icons/Props.d.ts'),
ps.resolve(`${distDir}/react`, 'Props.d.ts')
)
// convert the SVG files into Astro components
// convert the SVG files into Astro & React components
let contentOfIndexJS = '// @ts-nocheck\n'
let contentOfIndexReactJS = '// @ts-nocheck\n'
for (const src of srcDirs) {
for (let filepath of await fs.readdir(src, { encoding: 'utf8' })) {
@ -83,16 +91,33 @@ export async function generateIcons(distDir: string) {
'utf8'
)
// write the react component to a file
await fs.writeFile(
ps.resolve(`${distDir}/react`, `${baseName}.tsx`),
toReactComponent(innerSVG, title),
'utf8'
)
// add the astro component export to the main entry `index.ts` file
contentOfIndexJS += `\nexport { default as ${baseName} } from './${baseName}.astro'`
// add the react component export to the main entry `react/index.ts` file
contentOfIndexReactJS += `\nexport { Icon as ${baseName} } from './${baseName}.tsx'`
icons.push({ name, baseName, title })
}
}
// write the main entry `index.ts` file
// write the main Astro entry `index.ts` file
await fs.writeFile(ps.resolve(distDir, 'index.ts'), contentOfIndexJS, 'utf8')
// write the main React entry `react/index.ts` file
await fs.writeFile(
ps.resolve(`${distDir}/react`, 'index.ts'),
contentOfIndexReactJS,
'utf8'
)
spinner.succeed(
`${chalk.bold('[create-icons]')} Generated ${
icons.length

View File

@ -1,43 +1,5 @@
import { optimize as optimizeSVGNative } from 'svgo'
export const toAstroComponent = (innerSVG: string, title: string) => `---
import type { Props } from './Props.ts';
export type { Props };
let {
size = '24px',
title,
width = size,
height = size,
...props
}: Props = {
'fill': 'none',
'title': '${title}',
'viewBox': '0 0 24 24',
...Astro.props
}
const toAttributeSize = (size: number | string) =>
String(size).replace(/(?<=[0-9])x$/, 'em')
size = toAttributeSize(size)
width = toAttributeSize(width)
height = toAttributeSize(height)
---
<style is:global>
.icon {
width: 1em;
height: 1em;
stroke: currentcolor;
stroke-width: var(--border-width);
stroke-linecap: round;
stroke-linejoin: round;
fill: none;
vertical-align: baseline;
}
</style>
<svg {width} {height} {...props} class="icon">{title ? (<title>{title}</title>) : ''}${innerSVG}</svg>`
export const toInnerSvg = (input: string) =>
optimizeSVGNative(input, {
plugins: [

View File

@ -0,0 +1,37 @@
export const toAstroComponent = (innerSVG: string, title: string) => `---
import type { Props } from './Props.d.ts';
export type { Props };
let {
size = '24px',
title,
width = size,
height = size,
...props
}: Props = {
'fill': 'none',
'title': '${title}',
'viewBox': '0 0 24 24',
...Astro.props
}
const toAttributeSize = (size: number | string) =>
String(size).replace(/(?<=[0-9])x$/, 'em')
size = toAttributeSize(size)
width = toAttributeSize(width)
height = toAttributeSize(height)
---
<style is:global>
.icon {
width: 1em;
height: 1em;
stroke: currentcolor;
stroke-width: var(--border-width);
stroke-linecap: round;
stroke-linejoin: round;
fill: none;
vertical-align: baseline;
}
</style>
<svg width={width} height={height} {...props} class="icon">{title ? (<title>{title}</title>) : ''}${innerSVG}</svg>`

View File

@ -0,0 +1,35 @@
export const toReactComponent = (innerSVG: string, title: string) => `
import type { Props } from './Props.d.ts';
export function Icon(props: Props) {
let {
size = '24px',
title,
width = size,
height = size
}: Props = {
'title': '${title}',
...props
}
const toAttributeSize = (size: number | string) =>
String(size).replace(/(?<=[0-9])x$/, 'em')
size = toAttributeSize(size)
width = toAttributeSize(width)
height = toAttributeSize(height)
const style = {
'width': '1em',
'height': '1em',
'stroke': 'currentcolor',
'strokeWidth': 'var(--border-width)',
'strokeLinecap': 'round',
'strokeLinejoin': 'round',
'fill': 'none',
'verticalAlign': 'baseline'
}
return <svg width={width} height={height} fill="none" viewBox="0 0 24 24" {...props} style={style}>{title ? (<title>{title}</title>) : ''}${innerSVG}</svg>
}
`