-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature] svg 아이콘 컴포넌트 방식 변환 자동화 스크립트 작성 #31
Changes from 25 commits
84d7adc
6edcbd0
6ab2230
db49121
6f8a213
7687ddc
9cbab74
807ab4a
6c14c6c
2ca243f
15b5dd0
1662cd8
f173ef7
feb0396
3637b50
e9c6de7
5d29bbb
8aa7e42
476fa30
e835760
d518a60
988a436
87c8894
8ea29c1
1ffb655
fc4a5e3
8c1f90d
c1a56a8
0704e9b
7efae93
4f29092
8d39402
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,7 @@ module.exports = { | |
globals: { | ||
React: true, | ||
JSX: true, | ||
console: true, | ||
}, | ||
env: { | ||
browser: true, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import { existsSync, promises as fs } from "fs"; | ||
import path from "path"; | ||
|
||
const SVG_DIR = "../wow-icons/src/svg"; | ||
const COMPONENT_DIR = "../wow-icons/src/react"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아하 넵 일단은 svg 폴더 내부에 필요한 파일들 다 넣어두고 스크립트 실행시키면 react 폴더에 변환된 아이콘 + export 파일 생성되게 하는 걸 의도했어요 |
||
|
||
type SvgComponentMap = { [key: string]: string }; | ||
|
||
const generateSvgComponentMap = async () => { | ||
const svgFiles = (await fs.readdir(SVG_DIR)).reduce<SvgComponentMap>( | ||
(map, svgFile) => { | ||
const componentName = path | ||
.basename(svgFile, ".svg") | ||
.replace(/(^\w|-\w)/g, (match) => match.replace("-", "").toUpperCase()); | ||
map[componentName] = svgFile; | ||
|
||
return map; | ||
}, | ||
{} | ||
); | ||
|
||
return svgFiles; | ||
}; | ||
|
||
const deleteUnusedComponentFiles = async (svgComponentMap: SvgComponentMap) => { | ||
if (!existsSync(COMPONENT_DIR)) { | ||
fs.mkdir(COMPONENT_DIR); | ||
return; | ||
} | ||
|
||
const componentFiles = await fs.readdir(COMPONENT_DIR); | ||
const componentFilesToDelete = componentFiles.filter((componentFile) => { | ||
const componentName = path.basename(componentFile, ".tsx"); | ||
return !(componentName in svgComponentMap); | ||
}); | ||
|
||
await Promise.all( | ||
componentFilesToDelete.map((file) => { | ||
const componentFilePath = path.resolve(COMPONENT_DIR, file); | ||
return fs.unlink(componentFilePath); | ||
}) | ||
); | ||
}; | ||
|
||
const createComponentContent = ( | ||
componentName: string, | ||
svgContent: string, | ||
svgFile: string | ||
): string => { | ||
const iconName = path.basename(svgFile, ".svg"); | ||
const modifiedSvgContent = svgContent | ||
.replace(/-(\w)/g, (_, letter) => letter.toUpperCase()) | ||
.replace(/width="(\d+)"/g, `width={width}`) | ||
.replace(/height="(\d+)"/g, `height={height}`) | ||
.replace(/viewBox="(.*?)"/g, `viewBox={viewBox}`) | ||
.replace(/<svg([^>]*)fill="[^"]*"([^>]*)>/, "<svg$1$2>") | ||
.replace(/fill="([^"]+)"/g, `fill={color[fill]}`) | ||
.replace(/stroke="([^"]+)"/g, `stroke={color[stroke]}`) | ||
.replace( | ||
/<svg([^>]*)>/, | ||
`<svg$1 aria-label="${iconName} icon" fill="none" ref={ref} className={className} {...rest}>` | ||
); | ||
|
||
return ` | ||
import { forwardRef } from 'react'; | ||
import { color } from "wowds-tokens"; | ||
|
||
import type { IconProps } from "@/types/Icon.ts"; | ||
|
||
const ${componentName} = forwardRef<SVGSVGElement, IconProps>( | ||
({ className, width = 24, height = 24, viewBox = "0 0 24 24", fill = "white", stroke = "white", ...rest }, ref) => { | ||
return ( | ||
${modifiedSvgContent} | ||
); | ||
} | ||
); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 두 번째 방식으로 해결했어요! |
||
${componentName}.displayName = '${componentName}'; | ||
export default ${componentName}; | ||
`; | ||
}; | ||
|
||
const generateComponentFiles = async (svgComponentMap: SvgComponentMap) => { | ||
const components = []; | ||
|
||
for (const [componentName, svgFile] of Object.entries(svgComponentMap)) { | ||
const svgFilePath = path.resolve(SVG_DIR, svgFile); | ||
const svgContent = (await fs.readFile(svgFilePath)).toString(); | ||
|
||
const componentContent = createComponentContent( | ||
componentName, | ||
svgContent, | ||
svgFile | ||
); | ||
const componentFilePath = path.resolve( | ||
COMPONENT_DIR, | ||
`${componentName}.tsx` | ||
); | ||
|
||
await fs.writeFile(componentFilePath, componentContent); | ||
components.push(componentName); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p4; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 굿굿 좋습니다! 반영해뒀습니다~! |
||
|
||
return components; | ||
}; | ||
|
||
const generateExportFile = async (components: string[]) => { | ||
const EXPORT_FILE_PATH = "../wow-icons/src/react/index.ts"; | ||
const exportFileContent = components | ||
.map( | ||
(component) => | ||
`export { default as ${component} } from "./${component}.tsx";` | ||
) | ||
.join("\n"); | ||
|
||
await fs.writeFile(EXPORT_FILE_PATH, exportFileContent); | ||
}; | ||
|
||
(async () => { | ||
try { | ||
const svgComponentMap = await generateSvgComponentMap(); | ||
await deleteUnusedComponentFiles(svgComponentMap); | ||
const components = await generateComponentFiles(svgComponentMap); | ||
await generateExportFile(components); | ||
} catch (error) { | ||
console.log("Error generating components:", error); | ||
} | ||
})(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
export type ColorToken = | ||
| "red50" | ||
| "red100" | ||
| "red150" | ||
| "red200" | ||
| "red300" | ||
| "red400" | ||
| "red500" | ||
| "red600" | ||
| "red700" | ||
| "red800" | ||
| "red850" | ||
| "red900" | ||
| "red950" | ||
| "blue50" | ||
| "blue100" | ||
| "blue150" | ||
| "blue200" | ||
| "blue300" | ||
| "blue400" | ||
| "blue500" | ||
| "blue600" | ||
| "blue700" | ||
| "blue800" | ||
| "blue850" | ||
| "blue900" | ||
| "blue950" | ||
| "yellow50" | ||
| "yellow100" | ||
| "yellow150" | ||
| "yellow200" | ||
| "yellow300" | ||
| "yellow400" | ||
| "yellow500" | ||
| "yellow600" | ||
| "yellow700" | ||
| "yellow800" | ||
| "yellow850" | ||
| "yellow900" | ||
| "yellow950" | ||
| "green50" | ||
| "green100" | ||
| "green150" | ||
| "green200" | ||
| "green300" | ||
| "green400" | ||
| "green500" | ||
| "green600" | ||
| "green700" | ||
| "green800" | ||
| "green850" | ||
| "green900" | ||
| "green950" | ||
| "mono50" | ||
| "mono100" | ||
| "mono150" | ||
| "mono200" | ||
| "mono300" | ||
| "mono400" | ||
| "mono500" | ||
| "mono600" | ||
| "mono700" | ||
| "mono800" | ||
| "mono850" | ||
| "mono900" | ||
| "mono950" | ||
| "white" | ||
| "black" | ||
| "whiteOpacity20" | ||
| "whiteOpacity40" | ||
| "whiteOpacity60" | ||
| "whiteOpacity80" | ||
| "blackOpacity20" | ||
| "blackOpacity40" | ||
| "blackOpacity60" | ||
| "blackOpacity80" | ||
| "primary" | ||
| "success" | ||
| "error" | ||
| "backgroundNormal" | ||
| "backgroundAlternative" | ||
| "backgroundDimmer" | ||
| "sub" | ||
| "outline" | ||
| "textBlack" | ||
| "textWhite" | ||
| "darkDisabled" | ||
| "lightDisabled" | ||
| "blueHover" | ||
| "monoHover" | ||
| "elevatedHover" | ||
| "bluePressed" | ||
| "blueBackgroundPressed" | ||
| "monoBackgroundPressed" | ||
| "shadowSmall" | ||
| "shadowMedium" | ||
| "blueShadow" | ||
| "discord" | ||
| "github"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./Color.ts"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,14 +19,17 @@ | |
"types": "./dist/index.d.ts", | ||
"exports": { | ||
".": { | ||
"types": "./dist/index.d.ts", | ||
"types": "./dist/react/index.d.ts", | ||
"require": "./dist/index.cjs", | ||
"import": "./dist/index.js", | ||
"default": "./dist/index.js" | ||
} | ||
}, | ||
"scripts": { | ||
"build": "rm -rf dist && rollup -c --bundleConfigAsCjs && tsc --emitDeclarationOnly" | ||
"build": "pnpm generate:icons && rm -rf dist && rollup -c --bundleConfigAsCjs && tsc --emitDeclarationOnly", | ||
"generate:icons": "tsx ../scripts/generateReactComponentFromSvg.ts && pnpm format && pnpm lint", | ||
"lint": "eslint --fix ./src/react/**/*.tsx", | ||
"format": "prettier --write ./src/react/**/*.tsx ./src/react/index.ts" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wow-icons 빌드할 때 아이콘 자동 변환하도록 해놨습니다. |
||
}, | ||
"keywords": [], | ||
"author": "gdsc-hongik", | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이건 자꾸 console이 정의되지 않은 거라고 되어 있어서 추가했습니다