@elliemae/ds-codemods 3.70.0-next.2 → 3.70.0-next.4

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.
@@ -0,0 +1,25 @@
1
+ export const getLegacyDeprecation27dot1Questions = (originalOptions) => {
2
+ const extraOptions = [];
3
+ const { startingDirPath, gitIgnorePath } = originalOptions;
4
+ if (!startingDirPath && startingDirPath !== '') {
5
+ extraOptions.push({
6
+ type: 'input',
7
+ name: 'startingDirPath',
8
+ message: 'Please provide the project root directory (where node_modules folder lives)',
9
+ default: './',
10
+ });
11
+ }
12
+ if (!gitIgnorePath && gitIgnorePath !== '') {
13
+ extraOptions.push({
14
+ type: 'input',
15
+ name: 'gitIgnorePath',
16
+ message:
17
+ 'Please provide the path to the .gitignore file' +
18
+ '(if a non-matching path is provided, node_modules, dist and build will be ignored)',
19
+ default: './.gitignore',
20
+ });
21
+ }
22
+ // --debug is a CLI flag, not available as a question.
23
+
24
+ return extraOptions;
25
+ };
@@ -1,3 +1,4 @@
1
+ /* eslint-disable complexity */
1
2
  import { fixLegacyImports } from './fix-legacy-imports/index.mjs';
2
3
  import { checkPackagesInconsistencies } from './check-packages-inconsistencies/index.mjs';
3
4
  import { checkMissingPackages } from './check-missing-packages/index.mjs';
@@ -6,6 +7,7 @@ import { helpMigrateToV3 } from './help-migrate-to-v3/index.mjs';
6
7
  import { deprecatedComponentsUsageReport } from './deprecated-components-usage-report/index.mjs';
7
8
  import { componentsUsageReport } from './components-usage-report/index.mjs';
8
9
  import { legacyDeprecation26dot3 } from './legacy-deprecation-26-3/index.mjs';
10
+ import { legacyDeprecation27dot1 } from './legacy-deprecation-27-1/index.mjs';
9
11
  import { COMMANDS } from '../commands.mjs';
10
12
 
