123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- 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'])
- })()
|