-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement codemod ibm-products-update-http-errors (#17960)
* feat(ibm-update-http-errors): implement ibm-update-http-errors codemod * feat(upgrade): include ibm-products-update-http-errors in migrate cli * test(ibm-products-update-http-errors): include sample in fixtures folder --------- Co-authored-by: Nikhil Tomar <[email protected]> Co-authored-by: Riddhi Bansal <[email protected]> Co-authored-by: Guilherme Datilio Ribeiro <[email protected]>
- Loading branch information
1 parent
4e89908
commit 22f19fc
Showing
5 changed files
with
357 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
82 changes: 82 additions & 0 deletions
82
packages/upgrade/transforms/__testfixtures__/ibm-products-update-http-errors.input.js
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 React from 'react'; | ||
import { | ||
HTTPError403, | ||
HTTPError404, | ||
HTTPErrorOther, | ||
} from '@carbon/ibm-products'; | ||
import './_example.scss'; | ||
|
||
export const Example = () => ( | ||
<> | ||
<HTTPError403 | ||
description="You are not authorized to access this resource." | ||
errorCodeLabel="Error 403" | ||
links={[ | ||
{ | ||
href: 'https://www.carbondesignsystem.com', | ||
text: 'Carbon Design System', | ||
}, | ||
{ | ||
href: 'https://github.com/carbon-design-system/ibm-products', | ||
text: 'Carbon for IBM Products component library', | ||
}, | ||
{ | ||
href: 'https://github.com/carbon-design-system/ibm-products', | ||
text: 'Carbon for IBM Products component library', | ||
}, | ||
{ | ||
href: 'https://github.com/carbon-design-system/ibm-products', | ||
text: 'Carbon for IBM Products component library', | ||
}, | ||
{ | ||
href: 'https://github.com/carbon-design-system/ibm-products', | ||
text: 'Carbon for IBM Products component library', | ||
}, | ||
]} | ||
title="Forbidden" | ||
/> | ||
<HTTPError404 | ||
description="The page you are looking for was not found." | ||
errorCodeLabel="Error 404" | ||
links={[ | ||
{ | ||
href: 'https://www.carbondesignsystem.com', | ||
text: 'Carbon Design System', | ||
}, | ||
{ | ||
href: 'https://github.com/carbon-design-system/ibm-products', | ||
text: 'Carbon for IBM Products component library', | ||
}, | ||
]} | ||
title="Page not found" | ||
/> | ||
<HTTPErrorOther | ||
description="Received an invalid response." | ||
errorCodeLabel="Error 502" | ||
links={[ | ||
{ | ||
href: 'https://www.carbondesignsystem.com', | ||
text: 'Carbon Design System', | ||
}, | ||
{ | ||
href: 'https://github.com/carbon-design-system/ibm-products', | ||
text: 'Carbon for IBM Products component library', | ||
}, | ||
]} | ||
title="Bad gateway" | ||
/> | ||
<MyComponent | ||
errorCodeLabel="error" | ||
links={[ | ||
{ | ||
href: 'https://www.carbondesignsystem.com', | ||
text: 'Carbon Design System', | ||
}, | ||
{ | ||
href: 'https://github.com/carbon-design-system/ibm-products', | ||
text: 'Carbon for IBM Products component library', | ||
}, | ||
]} | ||
/> | ||
</> | ||
); |
40 changes: 40 additions & 0 deletions
40
packages/upgrade/transforms/__testfixtures__/ibm-products-update-http-errors.output.js
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,40 @@ | ||
import { Link } from "@carbon/react"; | ||
import React from 'react'; | ||
import { FullPageError } from '@carbon/ibm-products'; | ||
import './_example.scss'; | ||
|
||
export const Example = () => ( | ||
<> | ||
<FullPageError | ||
description="You are not authorized to access this resource." | ||
label="Error 403" | ||
children={<><Link href={"https://www.carbondesignsystem.com"}>Carbon Design System</Link><br /><Link href={"https://github.com/carbon-design-system/ibm-products"}>Carbon for IBM Products component library</Link><br /><Link href={"https://github.com/carbon-design-system/ibm-products"}>Carbon for IBM Products component library</Link><br /><Link href={"https://github.com/carbon-design-system/ibm-products"}>Carbon for IBM Products component library</Link><br /><Link href={"https://github.com/carbon-design-system/ibm-products"}>Carbon for IBM Products component library</Link></>} | ||
title="Forbidden" | ||
kind="403" /> | ||
<FullPageError | ||
description="The page you are looking for was not found." | ||
label="Error 404" | ||
children={<><Link href={"https://www.carbondesignsystem.com"}>Carbon Design System</Link><br /><Link href={"https://github.com/carbon-design-system/ibm-products"}>Carbon for IBM Products component library</Link></>} | ||
title="Page not found" | ||
kind="404" /> | ||
<FullPageError | ||
description="Received an invalid response." | ||
label="Error 502" | ||
children={<><Link href={"https://www.carbondesignsystem.com"}>Carbon Design System</Link><br /><Link href={"https://github.com/carbon-design-system/ibm-products"}>Carbon for IBM Products component library</Link></>} | ||
title="Bad gateway" | ||
kind="custom" /> | ||
<MyComponent | ||
errorCodeLabel="error" | ||
links={[ | ||
{ | ||
href: 'https://www.carbondesignsystem.com', | ||
text: 'Carbon Design System', | ||
}, | ||
{ | ||
href: 'https://github.com/carbon-design-system/ibm-products', | ||
text: 'Carbon for IBM Products component library', | ||
}, | ||
]} | ||
/> | ||
</> | ||
); |
12 changes: 12 additions & 0 deletions
12
packages/upgrade/transforms/__tests__/ibm-products-update-http-errors-test.js
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 @@ | ||
/** | ||
* Copyright IBM Corp. 2021, 2024 | ||
* | ||
* This source code is licensed under the Apache-2.0 license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const { defineTest } = require('jscodeshift/dist/testUtils'); | ||
|
||
defineTest(__dirname, 'ibm-products-update-http-errors'); |
192 changes: 192 additions & 0 deletions
192
packages/upgrade/transforms/ibm-products-update-http-errors.js
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,192 @@ | ||
/** | ||
* Copyright IBM Corp. 2021, 2024 | ||
* | ||
* This source code is licensed under the Apache-2.0 license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* Rewrites HttpError403, HttpError404, HttpErrorOther to FullPageError | ||
* | ||
* Transforms: | ||
* | ||
* <HttpError403 />, <HttpError404 />, <HttpErrorOther /> | ||
* | ||
* Into: | ||
* | ||
* <FullPageError /> | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const transform = (file, api) => { | ||
const j = api.jscodeshift; | ||
const root = j(file.source); | ||
let dirtyFlag = false; | ||
|
||
const componentKindMap = { | ||
HTTPError403: '403', | ||
HTTPError404: '404', | ||
HTTPErrorOther: 'custom', | ||
}; | ||
const NEW_COMPONENT = 'FullPageError'; | ||
const LINK_COMPONENT = 'Link'; | ||
|
||
root.find(j.JSXElement).forEach((element) => { | ||
const openingElement = element.node.openingElement; | ||
const componentName = openingElement.name.name; | ||
|
||
if (componentKindMap[componentName]) { | ||
dirtyFlag = true; | ||
// Opening tag | ||
openingElement.name.name = NEW_COMPONENT; | ||
|
||
if (element.node.closingElement) { | ||
// Closing tag | ||
element.node.closingElement.name.name = NEW_COMPONENT; | ||
} | ||
|
||
// Attach new 'kind' attribute | ||
const kindAttribute = j.jsxAttribute( | ||
j.jsxIdentifier('kind'), | ||
j.literal(componentKindMap[componentName]) | ||
); | ||
openingElement.attributes.push(kindAttribute); | ||
|
||
// Change errorCodeLabel attribute to label | ||
const errorCodeLabelProp = openingElement.attributes.find( | ||
(attr) => attr.name.name === 'errorCodeLabel' | ||
); | ||
if (errorCodeLabelProp) { | ||
errorCodeLabelProp.name.name = 'label'; | ||
} | ||
|
||
// Convert 'links' prop to 'children' with <Link></Link> tags | ||
const linksProp = openingElement.attributes.find( | ||
(attr) => attr.name.name === 'links' | ||
); | ||
if (linksProp) { | ||
linksProp.name.name = 'children'; | ||
|
||
// Convert link value elements | ||
const linkValues = linksProp.value.expression.elements; | ||
let linksLen = linkValues.length; | ||
const linkElements = []; | ||
|
||
linkValues.forEach((link) => { | ||
const linkTag = j.jsxElement( | ||
j.jsxOpeningElement(j.jsxIdentifier(LINK_COMPONENT), [ | ||
j.jsxAttribute( | ||
j.jsxIdentifier('href'), | ||
j.jsxExpressionContainer( | ||
j.literal(link.properties[0].value.value) | ||
), | ||
j.jsxClosingElement(j.jsxIdentifier(LINK_COMPONENT)) | ||
), | ||
]), | ||
j.jsxClosingElement(j.jsxIdentifier(LINK_COMPONENT)), | ||
[j.jsxText(link.properties[1].value.value)] | ||
); | ||
linkElements.push(linkTag); | ||
linksLen--; | ||
if (linksLen) { | ||
const brTag = j.jsxElement( | ||
j.jsxOpeningElement(j.jsxIdentifier('br'), [], true) | ||
); | ||
|
||
linkElements.push(brTag); | ||
} | ||
}); | ||
|
||
const linksFragment = j.jsxFragment( | ||
j.jsxOpeningFragment(), | ||
j.jsxClosingFragment(), | ||
linkElements | ||
); | ||
|
||
linksProp.value = j.jsxExpressionContainer(linksFragment); | ||
} | ||
} | ||
}); | ||
|
||
// Transform import statements if necessary | ||
if (dirtyFlag) { | ||
const importDeclarations = root.find(j.ImportDeclaration); | ||
const CARBON_PATH = '@carbon/react'; | ||
const linkComponentSpecifier = j.importSpecifier( | ||
j.identifier(LINK_COMPONENT), | ||
j.identifier(LINK_COMPONENT) | ||
); | ||
|
||
// Check for '@carbon/react' import statement exists | ||
const existCarbonImport = importDeclarations.some( | ||
(importDeclaration) => importDeclaration.node.source.value === CARBON_PATH | ||
); | ||
|
||
// Update @carbon/imb-products import statement | ||
importDeclarations.forEach((statement) => { | ||
const specifiers = statement.node.specifiers; | ||
const source = statement.node.source; | ||
const PRODUCTS_PATH = '@carbon/ibm-products'; | ||
|
||
if (source.value === PRODUCTS_PATH) { | ||
// Check new component name already imported | ||
const isNewIdExists = specifiers.some( | ||
(spec) => spec.imported.name === NEW_COMPONENT | ||
); | ||
|
||
// If the new component not already imported, import it | ||
if (!isNewIdExists) { | ||
const newSpecifier = j.importSpecifier( | ||
j.identifier(NEW_COMPONENT), | ||
j.identifier(NEW_COMPONENT) | ||
); | ||
// Including the new specifier into @carbon/ibm-products import statement | ||
specifiers.push(newSpecifier); | ||
} | ||
|
||
Object.keys(componentKindMap).forEach((key) => { | ||
// Check the old component names exists | ||
const identifierIndex = specifiers.findIndex( | ||
(spec) => spec.imported.name === key | ||
); | ||
|
||
// Remove all old components' import | ||
if (identifierIndex !== -1) { | ||
specifiers.splice(identifierIndex, 1); | ||
} | ||
}); | ||
} | ||
|
||
// if the @carbon/react import statement already exists update the import | ||
if (existCarbonImport && source.value === CARBON_PATH) { | ||
// Check Link component already imported | ||
const linkIdIndex = specifiers.findIndex( | ||
(spec) => spec.imported.name === LINK_COMPONENT | ||
); | ||
|
||
// if Link component does not in the imported components list | ||
if (linkIdIndex === -1) { | ||
// Include the Link component in imported components list | ||
specifiers.push(linkComponentSpecifier); | ||
} | ||
} | ||
}); | ||
|
||
// If @carbon/react import statement not exists | ||
if (!existCarbonImport) { | ||
const carbonImportDeclaration = j.importDeclaration( | ||
[linkComponentSpecifier], | ||
j.literal(CARBON_PATH) | ||
); | ||
|
||
root | ||
.find(j.Program) | ||
.forEach((program) => | ||
program.node.body.unshift(carbonImportDeclaration) | ||
); | ||
} | ||
} | ||
|
||
return root.toSource(); | ||
}; | ||
|
||
module.exports = transform; |