Skip to content

Commit 22f19fc

Browse files
makafsal2nikhiltomriddhybansalguidari
authored
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 <63502271+2nikhiltom@users.noreply.github.com> Co-authored-by: Riddhi Bansal <41935566+riddhybansal@users.noreply.github.com> Co-authored-by: Guilherme Datilio Ribeiro <guilhermedatilio@gmail.com>
1 parent 4e89908 commit 22f19fc

File tree

5 files changed

+357
-0
lines changed

5 files changed

+357
-0
lines changed

packages/upgrade/src/upgrades.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,37 @@ export const upgrades = [
396396
});
397397
},
398398
},
399+
{
400+
name: 'ibm-products-update-http-errors',
401+
description:
402+
'Rewrites HttpError403, HttpError404, HttpErrorOther to FullPageError',
403+
migrate: async (options) => {
404+
const transform = path.join(
405+
TRANSFORM_DIR,
406+
'ibm-products-update-http-errors.js'
407+
);
408+
const paths =
409+
Array.isArray(options.paths) && options.paths.length > 0
410+
? options.paths
411+
: await glob(['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'], {
412+
cwd: options.workspaceDir,
413+
ignore: [
414+
'**/es/**',
415+
'**/lib/**',
416+
'**/umd/**',
417+
'**/node_modules/**',
418+
'**/storybook-static/**',
419+
],
420+
});
421+
422+
await run({
423+
dry: !options.write,
424+
transform,
425+
paths,
426+
verbose: options.verbose,
427+
});
428+
},
429+
},
399430
{
400431
name: 'ibm-products-update-userprofileimage',
401432
description: 'Rewrites UserProfileImage to UserAvatar',
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import React from 'react';
2+
import {
3+
HTTPError403,
4+
HTTPError404,
5+
HTTPErrorOther,
6+
} from '@carbon/ibm-products';
7+
import './_example.scss';
8+
9+
export const Example = () => (
10+
<>
11+
<HTTPError403
12+
description="You are not authorized to access this resource."
13+
errorCodeLabel="Error 403"
14+
links={[
15+
{
16+
href: 'https://www.carbondesignsystem.com',
17+
text: 'Carbon Design System',
18+
},
19+
{
20+
href: 'https://github.com/carbon-design-system/ibm-products',
21+
text: 'Carbon for IBM Products component library',
22+
},
23+
{
24+
href: 'https://github.com/carbon-design-system/ibm-products',
25+
text: 'Carbon for IBM Products component library',
26+
},
27+
{
28+
href: 'https://github.com/carbon-design-system/ibm-products',
29+
text: 'Carbon for IBM Products component library',
30+
},
31+
{
32+
href: 'https://github.com/carbon-design-system/ibm-products',
33+
text: 'Carbon for IBM Products component library',
34+
},
35+
]}
36+
title="Forbidden"
37+
/>
38+
<HTTPError404
39+
description="The page you are looking for was not found."
40+
errorCodeLabel="Error 404"
41+
links={[
42+
{
43+
href: 'https://www.carbondesignsystem.com',
44+
text: 'Carbon Design System',
45+
},
46+
{
47+
href: 'https://github.com/carbon-design-system/ibm-products',
48+
text: 'Carbon for IBM Products component library',
49+
},
50+
]}
51+
title="Page not found"
52+
/>
53+
<HTTPErrorOther
54+
description="Received an invalid response."
55+
errorCodeLabel="Error 502"
56+
links={[
57+
{
58+
href: 'https://www.carbondesignsystem.com',
59+
text: 'Carbon Design System',
60+
},
61+
{
62+
href: 'https://github.com/carbon-design-system/ibm-products',
63+
text: 'Carbon for IBM Products component library',
64+
},
65+
]}
66+
title="Bad gateway"
67+
/>
68+
<MyComponent
69+
errorCodeLabel="error"
70+
links={[
71+
{
72+
href: 'https://www.carbondesignsystem.com',
73+
text: 'Carbon Design System',
74+
},
75+
{
76+
href: 'https://github.com/carbon-design-system/ibm-products',
77+
text: 'Carbon for IBM Products component library',
78+
},
79+
]}
80+
/>
81+
</>
82+
);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Link } from "@carbon/react";
2+
import React from 'react';
3+
import { FullPageError } from '@carbon/ibm-products';
4+
import './_example.scss';
5+
6+
export const Example = () => (
7+
<>
8+
<FullPageError
9+
description="You are not authorized to access this resource."
10+
label="Error 403"
11+
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></>}
12+
title="Forbidden"
13+
kind="403" />
14+
<FullPageError
15+
description="The page you are looking for was not found."
16+
label="Error 404"
17+
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></>}
18+
title="Page not found"
19+
kind="404" />
20+
<FullPageError
21+
description="Received an invalid response."
22+
label="Error 502"
23+
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></>}
24+
title="Bad gateway"
25+
kind="custom" />
26+
<MyComponent
27+
errorCodeLabel="error"
28+
links={[
29+
{
30+
href: 'https://www.carbondesignsystem.com',
31+
text: 'Carbon Design System',
32+
},
33+
{
34+
href: 'https://github.com/carbon-design-system/ibm-products',
35+
text: 'Carbon for IBM Products component library',
36+
},
37+
]}
38+
/>
39+
</>
40+
);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Copyright IBM Corp. 2021, 2024
3+
*
4+
* This source code is licensed under the Apache-2.0 license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
'use strict';
9+
10+
const { defineTest } = require('jscodeshift/dist/testUtils');
11+
12+
defineTest(__dirname, 'ibm-products-update-http-errors');
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/**
2+
* Copyright IBM Corp. 2021, 2024
3+
*
4+
* This source code is licensed under the Apache-2.0 license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* Rewrites HttpError403, HttpError404, HttpErrorOther to FullPageError
8+
*
9+
* Transforms:
10+
*
11+
* <HttpError403 />, <HttpError404 />, <HttpErrorOther />
12+
*
13+
* Into:
14+
*
15+
* <FullPageError />
16+
*/
17+
18+
'use strict';
19+
20+
const transform = (file, api) => {
21+
const j = api.jscodeshift;
22+
const root = j(file.source);
23+
let dirtyFlag = false;
24+
25+
const componentKindMap = {
26+
HTTPError403: '403',
27+
HTTPError404: '404',
28+
HTTPErrorOther: 'custom',
29+
};
30+
const NEW_COMPONENT = 'FullPageError';
31+
const LINK_COMPONENT = 'Link';
32+
33+
root.find(j.JSXElement).forEach((element) => {
34+
const openingElement = element.node.openingElement;
35+
const componentName = openingElement.name.name;
36+
37+
if (componentKindMap[componentName]) {
38+
dirtyFlag = true;
39+
// Opening tag
40+
openingElement.name.name = NEW_COMPONENT;
41+
42+
if (element.node.closingElement) {
43+
// Closing tag
44+
element.node.closingElement.name.name = NEW_COMPONENT;
45+
}
46+
47+
// Attach new 'kind' attribute
48+
const kindAttribute = j.jsxAttribute(
49+
j.jsxIdentifier('kind'),
50+
j.literal(componentKindMap[componentName])
51+
);
52+
openingElement.attributes.push(kindAttribute);
53+
54+
// Change errorCodeLabel attribute to label
55+
const errorCodeLabelProp = openingElement.attributes.find(
56+
(attr) => attr.name.name === 'errorCodeLabel'
57+
);
58+
if (errorCodeLabelProp) {
59+
errorCodeLabelProp.name.name = 'label';
60+
}
61+
62+
// Convert 'links' prop to 'children' with <Link></Link> tags
63+
const linksProp = openingElement.attributes.find(
64+
(attr) => attr.name.name === 'links'
65+
);
66+
if (linksProp) {
67+
linksProp.name.name = 'children';
68+
69+
// Convert link value elements
70+
const linkValues = linksProp.value.expression.elements;
71+
let linksLen = linkValues.length;
72+
const linkElements = [];
73+
74+
linkValues.forEach((link) => {
75+
const linkTag = j.jsxElement(
76+
j.jsxOpeningElement(j.jsxIdentifier(LINK_COMPONENT), [
77+
j.jsxAttribute(
78+
j.jsxIdentifier('href'),
79+
j.jsxExpressionContainer(
80+
j.literal(link.properties[0].value.value)
81+
),
82+
j.jsxClosingElement(j.jsxIdentifier(LINK_COMPONENT))
83+
),
84+
]),
85+
j.jsxClosingElement(j.jsxIdentifier(LINK_COMPONENT)),
86+
[j.jsxText(link.properties[1].value.value)]
87+
);
88+
linkElements.push(linkTag);
89+
linksLen--;
90+
if (linksLen) {
91+
const brTag = j.jsxElement(
92+
j.jsxOpeningElement(j.jsxIdentifier('br'), [], true)
93+
);
94+
95+
linkElements.push(brTag);
96+
}
97+
});
98+
99+
const linksFragment = j.jsxFragment(
100+
j.jsxOpeningFragment(),
101+
j.jsxClosingFragment(),
102+
linkElements
103+
);
104+
105+
linksProp.value = j.jsxExpressionContainer(linksFragment);
106+
}
107+
}
108+
});
109+
110+
// Transform import statements if necessary
111+
if (dirtyFlag) {
112+
const importDeclarations = root.find(j.ImportDeclaration);
113+
const CARBON_PATH = '@carbon/react';
114+
const linkComponentSpecifier = j.importSpecifier(
115+
j.identifier(LINK_COMPONENT),
116+
j.identifier(LINK_COMPONENT)
117+
);
118+
119+
// Check for '@carbon/react' import statement exists
120+
const existCarbonImport = importDeclarations.some(
121+
(importDeclaration) => importDeclaration.node.source.value === CARBON_PATH
122+
);
123+
124+
// Update @carbon/imb-products import statement
125+
importDeclarations.forEach((statement) => {
126+
const specifiers = statement.node.specifiers;
127+
const source = statement.node.source;
128+
const PRODUCTS_PATH = '@carbon/ibm-products';
129+
130+
if (source.value === PRODUCTS_PATH) {
131+
// Check new component name already imported
132+
const isNewIdExists = specifiers.some(
133+
(spec) => spec.imported.name === NEW_COMPONENT
134+
);
135+
136+
// If the new component not already imported, import it
137+
if (!isNewIdExists) {
138+
const newSpecifier = j.importSpecifier(
139+
j.identifier(NEW_COMPONENT),
140+
j.identifier(NEW_COMPONENT)
141+
);
142+
// Including the new specifier into @carbon/ibm-products import statement
143+
specifiers.push(newSpecifier);
144+
}
145+
146+
Object.keys(componentKindMap).forEach((key) => {
147+
// Check the old component names exists
148+
const identifierIndex = specifiers.findIndex(
149+
(spec) => spec.imported.name === key
150+
);
151+
152+
// Remove all old components' import
153+
if (identifierIndex !== -1) {
154+
specifiers.splice(identifierIndex, 1);
155+
}
156+
});
157+
}
158+
159+
// if the @carbon/react import statement already exists update the import
160+
if (existCarbonImport && source.value === CARBON_PATH) {
161+
// Check Link component already imported
162+
const linkIdIndex = specifiers.findIndex(
163+
(spec) => spec.imported.name === LINK_COMPONENT
164+
);
165+
166+
// if Link component does not in the imported components list
167+
if (linkIdIndex === -1) {
168+
// Include the Link component in imported components list
169+
specifiers.push(linkComponentSpecifier);
170+
}
171+
}
172+
});
173+
174+
// If @carbon/react import statement not exists
175+
if (!existCarbonImport) {
176+
const carbonImportDeclaration = j.importDeclaration(
177+
[linkComponentSpecifier],
178+
j.literal(CARBON_PATH)
179+
);
180+
181+
root
182+
.find(j.Program)
183+
.forEach((program) =>
184+
program.node.body.unshift(carbonImportDeclaration)
185+
);
186+
}
187+
}
188+
189+
return root.toSource();
190+
};
191+
192+
module.exports = transform;

0 commit comments

Comments
 (0)