@elliemae/ds-codemods 3.80.0-next.1 → 3.80.0-next.12
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/bin/cli/code-mods/cli.mjs +6 -0
- package/bin/cli/code-mods/command-arguments/getLegacyDeprecationQuestions.mjs +26 -0
- package/bin/cli/code-mods/command-logics/deprecated-components-usage-report/index.mjs +7 -2
- package/bin/cli/code-mods/command-logics/deprecated-components-usage-report/utils.mjs +68 -5
- package/bin/cli/code-mods/command-logics/execute-commands-map.mjs +8 -4
- package/bin/cli/code-mods/command-logics/{legacy-deprecation-26-3 → legacy-deprecation/26-3}/packageJsonReplaceLogic.mjs +9 -7
- package/bin/cli/code-mods/command-logics/{legacy-deprecation-26-3/index.mjs → legacy-deprecation/26-3/run26dot3MigrationLogic.mjs} +3 -3
- package/bin/cli/code-mods/command-logics/{legacy-deprecation-27-1 → legacy-deprecation/27-1}/packageJsonReplaceLogic.mjs +9 -7
- package/bin/cli/code-mods/command-logics/{legacy-deprecation-27-1/index.mjs → legacy-deprecation/27-1/run27dot1MigrationLogic.mjs} +3 -3
- package/bin/cli/code-mods/command-logics/legacy-deprecation/27-2/legacy27dot2ImportMap.mjs +44 -0
- package/bin/cli/code-mods/command-logics/legacy-deprecation/27-2/packageJsonReplaceLogic.mjs +126 -0
- package/bin/cli/code-mods/command-logics/legacy-deprecation/27-2/run27dot2MigrationLogic.mjs +52 -0
- package/bin/cli/code-mods/command-logics/legacy-deprecation/AGENTS.md +67 -0
- package/bin/cli/code-mods/command-logics/legacy-deprecation/CLAUDE.md +14 -0
- package/bin/cli/code-mods/command-logics/legacy-deprecation/index.mjs +58 -0
- package/bin/cli/code-mods/commands.mjs +1 -0
- package/bin/cli/code-mods/inquirer-questions-prompter.mjs +4 -0
- package/bin/cli/constants/deprecatedPackages.mjs +24 -1
- package/bin/cli/constants/index.mjs +1 -0
- package/package.json +1 -1
- /package/bin/cli/code-mods/command-logics/{legacy-deprecation-26-3 → legacy-deprecation/26-3}/legacy26dot3ImportMap.mjs +0 -0
- /package/bin/cli/code-mods/command-logics/{legacy-deprecation-27-1 → legacy-deprecation/27-1}/legacy27dot1ImportMap.mjs +0 -0
|
@@ -13,7 +13,11 @@ function parseArgumentsIntoOptions(rawArgs) {
|
|
|
13
13
|
'--ignoreFilesGlobPattern': String,
|
|
14
14
|
'--outputPath': String,
|
|
15
15
|
'--startingDirPath': String,
|
|
16
|
+
'--filesPath': String, // CLI flag, not available as a question, defaults to --startingDirPath if not passed.
|
|
16
17
|
'--gitIgnorePath': String,
|
|
18
|
+
// CLI flag, not available as a question - comma separated (E.G. "26.3,27.1") or "all" (default),
|
|
19
|
+
// legacy-deprecation command only.
|
|
20
|
+
'--migrations': String,
|
|
17
21
|
// CLI flag, not available as a question -> Boolean are not supported by the combo of arg and (dynamic) inquirer.
|
|
18
22
|
// it's impossible to determine if the user has passed the flag or not because
|
|
19
23
|
// for arg Boolean are either true or undefined.
|
|
@@ -36,12 +40,14 @@ function parseArgumentsIntoOptions(rawArgs) {
|
|
|
36
40
|
ignoreFilesGlobPattern: args['--ignoreFilesGlobPattern'],
|
|
37
41
|
outputPath: args['--outputPath'],
|
|
38
42
|
startingDirPath: args['--startingDirPath'],
|
|
43
|
+
filesPath: args['--filesPath'], // CLI flag, not available as a question.
|
|
39
44
|
debug: args['--debug'], // CLI flag, not available as a question.
|
|
40
45
|
fileExtensions: args['--fileExtensions'], // CLI flag, not available as a question.
|
|
41
46
|
append: args['--append'], // CLI flag, not available as a question.
|
|
42
47
|
org: args['--org'], // CLI flag, not available as a question.
|
|
43
48
|
repo: args['--repo'], // CLI flag, not available as a question.
|
|
44
49
|
gitIgnorePath: args['--gitIgnorePath'],
|
|
50
|
+
migrations: args['--migrations'], // CLI flag, not available as a question.
|
|
45
51
|
script: args._[0],
|
|
46
52
|
};
|
|
47
53
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const getLegacyDeprecationQuestions = (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
|
+
// --migrations is a CLI flag, not available as a question - defaults to "all" when not passed.
|
|
24
|
+
|
|
25
|
+
return extraOptions;
|
|
26
|
+
};
|
|
@@ -8,6 +8,9 @@ import { generateCsvRows, checkFileForDeprecatedComponents } from './utils.mjs';
|
|
|
8
8
|
* @param {object} options - options *
|
|
9
9
|
* @param {string} options.outputPath - path to the .csv file to write the report to
|
|
10
10
|
* @param {string} options.startingDirPath - path to the project root directory (where node_modules folder lives)
|
|
11
|
+
* @param {string} [options.filesPath] - optional, CLI-only (not prompted for) path to scan for files to check,
|
|
12
|
+
* in case you want it to differ from startingDirPath (E.G. only checking a subfolder or a single file). Defaults
|
|
13
|
+
* to startingDirPath, i.e. today's behavior of scanning the whole project root, if not provided.
|
|
11
14
|
* @param {string} options.gitIgnorePath - path to the .gitignore file
|
|
12
15
|
* @param {string} options.org - optional org, if present an extra column will be added to the report
|
|
13
16
|
* @param {string} options.repo - optional repo, if present an extra column will be added to the report
|
|
@@ -17,9 +20,11 @@ import { generateCsvRows, checkFileForDeprecatedComponents } from './utils.mjs';
|
|
|
17
20
|
* @returns {void}
|
|
18
21
|
*/
|
|
19
22
|
export const deprecatedComponentsUsageReport = (options) => {
|
|
20
|
-
const { outputPath, append } = options;
|
|
23
|
+
const { outputPath, append, filesPath, startingDirPath } = options;
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
// filesPath lets a CLI-savvy caller scan a narrower path than the project root used for npm ls -
|
|
26
|
+
// if not provided we fall back to startingDirPath, i.e. today's behavior, unchanged.
|
|
27
|
+
const filesToParse = filePathsWithGitIgnore({ ...options, startingDirPath: filesPath || startingDirPath });
|
|
23
28
|
// we have a lot more info than the one we use in the csv,
|
|
24
29
|
// in case we want to expand it in the future we can store it in the map...
|
|
25
30
|
// const problematicFiles = new Map();
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import {
|
|
4
4
|
DISMISSED_BEFORE_DEPRECATION_FLOWS_EXISTED,
|
|
5
|
+
DISMISSED_UNFUNDED_CASE_BY_CASE,
|
|
5
6
|
DISMISSED_WITH_EXAMPLE,
|
|
6
7
|
LEGACY_WITH_NEW_LARGE_MIGRATION_EFFORT,
|
|
7
8
|
LEGACY_WITH_NEW_MEDIUM_MIGRATION_EFFORT,
|
|
@@ -21,6 +22,7 @@ import { dimsumImportStatementsRegExp } from './regexp.mjs';
|
|
|
21
22
|
* @typedef {typeof DISMISSED_WITH_EXAMPLE[number]} dismissedWithExampleEntry
|
|
22
23
|
* @typedef {typeof NEVER_INTENDED_FOR_EXTERNAL_USAGE[number]} reportingUsageOfInternalOnlyEntry
|
|
23
24
|
* @typedef {typeof DISMISSED_BEFORE_DEPRECATION_FLOWS_EXISTED[number]} dismissedBeforeDeprecationFlowsExistedEntry
|
|
25
|
+
* @typedef {typeof DISMISSED_UNFUNDED_CASE_BY_CASE[number]} dismissedUnfundedCaseByCaseEntry
|
|
24
26
|
*
|
|
25
27
|
* @typedef { object } regexpInfo
|
|
26
28
|
* @property { string } regexpInfo.fullMatch
|
|
@@ -42,12 +44,16 @@ import { dimsumImportStatementsRegExp } from './regexp.mjs';
|
|
|
42
44
|
* @typedef { object } matchDeprecatedBeforeDeprecationFlowsExistedEntryPropeties
|
|
43
45
|
* @property { dismissedBeforeDeprecationFlowsExistedEntry[] } matchDeprecatedBeforeDeprecationFlowsExistedEntryPropeties.matchingEffortsMetainfo
|
|
44
46
|
*
|
|
47
|
+
* @typedef { object } matchDismissedUnfundedCaseByCaseEntryPropeties
|
|
48
|
+
* @property { dismissedUnfundedCaseByCaseEntry[] } matchDismissedUnfundedCaseByCaseEntryPropeties.matchingEffortsMetainfo
|
|
49
|
+
*
|
|
45
50
|
* @typedef {matchSharedPropieties & matchWithEffortEntryPropeties} matchWithEffortMetaInfo
|
|
46
51
|
* @typedef {matchSharedPropieties & matchDeprecatedEntryPropeties} matchDeprecatedWithExample
|
|
47
52
|
* @typedef {matchSharedPropieties & matchInternalOnlyEntryPropeties} matchInternalOnlyUsedExternally
|
|
48
53
|
* @typedef {matchSharedPropieties & matchDeprecatedBeforeDeprecationFlowsExistedEntryPropeties} matchDeprecatedBeforeDeprecationFlowsExisted
|
|
54
|
+
* @typedef {matchSharedPropieties & matchDismissedUnfundedCaseByCaseEntryPropeties} matchDismissedUnfundedCaseByCase
|
|
49
55
|
*
|
|
50
|
-
* @typedef {matchWithEffortMetaInfo | matchDeprecatedWithExample | matchInternalOnlyUsedExternally | matchDeprecatedBeforeDeprecationFlowsExisted} matchedEffortMapValue
|
|
56
|
+
* @typedef {matchWithEffortMetaInfo | matchDeprecatedWithExample | matchInternalOnlyUsedExternally | matchDeprecatedBeforeDeprecationFlowsExisted | matchDismissedUnfundedCaseByCase} matchedEffortMapValue
|
|
51
57
|
*
|
|
52
58
|
* @typedef {Map<string, matchedEffortMapValue>} matchedEffortMap
|
|
53
59
|
*
|
|
@@ -59,13 +65,18 @@ import { dimsumImportStatementsRegExp } from './regexp.mjs';
|
|
|
59
65
|
* @property {matchDeprecatedWithExample} fileReport.deprecatedWithExample
|
|
60
66
|
* @property {matchInternalOnlyUsedExternally} fileReport.reportingUsageOfInternalOnly
|
|
61
67
|
* @property {matchDeprecatedBeforeDeprecationFlowsExisted} fileReport.deprecatedBeforeDeprecationFlowsExisted
|
|
68
|
+
* @property {matchDismissedUnfundedCaseByCase} fileReport.dismissedUnfundedCaseByCase
|
|
62
69
|
*
|
|
63
70
|
*/
|
|
64
71
|
|
|
65
72
|
const subStringAdhocDSMatcher = (str, substr) => {
|
|
66
73
|
if (str === substr || substr === '*') return true;
|
|
74
|
+
// for @elliemae/ds-* values we anchor the end so "@elliemae/ds-button" does not also match
|
|
75
|
+
// "@elliemae/ds-button-v1" (a different package entirely) - but we still allow it to match
|
|
76
|
+
// "@elliemae/ds-button/some/deep/subpath" (the *same* package, just a deep import), since a
|
|
77
|
+
// subpath continuation always starts with "/" right after the package name, never anything else.
|
|
67
78
|
const substrRegExp = substr.startsWith('@elliemae/ds-')
|
|
68
|
-
? new RegExp(
|
|
79
|
+
? new RegExp(`^@\\belliemae\/ds-${substr.split('@elliemae/ds-')[1]}($|\\/)`, 'g')
|
|
69
80
|
: new RegExp(`\\b${substr}\\b`, 'g');
|
|
70
81
|
return str.match(substrRegExp) !== null;
|
|
71
82
|
};
|
|
@@ -167,8 +178,24 @@ const checkDeprecatedEffort = (importStatements, deprecatedEffortMap, effortCach
|
|
|
167
178
|
// const matchingEffortMetainfoIndx = deprecatedEffortMap.findIndex((effortMetainfo) =>
|
|
168
179
|
// subStringAdhocDSMatcher(importedComponent, effortMetainfo.component),
|
|
169
180
|
// );
|
|
170
|
-
|
|
171
|
-
|
|
181
|
+
// component name alone is not enough - the same component name can have multiple rows across different
|
|
182
|
+
// oldPackage spellings (E.G. DSButton from ds-button-v1 vs ds-legacy-button-v1), we only want the row(s)
|
|
183
|
+
// matching the package actually imported from, otherwise we'd report packages the file never imported.
|
|
184
|
+
const matchingDeprecationsBeforeDedup = deprecatedEffortMap.filter(
|
|
185
|
+
(effortMetainfo) =>
|
|
186
|
+
subStringAdhocDSMatcher(importedComponent, effortMetainfo.component) &&
|
|
187
|
+
subStringAdhocDSMatcher(packageName, effortMetainfo.oldPackage),
|
|
188
|
+
);
|
|
189
|
+
// some entries share the exact same component + oldPackage pair (E.G. DSSlider from ds-slider pointing at
|
|
190
|
+
// two different newPackage suggestions) - that is still one real match on the file, so we only want to
|
|
191
|
+
// report it once, not once per row.
|
|
192
|
+
const matchingDeprecations = matchingDeprecationsBeforeDedup.filter(
|
|
193
|
+
(effortMetainfo, indx) =>
|
|
194
|
+
matchingDeprecationsBeforeDedup.findIndex(
|
|
195
|
+
(otherEffortMetainfo) =>
|
|
196
|
+
otherEffortMetainfo.component === effortMetainfo.component &&
|
|
197
|
+
otherEffortMetainfo.oldPackage === effortMetainfo.oldPackage,
|
|
198
|
+
) === indx,
|
|
172
199
|
);
|
|
173
200
|
if (matchingDeprecations.length < 1) return;
|
|
174
201
|
// if (matchingEffortMetainfoIndx === -1) return;
|
|
@@ -276,6 +303,31 @@ export const checkDeprecatedBeforeDeprecationFlowExisted = (importStatements) =>
|
|
|
276
303
|
return matchedEfforts;
|
|
277
304
|
};
|
|
278
305
|
|
|
306
|
+
/**
|
|
307
|
+
* given a list of import statements, it checks if any of them is importing a component we are not
|
|
308
|
+
* actively investing in, that has no committed replacement, and has no known active consumers
|
|
309
|
+
* @param {string[][]} importStatements - list of import statements to check generated by the regex
|
|
310
|
+
* @returns {Map<string, matchDismissedUnfundedCaseByCase>} - a map of matched statements
|
|
311
|
+
*/
|
|
312
|
+
export const checkDismissedUnfundedCaseByCase = (importStatements) => {
|
|
313
|
+
const matchedEfforts = new Map();
|
|
314
|
+
importStatements.forEach((importStatement) => {
|
|
315
|
+
const [fullMatch, importedComponent, packageName] = importStatement;
|
|
316
|
+
const matchingDeprecations = DISMISSED_UNFUNDED_CASE_BY_CASE.filter(
|
|
317
|
+
(effortMetainfo) =>
|
|
318
|
+
subStringAdhocDSMatcher(importedComponent, effortMetainfo.component) &&
|
|
319
|
+
// {DSGroupBox} from 'ds-mobile' is not (yet) deprecated, DSGroupBox is a repeated component name, we need the extra package check
|
|
320
|
+
subStringAdhocDSMatcher(packageName, effortMetainfo.oldPackage),
|
|
321
|
+
);
|
|
322
|
+
if (matchingDeprecations.length < 1) return;
|
|
323
|
+
matchedEfforts.set(fullMatch, {
|
|
324
|
+
regexpInfo: { fullMatch, importedComponent, packageName },
|
|
325
|
+
matchingEffortsMetainfo: matchingDeprecations,
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
return matchedEfforts;
|
|
329
|
+
};
|
|
330
|
+
|
|
279
331
|
/**
|
|
280
332
|
* given a file path, it checks if the file contains any deprecated components
|
|
281
333
|
* @param {string} filePath - path to the file to check
|
|
@@ -291,6 +343,7 @@ export const checkFileForDeprecatedComponents = (filePath) => {
|
|
|
291
343
|
const deprecatedWithExample = checkDeprecatedWithExample(importStatements);
|
|
292
344
|
const reportingUsageOfInternalOnly = checkInternalUsageOnlyIntended(importStatements);
|
|
293
345
|
const deprecatedBeforeDeprecationFlowsExisted = checkDeprecatedBeforeDeprecationFlowExisted(importStatements);
|
|
346
|
+
const dismissedUnfundedCaseByCase = checkDismissedUnfundedCaseByCase(importStatements);
|
|
294
347
|
|
|
295
348
|
const isFileProblematic =
|
|
296
349
|
deprecatedSmallEffort.size > 0 ||
|
|
@@ -298,7 +351,8 @@ export const checkFileForDeprecatedComponents = (filePath) => {
|
|
|
298
351
|
deprecatedLargeEffort.size > 0 ||
|
|
299
352
|
deprecatedWithExample.size > 0 ||
|
|
300
353
|
reportingUsageOfInternalOnly.size > 0 ||
|
|
301
|
-
deprecatedBeforeDeprecationFlowsExisted.size > 0
|
|
354
|
+
deprecatedBeforeDeprecationFlowsExisted.size > 0 ||
|
|
355
|
+
dismissedUnfundedCaseByCase.size > 0;
|
|
302
356
|
|
|
303
357
|
return {
|
|
304
358
|
isFileProblematic,
|
|
@@ -308,6 +362,7 @@ export const checkFileForDeprecatedComponents = (filePath) => {
|
|
|
308
362
|
deprecatedWithExample,
|
|
309
363
|
reportingUsageOfInternalOnly,
|
|
310
364
|
deprecatedBeforeDeprecationFlowsExisted,
|
|
365
|
+
dismissedUnfundedCaseByCase,
|
|
311
366
|
};
|
|
312
367
|
};
|
|
313
368
|
|
|
@@ -357,6 +412,7 @@ export const generateCsvRows = (fileReport, filePath, org, repo, packagesVersion
|
|
|
357
412
|
deprecatedWithExample,
|
|
358
413
|
reportingUsageOfInternalOnly,
|
|
359
414
|
deprecatedBeforeDeprecationFlowsExisted,
|
|
415
|
+
dismissedUnfundedCaseByCase,
|
|
360
416
|
} = fileReport;
|
|
361
417
|
// csv structure = `Legacy Component,${options.org ? 'Org,' : ''}${options.repo ? 'Repository,' : ''}Legacy Package,Filename,File Path,Effort Size,Story Points,Version(s)`;
|
|
362
418
|
// Legacy Component -> matchingEffortMetainfo.component
|
|
@@ -406,5 +462,12 @@ export const generateCsvRows = (fileReport, filePath, org, repo, packagesVersion
|
|
|
406
462
|
});
|
|
407
463
|
});
|
|
408
464
|
|
|
465
|
+
dismissedUnfundedCaseByCase.forEach((effort) => {
|
|
466
|
+
const { matchingEffortsMetainfo } = effort;
|
|
467
|
+
matchingEffortsMetainfo.forEach((effort) => {
|
|
468
|
+
csvRows.push(generateCsvRowFromMatchedEffort(effort, filePath, org, repo, packagesVersions));
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
|
|
409
472
|
return csvRows;
|
|
410
473
|
};
|
|
@@ -6,8 +6,9 @@ import { checkDeprecatedPackages } from './check-deprecated-packages/index.mjs';
|
|
|
6
6
|
import { helpMigrateToV3 } from './help-migrate-to-v3/index.mjs';
|
|
7
7
|
import { deprecatedComponentsUsageReport } from './deprecated-components-usage-report/index.mjs';
|
|
8
8
|
import { componentsUsageReport } from './components-usage-report/index.mjs';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { run26dot3MigrationLogic } from './legacy-deprecation/26-3/run26dot3MigrationLogic.mjs';
|
|
10
|
+
import { run27dot1MigrationLogic } from './legacy-deprecation/27-1/run27dot1MigrationLogic.mjs';
|
|
11
|
+
import { legacyDeprecation } from './legacy-deprecation/index.mjs';
|
|
11
12
|
import { COMMANDS } from '../commands.mjs';
|
|
12
13
|
|
|
13
14
|
export async function executeCommandsMap(args, options) {
|
|
@@ -37,10 +38,13 @@ export async function executeCommandsMap(args, options) {
|
|
|
37
38
|
componentsUsageReport(options);
|
|
38
39
|
break;
|
|
39
40
|
case COMMANDS.LEGACY_DEPRECATION_26DOT3:
|
|
40
|
-
|
|
41
|
+
run26dot3MigrationLogic(options);
|
|
41
42
|
break;
|
|
42
43
|
case COMMANDS.LEGACY_DEPRECATION_27DOT1:
|
|
43
|
-
|
|
44
|
+
run27dot1MigrationLogic(options);
|
|
45
|
+
break;
|
|
46
|
+
case COMMANDS.LEGACY_DEPRECATION:
|
|
47
|
+
legacyDeprecation(options);
|
|
44
48
|
break;
|
|
45
49
|
default:
|
|
46
50
|
break;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable max-statements */
|
|
2
2
|
import fs from 'fs';
|
|
3
|
-
import { boldRedStr, yellowStr } from '
|
|
4
|
-
import { filePathsWithGitIgnore } from '
|
|
3
|
+
import { boldRedStr, yellowStr } from '../../../../utils/CLI_COLORS.mjs';
|
|
4
|
+
import { filePathsWithGitIgnore } from '../../../../utils/filepathsMatchers.mjs';
|
|
5
5
|
import { legacy26dot3ImportMap } from './legacy26dot3ImportMap.mjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -9,7 +9,9 @@ import { legacy26dot3ImportMap } from './legacy26dot3ImportMap.mjs';
|
|
|
9
9
|
* parse a package.json, checks the dependencies(dev and peer included) for any legacy component
|
|
10
10
|
*
|
|
11
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 "
|
|
12
|
+
* generates a new content for the file with the legacy component replaced with the new one and version set to "*"
|
|
13
|
+
* ("latest" is an npm dist-tag, not a valid semver range - yarn/pnpm expect a real range, so we use the
|
|
14
|
+
* universally-supported wildcard instead)
|
|
13
15
|
*
|
|
14
16
|
* @param {string} path - the package.json file path
|
|
15
17
|
* @param {object} options - options *
|
|
@@ -33,7 +35,7 @@ const specificPackageJsonReplaceLogic = (path, options) => {
|
|
|
33
35
|
Object.keys(parsedContent.dependencies || {}).forEach((oldPackageString) => {
|
|
34
36
|
if (legacy26dot3ImportMap[oldPackageString]) {
|
|
35
37
|
const newPackageString = legacy26dot3ImportMap[oldPackageString].matchChange;
|
|
36
|
-
newDeps[newPackageString] = '
|
|
38
|
+
newDeps[newPackageString] = '*';
|
|
37
39
|
delete newDeps[oldPackageString];
|
|
38
40
|
depsChanged = true;
|
|
39
41
|
}
|
|
@@ -44,7 +46,7 @@ const specificPackageJsonReplaceLogic = (path, options) => {
|
|
|
44
46
|
Object.keys(parsedContent.devDependencies || {}).forEach((oldPackageString) => {
|
|
45
47
|
if (legacy26dot3ImportMap[oldPackageString]) {
|
|
46
48
|
const newPackageString = legacy26dot3ImportMap[oldPackageString].matchChange;
|
|
47
|
-
newDevDeps[newPackageString] = '
|
|
49
|
+
newDevDeps[newPackageString] = '*';
|
|
48
50
|
delete newDevDeps[oldPackageString];
|
|
49
51
|
devDepsChanged = true;
|
|
50
52
|
}
|
|
@@ -54,7 +56,7 @@ const specificPackageJsonReplaceLogic = (path, options) => {
|
|
|
54
56
|
Object.keys(parsedContent.peerDependencies || {}).forEach((oldPackageString) => {
|
|
55
57
|
if (legacy26dot3ImportMap[oldPackageString]) {
|
|
56
58
|
const newPackageString = legacy26dot3ImportMap[oldPackageString].matchChange;
|
|
57
|
-
newPeerDeps[newPackageString] = '
|
|
59
|
+
newPeerDeps[newPackageString] = '*';
|
|
58
60
|
delete newPeerDeps[oldPackageString];
|
|
59
61
|
peerDepsChanged = true;
|
|
60
62
|
}
|
|
@@ -104,7 +106,7 @@ export const packageJsonReplaceLogic = (options) => {
|
|
|
104
106
|
`To make sure migration is successful manually:
|
|
105
107
|
- check for any legacy components in your package.json
|
|
106
108
|
- replace them with the new ones.
|
|
107
|
-
- point them to "
|
|
109
|
+
- point them to "*", the legacy library only get updated with critical security fixes and very rarely so.
|
|
108
110
|
- regenerate the lock file ( pnpm i --fix-lockfile or equivalent command/flow for your package manager)
|
|
109
111
|
|
|
110
112
|
The legacy components to look for are:`,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import fse from 'fs-extra';
|
|
3
|
-
import { filePathsWithGitIgnore } from '
|
|
3
|
+
import { filePathsWithGitIgnore } from '../../../../utils/filepathsMatchers.mjs';
|
|
4
4
|
import { legacy26dot3ImportMap, executeMapReplacement } from './legacy26dot3ImportMap.mjs';
|
|
5
5
|
import { packageJsonReplaceLogic } from './packageJsonReplaceLogic.mjs';
|
|
6
6
|
|
|
@@ -14,7 +14,7 @@ import { packageJsonReplaceLogic } from './packageJsonReplaceLogic.mjs';
|
|
|
14
14
|
*
|
|
15
15
|
* @returns {void}
|
|
16
16
|
*/
|
|
17
|
-
export const
|
|
17
|
+
export const run26dot3MigrationLogic = (options) => {
|
|
18
18
|
// 1- get all the "raw" code file paths using gitignore blacklist and file extension filter
|
|
19
19
|
const filesToParse = filePathsWithGitIgnore(options);
|
|
20
20
|
if (options.debug) console.log('legacy26dot3ImportMap :>> ', legacy26dot3ImportMap);
|
|
@@ -49,4 +49,4 @@ export const legacyDeprecation26dot3 = (options) => {
|
|
|
49
49
|
});
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
export default
|
|
52
|
+
export default run26dot3MigrationLogic;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable max-statements */
|
|
2
2
|
import fs from 'fs';
|
|
3
|
-
import { boldRedStr, yellowStr } from '
|
|
4
|
-
import { filePathsWithGitIgnore } from '
|
|
3
|
+
import { boldRedStr, yellowStr } from '../../../../utils/CLI_COLORS.mjs';
|
|
4
|
+
import { filePathsWithGitIgnore } from '../../../../utils/filepathsMatchers.mjs';
|
|
5
5
|
import { legacy27dot1ImportMap } from './legacy27dot1ImportMap.mjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -9,7 +9,9 @@ import { legacy27dot1ImportMap } from './legacy27dot1ImportMap.mjs';
|
|
|
9
9
|
* parse a package.json, checks the dependencies(dev and peer included) for any legacy component
|
|
10
10
|
*
|
|
11
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 "
|
|
12
|
+
* generates a new content for the file with the legacy component replaced with the new one and version set to "*"
|
|
13
|
+
* ("latest" is an npm dist-tag, not a valid semver range - yarn/pnpm expect a real range, so we use the
|
|
14
|
+
* universally-supported wildcard instead)
|
|
13
15
|
*
|
|
14
16
|
* @param {string} path - the package.json file path
|
|
15
17
|
* @param {object} options - options *
|
|
@@ -33,7 +35,7 @@ const specificPackageJsonReplaceLogic = (path, options) => {
|
|
|
33
35
|
Object.keys(parsedContent.dependencies || {}).forEach((oldPackageString) => {
|
|
34
36
|
if (legacy27dot1ImportMap[oldPackageString]) {
|
|
35
37
|
const newPackageString = legacy27dot1ImportMap[oldPackageString].matchChange;
|
|
36
|
-
newDeps[newPackageString] = '
|
|
38
|
+
newDeps[newPackageString] = '*';
|
|
37
39
|
delete newDeps[oldPackageString];
|
|
38
40
|
depsChanged = true;
|
|
39
41
|
}
|
|
@@ -44,7 +46,7 @@ const specificPackageJsonReplaceLogic = (path, options) => {
|
|
|
44
46
|
Object.keys(parsedContent.devDependencies || {}).forEach((oldPackageString) => {
|
|
45
47
|
if (legacy27dot1ImportMap[oldPackageString]) {
|
|
46
48
|
const newPackageString = legacy27dot1ImportMap[oldPackageString].matchChange;
|
|
47
|
-
newDevDeps[newPackageString] = '
|
|
49
|
+
newDevDeps[newPackageString] = '*';
|
|
48
50
|
delete newDevDeps[oldPackageString];
|
|
49
51
|
devDepsChanged = true;
|
|
50
52
|
}
|
|
@@ -54,7 +56,7 @@ const specificPackageJsonReplaceLogic = (path, options) => {
|
|
|
54
56
|
Object.keys(parsedContent.peerDependencies || {}).forEach((oldPackageString) => {
|
|
55
57
|
if (legacy27dot1ImportMap[oldPackageString]) {
|
|
56
58
|
const newPackageString = legacy27dot1ImportMap[oldPackageString].matchChange;
|
|
57
|
-
newPeerDeps[newPackageString] = '
|
|
59
|
+
newPeerDeps[newPackageString] = '*';
|
|
58
60
|
delete newPeerDeps[oldPackageString];
|
|
59
61
|
peerDepsChanged = true;
|
|
60
62
|
}
|
|
@@ -104,7 +106,7 @@ export const packageJsonReplaceLogic = (options) => {
|
|
|
104
106
|
`To make sure migration is successful manually:
|
|
105
107
|
- check for any legacy components in your package.json
|
|
106
108
|
- replace them with the new ones.
|
|
107
|
-
- point them to "
|
|
109
|
+
- point them to "*", the legacy library only get updated with critical security fixes and very rarely so.
|
|
108
110
|
- regenerate the lock file ( pnpm i --fix-lockfile or equivalent command/flow for your package manager)
|
|
109
111
|
|
|
110
112
|
The legacy components to look for are:`,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import fse from 'fs-extra';
|
|
3
|
-
import { filePathsWithGitIgnore } from '
|
|
3
|
+
import { filePathsWithGitIgnore } from '../../../../utils/filepathsMatchers.mjs';
|
|
4
4
|
import { legacy27dot1ImportMap, executeMapReplacement } from './legacy27dot1ImportMap.mjs';
|
|
5
5
|
import { packageJsonReplaceLogic } from './packageJsonReplaceLogic.mjs';
|
|
6
6
|
|
|
@@ -14,7 +14,7 @@ import { packageJsonReplaceLogic } from './packageJsonReplaceLogic.mjs';
|
|
|
14
14
|
*
|
|
15
15
|
* @returns {void}
|
|
16
16
|
*/
|
|
17
|
-
export const
|
|
17
|
+
export const run27dot1MigrationLogic = (options) => {
|
|
18
18
|
// 1- get all the "raw" code file paths using gitignore blacklist and file extension filter
|
|
19
19
|
const filesToParse = filePathsWithGitIgnore(options);
|
|
20
20
|
if (options.debug) console.log('legacy27dot1ImportMap :>> ', legacy27dot1ImportMap);
|
|
@@ -49,4 +49,4 @@ export const legacyDeprecation27dot1 = (options) => {
|
|
|
49
49
|
});
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
export default
|
|
52
|
+
export default run27dot1MigrationLogic;
|
|
@@ -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 legacy27dot2ImportMap = {
|
|
23
|
+
'@elliemae/ds-mobile': {
|
|
24
|
+
matchChange: '@elliemae/ds-legacy-mobile',
|
|
25
|
+
regexp: new RegExp(/(from ['"])(@elliemae\/ds-mobile)(['"])/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(legacy27dot2ImportMap).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,126 @@
|
|
|
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 { legacy27dot2ImportMap } from './legacy27dot2ImportMap.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 "*"
|
|
13
|
+
* ("latest" is an npm dist-tag, not a valid semver range - yarn/pnpm expect a real range, so we use the
|
|
14
|
+
* universally-supported wildcard instead)
|
|
15
|
+
*
|
|
16
|
+
* @param {string} path - the package.json file path
|
|
17
|
+
* @param {object} options - options *
|
|
18
|
+
* @param {boolean} options.debug - debug flag
|
|
19
|
+
* @returns {string | -1} - the new content (json to string) if any change is needed,
|
|
20
|
+
* or -1 if no change is needed or in case of error
|
|
21
|
+
*/
|
|
22
|
+
// eslint-disable-next-line complexity
|
|
23
|
+
const specificPackageJsonReplaceLogic = (path, options) => {
|
|
24
|
+
const fileContent = fs.readFileSync(path, {
|
|
25
|
+
encoding: 'utf8',
|
|
26
|
+
flag: 'r',
|
|
27
|
+
});
|
|
28
|
+
if (!fileContent || typeof fileContent !== 'string') {
|
|
29
|
+
if (options.debug) console.log(yellowStr(`Empty or invalid content in ${path}`));
|
|
30
|
+
return -1;
|
|
31
|
+
}
|
|
32
|
+
const parsedContent = JSON.parse(fileContent);
|
|
33
|
+
const newDeps = { ...parsedContent.dependencies };
|
|
34
|
+
let depsChanged = false;
|
|
35
|
+
Object.keys(parsedContent.dependencies || {}).forEach((oldPackageString) => {
|
|
36
|
+
if (legacy27dot2ImportMap[oldPackageString]) {
|
|
37
|
+
const newPackageString = legacy27dot2ImportMap[oldPackageString].matchChange;
|
|
38
|
+
newDeps[newPackageString] = '*';
|
|
39
|
+
delete newDeps[oldPackageString];
|
|
40
|
+
depsChanged = true;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const newDevDeps = { ...parsedContent.devDependencies };
|
|
45
|
+
let devDepsChanged = false;
|
|
46
|
+
Object.keys(parsedContent.devDependencies || {}).forEach((oldPackageString) => {
|
|
47
|
+
if (legacy27dot2ImportMap[oldPackageString]) {
|
|
48
|
+
const newPackageString = legacy27dot2ImportMap[oldPackageString].matchChange;
|
|
49
|
+
newDevDeps[newPackageString] = '*';
|
|
50
|
+
delete newDevDeps[oldPackageString];
|
|
51
|
+
devDepsChanged = true;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
const newPeerDeps = { ...parsedContent.peerDependencies };
|
|
55
|
+
let peerDepsChanged = false;
|
|
56
|
+
Object.keys(parsedContent.peerDependencies || {}).forEach((oldPackageString) => {
|
|
57
|
+
if (legacy27dot2ImportMap[oldPackageString]) {
|
|
58
|
+
const newPackageString = legacy27dot2ImportMap[oldPackageString].matchChange;
|
|
59
|
+
newPeerDeps[newPackageString] = '*';
|
|
60
|
+
delete newPeerDeps[oldPackageString];
|
|
61
|
+
peerDepsChanged = true;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
if (!depsChanged && !devDepsChanged && !peerDepsChanged) {
|
|
65
|
+
if (options.debug) console.log(yellowStr(`No legacy dependencies found in ${path}`));
|
|
66
|
+
return -1;
|
|
67
|
+
}
|
|
68
|
+
const newContent = {
|
|
69
|
+
...parsedContent,
|
|
70
|
+
...(depsChanged && { dependencies: newDeps }),
|
|
71
|
+
...(devDepsChanged && { devDependencies: newDevDeps }),
|
|
72
|
+
...(peerDepsChanged && { peerDependencies: newPeerDeps }),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return JSON.stringify(newContent, null, 2);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* do something similar, but with opinionated "package.json" files and logic
|
|
80
|
+
*
|
|
81
|
+
* in particular:
|
|
82
|
+
* - we get the package.json files (multiple for monorepos?) and we parse them as json.
|
|
83
|
+
* - check the dependencies and devDependencies and peerDependencies for any legacy component
|
|
84
|
+
* (keyof legacy27dot2ImportMap) and replace it with the new one (value of legacy27dot2ImportMap)
|
|
85
|
+
* - if any change is needed, we return an object so that the caller can decide what to do with it
|
|
86
|
+
* (write it or just log it for the user to review)
|
|
87
|
+
* @param {object} options - options *
|
|
88
|
+
* @param {string} options.startingDirPath - path to the project root directory (where node_modules folder lives)
|
|
89
|
+
* @param {string} options.gitIgnorePath - path to the .gitignore file
|
|
90
|
+
* @param {boolean} options.debug - debug flag
|
|
91
|
+
* @param {string} options.fileExtensions - comma separated list of file extensions to filter by
|
|
92
|
+
* @returns {object} - an object with the (package.json) file path as key and the new content (json to string) as value,
|
|
93
|
+
* for each package.json file that needs changes
|
|
94
|
+
*/
|
|
95
|
+
export const packageJsonReplaceLogic = (options) => {
|
|
96
|
+
// 1- get the package.json files (multiple for monorepos?) paths
|
|
97
|
+
const jsonFilesPaths = filePathsWithGitIgnore({ ...options, fileExtensions: '.json' });
|
|
98
|
+
// the above function doesn't allow for specific filename matching,
|
|
99
|
+
// this is a one-off right now so not extending the function
|
|
100
|
+
// instead we filter the results with a simple string match, to get only the package.json files paths
|
|
101
|
+
const packageJsonFilesPaths = jsonFilesPaths.filter((path) => path.endsWith('package.json'));
|
|
102
|
+
if (packageJsonFilesPaths.length === 0) {
|
|
103
|
+
console.log(boldRedStr('No package.json files! Please check the startingDirPath and gitIgnorePath options.'));
|
|
104
|
+
console.log(
|
|
105
|
+
yellowStr(
|
|
106
|
+
`To make sure migration is successful manually:
|
|
107
|
+
- check for any legacy components in your package.json
|
|
108
|
+
- replace them with the new ones.
|
|
109
|
+
- point them to "*", the legacy library only get updated with critical security fixes and very rarely so.
|
|
110
|
+
- regenerate the lock file ( pnpm i --fix-lockfile or equivalent command/flow for your package manager)
|
|
111
|
+
|
|
112
|
+
The legacy components to look for are:`,
|
|
113
|
+
),
|
|
114
|
+
);
|
|
115
|
+
console.log(legacy27dot2ImportMap);
|
|
116
|
+
return {};
|
|
117
|
+
}
|
|
118
|
+
const filesToChange = {};
|
|
119
|
+
// 2 - parse, check and generate new content for each package.json file
|
|
120
|
+
packageJsonFilesPaths.forEach((path) => {
|
|
121
|
+
if (options.debug) console.log('path :>> ', path);
|
|
122
|
+
const newContent = specificPackageJsonReplaceLogic(path, options);
|
|
123
|
+
if (newContent !== -1) filesToChange[path] = newContent;
|
|
124
|
+
});
|
|
125
|
+
return filesToChange;
|
|
126
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import fse from 'fs-extra';
|
|
3
|
+
import { filePathsWithGitIgnore } from '../../../../utils/filepathsMatchers.mjs';
|
|
4
|
+
import { legacy27dot2ImportMap, executeMapReplacement } from './legacy27dot2ImportMap.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 run27dot2MigrationLogic = (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('legacy27dot2ImportMap :>> ', legacy27dot2ImportMap);
|
|
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 legacy27dot2ImportMap) and replace it with the new one (value of legacy27dot2ImportMap)
|
|
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 run27dot2MigrationLogic;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# `legacy-deprecation` — why this folder exists and how to extend it
|
|
2
|
+
|
|
3
|
+
## History
|
|
4
|
+
|
|
5
|
+
Every time a package gets pulled out of live's normal publish set and only remains available
|
|
6
|
+
through the `-legacy-` prefixed gateway package, consumers importing the old plain package name
|
|
7
|
+
need a mechanical rename to the `-legacy-` prefixed one (imports + `package.json`
|
|
8
|
+
dependencies/devDependencies/peerDependencies, version set to `"*"`). Each of these used to get its
|
|
9
|
+
own dedicated command and its own top-level folder, named after the dot-release it shipped in:
|
|
10
|
+
`legacy-deprecation-26-3`, `legacy-deprecation-27-1`, then (briefly, within the same session that
|
|
11
|
+
created this folder) `legacy-deprecation-27-2`.
|
|
12
|
+
|
|
13
|
+
That doesn't scale - a new dedicated command, a new folder, a new `commands.mjs` entry, a new
|
|
14
|
+
`execute-commands-map.mjs` case, a new `inquirer-questions-prompter.mjs` case, and a new
|
|
15
|
+
`command-arguments/getXQuestions.mjs` file, every single time one more package needs this exact
|
|
16
|
+
same treatment. So starting with what would have been `27dot2` (the `@elliemae/ds-mobile` ->
|
|
17
|
+
`@elliemae/ds-legacy-mobile` rename), this became a single, all-encompassing command instead:
|
|
18
|
+
**`legacy-deprecation`**, version-agnostic on purpose - "regardless of the release version, give me
|
|
19
|
+
the latest migration(s)".
|
|
20
|
+
|
|
21
|
+
## Why `26-3/` and `27-1/` still exist as their own subfolders here
|
|
22
|
+
|
|
23
|
+
`legacy-deprecation-26dot3` and `legacy-deprecation-27dot1` are still their own, separately
|
|
24
|
+
registered, separately invocable CLI commands (see `commands.mjs`,
|
|
25
|
+
`execute-commands-map.mjs`, `inquirer-questions-prompter.mjs`, and their own
|
|
26
|
+
`command-arguments/getLegacyDeprecationXXdotYQuestions.mjs` files - none of that changed). They are
|
|
27
|
+
referenced by name in Confluence pages and recorded walkthroughs, so the command names and their
|
|
28
|
+
CLI-visible behavior must keep working exactly as they did before, indefinitely.
|
|
29
|
+
|
|
30
|
+
What moved is only *where their implementation lives on disk*: their import maps,
|
|
31
|
+
`packageJsonReplaceLogic.mjs`, and core logic now live in `26-3/` and `27-1/` in this folder, each
|
|
32
|
+
exposing a `run26dot3MigrationLogic()` / `run27dot1MigrationLogic()` function.
|
|
33
|
+
`execute-commands-map.mjs`'s cases for those two commands call those functions directly - they are
|
|
34
|
+
not wrapped through this folder's `legacyDeprecation()` dispatcher, they're just physically
|
|
35
|
+
scaffolded alongside it now instead of living in their own now-deleted top-level folders.
|
|
36
|
+
|
|
37
|
+
## How to extend this going forward
|
|
38
|
+
|
|
39
|
+
When another package needs this same `-legacy-` prefix rename treatment, **do not create a new
|
|
40
|
+
dedicated command**. Instead:
|
|
41
|
+
|
|
42
|
+
1. Add a new subfolder here, named after the dot-release (E.G. `27-3/`).
|
|
43
|
+
2. Give it its own `legacyXXdotYImportMap.mjs` (exact-match capture-group regex, same technique as
|
|
44
|
+
every other one here) and `packageJsonReplaceLogic.mjs`, mirroring `27-2/`'s exact shape - `27-2/`
|
|
45
|
+
is the reference pattern for a *fully self-contained* subfolder (not a thin wrapper around an
|
|
46
|
+
external, independently-registered command, unlike `26-3/`/`27-1/`).
|
|
47
|
+
3. Expose a `runXXdotYMigrationLogic()` function from an `index.mjs`-equivalent file in that
|
|
48
|
+
subfolder (name it `runXXdotYMigrationLogic.mjs`, matching the existing convention).
|
|
49
|
+
4. Wire it into this folder's `index.mjs`: import it, add its dot-release key to
|
|
50
|
+
`AVAILABLE_MIGRATIONS`, and add its conditional call in `legacyDeprecation()`.
|
|
51
|
+
|
|
52
|
+
No new `commands.mjs` entry, no new `execute-commands-map.mjs` case, no new
|
|
53
|
+
`inquirer-questions-prompter.mjs` case, no new CLI flag - the existing `legacy-deprecation` command
|
|
54
|
+
and its `--migrations` flag already cover it.
|
|
55
|
+
|
|
56
|
+
## The `migrations` selection behavior
|
|
57
|
+
|
|
58
|
+
`options.migrations` is **CLI-flag only, deliberately not prompted for** (see
|
|
59
|
+
`command-arguments/getLegacyDeprecationQuestions.mjs` - it only prompts for `startingDirPath` and
|
|
60
|
+
`gitIgnorePath`). This is unlike `startingDirPath`/`gitIgnorePath`, which fall back to an interactive
|
|
61
|
+
prompt when omitted: picking specific migrations is expected to be rare and deliberate (someone who
|
|
62
|
+
already knows exactly which one(s) they want), so it shouldn't force every interactive run through an
|
|
63
|
+
extra prompt. Pass it as a comma separated string, E.G. `--migrations 26.3,27.1`. It defaults to
|
|
64
|
+
`"all"` when omitted. **`"all"` always wins**, even if the user also passes specific migrations
|
|
65
|
+
alongside it (E.G. `--migrations 26.3,all`) - this is intentional, not a bug: "all" existing in the
|
|
66
|
+
list at all means "run everything," full stop, so a consumer/CI script that defaults to `all` can't
|
|
67
|
+
be accidentally narrowed by a stray extra pick.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# `legacy-deprecation` — Claude Code Notes
|
|
2
|
+
|
|
3
|
+
This folder's rationale, its `26-3`/`27-1` vs `27-2`-and-onward folder shapes, and the extension
|
|
4
|
+
process for adding a new release's migration are documented in **`AGENTS.md`** in this same
|
|
5
|
+
directory. Read it before touching anything here.
|
|
6
|
+
|
|
7
|
+
Short version: `legacy-deprecation-26dot3` and `legacy-deprecation-27dot1` are still their own,
|
|
8
|
+
separately registered CLI commands and must keep working unchanged (Confluence/video references) -
|
|
9
|
+
only their implementation files moved into `26-3/`/`27-1/` here. Everything from `27-2/` onward is
|
|
10
|
+
reached exclusively through the single all-encompassing `legacy-deprecation` command (this folder's
|
|
11
|
+
`index.mjs`), selected via the CLI-only `--migrations` flag ("all" by default, not prompted for, and
|
|
12
|
+
"all" always wins). Adding a new dot-release migration means adding a new subfolder here, not a new
|
|
13
|
+
command - see
|
|
14
|
+
AGENTS.md's "How to extend this going forward" section.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { run26dot3MigrationLogic } from './26-3/run26dot3MigrationLogic.mjs';
|
|
2
|
+
import { run27dot1MigrationLogic } from './27-1/run27dot1MigrationLogic.mjs';
|
|
3
|
+
import { run27dot2MigrationLogic } from './27-2/run27dot2MigrationLogic.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @type {object}
|
|
7
|
+
* the known migrations this command can run, plus the "all" pseudo-migration that always wins
|
|
8
|
+
* even if the user also fine-picked specific ones alongside it - see AGENTS.md in this folder
|
|
9
|
+
* for why this command exists instead of a new dedicated command per release.
|
|
10
|
+
*/
|
|
11
|
+
export const AVAILABLE_MIGRATIONS = {
|
|
12
|
+
ALL: 'all',
|
|
13
|
+
DOT_26_3: '26.3',
|
|
14
|
+
DOT_27_1: '27.1',
|
|
15
|
+
DOT_27_2: '27.2',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* normalizes options.migrations (a comma separated string from the CLI flag, or an array from the
|
|
20
|
+
* inquirer checkbox prompt) into an array of trimmed, non-empty migration keys.
|
|
21
|
+
* @param {string | string[] | undefined} migrations
|
|
22
|
+
* @returns {string[]}
|
|
23
|
+
*/
|
|
24
|
+
const normalizeMigrations = (migrations) => {
|
|
25
|
+
if (Array.isArray(migrations)) return migrations.map((migration) => migration.trim()).filter(Boolean);
|
|
26
|
+
if (typeof migrations === 'string') {
|
|
27
|
+
return migrations
|
|
28
|
+
.split(',')
|
|
29
|
+
.map((migration) => migration.trim())
|
|
30
|
+
.filter(Boolean);
|
|
31
|
+
}
|
|
32
|
+
return [];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* runs the requested legacy-to-legacy import/package.json rename migration(s).
|
|
37
|
+
* "all" is the default and always wins, even if specific migrations are also picked alongside it -
|
|
38
|
+
* see AGENTS.md in this folder for the rationale.
|
|
39
|
+
* @param {object} options - options *
|
|
40
|
+
* @param {string} options.startingDirPath - path to the project root directory (where node_modules folder lives)
|
|
41
|
+
* @param {string} options.gitIgnorePath - path to the .gitignore file
|
|
42
|
+
* @param {boolean} options.debug - debug flag
|
|
43
|
+
* @param {string} options.fileExtensions - comma separated list of file extensions to filter by
|
|
44
|
+
* @param {string | string[]} [options.migrations] - which migration(s) to run - "26.3", "27.1", "27.2", or "all"
|
|
45
|
+
* (default). Comma separated when passed as a CLI flag.
|
|
46
|
+
*
|
|
47
|
+
* @returns {void}
|
|
48
|
+
*/
|
|
49
|
+
export const legacyDeprecation = (options) => {
|
|
50
|
+
const requestedMigrations = normalizeMigrations(options.migrations);
|
|
51
|
+
const runAll = requestedMigrations.length === 0 || requestedMigrations.includes(AVAILABLE_MIGRATIONS.ALL);
|
|
52
|
+
|
|
53
|
+
if (runAll || requestedMigrations.includes(AVAILABLE_MIGRATIONS.DOT_26_3)) run26dot3MigrationLogic(options);
|
|
54
|
+
if (runAll || requestedMigrations.includes(AVAILABLE_MIGRATIONS.DOT_27_1)) run27dot1MigrationLogic(options);
|
|
55
|
+
if (runAll || requestedMigrations.includes(AVAILABLE_MIGRATIONS.DOT_27_2)) run27dot2MigrationLogic(options);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default legacyDeprecation;
|
|
@@ -9,6 +9,7 @@ export const COMMANDS = {
|
|
|
9
9
|
COMPONENT_USAGE_REPORT: 'components-usage-report',
|
|
10
10
|
LEGACY_DEPRECATION_26DOT3: 'legacy-deprecation-26dot3',
|
|
11
11
|
LEGACY_DEPRECATION_27DOT1: 'legacy-deprecation-27dot1',
|
|
12
|
+
LEGACY_DEPRECATION: 'legacy-deprecation',
|
|
12
13
|
EXIT: 'exit',
|
|
13
14
|
};
|
|
14
15
|
|
|
@@ -8,6 +8,7 @@ import { getMissingPackagesQuestions } from './command-arguments/promptMissingPa
|
|
|
8
8
|
import { getDeprecatedUsageReportQuestions } from './command-arguments/getDeprecatedUsageReportQuestions.mjs';
|
|
9
9
|
import { getLegacyDeprecation26dot3Questions } from './command-arguments/getLegacyDeprecation26dot3Questions.mjs';
|
|
10
10
|
import { getLegacyDeprecation27dot1Questions } from './command-arguments/getLegacyDeprecation27dot1Questions.mjs';
|
|
11
|
+
import { getLegacyDeprecationQuestions } from './command-arguments/getLegacyDeprecationQuestions.mjs';
|
|
11
12
|
|
|
12
13
|
// eslint-disable-next-line complexity
|
|
13
14
|
async function promptForMissingScriptSpecificOptions({ originalOptions, promptOptions }) {
|
|
@@ -40,6 +41,9 @@ async function promptForMissingScriptSpecificOptions({ originalOptions, promptOp
|
|
|
40
41
|
case COMMANDS.LEGACY_DEPRECATION_27DOT1:
|
|
41
42
|
scriptQuestions.push(...getLegacyDeprecation27dot1Questions(originalOptions));
|
|
42
43
|
break;
|
|
44
|
+
case COMMANDS.LEGACY_DEPRECATION:
|
|
45
|
+
scriptQuestions.push(...getLegacyDeprecationQuestions(originalOptions));
|
|
46
|
+
break;
|
|
43
47
|
default:
|
|
44
48
|
break;
|
|
45
49
|
}
|
|
@@ -492,6 +492,11 @@ export const LEGACY_WITH_NEW_MEDIUM_MIGRATION_EFFORT = [
|
|
|
492
492
|
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ], storyPoints: "250",
|
|
493
493
|
},
|
|
494
494
|
|
|
495
|
+
{ component: 'DSSlider', oldPackage: '@elliemae/ds-legacy-slider',
|
|
496
|
+
newComponent: 'DSSliderV2', newPackage: '@elliemae/ds-legacy-slider-v2',
|
|
497
|
+
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ], storyPoints: "250",
|
|
498
|
+
},
|
|
499
|
+
|
|
495
500
|
{ component: 'DSZipCodeSearch', oldPackage: '@elliemae/ds-zipcode-search',
|
|
496
501
|
newComponent: 'DSAutocomplete', newPackage: '@elliemae/ds-form-layout-autocomplete',
|
|
497
502
|
tags:[ SEVERITY_TAGS.A11Y_LIMITATIONS ], storyPoints: "250",
|
|
@@ -698,7 +703,25 @@ export const NEVER_INTENDED_FOR_EXTERNAL_USAGE = [
|
|
|
698
703
|
*/
|
|
699
704
|
/* prettier-ignore */
|
|
700
705
|
export const DISMISSED_BEFORE_DEPRECATION_FLOWS_EXISTED = [
|
|
701
|
-
{ component: '*', oldPackage: '@elliemae/ds-imagelibrarymodal',
|
|
706
|
+
{ component: '*', oldPackage: '@elliemae/ds-imagelibrarymodal',
|
|
707
|
+
storyPoints: "300",
|
|
708
|
+
},
|
|
709
|
+
];
|
|
710
|
+
/**
|
|
711
|
+
* for packages we are not actively investing in and have no committed replacement (unlike
|
|
712
|
+
* LEGACY_WITH_NEW_*_MIGRATION_EFFORT) and that are not being "removed in favor of examples" either
|
|
713
|
+
* (unlike DISMISSED_WITH_EXAMPLE) - we still have no known active consumers, so there is nothing to
|
|
714
|
+
* migrate anyone to yet. If/when a real consumer shows up asking "what do I use instead", that becomes
|
|
715
|
+
* an ad-hoc, case-by-case discussion (potentially a dedicated effort for the specific components they
|
|
716
|
+
* need) rather than a pre-baked migration path here.
|
|
717
|
+
* @type {DeprecatedPackageMatch[]}
|
|
718
|
+
*/
|
|
719
|
+
/* prettier-ignore */
|
|
720
|
+
export const DISMISSED_UNFUNDED_CASE_BY_CASE = [
|
|
721
|
+
{ component: '*', oldPackage: '@elliemae/ds-mobile',
|
|
722
|
+
storyPoints: "300",
|
|
723
|
+
},
|
|
724
|
+
{ component: '*', oldPackage: '@elliemae/ds-legacy-mobile',
|
|
702
725
|
storyPoints: "300",
|
|
703
726
|
},
|
|
704
727
|
];
|
package/package.json
CHANGED
|
File without changes
|