-
Notifications
You must be signed in to change notification settings - Fork 48
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
Add codemod for z-index interpolation #1845
Changes from all commits
8ce1df5
67a73e3
fbc8063
600e433
7869da7
6b03348
af607b7
5a454b9
db6c0ad
2f43af9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@channel.io/bezier-codemod": minor | ||
--- | ||
|
||
Add codemod for z-index interpolation and enum |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { | ||
type SourceFile, | ||
SyntaxKind, | ||
} from 'ts-morph' | ||
|
||
import { renameEnumMember } from '../utils/enum.js' | ||
import { hasNamedImportInImportDeclaration } from '../utils/import.js' | ||
|
||
type Name = string | ||
type Member = string | ||
type Value = string | ||
export type EnumTransformMap = Record<Name, Record<Member, Value>> | ||
|
||
export const transformEnumMemberToStringLiteral = (sourceFile: SourceFile, enumTransforms: EnumTransformMap) => { | ||
const transformedEnumNames: string[] = [] | ||
|
||
Object | ||
.keys(enumTransforms) | ||
.forEach((enumName) => { | ||
if (hasNamedImportInImportDeclaration(sourceFile, enumName, '@channel.io/bezier-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. 👍 |
||
sourceFile | ||
.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression) | ||
.filter((node) => node.getFirstChildByKind(SyntaxKind.Identifier)?.getText() === enumName) | ||
.forEach((node) => { | ||
const enumValue = node.getLastChildByKind(SyntaxKind.Identifier)?.getText() | ||
if (enumValue) { | ||
renameEnumMember(node, enumTransforms[enumName][enumValue]) | ||
transformedEnumNames.push(enumName) | ||
} | ||
}) | ||
} | ||
}) | ||
|
||
if (transformedEnumNames.length > 0) { | ||
sourceFile.fixUnusedIdentifiers() | ||
return true | ||
} | ||
|
||
return undefined | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,24 @@ | ||
import { | ||
Node, | ||
type SourceFile, | ||
SyntaxKind, | ||
} from 'ts-morph' | ||
|
||
type EnumTransforms = Record<string, Record<string, string>> | ||
|
||
function transformEnumMemberToStringLiteral(sourceFile: SourceFile, enumTransforms: EnumTransforms) { | ||
const transformedEnumNames: string[] = [] | ||
|
||
sourceFile.forEachDescendant((node) => { | ||
if (node.isKind(SyntaxKind.PropertyAccessExpression)) { | ||
const firstIdentifier = node.getFirstChildByKind(SyntaxKind.Identifier) | ||
const lastIdentifier = node.getLastChildByKind(SyntaxKind.Identifier) | ||
|
||
if (firstIdentifier && lastIdentifier) { | ||
const declarationSymbol = firstIdentifier.getSymbol() | ||
const memberSymbol = lastIdentifier.getSymbol() | ||
const memberValueDeclaration = memberSymbol?.getValueDeclaration() | ||
import { type SourceFile } from 'ts-morph' | ||
|
||
if (Node.isEnumMember(memberValueDeclaration)) { | ||
const enumName = declarationSymbol?.getName() | ||
const enumMember = memberSymbol?.getName() | ||
|
||
if (enumName && enumMember) { | ||
const newEnumMemberValue = enumTransforms[enumName][enumMember] | ||
const ancestor = node.getFirstAncestor() | ||
if (ancestor?.isKind(SyntaxKind.JsxExpression)) { | ||
ancestor.replaceWithText(`'${newEnumMemberValue}'`) | ||
} else { | ||
node.replaceWithText(`'${newEnumMemberValue}'`) | ||
} | ||
|
||
transformedEnumNames.push(enumName) | ||
} | ||
} | ||
} | ||
} | ||
}) | ||
|
||
if (transformedEnumNames.length > 0) { | ||
sourceFile.fixUnusedIdentifiers() | ||
return true | ||
} | ||
|
||
return undefined | ||
import { | ||
type EnumTransformMap, | ||
transformEnumMemberToStringLiteral, | ||
} from '../../shared/enum.js' | ||
|
||
const ENUM_TRANSFORM_MAP: EnumTransformMap = { | ||
ProgressBarSize: { | ||
M: 'm', | ||
S: 's', | ||
}, | ||
ProgressBarVariant: { | ||
Green: 'green', | ||
GreenAlt: 'green-alt', | ||
Monochrome: 'monochrome', | ||
}, | ||
} | ||
|
||
function enumMemberToStringLiteral(sourceFile: SourceFile): true | void { | ||
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. 추후에 이 함수에 v2 prefix 를 붙이고 변환해야하는 enum 을 모두 포함해야 합니다. (#1815) |
||
const enumTransforms: EnumTransforms = { | ||
ProgressBarSize: { | ||
M: 'm', | ||
S: 's', | ||
}, | ||
ProgressBarVariant: { | ||
Green: 'green', | ||
GreenAlt: 'green-alt', | ||
Monochrome: 'monochrome', | ||
}, | ||
} | ||
return transformEnumMemberToStringLiteral(sourceFile, enumTransforms) | ||
return transformEnumMemberToStringLiteral(sourceFile, ENUM_TRANSFORM_MAP) | ||
} | ||
|
||
export default enumMemberToStringLiteral |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Overlay, ZIndex } from '@channel.io/bezier-react' | ||
|
||
export function SelectionOverlay () { | ||
return ( | ||
<Overlay | ||
container={{ style: { zIndex: ZIndex.Overlay } }} | ||
/> | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Overlay } from '@channel.io/bezier-react' | ||
|
||
export function SelectionOverlay () { | ||
return ( | ||
<Overlay | ||
container={{ style: { zIndex: 'var(--z-index-overlay)' } }} | ||
/> | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { ZIndex } from 'some-library' | ||
|
||
export const OVERLAY_POSITION1 = { | ||
zIndex: ZIndex.Modal, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { ZIndex } from 'some-library' | ||
|
||
export const OVERLAY_POSITION1 = { | ||
zIndex: ZIndex.Modal, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { ZIndex } from "@channel.io/bezier-react" | ||
|
||
export const OVERLAY_POSITION1 = { | ||
zIndex: ZIndex.Modal, | ||
} | ||
|
||
export const OVERLAY_POSITION2 = { | ||
zIndex: ZIndex.Float, | ||
} | ||
|
||
export const OVERLAY_POSITION3 = { | ||
zIndex: ZIndex.Important, | ||
} | ||
|
||
export const OVERLAY_POSITION4 = { | ||
zIndex: ZIndex.Hide, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
|
||
export const OVERLAY_POSITION1 = { | ||
zIndex: 'var(--z-index-modal)', | ||
} | ||
|
||
export const OVERLAY_POSITION2 = { | ||
zIndex: 'var(--z-index-float)', | ||
} | ||
|
||
export const OVERLAY_POSITION3 = { | ||
zIndex: 'var(--z-index-important)', | ||
} | ||
|
||
export const OVERLAY_POSITION4 = { | ||
zIndex: 'var(--z-index-hidden)', | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { ZIndex, styled } from "@channel.io/bezier-react"; | ||
|
||
const Wrapper = styled.div` | ||
z-index: ${ZIndex.Hide} | ||
` | ||
|
||
const Wrapper = styled.div` | ||
z-index: ${ZIndex.Base}; | ||
` | ||
|
||
const Wrapper = styled.div` | ||
z-index: ${ZIndex.Float}; | ||
` | ||
|
||
const Wrapper = styled.div` | ||
z-index: ${ZIndex.Tooltip} | ||
` | ||
|
||
const Wrapper = styled.div` | ||
z-index: ${ZIndex.Important} | ||
` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { styled } from "@channel.io/bezier-react"; | ||
|
||
const Wrapper = styled.div` | ||
z-index: var(--z-index-hidden); | ||
` | ||
|
||
const Wrapper = styled.div` | ||
z-index: var(--z-index-base); | ||
` | ||
|
||
const Wrapper = styled.div` | ||
z-index: var(--z-index-float); | ||
` | ||
|
||
const Wrapper = styled.div` | ||
z-index: var(--z-index-tooltip); | ||
` | ||
|
||
const Wrapper = styled.div` | ||
z-index: var(--z-index-important); | ||
` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { testTransformFunction } from '../../utils/test.js' | ||
|
||
import interpolationTransform from './transform.js' | ||
|
||
describe('z-index interpolation transform', () => { | ||
it('should transform z-index interpolation to css variable in styled-component', () => { | ||
testTransformFunction(__dirname, 'z-index-interpolation-in-styled-component', interpolationTransform) | ||
}) | ||
|
||
it('should transform z-index enum to css variable', () => { | ||
testTransformFunction(__dirname, 'z-index-enum', interpolationTransform) | ||
}) | ||
|
||
it('should transform z-index enum to css variable when used as prop', () => { | ||
testTransformFunction(__dirname, 'z-index-enum-as-prop', interpolationTransform) | ||
}) | ||
|
||
it('should not transform z-index enum if it is not imported from bezier-react', () => { | ||
testTransformFunction(__dirname, 'z-index-enum-not-from-bezier-react', interpolationTransform) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* eslint-disable no-template-curly-in-string */ | ||
import { type SourceFile } from 'ts-morph' | ||
|
||
import { transformEnumMemberToStringLiteral } from '../../shared/enum.js' | ||
import { interpolationTransform } from '../../shared/interpolation.js' | ||
import { removeUnusedNamedImport } from '../../utils/import.js' | ||
|
||
const cssVariableByInterpolation = { | ||
'ZIndex.Hide': 'var(--z-index-hidden);', | ||
'ZIndex.Auto': 'var(--z-index-auto);', | ||
'ZIndex.Base': 'var(--z-index-base);', | ||
'ZIndex.Float': 'var(--z-index-float);', | ||
'ZIndex.Overlay': 'var(--z-index-overlay);', | ||
'ZIndex.Modal': 'var(--z-index-modal);', | ||
'ZIndex.Toast': 'var(--z-index-toast);', | ||
'ZIndex.Tooltip': 'var(--z-index-tooltip);', | ||
'ZIndex.Important': 'var(--z-index-important);', | ||
} | ||
|
||
const replaceZIndexInterpolation = (sourceFile: SourceFile) => { | ||
const oldSourceFileText = sourceFile.getText() | ||
|
||
interpolationTransform(sourceFile, cssVariableByInterpolation) | ||
transformEnumMemberToStringLiteral(sourceFile, { | ||
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. 그런데 다시보니 이 함수는 bezier-react 에서 import 할 때를 체크하고 있지는 않아서 사용처에서 동일한 이름의 enum 을 직접 선언해서 사용하면 사이드 이펙트를 발생시키네요. 홈페이지에서 그렇게 사용하는 부분이 있는 것으로 알고 있어서, bezier-react 에서 타켓 enum 을 import 하고 있는지 방어 로직을 추가하겠습니다 |
||
ZIndex: { | ||
Hide: 'var(--z-index-hidden)', | ||
Base: 'var(--z-index-base)', | ||
Float: 'var(--z-index-float)', | ||
Overlay: 'var(--z-index-overlay)', | ||
Modal: 'var(--z-index-modal)', | ||
Toast: 'var(--z-index-toast)', | ||
Tooltip: 'var(--z-index-tooltip)', | ||
Important: 'var(--z-index-important)', | ||
}, | ||
}) | ||
|
||
const isChanged = sourceFile.getText() !== oldSourceFileText | ||
if (isChanged) { | ||
removeUnusedNamedImport(sourceFile) | ||
} | ||
return isChanged | ||
} | ||
|
||
export default replaceZIndexInterpolation |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { | ||
type PropertyAccessExpression, | ||
SyntaxKind, | ||
} from 'ts-morph' | ||
|
||
export const renameEnumMember = (node: PropertyAccessExpression, to: string) => { | ||
const ancestor = node.getFirstAncestor() | ||
|
||
if (ancestor?.isKind(SyntaxKind.JsxExpression)) { | ||
ancestor.replaceWithText(`'${to}'`) | ||
} else { | ||
node.replaceWithText(`'${to}'`) | ||
} | ||
} |
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.
'styled-component 내에서 interpolation 으로 사용될 때' 에 대한 내용도 포함되면 좋을 거 같아요