11
13
  export async function executeCommandsMap(args, options) {
@@ -37,6 +39,9 @@ export async function executeCommandsMap(args, options) {
37
39
  case COMMANDS.LEGACY_DEPRECATION_26DOT3:
38
40
  legacyDeprecation26dot3(options);
39
41
  break;
42
+ case COMMANDS.LEGACY_DEPRECATION_27DOT1:
43
+ legacyDeprecation27dot1(options);
44
+ break;
40
45
  default:
41
46
  break;
42
47
  }
@@ -0,0 +1,52 @@
1
+ import fs from 'fs';
2
+ import fse from 'fs-extra';
3
+ import { filePathsWithGitIgnore } from '../../../utils/filepathsMatchers.mjs';
4
+ import { legacy27dot1ImportMap, executeMapReplacement } from './legacy27dot1ImportMap.mjs';
5
+ import { packageJsonReplaceLogic } from './packageJsonReplaceLogic.mjs';
6
+
7
+ /**
8
+ * generates a report of deprecated components usage
9
+ * @param {object} options - options *
10
+ * @param {string} options.startingDirPath - path to the project root directory (where node_modules folder lives)
11
+ * @param {string} options.gitIgnorePath - path to the .gitignore file
12
+ * @param {boolean} options.debug - debug flag
13
+ * @param {string} options.fileExtensions - comma separated list of file extensions to filter by
14
+ *
15
+ * @returns {void}
16
+ */
17
+ export const legacyDeprecation27dot1 = (options) => {
18
+ // 1- get all the "raw" code file paths using gitignore blacklist and file extension filter
19
+ const filesToParse = filePathsWithGitIgnore(options);
20
+ if (options.debug) console.log('legacy27dot1ImportMap :>> ', legacy27dot1ImportMap);
21
+ // 2- for each file, replace the imports from the legacy components with the new ones
22
+ filesToParse.forEach((path) => {
23
+ const fileContent = fs.readFileSync(path, {
24
+ encoding: 'utf8',
25
+ flag: 'r',
26
+ });
27
+ if (fileContent && typeof fileContent === 'string') {
28
+ const { finalString, matchesFound } = executeMapReplacement(fileContent);
29
+ const didChangeSomething = matchesFound.length > 0;
30
+ if (didChangeSomething) {
31
+ if (!options.debug) fse.outputFileSync(path, finalString);
32
+ else {
33
+ console.log(`File: ${path} - import(s) replacement(s) found!`);
34
+ console.log('matchesFound :>> ', matchesFound);
35
+ }
36
+ }
37
+ }
38
+ });
39
+ // 3- do something similar, but with opinionated "package.json" files and logic
40
+ // in particular:
41
+ // - we get the package.json files (multiple for monorepos?) and we parse them as json.
42
+ // - check the dependencies and devDependencies and peerDependencies for any legacy component
43
+ // (keyof legacy27dot1ImportMap) and replace it with the new one (value of legacy27dot1ImportMap)
44
+ const packageJsonPendingReplacements = packageJsonReplaceLogic(options);
45
+ if (options.debug) console.log('packageJsonPendingReplacements :>> ', packageJsonPendingReplacements);
46
+ else
47
+ Object.entries(packageJsonPendingReplacements).forEach(([path, newContent]) => {
48
+ fse.outputFileSync(path, newContent);
49
+ });
50
+ };
51
+
52
+ export default legacyDeprecation27dot1;
@@ -0,0 +1,44 @@
1
+ /*
2
+ we need an exact match of the imports, we can't do a lose match
3
+ why?
4
+ we have packages that are the old package name with a different append
5
+ E.G. @elliemae/ds-button (legacy) and @elliemae/ds-button-v2 (new)
6
+ we can't replace
7
+ @elliemae/ds-button-v2 to @elliemae/ds-legacy-button-v2
8
+ because @elliemae/ds-button is a substring of @elliemae/ds-button-v2
9
+ how do we solve this?
10
+ key: exact match of the imports
11
+ value:{
12
+ matchChange: string; // the new package name to replace with
13
+ regexp
14
+ regexp: RegExp; // new Regexp(/(['"])(${key})(['"])/)
15
+ }
16
+ this will allow regexp match and replace based on group capture, so we can keep the same quotes as the original import
17
+ E.G. we can replace
18
+ from '@elliemae/ds-truncated-tooltip-text' to '@elliemae/ds-legacy-truncated-tooltip-text'
19
+ but also
20
+ from "@elliemae/ds-truncated-tooltip-text" to "@elliemae/ds-legacy-truncated-tooltip-text"
21
+ */
22
+ export const legacy27dot1ImportMap = {
23
+ '@elliemae/ds-truncated-tooltip-text': {
24
+ matchChange: '@elliemae/ds-legacy-truncated-tooltip-text',
25
+ regexp: new RegExp(/(from ['"])(@elliemae\/ds-truncated-tooltip-text)(['"])/g),
26
+ },
27
+ };
28
+
29
+ export const executeMapReplacement = (fullFileStringContent) => {
30
+ if (typeof fullFileStringContent !== 'string') {
31
+ const error = `expected string argument but received ${typeof fullFileStringContent}`;
32
+ console.error(error);
33
+ throw new Error(error);
34
+ }
35
+ let finalString = fullFileStringContent;
36
+ const matchesFound = [];
37
+ Object.entries(legacy27dot1ImportMap).forEach(([key, { matchChange, regexp }]) => {
38
+ if (regexp.test(finalString)) {
39
+ matchesFound.push(key);
40
+ finalString = finalString.replaceAll(regexp, `$1${matchChange}$3`);
41
+ }
42
+ });
43
+ return { finalString, matchesFound };
44
+ };
@@ -0,0 +1,124 @@
1
+ /* eslint-disable max-statements */
2
+ import fs from 'fs';
3
+ import { boldRedStr, yellowStr } from '../../../utils/CLI_COLORS.mjs';
4
+ import { filePathsWithGitIgnore } from '../../../utils/filepathsMatchers.mjs';
5
+ import { legacy27dot1ImportMap } from './legacy27dot1ImportMap.mjs';
6
+
7
+ /**
8
+ *
9
+ * parse a package.json, checks the dependencies(dev and peer included) for any legacy component
10
+ *
11
+ * if any legacy component is found,
12
+ * generates a new content for the file with the legacy component replaced with the new one and version set to "latest"
13
+ *
14
+ * @param {string} path - the package.json file path
15
+ * @param {object} options - options *
16
+ * @param {boolean} options.debug - debug flag
17
+ * @returns {string | -1} - the new content (json to string) if any change is needed,
18
+ * or -1 if no change is needed or in case of error
19
+ */
20
+ // eslint-disable-next-line complexity
21
+ const specificPackageJsonReplaceLogic = (path, options) => {
22
+ const fileContent = fs.readFileSync(path, {
23
+ encoding: 'utf8',
24
+ flag: 'r',
25
+ });
26
+ if (!fileContent || typeof fileContent !== 'string') {
27
+ if (options.debug) console.log(yellowStr(`Empty or invalid content in ${path}`));
28
+ return -1;
29
+ }
30
+ const parsedContent = JSON.parse(fileContent);
31
+ const newDeps = { ...parsedContent.dependencies };
32
+ let depsChanged = false;
33
+ Object.keys(parsedContent.dependencies || {}).forEach((oldPackageString) => {
34
+ if (legacy27dot1ImportMap[oldPackageString]) {
35
+ const newPackageString = legacy27dot1ImportMap[oldPackageString].matchChange;
36
+ newDeps[newPackageString] = 'latest';
37
+ delete newDeps[oldPackageString];
38
+ depsChanged = true;
39
+ }
40
+ });
41
+
42
+ const newDevDeps = { ...parsedContent.devDependencies };
43
+ let devDepsChanged = false;
44
+ Object.keys(parsedContent.devDependencies || {}).forEach((oldPackageString) => {
45
+ if (legacy27dot1ImportMap[oldPackageString]) {
46
+ const newPackageString = legacy27dot1ImportMap[oldPackageString].matchChange;
47
+ newDevDeps[newPackageString] = 'latest';
48
+ delete newDevDeps[oldPackageString];
49
+ devDepsChanged = true;
50
+ }
51
+ });
52
+ const newPeerDeps = { ...parsedContent.peerDependencies };
53
+ let peerDepsChanged = false;
54
+ Object.keys(parsedContent.peerDependencies || {}).forEach((oldPackageString) => {
55
+ if (legacy27dot1ImportMap[oldPackageString]) {
56
+ const newPackageString = legacy27dot1ImportMap[oldPackageString].matchChange;
57
+ newPeerDeps[newPackageString] = 'latest';
58
+ delete newPeerDeps[oldPackageString];
59
+ peerDepsChanged = true;
60
+ }
61
+ });
62
+ if (!depsChanged && !devDepsChanged && !peerDepsChanged) {
63
+ if (options.debug) console.log(yellowStr(`No legacy dependencies found in ${path}`));
64
+ return -1;
65
+ }
66
+ const newContent = {
67
+ ...parsedContent,
68
+ ...(depsChanged && { dependencies: newDeps }),
69
+ ...(devDepsChanged && { devDependencies: newDevDeps }),
70
+ ...(peerDepsChanged && { peerDependencies: newPeerDeps }),
71
+ };
72
+
73
+ return JSON.stringify(newContent, null, 2);
74
+ };
75
+
76
+ /**
77
+ * do something similar, but with opinionated "package.json" files and logic
78
+ *
79
+ * in particular:
80
+ * - we get the package.json files (multiple for monorepos?) and we parse them as json.
81
+ * - check the dependencies and devDependencies and peerDependencies for any legacy component
82
+ * (keyof legacy27dot1ImportMap) and replace it with the new one (value of legacy27dot1ImportMap)
83
+ * - if any change is needed, we return an object so that the caller can decide what to do with it
84
+ * (write it or just log it for the user to review)
85
+ * @param {object} options - options *
86
+ * @param {string} options.startingDirPath - path to the project root directory (where node_modules folder lives)
87
+ * @param {string} options.gitIgnorePath - path to the .gitignore file
88
+ * @param {boolean} options.debug - debug flag
89
+ * @param {string} options.fileExtensions - comma separated list of file extensions to filter by
90
+ * @returns {object} - an object with the (package.json) file path as key and the new content (json to string) as value,
91
+ * for each package.json file that needs changes
92
+ */
93
+ export const packageJsonReplaceLogic = (options) => {
94
+ // 1- get the package.json files (multiple for monorepos?) paths
95
+ const jsonFilesPaths = filePathsWithGitIgnore({ ...options, fileExtensions: '.json' });
96
+ // the above function doesn't allow for specific filename matching,
97
+ // this is a one-off right now so not extending the function
98
+ // instead we filter the results with a simple string match, to get only the package.json files paths
99
+ const packageJsonFilesPaths = jsonFilesPaths.filter((path) => path.endsWith('package.json'));
100
+ if (packageJsonFilesPaths.length === 0) {
101
+ console.log(boldRedStr('No package.json files! Please check the startingDirPath and gitIgnorePath options.'));
102
+ console.log(
103
+ yellowStr(
104
+ `To make sure migration is successful manually:
105
+ - check for any legacy components in your package.json
106
+ - replace them with the new ones.
107
+ - point them to "latest" version, the legacy library only get updated with critical security fixes and very rarely so.
108
+ - regenerate the lock file ( pnpm i --fix-lockfile or equivalent command/flow for your package manager)
109
+
110
+ The legacy components to look for are:`,
111
+ ),
112
+ );
113
+ console.log(legacy27dot1ImportMap);
114
+ return {};
115
+ }
116
+ const filesToChange = {};
117
+ // 2 - parse, check and generate new content for each package.json file
118
+ packageJsonFilesPaths.forEach((path) => {
119
+ if (options.debug) console.log('path :>> ', path);
120
+ const newContent = specificPackageJsonReplaceLogic(path, options);
121
+ if (newContent !== -1) filesToChange[path] = newContent;
122
+ });
123
+ return filesToChange;
124
+ };
@@ -8,6 +8,7 @@ export const COMMANDS = {
8
8
  DEPRECATED_PACKAGES_USAGE_REPORT: 'deprecated-components-usage-report',
9
9
  COMPONENT_USAGE_REPORT: 'components-usage-report',
10
10
  LEGACY_DEPRECATION_26DOT3: 'legacy-deprecation-26dot3',
11
+ LEGACY_DEPRECATION_27DOT1: 'legacy-deprecation-27dot1',
11
12
  EXIT: 'exit',
12
13
  };
13
14
 
@@ -7,6 +7,7 @@ import { getHelpMigrateToV3Questions } from './command-arguments/promptHelpMigra
7
7
  import { getMissingPackagesQuestions } from './command-arguments/promptMissingPackages.mjs';
8
8
  import { getDeprecatedUsageReportQuestions } from './command-arguments/getDeprecatedUsageReportQuestions.mjs';
9
9
  import { getLegacyDeprecation26dot3Questions } from './command-arguments/getLegacyDeprecation26dot3Questions.mjs';
10
+ import { getLegacyDeprecation27dot1Questions } from './command-arguments/getLegacyDeprecation27dot1Questions.mjs';
10
11
 
11
12
  // eslint-disable-next-line complexity
12
13
  async function promptForMissingScriptSpecificOptions({ originalOptions, promptOptions }) {
@@ -36,6 +37,9 @@ async function promptForMissingScriptSpecificOptions({ originalOptions, promptOp
36
37
  case COMMANDS.LEGACY_DEPRECATION_26DOT3:
37
38
  scriptQuestions.push(...getLegacyDeprecation26dot3Questions(originalOptions));
38
39
  break;
40
+ case COMMANDS.LEGACY_DEPRECATION_27DOT1:
41
+ scriptQuestions.push(...getLegacyDeprecation27dot1Questions(originalOptions));
42
+ break;
39
43
  default:
40
44
  break;
41
45
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elliemae/ds-codemods",
3
- "version": "3.70.0-next.2",
3
+ "version": "3.70.0-next.4",
4
4
  "license": "MIT",
5
5
  "description": "ICE MT - Dimsum - Code Mods",
6
6
  "files": [