From 360052afe6b2f5fddffa556ec09c4780ba702203 Mon Sep 17 00:00:00 2001 From: yujeong-jeon Date: Tue, 12 Aug 2025 11:42:02 +0900 Subject: [PATCH 1/6] [#123] Modify svgValidator logic to remove unnecessary props check --- .../eslint-plugin/lib/constants/test-data.js | 86 +++++++++++++++++++ .../lib/rules/optimize-svg-components.js | 6 +- .../eslint-plugin/lib/utils/svg/props.test.js | 2 + 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/lib/constants/test-data.js b/packages/eslint-plugin/lib/constants/test-data.js index 2859f0b..2b8aa39 100644 --- a/packages/eslint-plugin/lib/constants/test-data.js +++ b/packages/eslint-plugin/lib/constants/test-data.js @@ -956,3 +956,89 @@ export const EXPORT_DEFAULT_함수형_컴포넌트 = { /> `, } + +export const EMPTY_PROPS_COMPONENT = { + 컴포넌트_코드: ` + import {memo} from 'react'; + + const IconNPayTextShadow = ({}) => ( + + + + + + + + + + + + + + + + + + ); + + export default memo(IconNPayTextShadow); + `, + PROPS_없는_SVG_코드: ` + + + + + + + + + + + + + + + + + `, + PROPS: {}, + 완료된_SVG_코드: ` + + + + + + + + + + + + + + + + + `, +} diff --git a/packages/eslint-plugin/lib/rules/optimize-svg-components.js b/packages/eslint-plugin/lib/rules/optimize-svg-components.js index 8360ca3..76c2af4 100644 --- a/packages/eslint-plugin/lib/rules/optimize-svg-components.js +++ b/packages/eslint-plugin/lib/rules/optimize-svg-components.js @@ -1,9 +1,8 @@ -import {getJSXElement, getImportDeclarations, extractComponentProps} from '@naverpay/ast-parser' +import {getJSXElement, getImportDeclarations} from '@naverpay/ast-parser' import {minimatch} from 'minimatch' import {SVG_OPTIMIZED_COMMENT_CONTENT} from '../constants/index.js' import {findSpecificImportDeclaration, hasSpecificReturnStatement, getAllComments} from '../utils/astParser.js' -import {isEmpty} from '../utils/index.js' import { insertCustomImport, svgoOptimize, @@ -26,12 +25,11 @@ const svgValidator = (context, globalScope) => { findSpecificImportDeclaration(importDeclarations, { from: 'styled-components', }) - const props = hasClassNames ? null : extractComponentProps(globalScope.block) const comments = getAllComments(context) const isOptimizedAlready = comments.some(({value}) => value.includes(SVG_OPTIMIZED_COMMENT_CONTENT)) - return !isEmpty(props) && !hasClassNames && !isOptimizedAlready + return !hasClassNames && !isOptimizedAlready } const includeHangul = (sourceCode, node) => { diff --git a/packages/eslint-plugin/lib/utils/svg/props.test.js b/packages/eslint-plugin/lib/utils/svg/props.test.js index 05e7d46..ea0f5c1 100644 --- a/packages/eslint-plugin/lib/utils/svg/props.test.js +++ b/packages/eslint-plugin/lib/utils/svg/props.test.js @@ -11,6 +11,7 @@ import { STYLE_OBJECT를_포함한_컴포넌트, 함수형_컴포넌트, EXPORT_DEFAULT_함수형_컴포넌트, + EMPTY_PROPS_COMPONENT, } from '../../constants/test-data' import {trimAll} from '../string' import {extractPropsFromLiteralCode} from './props' @@ -35,6 +36,7 @@ describe('extractComponentProps', () => { [STYLE_OBJECT를_포함한_컴포넌트.컴포넌트_코드, STYLE_OBJECT를_포함한_컴포넌트.PROPS], [함수형_컴포넌트.컴포넌트_코드, 함수형_컴포넌트.PROPS], [EXPORT_DEFAULT_함수형_컴포넌트.컴포넌트_코드, EXPORT_DEFAULT_함수형_컴포넌트.PROPS], + [EMPTY_PROPS_COMPONENT.컴포넌트_코드, EMPTY_PROPS_COMPONENT.PROPS], ])('ast tree에서 props를 추출한다.', (code, expectedProps) => { const scope = parseCode(code) From fc6015cd50ddc870af7e22d99ea8142cf1c6f114 Mon Sep 17 00:00:00 2001 From: yujeong-jeon Date: Tue, 12 Aug 2025 11:43:18 +0900 Subject: [PATCH 2/6] [#123] Remove unused index file --- packages/eslint-plugin/lib/utils/index.js | 4 ---- packages/eslint-plugin/lib/utils/svg/validator.test.js | 6 ++---- 2 files changed, 2 insertions(+), 8 deletions(-) delete mode 100644 packages/eslint-plugin/lib/utils/index.js diff --git a/packages/eslint-plugin/lib/utils/index.js b/packages/eslint-plugin/lib/utils/index.js deleted file mode 100644 index 6c165f2..0000000 --- a/packages/eslint-plugin/lib/utils/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import {isEmptyObject} from './object.js' -import {isEmptyString} from './string.js' - -export const isEmpty = (value) => isEmptyString(value) || isEmptyObject(value) || !value diff --git a/packages/eslint-plugin/lib/utils/svg/validator.test.js b/packages/eslint-plugin/lib/utils/svg/validator.test.js index 95421bc..60379a4 100644 --- a/packages/eslint-plugin/lib/utils/svg/validator.test.js +++ b/packages/eslint-plugin/lib/utils/svg/validator.test.js @@ -1,7 +1,6 @@ -import {extractComponentProps, parseToAST, getImportDeclarations} from '@naverpay/ast-parser' +import {parseToAST, getImportDeclarations} from '@naverpay/ast-parser' import {describe, test, expect} from 'vitest' -import {isEmpty} from '..' import {SVG_OPTIMIZED_COMMENT_CONTENT} from '../../constants' import {findSpecificImportDeclaration} from '../astParser' @@ -34,13 +33,12 @@ const svgValidator = (globalScope) => { findSpecificImportDeclaration(importDeclarations, { from: 'styled-components', }) - const props = hasClassNames ? null : extractComponentProps(globalScope.block) const comments = globalScope.comments const isOptimizedAlready = comments.some(({value}) => value.includes(SVG_OPTIMIZED_COMMENT_CONTENT)) - return !isEmpty(props) && !hasClassNames && !isOptimizedAlready + return !hasClassNames && !isOptimizedAlready } describe('svgValidator', () => { From e4ee455f1c4b27ba3129a743e10a7bc78d11bc25 Mon Sep 17 00:00:00 2001 From: yujeong-jeon Date: Tue, 12 Aug 2025 11:55:08 +0900 Subject: [PATCH 3/6] [#123] test: {} -> undefined --- packages/eslint-plugin/lib/constants/test-data.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/lib/constants/test-data.js b/packages/eslint-plugin/lib/constants/test-data.js index 2b8aa39..168b340 100644 --- a/packages/eslint-plugin/lib/constants/test-data.js +++ b/packages/eslint-plugin/lib/constants/test-data.js @@ -961,7 +961,7 @@ export const EMPTY_PROPS_COMPONENT = { 컴포넌트_코드: ` import {memo} from 'react'; - const IconNPayTextShadow = ({}) => ( + const IconNPayTextShadow = () => ( From 93d42c38ff2ddf605bec374e09fead0bf29652ac Mon Sep 17 00:00:00 2001 From: yujeong-jeon <148525642+yujeong-jeon@users.noreply.github.com> Date: Tue, 12 Aug 2025 11:59:41 +0900 Subject: [PATCH 4/6] Create khaki-mammals-retire.md --- .changeset/khaki-mammals-retire.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/khaki-mammals-retire.md diff --git a/.changeset/khaki-mammals-retire.md b/.changeset/khaki-mammals-retire.md new file mode 100644 index 0000000..e09fed7 --- /dev/null +++ b/.changeset/khaki-mammals-retire.md @@ -0,0 +1,7 @@ +--- +"@naverpay/eslint-plugin": patch +--- + +[eslint-plugins] Remove empty props check + +PR: [[eslint-plugins] Remove empty props check](https://github.com/NaverPayDev/code-style/pull/124) From fb0cb72dd02b5a97c3d200d157d19ab367a43dce Mon Sep 17 00:00:00 2001 From: yujeong-jeon Date: Thu, 14 Aug 2025 16:51:46 +0900 Subject: [PATCH 5/6] [#123] Modify svgoOptimize to include removeEmptyPaths plugin for handling empty path elements --- .../eslint-plugin/lib/utils/svg/transform.js | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/eslint-plugin/lib/utils/svg/transform.js b/packages/eslint-plugin/lib/utils/svg/transform.js index fb7d82a..275fbe6 100644 --- a/packages/eslint-plugin/lib/utils/svg/transform.js +++ b/packages/eslint-plugin/lib/utils/svg/transform.js @@ -134,6 +134,32 @@ export function insertCustomImport({fixer, scope, context}) { return result } +/** + * @description + * + * svgo 플러그인 중, path 요소를 제어하는 플러그인은 과한 최적화 진행하므로 본 플러그인의 의도와 불일치 + * + * (사전 테스트에서 svg 이모지의 형상이 달라지는 현상 발생) + * + * 따라서 path 요소를 제거하는 플러그인을 추가하여, d 속성이 없는 path 요소만을 제거 + */ +const removeEmptyPaths = { + name: 'removeEmptyPaths', + fn: () => { + return { + element: { + enter: (node, parentNode) => { + // path 요소이면서 d 속성이 없거나 빈 경우 + if (node.name === 'path' && (!node.attributes.d || node.attributes.d.trim() === '')) { + // 부모에서 이 노드를 제거 + parentNode.children = parentNode.children.filter((child) => child !== node) + } + }, + }, + } + }, +} + /** * @typedef NextSvgContent * @type {object} @@ -160,6 +186,10 @@ export const svgoOptimize = ({svgCode, props, exceptAttr}) => { attrs: 'g', }, }, + { + name: 'removeEmptyAttrs', + active: true, + }, { name: 'collapseGroups', active: true, @@ -172,6 +202,7 @@ export const svgoOptimize = ({svgCode, props, exceptAttr}) => { name: 'mergePaths', active: true, }, + removeEmptyPaths, ], multipass: false, }) From 9079f0cea68f4cc65fd4eb6619799a53ac00f2c4 Mon Sep 17 00:00:00 2001 From: yujeong-jeon Date: Thu, 14 Aug 2025 16:52:25 +0900 Subject: [PATCH 6/6] [#123] Modify tests --- .../eslint-plugin/lib/constants/test-data.js | 172 ++++++++++-------- .../eslint-plugin/lib/utils/svg/props.test.js | 2 + .../lib/utils/svg/transform.test.js | 16 ++ 3 files changed, 112 insertions(+), 78 deletions(-) diff --git a/packages/eslint-plugin/lib/constants/test-data.js b/packages/eslint-plugin/lib/constants/test-data.js index 168b340..332fa8f 100644 --- a/packages/eslint-plugin/lib/constants/test-data.js +++ b/packages/eslint-plugin/lib/constants/test-data.js @@ -958,87 +958,103 @@ export const EXPORT_DEFAULT_함수형_컴포넌트 = { } export const EMPTY_PROPS_COMPONENT = { - 컴포넌트_코드: ` - import {memo} from 'react'; - - const IconNPayTextShadow = () => ( - - - - - - - - - - - - - - - - - - ); + 컴포넌트_코드: `import React from 'react' + import * as CSS from 'csstype' - export default memo(IconNPayTextShadow); - `, - PROPS_없는_SVG_코드: ` - - - - - - - - - - - - - - - - + export default function IconExample() { + return ( + + + + ) + }`, + PROPS: {}, + PROPS_없는_SVG_코드: ` + `, + /** + * props가 없는 경우 동적으로 변경되는 값이 없으므로 그대로 유지 + */ + 완료된_SVG_코드: ` + + `, +} + +export const EMPTY_CONTAINER_COMPONENT = { + 컴포넌트_코드: `import React from 'react' + import * as CSS from 'csstype' + + export default function IconExample() { + return ( + + + + + + + + + + + ) + }`, PROPS: {}, - 완료된_SVG_코드: ` - - - + PROPS_없는_SVG_코드: ` + + + + - - - - - - - - - - - - + + + + `, + /** + * props가 없는 경우 동적으로 변경되는 값이 없으므로 그대로 유지 + */ + 완료된_SVG_코드: ` + `, } diff --git a/packages/eslint-plugin/lib/utils/svg/props.test.js b/packages/eslint-plugin/lib/utils/svg/props.test.js index ea0f5c1..b6de916 100644 --- a/packages/eslint-plugin/lib/utils/svg/props.test.js +++ b/packages/eslint-plugin/lib/utils/svg/props.test.js @@ -12,6 +12,7 @@ import { 함수형_컴포넌트, EXPORT_DEFAULT_함수형_컴포넌트, EMPTY_PROPS_COMPONENT, + EMPTY_CONTAINER_COMPONENT, } from '../../constants/test-data' import {trimAll} from '../string' import {extractPropsFromLiteralCode} from './props' @@ -37,6 +38,7 @@ describe('extractComponentProps', () => { [함수형_컴포넌트.컴포넌트_코드, 함수형_컴포넌트.PROPS], [EXPORT_DEFAULT_함수형_컴포넌트.컴포넌트_코드, EXPORT_DEFAULT_함수형_컴포넌트.PROPS], [EMPTY_PROPS_COMPONENT.컴포넌트_코드, EMPTY_PROPS_COMPONENT.PROPS], + [EMPTY_CONTAINER_COMPONENT.컴포넌트_코드, EMPTY_CONTAINER_COMPONENT.PROPS], ])('ast tree에서 props를 추출한다.', (code, expectedProps) => { const scope = parseCode(code) diff --git a/packages/eslint-plugin/lib/utils/svg/transform.test.js b/packages/eslint-plugin/lib/utils/svg/transform.test.js index 720cd1c..ab2f43f 100644 --- a/packages/eslint-plugin/lib/utils/svg/transform.test.js +++ b/packages/eslint-plugin/lib/utils/svg/transform.test.js @@ -9,6 +9,8 @@ import { PROPS_변수_컴포넌트, 함수형_컴포넌트, EXPORT_DEFAULT_함수형_컴포넌트, + EMPTY_PROPS_COMPONENT, + EMPTY_CONTAINER_COMPONENT, } from '../../constants/test-data' import {trimAll} from '../string' import {svgoOptimize} from './transform' @@ -95,6 +97,20 @@ describe('transform', () => { EXPORT_DEFAULT_함수형_컴포넌트.완료된_SVG_코드, undefined, ], + [ + 'EMPTY_PROPS_COMPONENT', + EMPTY_PROPS_COMPONENT.PROPS_없는_SVG_코드, + EMPTY_PROPS_COMPONENT.PROPS, + EMPTY_PROPS_COMPONENT.완료된_SVG_코드, + undefined, + ], + [ + 'EMPTY_CONTAINER_COMPONENT', + EMPTY_CONTAINER_COMPONENT.PROPS_없는_SVG_코드, + EMPTY_CONTAINER_COMPONENT.PROPS, + EMPTY_CONTAINER_COMPONENT.완료된_SVG_코드, + undefined, + ], ])('[%s] 정상적으로 svg transformation을 수행한다.', (_, svgCode, props, result, exceptAttr) => { const data = svgoOptimize({svgCode, props, exceptAttr})