+ `,
+
+ migrate: async (options) => {
+ const transform = path.join(
+ TRANSFORM_DIR,
+ 'featureflag-deprecate-flags-prop.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,
+ });
+ },
+ },
],
},
{
diff --git a/packages/upgrade/transforms/__testfixtures__/featureflag-deprecate-flags-prop.input.js b/packages/upgrade/transforms/__testfixtures__/featureflag-deprecate-flags-prop.input.js
new file mode 100644
index 000000000000..0de313b749e7
--- /dev/null
+++ b/packages/upgrade/transforms/__testfixtures__/featureflag-deprecate-flags-prop.input.js
@@ -0,0 +1,134 @@
+import { FeatureFlags } from '../FeatureFlags';
+import {
+ RadioTile,
+ TileGroup,
+ TreeView,
+ VStack,
+ TreeNode,
+ OverflowMenu,
+ MenuItem,
+} from '@carbon/react';
+import { Document, Folder } from '@carbon/icons-react';
+export const EnableV12TileDefaultIconsFlag = () => {
+ return (
+
+
+
+ Option 1
+
+
+ Option 2
+
+
+
+ );
+};
+export const EnableExperimentalFocusWrapWithoutSentinels = () => {
+ return (
+ // prettier-ignore
+
+
+
+ );
+};
+export const EnableTreeviewControllable = (args) => {
+ return (
+
+
+ {
+ console.log('test');
+ }}
+ selected={[]}
+ onSelect={() => {
+ console.log('test');
+ }}>
+ {renderTree(nodes)}
+
+
+
+ );
+};
+export const EnableV12Overflowmenu = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+};
+export const EnableV12TileRadioIcons = () => {
+ return (
+
+
+
+ Option 1
+
+
+ Option 2
+
+
+
+ );
+};
+export const TestRegularJsx = () => {
+ return ;
+};
+export const CombinedFlags = () => {
+ return (
+ // prettier-ignore
+
+
+
+ Option 1
+
+
+ Option 2
+
+
+
+ );
+};
+export const OldFlags = () => {
+ return (
+
+
+
+ Option 1
+
+
+ Option 2
+
+
+
+ );
+};
diff --git a/packages/upgrade/transforms/__testfixtures__/featureflag-deprecate-flags-prop.output.js b/packages/upgrade/transforms/__testfixtures__/featureflag-deprecate-flags-prop.output.js
new file mode 100644
index 000000000000..4fff2eb0a5f5
--- /dev/null
+++ b/packages/upgrade/transforms/__testfixtures__/featureflag-deprecate-flags-prop.output.js
@@ -0,0 +1,124 @@
+import { FeatureFlags } from '../FeatureFlags';
+import {
+ RadioTile,
+ TileGroup,
+ TreeView,
+ VStack,
+ TreeNode,
+ OverflowMenu,
+ MenuItem,
+} from '@carbon/react';
+import { Document, Folder } from '@carbon/icons-react';
+export const EnableV12TileDefaultIconsFlag = () => {
+ return (
+ (
+
+
+ Option 1
+
+
+ Option 2
+
+
+ )
+ );
+};
+export const EnableExperimentalFocusWrapWithoutSentinels = () => {
+ return (
+ // prettier-ignore
+ (
+
+ )
+ );
+};
+export const EnableTreeviewControllable = (args) => {
+ return (
+ (
+
+ {
+ console.log('test');
+ }}
+ selected={[]}
+ onSelect={() => {
+ console.log('test');
+ }}>
+ {renderTree(nodes)}
+
+
+
)
+ );
+};
+export const EnableV12Overflowmenu = () => {
+ return (
+ (
+
+
+
+
+
+
+
+
+ )
+ );
+};
+export const EnableV12TileRadioIcons = () => {
+ return (
+ (
+
+
+ Option 1
+
+
+ Option 2
+
+
+ )
+ );
+};
+export const TestRegularJsx = () => {
+ return ;
+};
+export const CombinedFlags = () => {
+ return (
+ // prettier-ignore
+ (
+
+
+ Option 1
+
+
+ Option 2
+
+
+ )
+ );
+};
+export const OldFlags = () => {
+ return (
+ (
+
+
+ Option 1
+
+
+ Option 2
+
+
+ )
+ );
+};
diff --git a/packages/upgrade/transforms/__tests__/featureflag-deprecate-flags-prop-test.js b/packages/upgrade/transforms/__tests__/featureflag-deprecate-flags-prop-test.js
new file mode 100644
index 000000000000..be9af914cd88
--- /dev/null
+++ b/packages/upgrade/transforms/__tests__/featureflag-deprecate-flags-prop-test.js
@@ -0,0 +1,12 @@
+/**
+ * Copyright IBM Corp. 2016, 2023
+ *
+ * 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, 'featureflag-deprecate-flags-prop');
diff --git a/packages/upgrade/transforms/featureflag-deprecate-flags-prop.js b/packages/upgrade/transforms/featureflag-deprecate-flags-prop.js
new file mode 100644
index 000000000000..98bd87f9a24c
--- /dev/null
+++ b/packages/upgrade/transforms/featureflag-deprecate-flags-prop.js
@@ -0,0 +1,80 @@
+/**
+ * Copyright IBM Corp. 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.
+ *
+ * Migrate the `flags` object prop to individual boolean props
+ *
+ * Transforms:
+ *
+ *
+ *
+ * Into:
+ *
+ *
+ */
+
+'use strict';
+
+const defaultOptions = {
+ quote: 'single',
+ trailingComma: true,
+};
+
+//This list can be updated as needed, if any flags are made true by default
+const flagsToRemove = [
+ 'enable-v11-release',
+ 'enable-css-custom-properties',
+ 'enable-css-grid',
+];
+
+function transform(fileInfo, api, options) {
+ const { jscodeshift: j } = api;
+ const root = j(fileInfo.source);
+ const printOptions = options.printOptions || defaultOptions;
+ if (
+ !root.find(j.JSXOpeningElement, { name: { name: 'FeatureFlags' } }).size()
+ ) {
+ return null; // if no FeatureFlags found, don't modify & return the file
+ }
+ root
+ .find(j.JSXOpeningElement, { name: { name: 'FeatureFlags' } })
+ .forEach((path) => {
+ const flagsAttribute = path.node.attributes.find(
+ (attr) => attr.type === 'JSXAttribute' && attr.name.name === 'flags'
+ );
+
+ if (flagsAttribute?.value?.expression?.type === 'ObjectExpression') {
+ const properties = flagsAttribute.value.expression.properties;
+
+ // Filter out flags to remove
+ const filteredProperties = properties.filter((prop) => {
+ const keyName =
+ prop.key.type === 'Identifier' ? prop.key.name : prop.key.value;
+ return !flagsToRemove.includes(keyName);
+ });
+
+ // Convert remaining flags to boolean props
+ const newAttributes = filteredProperties
+ .filter((flag) => flag.value.value === true)
+ .map((flag) => {
+ const flagName =
+ flag.key.type === 'Identifier' ? flag.key.name : flag.key.value;
+ const propName = flagName.replace(/-(\w)/g, (_, c) =>
+ c.toUpperCase()
+ );
+ return j.jsxAttribute(j.jsxIdentifier(propName));
+ });
+
+ path.node.attributes = [
+ ...path.node.attributes.filter((attr) => attr.name.name !== 'flags'),
+ ...newAttributes,
+ ];
+ }
+ });
+
+ return root.toSource(printOptions);
+}
+
+module.exports = transform;