Skip to content

Commit

Permalink
Add codemod for z-index interpolation (#1845)
Browse files Browse the repository at this point in the history
<!--
  How to write a good PR title:
- Follow [the Conventional Commits
specification](https://www.conventionalcommits.org/en/v1.0.0/).
  - Give as much context as necessary and as little as possible
  - Prefix it with [WIP] while it’s a work in progress
-->

## Self Checklist

- [x] I wrote a PR title in **English** and added an appropriate
**label** to the PR.
- [x] I wrote the commit message in **English** and to follow [**the
Conventional Commits
specification**](https://www.conventionalcommits.org/en/v1.0.0/).
- [x] I [added the
**changeset**](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md)
about the changes that needed to be released. (or didn't have to)
- [x] I wrote or updated **documentation** related to the changes. (or
didn't have to)
- [x] I wrote or updated **tests** related to the changes. (or didn't
have to)
- [x] I tested the changes in various browsers. (or didn't have to)
  - Windows: Chrome, Edge, (Optional) Firefox
  - macOS: Chrome, Edge, Safari, (Optional) Firefox

## Related Issue
<!-- Please link to issue if one exists -->

<!-- Fixes #0000 -->

- Fixes #1793

## Summary
<!-- Please brief explanation of the changes made -->

- #1844 머지 후 rebase 예정
(from
[252c358](252c358))
- z-index interpolation 을 위한 codemod 를 추가합니다. 변환을 고려해야 하는 상황은 다음과 같이
2가지가 있습니다.
1. styled-component 내에서 interpolation 으로 사용될 때
```tsx 
// As-is
export const Box = styled.div`
  z-index: ${ZIndex.Hide};
`

// To-be
export const Box = styled.div`
  z-index: var(--z-index-hidden);
`
```
2. .tsx 파일에서 객체로 사용될 때

```tsx
// As-is
const overlayStyle = {
  zIndex: ZIndex.Tooltip,
}

// To-be
const overlayStyle = {
  zIndex: 'var(--z-index-tooltip)',
}
```


## Details
<!-- Please elaborate description of the changes -->
- 2가지 모두 기존에 만들어 놓았던 codemod 를 사용해서 구현가능하기 때문에 특별히 어려운 점은 없었습니다.
`${ZIndex.Auto}`의 경우 지워지지 않고 남아있어도 상관없을 듯 하여 변환하게 두었습니다. 데스크 기준으로 사용처가
없기도 합니다.

### Breaking change? (Yes/No)
<!-- If Yes, please describe the impact and migration path for users -->

- No

## References
<!-- Please list any other resources or points the reviewer should be
aware of -->

- #1793
  • Loading branch information
yangwooseong authored Dec 22, 2023
1 parent f1c05b9 commit f33060f
Show file tree
Hide file tree
Showing 17 changed files with 287 additions and 59 deletions.
5 changes: 5 additions & 0 deletions .changeset/slimy-lobsters-sparkle.md
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
31 changes: 31 additions & 0 deletions packages/bezier-codemod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,37 @@ const Wrapper = styled.div`
`;
```

**`v2-z-index-interpolation-to-css-variable`**

Replace z-index interpolation to css variable
For example:

```tsx
import { ZIndex, styled } from "@channel.io/bezier-react";

const OVERLAY_POSITION = {
zIndex: ZIndex.Modal,
};

const Wrapper = styled.div`
z-index: ${ZIndex.Hide};
`;
```

Transforms into:

```tsx
import { styled } from "@channel.io/bezier-react";

const OVERLAY_POSITION = {
zIndex: "var(--z-index-modal)",
};

const Wrapper = styled.div`
z-index: var(--z-index-hidden);
`;
```

### Styled from @channel.io/bezier-react to styled-components

**`v2-styled-to-styled-components`**
Expand Down
5 changes: 4 additions & 1 deletion packages/bezier-codemod/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import styledToStyledComponents from './transforms/v2-import-styled-from-styled-
import inputInterpolationToCssVariable from './transforms/v2-input-interpolation-to-css-variable/transform.js'
import removeAlphaFromAlphaStack from './transforms/v2-remove-alpha-from-alpha-stack/transform.js'
import typographyInterpolationToCssVariable from './transforms/v2-typography-interpolation-to-css-variable/transform.js'
import zIndexInterpolationToCssVariable from './transforms/v2-z-index-interpolation-to-css-variable/transform.js'

enum Step {
SelectTransformer,
Expand All @@ -52,7 +53,8 @@ enum Option {
V2InputInterpolationToCssVariable = 'v2-input-interpolation-to-css-variable',
V2TypographyInterpolationToCssVariable = 'v2-typography-interpolation-to-css-variable',
V2StyledToStyledComponents = 'v2-styled-to-styled-components',
V2RemoveAlphaFromAlphaStack = 'remove-alpha-from-alpha-stack',
V2RemoveAlphaFromAlphaStack = 'v2-remove-alpha-from-alpha-stack',
V2ZIndexInterpolationToCssVariable = 'v2-z-index-interpolation-to-css-variable',
Exit = 'Exit',
}

Expand All @@ -72,6 +74,7 @@ const transformMap = {
[Option.V2TypographyInterpolationToCssVariable]: typographyInterpolationToCssVariable,
[Option.V2StyledToStyledComponents]: styledToStyledComponents,
[Option.V2RemoveAlphaFromAlphaStack]: removeAlphaFromAlphaStack,
[Option.V2ZIndexInterpolationToCssVariable]: zIndexInterpolationToCssVariable,
}

const options = (Object.keys(transformMap) as Option[]).map((transformName) => ({
Expand Down
40 changes: 40 additions & 0 deletions packages/bezier-codemod/src/shared/enum.ts
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')) {
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 {
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, {
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
14 changes: 14 additions & 0 deletions packages/bezier-codemod/src/utils/enum.ts
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}'`)
}
}
Loading

0 comments on commit f33060f

Please sign in to comment.