@carbon/upgrade 11.42.0 → 11.43.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/README.md +38 -0
- package/cli.js +42 -2
- package/package.json +2 -2
- package/transforms/__testfixtures__/enable-v12-release-aliased.input.tsx +20 -0
- package/transforms/__testfixtures__/enable-v12-release-aliased.output.tsx +22 -0
- package/transforms/__testfixtures__/enable-v12-release.input.tsx +22 -0
- package/transforms/__testfixtures__/enable-v12-release.output.tsx +30 -0
- package/transforms/__tests__/enable-v12-release-test.js +35 -0
- package/transforms/enable-v12-release.js +406 -0
package/README.md
CHANGED
|
@@ -81,6 +81,44 @@ The following codemods help you adopt changes introduced by feature flags in
|
|
|
81
81
|
preparation for Carbon v12. Each codemod transforms your code to work with
|
|
82
82
|
specific feature flags enabled.
|
|
83
83
|
|
|
84
|
+
### Enable v12 release
|
|
85
|
+
|
|
86
|
+
Enables the complete Carbon v12 experience for a React application.
|
|
87
|
+
|
|
88
|
+
**Usage:**
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npx @carbon/upgrade migrate enable-v12-release --write
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
This codemod wraps the application passed to a React root with
|
|
95
|
+
`<FeatureFlags enableV12Release>` and adds or updates the necessary
|
|
96
|
+
`@carbon/react` import. It supports modern `createRoot` and `hydrateRoot` entry
|
|
97
|
+
points as well as legacy `ReactDOM.render`.
|
|
98
|
+
|
|
99
|
+
**Example:**
|
|
100
|
+
|
|
101
|
+
```jsx
|
|
102
|
+
// Before
|
|
103
|
+
import { createRoot } from 'react-dom/client';
|
|
104
|
+
import App from './App';
|
|
105
|
+
|
|
106
|
+
const root = createRoot(document.getElementById('root'));
|
|
107
|
+
root.render(<App />);
|
|
108
|
+
|
|
109
|
+
// After
|
|
110
|
+
import { createRoot } from 'react-dom/client';
|
|
111
|
+
import { FeatureFlags } from '@carbon/react';
|
|
112
|
+
import App from './App';
|
|
113
|
+
|
|
114
|
+
const root = createRoot(document.getElementById('root'));
|
|
115
|
+
root.render(
|
|
116
|
+
<FeatureFlags enableV12Release>
|
|
117
|
+
<App />
|
|
118
|
+
</FeatureFlags>
|
|
119
|
+
);
|
|
120
|
+
```
|
|
121
|
+
|
|
84
122
|
### Enable v12 tile default icons
|
|
85
123
|
|
|
86
124
|
Enables default icons for Tile components.
|
package/cli.js
CHANGED
|
@@ -44083,7 +44083,7 @@ async function run$1(options) {
|
|
|
44083
44083
|
//#endregion
|
|
44084
44084
|
//#region src/upgrades.js
|
|
44085
44085
|
/**
|
|
44086
|
-
* Copyright IBM Corp. 2019,
|
|
44086
|
+
* Copyright IBM Corp. 2019, 2026
|
|
44087
44087
|
*
|
|
44088
44088
|
* This source code is licensed under the Apache-2.0 license found in the
|
|
44089
44089
|
* LICENSE file in the root directory of this source tree.
|
|
@@ -44364,6 +44364,46 @@ const upgrades = [
|
|
|
44364
44364
|
});
|
|
44365
44365
|
}
|
|
44366
44366
|
},
|
|
44367
|
+
{
|
|
44368
|
+
name: "enable-v12-release",
|
|
44369
|
+
description: `
|
|
44370
|
+
Wrap the application rendered by ReactDOM with FeatureFlags enableV12Release.
|
|
44371
|
+
|
|
44372
|
+
Transforms:
|
|
44373
|
+
root.render(<App />);
|
|
44374
|
+
|
|
44375
|
+
Into:
|
|
44376
|
+
root.render(
|
|
44377
|
+
<FeatureFlags enableV12Release>
|
|
44378
|
+
<App />
|
|
44379
|
+
</FeatureFlags>
|
|
44380
|
+
);
|
|
44381
|
+
`,
|
|
44382
|
+
migrate: async (options) => {
|
|
44383
|
+
const transform = path.default.join(TRANSFORM_DIR, "enable-v12-release.js");
|
|
44384
|
+
const paths = Array.isArray(options.paths) && options.paths.length > 0 ? options.paths : await (0, import_out.default)(["**/*.{js,jsx,ts,tsx}"], {
|
|
44385
|
+
cwd: options.workspaceDir,
|
|
44386
|
+
ignore: [
|
|
44387
|
+
"**/es/**",
|
|
44388
|
+
"**/lib/**",
|
|
44389
|
+
"**/umd/**",
|
|
44390
|
+
"**/node_modules/**",
|
|
44391
|
+
"**/storybook-static/**",
|
|
44392
|
+
"**/dist/**",
|
|
44393
|
+
"**/build/**",
|
|
44394
|
+
"**/*.d.ts",
|
|
44395
|
+
"**/coverage/**"
|
|
44396
|
+
]
|
|
44397
|
+
});
|
|
44398
|
+
await runCodemod(options, {
|
|
44399
|
+
dry: !options.write,
|
|
44400
|
+
transform,
|
|
44401
|
+
paths,
|
|
44402
|
+
verbose: options.verbose,
|
|
44403
|
+
parser: "tsx"
|
|
44404
|
+
});
|
|
44405
|
+
}
|
|
44406
|
+
},
|
|
44367
44407
|
{
|
|
44368
44408
|
name: "enable-v12-overflowmenu",
|
|
44369
44409
|
description: `
|
|
@@ -44964,7 +45004,7 @@ const upgrades = [
|
|
|
44964
45004
|
//#endregion
|
|
44965
45005
|
//#region package.json
|
|
44966
45006
|
var name = "@carbon/upgrade";
|
|
44967
|
-
var version = "11.
|
|
45007
|
+
var version = "11.43.0-rc.0";
|
|
44968
45008
|
//#endregion
|
|
44969
45009
|
//#region ../../node_modules/y18n/build/index.cjs
|
|
44970
45010
|
var require_build$3 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
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.43.0-rc.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
7
7
|
"carbon-upgrade": "./bin/carbon-upgrade.js"
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"@ibm/telemetry-js": "^1.5.0",
|
|
61
61
|
"jscodeshift": "^17.0.0"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "82f04fbfdb385323883f4f8e3ce97a14865a744a"
|
|
64
64
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as Carbon from '@carbon/react';
|
|
2
|
+
import { createRoot as mountRoot } from 'react-dom/client';
|
|
3
|
+
import App from './App';
|
|
4
|
+
|
|
5
|
+
const root = mountRoot(document.getElementById('root')!);
|
|
6
|
+
root.render(
|
|
7
|
+
<Carbon.FeatureFlags enableV12Release={false} {...featureFlags}>
|
|
8
|
+
<App />
|
|
9
|
+
</Carbon.FeatureFlags>
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
const unwrappedRoot = mountRoot(document.getElementById('unwrapped-root')!);
|
|
13
|
+
unwrappedRoot.render(<App />);
|
|
14
|
+
|
|
15
|
+
const wrappedRoot = mountRoot(document.getElementById('wrapped-root')!);
|
|
16
|
+
wrappedRoot.render(
|
|
17
|
+
<Carbon.FeatureFlags>
|
|
18
|
+
<App />
|
|
19
|
+
</Carbon.FeatureFlags>
|
|
20
|
+
);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as Carbon from '@carbon/react';
|
|
2
|
+
import { createRoot as mountRoot } from 'react-dom/client';
|
|
3
|
+
import App from './App';
|
|
4
|
+
|
|
5
|
+
const root = mountRoot(document.getElementById('root')!);
|
|
6
|
+
root.render(
|
|
7
|
+
<Carbon.FeatureFlags {...featureFlags} enableV12Release>
|
|
8
|
+
<App />
|
|
9
|
+
</Carbon.FeatureFlags>
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
const unwrappedRoot = mountRoot(document.getElementById('unwrapped-root')!);
|
|
13
|
+
unwrappedRoot.render(<Carbon.FeatureFlags enableV12Release>
|
|
14
|
+
<App />
|
|
15
|
+
</Carbon.FeatureFlags>);
|
|
16
|
+
|
|
17
|
+
const wrappedRoot = mountRoot(document.getElementById('wrapped-root')!);
|
|
18
|
+
wrappedRoot.render(
|
|
19
|
+
<Carbon.FeatureFlags enableV12Release>
|
|
20
|
+
<App />
|
|
21
|
+
</Carbon.FeatureFlags>
|
|
22
|
+
);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom';
|
|
3
|
+
import ReactDOMClient, { createRoot, hydrateRoot } from 'react-dom/client';
|
|
4
|
+
import { Button } from '@carbon/react';
|
|
5
|
+
import App from './App';
|
|
6
|
+
|
|
7
|
+
const root = createRoot(document.getElementById('root'));
|
|
8
|
+
root.render(
|
|
9
|
+
<React.StrictMode>
|
|
10
|
+
<App />
|
|
11
|
+
</React.StrictMode>
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
ReactDOM.render(<App />, document.getElementById('legacy-root'));
|
|
15
|
+
|
|
16
|
+
hydrateRoot(document.getElementById('hydrated-root'), <App />);
|
|
17
|
+
|
|
18
|
+
ReactDOMClient.createRoot(document.getElementById('chained-root')).render(
|
|
19
|
+
<App />
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const preview = <App />;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom';
|
|
3
|
+
import ReactDOMClient, { createRoot, hydrateRoot } from 'react-dom/client';
|
|
4
|
+
import { Button, FeatureFlags } from '@carbon/react';
|
|
5
|
+
import App from './App';
|
|
6
|
+
|
|
7
|
+
const root = createRoot(document.getElementById('root'));
|
|
8
|
+
root.render(
|
|
9
|
+
<FeatureFlags enableV12Release>
|
|
10
|
+
<React.StrictMode>
|
|
11
|
+
<App />
|
|
12
|
+
</React.StrictMode>
|
|
13
|
+
</FeatureFlags>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
ReactDOM.render(<FeatureFlags enableV12Release>
|
|
17
|
+
<App />
|
|
18
|
+
</FeatureFlags>, document.getElementById('legacy-root'));
|
|
19
|
+
|
|
20
|
+
hydrateRoot(document.getElementById('hydrated-root'), <FeatureFlags enableV12Release>
|
|
21
|
+
<App />
|
|
22
|
+
</FeatureFlags>);
|
|
23
|
+
|
|
24
|
+
ReactDOMClient.createRoot(document.getElementById('chained-root')).render(
|
|
25
|
+
<FeatureFlags enableV12Release>
|
|
26
|
+
<App />
|
|
27
|
+
</FeatureFlags>
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const preview = <App />;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment node
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Copyright IBM Corp. 2026
|
|
7
|
+
*
|
|
8
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
9
|
+
* LICENSE file in the root directory of this source tree.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const { defineTest, applyTransform } = require('jscodeshift/dist/testUtils');
|
|
15
|
+
const transform = require('../enable-v12-release');
|
|
16
|
+
|
|
17
|
+
defineTest(__dirname, 'enable-v12-release');
|
|
18
|
+
defineTest(__dirname, 'enable-v12-release', null, 'enable-v12-release-aliased');
|
|
19
|
+
|
|
20
|
+
it('does not change an application with the v12 release flag enabled', () => {
|
|
21
|
+
const source = `
|
|
22
|
+
import { FeatureFlags } from '@carbon/react';
|
|
23
|
+
import { createRoot } from 'react-dom/client';
|
|
24
|
+
import App from './App';
|
|
25
|
+
|
|
26
|
+
const root = createRoot(document.getElementById('root'));
|
|
27
|
+
root.render(
|
|
28
|
+
<FeatureFlags enableV12Release>
|
|
29
|
+
<App />
|
|
30
|
+
</FeatureFlags>
|
|
31
|
+
);
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
expect(applyTransform(transform, {}, { source })).toBe('');
|
|
35
|
+
});
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2026
|
|
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
|
+
/**
|
|
9
|
+
* Wrap the application passed to a React root with the v12 release feature flag.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const defaultOptions = {
|
|
15
|
+
quote: 'single',
|
|
16
|
+
trailingComma: true,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function transform(fileInfo, api, options) {
|
|
20
|
+
const j = api.jscodeshift;
|
|
21
|
+
const root = j(fileInfo.source);
|
|
22
|
+
const printOptions = options.printOptions || defaultOptions;
|
|
23
|
+
const reactDomBindings = getReactDomBindings(root, j);
|
|
24
|
+
const reactRootBindings = getReactRootBindings(root, j, reactDomBindings);
|
|
25
|
+
const renderArguments = findRenderArguments(
|
|
26
|
+
root,
|
|
27
|
+
j,
|
|
28
|
+
reactDomBindings,
|
|
29
|
+
reactRootBindings
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if (renderArguments.length === 0) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const featureFlags = getFeatureFlagsReference(root, j);
|
|
37
|
+
let didChange = false;
|
|
38
|
+
|
|
39
|
+
for (const argumentPath of renderArguments) {
|
|
40
|
+
const application = argumentPath.node;
|
|
41
|
+
|
|
42
|
+
if (isNamedJSXElement(application, featureFlags.jsxName)) {
|
|
43
|
+
didChange = enableV12Release(application.openingElement, j) || didChange;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
argumentPath.replace(
|
|
48
|
+
createFeatureFlagsWrapper(application, featureFlags, j)
|
|
49
|
+
);
|
|
50
|
+
didChange = true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!didChange) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (featureFlags.needsImport) {
|
|
58
|
+
addFeatureFlagsImport(root, featureFlags, j);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return root.toSource(printOptions);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getReactDomBindings(root, j) {
|
|
65
|
+
const bindings = {
|
|
66
|
+
createRoot: new Set(),
|
|
67
|
+
hydrateRoot: new Set(),
|
|
68
|
+
render: new Set(),
|
|
69
|
+
reactDom: new Set(),
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
root
|
|
73
|
+
.find(j.ImportDeclaration)
|
|
74
|
+
.filter(
|
|
75
|
+
(path) =>
|
|
76
|
+
path.node.source.value === 'react-dom' ||
|
|
77
|
+
path.node.source.value === 'react-dom/client'
|
|
78
|
+
)
|
|
79
|
+
.forEach((path) => {
|
|
80
|
+
const source = path.node.source.value;
|
|
81
|
+
|
|
82
|
+
for (const specifier of path.node.specifiers) {
|
|
83
|
+
if (
|
|
84
|
+
specifier.type === 'ImportDefaultSpecifier' ||
|
|
85
|
+
specifier.type === 'ImportNamespaceSpecifier'
|
|
86
|
+
) {
|
|
87
|
+
bindings.reactDom.add(specifier.local.name);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (
|
|
92
|
+
specifier.type !== 'ImportSpecifier' ||
|
|
93
|
+
path.node.importKind === 'type' ||
|
|
94
|
+
specifier.importKind === 'type'
|
|
95
|
+
) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const importedName = specifier.imported.name;
|
|
100
|
+
if (
|
|
101
|
+
source === 'react-dom/client' &&
|
|
102
|
+
(importedName === 'createRoot' || importedName === 'hydrateRoot')
|
|
103
|
+
) {
|
|
104
|
+
bindings[importedName].add(specifier.local.name);
|
|
105
|
+
} else if (source === 'react-dom' && importedName === 'render') {
|
|
106
|
+
bindings.render.add(specifier.local.name);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return bindings;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function getReactRootBindings(root, j, reactDomBindings) {
|
|
115
|
+
const rootBindings = new Set();
|
|
116
|
+
|
|
117
|
+
root.find(j.VariableDeclarator).forEach((path) => {
|
|
118
|
+
if (
|
|
119
|
+
path.node.id.type === 'Identifier' &&
|
|
120
|
+
isCreateRootCall(path.node.init, reactDomBindings)
|
|
121
|
+
) {
|
|
122
|
+
rootBindings.add(path.node.id.name);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
root.find(j.AssignmentExpression).forEach((path) => {
|
|
127
|
+
if (
|
|
128
|
+
path.node.left.type === 'Identifier' &&
|
|
129
|
+
isCreateRootCall(path.node.right, reactDomBindings)
|
|
130
|
+
) {
|
|
131
|
+
rootBindings.add(path.node.left.name);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return rootBindings;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function findRenderArguments(root, j, reactDomBindings, reactRootBindings) {
|
|
139
|
+
const argumentsToWrap = [];
|
|
140
|
+
|
|
141
|
+
root.find(j.CallExpression).forEach((path) => {
|
|
142
|
+
const { callee } = path.node;
|
|
143
|
+
|
|
144
|
+
if (isHydrateRootCall(path.node, reactDomBindings)) {
|
|
145
|
+
addJSXArgument(path, 1, argumentsToWrap);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (
|
|
150
|
+
callee.type === 'Identifier' &&
|
|
151
|
+
reactDomBindings.render.has(callee.name)
|
|
152
|
+
) {
|
|
153
|
+
addJSXArgument(path, 0, argumentsToWrap);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (!isMemberCall(callee, 'render') && !isMemberCall(callee, 'hydrate')) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (
|
|
162
|
+
(callee.object.type === 'Identifier' &&
|
|
163
|
+
(reactRootBindings.has(callee.object.name) ||
|
|
164
|
+
reactDomBindings.reactDom.has(callee.object.name))) ||
|
|
165
|
+
isCreateRootCall(callee.object, reactDomBindings)
|
|
166
|
+
) {
|
|
167
|
+
addJSXArgument(path, 0, argumentsToWrap);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return argumentsToWrap;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function addJSXArgument(callPath, argumentIndex, argumentsToWrap) {
|
|
175
|
+
const argument = callPath.node.arguments[argumentIndex];
|
|
176
|
+
if (argument?.type === 'JSXElement' || argument?.type === 'JSXFragment') {
|
|
177
|
+
argumentsToWrap.push(callPath.get('arguments', argumentIndex));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function isCreateRootCall(node, reactDomBindings) {
|
|
182
|
+
if (!node || node.type !== 'CallExpression') {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (
|
|
187
|
+
node.callee.type === 'Identifier' &&
|
|
188
|
+
reactDomBindings.createRoot.has(node.callee.name)
|
|
189
|
+
) {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
isMemberCall(node.callee, 'createRoot') &&
|
|
195
|
+
node.callee.object.type === 'Identifier' &&
|
|
196
|
+
reactDomBindings.reactDom.has(node.callee.object.name)
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function isHydrateRootCall(node, reactDomBindings) {
|
|
201
|
+
if (node.callee.type === 'Identifier') {
|
|
202
|
+
return reactDomBindings.hydrateRoot.has(node.callee.name);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
isMemberCall(node.callee, 'hydrateRoot') &&
|
|
207
|
+
node.callee.object.type === 'Identifier' &&
|
|
208
|
+
reactDomBindings.reactDom.has(node.callee.object.name)
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function isMemberCall(callee, propertyName) {
|
|
213
|
+
if (
|
|
214
|
+
callee.type !== 'MemberExpression' &&
|
|
215
|
+
callee.type !== 'OptionalMemberExpression'
|
|
216
|
+
) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (callee.computed) {
|
|
221
|
+
return (
|
|
222
|
+
(callee.property.type === 'Literal' ||
|
|
223
|
+
callee.property.type === 'StringLiteral') &&
|
|
224
|
+
callee.property.value === propertyName
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
callee.property.type === 'Identifier' &&
|
|
230
|
+
callee.property.name === propertyName
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function getFeatureFlagsReference(root, j) {
|
|
235
|
+
const carbonImports = root
|
|
236
|
+
.find(j.ImportDeclaration, {
|
|
237
|
+
source: { value: '@carbon/react' },
|
|
238
|
+
})
|
|
239
|
+
.paths();
|
|
240
|
+
|
|
241
|
+
for (const path of carbonImports) {
|
|
242
|
+
if (path.node.importKind === 'type') {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
for (const specifier of path.node.specifiers) {
|
|
247
|
+
if (
|
|
248
|
+
specifier.type === 'ImportSpecifier' &&
|
|
249
|
+
specifier.importKind !== 'type' &&
|
|
250
|
+
specifier.imported.name === 'FeatureFlags'
|
|
251
|
+
) {
|
|
252
|
+
return {
|
|
253
|
+
importName: specifier.local.name,
|
|
254
|
+
jsxName: j.jsxIdentifier(specifier.local.name),
|
|
255
|
+
needsImport: false,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (specifier.type === 'ImportNamespaceSpecifier') {
|
|
260
|
+
return {
|
|
261
|
+
importName: null,
|
|
262
|
+
jsxName: j.jsxMemberExpression(
|
|
263
|
+
j.jsxIdentifier(specifier.local.name),
|
|
264
|
+
j.jsxIdentifier('FeatureFlags')
|
|
265
|
+
),
|
|
266
|
+
needsImport: false,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const importName = getAvailableImportName(root, j);
|
|
273
|
+
return {
|
|
274
|
+
importName,
|
|
275
|
+
jsxName: j.jsxIdentifier(importName),
|
|
276
|
+
needsImport: true,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function getAvailableImportName(root, j) {
|
|
281
|
+
let importName = 'FeatureFlags';
|
|
282
|
+
let suffix = 2;
|
|
283
|
+
|
|
284
|
+
while (
|
|
285
|
+
root.find(j.Identifier, { name: importName }).size() > 0 ||
|
|
286
|
+
root.find(j.JSXIdentifier, { name: importName }).size() > 0
|
|
287
|
+
) {
|
|
288
|
+
importName = `CarbonFeatureFlags${suffix === 2 ? '' : suffix}`;
|
|
289
|
+
suffix++;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return importName;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function addFeatureFlagsImport(root, featureFlags, j) {
|
|
296
|
+
const carbonImport = root
|
|
297
|
+
.find(j.ImportDeclaration, {
|
|
298
|
+
source: { value: '@carbon/react' },
|
|
299
|
+
})
|
|
300
|
+
.filter(
|
|
301
|
+
(path) =>
|
|
302
|
+
path.node.importKind !== 'type' &&
|
|
303
|
+
!path.node.specifiers.some(
|
|
304
|
+
(specifier) => specifier.type === 'ImportNamespaceSpecifier'
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
.at(0);
|
|
308
|
+
const local =
|
|
309
|
+
featureFlags.importName === 'FeatureFlags'
|
|
310
|
+
? null
|
|
311
|
+
: j.identifier(featureFlags.importName);
|
|
312
|
+
const specifier = j.importSpecifier(j.identifier('FeatureFlags'), local);
|
|
313
|
+
|
|
314
|
+
if (carbonImport.size() > 0) {
|
|
315
|
+
carbonImport.get().node.specifiers.push(specifier);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const declaration = j.importDeclaration(
|
|
320
|
+
[specifier],
|
|
321
|
+
j.literal('@carbon/react')
|
|
322
|
+
);
|
|
323
|
+
const lastImport = root.find(j.ImportDeclaration).at(-1);
|
|
324
|
+
|
|
325
|
+
if (lastImport.size() > 0) {
|
|
326
|
+
lastImport.insertAfter(declaration);
|
|
327
|
+
} else {
|
|
328
|
+
root.get().node.program.body.unshift(declaration);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function isNamedJSXElement(node, expectedName) {
|
|
333
|
+
return (
|
|
334
|
+
node.type === 'JSXElement' &&
|
|
335
|
+
areJSXNamesEqual(node.openingElement.name, expectedName)
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function areJSXNamesEqual(actual, expected) {
|
|
340
|
+
if (actual.type !== expected.type) {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (actual.type === 'JSXIdentifier') {
|
|
345
|
+
return actual.name === expected.name;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (actual.type === 'JSXMemberExpression') {
|
|
349
|
+
return (
|
|
350
|
+
areJSXNamesEqual(actual.object, expected.object) &&
|
|
351
|
+
areJSXNamesEqual(actual.property, expected.property)
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function enableV12Release(openingElement, j) {
|
|
359
|
+
const attributes = openingElement.attributes;
|
|
360
|
+
const releaseAttributes = attributes.filter(
|
|
361
|
+
(attribute) =>
|
|
362
|
+
attribute.type === 'JSXAttribute' &&
|
|
363
|
+
attribute.name.name === 'enableV12Release'
|
|
364
|
+
);
|
|
365
|
+
const lastAttribute = attributes[attributes.length - 1];
|
|
366
|
+
const isEnabled =
|
|
367
|
+
releaseAttributes.length === 1 &&
|
|
368
|
+
releaseAttributes[0] === lastAttribute &&
|
|
369
|
+
(lastAttribute.value === null ||
|
|
370
|
+
(lastAttribute.value.type === 'JSXExpressionContainer' &&
|
|
371
|
+
(lastAttribute.value.expression.type === 'BooleanLiteral' ||
|
|
372
|
+
lastAttribute.value.expression.type === 'Literal') &&
|
|
373
|
+
lastAttribute.value.expression.value === true));
|
|
374
|
+
|
|
375
|
+
if (isEnabled) {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
openingElement.attributes = attributes
|
|
380
|
+
.filter(
|
|
381
|
+
(attribute) =>
|
|
382
|
+
attribute.type !== 'JSXAttribute' ||
|
|
383
|
+
attribute.name.name !== 'enableV12Release'
|
|
384
|
+
)
|
|
385
|
+
.concat(j.jsxAttribute(j.jsxIdentifier('enableV12Release')));
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function createFeatureFlagsWrapper(application, featureFlags, j) {
|
|
390
|
+
return j.jsxElement(
|
|
391
|
+
j.jsxOpeningElement(
|
|
392
|
+
cloneJSXName(featureFlags.jsxName),
|
|
393
|
+
[j.jsxAttribute(j.jsxIdentifier('enableV12Release'))],
|
|
394
|
+
false
|
|
395
|
+
),
|
|
396
|
+
j.jsxClosingElement(cloneJSXName(featureFlags.jsxName)),
|
|
397
|
+
[j.jsxText('\n'), application, j.jsxText('\n')]
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function cloneJSXName(name) {
|
|
402
|
+
return JSON.parse(JSON.stringify(name));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
module.exports = transform;
|
|
406
|
+
module.exports.parser = 'tsx';
|