@carbon/upgrade 11.23.0-rc.0 → 11.24.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +94 -4
- package/package.json +3 -3
- package/transforms/__testfixtures__/enable-v12-overflowmenu-nowrap.input.js +41 -0
- package/transforms/__testfixtures__/enable-v12-overflowmenu-nowrap.input.tsx +37 -0
- package/transforms/__testfixtures__/enable-v12-overflowmenu-nowrap.output.js +50 -0
- package/transforms/__testfixtures__/enable-v12-overflowmenu-nowrap.output.tsx +42 -0
- package/transforms/__testfixtures__/enable-v12-overflowmenu.input.js +45 -0
- package/transforms/__testfixtures__/enable-v12-overflowmenu.input.tsx +37 -0
- package/transforms/__testfixtures__/enable-v12-overflowmenu.output.js +54 -0
- package/transforms/__testfixtures__/enable-v12-overflowmenu.output.tsx +42 -0
- package/transforms/__testfixtures__/slug-prop-to-decorator-prop.input.js +41 -0
- package/transforms/__testfixtures__/slug-prop-to-decorator-prop.input.tsx +41 -0
- package/transforms/__testfixtures__/slug-prop-to-decorator-prop.output.js +41 -0
- package/transforms/__testfixtures__/slug-prop-to-decorator-prop.output.tsx +41 -0
- package/transforms/__tests__/enable-v12-overflowmenu-test.js +26 -0
- package/transforms/__tests__/slug-prop-to-decorator-prop-test.js +12 -0
- package/transforms/enable-v12-overflowmenu.js +218 -0
- package/transforms/slug-prop-to-decorator-prop.js +47 -0
package/cli.js
CHANGED
|
@@ -52641,6 +52641,87 @@ var upgrades = [
|
|
|
52641
52641
|
});
|
|
52642
52642
|
}
|
|
52643
52643
|
},
|
|
52644
|
+
{
|
|
52645
|
+
name: "enable-v12-overflowmenu",
|
|
52646
|
+
description: `
|
|
52647
|
+
Updates OverflowMenu components to v12 with optional FeatureFlags wrapping:
|
|
52648
|
+
1. Migrates to new API (OverflowMenuItem -> MenuItem)
|
|
52649
|
+
2. Updates props (itemText -> label, etc)
|
|
52650
|
+
3. Optional FeatureFlags wrapping (--wrapWithFeatureFlag=false to disable)
|
|
52651
|
+
|
|
52652
|
+
Example:
|
|
52653
|
+
Before: <OverflowMenu aria-label="menu"><OverflowMenuItem itemText="Option" /></OverflowMenu>
|
|
52654
|
+
After: <FeatureFlags enableV12Overflowmenu>
|
|
52655
|
+
<OverflowMenu label="menu"><MenuItem label="Option" /></OverflowMenu>
|
|
52656
|
+
</FeatureFlags>
|
|
52657
|
+
`,
|
|
52658
|
+
migrate: async (options) => {
|
|
52659
|
+
const transform = import_path2.default.join(
|
|
52660
|
+
TRANSFORM_DIR,
|
|
52661
|
+
"enable-v12-overflowmenu.js"
|
|
52662
|
+
);
|
|
52663
|
+
const paths = Array.isArray(options.paths) && options.paths.length > 0 ? options.paths : await (0, import_fast_glob2.default)(["**/*.{js,jsx,ts,tsx}"], {
|
|
52664
|
+
cwd: options.workspaceDir,
|
|
52665
|
+
ignore: [
|
|
52666
|
+
"**/es/**",
|
|
52667
|
+
"**/lib/**",
|
|
52668
|
+
"**/umd/**",
|
|
52669
|
+
"**/node_modules/**",
|
|
52670
|
+
"**/storybook-static/**"
|
|
52671
|
+
]
|
|
52672
|
+
});
|
|
52673
|
+
await run2({
|
|
52674
|
+
dry: !options.write,
|
|
52675
|
+
transform,
|
|
52676
|
+
paths,
|
|
52677
|
+
verbose: options.verbose,
|
|
52678
|
+
wrapWithFeatureFlag: options.wrapWithFeatureFlag
|
|
52679
|
+
});
|
|
52680
|
+
}
|
|
52681
|
+
},
|
|
52682
|
+
{
|
|
52683
|
+
name: "slug-prop-to-decorator-prop",
|
|
52684
|
+
description: `
|
|
52685
|
+
Replace slug prop with decorator
|
|
52686
|
+
|
|
52687
|
+
Transforms:
|
|
52688
|
+
<Component slug="value">
|
|
52689
|
+
content
|
|
52690
|
+
</Component>
|
|
52691
|
+
|
|
52692
|
+
Into:
|
|
52693
|
+
<Component decorator="value">
|
|
52694
|
+
content
|
|
52695
|
+
</Component>
|
|
52696
|
+
`,
|
|
52697
|
+
migrate: async (options) => {
|
|
52698
|
+
const transform = import_path2.default.join(
|
|
52699
|
+
TRANSFORM_DIR,
|
|
52700
|
+
"slug-prop-to-decorator-prop.js"
|
|
52701
|
+
);
|
|
52702
|
+
const paths = Array.isArray(options.paths) && options.paths.length > 0 ? options.paths : await (0, import_fast_glob2.default)(["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"], {
|
|
52703
|
+
cwd: options.workspaceDir,
|
|
52704
|
+
ignore: [
|
|
52705
|
+
"**/es/**",
|
|
52706
|
+
"**/lib/**",
|
|
52707
|
+
"**/umd/**",
|
|
52708
|
+
"**/node_modules/**",
|
|
52709
|
+
"**/storybook-static/**",
|
|
52710
|
+
"**/dist/**",
|
|
52711
|
+
"**/build/**",
|
|
52712
|
+
"**/*.d.ts",
|
|
52713
|
+
"**/coverage/**"
|
|
52714
|
+
]
|
|
52715
|
+
});
|
|
52716
|
+
await run2({
|
|
52717
|
+
dry: !options.write,
|
|
52718
|
+
transform,
|
|
52719
|
+
paths,
|
|
52720
|
+
verbose: options.verbose,
|
|
52721
|
+
parser: "tsx"
|
|
52722
|
+
});
|
|
52723
|
+
}
|
|
52724
|
+
},
|
|
52644
52725
|
{
|
|
52645
52726
|
name: "refactor-light-to-layer",
|
|
52646
52727
|
description: `
|
|
@@ -52886,7 +52967,7 @@ var upgrades = [
|
|
|
52886
52967
|
var package_default = {
|
|
52887
52968
|
name: "@carbon/upgrade",
|
|
52888
52969
|
description: "A tool for upgrading Carbon versions",
|
|
52889
|
-
version: "11.
|
|
52970
|
+
version: "11.24.0-rc.0",
|
|
52890
52971
|
license: "Apache-2.0",
|
|
52891
52972
|
bin: {
|
|
52892
52973
|
"carbon-upgrade": "./bin/carbon-upgrade.js"
|
|
@@ -52926,7 +53007,7 @@ var package_default = {
|
|
|
52926
53007
|
devDependencies: {
|
|
52927
53008
|
chalk: "^4.1.1",
|
|
52928
53009
|
"change-case": "^4.1.2",
|
|
52929
|
-
esbuild: "^0.
|
|
53010
|
+
esbuild: "^0.25.0",
|
|
52930
53011
|
execa: "^5.1.1",
|
|
52931
53012
|
"fast-glob": "^3.2.11",
|
|
52932
53013
|
"fs-extra": "^11.0.0",
|
|
@@ -52966,6 +53047,10 @@ async function main({ argv, cwd }) {
|
|
|
52966
53047
|
default: false,
|
|
52967
53048
|
describe: "optionally include additional logs, useful for debugging",
|
|
52968
53049
|
type: "boolean"
|
|
53050
|
+
}).option("wrapWithFeatureFlag", {
|
|
53051
|
+
default: true,
|
|
53052
|
+
describe: "wrap the migration with a feature flag",
|
|
53053
|
+
type: "boolean"
|
|
52969
53054
|
});
|
|
52970
53055
|
cli.usage("Usage: $0 [options]").command(
|
|
52971
53056
|
["upgrade", "$0"],
|
|
@@ -52997,14 +53082,19 @@ async function main({ argv, cwd }) {
|
|
|
52997
53082
|
);
|
|
52998
53083
|
},
|
|
52999
53084
|
run3(async (args) => {
|
|
53000
|
-
const { verbose, migration, write, paths } = args;
|
|
53085
|
+
const { verbose, migration, write, paths, wrapWithFeatureFlag } = args;
|
|
53001
53086
|
const options = {
|
|
53002
53087
|
cwd: cwd(),
|
|
53003
53088
|
verbose,
|
|
53004
53089
|
write,
|
|
53005
53090
|
migration,
|
|
53006
|
-
paths
|
|
53091
|
+
paths,
|
|
53092
|
+
wrapWithFeatureFlag
|
|
53007
53093
|
};
|
|
53094
|
+
console.log(
|
|
53095
|
+
"CLI options wrapWithFeatureFlag:",
|
|
53096
|
+
options.wrapWithFeatureFlag
|
|
53097
|
+
);
|
|
53008
53098
|
await migrate(options, upgrades);
|
|
53009
53099
|
})
|
|
53010
53100
|
);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carbon/upgrade",
|
|
3
3
|
"description": "A tool for upgrading Carbon versions",
|
|
4
|
-
"version": "11.
|
|
4
|
+
"version": "11.24.0-rc.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
7
7
|
"carbon-upgrade": "./bin/carbon-upgrade.js"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"chalk": "^4.1.1",
|
|
43
43
|
"change-case": "^4.1.2",
|
|
44
|
-
"esbuild": "^0.
|
|
44
|
+
"esbuild": "^0.25.0",
|
|
45
45
|
"execa": "^5.1.1",
|
|
46
46
|
"fast-glob": "^3.2.11",
|
|
47
47
|
"fs-extra": "^11.0.0",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"@ibm/telemetry-js": "^1.5.0",
|
|
62
62
|
"jscodeshift": "^17.0.0"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "faf649817d3be3e8e258aba866e14e9378b5c68e"
|
|
65
65
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { OverflowMenu, OverflowMenuItem } from '@carbon/react';
|
|
4
|
+
|
|
5
|
+
function TestComponent({ menuProps }) {
|
|
6
|
+
return (
|
|
7
|
+
<div>
|
|
8
|
+
{/* Old API usage - with explicit props */}
|
|
9
|
+
<OverflowMenu
|
|
10
|
+
aria-label="overflow-menu"
|
|
11
|
+
align="bottom"
|
|
12
|
+
flipped={true}
|
|
13
|
+
light={true}
|
|
14
|
+
size="xl">
|
|
15
|
+
<OverflowMenuItem
|
|
16
|
+
className="test-class"
|
|
17
|
+
itemText="Stop app"
|
|
18
|
+
disabled={false}
|
|
19
|
+
onClick={() => {}}
|
|
20
|
+
/>
|
|
21
|
+
<OverflowMenuItem itemText="Restart app" />
|
|
22
|
+
<OverflowMenuItem hasDivider isDelete itemText="Delete app" />
|
|
23
|
+
</OverflowMenu>
|
|
24
|
+
|
|
25
|
+
{/* Old API with spread props */}
|
|
26
|
+
<OverflowMenu {...menuProps}>
|
|
27
|
+
<OverflowMenuItem itemText="Dynamic item" />
|
|
28
|
+
<OverflowMenuItem hasDivider isDelete itemText="Remove" />
|
|
29
|
+
</OverflowMenu>
|
|
30
|
+
|
|
31
|
+
{/* Already using new API - should not be transformed */}
|
|
32
|
+
<OverflowMenu label="Already migrated">
|
|
33
|
+
<MenuItem label="Option 1" />
|
|
34
|
+
<MenuItemDivider />
|
|
35
|
+
<MenuItem label="Delete" kind="danger" />
|
|
36
|
+
</OverflowMenu>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default TestComponent;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { FC } from 'react';
|
|
4
|
+
import { OverflowMenu, OverflowMenuItem } from '@carbon/react';
|
|
5
|
+
|
|
6
|
+
interface MenuItem {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
action?: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
items: MenuItem[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const TestComponent: FC<Props> = ({ items }) => {
|
|
17
|
+
return (
|
|
18
|
+
<div>
|
|
19
|
+
{/* Old API usage - mapped items */}
|
|
20
|
+
<OverflowMenu aria-label="mapped-menu">
|
|
21
|
+
{items.map((item) => (
|
|
22
|
+
<OverflowMenuItem
|
|
23
|
+
key={item.id}
|
|
24
|
+
itemText={item.label}
|
|
25
|
+
onClick={item.action}
|
|
26
|
+
/>
|
|
27
|
+
))}
|
|
28
|
+
</OverflowMenu>
|
|
29
|
+
{/* Old API - explicit props */}
|
|
30
|
+
<OverflowMenu direction="top" size="lg" flipped={true}>
|
|
31
|
+
<OverflowMenuItem hasDivider isDelete itemText="TypeScript Item" />
|
|
32
|
+
</OverflowMenu>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default TestComponent;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { OverflowMenu, OverflowMenuItem, MenuItem, MenuItemDivider, Button } from '@carbon/react';
|
|
4
|
+
import { FeatureFlags } from '@carbon/feature-flags';
|
|
5
|
+
|
|
6
|
+
function TestComponent({ menuProps }) {
|
|
7
|
+
return (
|
|
8
|
+
<div>
|
|
9
|
+
{/* Old API usage with props - transformed */}
|
|
10
|
+
<OverflowMenu
|
|
11
|
+
label="overflow-menu"
|
|
12
|
+
align="bottom"
|
|
13
|
+
flipped={true}
|
|
14
|
+
light={true}
|
|
15
|
+
size="xl"
|
|
16
|
+
>
|
|
17
|
+
<MenuItem
|
|
18
|
+
className="test-class"
|
|
19
|
+
label="Stop app"
|
|
20
|
+
disabled={false}
|
|
21
|
+
onClick={() => {}}
|
|
22
|
+
/>
|
|
23
|
+
<MenuItem label="Restart app" />
|
|
24
|
+
<MenuItemDivider />
|
|
25
|
+
<MenuItem label="Delete app" kind="danger" />
|
|
26
|
+
</OverflowMenu>
|
|
27
|
+
|
|
28
|
+
{/* Old API with spread props */}
|
|
29
|
+
<OverflowMenu {...menuProps}>
|
|
30
|
+
<MenuItem label="Dynamic item" />
|
|
31
|
+
<MenuItemDivider />
|
|
32
|
+
<MenuItem label="Remove" kind="danger" />
|
|
33
|
+
</OverflowMenu>
|
|
34
|
+
|
|
35
|
+
{/* Already using new API - should not be transformed */}
|
|
36
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
37
|
+
<OverflowMenu label="Already migrated">
|
|
38
|
+
<MenuItem label="Option 1" />
|
|
39
|
+
<MenuItemDivider />
|
|
40
|
+
<MenuItem label="Delete" kind="danger" />
|
|
41
|
+
</OverflowMenu>
|
|
42
|
+
</FeatureFlags>
|
|
43
|
+
|
|
44
|
+
{/* Other components - unchanged */}
|
|
45
|
+
<Button>Normal button</Button>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default TestComponent;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { FeatureFlags } from '@carbon/feature-flags';
|
|
4
|
+
import type { FC } from 'react';
|
|
5
|
+
import { MenuItem, MenuItemDivider, OverflowMenu, OverflowMenuItem } from '@carbon/react';
|
|
6
|
+
|
|
7
|
+
interface MenuItem {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
action?: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
items: MenuItem[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const TestComponent: FC<Props> = ({ items }) => {
|
|
18
|
+
return (
|
|
19
|
+
(<div>
|
|
20
|
+
{/* Old API usage - mapped items */}
|
|
21
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
22
|
+
<OverflowMenu aria-label="mapped-menu">
|
|
23
|
+
{items.map((item) => (
|
|
24
|
+
<OverflowMenuItem
|
|
25
|
+
key={item.id}
|
|
26
|
+
itemText={item.label}
|
|
27
|
+
onClick={item.action}
|
|
28
|
+
/>
|
|
29
|
+
))}
|
|
30
|
+
</OverflowMenu>
|
|
31
|
+
</FeatureFlags>
|
|
32
|
+
{/* Old API - explicit props */}
|
|
33
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
34
|
+
<OverflowMenu direction="top" size="lg" flipped={true}>
|
|
35
|
+
<MenuItemDivider /><MenuItem kind='danger' label="TypeScript Item" />
|
|
36
|
+
</OverflowMenu>
|
|
37
|
+
</FeatureFlags>
|
|
38
|
+
</div>)
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default TestComponent;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { OverflowMenu, OverflowMenuItem, Button } from '@carbon/react';
|
|
4
|
+
|
|
5
|
+
function TestComponent({ menuProps }) {
|
|
6
|
+
return (
|
|
7
|
+
<div>
|
|
8
|
+
{/* Old API usage - with explicit props */}
|
|
9
|
+
<OverflowMenu
|
|
10
|
+
aria-label="overflow-menu"
|
|
11
|
+
flipped={true}
|
|
12
|
+
light={true}
|
|
13
|
+
size="xl">
|
|
14
|
+
<OverflowMenuItem
|
|
15
|
+
className="test-class"
|
|
16
|
+
itemText="Stop app"
|
|
17
|
+
disabled={false}
|
|
18
|
+
onClick={() => {}}
|
|
19
|
+
/>
|
|
20
|
+
<OverflowMenuItem itemText="Restart app" />
|
|
21
|
+
<OverflowMenuItem hasDivider isDelete itemText="Delete app" />
|
|
22
|
+
</OverflowMenu>
|
|
23
|
+
|
|
24
|
+
{/* Old API with spread props */}
|
|
25
|
+
<OverflowMenu {...menuProps}>
|
|
26
|
+
<OverflowMenuItem itemText="Dynamic item" />
|
|
27
|
+
<OverflowMenuItem hasDivider isDelete itemText="Remove" />
|
|
28
|
+
</OverflowMenu>
|
|
29
|
+
|
|
30
|
+
{/* Already using new API - should not be transformed */}
|
|
31
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
32
|
+
<OverflowMenu label="Already migrated">
|
|
33
|
+
<MenuItem label="Option 1" />
|
|
34
|
+
<MenuItemDivider />
|
|
35
|
+
<MenuItem label="Delete" kind="danger" />
|
|
36
|
+
</OverflowMenu>
|
|
37
|
+
</FeatureFlags>
|
|
38
|
+
|
|
39
|
+
{/* Other components - should not be transformed */}
|
|
40
|
+
<Button>Normal button</Button>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default TestComponent;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { FC } from 'react';
|
|
4
|
+
import { OverflowMenu, OverflowMenuItem } from '@carbon/react';
|
|
5
|
+
|
|
6
|
+
interface MenuItem {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
action?: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
items: MenuItem[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const TestComponent: FC<Props> = ({ items }) => {
|
|
17
|
+
return (
|
|
18
|
+
<div>
|
|
19
|
+
{/* Old API usage - mapped items */}
|
|
20
|
+
<OverflowMenu aria-label="mapped-menu">
|
|
21
|
+
{items.map((item) => (
|
|
22
|
+
<OverflowMenuItem
|
|
23
|
+
key={item.id}
|
|
24
|
+
itemText={item.label}
|
|
25
|
+
onClick={item.action}
|
|
26
|
+
/>
|
|
27
|
+
))}
|
|
28
|
+
</OverflowMenu>
|
|
29
|
+
{/* Old API - explicit props */}
|
|
30
|
+
<OverflowMenu direction="top" size="lg" flipped={true}>
|
|
31
|
+
<OverflowMenuItem hasDivider isDelete itemText="TypeScript Item" />
|
|
32
|
+
</OverflowMenu>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default TestComponent;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { OverflowMenu, OverflowMenuItem, MenuItem, MenuItemDivider, Button } from '@carbon/react';
|
|
4
|
+
import { FeatureFlags } from '@carbon/feature-flags';
|
|
5
|
+
|
|
6
|
+
function TestComponent({ menuProps }) {
|
|
7
|
+
return (
|
|
8
|
+
(<div>
|
|
9
|
+
{/* Old API usage with props - transformed */}
|
|
10
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
11
|
+
<OverflowMenu
|
|
12
|
+
label="overflow-menu"
|
|
13
|
+
align="bottom"
|
|
14
|
+
flipped={true}
|
|
15
|
+
light={true}
|
|
16
|
+
size="xl"
|
|
17
|
+
>
|
|
18
|
+
<MenuItem
|
|
19
|
+
className="test-class"
|
|
20
|
+
label="Stop app"
|
|
21
|
+
disabled={false}
|
|
22
|
+
onClick={() => {}}
|
|
23
|
+
/>
|
|
24
|
+
<MenuItem label="Restart app" />
|
|
25
|
+
<MenuItemDivider />
|
|
26
|
+
<MenuItem label="Delete app" kind="danger" />
|
|
27
|
+
</OverflowMenu>
|
|
28
|
+
</FeatureFlags>
|
|
29
|
+
|
|
30
|
+
{/* Old API with spread props */}
|
|
31
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
32
|
+
<OverflowMenu {...menuProps}>
|
|
33
|
+
<MenuItem label="Dynamic item" />
|
|
34
|
+
<MenuItemDivider />
|
|
35
|
+
<MenuItem label="Remove" kind="danger" />
|
|
36
|
+
</OverflowMenu>
|
|
37
|
+
</FeatureFlags>
|
|
38
|
+
|
|
39
|
+
{/* Already using new API - should not be transformed */}
|
|
40
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
41
|
+
<OverflowMenu label="Already migrated">
|
|
42
|
+
<MenuItem label="Option 1" />
|
|
43
|
+
<MenuItemDivider />
|
|
44
|
+
<MenuItem label="Delete" kind="danger" />
|
|
45
|
+
</OverflowMenu>
|
|
46
|
+
</FeatureFlags>
|
|
47
|
+
|
|
48
|
+
{/* Other components - unchanged */}
|
|
49
|
+
<Button>Normal button</Button>
|
|
50
|
+
</div>)
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default TestComponent;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { FeatureFlags } from '@carbon/feature-flags';
|
|
4
|
+
import type { FC } from 'react';
|
|
5
|
+
import { MenuItem, MenuItemDivider, OverflowMenu, OverflowMenuItem } from '@carbon/react';
|
|
6
|
+
|
|
7
|
+
interface MenuItem {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
action?: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
items: MenuItem[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const TestComponent: FC<Props> = ({ items }) => {
|
|
18
|
+
return (
|
|
19
|
+
(<div>
|
|
20
|
+
{/* Old API usage - mapped items */}
|
|
21
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
22
|
+
<OverflowMenu aria-label="mapped-menu">
|
|
23
|
+
{items.map((item) => (
|
|
24
|
+
<OverflowMenuItem
|
|
25
|
+
key={item.id}
|
|
26
|
+
itemText={item.label}
|
|
27
|
+
onClick={item.action}
|
|
28
|
+
/>
|
|
29
|
+
))}
|
|
30
|
+
</OverflowMenu>
|
|
31
|
+
</FeatureFlags>
|
|
32
|
+
{/* Old API - explicit props */}
|
|
33
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
34
|
+
<OverflowMenu direction="top" size="lg" flipped={true}>
|
|
35
|
+
<MenuItemDivider /><MenuItem kind='danger' label="TypeScript Item" />
|
|
36
|
+
</OverflowMenu>
|
|
37
|
+
</FeatureFlags>
|
|
38
|
+
</div>)
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default TestComponent;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Dropdown, Checkbox, Tag } from '@carbon/react';
|
|
3
|
+
|
|
4
|
+
function TestComponent() {
|
|
5
|
+
return (
|
|
6
|
+
//prettier-ignore
|
|
7
|
+
<div>
|
|
8
|
+
{/* Basic Dropdown usage */}
|
|
9
|
+
<Dropdown
|
|
10
|
+
label="Select an option"
|
|
11
|
+
slug="dropdown-1"
|
|
12
|
+
items={['Option 1', 'Option 2']}
|
|
13
|
+
id="dropdown-1"
|
|
14
|
+
titleText="Dropdown"
|
|
15
|
+
/>
|
|
16
|
+
{/* Checkbox with expression */}
|
|
17
|
+
<Checkbox
|
|
18
|
+
labelText="Check me"
|
|
19
|
+
slug={'checkbox-1'}
|
|
20
|
+
id="checkbox-1"
|
|
21
|
+
/>
|
|
22
|
+
{/* Tag with string literal */}
|
|
23
|
+
<Tag slug={'static-tag'} type="red">
|
|
24
|
+
Important
|
|
25
|
+
</Tag>
|
|
26
|
+
{/* Nested structure */}
|
|
27
|
+
<div>
|
|
28
|
+
<Tag slug="tag-1" type="blue">
|
|
29
|
+
Active
|
|
30
|
+
</Tag>
|
|
31
|
+
<Checkbox
|
|
32
|
+
slug="checkbox-2"
|
|
33
|
+
labelText="Enable feature"
|
|
34
|
+
id="checkbox-2"
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default TestComponent;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Dropdown, Checkbox, Tag } from '@carbon/react';
|
|
3
|
+
|
|
4
|
+
const TestComponent: React.FC = () => {
|
|
5
|
+
return (
|
|
6
|
+
//prettier-ignore
|
|
7
|
+
<div>
|
|
8
|
+
{/* Basic Dropdown usage */}
|
|
9
|
+
<Dropdown
|
|
10
|
+
label="Select an option"
|
|
11
|
+
slug="dropdown-1"
|
|
12
|
+
items={['Option 1', 'Option 2']}
|
|
13
|
+
id="dropdown-1"
|
|
14
|
+
titleText="Dropdown"
|
|
15
|
+
/>
|
|
16
|
+
{/* Checkbox with expression */}
|
|
17
|
+
<Checkbox
|
|
18
|
+
labelText="Check me"
|
|
19
|
+
slug={'checkbox-1'}
|
|
20
|
+
id="checkbox-1"
|
|
21
|
+
/>
|
|
22
|
+
{/* Tag with string literal */}
|
|
23
|
+
<Tag slug={'static-tag'} type="red">
|
|
24
|
+
Important
|
|
25
|
+
</Tag>
|
|
26
|
+
{/* Nested structure */}
|
|
27
|
+
<div>
|
|
28
|
+
<Tag slug="tag-1" type="blue">
|
|
29
|
+
Active
|
|
30
|
+
</Tag>
|
|
31
|
+
<Checkbox
|
|
32
|
+
slug="checkbox-2"
|
|
33
|
+
labelText="Enable feature"
|
|
34
|
+
id="checkbox-2"
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default TestComponent;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Dropdown, Checkbox, Tag } from '@carbon/react';
|
|
3
|
+
|
|
4
|
+
function TestComponent() {
|
|
5
|
+
return (
|
|
6
|
+
//prettier-ignore
|
|
7
|
+
(<div>
|
|
8
|
+
{/* Basic Dropdown usage */}
|
|
9
|
+
<Dropdown
|
|
10
|
+
label="Select an option"
|
|
11
|
+
decorator="dropdown-1"
|
|
12
|
+
items={['Option 1', 'Option 2']}
|
|
13
|
+
id="dropdown-1"
|
|
14
|
+
titleText="Dropdown"
|
|
15
|
+
/>
|
|
16
|
+
{/* Checkbox with expression */}
|
|
17
|
+
<Checkbox
|
|
18
|
+
labelText="Check me"
|
|
19
|
+
decorator={'checkbox-1'}
|
|
20
|
+
id="checkbox-1"
|
|
21
|
+
/>
|
|
22
|
+
{/* Tag with string literal */}
|
|
23
|
+
<Tag decorator={'static-tag'} type="red">
|
|
24
|
+
Important
|
|
25
|
+
</Tag>
|
|
26
|
+
{/* Nested structure */}
|
|
27
|
+
<div>
|
|
28
|
+
<Tag decorator="tag-1" type="blue">
|
|
29
|
+
Active
|
|
30
|
+
</Tag>
|
|
31
|
+
<Checkbox
|
|
32
|
+
decorator="checkbox-2"
|
|
33
|
+
labelText="Enable feature"
|
|
34
|
+
id="checkbox-2"
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
</div>)
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default TestComponent;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Dropdown, Checkbox, Tag } from '@carbon/react';
|
|
3
|
+
|
|
4
|
+
const TestComponent: React.FC = () => {
|
|
5
|
+
return (
|
|
6
|
+
//prettier-ignore
|
|
7
|
+
(<div>
|
|
8
|
+
{/* Basic Dropdown usage */}
|
|
9
|
+
<Dropdown
|
|
10
|
+
label="Select an option"
|
|
11
|
+
decorator="dropdown-1"
|
|
12
|
+
items={['Option 1', 'Option 2']}
|
|
13
|
+
id="dropdown-1"
|
|
14
|
+
titleText="Dropdown"
|
|
15
|
+
/>
|
|
16
|
+
{/* Checkbox with expression */}
|
|
17
|
+
<Checkbox
|
|
18
|
+
labelText="Check me"
|
|
19
|
+
decorator={'checkbox-1'}
|
|
20
|
+
id="checkbox-1"
|
|
21
|
+
/>
|
|
22
|
+
{/* Tag with string literal */}
|
|
23
|
+
<Tag decorator={'static-tag'} type="red">
|
|
24
|
+
Important
|
|
25
|
+
</Tag>
|
|
26
|
+
{/* Nested structure */}
|
|
27
|
+
<div>
|
|
28
|
+
<Tag decorator="tag-1" type="blue">
|
|
29
|
+
Active
|
|
30
|
+
</Tag>
|
|
31
|
+
<Checkbox
|
|
32
|
+
decorator="checkbox-2"
|
|
33
|
+
labelText="Enable feature"
|
|
34
|
+
id="checkbox-2"
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
</div>)
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default TestComponent;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2025
|
|
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
|
+
// Test with wrapping (default)
|
|
13
|
+
defineTest(
|
|
14
|
+
__dirname,
|
|
15
|
+
'enable-v12-overflowmenu',
|
|
16
|
+
{ wrapWithFeatureFlag: 'true' },
|
|
17
|
+
'enable-v12-overflowmenu'
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// Test without wrapping
|
|
21
|
+
defineTest(
|
|
22
|
+
__dirname,
|
|
23
|
+
'enable-v12-overflowmenu',
|
|
24
|
+
{ wrapWithFeatureFlag: 'false' },
|
|
25
|
+
'enable-v12-overflowmenu-nowrap'
|
|
26
|
+
);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2025
|
|
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, 'slug-prop-to-decorator-prop');
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2025
|
|
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
|
+
* Migrate OverflowMenu components to v12 API
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const defaultOptions = {
|
|
13
|
+
quote: 'single',
|
|
14
|
+
trailingComma: true,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Props mapping from OverflowMenuItem to MenuItem
|
|
18
|
+
const MENU_ITEM_PROPS_MAP = {
|
|
19
|
+
itemText: 'label',
|
|
20
|
+
href: 'href',
|
|
21
|
+
disabled: 'disabled',
|
|
22
|
+
className: 'className',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const EVENT_HANDLERS = new Set(['onClick']);
|
|
26
|
+
|
|
27
|
+
function transform(fileInfo, api, options) {
|
|
28
|
+
const { jscodeshift: j } = api;
|
|
29
|
+
const root = j(fileInfo.source);
|
|
30
|
+
const printOptions = options.printOptions || defaultOptions;
|
|
31
|
+
const shouldWrapWithFlags = options.wrapWithFeatureFlag !== false;
|
|
32
|
+
const overflowMenuElements = root
|
|
33
|
+
.find(j.JSXElement, {
|
|
34
|
+
openingElement: { name: { name: 'OverflowMenu' } },
|
|
35
|
+
})
|
|
36
|
+
.filter((path) => {
|
|
37
|
+
let parent = path.parent;
|
|
38
|
+
while (parent) {
|
|
39
|
+
if (
|
|
40
|
+
parent.node.type === 'JSXElement' &&
|
|
41
|
+
parent.node.openingElement.name.name === 'FeatureFlags'
|
|
42
|
+
) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
parent = parent.parent;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (!overflowMenuElements.length) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Add required imports
|
|
55
|
+
const importsToAdd = ['MenuItem', 'MenuItemDivider'].sort();
|
|
56
|
+
const carbonImport = root.find(j.ImportDeclaration, {
|
|
57
|
+
source: { value: '@carbon/react' },
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (carbonImport.length) {
|
|
61
|
+
const importNode = carbonImport.get(0);
|
|
62
|
+
const existingSpecifiers = new Set(
|
|
63
|
+
importNode.node.specifiers
|
|
64
|
+
.filter((spec) => spec.type === 'ImportSpecifier')
|
|
65
|
+
.map((spec) => spec.imported.name)
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
importsToAdd.forEach((importName) => {
|
|
69
|
+
if (!existingSpecifiers.has(importName)) {
|
|
70
|
+
importNode.node.specifiers.push(
|
|
71
|
+
j.importSpecifier(j.identifier(importName))
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Sort specifiers alphabetically
|
|
77
|
+
importNode.node.specifiers.sort((a, b) =>
|
|
78
|
+
a.imported.name.localeCompare(b.imported.name)
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function transformOverflowMenuItems(elements) {
|
|
83
|
+
elements.forEach((path) => {
|
|
84
|
+
path.node.children.forEach((child, index) => {
|
|
85
|
+
if (
|
|
86
|
+
child.type === 'JSXElement' &&
|
|
87
|
+
child.openingElement.name.name === 'OverflowMenuItem'
|
|
88
|
+
) {
|
|
89
|
+
const itemProps = [];
|
|
90
|
+
let needsDivider = false;
|
|
91
|
+
let classNames = [];
|
|
92
|
+
|
|
93
|
+
child.openingElement.attributes.forEach((attr) => {
|
|
94
|
+
if (attr.type === 'JSXSpreadAttribute') {
|
|
95
|
+
itemProps.push(attr);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const propName = attr.name.name;
|
|
100
|
+
|
|
101
|
+
if (MENU_ITEM_PROPS_MAP[propName]) {
|
|
102
|
+
if (propName === 'className') {
|
|
103
|
+
classNames.push(attr.value.value);
|
|
104
|
+
} else {
|
|
105
|
+
itemProps.push(
|
|
106
|
+
j.jsxAttribute(
|
|
107
|
+
j.jsxIdentifier(MENU_ITEM_PROPS_MAP[propName]),
|
|
108
|
+
attr.value
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
} else if (propName === 'wrapperClassName') {
|
|
113
|
+
classNames.push(attr.value.value);
|
|
114
|
+
} else if (propName === 'hasDivider') {
|
|
115
|
+
needsDivider = true;
|
|
116
|
+
} else if (propName === 'isDelete') {
|
|
117
|
+
itemProps.push(
|
|
118
|
+
j.jsxAttribute(
|
|
119
|
+
j.jsxIdentifier('kind'),
|
|
120
|
+
j.stringLiteral('danger')
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
} else if (EVENT_HANDLERS.has(propName)) {
|
|
124
|
+
itemProps.push(attr);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (classNames.length > 0) {
|
|
129
|
+
itemProps.push(
|
|
130
|
+
j.jsxAttribute(
|
|
131
|
+
j.jsxIdentifier('className'),
|
|
132
|
+
j.stringLiteral(classNames.join(' '))
|
|
133
|
+
)
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (needsDivider) {
|
|
138
|
+
path.node.children.splice(
|
|
139
|
+
index,
|
|
140
|
+
0,
|
|
141
|
+
j.jsxElement(
|
|
142
|
+
j.jsxOpeningElement(
|
|
143
|
+
j.jsxIdentifier('MenuItemDivider'),
|
|
144
|
+
[],
|
|
145
|
+
true
|
|
146
|
+
),
|
|
147
|
+
null,
|
|
148
|
+
[],
|
|
149
|
+
true
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
child.openingElement.name = j.jsxIdentifier('MenuItem');
|
|
155
|
+
child.openingElement.attributes = itemProps;
|
|
156
|
+
if (child.closingElement) {
|
|
157
|
+
child.closingElement.name = j.jsxIdentifier('MenuItem');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function addFeatureFlagsImport() {
|
|
165
|
+
const hasFeatureFlagsImport = root
|
|
166
|
+
.find(j.ImportDeclaration)
|
|
167
|
+
.some(
|
|
168
|
+
(path) =>
|
|
169
|
+
path.node.source.value === '@carbon/feature-flags' &&
|
|
170
|
+
path.node.specifiers.some(
|
|
171
|
+
(spec) => spec.imported && spec.imported.name === 'FeatureFlags'
|
|
172
|
+
)
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
if (!hasFeatureFlagsImport) {
|
|
176
|
+
const featureFlagsImport = j.importDeclaration(
|
|
177
|
+
[j.importSpecifier(j.identifier('FeatureFlags'))],
|
|
178
|
+
j.literal('@carbon/feature-flags')
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const firstImport = root.find(j.ImportDeclaration).at(0);
|
|
182
|
+
if (firstImport.length) {
|
|
183
|
+
firstImport.insertAfter(featureFlagsImport);
|
|
184
|
+
} else {
|
|
185
|
+
root.get().node.program.body.unshift(featureFlagsImport);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function wrapWithFeatureFlags(elements) {
|
|
191
|
+
elements.forEach((path) => {
|
|
192
|
+
const wrappedElement = j.jsxElement(
|
|
193
|
+
j.jsxOpeningElement(
|
|
194
|
+
j.jsxIdentifier('FeatureFlags'),
|
|
195
|
+
[j.jsxAttribute(j.jsxIdentifier('enableV12Overflowmenu'))],
|
|
196
|
+
false
|
|
197
|
+
),
|
|
198
|
+
j.jsxClosingElement(j.jsxIdentifier('FeatureFlags')),
|
|
199
|
+
[j.jsxText('\n '), path.node, j.jsxText('\n ')]
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
j(path).replaceWith(wrappedElement);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Transform based on wrap option
|
|
207
|
+
transformOverflowMenuItems(overflowMenuElements);
|
|
208
|
+
|
|
209
|
+
if (shouldWrapWithFlags) {
|
|
210
|
+
addFeatureFlagsImport();
|
|
211
|
+
wrapWithFeatureFlags(overflowMenuElements);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return root.toSource(printOptions);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
module.exports = transform;
|
|
218
|
+
module.exports.parser = 'tsx';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2025
|
|
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
|
+
* Replace slug prop with decorator
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const defaultOptions = {
|
|
13
|
+
quote: 'single',
|
|
14
|
+
trailingComma: true,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function transform(fileInfo, api, options) {
|
|
18
|
+
const j = api.jscodeshift;
|
|
19
|
+
const root = j(fileInfo.source);
|
|
20
|
+
const printOptions = options.printOptions || defaultOptions;
|
|
21
|
+
|
|
22
|
+
// Early return if no JSX elements with slug prop found
|
|
23
|
+
if (!root.find(j.JSXAttribute, { name: { name: 'slug' } }).size()) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Replace slug with decorator
|
|
28
|
+
root
|
|
29
|
+
.find(j.JSXAttribute, {
|
|
30
|
+
name: { name: 'slug' },
|
|
31
|
+
})
|
|
32
|
+
.forEach((path) => {
|
|
33
|
+
// Create new decorator attribute with same value as slug
|
|
34
|
+
const newAttribute = j.jsxAttribute(
|
|
35
|
+
j.jsxIdentifier('decorator'),
|
|
36
|
+
path.node.value
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// Replace the slug attribute with decorator
|
|
40
|
+
j(path).replaceWith(newAttribute);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return root.toSource(printOptions);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = transform;
|
|
47
|
+
module.exports.parser = 'tsx';
|