diff --git a/packages/upgrade/src/upgrades.js b/packages/upgrade/src/upgrades.js
index 8a9bd4c05f14..018120b5bd89 100644
--- a/packages/upgrade/src/upgrades.js
+++ b/packages/upgrade/src/upgrades.js
@@ -396,6 +396,37 @@ export const upgrades = [
});
},
},
+ {
+ name: 'ibm-products-update-http-errors',
+ description:
+ 'Rewrites HttpError403, HttpError404, HttpErrorOther to FullPageError',
+ migrate: async (options) => {
+ const transform = path.join(
+ TRANSFORM_DIR,
+ 'ibm-products-update-http-errors.js'
+ );
+ const paths =
+ Array.isArray(options.paths) && options.paths.length > 0
+ ? options.paths
+ : await glob(['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'], {
+ cwd: options.workspaceDir,
+ ignore: [
+ '**/es/**',
+ '**/lib/**',
+ '**/umd/**',
+ '**/node_modules/**',
+ '**/storybook-static/**',
+ ],
+ });
+
+ await run({
+ dry: !options.write,
+ transform,
+ paths,
+ verbose: options.verbose,
+ });
+ },
+ },
{
name: 'ibm-products-update-userprofileimage',
description: 'Rewrites UserProfileImage to UserAvatar',
diff --git a/packages/upgrade/transforms/__testfixtures__/ibm-products-update-http-errors.input.js b/packages/upgrade/transforms/__testfixtures__/ibm-products-update-http-errors.input.js
new file mode 100644
index 000000000000..eb58558f45f9
--- /dev/null
+++ b/packages/upgrade/transforms/__testfixtures__/ibm-products-update-http-errors.input.js
@@ -0,0 +1,82 @@
+import React from 'react';
+import {
+ HTTPError403,
+ HTTPError404,
+ HTTPErrorOther,
+} from '@carbon/ibm-products';
+import './_example.scss';
+
+export const Example = () => (
+ <>
+
+
+
+
+ >
+);
diff --git a/packages/upgrade/transforms/__testfixtures__/ibm-products-update-http-errors.output.js b/packages/upgrade/transforms/__testfixtures__/ibm-products-update-http-errors.output.js
new file mode 100644
index 000000000000..940131571225
--- /dev/null
+++ b/packages/upgrade/transforms/__testfixtures__/ibm-products-update-http-errors.output.js
@@ -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 = () => (
+ <>
+ Carbon Design System
Carbon for IBM Products component library
Carbon for IBM Products component library
Carbon for IBM Products component library
Carbon for IBM Products component library>}
+ title="Forbidden"
+ kind="403" />
+ Carbon Design System
Carbon for IBM Products component library>}
+ title="Page not found"
+ kind="404" />
+ Carbon Design System
Carbon for IBM Products component library>}
+ title="Bad gateway"
+ kind="custom" />
+
+ >
+);
diff --git a/packages/upgrade/transforms/__tests__/ibm-products-update-http-errors-test.js b/packages/upgrade/transforms/__tests__/ibm-products-update-http-errors-test.js
new file mode 100644
index 000000000000..c5b5c5c44e10
--- /dev/null
+++ b/packages/upgrade/transforms/__tests__/ibm-products-update-http-errors-test.js
@@ -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');
diff --git a/packages/upgrade/transforms/ibm-products-update-http-errors.js b/packages/upgrade/transforms/ibm-products-update-http-errors.js
new file mode 100644
index 000000000000..a40f795d0fbc
--- /dev/null
+++ b/packages/upgrade/transforms/ibm-products-update-http-errors.js
@@ -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:
+ *
+ * , ,
+ *
+ * Into:
+ *
+ *
+ */
+
+'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 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;