Skip to content
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 to transform foundation to css variable #1781

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4663732
feat(bezier-codemod): add transform function for foundation theme
yangwooseong Dec 8, 2023
6a4a6a7
feat(bezier-codemod): add transform function for foundation border
yangwooseong Dec 8, 2023
a44297d
feat(bezier-codemod): add transform function for foundation elevation
yangwooseong Dec 8, 2023
01480e2
feat(bezier-codemod): add transform function for foundation rounding
yangwooseong Dec 8, 2023
90e3010
feat(bezier-codemod): add transform function for foundation transition
yangwooseong Dec 8, 2023
54a1c0a
feat(bezier-codemod): add test code
yangwooseong Dec 8, 2023
6ebeb4d
feat(bezier-codemod): add transform to map in App.tsx
yangwooseong Dec 8, 2023
dd1efeb
refactor(bezier-codemod): separate arrow function getter logic
yangwooseong Dec 8, 2023
19ee73f
refactor(bezier-codemod): separate test util function
yangwooseong Dec 8, 2023
b3a8db3
feat(bezier-codemod): add combined transform function for foundation
yangwooseong Dec 8, 2023
7a89ded
feat(bezier-codemod): add changeset
yangwooseong Dec 8, 2023
2fb9bc6
feat(bezier-codemod): add README.md
yangwooseong Dec 8, 2023
693f595
fix(bezier-codemod): resolve test failed
yangwooseong Dec 8, 2023
4dbe61b
feat(bezier-codemod): separate test code according to whether transfo…
yangwooseong Dec 8, 2023
ac63e74
feat(bezier-codemod): add bg-color to elevation transform
yangwooseong Dec 11, 2023
79a3bfa
refactor(bezier-codemod): use shorthand css variable for transition t…
yangwooseong Dec 11, 2023
d406693
feat(beizer-codemod): modify README.md
yangwooseong Dec 11, 2023
2fedd1e
fix(bezier-codemod): typo fix
yangwooseong Dec 12, 2023
2a10dcf
fix(bezier-codemod): add overflow: hidden to rounding transform
yangwooseong Dec 12, 2023
d112f31
refactor(bezier-codemod): adopt border-style property so that transfo…
yangwooseong Dec 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/tough-lions-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@channel.io/bezier-codemod": minor
---

Add transform functions to help to migrate foundation to css variable generated by `@channel.io/bezier-tokens` package.
They cover theme, transition, elevation, rounding, and border of foundation.
90 changes: 73 additions & 17 deletions packages/bezier-codemod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,31 @@ Update the import syntax for the icon source moved from `@channel.io/bezier-reac
For example:

```tsx
import React from 'react'
import { AllIcon, Button, CheckIcon as CheckIconSource, Icon, type IconName, IconSize } from '@channel.io/bezier-react'

import Foo from './foo'
import React from "react";
import {
AllIcon,
Button,
CheckIcon as CheckIconSource,
Icon,
type IconName,
IconSize,
} from "@channel.io/bezier-react";

import Foo from "./foo";
```

Transforms into:

```tsx
import React from 'react'
import { AllIcon, CheckIcon as CheckIconSource, type IconName } from '@channel.io/bezier-icons'
import { Button, Icon, IconSize } from '@channel.io/bezier-react'

import Foo from './foo'
import React from "react";
import {
AllIcon,
CheckIcon as CheckIconSource,
type IconName,
} from "@channel.io/bezier-icons";
import { Button, Icon, IconSize } from "@channel.io/bezier-react";

import Foo from "./foo";
```

### Enum Member to String Literal
Expand All @@ -46,29 +57,74 @@ Replace deprecated enum usage to string literal.
For example:

```tsx
import { ProgressBar, ProgressBarSize, ProgressBarVariant } from '@channel.io/bezier-react'
import {
ProgressBar,
ProgressBarSize,
ProgressBarVariant,
} from "@channel.io/bezier-react";

export default () => (
<ProgressBar
width='100%'
width="100%"
size={ProgressBarSize.M}
variant={ProgressBarVariant.GreenAlt}
value={uploadProgressPercentage / 100}
/>
)
);
```

Transforms into:

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

export default () => (
<ProgressBar
width='100%'
size='m'
variant='green-alt'
width="100%"
size="m"
variant="green-alt"
value={uploadProgressPercentage / 100}
/>
)
);
```

### Foundation to CSS Variable

**`foundation-to-css-variable`**

Replace foundation to css variable
You can also use individual transform function such as `foundation-to-css-theme`, `foundation-to-css-rounding`, and so on.

For example:

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

const Wrapper = styled.div`
color: ${({ foundation }) => foundation?.theme?.["txt-black-dark"]};
${({ foundation }) => foundation?.rounding.round12};
${({ foundation }) =>
foundation?.border?.getBorder(0.5, foundation.theme["bdr-black-light"], {
top: false,
right: false,
left: false,
})};
${({ foundation }) => foundation?.elevation?.ev1()};
${({ foundation }) => foundation?.transition?.getTransitionsCSS("color")};
`;
```

Transforms into:

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

const Wrapper = styled.div`
color: var(--txt-black-dark);
border-radius: var(--radius-12);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존 radius도 overflow:hidden 스타일을 가지고 있어요. 큰 문제는 없을 거 같지만, 안전한 마이그레이션을 위해서 추가해주면 좋을 거 같습니다.

border-bottom: 0.5px solid var(--bdr-black-light);
background-color: var(--bg-white-low);
box-shadow: var(--ev-1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존 elevation에 background-color도 포함되어있어서, bgColor, shadow 2개 속성을 포함하도록 변환해줘야할 거 같습니다!

transition: color var(--transition-s);
`;
```
18 changes: 18 additions & 0 deletions packages/bezier-codemod/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ import {

import project from './project.js'
import enumMemberToStringLiteral from './transforms/enum-member-to-string-literal.js'
import foundationToCssVariableBorder from './transforms/foundation-to-css-variable-border.js'
import foundationToCssVariableElevation from './transforms/foundation-to-css-variable-elevation.js'
import foundationToCssVariableRounding from './transforms/foundation-to-css-variable-rounding.js'
import foundationToCssVariableTheme from './transforms/foundation-to-css-variable-theme.js'
import foundationToCssVariableTransition from './transforms/foundation-to-css-variable-transition.js'
import foundationToCssVariable from './transforms/foundation-to-css-variable.js'
import iconNameToBezierIcon from './transforms/icon-name-to-bezier-icon.js'
import iconsToBezierIcons from './transforms/icons-to-bezier-icons.js'

Expand All @@ -33,6 +39,12 @@ enum Option {
IconsToBezierIcons = 'icons-to-bezier-icons',
IconNameToBezierIcon = 'icon-name-to-bezier-icon',
EnumMemberToStringLiteral = 'enum-member-to-string-literal',
FoundationToCssVariableTheme = 'foundation-to-css-variable-theme',
FoundationToCssVariableBorder = 'foundation-to-css-variable-border',
FoundationToCssVariableElevation = 'foundation-to-css-variable-elevation',
FoundationToCssVariableRounding = 'foundation-to-css-variable-rounding',
FoundationToCssVariableTransition = 'foundation-to-css-variable-transition',
FoundationToCssVariable = 'foundation-to-css-variable',
Exit = 'Exit',
}

Expand All @@ -42,6 +54,12 @@ const transformMap = {
[Option.IconsToBezierIcons]: iconsToBezierIcons,
[Option.IconNameToBezierIcon]: iconNameToBezierIcon,
[Option.EnumMemberToStringLiteral]: enumMemberToStringLiteral,
[Option.FoundationToCssVariableTheme]: foundationToCssVariableTheme,
[Option.FoundationToCssVariableBorder]: foundationToCssVariableBorder,
[Option.FoundationToCssVariableElevation]: foundationToCssVariableElevation,
[Option.FoundationToCssVariableRounding]: foundationToCssVariableRounding,
[Option.FoundationToCssVariableTransition]: foundationToCssVariableTransition,
[Option.FoundationToCssVariable]: foundationToCssVariable,
}

const options = (Object.keys(transformMap) as Option[]).map((transformName) => ({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/* eslint-disable no-template-curly-in-string */
import {
type CallExpression,
Node,
type SourceFile,
SyntaxKind,
type ts,
} from 'ts-morph'

import { getArrowFunctionsWithOneArgument } from '../utils/function.js'

const getBorderStyle = (borderCallExpression: CallExpression<ts.CallExpression>) => {
const width = borderCallExpression
.getArguments()
.find(Node.isNumericLiteral)
?.getText()

const color = borderCallExpression
.getArguments()
.find(Node.isElementAccessExpression)
?.getArgumentExpression()
?.getText()
.slice(1, -1)

if (!color || !width) { return null }

const borderDirection = borderCallExpression
.getArguments()
.find(Node.isObjectLiteralExpression)

const hasBorders = ['top', 'right', 'bottom', 'left']
.map((value) => borderDirection?.getProperty(value)?.getText() !== `${value}: false`)

let borderStyle = ''

if (hasBorders.every((v => v))) {
borderStyle = `border: ${width}px solid var(--${color});\n`
} else {
borderStyle += `border-color: var(--${color});\n`
borderStyle += ` border-style: ${hasBorders
.map((hasBorder) => (hasBorder ? 'solid' : 'none'))
.join(' ')
};\n`
borderStyle += ` border-width: ${width}px;\n`
}

return borderStyle
}

const hasBorderFoundation = (node: Node) => node.getText().includes('getBorder')

const replaceBorder = (sourceFile: SourceFile) => {
const oldSourceFileText = sourceFile.getText()
sourceFile.forEachDescendant((node) => {
if (Node.isTemplateExpression(node)) {
const borderCallExpression = node
.getDescendantsOfKind(SyntaxKind.CallExpression)
.find(hasBorderFoundation)

if (!borderCallExpression) { return }

const borderArrowFunctions = getArrowFunctionsWithOneArgument(
node, hasBorderFoundation,
)
const borderStyle = getBorderStyle(borderCallExpression)

if (!borderStyle) { return }

borderArrowFunctions
.map((v) => v.getText())
.forEach((text) => {
node.replaceWithText(
node.getText()
.replace(`\${${text}};\n` ?? '', borderStyle)
.replace(`\${${text}}\n` ?? '', borderStyle),
)
})
}
})
return sourceFile.getText() !== oldSourceFileText
}

export default replaceBorder
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* eslint-disable no-template-curly-in-string */
import {
Node,
type SourceFile,
} from 'ts-morph'

import { getArrowFunctionsWithOneArgument } from '../utils/function.js'

const getElevationNum = (text: string) => text.match(/ev(\d+)/)?.[1]

const bgColorByElevationNum = (ev?: string) => {
switch (ev) {
case '1':
case '2':
return 'bg-white-low'
case '3':
case '4':
case '5':
case '6':
default:
return 'bg-white-high'
}
}

const getCssVarCode = (arrowFn: string) => {
const ev = getElevationNum(arrowFn)
let css = ''
css += `background-color: var(--${bgColorByElevationNum(ev)});\n`
css += ` box-shadow: var(--ev-${getElevationNum(arrowFn)});`

return css
}

const isElevationTheme = (node: Node) =>
node.getText().includes('foundation?.elevation') || node.getText().includes('foundation.elevation')

const replaceElevation = (sourceFile: SourceFile) => {
const oldSourceFileText = sourceFile.getText()
sourceFile.forEachDescendant((node) => {
if (Node.isTemplateExpression(node)) {
const elevationArrowFunctions = getArrowFunctionsWithOneArgument(
node, isElevationTheme,
)
elevationArrowFunctions
.map((v => v.getText()))
.forEach(text => {
node.replaceWithText(
node.getText()
.replace(`\${${text}}`, getCssVarCode(text))
.replaceAll(';;', ';'),
)
})
}
})
return sourceFile.getText() !== oldSourceFileText
}

export default replaceElevation
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* eslint-disable no-template-curly-in-string */
import {
Node,
type SourceFile,
} from 'ts-morph'

import { getArrowFunctionsWithOneArgument } from '../utils/function.js'

const getRound = (text: string) => text.match(/round(\d+)/)?.[1]

const isRoundingTheme = (node: Node) => node.getText().includes('foundation?.rounding')

const getRoundStyle = (text: string) => {
let roundStyle = ''
roundStyle += 'overflow: hidden;\n'
roundStyle += ` border-radius: var(--radius-${getRound(text)});`
return roundStyle
}

const replaceRound = (sourceFile: SourceFile) => {
const oldSourceFileText = sourceFile.getText()
sourceFile.forEachDescendant((node) => {
if (Node.isTemplateExpression(node)) {
const roundArrowFunctions = getArrowFunctionsWithOneArgument(
node, isRoundingTheme,
)
roundArrowFunctions
.map((v) => v.getText())
.forEach(text => {
node.replaceWithText(
node.getText()
.replace(`\${${text}}`, getRoundStyle(text))
.replaceAll(';;', ';'),
)
})
}
})
return oldSourceFileText !== sourceFile.getText()
}

export default replaceRound
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* eslint-disable no-template-curly-in-string */
import {
Node,
type SourceFile,
} from 'ts-morph'

import { getArrowFunctionsWithOneArgument } from '../utils/function.js'

const getColor = (text: string) => text.match(/\['([a-z-]+)'\]/)?.[1] ?? ''

const isFoundationTheme = (node: Node) => node.getText().includes('foundation?.theme') && !node.getText().includes('getBorder')

const replaceTheme = (sourceFile: SourceFile) => {
const oldSourceFileText = sourceFile.getText()
sourceFile.forEachDescendant((node) => {
if (Node.isTemplateExpression(node)) {
const themeArrowFunctions = getArrowFunctionsWithOneArgument(node, isFoundationTheme)

themeArrowFunctions
.map(v => v.getText())
.forEach(text => {
const color = getColor(text)

if (color) {
node.replaceWithText(
node.getText()
.replace(`\${${text}}`, `var(--${getColor(text)})`),
)
}
})
}
})
return sourceFile.getText() !== oldSourceFileText
}

export default replaceTheme
Loading