@strapi/upgrade 5.0.0-rc.3 → 5.0.0-rc.30
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/dist/cli.js +68 -35
- package/dist/cli.js.map +1 -1
- package/dist/index.js +74 -39
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +73 -39
- package/dist/index.mjs.map +1 -1
- package/dist/modules/file-scanner/scanner.d.ts.map +1 -1
- package/dist/modules/format/formats.d.ts +2 -1
- package/dist/modules/format/formats.d.ts.map +1 -1
- package/dist/modules/project/constants.d.ts +6 -5
- package/dist/modules/project/constants.d.ts.map +1 -1
- package/dist/modules/project/project.d.ts +16 -2
- package/dist/modules/project/project.d.ts.map +1 -1
- package/dist/modules/project/types.d.ts +3 -0
- package/dist/modules/project/types.d.ts.map +1 -1
- package/dist/tasks/upgrade/upgrade.d.ts.map +1 -1
- package/package.json +6 -6
- package/resources/codemods/5.0.0/comment-out-lifecycle-files.code.ts +63 -0
- package/resources/codemods/5.0.0/dependency-upgrade-react-and-react-dom.json.ts +67 -0
- package/resources/codemods/5.0.0/dependency-upgrade-styled-components.json.ts +49 -0
- package/resources/codemods/5.0.0/deprecate-helper-plugin.code.ts +186 -0
- package/resources/utils/change-import.ts +118 -0
- package/resources/utils/replace-jsx.ts +49 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../../src/tasks/upgrade/upgrade.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,eAAO,MAAM,OAAO,YAAmB,cAAc,
|
|
1
|
+
{"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../../src/tasks/upgrade/upgrade.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,eAAO,MAAM,OAAO,YAAmB,cAAc,kBAuDpD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/upgrade",
|
|
3
|
-
"version": "5.0.0-rc.
|
|
3
|
+
"version": "5.0.0-rc.30",
|
|
4
4
|
"description": "CLI to upgrade Strapi applications effortless",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"strapi",
|
|
@@ -59,13 +59,13 @@
|
|
|
59
59
|
"watch": "pack-up watch"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@strapi/utils": "5.0.0-rc.
|
|
62
|
+
"@strapi/utils": "5.0.0-rc.30",
|
|
63
63
|
"chalk": "4.1.2",
|
|
64
64
|
"cli-table3": "0.6.2",
|
|
65
65
|
"commander": "8.3.0",
|
|
66
66
|
"esbuild-register": "3.5.0",
|
|
67
|
+
"fast-glob": "3.3.2",
|
|
67
68
|
"fs-extra": "11.2.0",
|
|
68
|
-
"glob": "10.3.10",
|
|
69
69
|
"jscodeshift": "0.15.1",
|
|
70
70
|
"lodash": "4.17.21",
|
|
71
71
|
"memfs": "4.6.0",
|
|
@@ -76,15 +76,15 @@
|
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"@strapi/pack-up": "5.0.0",
|
|
79
|
-
"@strapi/types": "5.0.0-rc.
|
|
79
|
+
"@strapi/types": "5.0.0-rc.30",
|
|
80
80
|
"@types/fs-extra": "11.0.4",
|
|
81
81
|
"@types/jscodeshift": "0.11.10",
|
|
82
|
-
"eslint-config-custom": "5.0.0-rc.
|
|
82
|
+
"eslint-config-custom": "5.0.0-rc.30",
|
|
83
83
|
"rimraf": "5.0.5"
|
|
84
84
|
},
|
|
85
85
|
"engines": {
|
|
86
86
|
"node": ">=18.0.0 <=20.x.x",
|
|
87
87
|
"npm": ">=6.0.0"
|
|
88
88
|
},
|
|
89
|
-
"gitHead": "
|
|
89
|
+
"gitHead": "506f8c8a6e41a81392bf59c5743b8a0db8790916"
|
|
90
90
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Transform } from 'jscodeshift';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* comments out lifecycles.js/ts files and adds a description for the reason at the top
|
|
5
|
+
*/
|
|
6
|
+
const transform: Transform = (file, api) => {
|
|
7
|
+
const j = api.jscodeshift;
|
|
8
|
+
const root = j(file.source);
|
|
9
|
+
|
|
10
|
+
// check if file path follows this pattern `content-types/[content-type-name]/lifecycles`
|
|
11
|
+
if (/content-types\/[^/]+\/lifecycles\.(js|ts)$/.test(file.path)) {
|
|
12
|
+
// Get the entire source code as a string
|
|
13
|
+
const sourceCode = root.toSource();
|
|
14
|
+
|
|
15
|
+
// Split the source code into lines and prepend // to each line
|
|
16
|
+
// we are using line comments instead of block comments so we don't face issues with existing block comments
|
|
17
|
+
const commentedCode = sourceCode
|
|
18
|
+
.split('\n')
|
|
19
|
+
.map((line) => `// ${line}`)
|
|
20
|
+
.join('\n');
|
|
21
|
+
|
|
22
|
+
// Add a header comment at the top to explain why the file is commented out
|
|
23
|
+
const headerComment = `
|
|
24
|
+
/*
|
|
25
|
+
*
|
|
26
|
+
* ============================================================
|
|
27
|
+
* WARNING: THIS FILE HAS BEEN COMMENTED OUT
|
|
28
|
+
* ============================================================
|
|
29
|
+
*
|
|
30
|
+
* CONTEXT:
|
|
31
|
+
*
|
|
32
|
+
* The lifecycles.js file has been commented out to prevent unintended side effects when starting Strapi 5 for the first time after migrating to the document service.
|
|
33
|
+
*
|
|
34
|
+
* STRAPI 5 introduces a new document service that handles lifecycles differently compared to previous versions. Without migrating your lifecycles to document service middlewares, you may experience issues such as:
|
|
35
|
+
*
|
|
36
|
+
* - \`unpublish\` actions triggering \`delete\` lifecycles for every locale with a published entity, which differs from the expected behavior in v4.
|
|
37
|
+
* - \`discardDraft\` actions triggering both \`create\` and \`delete\` lifecycles, leading to potential confusion.
|
|
38
|
+
*
|
|
39
|
+
* MIGRATION GUIDE:
|
|
40
|
+
*
|
|
41
|
+
* For a thorough guide on migrating your lifecycles to document service middlewares, please refer to the following link:
|
|
42
|
+
* [Document Services Middlewares Migration Guide](https://docs.strapi.io/dev-docs/migration/v4-to-v5/breaking-changes/lifecycle-hooks-document-service)
|
|
43
|
+
*
|
|
44
|
+
* IMPORTANT:
|
|
45
|
+
*
|
|
46
|
+
* Simply uncommenting this file without following the migration guide may result in unexpected behavior and inconsistencies. Ensure that you have completed the migration process before re-enabling this file.
|
|
47
|
+
*
|
|
48
|
+
* ============================================================
|
|
49
|
+
*/
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
// Combine the header comment with the commented-out code
|
|
53
|
+
const finalCode = `${headerComment}\n${commentedCode}`;
|
|
54
|
+
|
|
55
|
+
return finalCode;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return root.toSource();
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const parser = 'tsx';
|
|
62
|
+
|
|
63
|
+
export default transform;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import semver from 'semver';
|
|
3
|
+
|
|
4
|
+
import type { modules } from '../../../dist';
|
|
5
|
+
|
|
6
|
+
const REACT_DEP_NAME = 'react';
|
|
7
|
+
const REACT_DEP_PATH = `dependencies.${REACT_DEP_NAME}`;
|
|
8
|
+
|
|
9
|
+
const REACT_DOM_DEP_NAME = 'react-dom';
|
|
10
|
+
const REACT_DOM_DEP_PATH = `dependencies.${REACT_DOM_DEP_NAME}`;
|
|
11
|
+
|
|
12
|
+
const DEP_NEW_VERSION_RANGE = '^18.0.0';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Specifically targets the root package.json and updates the react and react-dom dependency version.
|
|
16
|
+
*
|
|
17
|
+
* We first check if the react and react-dom dependency is listed in the package.json. If the dependency is
|
|
18
|
+
* found, we verify its version.
|
|
19
|
+
*
|
|
20
|
+
* If the detected version does not satisfy the new version range, we replace it with the new one.
|
|
21
|
+
*
|
|
22
|
+
* Conversely, if no react or react-dom dependency is listed, we add it with the new version range.
|
|
23
|
+
*/
|
|
24
|
+
const transform: modules.runner.json.JSONTransform = (file, params) => {
|
|
25
|
+
const { cwd, json } = params;
|
|
26
|
+
|
|
27
|
+
const rootPackageJsonPath = path.join(cwd, 'package.json');
|
|
28
|
+
|
|
29
|
+
if (file.path !== rootPackageJsonPath) {
|
|
30
|
+
return file.json;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const j = json(file.json);
|
|
34
|
+
|
|
35
|
+
if (j.has(REACT_DEP_PATH) && j.has(REACT_DOM_DEP_PATH)) {
|
|
36
|
+
const currentReactVersion = j.get(REACT_DEP_PATH);
|
|
37
|
+
const currentReactDOMVersion = j.get(REACT_DOM_DEP_PATH);
|
|
38
|
+
|
|
39
|
+
// If the current version is not a string, then something is wrong, abort
|
|
40
|
+
if (typeof currentReactVersion !== 'string' || typeof currentReactDOMVersion !== 'string') {
|
|
41
|
+
return j.root();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const currentSatisfiesNew =
|
|
45
|
+
semver.satisfies(currentReactVersion, DEP_NEW_VERSION_RANGE) &&
|
|
46
|
+
semver.satisfies(currentReactDOMVersion, DEP_NEW_VERSION_RANGE);
|
|
47
|
+
|
|
48
|
+
// if the current version satisfies the new range, keep it as is and abort
|
|
49
|
+
if (currentSatisfiesNew) {
|
|
50
|
+
return j.root();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// else, update the version with the new one
|
|
54
|
+
j.set(REACT_DEP_PATH, DEP_NEW_VERSION_RANGE);
|
|
55
|
+
j.set(REACT_DOM_DEP_PATH, DEP_NEW_VERSION_RANGE);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// If the dependency is not listed yet, add it
|
|
59
|
+
else {
|
|
60
|
+
j.set(REACT_DEP_PATH, DEP_NEW_VERSION_RANGE);
|
|
61
|
+
j.set(REACT_DOM_DEP_PATH, DEP_NEW_VERSION_RANGE);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return j.root();
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default transform;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import semver from 'semver';
|
|
3
|
+
|
|
4
|
+
import type { modules } from '../../../dist';
|
|
5
|
+
|
|
6
|
+
const DEP_NAME = 'styled-components';
|
|
7
|
+
const DEP_PATH = `dependencies.${DEP_NAME}`;
|
|
8
|
+
|
|
9
|
+
const DEP_NEW_VERSION_RANGE = '^6.0.0';
|
|
10
|
+
|
|
11
|
+
const transform: modules.runner.json.JSONTransform = (file, params) => {
|
|
12
|
+
const { cwd, json } = params;
|
|
13
|
+
|
|
14
|
+
const rootPackageJsonPath = path.join(cwd, 'package.json');
|
|
15
|
+
|
|
16
|
+
if (file.path !== rootPackageJsonPath) {
|
|
17
|
+
return file.json;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const j = json(file.json);
|
|
21
|
+
|
|
22
|
+
if (j.has(DEP_PATH)) {
|
|
23
|
+
const currentVersion = j.get(DEP_PATH);
|
|
24
|
+
|
|
25
|
+
// If the current version is not a string, then something is wrong, abort
|
|
26
|
+
if (typeof currentVersion !== 'string') {
|
|
27
|
+
return j.root();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const currentSatisfiesNew = semver.satisfies(currentVersion, DEP_NEW_VERSION_RANGE);
|
|
31
|
+
|
|
32
|
+
// if the current version satisfies the new range, keep it as is and abort
|
|
33
|
+
if (currentSatisfiesNew) {
|
|
34
|
+
return j.root();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// else, update the version with the new one
|
|
38
|
+
j.set(DEP_PATH, DEP_NEW_VERSION_RANGE);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// If the dependency is not listed yet, add it
|
|
42
|
+
else {
|
|
43
|
+
j.set(DEP_PATH, DEP_NEW_VERSION_RANGE);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return j.root();
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default transform;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { Transform } from 'jscodeshift';
|
|
2
|
+
import { changeImportSpecifier } from '../../utils/change-import';
|
|
3
|
+
import { replaceJSXElement } from '../../utils/replace-jsx';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* This codemods automates all the imports and naming changes
|
|
7
|
+
* for methods or components that used to be imported from '@strapi/helper-plugin'
|
|
8
|
+
*/
|
|
9
|
+
const transform: Transform = (file, api) => {
|
|
10
|
+
const { j } = api;
|
|
11
|
+
|
|
12
|
+
const root = j.withParser('tsx')(file.source);
|
|
13
|
+
|
|
14
|
+
type Replacement = {
|
|
15
|
+
oldName: string;
|
|
16
|
+
oldDependency: string;
|
|
17
|
+
toReplace: boolean;
|
|
18
|
+
toChangeImportSpecifier: boolean;
|
|
19
|
+
newDependency?: string;
|
|
20
|
+
newName?: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const replacements: Replacement[] = [
|
|
24
|
+
{
|
|
25
|
+
oldName: 'AnErrorOccurred',
|
|
26
|
+
newName: 'Page.Error',
|
|
27
|
+
oldDependency: '@strapi/helper-plugin',
|
|
28
|
+
newDependency: '@strapi/strapi/admin',
|
|
29
|
+
toReplace: true,
|
|
30
|
+
toChangeImportSpecifier: true,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
oldName: 'CheckPagePermissions',
|
|
34
|
+
newName: 'Page.Protect',
|
|
35
|
+
oldDependency: '@strapi/helper-plugin',
|
|
36
|
+
newDependency: '@strapi/strapi/admin',
|
|
37
|
+
toReplace: true,
|
|
38
|
+
toChangeImportSpecifier: true,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
oldName: 'ConfirmDialog',
|
|
42
|
+
oldDependency: '@strapi/helper-plugin',
|
|
43
|
+
newDependency: '@strapi/strapi/admin',
|
|
44
|
+
toChangeImportSpecifier: true,
|
|
45
|
+
toReplace: false,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
oldName: 'DateTimePicker',
|
|
49
|
+
oldDependency: '@strapi/helper-plugin',
|
|
50
|
+
newDependency: '@strapi/design-system',
|
|
51
|
+
toChangeImportSpecifier: true,
|
|
52
|
+
toReplace: false,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
oldName: 'getFetchClient',
|
|
56
|
+
oldDependency: '@strapi/helper-plugin',
|
|
57
|
+
newDependency: '@strapi/strapi/admin',
|
|
58
|
+
toChangeImportSpecifier: true,
|
|
59
|
+
toReplace: false,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
oldName: 'LoadingIndicatorPage',
|
|
63
|
+
newName: 'Page.Loading',
|
|
64
|
+
oldDependency: '@strapi/helper-plugin',
|
|
65
|
+
newDependency: '@strapi/strapi/admin',
|
|
66
|
+
toReplace: true,
|
|
67
|
+
toChangeImportSpecifier: true,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
oldName: 'NoContent',
|
|
71
|
+
newName: 'EmptyStateLayout',
|
|
72
|
+
oldDependency: '@strapi/helper-plugin',
|
|
73
|
+
newDependency: '@strapi/design-system',
|
|
74
|
+
toReplace: true,
|
|
75
|
+
toChangeImportSpecifier: true,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
oldName: 'NoPermissions',
|
|
79
|
+
newName: 'Page.NoPermissions',
|
|
80
|
+
oldDependency: '@strapi/helper-plugin',
|
|
81
|
+
newDependency: '@strapi/strapi/admin',
|
|
82
|
+
toReplace: true,
|
|
83
|
+
toChangeImportSpecifier: true,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
oldName: 'Status',
|
|
87
|
+
oldDependency: '@strapi/helper-plugin',
|
|
88
|
+
newDependency: '@strapi/design-system',
|
|
89
|
+
toChangeImportSpecifier: true,
|
|
90
|
+
toReplace: false,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
oldName: 'translatedErrors',
|
|
94
|
+
oldDependency: '@strapi/helper-plugin',
|
|
95
|
+
newDependency: '@strapi/strapi/admin',
|
|
96
|
+
toChangeImportSpecifier: true,
|
|
97
|
+
toReplace: false,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
oldName: 'useAPIErrorHandler',
|
|
101
|
+
oldDependency: '@strapi/helper-plugin',
|
|
102
|
+
newDependency: '@strapi/strapi/admin',
|
|
103
|
+
toChangeImportSpecifier: true,
|
|
104
|
+
toReplace: false,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
oldName: 'useCallbackRef',
|
|
108
|
+
oldDependency: '@strapi/helper-plugin',
|
|
109
|
+
newDependency: '@strapi/design-system',
|
|
110
|
+
toChangeImportSpecifier: true,
|
|
111
|
+
toReplace: false,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
oldName: 'useCollator',
|
|
115
|
+
oldDependency: '@strapi/helper-plugin',
|
|
116
|
+
newDependency: '@strapi/design-system',
|
|
117
|
+
toChangeImportSpecifier: true,
|
|
118
|
+
toReplace: false,
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
oldName: 'useFetchClient',
|
|
122
|
+
oldDependency: '@strapi/helper-plugin',
|
|
123
|
+
newDependency: '@strapi/strapi/admin',
|
|
124
|
+
toChangeImportSpecifier: true,
|
|
125
|
+
toReplace: false,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
oldName: 'useFilter',
|
|
129
|
+
oldDependency: '@strapi/helper-plugin',
|
|
130
|
+
newDependency: '@strapi/design-system',
|
|
131
|
+
toChangeImportSpecifier: true,
|
|
132
|
+
toReplace: false,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
oldName: 'useQueryParams',
|
|
136
|
+
oldDependency: '@strapi/helper-plugin',
|
|
137
|
+
newDependency: '@strapi/strapi/admin',
|
|
138
|
+
toChangeImportSpecifier: true,
|
|
139
|
+
toReplace: false,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
oldName: 'useRBAC',
|
|
143
|
+
oldDependency: '@strapi/helper-plugin',
|
|
144
|
+
newDependency: '@strapi/strapi/admin',
|
|
145
|
+
toChangeImportSpecifier: true,
|
|
146
|
+
toReplace: false,
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
oldName: 'SearchURLQuery',
|
|
150
|
+
oldDependency: '@strapi/helper-plugin',
|
|
151
|
+
newDependency: '@strapi/strapi/admin',
|
|
152
|
+
toChangeImportSpecifier: true,
|
|
153
|
+
toReplace: false,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
oldName: 'useSettingsForm',
|
|
157
|
+
oldDependency: '@strapi/helper-plugin',
|
|
158
|
+
newDependency: '@strapi/strapi/admin',
|
|
159
|
+
toChangeImportSpecifier: true,
|
|
160
|
+
toReplace: false,
|
|
161
|
+
},
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
replacements.forEach((replacement) => {
|
|
165
|
+
if (replacement.toReplace && replacement.newName) {
|
|
166
|
+
replaceJSXElement(root, j, {
|
|
167
|
+
oldElementName: replacement.oldName,
|
|
168
|
+
newElementName: replacement.newName,
|
|
169
|
+
oldDependency: replacement.oldDependency,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (replacement.toChangeImportSpecifier && replacement.newDependency) {
|
|
174
|
+
changeImportSpecifier(root, j, {
|
|
175
|
+
oldMethodName: replacement.oldName,
|
|
176
|
+
newMethodName: replacement.newName,
|
|
177
|
+
oldDependency: replacement.oldDependency,
|
|
178
|
+
newDependency: replacement.newDependency,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
return root.toSource();
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export default transform;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { ImportDeclaration, JSCodeshift, Collection } from 'jscodeshift';
|
|
2
|
+
|
|
3
|
+
export const changeImportSpecifier = (
|
|
4
|
+
root: Collection,
|
|
5
|
+
j: JSCodeshift,
|
|
6
|
+
options: {
|
|
7
|
+
oldDependency: string;
|
|
8
|
+
newDependency: string;
|
|
9
|
+
oldMethodName: string;
|
|
10
|
+
newMethodName?: string;
|
|
11
|
+
}
|
|
12
|
+
): void => {
|
|
13
|
+
const { oldMethodName, newMethodName, oldDependency, newDependency } = options;
|
|
14
|
+
const methodNameToReplace = newMethodName ?? oldMethodName;
|
|
15
|
+
|
|
16
|
+
// Flag to check if the method was imported from the old dependency
|
|
17
|
+
let methodImportedFromOldDependency = false;
|
|
18
|
+
const methodAliases: string[] = [];
|
|
19
|
+
|
|
20
|
+
// Remove the method from the old dependency and check if it was imported
|
|
21
|
+
root
|
|
22
|
+
.find(j.ImportDeclaration)
|
|
23
|
+
.filter((path) => path.node.source.value === oldDependency)
|
|
24
|
+
.forEach((path) => {
|
|
25
|
+
const importDeclaration: ImportDeclaration = path.node;
|
|
26
|
+
|
|
27
|
+
// Check if the method is imported from the old dependency
|
|
28
|
+
const methodSpecifiers = importDeclaration.specifiers?.filter(
|
|
29
|
+
(specifier) =>
|
|
30
|
+
specifier.type === 'ImportSpecifier' && specifier.imported.name === oldMethodName
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
if (methodSpecifiers && methodSpecifiers.length > 0) {
|
|
34
|
+
methodImportedFromOldDependency = true;
|
|
35
|
+
|
|
36
|
+
// Collect all aliases for the method
|
|
37
|
+
methodSpecifiers.forEach((specifier) => {
|
|
38
|
+
if (specifier.local && specifier.local.name !== oldMethodName) {
|
|
39
|
+
methodAliases.push(specifier.local.name);
|
|
40
|
+
} else {
|
|
41
|
+
methodAliases.push(methodNameToReplace);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Remove the method specifiers from the old import
|
|
46
|
+
const updatedSpecifiers = importDeclaration.specifiers?.filter(
|
|
47
|
+
(specifier) =>
|
|
48
|
+
specifier.type !== 'ImportSpecifier' || specifier.imported.name !== oldMethodName
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
if (updatedSpecifiers && updatedSpecifiers.length > 0) {
|
|
52
|
+
// Replace the import with the updated specifiers if there are other imports left
|
|
53
|
+
j(path).replaceWith(j.importDeclaration(updatedSpecifiers, j.literal(oldDependency)));
|
|
54
|
+
} else {
|
|
55
|
+
// Remove the entire import statement if the specified method was the only import
|
|
56
|
+
j(path).remove();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Add new import dependency if the method was imported from the old dependency
|
|
62
|
+
if (methodImportedFromOldDependency) {
|
|
63
|
+
const dependencies = root
|
|
64
|
+
.find(j.ImportDeclaration)
|
|
65
|
+
.filter((path) => path.node.source.value === newDependency);
|
|
66
|
+
|
|
67
|
+
if (dependencies.length > 0) {
|
|
68
|
+
// we have to use a flag to prevent adding the method to multiple imports
|
|
69
|
+
let methodAdded = false;
|
|
70
|
+
dependencies.forEach((path) => {
|
|
71
|
+
const importDeclaration: ImportDeclaration = path.node;
|
|
72
|
+
if (!methodAdded) {
|
|
73
|
+
methodAliases.forEach((alias) => {
|
|
74
|
+
// Check if the methodNameToReplace or its alias is already imported
|
|
75
|
+
const specifiersArray = importDeclaration.specifiers || [];
|
|
76
|
+
const methodAlreadyExists = specifiersArray.some(
|
|
77
|
+
(specifier) =>
|
|
78
|
+
specifier.type === 'ImportSpecifier' &&
|
|
79
|
+
specifier.imported.name === methodNameToReplace && // Check if imported method matches
|
|
80
|
+
specifier.local?.name === alias // Check if local alias matches
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (!methodAlreadyExists) {
|
|
84
|
+
// If method does not exist, add it
|
|
85
|
+
const newSpecifier = j.importSpecifier(
|
|
86
|
+
j.identifier(methodNameToReplace),
|
|
87
|
+
j.identifier(alias)
|
|
88
|
+
);
|
|
89
|
+
path.get('specifiers').replace([...specifiersArray, newSpecifier]);
|
|
90
|
+
methodAdded = true;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
} else {
|
|
96
|
+
const newSpecifiers = methodAliases.map((alias) =>
|
|
97
|
+
j.importSpecifier(j.identifier(methodNameToReplace), j.identifier(alias))
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const newImportDeclaration = j.importDeclaration(newSpecifiers, j.literal(newDependency));
|
|
101
|
+
|
|
102
|
+
// Find the index of the first non-import declaration
|
|
103
|
+
const body = root.get().node.program.body;
|
|
104
|
+
const lastImportIndex = body.findIndex((node) => node.type !== 'ImportDeclaration');
|
|
105
|
+
|
|
106
|
+
if (lastImportIndex > -1) {
|
|
107
|
+
// Insert the new import declaration just before the first non-import node
|
|
108
|
+
body.splice(lastImportIndex, 0, newImportDeclaration);
|
|
109
|
+
} else {
|
|
110
|
+
// Check if 'use strict' exists at the beginning
|
|
111
|
+
const hasUseStrict =
|
|
112
|
+
body[0]?.type === 'ExpressionStatement' && body[0]?.expression?.value === 'use strict';
|
|
113
|
+
// Add the new import after 'use strict' if it exists, otherwise at the beginning
|
|
114
|
+
body.splice(hasUseStrict ? 1 : 0, 0, newImportDeclaration);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { JSCodeshift, Collection } from 'jscodeshift';
|
|
2
|
+
|
|
3
|
+
export const replaceJSXElement = (
|
|
4
|
+
root: Collection,
|
|
5
|
+
j: JSCodeshift,
|
|
6
|
+
{
|
|
7
|
+
oldElementName,
|
|
8
|
+
newElementName,
|
|
9
|
+
oldDependency,
|
|
10
|
+
}: {
|
|
11
|
+
oldElementName: string;
|
|
12
|
+
newElementName: string;
|
|
13
|
+
oldDependency: string;
|
|
14
|
+
}
|
|
15
|
+
) => {
|
|
16
|
+
// Find the import declaration for the old dependency
|
|
17
|
+
const importDeclaration = root.find(j.ImportDeclaration, {
|
|
18
|
+
source: { value: oldDependency },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (importDeclaration.size() === 0) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Get the local name of the imported element
|
|
26
|
+
const localName = importDeclaration
|
|
27
|
+
.find(j.ImportSpecifier, {
|
|
28
|
+
imported: { name: oldElementName },
|
|
29
|
+
})
|
|
30
|
+
.nodes()[0]?.local?.name;
|
|
31
|
+
|
|
32
|
+
if (!localName) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Replace JSX elements
|
|
37
|
+
root.findJSXElements(localName).forEach((path) => {
|
|
38
|
+
const openingElement = path.node.openingElement;
|
|
39
|
+
const closingElement = path.node.closingElement;
|
|
40
|
+
|
|
41
|
+
if (j.JSXIdentifier.check(openingElement.name)) {
|
|
42
|
+
openingElement.name.name = newElementName;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (closingElement && j.JSXIdentifier.check(closingElement.name)) {
|
|
46
|
+
closingElement.name.name = newElementName;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
};
|