-
Notifications
You must be signed in to change notification settings - Fork 221
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
Showing
106 changed files
with
2,195 additions
and
1,669 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
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
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,157 @@ | ||
import { | ||
API, | ||
FileInfo, | ||
Options, | ||
StringLiteral, | ||
JSXIdentifier, | ||
JSXAttribute, | ||
ASTPath, | ||
JSXElement, | ||
} from 'jscodeshift'; | ||
import {getImportRenameMap} from './utils/getImportRenameMap'; | ||
|
||
const updateJSXTag = (nodePath: ASTPath<JSXElement>, newTag: string) => { | ||
const {name: componentName} = nodePath.value.openingElement.name as JSXIdentifier; | ||
if (componentName === 'IconButton') { | ||
(nodePath.value.openingElement.name as JSXIdentifier).name = newTag; | ||
if (nodePath.value.closingElement) { | ||
(nodePath.value.closingElement.name as JSXIdentifier).name = newTag; | ||
} | ||
} | ||
}; | ||
|
||
export default function transformer(file: FileInfo, api: API, options: Options) { | ||
const j = api.jscodeshift; | ||
const root = j(file.source); | ||
const requiredImportSpecifiers: string[] = []; | ||
|
||
/** | ||
* 1. Find button imports | ||
*/ | ||
const {containsCanvasImports, importMap, styledMap} = getImportRenameMap( | ||
j, | ||
root, | ||
'@workday/canvas-kit-react/button' | ||
); | ||
if (!containsCanvasImports) { | ||
return file.source; | ||
} | ||
|
||
let buttonType = 'TertiaryButton'; //default button if no variant is specified | ||
|
||
// Button Mapping | ||
// circle -> tertiary | ||
// circle-filled -> secondary | ||
// inverse -> tertiary inverse | ||
// inverse-filled -> secondary inverse | ||
// plain/square -> not supported | ||
// square-filled -> not supported | ||
|
||
/** | ||
* 2. Find `IconButton` | ||
* - If it has no variant, it means it's "circle" which is the default | ||
* - Swap to `TertiaryButton` | ||
* - Add `TertiaryButton` import if it doesn't exist. | ||
* - If not | ||
* - check the variant of the IconButton | ||
* - Swap it out for the appropriate mapping | ||
*/ | ||
root | ||
.find( | ||
j.JSXElement, | ||
(value: JSXElement) => | ||
value.openingElement.name.type === 'JSXIdentifier' && | ||
(value.openingElement.name.name === importMap.IconButton || | ||
value.openingElement.name.name === styledMap.IconButton) | ||
) | ||
.forEach(nodePath => { | ||
const attrs = nodePath.value.openingElement.attributes; | ||
|
||
const variantProp = attrs?.find( | ||
attr => attr.type === 'JSXAttribute' && attr.name.name === 'variant' | ||
); | ||
|
||
// Default IconButton variant is `circle` | ||
if (variantProp) { | ||
const variantPropValue = ((variantProp as JSXAttribute).value as StringLiteral)?.value; | ||
buttonType = /filled/gi.test(variantPropValue) ? 'SecondaryButton' : 'TertiaryButton'; | ||
|
||
if (!variantPropValue.includes('inverse')) { | ||
nodePath.value.openingElement.attributes?.splice(attrs?.indexOf(variantProp)!, 1); | ||
} | ||
} | ||
|
||
updateJSXTag(nodePath, buttonType); | ||
requiredImportSpecifiers.push(buttonType); | ||
}); | ||
|
||
// Find all instances of IconButton within a style function | ||
// const StyledIconButton = styled(IconButton) gets renamed to | ||
// const StyledIconButton = styled(CorrectButtonMapping) | ||
root.find(j.VariableDeclarator).forEach(nodePath => { | ||
if ( | ||
nodePath.value.init?.type === 'CallExpression' && | ||
nodePath.value.init.callee.type === 'CallExpression' && | ||
nodePath.value.init.callee.arguments[0].type === 'Identifier' | ||
) { | ||
nodePath.value.init.callee.arguments[0].name = buttonType; | ||
requiredImportSpecifiers.push(buttonType); | ||
} | ||
}); | ||
|
||
/** | ||
* Remove old imports: `IconButton` | ||
* Add new required imports | ||
*/ | ||
const buttonImports = root.find(j.ImportDeclaration, { | ||
source: {value: (value: string) => value.includes('@workday/canvas-kit-react')}, | ||
}); | ||
|
||
if (!buttonImports.length) { | ||
// Add new specifiers to a new import | ||
const allImports = root.find(j.ImportDeclaration); | ||
|
||
const lastImport = allImports.at(allImports.length); | ||
if (lastImport) { | ||
lastImport.insertAfter( | ||
j.importDeclaration( | ||
requiredImportSpecifiers.map(specifier => j.importSpecifier(j.identifier(specifier))), | ||
j.stringLiteral('@workday/canvas-kit-react/button') | ||
) | ||
); | ||
} | ||
} else { | ||
buttonImports.forEach(({node}) => { | ||
const specifiersToRemove = ['IconButton']; | ||
|
||
// Remove old specifiers | ||
if ( | ||
typeof node.source.value === 'string' && | ||
node.source.value.includes('@workday/canvas-kit-react') | ||
) { | ||
node.specifiers?.forEach(specifier => { | ||
if ( | ||
specifier.type === 'ImportSpecifier' && | ||
specifiersToRemove.includes(specifier.imported.name) | ||
) { | ||
// delete specifier | ||
node.specifiers?.splice(node.specifiers?.indexOf(specifier)!, 1); | ||
} | ||
}); | ||
} | ||
|
||
// Add new specifiers to existing import | ||
requiredImportSpecifiers.forEach(specifier => { | ||
if ( | ||
!node.specifiers?.find( | ||
existing => existing.type === 'ImportSpecifier' && existing.imported.name === specifier | ||
) | ||
) { | ||
node.specifiers?.push(j.importSpecifier(j.identifier(specifier))); | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
return root.toSource(); | ||
} |
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,52 @@ | ||
import {API, FileInfo, Options, JSXElement, JSXAttribute, JSXSpreadAttribute} from 'jscodeshift'; | ||
|
||
import {getImportRenameMap} from './utils/getImportRenameMap'; | ||
|
||
export default function transformer(file: FileInfo, api: API, options: Options) { | ||
const j = api.jscodeshift; | ||
|
||
const root = j(file.source); | ||
|
||
const {containsCanvasImports, importMap, styledMap} = getImportRenameMap( | ||
j, | ||
root, | ||
'@workday/canvas-kit-react/button' | ||
); | ||
|
||
if (!containsCanvasImports) { | ||
return file.source; | ||
} | ||
|
||
root | ||
.find( | ||
j.JSXElement, | ||
(value: JSXElement) => | ||
value.openingElement.name.type === 'JSXIdentifier' && | ||
(value.openingElement.name.name === | ||
(importMap.PrimaryButton || importMap.SecondaryButton) || | ||
value.openingElement.name.name === (styledMap.PrimaryButton || styledMap.SecondaryButton)) | ||
) | ||
.forEach(nodePath => { | ||
const findAttribute = (name: string) => (item: JSXAttribute | JSXSpreadAttribute) => | ||
item.type === 'JSXAttribute' && | ||
item.name.type === 'JSXIdentifier' && | ||
item.name.name === name; | ||
|
||
const attributes = nodePath.value.openingElement.attributes; | ||
if (attributes) { | ||
const iconPosition = attributes.find(findAttribute('iconPosition')) as | ||
| JSXAttribute | ||
| undefined; | ||
|
||
if (iconPosition?.value?.type === 'StringLiteral') { | ||
if (iconPosition.value.value === 'left') { | ||
iconPosition.value.value = 'start'; | ||
} else { | ||
iconPosition.value.value = 'end'; | ||
} | ||
} | ||
} | ||
}); | ||
|
||
return root.toSource(); | ||
} |
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,82 @@ | ||
import {API, FileInfo, Options, JSXIdentifier} from 'jscodeshift'; | ||
import {getImportRenameMap} from './utils/getImportRenameMap'; | ||
|
||
const inputsMap: { | ||
[packageName: string]: string[]; | ||
} = { | ||
'@workday/canvas-kit-react/icon': [ | ||
'AccentIcon', | ||
'AppletIcon', | ||
'SystemIcon', | ||
'SystemIconCircle', | ||
'Graphic', | ||
'Svg', | ||
], | ||
}; | ||
|
||
export default function transformer(file: FileInfo, api: API, options: Options) { | ||
const j = api.jscodeshift; | ||
|
||
const root = j(file.source); | ||
|
||
// defaultImports should be an array of local names that were imported using | ||
// a default import from any of the modules listed in inputsMap. For example, | ||
// given... | ||
// | ||
// ``` | ||
// import Checkbox from '@workday/canvas-kit-react/checkbox'; | ||
// import CanvasTextArea from '@workday/canvas-kit-react/text-area'; | ||
// import {TextInput} from '@workday/canvas-kit-react/text-input'; | ||
// import StatusIndicator from '@workday/canvas-kit-react/status-indicator'; | ||
// ``` | ||
// | ||
// ... defaultImports should be set to `['Checkbox', 'CanvasTextArea']` | ||
const defaultImports: string[] = []; | ||
root.find(j.ImportDefaultSpecifier).forEach(nodePath => { | ||
const packageName = nodePath.parent.node.source.value; | ||
const localName = nodePath.value.local?.name; | ||
if (packageName in inputsMap && localName) { | ||
defaultImports.push(localName); | ||
} | ||
}); | ||
|
||
let runningSource = file.source; | ||
for (const inputPackageName of Object.keys(inputsMap)) { | ||
const currentRoot = j(runningSource); | ||
|
||
const {containsCanvasImports, importMap, styledMap} = getImportRenameMap( | ||
j, | ||
currentRoot, | ||
inputPackageName | ||
); | ||
|
||
if (containsCanvasImports) { | ||
const inputNames = inputsMap[inputPackageName]; | ||
currentRoot | ||
.find(j.JSXIdentifier, (value: JSXIdentifier) => value.name === 'iconRef') | ||
.replaceWith(nodePath => { | ||
const elementName = nodePath.parent?.parent?.value?.name?.name; | ||
// Rewrite inputRef to ref if any of the following are true: | ||
// (a) elementName was the name used in a default import of the current | ||
// input being processed (e.g., `import TextInput ...`) | ||
// (b) elementName was the name used in a named import of the current | ||
// input being processed (e.g., `import {TextInput} ...` or | ||
// `import {TextInput as CanvasTextInput} ...`) | ||
// (c) elementName was created by calling `styled` on the current input | ||
// being processed (e.g., `const StyledTextInput = styled(TextInput)`) | ||
if ( | ||
defaultImports.includes(elementName) || | ||
inputNames.map(n => importMap[n]).includes(elementName) || | ||
inputNames.map(n => styledMap[n]).includes(elementName) | ||
) { | ||
return j.jsxIdentifier('ref'); | ||
} | ||
return nodePath.value; | ||
}); | ||
|
||
runningSource = currentRoot.toSource(); | ||
} | ||
} | ||
|
||
return runningSource; | ||
} |
Oops, something went wrong.