Last active
June 13, 2023 10:30
-
-
Save FlatMapIO/23904f07dc7b6bd10efbf680b6622cad to your computer and use it in GitHub Desktop.
generate qwik svg icons
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as fs from 'node:fs' | |
import * as path from 'node:path' | |
import { startCase } from 'lodash-es' | |
import { format } from 'prettier' | |
import * as Rx from 'rxjs' | |
import * as svgo from 'svgo' | |
import config from './iconset' | |
const formatOpts = require('../prettier.config.cjs') | |
const typesTs = `import { QwikIntrinsicElements } from '@builder.io/qwik' | |
type Size = 16 | 20 | 24 | 28 | 32 | 36 | 40 | 44 | 48 | string | |
export type IconProps = QwikIntrinsicElements["svg"] | |
export type RectIconProps = Omit<IconProps, 'width' | 'height'> & { size?: Size} | |
` | |
const header = `/** | |
* This file was automatically generated by scripts/gen-icons.ts | |
**/ | |
import type { IconProps, RectIconProps } from './types' | |
` | |
const generateQwikSvgJsx = ({ name, svg }: { name: string; svg: string }) => { | |
let width = '' | |
let height = '' | |
const processed = svgo.optimize(svg, { | |
// https://github.com/svg/svgo | |
plugins: [ | |
'removeComments', | |
'removeDesc', | |
'removeDoctype', | |
'removeMetadata', | |
'removeEditorsNSData', | |
'removeTitle', | |
'removeXMLProcInst', | |
'removeHiddenElems', | |
'removeEmptyContainers', | |
{ | |
name: 'rewrite', | |
fn: (root, config) => { | |
return { | |
element: { | |
enter(node, parentNode) { | |
if (node.name === 'svg') { | |
node.attributes['PROPS'] = '%PROPS_HOLDER%' | |
node.attributes['key'] = '%KEY_HOLDER%' | |
width = node.attributes['width'] | |
height = node.attributes['height'] | |
if (!width || !height) { | |
const vbox = node.attributes['viewBox'] | |
if (!vbox) | |
throw new Error('width, height, viewBox not found') | |
const [, , vw, vh] = vbox.split(' ') | |
if (!width) { | |
width = vw | |
} | |
if (!height) { | |
height = vh | |
} | |
} | |
node.attributes['width'] = '%WIDTH_HOLDER%' | |
node.attributes['height'] = '%HEIGHT_HOLDER%' | |
} | |
}, | |
}, | |
} | |
}, | |
}, | |
], | |
}).data | |
width = width.trim() | |
height = height.trim() | |
const withUnit = /^\d+[a-zA-Z]+$/ | |
let replaced = processed | |
.replaceAll(`PROPS="%PROPS_HOLDER%"`, '{...props}') | |
.replaceAll(`"%KEY_HOLDER%"`, `{key}`) | |
if (width === height) { | |
replaced = replaced | |
.replaceAll('"%WIDTH_HOLDER%"', `{size}`) | |
.replaceAll('"%HEIGHT_HOLDER%"', `{size}`) | |
return `export const ${name} = ({size=${ | |
withUnit.test(width) ? `"${width}"` : width | |
}, ...props}: RectIconProps, key: string) => {\n return (\n ${replaced})\n}` | |
} else { | |
replaced = replaced | |
.replaceAll( | |
'"%WIDTH_HOLDER%"', | |
`{${withUnit.test(width) ? `"${width}"` : width}}`, | |
) | |
.replaceAll( | |
'"%HEIGHT_HOLDER%"', | |
`{${withUnit.test(height) ? `"${height}"` : height}}`, | |
) | |
return `export const ${name} = (props: IconProps, key: string) => {\n return (\n ${replaced})\n}` | |
} | |
} | |
type IconSetEntry = { | |
name: string | |
refer: string | |
prefix: string | |
baseUrl: string | |
icons: string[] | |
} | |
export type GeneratorConfig = { | |
outDir: string | |
entries: IconSetEntry[] | |
} | |
async function generateIconSet(entry: IconSetEntry) { | |
const iconSet = Array.from(new Set(entry.icons).keys()) | |
const result$ = Rx.from(iconSet).pipe( | |
Rx.tap((it) => console.log(`fetching ${entry.name}/${it}`)), | |
Rx.mergeMap( | |
// (it) => Rx.from(fetch(path.join(entry.baseUrl, it + '.svg'))), | |
(it) => | |
Rx.zip([ | |
Rx.of(it), | |
Rx.from(fetch(path.join(entry.baseUrl, it + '.svg'))).pipe( | |
Rx.mergeMap((it) => it.text()), | |
Rx.map((it) => ({ data: it })), | |
Rx.catchError((it: Error) => Rx.of({ error: it })), | |
), | |
]), | |
100, | |
), | |
Rx.map(([name, result]) => ({ name, result })), | |
Rx.toArray(), | |
) | |
const result = await Rx.lastValueFrom(result$) | |
let code = header | |
for (let it of result) { | |
if ('error' in it.result) { | |
console.log(`${entry.name}/${it.name} fetch failed`) | |
continue | |
} | |
const iconName = entry.prefix + startCase(it.name).replaceAll(' ', '') | |
code += | |
generateQwikSvgJsx({ | |
name: iconName, | |
svg: it.result.data, | |
}) + '\n' | |
} | |
const formatted: string = format(code, formatOpts) | |
return formatted | |
} | |
async function build(config: GeneratorConfig) { | |
if (!fs.statSync(config.outDir).isDirectory()) { | |
fs.mkdirSync(config.outDir) | |
} | |
if (!fs.existsSync(path.join(config.outDir, 'types.ts'))) { | |
fs.writeFileSync(path.join(config.outDir, 'types.ts'), typesTs, 'utf-8') | |
} | |
const all = await Promise.all(config.entries.map(generateIconSet)) | |
for (let i = 0; i < all.length; i++) { | |
const it = config.entries[i] | |
fs.writeFileSync(`${config.outDir}/${it.name}.tsx`, all[i], 'utf-8') | |
} | |
} | |
build(config) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { QwikIntrinsicElements } from '@builder.io/qwik' | |
type Size = 16 | 20 | 24 | 28 | 32 | 36 | 40 | 44 | 48 | number | string | |
type IconProps = QwikIntrinsicElements['svg'] | |
type RectIconProps = Omit<IconProps, 'width' | 'height'> & { size?: Size } | |
export const LuChevronUp = ( | |
{ size = 24, ...props }: RectIconProps, | |
key: string, | |
) => { | |
return ( | |
<svg | |
xmlns='http://www.w3.org/2000/svg' | |
width={size} | |
height={size} | |
fill='none' | |
stroke='currentColor' | |
stroke-linecap='round' | |
stroke-linejoin='round' | |
stroke-width='2' | |
{...props} | |
key={key} | |
> | |
<path d='M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z' /> | |
</svg> | |
) | |
} | |
export const LuChevronDown = ( | |
{ size = 24, ...props }: RectIconProps, | |
key: string, | |
) => { | |
return ( | |
<svg | |
xmlns='http://www.w3.org/2000/svg' | |
width={size} | |
height={size} | |
fill='none' | |
stroke='currentColor' | |
stroke-linecap='round' | |
stroke-linejoin='round' | |
stroke-width='2' | |
{...props} | |
key={key} | |
> | |
<circle cx='12' cy='12' r='10' /> | |
<path d='m4.93 4.93 14.14 14.14' /> | |
</svg> | |
) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { type GeneratorConfig } from './gen-icons' | |
const config: GeneratorConfig = { | |
outDir: './src/components/icons/', | |
entries: [ | |
{ | |
name: 'lucide', | |
refer: 'https://lucide.dev/icons/', | |
prefix: 'Lu', | |
baseUrl: 'https://cdn.jsdelivr.net/npm/lucide-static@latest/icons', | |
icons: [ | |
'chevron-up', | |
'chevron-down', | |
'repeat-2', | |
'clipboard', | |
'clipboard-check', | |
'bookmark', | |
'check', | |
'check-check', | |
'edit-3', | |
'settings-2', | |
'sun-medium', | |
'arrow-down-to-line', | |
'moon', | |
'credit-card', | |
'arrow-right-from-line', | |
'thumbs-down', | |
'thumbs-up', | |
'stop-circle', | |
'menu', | |
'more-horizontal', | |
'alert-triangle', | |
'help-circle', | |
'info', | |
'trash-2', | |
'loader-2', | |
'star', | |
'ban', | |
'log-out', | |
'mic', | |
'x', | |
'plus' | |
], | |
}, | |
{ | |
name: 'logos', | |
prefix: 'Lo', | |
refer: 'https://svgporn.com/', | |
baseUrl: 'https://raw.githubusercontent.com/gilbarbara/logos/main/logos/', | |
icons: [ | |
'github-icon', | |
'google-icon', | |
'google-bard-icon', | |
'openai', | |
'openai-icon', | |
'apple', | |
'stripe', | |
'zapier', | |
'qwik-icon', | |
'pinecone', | |
'notion-icon', | |
'replit', | |
'replit-icon', | |
], | |
}, | |
// { | |
// name: 'heroicons', | |
// prefix: 'He', | |
// refer: 'https://heroicons.com/', | |
// baseUrl: 'https://cdn.jsdelivr.net/npm/heroicons@latest/24/outline/', | |
// icons: ['backspace'], | |
// }, | |
// { | |
// name: 'carbon', | |
// prefix: 'Ca', | |
// refer: 'https://carbondesignsystem.com/guidelines/icons/library/', | |
// baseUrl: 'https://cdn.jsdelivr.net/npm/@carbon/icons@latest/svg/24/', | |
// icons: [ | |
// ], | |
// }, | |
], | |
} | |
export default config |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"scripts": { | |
"icons.gen": "tsx scripts/gen-icons.ts", | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment