Spaces:
Build error
Build error
const path = require('node:path') | |
const { open, readdir, access, mkdir, writeFile, appendFile, rm } = require('node:fs/promises') | |
const { parseXml } = require('@rgrove/parse-xml') | |
const camelCase = require('lodash/camelCase') | |
const template = require('lodash/template') | |
const generateDir = async (currentPath) => { | |
try { | |
await mkdir(currentPath, { recursive: true }) | |
} | |
catch (err) { | |
console.error(err.message) | |
} | |
} | |
const processSvgStructure = (svgStructure, replaceFillOrStrokeColor) => { | |
if (svgStructure?.children.length) { | |
svgStructure.children = svgStructure.children.filter(c => c.type !== 'text') | |
svgStructure.children.forEach((child) => { | |
if (child?.name === 'path' && replaceFillOrStrokeColor) { | |
if (child?.attributes?.stroke) | |
child.attributes.stroke = 'currentColor' | |
if (child?.attributes.fill) | |
child.attributes.fill = 'currentColor' | |
} | |
if (child?.children.length) | |
processSvgStructure(child, replaceFillOrStrokeColor) | |
}) | |
} | |
} | |
const generateSvgComponent = async (fileHandle, entry, pathList, replaceFillOrStrokeColor) => { | |
const currentPath = path.resolve(__dirname, 'src', ...pathList.slice(2)) | |
try { | |
await access(currentPath) | |
} | |
catch { | |
await generateDir(currentPath) | |
} | |
const svgString = await fileHandle.readFile({ encoding: 'utf8' }) | |
const svgJson = parseXml(svgString).toJSON() | |
const svgStructure = svgJson.children[0] | |
processSvgStructure(svgStructure, replaceFillOrStrokeColor) | |
const prefixFileName = camelCase(entry.split('.')[0]) | |
const fileName = prefixFileName.charAt(0).toUpperCase() + prefixFileName.slice(1) | |
const svgData = { | |
icon: svgStructure, | |
name: fileName, | |
} | |
const componentRender = template(` | |
// GENERATE BY script | |
// DON NOT EDIT IT MANUALLY | |
import * as React from 'react' | |
import data from './<%= svgName %>.json' | |
import IconBase from '@/app/components/base/icons/IconBase' | |
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' | |
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>(( | |
props, | |
ref, | |
) => <IconBase {...props} ref={ref} data={data as IconData} />) | |
Icon.displayName = '<%= svgName %>' | |
export default Icon | |
`.trim()) | |
await writeFile(path.resolve(currentPath, `${fileName}.json`), JSON.stringify(svgData, '', '\t')) | |
await writeFile(path.resolve(currentPath, `${fileName}.tsx`), `${componentRender({ svgName: fileName })}\n`) | |
const indexingRender = template(` | |
export { default as <%= svgName %> } from './<%= svgName %>' | |
`.trim()) | |
await appendFile(path.resolve(currentPath, 'index.ts'), `${indexingRender({ svgName: fileName })}\n`) | |
} | |
const generateImageComponent = async (entry, pathList) => { | |
const currentPath = path.resolve(__dirname, 'src', ...pathList.slice(2)) | |
try { | |
await access(currentPath) | |
} | |
catch { | |
await generateDir(currentPath) | |
} | |
const prefixFileName = camelCase(entry.split('.')[0]) | |
const fileName = prefixFileName.charAt(0).toUpperCase() + prefixFileName.slice(1) | |
const componentCSSRender = template(` | |
.wrapper { | |
display: inline-flex; | |
background: url(<%= assetPath %>) center center no-repeat; | |
background-size: contain; | |
} | |
`.trim()) | |
await writeFile(path.resolve(currentPath, `${fileName}.module.css`), `${componentCSSRender({ assetPath: path.join('~@/app/components/base/icons/assets', ...pathList.slice(2), entry) })}\n`) | |
const componentRender = template(` | |
// GENERATE BY script | |
// DON NOT EDIT IT MANUALLY | |
import * as React from 'react' | |
import cn from '@/utils/classnames' | |
import s from './<%= fileName %>.module.css' | |
const Icon = React.forwardRef<HTMLSpanElement, React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>>(( | |
{ className, ...restProps }, | |
ref, | |
) => <span className={cn(s.wrapper, className)} {...restProps} ref={ref} />) | |
Icon.displayName = '<%= fileName %>' | |
export default Icon | |
`.trim()) | |
await writeFile(path.resolve(currentPath, `${fileName}.tsx`), `${componentRender({ fileName })}\n`) | |
const indexingRender = template(` | |
export { default as <%= fileName %> } from './<%= fileName %>' | |
`.trim()) | |
await appendFile(path.resolve(currentPath, 'index.ts'), `${indexingRender({ fileName })}\n`) | |
} | |
const walk = async (entry, pathList, replaceFillOrStrokeColor) => { | |
const currentPath = path.resolve(...pathList, entry) | |
let fileHandle | |
try { | |
fileHandle = await open(currentPath) | |
const stat = await fileHandle.stat() | |
if (stat.isDirectory()) { | |
const files = await readdir(currentPath) | |
for (const file of files) | |
await walk(file, [...pathList, entry], replaceFillOrStrokeColor) | |
} | |
if (stat.isFile() && /.+\.svg$/g.test(entry)) | |
await generateSvgComponent(fileHandle, entry, pathList, replaceFillOrStrokeColor) | |
if (stat.isFile() && /.+\.png$/g.test(entry)) | |
await generateImageComponent(entry, pathList) | |
} | |
finally { | |
fileHandle?.close() | |
} | |
} | |
(async () => { | |
await rm(path.resolve(__dirname, 'src'), { recursive: true, force: true }) | |
await walk('public', [__dirname, 'assets']) | |
await walk('vender', [__dirname, 'assets'], true) | |
await walk('image', [__dirname, 'assets']) | |
})() | |