Skip to content

Instantly share code, notes, and snippets.

@repalash
Last active January 15, 2024 17:50
Build Icon components (JSX, React, Vue, Svelte, SVG)
import path from 'node:path'
import fs from 'node:fs'
const jsxTemplate = (code)=>`
export default function ({width=16, height=16}) {
return (
${code}
)
}
`
const vueTemplate = (code)=>`
<script setup>
defineProps({
width: {type: Number, default: 16},
height: {type: Number, default: 16},
})
</script>
<template>
${code}
</template>
`
const svelteTemplate = (code)=>`
<script>
export let width = 16;
export let height = 16;
</script>
${code}
`
function buildSvgString(svg, type/*: 'vue'|'svelte'|'jsx'|'html'*/ = 'jsx'){
return type === 'vue' ?
svg.replace(/width="(\d+)"/, 'width={{width}}').replace(/height="(\d+)"/, 'height={{height}}') :
type !== 'html' ?
svg.replace(/width="(\d+)"/, 'width={width}').replace(/height="(\d+)"/, 'height={height}') :
svg
// svg.replace(/width="(\d+)"/, 'width="100%"').replace(/height="(\d+)"/, 'height="100%"')
}
const readDirRecursive = async (filePath) => {
const dir = await fs.promises.readdir(filePath);
const files = await Promise.all(dir.map(async relativePath => {
const absolutePath = path.join(filePath, relativePath);
const stat = await fs.promises.lstat(absolutePath);
return stat.isDirectory() ? readDirRecursive(absolutePath) : absolutePath;
}));
return files.flat();
}
const dir = 'data'
const files = await readDirRecursive(dir)
if(!fs.existsSync('lib')) fs.mkdirSync('lib')
const jsxDir = 'lib/jsx'
if(!fs.existsSync(jsxDir)) fs.mkdirSync(jsxDir)
const vueDir = 'lib/vue'
if(!fs.existsSync(vueDir)) fs.mkdirSync(vueDir)
const svelteDir = 'lib/svelte'
if(!fs.existsSync(svelteDir)) fs.mkdirSync(svelteDir)
const svgDir = 'lib/svg'
if(!fs.existsSync(svelteDir)) fs.mkdirSync(svelteDir)
let jsxIndexFile = ``
let vueIndexFile = ``
let svelteIndexFile = ``
let svgIndexFile = ``
for (const file of files) {
if(!file.endsWith('.svg')) continue
const contents = fs.readFileSync(file, 'utf8').trim()
const jsx = jsxTemplate(buildSvgString(contents, 'jsx')).trim()
const vue = vueTemplate(buildSvgString(contents, 'vue')).trim()
const svelte = svelteTemplate(buildSvgString(contents, 'svelte')).trim()
const svg = buildSvgString(contents, 'html').trim()
let name = file.replace(/^data\//, '').replace('.svg', '');
let jsxPath = jsxDir + '/' + name + '.jsx'
let vuePath = vueDir + '/' + name + '.vue'
let sveltePath = svelteDir + '/' + name + '.svelte'
let svgPath = svgDir + '/' + name + '.svg'
try {
fs.mkdirSync(jsxPath.split('/').slice(0, -1).join('/'), { recursive: true })
fs.mkdirSync(vuePath.split('/').slice(0, -1).join('/'), { recursive: true })
fs.mkdirSync(sveltePath.split('/').slice(0, -1).join('/'), { recursive: true })
fs.mkdirSync(svgPath.split('/').slice(0, -1).join('/'), { recursive: true })
} catch (error) {}
fs.writeFileSync(jsxPath, jsx)
fs.writeFileSync(vuePath, vue)
fs.writeFileSync(sveltePath, svelte)
fs.writeFileSync(svgPath, svg)
const componentName = name.replace(/[\/\\\-.@]/g, '_') // replace invalid characters / \\ - . @
jsxIndexFile += `export { default as ${componentName} } from './${name}.jsx'\n`
vueIndexFile += `export { default as ${componentName} } from './${name}.vue'\n`
svelteIndexFile += `export { default as ${componentName} } from './${name}.svelte'\n`
svgIndexFile += `export { default as ${componentName} } from './${name}.svg'\n`
console.log(file)
}
fs.writeFileSync(jsxDir + '/index.js', jsxIndexFile)
fs.writeFileSync(vueDir + '/index.js', vueIndexFile)
fs.writeFileSync(svelteDir + '/index.js', svelteIndexFile)
fs.writeFileSync(svgDir + '/index.js', svgIndexFile)
@repalash
Copy link
Author

Make a directory named data and run the script with node build-icon-components.js in the same directory.

This will create a folder named libs with the components that can be exported in a package.json like:

"exports": {
    "./react": "./lib/jsx/index.js",
    "./vue": "./lib/vue/index.js",
    "./svelte": "./lib/svelte/index.js",
    "./svg": "./lib/svg/index.js"
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment