-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Martí Malek
committed
Aug 29, 2023
1 parent
6b46750
commit e7f73db
Showing
11 changed files
with
282 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
src/codemods/__testfixtures__/inverted-to-flip-theme/explicit-boolean-false.input.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Button } from '@freenow/wave'; | ||
|
||
interface Props { | ||
label: string; | ||
disabled?: boolean; | ||
onClick: () => void; | ||
} | ||
|
||
export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => ( | ||
<Button inverted={false} size="small" onClick={onClick} disabled={disabled}> | ||
{label} | ||
</Button> | ||
); |
13 changes: 13 additions & 0 deletions
13
src/codemods/__testfixtures__/inverted-to-flip-theme/explicit-boolean-false.output.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Button } from '@freenow/wave'; | ||
|
||
interface Props { | ||
label: string; | ||
disabled?: boolean; | ||
onClick: () => void; | ||
} | ||
|
||
export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => ( | ||
<Button size="small" onClick={onClick} disabled={disabled}> | ||
{label} | ||
</Button> | ||
); |
13 changes: 13 additions & 0 deletions
13
src/codemods/__testfixtures__/inverted-to-flip-theme/explicit-boolean-true.input.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Button } from '@freenow/wave'; | ||
|
||
interface Props { | ||
label: string; | ||
disabled?: boolean; | ||
onClick: () => void; | ||
} | ||
|
||
export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => ( | ||
<Button inverted={true} size="small" onClick={onClick} disabled={disabled}> | ||
{label} | ||
</Button> | ||
); |
17 changes: 17 additions & 0 deletions
17
src/codemods/__testfixtures__/inverted-to-flip-theme/explicit-boolean-true.output.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Button, FlipTheme } from '@freenow/wave'; | ||
|
||
interface Props { | ||
label: string; | ||
disabled?: boolean; | ||
onClick: () => void; | ||
} | ||
|
||
export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => ( | ||
<FlipTheme> | ||
( | ||
<Button size="small" onClick={onClick} disabled={disabled}> | ||
{label} | ||
</Button> | ||
) | ||
</FlipTheme> | ||
); |
13 changes: 13 additions & 0 deletions
13
src/codemods/__testfixtures__/inverted-to-flip-theme/local-rename.input.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Button } from '@freenow/wave'; | ||
|
||
interface Props { | ||
label: string; | ||
disabled?: boolean; | ||
onClick: () => void; | ||
} | ||
|
||
export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => ( | ||
<Button inverted size="small" onClick={onClick} disabled={disabled}> | ||
{label} | ||
</Button> | ||
); |
17 changes: 17 additions & 0 deletions
17
src/codemods/__testfixtures__/inverted-to-flip-theme/local-rename.output.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Button, FlipTheme } from '@freenow/wave'; | ||
|
||
interface Props { | ||
label: string; | ||
disabled?: boolean; | ||
onClick: () => void; | ||
} | ||
|
||
export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => ( | ||
<FlipTheme> | ||
( | ||
<Button size="small" onClick={onClick} disabled={disabled}> | ||
{label} | ||
</Button> | ||
) | ||
</FlipTheme> | ||
); |
25 changes: 25 additions & 0 deletions
25
src/codemods/__testfixtures__/inverted-to-flip-theme/styled-rename.input.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { Button, Colors, Spaces } from '@freenow/wave'; | ||
import styled from 'styled-components'; | ||
|
||
interface Props { | ||
label: string; | ||
disabled?: boolean; | ||
onClick: () => void; | ||
} | ||
|
||
const Action = styled(Button)` | ||
justify-content: flex-start; | ||
color: ${Colors.AUTHENTIC_BLUE_900}; | ||
font-weight: normal; | ||
line-height: 1.43; | ||
border-radius: 0; | ||
padding: ${Spaces[2]}; | ||
`; | ||
|
||
export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => ( | ||
<Action inverted size="small" onClick={onClick} disabled={disabled}> | ||
{label} | ||
</Action> | ||
); |
29 changes: 29 additions & 0 deletions
29
src/codemods/__testfixtures__/inverted-to-flip-theme/styled-rename.output.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { Button, Colors, Spaces, FlipTheme } from '@freenow/wave'; | ||
import styled from 'styled-components'; | ||
|
||
interface Props { | ||
label: string; | ||
disabled?: boolean; | ||
onClick: () => void; | ||
} | ||
|
||
const Action = styled(Button)` | ||
justify-content: flex-start; | ||
color: ${Colors.AUTHENTIC_BLUE_900}; | ||
font-weight: normal; | ||
line-height: 1.43; | ||
border-radius: 0; | ||
padding: ${Spaces[2]}; | ||
`; | ||
|
||
export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => ( | ||
<FlipTheme> | ||
( | ||
<Action size="small" onClick={onClick} disabled={disabled}> | ||
{label} | ||
</Action> | ||
) | ||
</FlipTheme> | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
jest.autoMockOff(); | ||
const { defineTest } = require('jscodeshift/dist/testUtils'); | ||
|
||
const tests = ['local-rename', 'explicit-boolean-false', 'explicit-boolean-true', 'styled-rename']; | ||
|
||
describe('inverted-to-flip-theme', () => { | ||
tests.forEach(test => | ||
defineTest(__dirname, 'inverted-to-flip-theme', { quote: 'single' }, `inverted-to-flip-theme/${test}`, { | ||
parser: 'tsx' | ||
}) | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import { API, FileInfo, ImportDeclaration, JSXAttribute, VariableDeclarator } from 'jscodeshift'; | ||
import { Options } from 'recast'; | ||
|
||
const ComponentNamesWithInvertedProp = [ | ||
'Input', | ||
'Password', | ||
'Textarea', | ||
'Button', | ||
'Select', | ||
'SelectList', | ||
'PhoneInput', | ||
'Datepicker', | ||
'Tooltip' | ||
]; | ||
|
||
const WRAPPER_COMPONENT_NAME = 'FlipTheme'; | ||
|
||
export default (file: FileInfo, api: API, options: Options) => { | ||
const j = api.jscodeshift; | ||
const ast = j(file.source); | ||
const printOptions = options ?? { quote: 'single' }; | ||
|
||
const localComponentNames: string[] = []; | ||
|
||
// Find @freenow/wave imports | ||
const waveImports = ast.find(j.ImportDeclaration, { | ||
source: { | ||
value: '@freenow/wave' | ||
} | ||
}); | ||
|
||
// Find components named imports in @freenow/wave imports that potentially have an inverted prop | ||
const componentImports = waveImports | ||
.find(j.ImportSpecifier) | ||
.filter(path => ComponentNamesWithInvertedProp.includes(path.node.imported.name)); | ||
|
||
// Get the local icons import names | ||
componentImports.forEach(spec => { | ||
if (spec.node.local?.name) localComponentNames.push(spec.node.local.name); | ||
}); | ||
|
||
// Find declarations of styled components that use a component which has the inverted prop | ||
const styledExpressions = ast.find(j.TaggedTemplateExpression, { | ||
tag: { | ||
arguments: ([argument]) => | ||
argument?.type === 'Identifier' && ComponentNamesWithInvertedProp.includes(argument.name) | ||
} | ||
}); | ||
|
||
styledExpressions.forEach(ex => { | ||
if (ex.parent?.node && ex.parent.node.type === 'VariableDeclarator') { | ||
const styledDeclaration: VariableDeclarator = ex.parent.node; | ||
// Mark the name of the declared styled component as a local component which can have the inverted prop | ||
if (styledDeclaration.id.type === 'Identifier') localComponentNames.push(styledDeclaration.id.name); | ||
} | ||
}); | ||
|
||
// Find usages of the components | ||
const jsxComponents = ast.find(j.JSXElement, { | ||
openingElement: { | ||
name: { | ||
name: name => localComponentNames.includes(name) | ||
} | ||
} | ||
}); | ||
|
||
let shouldAddWrapperImport = false; | ||
|
||
// Iterate over jsx components | ||
jsxComponents.forEach(el => { | ||
// Find inverted props | ||
const invertedProps = j(el).find(j.JSXAttribute, { name: name => name.name === 'inverted' }); | ||
|
||
if (invertedProps.size() !== 1) return; | ||
|
||
let shouldWrap = false; | ||
const invertedProp: JSXAttribute = invertedProps.get(0).node; | ||
|
||
// console.log(invertedProp) | ||
// In case the prop has a value (`inverted={true}` or `inverted={false}`) set shouldWrap based on the value | ||
if ( | ||
invertedProp.value && | ||
invertedProp.value.type === 'JSXExpressionContainer' && | ||
invertedProp.value.expression.type === 'BooleanLiteral' | ||
) { | ||
shouldWrap = invertedProp.value.expression.value; | ||
} else { | ||
// In case the prop has an implicit `true` value set shouldWrap to `true` | ||
shouldWrap = true; | ||
} | ||
|
||
// Remove the inverted prop | ||
invertedProps.at(0).remove(); | ||
|
||
// In case `inverted` was `true` wrap the element with the wrapper component | ||
if (shouldWrap) { | ||
shouldAddWrapperImport = true; | ||
|
||
// Build wrapper component with the current element as it's children | ||
const WrapperComponent = j.jsxElement( | ||
j.jsxOpeningElement(j.jsxIdentifier(WRAPPER_COMPONENT_NAME)), | ||
j.jsxClosingElement(j.jsxIdentifier(WRAPPER_COMPONENT_NAME)), | ||
[j.jsxText('\n ', '\n '), el.node, j.jsxText('\n ', '\n ')] | ||
); | ||
|
||
// Replace element with wrapper | ||
el.replace(WrapperComponent); | ||
} | ||
}); | ||
|
||
// Add wrapper import | ||
if (shouldAddWrapperImport && waveImports.size() > 0) { | ||
const importDeclaration: ImportDeclaration = waveImports.get(0).node; | ||
importDeclaration.specifiers.push(j.importSpecifier(j.identifier(WRAPPER_COMPONENT_NAME))); | ||
} | ||
|
||
return ast.toSource(printOptions); | ||
}; |